diff options
author | Damien Picard <dam.pic@free.fr> | 2019-12-09 14:02:34 +0300 |
---|---|---|
committer | Damien Picard <dam.pic@free.fr> | 2019-12-09 14:03:58 +0300 |
commit | efbc5e5db7c73ae43ddc11abf8db8bc8f98c9945 (patch) | |
tree | 2f5a3fce49e89a35264abf0d2576deaf932ae446 /sun_position/hdr.py | |
parent | c5f0bbde29a9406c33ac84a04aa1ab6b2a27ff35 (diff) |
sun_position: move to release: T69936
Diffstat (limited to 'sun_position/hdr.py')
-rw-r--r-- | sun_position/hdr.py | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/sun_position/hdr.py b/sun_position/hdr.py new file mode 100644 index 00000000..257daf58 --- /dev/null +++ b/sun_position/hdr.py @@ -0,0 +1,303 @@ +### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# -*- coding: utf-8 -*- + +import bpy +import gpu +import bgl +from gpu_extras.batch import batch_for_shader +from mathutils import Vector +from math import sqrt, pi, atan2, asin + + +vertex_shader = ''' +uniform mat4 ModelViewProjectionMatrix; + +/* Keep in sync with intern/opencolorio/gpu_shader_display_transform_vertex.glsl */ +in vec2 texCoord; +in vec2 pos; +out vec2 texCoord_interp; + +void main() +{ + gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f); + gl_Position.z = 1.0; + texCoord_interp = texCoord; +}''' + +fragment_shader = ''' +in vec2 texCoord_interp; +out vec4 fragColor; + +uniform sampler2D image; +uniform float exposure; + +void main() +{ + fragColor = texture(image, texCoord_interp) * exposure; +}''' + +# shader = gpu.types.GPUShader(vertex_shader, fragment_shader) + + +def draw_callback_px(self, context): + nt = context.scene.world.node_tree.nodes + env_tex_node = nt.get(context.scene.sun_pos_properties.hdr_texture) + image = env_tex_node.image + + if self.area != context.area: + return + + if image.gl_load(): + raise Exception() + + bottom = 0 + top = context.area.height + right = context.area.width + + position = Vector((right, top)) / 2 + self.offset + scale = Vector((context.area.width, context.area.width / 2)) * self.scale + + shader = gpu.types.GPUShader(vertex_shader, fragment_shader) + + coords = ((-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)) + uv_coords = ((0, 0), (1, 0), (1, 1), (0, 1)) + batch = batch_for_shader(shader, 'TRI_FAN', + {"pos" : coords, + "texCoord" : uv_coords}) + + bgl.glActiveTexture(bgl.GL_TEXTURE0) + bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode) + + + with gpu.matrix.push_pop(): + gpu.matrix.translate(position) + gpu.matrix.scale(scale) + + shader.bind() + shader.uniform_int("image", 0) + shader.uniform_float("exposure", self.exposure) + batch.draw(shader) + + # Crosshair + # vertical + coords = ((self.mouse_position[0], bottom), (self.mouse_position[0], top)) + colors = ((1,)*4,)*2 + shader = gpu.shader.from_builtin('2D_FLAT_COLOR') + batch = batch_for_shader(shader, 'LINES', + {"pos": coords, "color": colors}) + shader.bind() + batch.draw(shader) + + # horizontal + if bottom <= self.mouse_position[1] <= top: + coords = ((0, self.mouse_position[1]), (context.area.width, self.mouse_position[1])) + batch = batch_for_shader(shader, 'LINES', + {"pos": coords, "color": colors}) + shader.bind() + batch.draw(shader) + + +class SUNPOS_OT_ShowHdr(bpy.types.Operator): + """Tooltip""" + bl_idname = "world.sunpos_show_hdr" + bl_label = "Sync Sun to Texture" + + exposure = 1.0 + + @classmethod + def poll(self, context): + sun_props = context.scene.sun_pos_properties + return sun_props.hdr_texture and sun_props.sun_object is not None + + def update(self, context, event): + sun_props = context.scene.sun_pos_properties + mouse_position_abs = Vector((event.mouse_x, event.mouse_y)) + + # Get current area + for area in context.screen.areas: + # Compare absolute mouse position to area bounds + if (area.x < mouse_position_abs.x < area.x + area.width + and area.y < mouse_position_abs.y < area.y + area.height): + self.area = area + if area.type == 'VIEW_3D': + # Redraw all areas + area.tag_redraw() + + if self.area.type == 'VIEW_3D': + self.top = self.area.height + self.right = self.area.width + + nt = context.scene.world.node_tree.nodes + env_tex = nt.get(sun_props.hdr_texture) + + # Mouse position relative to window + self.mouse_position = Vector((mouse_position_abs.x - self.area.x, + mouse_position_abs.y - self.area.y)) + + self.selected_point = (self.mouse_position - self.offset - Vector((self.right, self.top))/2) / self.scale + u = self.selected_point.x / self.area.width + 0.5 + v = (self.selected_point.y) / (self.area.width / 2) + 0.5 + + # Set elevation and azimuth from selected point + if env_tex.projection == 'EQUIRECTANGULAR': + el = v * pi - pi/2 + az = u * pi*2 - pi/2 + env_tex.texture_mapping.rotation.z + + # Clamp elevation + el = max(el, -pi/2) + el = min(el, pi/2) + + sun_props.hdr_elevation = el + sun_props.hdr_azimuth = az + elif env_tex.projection == 'MIRROR_BALL': + # Formula from intern/cycles/kernel/kernel_projection.h + # Point on sphere + dir = Vector() + + # Normalize to -1, 1 + dir.x = 2.0 * u - 1.0 + dir.z = 2.0 * v - 1.0 + + # Outside bounds + if (dir.x * dir.x + dir.z * dir.z > 1.0): + dir = Vector() + + else: + dir.y = -sqrt(max(1.0 - dir.x * dir.x - dir.z * dir.z, 0.0)) + + # Reflection + i = Vector((0.0, -1.0, 0.0)) + + dir = 2.0 * dir.dot(i) * dir - i + + # Convert vector to euler + el = asin(dir.z) + az = atan2(dir.x, dir.y) + env_tex.texture_mapping.rotation.z + sun_props.hdr_elevation = el + sun_props.hdr_azimuth = az + + else: + self.report({'ERROR'}, 'Unknown projection') + return {'CANCELLED'} + + def pan(self, context, event): + self.offset += Vector((event.mouse_region_x - self.mouse_prev_x, + event.mouse_region_y - self.mouse_prev_y)) + self.mouse_prev_x, self.mouse_prev_y = event.mouse_region_x, event.mouse_region_y + + def modal(self, context, event): + self.area.tag_redraw() + if event.type == 'MOUSEMOVE': + if self.is_panning: + self.pan(context, event) + self.update(context, event) + + # Confirm + elif event.type in {'LEFTMOUSE', 'RET'}: + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + for area in context.screen.areas: + area.tag_redraw() + # Bind the environment texture to the sun + context.scene.sun_pos_properties.bind_to_sun = True + context.workspace.status_text_set(None) + return {'FINISHED'} + + # Cancel + elif event.type in {'RIGHTMOUSE', 'ESC'}: + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + for area in context.screen.areas: + area.tag_redraw() + # Reset previous values + context.scene.sun_pos_properties.hdr_elevation = self.initial_elevation + context.scene.sun_pos_properties.hdr_azimuth = self.initial_azimuth + context.workspace.status_text_set(None) + return {'CANCELLED'} + + # Set exposure or zoom + elif event.type == 'WHEELUPMOUSE': + # Exposure + if event.ctrl: + self.exposure *= 1.1 + # Zoom + else: + self.scale *= 1.1 + self.offset -= (self.mouse_position - (Vector((self.right, self.top)) / 2 + self.offset)) / 10.0 + self.update(context, event) + elif event.type == 'WHEELDOWNMOUSE': + # Exposure + if event.ctrl: + self.exposure /= 1.1 + # Zoom + else: + self.scale /= 1.1 + self.offset += (self.mouse_position - (Vector((self.right, self.top)) / 2 + self.offset)) / 11.0 + self.update(context, event) + + # Toggle pan + elif event.type == 'MIDDLEMOUSE': + if event.value == 'PRESS': + self.mouse_prev_x, self.mouse_prev_y = event.mouse_region_x, event.mouse_region_y + self.is_panning = True + elif event.value == 'RELEASE': + self.is_panning = False + + else: + return {'PASS_THROUGH'} + + return {'RUNNING_MODAL'} + + def invoke(self, context, event): + self.is_panning = False + self.mouse_prev_x = 0.0 + self.mouse_prev_y = 0.0 + self.offset = Vector((0.0, 0.0)) + self.scale = 1.0 + + # Get at least one 3D View + area_3d = None + for a in context.screen.areas: + if a.type == 'VIEW_3D': + area_3d = a + break + + if area_3d is None: + self.report({'ERROR'}, 'Could not find 3D View') + return {'CANCELLED'} + + nt = context.scene.world.node_tree.nodes + env_tex_node = nt.get(context.scene.sun_pos_properties.hdr_texture) + if env_tex_node.type != "TEX_ENVIRONMENT": + self.report({'ERROR'}, 'Please select an Environment Texture node') + return {'CANCELLED'} + + self.area = context.area + + self.mouse_position = event.mouse_region_x, event.mouse_region_y + + self.initial_elevation = context.scene.sun_pos_properties.hdr_elevation + self.initial_azimuth = context.scene.sun_pos_properties.hdr_azimuth + + context.workspace.status_text_set("Enter/LMB: confirm, Esc/RMB: cancel, MMB: pan, mouse wheel: zoom, Ctrl + mouse wheel: set exposure") + + self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, + (self, context), 'WINDOW', 'POST_PIXEL') + context.window_manager.modal_handler_add(self) + + return {'RUNNING_MODAL'} |