From 2941a4ad1a94cfc614d153ac72808ddb38cefe3f Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sun, 16 Mar 2025 10:56:18 +1100 Subject: [PATCH] See #1227. Recalculate normals and limited dissolve when layerset slicing and generating underside clipping geom. --- src/bonsai/bonsai/tool/loader.py | 33 ++++++++++++-------------------- src/bonsai/bonsai/tool/model.py | 9 ++++----- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index 9a0ad1da21..0b8c4f321e 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -20,7 +20,6 @@ from __future__ import annotations import os import re import bpy -import math import bmesh import logging import ifcopenshell.geom @@ -36,6 +35,7 @@ import bonsai.bim.import_ifc import numpy as np import numpy.typing as npt from ifcopenshell.util.shape_builder import np_to_4d +from math import atan, radians from mathutils import Vector, Matrix from pathlib import Path from typing import Union, Any, Optional @@ -857,9 +857,9 @@ class Loader(bonsai.core.tool.Loader): camera.BIMCameraProperties.height = height if width > height: - fov = 2 * math.atan(width / (2 * abs_min_z)) + fov = 2 * atan(width / (2 * abs_min_z)) else: - fov = 2 * math.atan(height / (2 * abs_min_z)) + fov = 2 * atan(height / (2 * abs_min_z)) camera.angle = fov @@ -1060,13 +1060,15 @@ class Loader(bonsai.core.tool.Loader): for i, material in enumerate(mesh.materials): if style := tool.Ifc.get_entity(material): styles[style] = i - for layer in layer_set.MaterialLayers[:-1]: + last_i = len(layer_set.MaterialLayers) - 1 + for i, layer in enumerate(layer_set.MaterialLayers): prev_co = co.copy() co += no * layer.LayerThickness * cls.unit_scale - bisect_geom = bmesh.ops.bisect_plane( - bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no - ) - bmesh.ops.duplicate(bm, geom=bisect_geom["geom_cut"]) + if i != last_i: + bisect_geom = bmesh.ops.bisect_plane( + bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no + ) + bmesh.ops.duplicate(bm, geom=bisect_geom["geom_cut"]) if style := ifcopenshell.util.representation.get_material_style(layer.Material, body): if (material_index := styles.get(style, None)) is None: material_index = len(mesh.materials) @@ -1078,19 +1080,8 @@ class Loader(bonsai.core.tool.Loader): face.material_index = material_index has_layer_styles = True - # Last layer - layer = layer_set.MaterialLayers[-1] - if style := ifcopenshell.util.representation.get_material_style(layer.Material, body): - if (material_index := styles.get(style, None)) is None: - material_index = len(mesh.materials) - mesh.materials.append(tool.Ifc.get_object(style)) - for face in bisect_geom["geom"]: - if isinstance(face, bmesh.types.BMFace): - center = face.calc_center_median() - # if center.y > co.y: - if (center - co).dot(no) >= 0: - face.material_index = material_index - has_layer_styles = True + bmesh.ops.dissolve_limit(bm, angle_limit=radians(1), verts=bm.verts, edges=bm.edges, delimit={"MATERIAL"}) + bmesh.ops.recalc_face_normals(bm, faces=bm.faces) bm.to_mesh(mesh) bm.free() diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index 358456b05a..b31dfacfb9 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -38,15 +38,12 @@ import ifcopenshell.util.unit import bonsai.core.geometry import bonsai.core.tool import bonsai.tool as tool -import bonsai.core.geometry as geometry -from math import atan, cos, degrees, pi, inf +from math import atan, cos, degrees, pi, radians from mathutils import Matrix, Vector from copy import deepcopy from functools import partial from bonsai.bim import import_ifc -# TODO: This line is somehow keeping the world from falling apart with a circular import error. -from bonsai.bim.module.geometry.helper import Helper from bonsai.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData from bonsai.bim.module.model.opening import FilledOpeningGenerator from ifcopenshell.util.shape_builder import ShapeBuilder @@ -2108,6 +2105,7 @@ class Model(bonsai.core.tool.Model): bm = bmesh.new() bm.from_mesh(obj.data) + bmesh.ops.dissolve_limit(bm, angle_limit=radians(1), verts=bm.verts, edges=bm.edges) bm.faces.ensure_lookup_table() clipping_bm = bmesh.new() @@ -2117,7 +2115,7 @@ class Model(bonsai.core.tool.Model): face.normal_update() normal = face.normal.to_4d() normal.w = 0 - if (obj.matrix_world @ normal).z >= 0: + if (obj.matrix_world @ normal).z >= -0.5: continue new_verts = [] for vert in face.verts: @@ -2130,6 +2128,7 @@ class Model(bonsai.core.tool.Model): if not len(clipping_bm.faces): return + bmesh.ops.recalc_face_normals(clipping_bm, faces=clipping_bm.faces) return clipping_bm # clipping_bm is in project units @classmethod