capture attributes of face domain

This commit is contained in:
Nicolas Predella 2026-06-03 19:33:20 +03:00
parent 086de70ad6
commit 90a2c61fb4
2 changed files with 114 additions and 7 deletions

View file

@ -50,9 +50,7 @@ class SLICENODE_SliceShells(SliceNode):
if not coll: return SlicePaths() if not coll: return SlicePaths()
merged_object = SlicingObject() merged_object = SlicingObject.merge(coll.objects)
for obj in coll.objects:
merged_object.mesh = np.concatenate([merged_object.mesh, obj.mesh], axis=0)
if rotation and any(rotation): if rotation and any(rotation):
from mathutils import Euler from mathutils import Euler

View file

@ -21,6 +21,7 @@ class SlicingObject():
name: str name: str
parent: str parent: str
mesh: NDArray[float64] mesh: NDArray[float64]
attributes: dict[str, dict]
object_type: str = "ModelPart" object_type: str = "ModelPart"
modifiers: list[dict] = [] modifiers: list[dict] = []
extruder: int = 1 extruder: int = 1
@ -30,6 +31,7 @@ class SlicingObject():
if obj: self.name = str(obj.name) if obj: self.name = str(obj.name)
self.parent = parent self.parent = parent
self.attributes = {}
depsgraph = bpy.context.evaluated_depsgraph_get() depsgraph = bpy.context.evaluated_depsgraph_get()
assert bpy.context.scene.unit_settings assert bpy.context.scene.unit_settings
@ -44,9 +46,47 @@ class SlicingObject():
if obj.mode == 'EDIT': if obj.mode == 'EDIT':
obj.update_from_editmode() obj.update_from_editmode()
eval_objects = obj.evaluated_get(depsgraph) eval_objects = obj.evaluated_get(depsgraph)
self.mesh = objects_to_tris([eval_objects], 1000 * scene_scale) self.mesh, self.attributes = objects_to_tris([eval_objects], 1000 * scene_scale)
else: else:
self.mesh = np.empty((0, 4, 3)) self.mesh = np.empty((0, 4, 3))
self.attributes = {}
@staticmethod
def merge(objects: list[SlicingObject]) -> SlicingObject:
merged = SlicingObject()
if not objects: return merged
# Take metadata from first object if available
merged.name = objects[0].name + "_merged"
merged.parent = objects[0].parent
merged.object_type = objects[0].object_type
merged.extruder = objects[0].extruder
valid_meshes = [o.mesh for o in objects if o.mesh.size]
merged.mesh = np.concatenate(valid_meshes, axis=0) if valid_meshes else np.empty((0, 4, 3))
all_attr_names = set()
for o in objects:
all_attr_names.update(o.attributes.keys())
for name in all_attr_names:
template_obj = next(o for o in objects if name in o.attributes)
template = template_obj.attributes[name]
val_list = []
for o in objects:
if name in o.attributes:
val_list.append(o.attributes[name]["values"])
else:
shape = (len(o.mesh),) + template["values"].shape[1:]
val_list.append(np.zeros(shape, dtype=template["values"].dtype))
merged.attributes[name] = {
"domain": template["domain"],
"type": template["type"],
"values": np.concatenate(val_list, axis=0)
}
return merged
def offset(self, offset: NDArray): def offset(self, offset: NDArray):
self.mesh[:, :3, :] += offset self.mesh[:, :3, :] += offset
@ -334,7 +374,7 @@ class TriMesh():
self.length_tris = t self.length_tris = t
self.matrix_world = m self.matrix_world = m
def _cached_objects_to_tris(obj_names: tuple[str, ...], matrices: tuple[tuple[float, ...], ...], scale: float) -> NDArray[float64]: 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 # Implementation remains largely the same but uses names to fetch objects
tris_count: int = 0 tris_count: int = 0
meshes: list[TriMesh] = [] meshes: list[TriMesh] = []
@ -366,6 +406,46 @@ def _cached_objects_to_tris(obj_names: tuple[str, ...], matrices: tuple[tuple[fl
tris_flat: np.ndarray = np.empty(tris_count * 4 * 3, dtype=np.float64) tris_flat: np.ndarray = np.empty(tris_count * 4 * 3, dtype=np.float64)
tris: np.ndarray = tris_flat.reshape(-1, 4, 3) tris: np.ndarray = tris_flat.reshape(-1, 4, 3)
# Identify all FACE domain attributes
face_attributes_info = {}
for trimesh in meshes:
for attr in trimesh.mesh.attributes:
if attr.domain == 'FACE':
face_attributes_info[attr.name] = attr.data_type
blender_type_map = {
'BOOLEAN': 'bool',
'INT': 'int',
'FLOAT': 'float',
'FLOAT_VECTOR': 'float_vector',
'FLOAT_COLOR': 'color',
'BYTE_COLOR': 'color',
}
attributes_data = {}
for name, data_type in face_attributes_info.items():
if data_type not in blender_type_map: continue
my_type = blender_type_map[data_type]
if data_type == 'BOOLEAN':
dtype, shape_ext = np.bool_, ()
elif data_type == 'INT':
dtype, shape_ext = np.int32, ()
elif data_type == 'FLOAT':
dtype, shape_ext = np.float32, ()
elif data_type == 'FLOAT_VECTOR':
dtype, shape_ext = np.float32, (3,)
elif data_type in ('BYTE_COLOR', 'FLOAT_COLOR'):
dtype, shape_ext = np.float32, (4,)
else: continue
attributes_data[name] = {
"domain": "face",
"type": my_type,
"values": np.zeros((tris_count,) + shape_ext, dtype=dtype)
}
col_idx = 0 col_idx = 0
for trimesh in meshes: for trimesh in meshes:
tris_v_i_flat: np.ndarray = np.empty(trimesh.length_tris * 3, dtype=np.int32) tris_v_i_flat: np.ndarray = np.empty(trimesh.length_tris * 3, dtype=np.int32)
@ -396,11 +476,40 @@ def _cached_objects_to_tris(obj_names: tuple[str, ...], matrices: tuple[tuple[fl
tris[col_idx:col_idx + trimesh.length_tris,:] = tris_coords_and_norm tris[col_idx:col_idx + trimesh.length_tris,:] = tris_coords_and_norm
# Attributes
if attributes_data:
poly_indices = np.empty(trimesh.length_tris, dtype=np.int32)
trimesh.mesh.loop_triangles.foreach_get("polygon_index", poly_indices)
for name, attr_dict in attributes_data.items():
if name in trimesh.mesh.attributes:
attr = trimesh.mesh.attributes[name]
data_len = len(attr.data)
if attr.data_type == 'FLOAT_VECTOR':
raw_values = np.empty(data_len * 3, dtype=np.float32)
attr.data.foreach_get("vector", raw_values)
raw_values = raw_values.reshape(-1, 3)
elif attr.data_type == 'FLOAT_COLOR':
raw_values = np.empty(data_len * 4, dtype=np.float32)
attr.data.foreach_get("color", raw_values)
raw_values = raw_values.reshape(-1, 4)
elif attr.data_type == 'BYTE_COLOR':
temp_values = np.empty(data_len * 4, dtype=np.uint8)
attr.data.foreach_get("color", temp_values)
raw_values = temp_values.astype(np.float32) / 255.0
raw_values = raw_values.reshape(-1, 4)
else:
raw_values = np.empty(data_len, dtype=attr_dict["values"].dtype)
attr.data.foreach_get("value", raw_values)
attr_dict["values"][col_idx:col_idx + trimesh.length_tris] = raw_values[poly_indices]
col_idx += trimesh.length_tris col_idx += trimesh.length_tris
return tris return tris, attributes_data
def objects_to_tris(objects: list[Object], scale) -> NDArray[float64]: def objects_to_tris(objects: list[Object], scale) -> tuple[NDArray[float64], dict[str, dict]]:
obj_names = tuple(o.name for o in objects) obj_names = tuple(o.name for o in objects)
matrices = tuple(tuple(val for row in o.matrix_world for val in row) for o in objects) matrices = tuple(tuple(val for row in o.matrix_world for val in row) for o in objects)
return _cached_objects_to_tris(obj_names, matrices, scale) return _cached_objects_to_tris(obj_names, matrices, scale)