summaryrefslogtreecommitdiffstats
path: root/BakeVertexData.py
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2025-06-08 14:34:22 -0700
committeryum <yum.food.vr@gmail.com>2025-06-08 14:34:22 -0700
commit5e75dcd4260b1d64b9360acb2f2887fe63230eb0 (patch)
tree68b2de38b48794e763e31cb0b35e35efd08e9377 /BakeVertexData.py
parent7cbb2c12028db11d811554c0fd8d1e6e79c4810d (diff)
switch to new baking method where we also add a rotating matrix
Diffstat (limited to 'BakeVertexData.py')
-rw-r--r--BakeVertexData.py707
1 files changed, 456 insertions, 251 deletions
diff --git a/BakeVertexData.py b/BakeVertexData.py
index 7493aef..2e2554a 100644
--- a/BakeVertexData.py
+++ b/BakeVertexData.py
@@ -15,252 +15,6 @@ from bpy.props import BoolProperty, FloatProperty, IntProperty
from bpy.types import Panel, Operator
-class MESH_OT_bake_vertex_vectors(Operator):
- bl_idname = "mesh.bake_vertex_vectors"
- bl_label = "Bake Vertex Vectors"
- bl_description = "Bake selected vertices with automatic center and scale calculation"
- bl_options = {'REGISTER', 'UNDO'}
-
- contiguous_mode: BoolProperty(
- name="Contiguous Groups",
- description="Process each contiguous group of vertices separately with its own center and scale",
- default=False
- )
-
- @classmethod
- def poll(cls, context):
- obj = context.active_object
- return (obj is not None and
- obj.type == 'MESH' and
- context.mode == 'EDIT_MESH')
-
- def get_vertex_islands(self, mesh, selected_indices):
- """Find contiguous groups of vertices using edge connectivity"""
- adjacency = {idx: set() for idx in selected_indices}
-
- for edge in mesh.edges:
- v0, v1 = edge.vertices
- if v0 in selected_indices and v1 in selected_indices:
- adjacency[v0].add(v1)
- adjacency[v1].add(v0)
-
- islands = []
- visited = set()
-
- for start_idx in selected_indices:
- if start_idx in visited:
- continue
-
- island = set()
- queue = [start_idx]
-
- while queue:
- current = queue.pop(0)
- if current in visited:
- continue
-
- visited.add(current)
- island.add(current)
-
- for neighbor in adjacency[current]:
- if neighbor not in visited:
- queue.append(neighbor)
-
- islands.append(island)
-
- return islands
-
- def get_island_info(self, mesh, island_indices):
- """Calculate center and scale for a single island"""
- verts = []
- for idx in island_indices:
- verts.append(mesh.vertices[idx].co)
-
- if not verts:
- return None, None, None
-
- center = mathutils.Vector((0, 0, 0))
- for co in verts:
- center += co
- center /= len(verts)
-
- max_dist = 0.0
- for co in verts:
- vec = co - center
- max_component = max(abs(vec.x), abs(vec.y), abs(vec.z))
- max_dist = max(max_dist, max_component)
-
- if max_dist > 0:
- scale = 1.0 / max_dist
- else:
- scale = 1.0
-
- return center, max_dist, scale
-
- def execute(self, context):
- obj = context.active_object
- mesh = obj.data
-
- try:
- bpy.ops.object.mode_set(mode='OBJECT')
- except Exception as e:
- self.report({'ERROR'}, f"Failed to switch to object mode: {str(e)}")
- return {'CANCELLED'}
-
- selected_indices = {v.index for v in mesh.vertices if v.select}
-
- if not selected_indices:
- self.report({'WARNING'}, "No vertices selected")
- try:
- bpy.ops.object.mode_set(mode='EDIT')
- except:
- pass
- return {'CANCELLED'}
-
- # Check if mesh has faces
- if not mesh.polygons:
- self.report({'ERROR'}, "Mesh has no faces. Vertex colors require faces to store data.")
- try:
- bpy.ops.object.mode_set(mode='EDIT')
- except:
- pass
- return {'CANCELLED'}
-
- if not mesh.vertex_colors:
- mesh.vertex_colors.new(name="BakedVectors")
-
- color_layer = mesh.vertex_colors.active
- if not color_layer:
- self.report({'ERROR'}, "Failed to create vertex color layer")
- try:
- bpy.ops.object.mode_set(mode='EDIT')
- except:
- pass
- return {'CANCELLED'}
-
- source_matrix = obj.matrix_world
-
- if self.contiguous_mode:
- # Build vertex to polygon mapping for efficiency
- vertex_to_polys = {}
- for poly_idx, poly in enumerate(mesh.polygons):
- for vertex_idx in poly.vertices:
- if vertex_idx in selected_indices:
- if vertex_idx not in vertex_to_polys:
- vertex_to_polys[vertex_idx] = []
- vertex_to_polys[vertex_idx].append(poly_idx)
-
- islands = self.get_vertex_islands(mesh, selected_indices)
- total_updated = 0
-
- for island_indices in islands:
- center, max_dist, scale = self.get_island_info(mesh, island_indices)
- if center is None:
- continue
-
- center_world = source_matrix @ center
-
- # Collect all polygons that contain vertices from this island
- relevant_polys = set()
- for vertex_idx in island_indices:
- if vertex_idx in vertex_to_polys:
- relevant_polys.update(vertex_to_polys[vertex_idx])
-
- # Only process relevant polygons
- for poly_idx in relevant_polys:
- poly = mesh.polygons[poly_idx]
- for loop_idx in poly.loop_indices:
- vertex_idx = mesh.loops[loop_idx].vertex_index
-
- if vertex_idx in island_indices:
- vertex = mesh.vertices[vertex_idx]
-
- vertex_world = source_matrix @ vertex.co
- vector_world = center_world - vertex_world
- vector_object = source_matrix.inverted_safe().to_3x3() @ vector_world
- vector_scaled = vector_object * scale
-
- color = mathutils.Vector((
- (vector_scaled.x + 1.0) * 0.5,
- (vector_scaled.y + 1.0) * 0.5,
- (vector_scaled.z + 1.0) * 0.5,
- scale
- ))
-
- color_layer.data[loop_idx].color = color
- total_updated += 1
-
- mesh.update()
-
- try:
- bpy.ops.object.mode_set(mode='EDIT')
- except Exception as e:
- self.report({'WARNING'}, f"Could not return to edit mode: {str(e)}")
-
- self.report({'INFO'}, f"Baked {len(islands)} contiguous groups ({len(selected_indices)} vertices)")
- return {'FINISHED'}
-
- else:
- verts = []
- for idx in selected_indices:
- verts.append(mesh.vertices[idx].co)
-
- center = mathutils.Vector((0, 0, 0))
- for co in verts:
- center += co
- center /= len(verts)
-
- max_dist = 0.0
- for co in verts:
- vec = co - center
- max_component = max(abs(vec.x), abs(vec.y), abs(vec.z))
- max_dist = max(max_dist, max_component)
-
- if max_dist > 0:
- scale = 1.0 / max_dist
- else:
- scale = 1.0
-
- center_world = source_matrix @ center
-
- updated_count = 0
- for poly in mesh.polygons:
- for loop_idx in poly.loop_indices:
- vertex_idx = mesh.loops[loop_idx].vertex_index
-
- if vertex_idx in selected_indices:
- vertex = mesh.vertices[vertex_idx]
-
- vertex_world = source_matrix @ vertex.co
- vector_world = center_world - vertex_world
- vector_object = source_matrix.inverted_safe().to_3x3() @ vector_world
- vector_scaled = vector_object * scale
-
- color = mathutils.Vector((
- (vector_scaled.x + 1.0) * 0.5,
- (vector_scaled.y + 1.0) * 0.5,
- (vector_scaled.z + 1.0) * 0.5,
- scale
- ))
-
- color_layer.data[loop_idx].color = color
- updated_count += 1
-
- mesh.update()
-
- try:
- bpy.ops.object.mode_set(mode='EDIT')
- except Exception as e:
- self.report({'WARNING'}, f"Could not return to edit mode: {str(e)}")
-
- self.report({'INFO'}, f"Baked {len(selected_indices)} vertices with scale {scale:.3f}")
- return {'FINISHED'}
-
- def draw(self, context):
- layout = self.layout
- layout.prop(self, "contiguous_mode")
-
-
class MESH_OT_select_all_linked_submeshes(Operator):
bl_idname = "mesh.select_all_linked_submeshes"
bl_label = "Select All Linked Submeshes"
@@ -357,6 +111,10 @@ class MESH_OT_select_all_linked_submeshes(Operator):
self.report({'INFO'}, f"Expanded selection in {affected_islands} submeshes ({expanded_count} new vertices)")
return {'FINISHED'}
+ def draw(self, context):
+ layout = self.layout
+ layout.prop(self, "contiguous_mode")
+
class MESH_OT_select_linked_across_boundaries(Operator):
bl_idname = "mesh.select_linked_across_boundaries"
@@ -1320,7 +1078,7 @@ class MESH_OT_merge_by_distance_in_submeshes(Operator):
queue = deque([start_idx])
while queue:
- current = queue.popleft()
+ current = queue.pop(0)
if current in visited:
continue
@@ -1448,6 +1206,449 @@ class MESH_OT_merge_by_distance_in_submeshes(Operator):
layout.label(text="Merges within submeshes only", icon='INFO')
+class MESH_OT_bake_vertex_and_rotation_combined(Operator):
+ bl_idname = "mesh.bake_vertex_and_rotation_combined"
+ bl_label = "Bake Vectors & Rotation Combined"
+ bl_description = "Bake vertex vectors relative to orthonormal basis and orientation quaternions in one operation"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ contiguous_mode: BoolProperty(
+ name="Contiguous Groups",
+ description="Process each contiguous group of vertices separately with its own center and scale",
+ default=False
+ )
+
+ normal_epsilon: FloatProperty(
+ name="Normal Epsilon",
+ description="Minimum angle difference (radians) between normals to consider them unique",
+ default=0.1,
+ min=0.01,
+ max=1.0,
+ precision=3
+ )
+
+ @classmethod
+ def poll(cls, context):
+ obj = context.active_object
+ return (obj is not None and
+ obj.type == 'MESH' and
+ context.mode == 'EDIT_MESH')
+
+ def get_vertex_islands(self, mesh, selected_indices):
+ """Find contiguous groups of vertices using edge connectivity"""
+ adjacency = {idx: set() for idx in selected_indices}
+
+ for edge in mesh.edges:
+ v0, v1 = edge.vertices
+ if v0 in selected_indices and v1 in selected_indices:
+ adjacency[v0].add(v1)
+ adjacency[v1].add(v0)
+
+ islands = []
+ visited = set()
+
+ for start_idx in selected_indices:
+ if start_idx in visited:
+ continue
+
+ island = set()
+ queue = [start_idx]
+
+ while queue:
+ current = queue.pop(0)
+ if current in visited:
+ continue
+
+ visited.add(current)
+ island.add(current)
+
+ for neighbor in adjacency[current]:
+ if neighbor not in visited:
+ queue.append(neighbor)
+
+ islands.append(island)
+
+ return islands
+
+ def get_submesh_faces_and_normals(self, mesh, island_indices):
+ """Get all faces and their normals for vertices in the island"""
+ faces_data = []
+
+ # Build vertex to faces mapping for efficiency
+ vertex_to_faces = {}
+ for face_idx, face in enumerate(mesh.polygons):
+ for vertex_idx in face.vertices:
+ if vertex_idx in island_indices:
+ if vertex_idx not in vertex_to_faces:
+ vertex_to_faces[vertex_idx] = []
+ vertex_to_faces[vertex_idx].append(face_idx)
+
+ # Collect unique faces that belong to this island
+ island_faces = set()
+ for vertex_idx in island_indices:
+ if vertex_idx in vertex_to_faces:
+ for face_idx in vertex_to_faces[vertex_idx]:
+ face = mesh.polygons[face_idx]
+ # Check if all vertices of this face are in the island
+ if all(v in island_indices for v in face.vertices):
+ island_faces.add(face_idx)
+
+ # Calculate face data
+ for face_idx in island_faces:
+ face = mesh.polygons[face_idx]
+ faces_data.append({
+ 'normal': face.normal.copy(),
+ 'area': face.area,
+ 'index': face_idx
+ })
+
+ return faces_data
+
+ def build_orthonormal_basis_from_faces(self, faces_data, epsilon):
+ """Build orthonormal basis from face normals"""
+ if not faces_data:
+ # Default to standard basis if no faces
+ return mathutils.Matrix.Identity(3)
+
+ # Sort faces by area (largest first)
+ faces_data.sort(key=lambda x: x['area'], reverse=True)
+
+ # Find up to 3 non-parallel normals
+ basis_normals = []
+ epsilon_cos = math.cos(epsilon)
+
+ for face_data in faces_data:
+ normal = face_data['normal'].normalized()
+
+ # Check if this normal is sufficiently different from existing ones
+ is_unique = True
+ for existing_normal in basis_normals:
+ dot_product = abs(normal.dot(existing_normal))
+ if dot_product > epsilon_cos:
+ is_unique = False
+ break
+
+ if is_unique:
+ basis_normals.append(normal)
+ if len(basis_normals) >= 3:
+ break
+
+ # Build orthonormal basis
+ if len(basis_normals) == 0:
+ # No faces, use default
+ return mathutils.Matrix.Identity(3)
+
+ # First basis vector is the largest face normal
+ x_axis = basis_normals[0].normalized()
+
+ if len(basis_normals) >= 2:
+ # Use second normal to define y-axis
+ y_candidate = basis_normals[1]
+ # Make it orthogonal to x
+ y_axis = (y_candidate - y_candidate.project(x_axis)).normalized()
+
+ # If y_axis is too small (normals were almost parallel), find a different vector
+ if y_axis.length < 0.1:
+ # Create arbitrary perpendicular vector
+ if abs(x_axis.z) < 0.9:
+ y_axis = mathutils.Vector((0, 0, 1)).cross(x_axis).normalized()
+ else:
+ y_axis = mathutils.Vector((1, 0, 0)).cross(x_axis).normalized()
+ else:
+ # Only one unique normal, create arbitrary perpendicular
+ if abs(x_axis.z) < 0.9:
+ y_axis = mathutils.Vector((0, 0, 1)).cross(x_axis).normalized()
+ else:
+ y_axis = mathutils.Vector((1, 0, 0)).cross(x_axis).normalized()
+
+ # Third axis from cross product
+ z_axis = x_axis.cross(y_axis).normalized()
+
+ # Build rotation matrix (columns are the basis vectors)
+ matrix = mathutils.Matrix((
+ (x_axis.x, y_axis.x, z_axis.x),
+ (x_axis.y, y_axis.y, z_axis.y),
+ (x_axis.z, y_axis.z, z_axis.z)
+ ))
+
+ # Ensure right-handed coordinate system
+ if matrix.determinant() < 0:
+ z_axis = -z_axis
+ matrix = mathutils.Matrix((
+ (x_axis.x, y_axis.x, z_axis.x),
+ (x_axis.y, y_axis.y, z_axis.y),
+ (x_axis.z, y_axis.z, z_axis.z)
+ ))
+
+ return matrix
+
+ def get_island_info(self, mesh, island_indices):
+ """Calculate center and scale for a single island"""
+ verts = []
+ for idx in island_indices:
+ verts.append(mesh.vertices[idx].co)
+
+ if not verts:
+ return None, None, None
+
+ center = mathutils.Vector((0, 0, 0))
+ for co in verts:
+ center += co
+ center /= len(verts)
+
+ max_dist = 0.0
+ for co in verts:
+ vec = co - center
+ max_component = max(abs(vec.x), abs(vec.y), abs(vec.z))
+ max_dist = max(max_dist, max_component)
+
+ if max_dist > 0:
+ scale = 1.0 / max_dist
+ else:
+ scale = 1.0
+
+ return center, max_dist, scale
+
+ def execute(self, context):
+ obj = context.active_object
+ mesh = obj.data
+
+ try:
+ bpy.ops.object.mode_set(mode='OBJECT')
+ except Exception as e:
+ self.report({'ERROR'}, f"Failed to switch to object mode: {str(e)}")
+ return {'CANCELLED'}
+
+ selected_indices = {v.index for v in mesh.vertices if v.select}
+
+ if not selected_indices:
+ self.report({'WARNING'}, "No vertices selected")
+ try:
+ bpy.ops.object.mode_set(mode='EDIT')
+ except:
+ pass
+ return {'CANCELLED'}
+
+ # Check if mesh has faces
+ if not mesh.polygons:
+ self.report({'ERROR'}, "Mesh has no faces. Both vertex colors and quaternion baking require faces.")
+ try:
+ bpy.ops.object.mode_set(mode='EDIT')
+ except:
+ pass
+ return {'CANCELLED'}
+
+ # Create vertex color layer
+ if not mesh.vertex_colors:
+ mesh.vertex_colors.new(name="BakedVectors")
+ color_layer = mesh.vertex_colors.active
+ if not color_layer:
+ self.report({'ERROR'}, "Failed to create vertex color layer")
+ try:
+ bpy.ops.object.mode_set(mode='EDIT')
+ except:
+ pass
+ return {'CANCELLED'}
+
+ # Remove existing UV maps and create new ones for quaternions
+ uv_names = ["BakedOriginAngle0", "BakedOriginAngle1"]
+ for uv_name in uv_names:
+ if uv_name in mesh.uv_layers:
+ mesh.uv_layers.remove(mesh.uv_layers[uv_name])
+
+ uv_layer0 = mesh.uv_layers.new(name="BakedOriginAngle0")
+ uv_layer1 = mesh.uv_layers.new(name="BakedOriginAngle1")
+
+ if not uv_layer0 or not uv_layer1:
+ self.report({'ERROR'}, "Failed to create UV layers")
+ try:
+ bpy.ops.object.mode_set(mode='EDIT')
+ except:
+ pass
+ return {'CANCELLED'}
+
+ source_matrix = obj.matrix_world
+
+ if self.contiguous_mode:
+ # Build vertex to polygon mapping for efficiency
+ vertex_to_polys = {}
+ for poly_idx, poly in enumerate(mesh.polygons):
+ for vertex_idx in poly.vertices:
+ if vertex_idx in selected_indices:
+ if vertex_idx not in vertex_to_polys:
+ vertex_to_polys[vertex_idx] = []
+ vertex_to_polys[vertex_idx].append(poly_idx)
+
+ islands = self.get_vertex_islands(mesh, selected_indices)
+ total_updated = 0
+
+ # Process each island separately
+ for island_indices in islands:
+ # Get vector data for this island
+ center, max_dist, scale = self.get_island_info(mesh, island_indices)
+ if center is None:
+ continue
+
+ # Get face data and build orthonormal basis for this island
+ faces_data = self.get_submesh_faces_and_normals(mesh, island_indices)
+ basis_matrix = self.build_orthonormal_basis_from_faces(faces_data, self.normal_epsilon)
+
+ # Convert basis to quaternion
+ quaternion = basis_matrix.to_quaternion()
+ quaternion.normalize()
+
+ if quaternion.w < 0:
+ quaternion.negate()
+
+ center_world = source_matrix @ center
+
+ # Collect all polygons that contain vertices from this island
+ relevant_polys = set()
+ for vertex_idx in island_indices:
+ if vertex_idx in vertex_to_polys:
+ relevant_polys.update(vertex_to_polys[vertex_idx])
+
+ # Only process relevant polygons
+ for poly_idx in relevant_polys:
+ poly = mesh.polygons[poly_idx]
+ for loop_idx in poly.loop_indices:
+ vertex_idx = mesh.loops[loop_idx].vertex_index
+
+ if vertex_idx in island_indices:
+ vertex = mesh.vertices[vertex_idx]
+
+ # Calculate vector from vertex to center in world space
+ vertex_world = source_matrix @ vertex.co
+ vector_world = vertex_world - center_world
+
+ # Transform to object space
+ vector_object = source_matrix.inverted_safe().to_3x3() @ vector_world
+
+ # Transform vector to local orthonormal basis coordinates
+ # Use transpose of basis matrix to transform from object to local
+ vector_local = basis_matrix.transposed() @ vector_object
+
+ # Apply scale
+ vector_scaled = vector_local * scale
+
+ # Store transformed vector in vertex colors
+ color = mathutils.Vector((
+ (vector_scaled.x + 1.0) * 0.5,
+ (vector_scaled.y + 1.0) * 0.5,
+ (vector_scaled.z + 1.0) * 0.5,
+ scale
+ ))
+ color_layer.data[loop_idx].color = color
+
+ # Store quaternion in UV coordinates
+ uv_layer0.data[loop_idx].uv = mathutils.Vector((quaternion.x, quaternion.y))
+ uv_layer1.data[loop_idx].uv = mathutils.Vector((quaternion.z, quaternion.w))
+
+ total_updated += 1
+
+ mesh.update()
+
+ try:
+ bpy.ops.object.mode_set(mode='EDIT')
+ except Exception as e:
+ self.report({'WARNING'}, f"Could not return to edit mode: {str(e)}")
+
+ self.report({'INFO'}, f"Baked vectors & quaternions for {len(islands)} contiguous groups ({len(selected_indices)} vertices)")
+ return {'FINISHED'}
+
+ else:
+ # Single group mode - process all selected vertices as one group
+
+ # Get vector data
+ verts = []
+ for idx in selected_indices:
+ verts.append(mesh.vertices[idx].co)
+
+ center = mathutils.Vector((0, 0, 0))
+ for co in verts:
+ center += co
+ center /= len(verts)
+
+ max_dist = 0.0
+ for co in verts:
+ vec = co - center
+ max_component = max(abs(vec.x), abs(vec.y), abs(vec.z))
+ max_dist = max(max_dist, max_component)
+
+ if max_dist > 0:
+ scale = 1.0 / max_dist
+ else:
+ scale = 1.0
+
+ # Get face data and build orthonormal basis for all selected vertices
+ faces_data = self.get_submesh_faces_and_normals(mesh, selected_indices)
+ basis_matrix = self.build_orthonormal_basis_from_faces(faces_data, self.normal_epsilon)
+
+ # Convert basis to quaternion
+ quaternion = basis_matrix.to_quaternion()
+ quaternion.normalize()
+
+ if quaternion.w < 0:
+ quaternion.negate()
+
+ center_world = source_matrix @ center
+
+ updated_count = 0
+ for poly in mesh.polygons:
+ for loop_idx in poly.loop_indices:
+ vertex_idx = mesh.loops[loop_idx].vertex_index
+
+ if vertex_idx in selected_indices:
+ vertex = mesh.vertices[vertex_idx]
+
+ # Calculate vector from vertex to center in world space
+ vertex_world = source_matrix @ vertex.co
+ vector_world = vertex_world - center_world
+
+ # Transform to object space
+ vector_object = source_matrix.inverted_safe().to_3x3() @ vector_world
+
+ # Transform vector to local orthonormal basis coordinates
+ vector_local = basis_matrix.transposed() @ vector_object
+
+ # Apply scale
+ vector_scaled = vector_local * scale
+
+ # Store transformed vector in vertex colors
+ color = mathutils.Vector((
+ (vector_scaled.x + 1.0) * 0.5,
+ (vector_scaled.y + 1.0) * 0.5,
+ (vector_scaled.z + 1.0) * 0.5,
+ scale
+ ))
+ color_layer.data[loop_idx].color = color
+
+ # Store quaternion in UV coordinates
+ uv_layer0.data[loop_idx].uv = mathutils.Vector((quaternion.x, quaternion.y))
+ uv_layer1.data[loop_idx].uv = mathutils.Vector((quaternion.z, quaternion.w))
+
+ updated_count += 1
+
+ mesh.update()
+
+ try:
+ bpy.ops.object.mode_set(mode='EDIT')
+ except Exception as e:
+ self.report({'WARNING'}, f"Could not return to edit mode: {str(e)}")
+
+ self.report({'INFO'}, f"Baked vectors & quaternion for {len(selected_indices)} vertices with scale {scale:.3f}")
+ return {'FINISHED'}
+
+ def draw(self, context):
+ layout = self.layout
+ layout.prop(self, "contiguous_mode")
+ layout.prop(self, "normal_epsilon")
+ layout.label(text="Vectors relative to orthonormal basis", icon='INFO')
+ layout.label(text="Stores quaternion in UV maps:", icon='INFO')
+ layout.label(text=" BakedOriginAngle0: X, Y components")
+ layout.label(text=" BakedOriginAngle1: Z, W components")
+
+
class MESH_PT_bake_vertex_panel(Panel):
bl_label = "Bake Vertex Vectors"
bl_idname = "MESH_PT_bake_vertex_vectors"
@@ -1469,7 +1670,9 @@ class MESH_PT_bake_vertex_panel(Panel):
col.operator("mesh.merge_by_distance_in_submeshes", icon='AUTOMERGE_ON')
col.operator("mesh.pack_uv_islands_by_submesh", icon='UV')
col.separator()
- col.operator("mesh.bake_vertex_vectors", icon='EXPORT')
+
+ # Combined operator
+ col.operator("mesh.bake_vertex_and_rotation_combined", icon='EXPORT', text="Bake Vectors & Rotation")
box = col.box()
box.label(text="Info:", icon='INFO')
@@ -1478,8 +1681,10 @@ class MESH_PT_bake_vertex_panel(Panel):
box.label(text="Deduplicate: Remove duplicate selected submeshes")
box.label(text="Merge: Merge vertices within submeshes")
box.label(text="Pack UV Islands: Sort UV islands by submesh Z position")
- box.label(text="Bake: Auto-scale selected vertices")
+ box.label(text="Bake Vectors & Rotation: Bake vectors relative to")
+ box.label(text=" orthonormal basis & orientation quaternions")
box.label(text="Toggle Contiguous Groups for separate islands")
+ box.label(text="Vectors stored in vertex colors, quaternions in UV maps")
box.label(text="Scale factor stored in alpha channel")
mesh = obj.data
@@ -1493,7 +1698,7 @@ class MESH_PT_bake_vertex_panel(Panel):
classes = [
- MESH_OT_bake_vertex_vectors,
+ MESH_OT_bake_vertex_and_rotation_combined,
MESH_OT_select_all_linked_submeshes,
MESH_OT_select_linked_across_boundaries,
MESH_OT_deduplicate_submeshes,
@@ -1510,7 +1715,7 @@ def menu_func(self, context):
self.layout.operator("mesh.deduplicate_submeshes", icon='DUPLICATE')
self.layout.operator("mesh.merge_by_distance_in_submeshes", icon='AUTOMERGE_ON')
self.layout.operator("mesh.pack_uv_islands_by_submesh", icon='UV')
- self.layout.operator("mesh.bake_vertex_vectors", icon='EXPORT')
+ self.layout.operator("mesh.bake_vertex_and_rotation_combined", icon='EXPORT')
def register():