1 ### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # -*- coding: utf-8 -*-
24 from gpu_extras
.batch
import batch_for_shader
25 from mathutils
import Vector
26 from math
import sqrt
, pi
, atan2
, asin
30 uniform mat4 ModelViewProjectionMatrix;
32 /* Keep in sync with intern/opencolorio/gpu_shader_display_transform_vertex.glsl */
35 out vec2 texCoord_interp;
39 gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f);
41 texCoord_interp = texCoord;
45 in vec2 texCoord_interp;
48 uniform sampler2D image;
49 uniform float exposure;
53 fragColor = texture(image, texCoord_interp) * exposure;
56 # shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
59 def draw_callback_px(self
, context
):
60 nt
= context
.scene
.world
.node_tree
.nodes
61 env_tex_node
= nt
.get(context
.scene
.sun_pos_properties
.hdr_texture
)
62 image
= env_tex_node
.image
64 if self
.area
!= context
.area
:
71 top
= context
.area
.height
72 right
= context
.area
.width
74 position
= Vector((right
, top
)) / 2 + self
.offset
75 scale
= Vector((context
.area
.width
, context
.area
.width
/ 2)) * self
.scale
77 shader
= gpu
.types
.GPUShader(vertex_shader
, fragment_shader
)
79 coords
= ((-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5))
80 uv_coords
= ((0, 0), (1, 0), (1, 1), (0, 1))
81 batch
= batch_for_shader(shader
, 'TRI_FAN',
83 "texCoord" : uv_coords
})
85 bgl
.glActiveTexture(bgl
.GL_TEXTURE0
)
86 bgl
.glBindTexture(bgl
.GL_TEXTURE_2D
, image
.bindcode
)
89 with gpu
.matrix
.push_pop():
90 gpu
.matrix
.translate(position
)
91 gpu
.matrix
.scale(scale
)
94 shader
.uniform_int("image", 0)
95 shader
.uniform_float("exposure", self
.exposure
)
100 coords
= ((self
.mouse_position
[0], bottom
), (self
.mouse_position
[0], top
))
102 shader
= gpu
.shader
.from_builtin('2D_FLAT_COLOR')
103 batch
= batch_for_shader(shader
, 'LINES',
104 {"pos": coords
, "color": colors
})
109 if bottom
<= self
.mouse_position
[1] <= top
:
110 coords
= ((0, self
.mouse_position
[1]), (context
.area
.width
, self
.mouse_position
[1]))
111 batch
= batch_for_shader(shader
, 'LINES',
112 {"pos": coords
, "color": colors
})
117 class SUNPOS_OT_ShowHdr(bpy
.types
.Operator
):
119 bl_idname
= "world.sunpos_show_hdr"
120 bl_label
= "Sync Sun to Texture"
125 def poll(self
, context
):
126 sun_props
= context
.scene
.sun_pos_properties
127 return sun_props
.hdr_texture
and sun_props
.sun_object
is not None
129 def update(self
, context
, event
):
130 sun_props
= context
.scene
.sun_pos_properties
131 mouse_position_abs
= Vector((event
.mouse_x
, event
.mouse_y
))
134 for area
in context
.screen
.areas
:
135 # Compare absolute mouse position to area bounds
136 if (area
.x
< mouse_position_abs
.x
< area
.x
+ area
.width
137 and area
.y
< mouse_position_abs
.y
< area
.y
+ area
.height
):
139 if area
.type == 'VIEW_3D':
143 if self
.area
.type == 'VIEW_3D':
144 self
.top
= self
.area
.height
145 self
.right
= self
.area
.width
147 nt
= context
.scene
.world
.node_tree
.nodes
148 env_tex
= nt
.get(sun_props
.hdr_texture
)
150 # Mouse position relative to window
151 self
.mouse_position
= Vector((mouse_position_abs
.x
- self
.area
.x
,
152 mouse_position_abs
.y
- self
.area
.y
))
154 self
.selected_point
= (self
.mouse_position
- self
.offset
- Vector((self
.right
, self
.top
))/2) / self
.scale
155 u
= self
.selected_point
.x
/ self
.area
.width
+ 0.5
156 v
= (self
.selected_point
.y
) / (self
.area
.width
/ 2) + 0.5
158 # Set elevation and azimuth from selected point
159 if env_tex
.projection
== 'EQUIRECTANGULAR':
161 az
= u
* pi
*2 - pi
/2 + env_tex
.texture_mapping
.rotation
.z
167 sun_props
.hdr_elevation
= el
168 sun_props
.hdr_azimuth
= az
169 elif env_tex
.projection
== 'MIRROR_BALL':
170 # Formula from intern/cycles/kernel/kernel_projection.h
175 dir.x
= 2.0 * u
- 1.0
176 dir.z
= 2.0 * v
- 1.0
179 if (dir.x
* dir.x
+ dir.z
* dir.z
> 1.0):
183 dir.y
= -sqrt(max(1.0 - dir.x
* dir.x
- dir.z
* dir.z
, 0.0))
186 i
= Vector((0.0, -1.0, 0.0))
188 dir = 2.0 * dir.dot(i
) * dir - i
190 # Convert vector to euler
192 az
= atan2(dir.x
, dir.y
) + env_tex
.texture_mapping
.rotation
.z
193 sun_props
.hdr_elevation
= el
194 sun_props
.hdr_azimuth
= az
197 self
.report({'ERROR'}, 'Unknown projection')
200 def pan(self
, context
, event
):
201 self
.offset
+= Vector((event
.mouse_region_x
- self
.mouse_prev_x
,
202 event
.mouse_region_y
- self
.mouse_prev_y
))
203 self
.mouse_prev_x
, self
.mouse_prev_y
= event
.mouse_region_x
, event
.mouse_region_y
205 def modal(self
, context
, event
):
206 self
.area
.tag_redraw()
207 if event
.type == 'MOUSEMOVE':
209 self
.pan(context
, event
)
210 self
.update(context
, event
)
213 elif event
.type in {'LEFTMOUSE', 'RET'}:
214 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
215 for area
in context
.screen
.areas
:
217 # Bind the environment texture to the sun
218 context
.scene
.sun_pos_properties
.bind_to_sun
= True
219 context
.workspace
.status_text_set(None)
223 elif event
.type in {'RIGHTMOUSE', 'ESC'}:
224 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
225 for area
in context
.screen
.areas
:
227 # Reset previous values
228 context
.scene
.sun_pos_properties
.hdr_elevation
= self
.initial_elevation
229 context
.scene
.sun_pos_properties
.hdr_azimuth
= self
.initial_azimuth
230 context
.workspace
.status_text_set(None)
233 # Set exposure or zoom
234 elif event
.type == 'WHEELUPMOUSE':
241 self
.offset
-= (self
.mouse_position
- (Vector((self
.right
, self
.top
)) / 2 + self
.offset
)) / 10.0
242 self
.update(context
, event
)
243 elif event
.type == 'WHEELDOWNMOUSE':
250 self
.offset
+= (self
.mouse_position
- (Vector((self
.right
, self
.top
)) / 2 + self
.offset
)) / 11.0
251 self
.update(context
, event
)
254 elif event
.type == 'MIDDLEMOUSE':
255 if event
.value
== 'PRESS':
256 self
.mouse_prev_x
, self
.mouse_prev_y
= event
.mouse_region_x
, event
.mouse_region_y
257 self
.is_panning
= True
258 elif event
.value
== 'RELEASE':
259 self
.is_panning
= False
262 return {'PASS_THROUGH'}
264 return {'RUNNING_MODAL'}
266 def invoke(self
, context
, event
):
267 self
.is_panning
= False
268 self
.mouse_prev_x
= 0.0
269 self
.mouse_prev_y
= 0.0
270 self
.offset
= Vector((0.0, 0.0))
273 # Get at least one 3D View
275 for a
in context
.screen
.areas
:
276 if a
.type == 'VIEW_3D':
281 self
.report({'ERROR'}, 'Could not find 3D View')
284 nt
= context
.scene
.world
.node_tree
.nodes
285 env_tex_node
= nt
.get(context
.scene
.sun_pos_properties
.hdr_texture
)
286 if env_tex_node
.type != "TEX_ENVIRONMENT":
287 self
.report({'ERROR'}, 'Please select an Environment Texture node')
290 self
.area
= context
.area
292 self
.mouse_position
= event
.mouse_region_x
, event
.mouse_region_y
294 self
.initial_elevation
= context
.scene
.sun_pos_properties
.hdr_elevation
295 self
.initial_azimuth
= context
.scene
.sun_pos_properties
.hdr_azimuth
297 context
.workspace
.status_text_set("Enter/LMB: confirm, Esc/RMB: cancel, MMB: pan, mouse wheel: zoom, Ctrl + mouse wheel: set exposure")
299 self
._handle
= bpy
.types
.SpaceView3D
.draw_handler_add(draw_callback_px
,
300 (self
, context
), 'WINDOW', 'POST_PIXEL')
301 context
.window_manager
.modal_handler_add(self
)
303 return {'RUNNING_MODAL'}