mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-16 21:42:19 +00:00
WIP gizmo for parametreic extrusion, see #1324
This commit is contained in:
@@ -6,7 +6,7 @@ bpy = sys.modules.get("bpy")
|
|||||||
if bpy is not None:
|
if bpy is not None:
|
||||||
import bpy
|
import bpy
|
||||||
import importlib
|
import importlib
|
||||||
from . import handler, ui, prop, operator
|
from . import handler, ui, prop, operator, gizmos
|
||||||
|
|
||||||
modules = {
|
modules = {
|
||||||
"project": None,
|
"project": None,
|
||||||
@@ -145,6 +145,11 @@ if bpy is not None:
|
|||||||
ui.BIM_UL_drawinglist,
|
ui.BIM_UL_drawinglist,
|
||||||
ui.BIM_UL_topics,
|
ui.BIM_UL_topics,
|
||||||
ui.BIM_ADDON_preferences,
|
ui.BIM_ADDON_preferences,
|
||||||
|
gizmos.UglyDotGizmo,
|
||||||
|
gizmos.DotGizmo,
|
||||||
|
gizmos.DimensionLabelGizmo,
|
||||||
|
gizmos.ExtrusionGuidesGizmo,
|
||||||
|
gizmos.ExtrusionWidget
|
||||||
]
|
]
|
||||||
|
|
||||||
for module in modules.values():
|
for module in modules.values():
|
||||||
|
|||||||
@@ -0,0 +1,400 @@
|
|||||||
|
import bpy
|
||||||
|
import blf
|
||||||
|
from bpy import types
|
||||||
|
import math
|
||||||
|
from mathutils import Vector, Matrix
|
||||||
|
from mathutils import geometry
|
||||||
|
import gpu, bgl
|
||||||
|
from bpy_extras import view3d_utils
|
||||||
|
|
||||||
|
|
||||||
|
from .shaders import DotsGizmoShader, ExtrusionGuidesShader, BaseLinesShader
|
||||||
|
|
||||||
|
|
||||||
|
"""Gizmos under the hood
|
||||||
|
|
||||||
|
## Transforms:
|
||||||
|
|
||||||
|
source/blender/windowmanager/gizmo/WM_gizmo_types.h
|
||||||
|
matrix_basis -- "Transformation of this gizmo." = placement in scene
|
||||||
|
matrix_offset -- "Custom offset from origin." = local transforms according to state/value
|
||||||
|
matrix_space -- "The space this gizmo is being modified in." used by some gizmos for undefined purposes
|
||||||
|
matrix_world -- final matrix, scaled according to viewport zoom and custom scale_basis
|
||||||
|
|
||||||
|
source/blender/windowmanager/gizmo/intern/wm_gizmo.c:WM_gizmo_calc_matrix_final_params
|
||||||
|
final = space @ (autoscale * (basis @ offset))
|
||||||
|
final = space @ (basis @ offset) -- if gizmo.use_draw_scale == False
|
||||||
|
final = space @ ((autoscale * basis) @ offset) -- if gizmo.use_draw_offset_scale
|
||||||
|
|
||||||
|
source/blender/windowmanager/gizmo/intern/wm_gizmo.c:wm_gizmo_calculate_scale
|
||||||
|
autoscale = gizmo.scale_basis * magic(preferences, matrix_space, matrix_basis, context.region_data)
|
||||||
|
magic -- making 1.0 to match preferences.view.gizmo_size pixels (75 by default)
|
||||||
|
|
||||||
|
|
||||||
|
## Selection
|
||||||
|
|
||||||
|
select_id -- apparently, id of a selectable part
|
||||||
|
test_select -- expected to return id of selection, doesn't seem to work
|
||||||
|
draw_select -- fake-draw of selection geometry for gpu-side cursor tracking
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# some geometries for Gizmo.custom_shape shaders
|
||||||
|
|
||||||
|
CUBE = (
|
||||||
|
(+1, +1, +1), (-1, +1, +1), (+1, -1, +1), # top
|
||||||
|
(+1, -1, +1), (-1, +1, +1), (-1, -1, +1),
|
||||||
|
(+1, +1, +1), (+1, -1, +1), (+1, +1, -1), # right
|
||||||
|
(+1, +1, -1), (+1, -1, +1), (+1, -1, -1),
|
||||||
|
(+1, +1, +1), (+1, +1, -1), (-1, +1, +1), # back
|
||||||
|
(-1, +1, +1), (+1, +1, -1), (-1, +1, -1),
|
||||||
|
(-1, -1, -1), (-1, +1, -1), (+1, -1, -1), # bot
|
||||||
|
(+1, -1, -1), (-1, +1, -1), (+1, +1, -1),
|
||||||
|
(-1, -1, -1), (-1, -1, +1), (-1, +1, -1), # left
|
||||||
|
(-1, +1, -1), (-1, -1, +1), (-1, +1, +1),
|
||||||
|
(-1, -1, -1), (+1, -1, -1), (-1, -1, +1), # front
|
||||||
|
(-1, -1, +1), (+1, -1, -1), (+1, -1, +1)
|
||||||
|
)
|
||||||
|
|
||||||
|
DISC = (
|
||||||
|
(0.0, 0.0, 0.0), (1.0, 0.0, 0), (0.8660254037844387, 0.49999999999999994, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0.8660254037844387, 0.49999999999999994, 0), (0.5000000000000001, 0.8660254037844386, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0.5000000000000001, 0.8660254037844386, 0), (6.123233995736766e-17, 1.0, 0),
|
||||||
|
(0.0, 0.0, 0.0), (6.123233995736766e-17, 1.0, 0), (-0.4999999999999998, 0.8660254037844387, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-0.4999999999999998, 0.8660254037844387, 0), (-0.8660254037844385, 0.5000000000000003, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-0.8660254037844385, 0.5000000000000003, 0), (-1.0, 1.2246467991473532e-16, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-1.0, 1.2246467991473532e-16, 0), (-0.8660254037844388, -0.4999999999999997, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-0.8660254037844388, -0.4999999999999997, 0), (-0.5000000000000004, -0.8660254037844384, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-0.5000000000000004, -0.8660254037844384, 0), (-1.8369701987210297e-16, -1.0, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-1.8369701987210297e-16, -1.0, 0), (0.49999999999999933, -0.866025403784439, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0.49999999999999933, -0.866025403784439, 0), (0.8660254037844384, -0.5000000000000004, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0.8660254037844384, -0.5000000000000004, 0), (1.0, 0.0, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
X3DISC = (
|
||||||
|
(0.0, 0.0, 0.0), (1.0, 0.0, 0), (0.8660254037844387, 0.49999999999999994, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0.8660254037844387, 0.49999999999999994, 0), (0.5000000000000001, 0.8660254037844386, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0.5000000000000001, 0.8660254037844386, 0), (6.123233995736766e-17, 1.0, 0),
|
||||||
|
(0.0, 0.0, 0.0), (6.123233995736766e-17, 1.0, 0), (-0.4999999999999998, 0.8660254037844387, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-0.4999999999999998, 0.8660254037844387, 0), (-0.8660254037844385, 0.5000000000000003, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-0.8660254037844385, 0.5000000000000003, 0), (-1.0, 1.2246467991473532e-16, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-1.0, 1.2246467991473532e-16, 0), (-0.8660254037844388, -0.4999999999999997, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-0.8660254037844388, -0.4999999999999997, 0), (-0.5000000000000004, -0.8660254037844384, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-0.5000000000000004, -0.8660254037844384, 0), (-1.8369701987210297e-16, -1.0, 0),
|
||||||
|
(0.0, 0.0, 0.0), (-1.8369701987210297e-16, -1.0, 0), (0.49999999999999933, -0.866025403784439, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0.49999999999999933, -0.866025403784439, 0), (0.8660254037844384, -0.5000000000000004, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0.8660254037844384, -0.5000000000000004, 0), (1.0, 0.0, 0),
|
||||||
|
(0.0, 0.0, 0.0), (0, 1.0, 0.0), (0, 0.8660254037844387, 0.49999999999999994),
|
||||||
|
(0.0, 0.0, 0.0), (0, 0.8660254037844387, 0.49999999999999994), (0, 0.5000000000000001, 0.8660254037844386),
|
||||||
|
(0.0, 0.0, 0.0), (0, 0.5000000000000001, 0.8660254037844386), (0, 6.123233995736766e-17, 1.0),
|
||||||
|
(0.0, 0.0, 0.0), (0, 6.123233995736766e-17, 1.0), (0, -0.4999999999999998, 0.8660254037844387),
|
||||||
|
(0.0, 0.0, 0.0), (0, -0.4999999999999998, 0.8660254037844387), (0, -0.8660254037844385, 0.5000000000000003),
|
||||||
|
(0.0, 0.0, 0.0), (0, -0.8660254037844385, 0.5000000000000003), (0, -1.0, 1.2246467991473532e-16),
|
||||||
|
(0.0, 0.0, 0.0), (0, -1.0, 1.2246467991473532e-16), (0, -0.8660254037844388, -0.4999999999999997),
|
||||||
|
(0.0, 0.0, 0.0), (0, -0.8660254037844388, -0.4999999999999997), (0, -0.5000000000000004, -0.8660254037844384),
|
||||||
|
(0.0, 0.0, 0.0), (0, -0.5000000000000004, -0.8660254037844384), (0, -1.8369701987210297e-16, -1.0),
|
||||||
|
(0.0, 0.0, 0.0), (0, -1.8369701987210297e-16, -1.0), (0, 0.49999999999999933, -0.866025403784439),
|
||||||
|
(0.0, 0.0, 0.0), (0, 0.49999999999999933, -0.866025403784439), (0, 0.8660254037844384, -0.5000000000000004),
|
||||||
|
(0.0, 0.0, 0.0), (0, 0.8660254037844384, -0.5000000000000004), (0, 1.0, 0.0),
|
||||||
|
(0.0, 0.0, 0.0), (0.0, 0, 1.0), (0.49999999999999994, 0, 0.8660254037844387),
|
||||||
|
(0.0, 0.0, 0.0), (0.49999999999999994, 0, 0.8660254037844387), (0.8660254037844386, 0, 0.5000000000000001),
|
||||||
|
(0.0, 0.0, 0.0), (0.8660254037844386, 0, 0.5000000000000001), (1.0, 0, 6.123233995736766e-17),
|
||||||
|
(0.0, 0.0, 0.0), (1.0, 0, 6.123233995736766e-17), (0.8660254037844387, 0, -0.4999999999999998),
|
||||||
|
(0.0, 0.0, 0.0), (0.8660254037844387, 0, -0.4999999999999998), (0.5000000000000003, 0, -0.8660254037844385),
|
||||||
|
(0.0, 0.0, 0.0), (0.5000000000000003, 0, -0.8660254037844385), (1.2246467991473532e-16, 0, -1.0),
|
||||||
|
(0.0, 0.0, 0.0), (1.2246467991473532e-16, 0, -1.0), (-0.4999999999999997, 0, -0.8660254037844388),
|
||||||
|
(0.0, 0.0, 0.0), (-0.4999999999999997, 0, -0.8660254037844388), (-0.8660254037844384, 0, -0.5000000000000004),
|
||||||
|
(0.0, 0.0, 0.0), (-0.8660254037844384, 0, -0.5000000000000004), (-1.0, 0, -1.8369701987210297e-16),
|
||||||
|
(0.0, 0.0, 0.0), (-1.0, 0, -1.8369701987210297e-16), (-0.866025403784439, 0, 0.49999999999999933),
|
||||||
|
(0.0, 0.0, 0.0), (-0.866025403784439, 0, 0.49999999999999933), (-0.5000000000000004, 0, 0.8660254037844384),
|
||||||
|
(0.0, 0.0, 0.0), (-0.5000000000000004, 0, 0.8660254037844384), (0.0, 0, 1.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomGizmo():
|
||||||
|
# FIXME: highliting/selection doesnt work
|
||||||
|
def draw_very_custom_shape(self, ctx, custom_shape, select_id=None):
|
||||||
|
# similar to draw_custom_shape
|
||||||
|
shape, batch, shader = custom_shape
|
||||||
|
|
||||||
|
shader.bind()
|
||||||
|
|
||||||
|
if select_id is not None:
|
||||||
|
gpu.select.load_id(select_id)
|
||||||
|
else:
|
||||||
|
if self.is_highlight:
|
||||||
|
color = (*self.color_highlight, self.alpha_highlight)
|
||||||
|
else:
|
||||||
|
color = (*self.color, self.alpha)
|
||||||
|
shader.uniform_float('color', color)
|
||||||
|
shape.glenable()
|
||||||
|
|
||||||
|
shape.uniform_region(ctx)
|
||||||
|
# shader.uniform_float('modelMatrix', self.matrix_world)
|
||||||
|
with gpu.matrix.push_pop():
|
||||||
|
gpu.matrix.multiply_matrix(self.matrix_world)
|
||||||
|
batch.draw()
|
||||||
|
|
||||||
|
bgl.glDisable(bgl.GL_BLEND)
|
||||||
|
|
||||||
|
|
||||||
|
class OffsetHandle():
|
||||||
|
"""Handling mouse to offset gizmo from base along Z axis"""
|
||||||
|
# FIXME: works a bit weird for rotated objects
|
||||||
|
|
||||||
|
def invoke(self, ctx, event):
|
||||||
|
self.init_value = self.target_get_value('offset') / self.scale_value
|
||||||
|
coordz = self.project_mouse(ctx, event)
|
||||||
|
if coordz is None:
|
||||||
|
return {'CANCELLED'}
|
||||||
|
self.init_coordz = coordz
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def modal(self, ctx, event, tweak):
|
||||||
|
coordz = self.project_mouse(ctx, event)
|
||||||
|
if coordz is None:
|
||||||
|
return {'CANCELLED'}
|
||||||
|
delta = coordz - self.init_coordz
|
||||||
|
if 'PRECISE' in tweak:
|
||||||
|
delta /= 10.0
|
||||||
|
value = max(0, self.init_value + delta)
|
||||||
|
value *= self.scale_value
|
||||||
|
# ctx.area.header_text_set(f"coords: {self.init_coordz} - {coordz}, delta: {delta}, value: {value}")
|
||||||
|
ctx.area.header_text_set(f"Depth: {value}")
|
||||||
|
self.target_set_value('offset', value)
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
def project_mouse(self, ctx, event):
|
||||||
|
"""Projecting mouse coords to local axis Z"""
|
||||||
|
# logic from source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c:gizmo_arrow_modal
|
||||||
|
|
||||||
|
mouse = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||||
|
region = ctx.region
|
||||||
|
region3d = ctx.region_data
|
||||||
|
ray_orig = view3d_utils.region_2d_to_origin_3d(region, region3d, mouse)
|
||||||
|
ray_norm = view3d_utils.region_2d_to_vector_3d(region, region3d, mouse)
|
||||||
|
|
||||||
|
# 'arrow' origin and direction
|
||||||
|
base = Vector((0, 0, 0))
|
||||||
|
axis = Vector((0, 0, 1))
|
||||||
|
|
||||||
|
# projecttion of the arrow to a plane, perpendicular to view ray
|
||||||
|
axis_proj = axis - ray_norm * axis.dot(ray_norm)
|
||||||
|
|
||||||
|
# intersection of the axis with the plane through view origin perpendicular to the arrow projection
|
||||||
|
coords = geometry.intersect_line_plane(base, axis, ray_orig, axis_proj)
|
||||||
|
|
||||||
|
return coords.z
|
||||||
|
|
||||||
|
def exit(self, ctx, cancel):
|
||||||
|
if cancel:
|
||||||
|
self.target_set_value('offset', self.init_value)
|
||||||
|
else:
|
||||||
|
self.group.update(ctx)
|
||||||
|
|
||||||
|
|
||||||
|
class UglyDotGizmo(OffsetHandle, types.Gizmo):
|
||||||
|
"""three orthogonal circles"""
|
||||||
|
bl_idname = "BIM_GT_uglydot_3d"
|
||||||
|
bl_target_properties = (
|
||||||
|
{'id': 'offset', 'type': 'FLOAT', 'array_length': 1},
|
||||||
|
)
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
'scale_value',
|
||||||
|
'custom_shape',
|
||||||
|
'init_value',
|
||||||
|
'init_coordz',
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.custom_shape = self.new_custom_shape(type='TRIS', verts=X3DISC)
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
offset = self.target_get_value('offset') / self.scale_value
|
||||||
|
self.matrix_offset.col[3][2] = offset # z-shift
|
||||||
|
|
||||||
|
def draw(self, ctx):
|
||||||
|
self.refresh()
|
||||||
|
self.draw_custom_shape(self.custom_shape)
|
||||||
|
|
||||||
|
def draw_select(self, ctx, select_id):
|
||||||
|
self.refresh()
|
||||||
|
self.draw_custom_shape(self.custom_shape, select_id=select_id)
|
||||||
|
|
||||||
|
|
||||||
|
class DotGizmo(CustomGizmo, OffsetHandle, types.Gizmo):
|
||||||
|
"""Single dot viewport-aligned"""
|
||||||
|
# FIXME: make it selectable
|
||||||
|
bl_idname = "BIM_GT_dot_2d"
|
||||||
|
bl_target_properties = (
|
||||||
|
{'id': 'offset', 'type': 'FLOAT', 'array_length': 1},
|
||||||
|
)
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
'scale_value',
|
||||||
|
'custom_shape',
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
shader = DotsGizmoShader()
|
||||||
|
self.custom_shape = shader, shader.batch(pos=((0, 0, 0),)), shader.prog
|
||||||
|
self.use_draw_scale = False
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
offset = self.target_get_value('offset') / self.scale_value
|
||||||
|
self.matrix_offset.col[3][2] = offset # z-shifted
|
||||||
|
|
||||||
|
def draw(self, ctx):
|
||||||
|
self.refresh()
|
||||||
|
self.draw_very_custom_shape(ctx, self.custom_shape)
|
||||||
|
|
||||||
|
def draw_select(self, ctx, select_id):
|
||||||
|
self.refresh()
|
||||||
|
self.draw_very_custom_shape(ctx, self.custom_shape, select_id=select_id)
|
||||||
|
|
||||||
|
# doesn't get called
|
||||||
|
# def test_select(self, ctx, location):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|
||||||
|
class ExtrusionGuidesGizmo(CustomGizmo, types.Gizmo):
|
||||||
|
"""Extrusion guides
|
||||||
|
|
||||||
|
Noninteractive gizmo to indicate extrusion depth and planes.
|
||||||
|
Draws main segment and orthogonal cross at endpoints.
|
||||||
|
"""
|
||||||
|
bl_idname = "BIM_GT_extrusion_guides"
|
||||||
|
bl_target_properties = (
|
||||||
|
{'id': 'depth', 'type': 'FLOAT', 'array_length': 1},
|
||||||
|
)
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
'scale_value',
|
||||||
|
'custom_shape'
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
shader = ExtrusionGuidesShader()
|
||||||
|
self.custom_shape = shader, shader.batch(pos=((0, 0, 0), (0, 0, 1))), shader.prog
|
||||||
|
self.use_draw_scale = False
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
depth = self.target_get_value('depth') / self.scale_value
|
||||||
|
self.matrix_offset.col[2][2] = depth # z-scaled
|
||||||
|
|
||||||
|
def draw(self, ctx):
|
||||||
|
self.refresh()
|
||||||
|
self.draw_very_custom_shape(ctx, self.custom_shape)
|
||||||
|
|
||||||
|
|
||||||
|
class DimensionLabelGizmo(types.Gizmo):
|
||||||
|
"""Text label for a dimension"""
|
||||||
|
# does not work properly, fonts are totally screwed up
|
||||||
|
|
||||||
|
bl_idname = "BIM_GT_dimension_label"
|
||||||
|
bl_target_properties = (
|
||||||
|
{'id': 'value', 'type': 'FLOAT', 'array_length': 1},
|
||||||
|
)
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
'text_label'
|
||||||
|
)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def refresh(self, ctx):
|
||||||
|
value = self.target_get_value('value')
|
||||||
|
self.matrix_offset.col[3][2] = value * .5
|
||||||
|
unit_system = ctx.scene.unit_settings.system
|
||||||
|
self.text_label = bpy.utils.units.to_string(unit_system, 'LENGTH', value, 3, split_unit=False)
|
||||||
|
|
||||||
|
def draw(self, ctx):
|
||||||
|
self.refresh(ctx)
|
||||||
|
self.draw_text(ctx)
|
||||||
|
|
||||||
|
def draw_text(self, ctx):
|
||||||
|
font_id = 0
|
||||||
|
font_size = 16
|
||||||
|
dpi = ctx.preferences.system.dpi
|
||||||
|
|
||||||
|
# pos = self.matrix_world @ Vector((0, 0, 0, 1))
|
||||||
|
# pos = Vector((0, 0, 0.5))
|
||||||
|
# region = ctx.region
|
||||||
|
# region3d = ctx.region_data
|
||||||
|
# pos = view3d_utils.location_3d_to_region_2d(region, region3d, pos)
|
||||||
|
|
||||||
|
# text = self.text_label
|
||||||
|
|
||||||
|
blf.size(font_id, font_size, dpi)
|
||||||
|
blf.position(font_id, 0, 0, 0)
|
||||||
|
blf.color(font_id, *self.color, self.alpha)
|
||||||
|
blf.draw(font_id, "ABC")
|
||||||
|
|
||||||
|
|
||||||
|
class ExtrusionWidget(types.GizmoGroup):
|
||||||
|
bl_idname = "bim.extrusion_widget"
|
||||||
|
bl_label = "Extrusion Gizmos"
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'WINDOW'
|
||||||
|
bl_options = {'3D', 'PERSISTENT'}
|
||||||
|
|
||||||
|
# FIXME: use proper scale from ifc value to blender units
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, ctx):
|
||||||
|
obj = ctx.object
|
||||||
|
return (obj and obj.type == 'MESH'
|
||||||
|
and obj.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth') is not None)
|
||||||
|
|
||||||
|
def setup(self, ctx):
|
||||||
|
target = ctx.object
|
||||||
|
prop = target.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth')
|
||||||
|
|
||||||
|
basis = target.matrix_world.normalized()
|
||||||
|
theme = ctx.preferences.themes[0].user_interface
|
||||||
|
|
||||||
|
gz = self.handle = self.gizmos.new('BIM_GT_uglydot_3d')
|
||||||
|
gz.matrix_basis = basis
|
||||||
|
gz.scale_basis = 0.1
|
||||||
|
gz.color = gz.color_highlight = tuple(theme.gizmo_primary)
|
||||||
|
gz.alpha = 0.5
|
||||||
|
gz.alpha_highlight = 1.0
|
||||||
|
gz.use_draw_modal = True
|
||||||
|
gz.target_set_prop('offset', prop, 'value')
|
||||||
|
gz.scale_value = 1000
|
||||||
|
|
||||||
|
gz = self.guides = self.gizmos.new('BIM_GT_extrusion_guides')
|
||||||
|
gz.matrix_basis = basis
|
||||||
|
gz.color = gz.color_highlight = tuple(theme.gizmo_secondary)
|
||||||
|
gz.alpha = gz.alpha_highlight = 0.5
|
||||||
|
gz.use_draw_modal = True
|
||||||
|
gz.target_set_prop('depth', prop, 'value')
|
||||||
|
gz.scale_value = 1000
|
||||||
|
|
||||||
|
# gz = self.label = self.gizmos.new('GIZMO_GT_dimension_label')
|
||||||
|
# gz.matrix_basis = basis
|
||||||
|
# gz.color = tuple(theme.gizmo_secondary)
|
||||||
|
# gz.alpha = 0.5
|
||||||
|
# gz.use_draw_modal = True
|
||||||
|
# gz.target_set_prop('value', target.demo, 'depth')
|
||||||
|
|
||||||
|
def refresh(self, ctx):
|
||||||
|
"""updating gizmos"""
|
||||||
|
target = ctx.object
|
||||||
|
basis = target.matrix_world.normalized()
|
||||||
|
self.handle.matrix_basis = basis
|
||||||
|
self.guides.matrix_basis = basis
|
||||||
|
|
||||||
|
def update(self, ctx):
|
||||||
|
"""updating object"""
|
||||||
|
bpy.ops.bim.update_parametric_representation()
|
||||||
|
# parameters disappear after update
|
||||||
|
# need to retrieve and rebind them again
|
||||||
|
bpy.ops.bim.get_representation_ifc_parameters()
|
||||||
|
target = ctx.object
|
||||||
|
prop = target.data.BIMMeshProperties.ifc_parameters.get('IfcExtrudedAreaSolid/Depth')
|
||||||
|
self.handle.target_set_prop('offset', prop, 'value')
|
||||||
|
self.guides.target_set_prop('depth', prop, 'value')
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
from mathutils import Matrix
|
||||||
|
import bgl
|
||||||
|
from gpu.types import GPUShader
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
|
|
||||||
|
|
||||||
|
class BaseShader():
|
||||||
|
"""Wrapepr for GPUShader
|
||||||
|
To use for viewport decorations with geometry generated on GPU side.
|
||||||
|
|
||||||
|
The Geometry shader works in clipping coords (aftre projecting before division and window scaling).
|
||||||
|
To make window-scale geometry, vectors should be calculated in window space and than back-projected to clipping spae.
|
||||||
|
Provide `winSize` uniform vector with halfsize of window, and use W2C and C2W macros.
|
||||||
|
|
||||||
|
Replace glsl code in derived classes.
|
||||||
|
Beware of unused attributes and uniforms: glsl compiler will optimize them out and blender fail with an exception.
|
||||||
|
|
||||||
|
|
||||||
|
Vertex topology attribute to use with line segments:
|
||||||
|
- 0 = inner
|
||||||
|
- 1 = starting
|
||||||
|
- 2 = ending
|
||||||
|
- 3 = isolated
|
||||||
|
"""
|
||||||
|
|
||||||
|
TYPE = None # should be LINES|POINTS|etc
|
||||||
|
|
||||||
|
DEF_GLSL = """
|
||||||
|
#define PI 3.141592653589793
|
||||||
|
"""
|
||||||
|
|
||||||
|
VERT_GLSL = """
|
||||||
|
uniform mat4 viewMatrix;
|
||||||
|
uniform mat4 modelMatrix;
|
||||||
|
|
||||||
|
in vec3 pos;
|
||||||
|
in uint topology;
|
||||||
|
out vec4 gl_Position;
|
||||||
|
out uint topo;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = viewMatrix * modelMatrix * vec4(pos, 1.0);
|
||||||
|
topo = topology;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# prepended to geom_glsl
|
||||||
|
LIB_GLSL = """
|
||||||
|
uniform vec2 winSize;
|
||||||
|
// convert camera to window
|
||||||
|
#define C2W(v) vec4(v.x * winSize.x / v.w, v.y * winSize.y / v.w, v.z / v.w, 1)
|
||||||
|
// convert window to camera
|
||||||
|
#define W2C(v) vec4(v.x * v.w / winSize.x, v.y * v.w / winSize.y, v.z * v.w, 1)
|
||||||
|
|
||||||
|
void emitSegment(vec4 p0, vec4 p1) {
|
||||||
|
gl_Position = p0;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p1;
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitTriangle(vec4 p1, vec4 p2, vec4 p3) {
|
||||||
|
gl_Position = p1;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p2;
|
||||||
|
EmitVertex();
|
||||||
|
gl_Position = p3;
|
||||||
|
EmitVertex();
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
GEOM_GLSL = """
|
||||||
|
"""
|
||||||
|
|
||||||
|
FRAG_GLSL = """
|
||||||
|
uniform vec4 color;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
fragColor = color;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# NB: libcode arg doesn't work
|
||||||
|
self.prog = GPUShader(vertexcode=self.VERT_GLSL,
|
||||||
|
fragcode=self.FRAG_GLSL,
|
||||||
|
geocode=self.LIB_GLSL + self.GEOM_GLSL,
|
||||||
|
defines=self.DEF_GLSL)
|
||||||
|
|
||||||
|
def batch(self, indices=None, **data):
|
||||||
|
"""Returns automatic GPUBatch filled with provided parameters
|
||||||
|
"""
|
||||||
|
batch = batch_for_shader(self.prog, self.TYPE, data, indices=indices)
|
||||||
|
batch.program_set(self.prog)
|
||||||
|
return batch
|
||||||
|
|
||||||
|
def bind(self):
|
||||||
|
self.prog.bind()
|
||||||
|
|
||||||
|
def glenable(self):
|
||||||
|
bgl.glEnable(bgl.GL_BLEND)
|
||||||
|
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
|
||||||
|
bgl.glBlendEquation(bgl.GL_FUNC_ADD)
|
||||||
|
|
||||||
|
# bgl.glEnable(bgl.GL_DEPTH_TEST)
|
||||||
|
# bgl.glDepthFunc(bgl.GL_LEQUAL)
|
||||||
|
# bgl.glDepthMask(True)
|
||||||
|
|
||||||
|
def uniform_region(self, ctx):
|
||||||
|
region = ctx.region
|
||||||
|
region3d = ctx.region_data
|
||||||
|
try:
|
||||||
|
self.prog.uniform_float('viewMatrix', region3d.perspective_matrix)
|
||||||
|
except ValueError: # unused uniform
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.prog.uniform_float('winSize', (region.width / 2, region.height / 2))
|
||||||
|
except ValueError: # unused uniform
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BaseLinesShader(BaseShader):
|
||||||
|
"""Draws line segments with gaps around vertices at endpoints
|
||||||
|
"""
|
||||||
|
TYPE = 'LINES'
|
||||||
|
|
||||||
|
DEF_GLSL = BaseShader.DEF_GLSL + """
|
||||||
|
#define GAP_SIZE {gap_size}
|
||||||
|
"""
|
||||||
|
|
||||||
|
GEOM_GLSL = """
|
||||||
|
layout(lines) in;
|
||||||
|
in uint topo[];
|
||||||
|
layout(line_strip, max_vertices=2) out;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||||
|
uint t0 = topo[0], t1 = topo[1];
|
||||||
|
|
||||||
|
vec4 p0w = C2W(p0), p1w = C2W(p1);
|
||||||
|
vec4 edge = p1w - p0w, dir = normalize(edge);
|
||||||
|
vec4 gap = dir * GAP_SIZE;
|
||||||
|
|
||||||
|
vec4 p0_ = p0w, p1_ = p1w;
|
||||||
|
|
||||||
|
if (t0 == 1u) {
|
||||||
|
p0_ += gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t1 == 2u) {
|
||||||
|
p1_ -= gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitSegment(W2C(p0_), W2C(p1_));
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, gap_size=16):
|
||||||
|
super().__init__(gap_size=gap_size)
|
||||||
|
|
||||||
|
def glenable(self):
|
||||||
|
super().glenable()
|
||||||
|
bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||||
|
|
||||||
|
|
||||||
|
class GizmoShader(BaseShader):
|
||||||
|
"""Gizmo 2D shader
|
||||||
|
|
||||||
|
Scaling to match viewport is partially controlled by user preferences and gizmo code.
|
||||||
|
"""
|
||||||
|
# TODO: add some magic to respect gizmo settings/params
|
||||||
|
|
||||||
|
VERT_GLSL = """
|
||||||
|
uniform mat4 ModelViewProjectionMatrix;
|
||||||
|
|
||||||
|
in vec3 pos;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class DotsGizmoShader(GizmoShader):
|
||||||
|
"""Draws circles of radius 1 around points"""
|
||||||
|
|
||||||
|
TYPE = 'POINTS'
|
||||||
|
|
||||||
|
DEF_GLSL = BaseShader.DEF_GLSL + """
|
||||||
|
#define CIRCLE_SEGMENTS 12
|
||||||
|
#define CIRCLE_RADIUS 8
|
||||||
|
"""
|
||||||
|
|
||||||
|
GEOM_GLSL = """
|
||||||
|
layout(points) in;
|
||||||
|
layout(triangle_strip, max_vertices=36) out;
|
||||||
|
|
||||||
|
// apparently can be precalculated
|
||||||
|
void makeCircle(out vec4 circle[CIRCLE_SEGMENTS+1]) {
|
||||||
|
float angle_d = PI * 2 / CIRCLE_SEGMENTS;
|
||||||
|
for(int i = 0; i<=CIRCLE_SEGMENTS; i++) {
|
||||||
|
float angle = angle_d * i;
|
||||||
|
circle[i] = vec4(cos(angle), sin(angle), 0, 0) * CIRCLE_RADIUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 p0 = gl_in[0].gl_Position;
|
||||||
|
vec4 p0w = C2W(p0);
|
||||||
|
|
||||||
|
vec4 circle[CIRCLE_SEGMENTS+1];
|
||||||
|
makeCircle(circle);
|
||||||
|
|
||||||
|
vec4 p1w, p2w;
|
||||||
|
|
||||||
|
for(int i = 0; i<=CIRCLE_SEGMENTS; i++) {
|
||||||
|
p1w = p0w + circle[i];
|
||||||
|
p2w = p0w + circle[i+1];
|
||||||
|
emitTriangle(p0, W2C(p1w), W2C(p2w));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
for(int i = 0; i<=CIRCLE_SEGMENTS; i++) {
|
||||||
|
p1w = p0 + circle[i];
|
||||||
|
p2w = p0 + circle[i+1];
|
||||||
|
emitTriangle(p0, p1w, p2w);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ExtrusionGuidesShader(GizmoShader):
|
||||||
|
"""Draws lines and add cross in XY plane at endpoints"""
|
||||||
|
|
||||||
|
TYPE = 'LINES'
|
||||||
|
|
||||||
|
DEF_GLSL = BaseShader.DEF_GLSL + """
|
||||||
|
#define CROSS_SIZE .5
|
||||||
|
"""
|
||||||
|
|
||||||
|
GEOM_GLSL = """
|
||||||
|
uniform mat4 ModelViewProjectionMatrix;
|
||||||
|
|
||||||
|
layout(lines) in;
|
||||||
|
layout(line_strip, max_vertices=10) out;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||||
|
vec4 p0w = C2W(p0), p1w = C2W(p1);
|
||||||
|
|
||||||
|
emitSegment(p0, p1);
|
||||||
|
|
||||||
|
vec4 bx = ModelViewProjectionMatrix[0] * CROSS_SIZE;
|
||||||
|
vec4 by = ModelViewProjectionMatrix[1] * CROSS_SIZE;
|
||||||
|
|
||||||
|
emitSegment(p0 - bx, p0 + bx);
|
||||||
|
emitSegment(p0 - by, p0 + by);
|
||||||
|
emitSegment(p1 - bx, p1 + bx);
|
||||||
|
emitSegment(p1 - by, p1 + by);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def glenable(self):
|
||||||
|
super().glenable()
|
||||||
|
bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||||
Reference in New Issue
Block a user