Import_3ds: Improved distance cue node setup
[blender-addons.git] / space_view3d_math_vis / draw.py
blobfe6eb30131046b8a8a330444a5c232f2c495823b
1 # SPDX-FileCopyrightText: 2010-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 import bpy
6 import blf
7 import gpu
8 from gpu_extras.batch import batch_for_shader
10 from . import utils
11 from mathutils import Vector
13 SpaceView3D = bpy.types.SpaceView3D
14 callback_handle = []
16 if not bpy.app.background:
17 single_color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
18 smooth_color_shader = gpu.shader.from_builtin('SMOOTH_COLOR')
19 else:
20 single_color_shader = None
21 smooth_color_shader = None
23 COLOR_POINT = (1.0, 0.0, 1.0, 1)
24 COLOR_LINE = (0.5, 0.5, 1, 1)
25 COLOR_LINE_ACTIVE = (1.0, 1.0, 0.5, 1)
26 COLOR_BOUNDING_BOX = (1.0, 1.0, 1.0, 1.0)
27 COLOR_BOUNDING_BOX_ACTIVE = (1.0, 0.5, 0.0, 1.0)
30 def tag_redraw_areas():
31 context = bpy.context
33 # Py can't access notifers
34 for window in context.window_manager.windows:
35 for area in window.screen.areas:
36 if area.type in ['VIEW_3D', 'PROPERTIES']:
37 area.tag_redraw()
40 def callback_enable():
41 if callback_handle:
42 return
44 handle_pixel = SpaceView3D.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
45 handle_view = SpaceView3D.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW')
46 callback_handle[:] = handle_pixel, handle_view
48 tag_redraw_areas()
51 def callback_disable():
52 if not callback_handle:
53 return
55 handle_pixel, handle_view = callback_handle
56 SpaceView3D.draw_handler_remove(handle_pixel, 'WINDOW')
57 SpaceView3D.draw_handler_remove(handle_view, 'WINDOW')
58 callback_handle[:] = []
60 tag_redraw_areas()
63 def draw_callback_px():
64 context = bpy.context
66 if context.window_manager.MathVisProp.name_hide:
67 return
69 font_id = 0
70 ui_scale = context.preferences.system.ui_scale
71 blf.size(font_id, round(12 * ui_scale))
73 data_matrix, data_quat, data_euler, data_vector, data_vector_array = utils.console_math_data()
74 if not data_matrix and not data_quat and not data_euler and not data_vector and not data_vector_array:
75 return
77 region = context.region
78 region3d = context.space_data.region_3d
80 region_mid_width = region.width / 2.0
81 region_mid_height = region.height / 2.0
83 perspective_matrix = region3d.perspective_matrix.copy()
85 def draw_text(text, vec, dx=3.0, dy=-4.0):
86 vec_4d = perspective_matrix @ vec.to_4d()
87 if vec_4d.w > 0.0:
88 x = region_mid_width + region_mid_width * (vec_4d.x / vec_4d.w)
89 y = region_mid_height + region_mid_height * (vec_4d.y / vec_4d.w)
91 blf.position(font_id, x + dx, y + dy, 0.0)
92 blf.draw(font_id, text)
94 if data_vector:
95 for key, vec in data_vector.items():
96 draw_text(key, vec)
98 if data_vector_array:
99 for key, vec in data_vector_array.items():
100 if vec:
101 draw_text(key, vec[0])
103 if data_matrix:
104 for key, mat in data_matrix.items():
105 loc = Vector((mat[0][3], mat[1][3], mat[2][3]))
106 draw_text(key, loc, dx=10, dy=-20)
108 offset_y = 20
109 if data_quat:
110 loc = context.scene.cursor.location.copy()
111 for key, mat in data_quat.items():
112 draw_text(key, loc, dy=-offset_y)
113 offset_y += 20
115 if data_euler:
116 loc = context.scene.cursor.location.copy()
117 for key, mat in data_euler.items():
118 draw_text(key, loc, dy=-offset_y)
119 offset_y += 20
122 def draw_callback_view():
123 settings = bpy.context.window_manager.MathVisProp
124 prop_states = bpy.context.window_manager.MathVisStatePropList
126 scale = settings.bbox_scale
127 with_bounding_box = not settings.bbox_hide
129 if settings.in_front:
130 gpu.state.depth_test_set('ALWAYS')
131 else:
132 gpu.state.depth_test_set('LESS')
134 data_matrix, data_quat, data_euler, data_vector, data_vector_array = utils.console_math_data()
135 if settings.index in range(0,len(prop_states)):
136 active_index = settings.index
137 active_key = prop_states[active_index].name
138 else:
139 active_index = -1
140 active_key = None
142 if data_vector:
143 coords = [tuple(vec.to_3d()) for vec in data_vector.values()]
144 draw_points(coords)
146 if data_vector_array:
147 for key, line in data_vector_array.items():
148 coords = [tuple(vec.to_3d()) for vec in line]
149 if key == active_key:
150 draw_line(coords, COLOR_LINE_ACTIVE)
151 else:
152 draw_line(coords, COLOR_LINE)
154 if data_matrix:
155 draw_matrices(data_matrix, scale, with_bounding_box, active_key)
157 if data_euler or data_quat:
158 cursor = bpy.context.scene.cursor.location.copy()
159 derived_matrices = dict()
160 for key, quat in data_quat.items():
161 matrix = quat.to_matrix().to_4x4()
162 matrix.translation = cursor
163 derived_matrices[key] = matrix
164 for key, eul in data_euler.items():
165 matrix = eul.to_matrix().to_4x4()
166 matrix.translation = cursor
167 derived_matrices[key] = matrix
168 draw_matrices(derived_matrices, scale, with_bounding_box, active_key)
171 def draw_points(points):
172 batch = batch_from_points(points, "POINTS")
173 single_color_shader.bind()
174 single_color_shader.uniform_float("color", COLOR_POINT)
175 batch.draw(single_color_shader)
178 def draw_line(points, color):
179 batch = batch_from_points(points, "LINE_STRIP")
180 single_color_shader.bind()
181 single_color_shader.uniform_float("color", color)
182 batch.draw(single_color_shader)
185 def batch_from_points(points, type):
186 return batch_for_shader(single_color_shader, type, {"pos": points})
189 def draw_matrices(matrices, scale, with_bounding_box, active_key):
190 x_p = Vector((scale, 0.0, 0.0))
191 x_n = Vector((-scale, 0.0, 0.0))
192 y_p = Vector((0.0, scale, 0.0))
193 y_n = Vector((0.0, -scale, 0.0))
194 z_p = Vector((0.0, 0.0, scale))
195 z_n = Vector((0.0, 0.0, -scale))
197 red_dark = (0.2, 0.0, 0.0, 1.0)
198 red_light = (1.0, 0.2, 0.2, 1.0)
199 green_dark = (0.0, 0.2, 0.0, 1.0)
200 green_light = (0.2, 1.0, 0.2, 1.0)
201 blue_dark = (0.0, 0.0, 0.2, 1.0)
202 blue_light = (0.4, 0.4, 1.0, 1.0)
204 coords = []
205 colors = []
206 selected = []
207 active = []
208 for key, matrix in matrices.items():
209 coords.append(matrix @ x_n)
210 coords.append(matrix @ x_p)
211 colors.extend((red_dark, red_light))
212 coords.append(matrix @ y_n)
213 coords.append(matrix @ y_p)
214 colors.extend((green_dark, green_light))
215 coords.append(matrix @ z_n)
216 coords.append(matrix @ z_p)
217 colors.extend((blue_dark, blue_light))
218 if key == active_key:
219 active.append(matrix)
220 else:
221 selected.append(matrix)
223 batch = batch_for_shader(smooth_color_shader, "LINES", {
224 "pos": coords,
225 "color": colors
227 batch.draw(smooth_color_shader)
229 if with_bounding_box:
230 if selected:
231 draw_bounding_boxes(selected, scale, COLOR_BOUNDING_BOX)
232 if active:
233 draw_bounding_boxes(active, scale, COLOR_BOUNDING_BOX_ACTIVE)
236 def draw_bounding_boxes(matrices, scale, color):
237 boundbox_points = []
238 for x in (-scale, scale):
239 for y in (-scale, scale):
240 for z in (-scale, scale):
241 boundbox_points.append(Vector((x, y, z)))
243 boundbox_lines = [
244 (0, 1), (1, 3), (3, 2), (2, 0), (0, 4), (4, 5),
245 (5, 7), (7, 6), (6, 4), (1, 5), (2, 6), (3, 7)
248 points = []
249 for matrix in matrices:
250 for v1, v2 in boundbox_lines:
251 points.append(matrix @ boundbox_points[v1])
252 points.append(matrix @ boundbox_points[v2])
254 batch = batch_from_points(points, "LINES")
256 single_color_shader.bind()
257 single_color_shader.uniform_float("color", color)
258 batch.draw(single_color_shader)