evaluate hidden
This commit is contained in:
parent
2b98e9bc69
commit
0e14fa19f7
1 changed files with 195 additions and 147 deletions
|
|
@ -3,6 +3,7 @@ from bpy.types import Object, Mesh
|
|||
import bpy
|
||||
import zlib, struct
|
||||
from functools import cached_property
|
||||
from contextlib import contextmanager, ExitStack
|
||||
|
||||
import numpy as np
|
||||
from numpy import dtype, float64
|
||||
|
|
@ -17,6 +18,74 @@ TYPES_NAME = 'DEFAULT'
|
|||
|
||||
class MeshCaptureException(Exception): pass
|
||||
|
||||
@contextmanager
|
||||
def get_evaluated_mesh(obj: Object):
|
||||
"""
|
||||
Context manager yielding (eval_obj, mesh) for a Blender object.
|
||||
Applies all modifiers even if the object is hidden in viewport or in a hidden/disabled collection,
|
||||
WITHOUT changing any visibility settings on the original object or its collections.
|
||||
"""
|
||||
if obj.mode == 'EDIT':
|
||||
obj.update_from_editmode()
|
||||
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
is_hidden = obj.hide_viewport or obj.hide_get()
|
||||
|
||||
mesh = None
|
||||
eval_obj = None
|
||||
is_temp = False
|
||||
temp_obj = None
|
||||
|
||||
# Try direct evaluation first
|
||||
try:
|
||||
eval_obj = obj.evaluated_get(depsgraph)
|
||||
m = eval_obj.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
||||
if m and len(m.vertices) > 0 and not is_hidden and (not obj.modifiers or len(m.vertices) != len(obj.data.vertices)):
|
||||
mesh = m
|
||||
elif m:
|
||||
eval_obj.to_mesh_clear()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if mesh is None:
|
||||
# Fallback for hidden object / excluded collection
|
||||
try:
|
||||
temp_obj = obj.copy()
|
||||
temp_obj.hide_viewport = False
|
||||
bpy.context.scene.collection.objects.link(temp_obj)
|
||||
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
depsgraph.update()
|
||||
|
||||
eval_obj = temp_obj.evaluated_get(depsgraph)
|
||||
mesh = eval_obj.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
||||
is_temp = True
|
||||
except Exception:
|
||||
if temp_obj is not None:
|
||||
try:
|
||||
bpy.context.scene.collection.objects.unlink(temp_obj)
|
||||
bpy.data.objects.remove(temp_obj, do_unlink=True)
|
||||
except Exception:
|
||||
pass
|
||||
mesh = None
|
||||
eval_obj = None
|
||||
|
||||
try:
|
||||
yield eval_obj, mesh
|
||||
finally:
|
||||
if mesh is not None and eval_obj is not None:
|
||||
try:
|
||||
eval_obj.to_mesh_clear()
|
||||
except Exception:
|
||||
pass
|
||||
if is_temp and temp_obj is not None:
|
||||
try:
|
||||
bpy.context.scene.collection.objects.unlink(temp_obj)
|
||||
bpy.data.objects.remove(temp_obj, do_unlink=True)
|
||||
bpy.context.evaluated_depsgraph_get().update()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
class SlicingObject():
|
||||
name: str
|
||||
parent: str
|
||||
|
|
@ -34,7 +103,6 @@ class SlicingObject():
|
|||
self.attributes = {}
|
||||
self.modifiers = []
|
||||
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
assert bpy.context.scene.unit_settings
|
||||
scene_scale: float = bpy.context.scene.unit_settings.scale_length
|
||||
|
||||
|
|
@ -44,10 +112,7 @@ class SlicingObject():
|
|||
self.modifiers = [{'param_id': m.field, 'param_value': m.value_str} for m in ns.modifiers]
|
||||
|
||||
if obj:
|
||||
if obj.mode == 'EDIT':
|
||||
obj.update_from_editmode()
|
||||
eval_objects = obj.evaluated_get(depsgraph)
|
||||
self.mesh, self.attributes = objects_to_tris([eval_objects], 1000 * scene_scale)
|
||||
self.mesh, self.attributes = objects_to_tris([obj], 1000 * scene_scale)
|
||||
else:
|
||||
self.mesh = np.empty((0, 4, 3))
|
||||
self.attributes = {}
|
||||
|
|
@ -347,15 +412,13 @@ class SlicingGroup():
|
|||
|
||||
def _extract_metadata(self, objs) -> None:
|
||||
import math
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
assert bpy.context.scene and bpy.context.scene.unit_settings
|
||||
scale_tx = 1000. * bpy.context.scene.unit_settings.scale_length #type: ignore
|
||||
|
||||
self.wipe_tower_xy = self.center_xy[0:2]
|
||||
for obj in objs:
|
||||
if obj.mode == 'EDIT':
|
||||
obj.update_from_editmode()
|
||||
eval_obj = obj.evaluated_get(depsgraph)
|
||||
if hasattr(eval_obj, 'nodeslicer'):
|
||||
with get_evaluated_mesh(obj) as (eval_obj, _m):
|
||||
if eval_obj and hasattr(eval_obj, 'nodeslicer'):
|
||||
if eval_obj.nodeslicer.object_type == 'WipeTower':
|
||||
self.wipe_tower_xy = np.array(eval_obj.location[0:2]) * scale_tx
|
||||
self.wipe_tower_rotation_deg = round(eval_obj.rotation_euler[2]*180/math.pi,5)
|
||||
|
|
@ -434,20 +497,16 @@ class TriMesh():
|
|||
self.matrix_world = m
|
||||
|
||||
def _cached_objects_to_tris(obj_names: tuple[str, ...], matrices: tuple[tuple[float, ...], ...], scale: float) -> tuple[NDArray[float64], dict[str, dict]]:
|
||||
# Implementation remains largely the same but uses names to fetch objects
|
||||
tris_count: int = 0
|
||||
meshes: list[TriMesh] = []
|
||||
|
||||
with ExitStack() as stack:
|
||||
for name in obj_names:
|
||||
obj = bpy.data.objects.get(name)
|
||||
if not obj: continue
|
||||
|
||||
try:
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
# We must use the evaluated object to get modifiers
|
||||
obj_eval = obj.evaluated_get(depsgraph)
|
||||
mesh: Mesh = obj_eval.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
||||
except: continue
|
||||
eval_obj, mesh = stack.enter_context(get_evaluated_mesh(obj))
|
||||
if not mesh: continue
|
||||
mesh.calc_loop_triangles()
|
||||
|
||||
cur_length = len(mesh.loop_triangles)
|
||||
|
|
@ -584,17 +643,10 @@ def _cached_capture_mesh_edges(
|
|||
obj = bpy.data.objects.get(obj_name)
|
||||
if not obj: return [], [], [], {}
|
||||
|
||||
try:
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
obj_eval = obj.evaluated_get(depsgraph)
|
||||
mesh = obj_eval.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph)
|
||||
except Exception:
|
||||
return [], [], [], {}
|
||||
|
||||
with get_evaluated_mesh(obj) as (eval_obj, mesh):
|
||||
if not mesh or not mesh.vertices:
|
||||
return [], [], [], {}
|
||||
|
||||
try:
|
||||
world_mat = np.array(matrix_tuple).reshape(4, 4)
|
||||
num_verts = len(mesh.vertices)
|
||||
verts = np.empty((num_verts, 3), dtype=np.float64)
|
||||
|
|
@ -655,10 +707,6 @@ def _cached_capture_mesh_edges(
|
|||
|
||||
return final_paths, final_e_mults, final_f_rates, final_extras
|
||||
|
||||
finally:
|
||||
if hasattr(obj_eval, "to_mesh_clear"):
|
||||
obj_eval.to_mesh_clear()
|
||||
|
||||
|
||||
def capture_mesh_edges(obj: Object, e_attr_name: str = "e_mult", f_attr_name: str = "f_rate", extra_attrs: dict[str, str] | None = None) -> tuple[list[NDArray[float64]], list[NDArray[float64]], list[NDArray[float64]], dict[str, list[NDArray[float64]]]]:
|
||||
if not obj or not obj.data: return [], [], [], {}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue