mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Add aggregate decorator.
This commit is contained in:
committed by
Bruno Perdigão
parent
3dcded819f
commit
0c7a0a6bda
@@ -0,0 +1,166 @@
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2024 Bruno Perdigão <contact@brunopo.com>
|
||||
#
|
||||
# This file is part of Bonsai.
|
||||
#
|
||||
# Bonsai 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 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Bonsai 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import gpu
|
||||
import ifcopenshell
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import SpaceView3D
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from mathutils import Vector
|
||||
from bonsai.bim.module.geometry.decorator import ItemDecorator
|
||||
|
||||
|
||||
class AggregateDecorator:
|
||||
is_installed = False
|
||||
handlers = []
|
||||
|
||||
@classmethod
|
||||
def install(cls, context):
|
||||
if cls.is_installed:
|
||||
cls.uninstall()
|
||||
handler = cls()
|
||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.draw_aggregate, (context,), "WINDOW", "POST_VIEW"))
|
||||
cls.handlers.append(SpaceView3D.draw_handler_add(handler.lock_objects, (context,), "WINDOW", "POST_VIEW"))
|
||||
cls.is_installed = True
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls):
|
||||
for handler in cls.handlers:
|
||||
try:
|
||||
SpaceView3D.draw_handler_remove(handler, "WINDOW")
|
||||
except ValueError:
|
||||
pass
|
||||
cls.is_installed = False
|
||||
|
||||
def transparent_color(self, color, alpha=0.1):
|
||||
color = [i for i in color]
|
||||
color[3] = alpha
|
||||
return color
|
||||
|
||||
def dotted_line_shader(self):
|
||||
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
|
||||
vert_out.smooth('FLOAT', "v_ArcLength")
|
||||
|
||||
shader_info = gpu.types.GPUShaderCreateInfo()
|
||||
shader_info.push_constant('MAT4', "u_ViewProjectionMatrix")
|
||||
shader_info.push_constant('FLOAT', "u_Scale")
|
||||
shader_info.vertex_in(0, 'VEC3', "position")
|
||||
shader_info.vertex_in(1, 'FLOAT', "arcLength")
|
||||
shader_info.vertex_out(vert_out)
|
||||
shader_info.fragment_out(0, 'VEC4', "FragColor")
|
||||
shader_info.push_constant('VEC4', "color")
|
||||
|
||||
shader_info.vertex_source(
|
||||
"void main()"
|
||||
"{"
|
||||
" v_ArcLength = arcLength;"
|
||||
" gl_Position = u_ViewProjectionMatrix * vec4(position, 1.0f);"
|
||||
"}"
|
||||
)
|
||||
|
||||
shader_info.fragment_source(
|
||||
"void main()"
|
||||
"{"
|
||||
" if (step(sin(v_ArcLength * u_Scale), 0.4) == 1) discard;"
|
||||
" FragColor = color;"
|
||||
"}"
|
||||
)
|
||||
|
||||
shader = gpu.shader.create_from_info(shader_info)
|
||||
del vert_out
|
||||
del shader_info
|
||||
return shader
|
||||
|
||||
|
||||
def draw_custom_batch(self, coords, color):
|
||||
shader = self.dotted_line_shader()
|
||||
|
||||
arc_lengths = [0]
|
||||
for a, b in zip(coords[:-1], coords[1:]):
|
||||
arc_lengths.append(arc_lengths[-1] + (a - b).length)
|
||||
|
||||
batch = batch_for_shader(
|
||||
shader, 'LINE_STRIP',
|
||||
{"position": coords, "arcLength": arc_lengths},
|
||||
)
|
||||
|
||||
matrix = bpy.context.region_data.perspective_matrix
|
||||
shader.uniform_float("color", color)
|
||||
shader.uniform_float("u_ViewProjectionMatrix", matrix)
|
||||
shader.uniform_float("u_Scale", 25)
|
||||
batch.draw(shader)
|
||||
|
||||
|
||||
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||
shader.uniform_float("color", color)
|
||||
batch.draw(shader)
|
||||
|
||||
def draw_aggregate(self, context):
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
decorator_color_special = self.addon_prefs.decorator_color_special
|
||||
decorator_color_selected = self.addon_prefs.decorator_color_selected
|
||||
decorator_color_error = self.addon_prefs.decorator_color_error
|
||||
decorator_color_unselected = self.addon_prefs.decorator_color_unselected
|
||||
decorator_color_background = self.addon_prefs.decorator_color_background
|
||||
theme = context.preferences.themes.items()[0][1]
|
||||
selected_object_color = (*theme.view_3d.object_active, 1)
|
||||
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
self.line_shader.bind()
|
||||
# POLYLINE_UNIFORM_COLOR specific uniforms
|
||||
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
|
||||
aggregates = []
|
||||
if not (selected_objects := context.selected_objects):
|
||||
return
|
||||
for obj in selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element or not element.is_a("IfcElement"):
|
||||
return
|
||||
|
||||
parts = ifcopenshell.util.element.get_parts(element)
|
||||
if parts:
|
||||
aggregates.append(obj)
|
||||
|
||||
aggregate = ifcopenshell.util.element.get_aggregate(element)
|
||||
if aggregate:
|
||||
aggregates.append(tool.Ifc.get_object(aggregate))
|
||||
|
||||
aggregates = set(aggregates)
|
||||
for aggregate in aggregates:
|
||||
self.line_shader.uniform_float("lineWidth", 2.0)
|
||||
color = decorator_color_unselected
|
||||
if aggregate in selected_objects:
|
||||
color = selected_object_color
|
||||
size = aggregate.empty_display_size
|
||||
location = aggregate.location
|
||||
line_x = (location - Vector((size, 0.0, 0.0)), location + Vector((size, 0.0, 0.0)))
|
||||
self.draw_batch("LINES", line_x, color, [(0, 1)])
|
||||
line_y = (location - Vector((0.0, size, 0.0)), location + Vector((0.0, size, 0.0)))
|
||||
self.draw_batch("LINES", line_y, color, [(0, 1)])
|
||||
line_z = (location - Vector((0.0, 0.0, size)), location + Vector((0.0, 0.0, size)))
|
||||
self.draw_batch("LINES", line_z, color, [(0, 1)])
|
||||
if context.scene.BIMAggregateProperties.in_edit_mode:
|
||||
return
|
||||
parts = ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(aggregate))
|
||||
for part in parts:
|
||||
part_obj = tool.Ifc.get_object(part)
|
||||
line = (part_obj.matrix_world.translation, location)
|
||||
self.draw_custom_batch(line, decorator_color_unselected)
|
||||
@@ -32,6 +32,7 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from bonsai.bim.module.aggregate.decorator import AggregateDecorator
|
||||
|
||||
|
||||
def can_aggregate(relating_obj: bpy.types.Object, related_obj: bpy.types.Object) -> bool:
|
||||
@@ -73,6 +74,13 @@ def poll_related_object(self: "BIMObjectAggregateProperties", related_obj: bpy.t
|
||||
return True
|
||||
|
||||
|
||||
def update_aggregate_decorator(self, context):
|
||||
if self.aggregate_decorator:
|
||||
AggregateDecorator.install(bpy.context)
|
||||
else:
|
||||
AggregateDecorator.uninstall()
|
||||
|
||||
|
||||
class BIMObjectAggregateProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
relating_object: PointerProperty(name="Relating Whole", type=bpy.types.Object, poll=poll_relating_object)
|
||||
@@ -89,4 +97,8 @@ class NotEditingObjects(bpy.types.PropertyGroup):
|
||||
class BIMAggregateProperties(PropertyGroup):
|
||||
in_edit_mode: BoolProperty(name="In Edit Mode")
|
||||
not_editing_objects: CollectionProperty(type=NotEditingObjects)
|
||||
|
||||
aggregate_decorator: BoolProperty(
|
||||
name="Display Aggregate",
|
||||
default=False,
|
||||
update=update_aggregate_decorator,
|
||||
)
|
||||
|
||||
@@ -47,6 +47,9 @@ class BIM_PT_aggregate(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
row = layout.row()
|
||||
row.label(text="Aggregate Decorator")
|
||||
row.prop(context.scene.BIMAggregateProperties, "aggregate_decorator", icon="HIDE_OFF", text="")
|
||||
if not AggregateData.is_loaded:
|
||||
AggregateData.load()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user