summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--BakeVertexData.py91
-rw-r--r--DecodeVertexData.cs252
2 files changed, 188 insertions, 155 deletions
diff --git a/BakeVertexData.py b/BakeVertexData.py
index dd8e3f5..3eecf05 100644
--- a/BakeVertexData.py
+++ b/BakeVertexData.py
@@ -778,7 +778,7 @@ class MESH_OT_merge_by_distance_per_submesh(BaseSubmeshOperator):
layout.prop(self, "merge_distance")
-class MESH_OT_bake_vertex_and_rotation_combined(BaseSubmeshOperator):
+class MESH_OT_bake_origin_and_orientation_combined(BaseSubmeshOperator):
bl_idname = "mesh.bake_submesh_origin_and_orientation"
bl_label = "Bake Submesh Data"
bl_description = "Bake vertex vectors and orientation quaternions"
@@ -800,27 +800,38 @@ class MESH_OT_bake_vertex_and_rotation_combined(BaseSubmeshOperator):
use_cache: BoolProperty(
name="Cache Identical Submeshes",
- description="Cache calculations for identical submeshes",
+ description="Cache calculations for identical submeshes to avoid recomputing basis and scale",
default=True
)
- def calculate_island_data(self, mesh, island_indices):
- """Calculate center and scale for an island"""
+ def calculate_island_center(self, mesh, island_indices):
+ """Calculate center for an island"""
if not island_indices:
- return None, 1.0
+ return None
center = mathutils.Vector((0.0, 0.0, 0.0))
for idx in island_indices:
center += mesh.vertices[idx].co
center /= len(island_indices)
- max_dist = max((abs(c - center[i])
- for idx in island_indices
- for i, c in enumerate(mesh.vertices[idx].co)),
- default=0)
-
- scale = 1.0 / max_dist if max_dist > 0 else 1.0
- return center, scale
+ return center
+
+ def calculate_island_scale(self, mesh, island_indices, center, basis_inv):
+ """Calculate scale using L-infinity norm in rotated basis"""
+ if not island_indices:
+ return 1.0
+
+ max_coord = 0.0
+ for idx in island_indices:
+ # Transform to local rotated basis
+ offset = mesh.vertices[idx].co - center
+ local_pos = basis_inv @ offset
+
+ # L-infinity norm: max of absolute values
+ max_coord = max(max_coord, abs(local_pos.x), abs(local_pos.y), abs(local_pos.z))
+
+ scale = 1.0 / max_coord if max_coord > 0 else 1.0
+ return scale
def build_basis_from_faces(self, mesh, face_indices, epsilon):
"""Build orthonormal basis from face normals"""
@@ -857,18 +868,19 @@ class MESH_OT_bake_vertex_and_rotation_combined(BaseSubmeshOperator):
return matrix
- def create_submesh_signature(self, mesh, island_indices, center, scale):
- """Create signature for caching"""
+ def create_submesh_signature(self, mesh, island_indices, center):
+ """Create signature for caching - based on relative positions only"""
tolerance = 0.0001
- local_positions = []
+ relative_positions = []
for idx in island_indices:
- local_pos = (mesh.vertices[idx].co - center) * scale
- rounded = tuple(round(local_pos[i] / tolerance) * tolerance for i in range(3))
- local_positions.append(rounded)
+ relative_pos = mesh.vertices[idx].co - center
+ # Round to tolerance to handle floating point precision
+ rounded = tuple(round(relative_pos[i] / tolerance) * tolerance for i in range(3))
+ relative_positions.append(rounded)
- local_positions.sort()
- return (len(island_indices), tuple(local_positions))
+ relative_positions.sort()
+ return (len(island_indices), tuple(relative_positions))
def process_object(self, context, obj):
"""Process a single object and return (success, stats)"""
@@ -913,40 +925,45 @@ class MESH_OT_bake_vertex_and_rotation_combined(BaseSubmeshOperator):
# Process each island
world_matrix = obj.matrix_world
world_inv = world_matrix.inverted()
+
+ # Cache for storing computed basis, scale, and quaternion for identical submeshes
+ # Key: geometry signature, Value: (scale, basis, quaternion, basis_inverse)
submesh_cache = {} if self.use_cache else None
for island in islands:
- center, scale = self.calculate_island_data(mesh, island)
+ center = self.calculate_island_center(mesh, island)
if center is None:
continue
- # Check cache
+ # Check cache first (if enabled)
+ cache_hit = False
if self.use_cache:
- signature = self.create_submesh_signature(mesh, island, center, scale)
+ signature = self.create_submesh_signature(mesh, island, center)
if signature in submesh_cache:
scale, basis, quat, basis_inv = submesh_cache[signature]
- else:
- island_faces = [f for f in selected_faces
- if all(v in island for v in mesh.polygons[f].vertices)]
- basis = self.build_basis_from_faces(mesh, island_faces, self.normal_epsilon)
- quat = basis.to_quaternion()
- quat.normalize()
- if quat.w < 0:
- quat.negate()
- quat = correction @ quat
- basis_inv = basis.inverted()
+ cache_hit = True
- submesh_cache[signature] = (scale, basis, quat, basis_inv)
- else:
+ # Only calculate if not in cache
+ if not cache_hit:
+ # Calculate basis (compute-intensive)
island_faces = [f for f in selected_faces
if all(v in island for v in mesh.polygons[f].vertices)]
basis = self.build_basis_from_faces(mesh, island_faces, self.normal_epsilon)
+ basis_inv = basis.inverted()
+
+ # Calculate scale using L-infinity norm in rotated basis (compute-intensive)
+ scale = self.calculate_island_scale(mesh, island, center, basis_inv)
+
+ # Calculate quaternion
quat = basis.to_quaternion()
quat.normalize()
if quat.w < 0:
quat.negate()
quat = correction @ quat
- basis_inv = basis.inverted()
+
+ # Store in cache if enabled
+ if self.use_cache:
+ submesh_cache[signature] = (scale, basis, quat, basis_inv)
# Transform vertices
center_world = world_matrix @ center
@@ -1034,7 +1051,7 @@ classes = [
MESH_OT_deduplicate_submeshes,
MESH_OT_pack_uv_islands_by_submesh_z,
MESH_OT_merge_by_distance_per_submesh,
- MESH_OT_bake_vertex_and_rotation_combined,
+ MESH_OT_bake_origin_and_orientation_combined,
MESH_PT_bake_vertex_panel
]
diff --git a/DecodeVertexData.cs b/DecodeVertexData.cs
index 32933ce..d248773 100644
--- a/DecodeVertexData.cs
+++ b/DecodeVertexData.cs
@@ -1,184 +1,200 @@
using UnityEngine;
using System.Collections.Generic;
+using System.Linq;
public class DecodeVertexVectors : MonoBehaviour
{
- [Header("Edge Interpolation")]
- [SerializeField] private int edgeSubdivisions = 5;
- [SerializeField] private float edgeGizmoScale = 0.3f;
- [SerializeField] private Color edgeVectorColor = new Color(0.5f, 0.8f, 1f, 0.7f);
- [SerializeField] private Color correctedVectorColor = new Color(1f, 0.5f, 0.2f, 0.7f);
-
- [Header("Orientation Visualization")]
+ [Header("Display Settings")]
+ [SerializeField] private int maxVertices = 100;
+ [SerializeField] private float vectorScale = 0.3f;
+
+ [Header("Edge Visualization")]
+ [SerializeField] private bool showEdges = true;
+ [SerializeField] private int edgeSubdivisions = 2;
+
+ [Header("Orientation")]
[SerializeField] private bool showOrientations = true;
- [SerializeField] private bool showAllAxes = true;
- [SerializeField] private float orientationVectorLength = 1.0f;
-
+ [SerializeField] private float orientationScale = 1.0f;
+
[Header("UV Channels")]
[SerializeField] private int quaternionXYChannel = 1;
[SerializeField] private int quaternionZWChannel = 2;
-
+
[Header("Colors")]
+ [SerializeField] private Color vectorColor = new Color(0.5f, 0.8f, 1f);
+ [SerializeField] private Color correctedVectorColor = new Color(1f, 0.5f, 0.2f);
[SerializeField] private Color forwardColor = Color.blue;
- [SerializeField] private Color rightColor = Color.red;
- [SerializeField] private Color upColor = Color.green;
private void OnDrawGizmos()
{
var meshFilter = GetComponent<MeshFilter>();
- if (meshFilter == null) return;
-
+ if (!meshFilter || !meshFilter.sharedMesh) return;
+
var mesh = meshFilter.sharedMesh;
- if (mesh == null) return;
-
var vertices = mesh.vertices;
- var vertexColors = mesh.colors;
-
- if (vertexColors != null && vertexColors.Length > 0)
+ var colors = mesh.colors;
+
+ // Draw vertex vectors from colors
+ if (colors != null && colors.Length > 0)
{
- DrawInterpolatedEdges(mesh, vertices, vertexColors);
+ DrawVertexVectors(mesh, vertices, colors);
}
-
+
+ // Draw orientations from UVs
if (showOrientations)
{
DrawOrientations(mesh, vertices);
}
}
-
- void DrawInterpolatedEdges(Mesh mesh, Vector3[] vertices, Color[] vertexColors)
+
+ void DrawVertexVectors(Mesh mesh, Vector3[] vertices, Color[] colors)
{
- var triangles = mesh.triangles;
- HashSet<(int, int)> drawnEdges = new HashSet<(int, int)>();
-
Vector2[] uvXY = GetUVData(mesh, quaternionXYChannel);
Vector2[] uvZW = GetUVData(mesh, quaternionZWChannel);
- bool hasQuaternions = uvXY != null && uvZW != null && uvXY.Length > 0 && uvZW.Length > 0;
-
- for (int i = 0; i < triangles.Length; i += 3)
+ bool hasQuaternions = uvXY != null && uvZW != null;
+
+ int vertexStep = Mathf.Max(1, vertices.Length / maxVertices);
+
+ // Draw vectors at vertices
+ for (int i = 0; i < vertices.Length; i += vertexStep)
+ {
+ if (i >= colors.Length) break;
+
+ Vector3 worldPos = transform.TransformPoint(vertices[i]);
+ Vector3 decodedVector = DecodeVectorFromColor(colors[i]);
+
+ // Basic vector
+ Gizmos.color = vectorColor;
+ DrawVector(worldPos, transform.TransformDirection(decodedVector), vectorScale);
+
+ // Quaternion-corrected vector
+ if (hasQuaternions && i < uvXY.Length && i < uvZW.Length)
+ {
+ Quaternion quat = GetQuaternionFromUV(uvXY[i], uvZW[i]);
+ Vector3 corrected = quat * decodedVector;
+
+ Gizmos.color = correctedVectorColor;
+ DrawVector(worldPos, transform.TransformDirection(corrected), vectorScale);
+ }
+ }
+
+ // Draw edge interpolations
+ if (showEdges && edgeSubdivisions > 0)
+ {
+ DrawEdgeInterpolations(mesh, vertices, colors, uvXY, uvZW);
+ }
+ }
+
+ void DrawEdgeInterpolations(Mesh mesh, Vector3[] vertices, Color[] colors, Vector2[] uvXY, Vector2[] uvZW)
+ {
+ var triangles = mesh.triangles;
+ HashSet<(int, int)> drawnEdges = new HashSet<(int, int)>();
+ bool hasQuaternions = uvXY != null && uvZW != null;
+
+ for (int i = 0; i < triangles.Length && drawnEdges.Count < maxVertices/2; i += 3)
{
for (int j = 0; j < 3; j++)
{
int v1 = triangles[i + j];
int v2 = triangles[i + ((j + 1) % 3)];
-
+
var edge = v1 < v2 ? (v1, v2) : (v2, v1);
- if (drawnEdges.Contains(edge)) continue;
- drawnEdges.Add(edge);
-
+ if (!drawnEdges.Add(edge)) continue;
+
if (v1 >= vertices.Length || v2 >= vertices.Length ||
- v1 >= vertexColors.Length || v2 >= vertexColors.Length)
- continue;
-
- bool canUseQuaternions = hasQuaternions &&
- v1 < uvXY.Length && v2 < uvXY.Length &&
- v1 < uvZW.Length && v2 < uvZW.Length;
-
- for (int k = 0; k <= edgeSubdivisions; k++)
+ v1 >= colors.Length || v2 >= colors.Length) continue;
+
+ // Draw subdivisions along edge
+ for (int k = 1; k < edgeSubdivisions; k++)
{
float t = k / (float)edgeSubdivisions;
-
- Vector3 localPos = Vector3.Lerp(vertices[v1], vertices[v2], t);
- Vector3 worldPos = transform.TransformPoint(localPos);
-
- Color interpolatedColor = Color.Lerp(vertexColors[v1], vertexColors[v2], t);
- Vector3 decodedVector = DecodeVectorFromColor(interpolatedColor);
-
- Gizmos.color = edgeVectorColor;
- Vector3 worldVector = transform.TransformDirection(decodedVector);
- DrawVector(worldPos, worldVector, edgeGizmoScale);
-
- if (canUseQuaternions)
+ Vector3 pos = Vector3.Lerp(vertices[v1], vertices[v2], t);
+ Color col = Color.Lerp(colors[v1], colors[v2], t);
+
+ Vector3 worldPos = transform.TransformPoint(pos);
+ Vector3 vec = DecodeVectorFromColor(col);
+
+ // Basic vector
+ Gizmos.color = vectorColor * 0.7f; // Slightly dimmer for edge points
+ DrawVector(worldPos, transform.TransformDirection(vec), vectorScale * 0.8f);
+
+ // Quaternion-corrected vector
+ if (hasQuaternions && v1 < uvXY.Length && v2 < uvXY.Length &&
+ v1 < uvZW.Length && v2 < uvZW.Length)
{
- Quaternion q1 = GetQuaternionFromUV(uvXY[v1], uvZW[v1]);
- Quaternion q2 = GetQuaternionFromUV(uvXY[v2], uvZW[v2]);
- // Slerp is more correct, but lerp is what we'll actually get in the shader.
- Quaternion interpolatedQuat = Quaternion.Lerp(q1, q2, t);
-
- Vector3 correctedVector = interpolatedQuat * decodedVector;
- Vector3 worldCorrectedVector = transform.TransformDirection(correctedVector);
-
- Gizmos.color = correctedVectorColor;
- DrawVector(worldPos, worldCorrectedVector, edgeGizmoScale);
+ Vector2 interpXY = Vector2.Lerp(uvXY[v1], uvXY[v2], t);
+ Vector2 interpZW = Vector2.Lerp(uvZW[v1], uvZW[v2], t);
+ Quaternion interpQuat = GetQuaternionFromUV(interpXY, interpZW);
+ Vector3 corrected = interpQuat * vec;
+
+ Gizmos.color = correctedVectorColor * 0.7f; // Slightly dimmer for edge points
+ DrawVector(worldPos, transform.TransformDirection(corrected), vectorScale * 0.8f);
}
}
}
}
}
-
- void DrawVector(Vector3 origin, Vector3 direction, float scale)
- {
- Vector3 end = origin + direction * scale;
- Gizmos.DrawSphere(end, 0.02f);
- Gizmos.DrawLine(origin, end);
- }
-
- Quaternion GetQuaternionFromUV(Vector2 xy, Vector2 zw)
- {
- float x = xy.x;
- float y = xy.y;
- float z = zw.x;
- float w = zw.y;
-
- return new Quaternion(x, y, z, w).normalized;
- }
-
+
void DrawOrientations(Mesh mesh, Vector3[] vertices)
{
Vector2[] uvXY = GetUVData(mesh, quaternionXYChannel);
Vector2[] uvZW = GetUVData(mesh, quaternionZWChannel);
-
- if (uvXY == null || uvZW == null || uvXY.Length == 0 || uvZW.Length == 0) return;
-
- int vertexCount = Mathf.Min(vertices.Length, uvXY.Length, uvZW.Length);
-
- for (int vertIdx = 0; vertIdx < vertexCount; vertIdx++)
+
+ if (uvXY == null || uvZW == null) return;
+
+ int vertexStep = Mathf.Max(1, vertices.Length / maxVertices);
+
+ for (int i = 0; i < vertices.Length; i += vertexStep)
{
- Quaternion quat = GetQuaternionFromUV(uvXY[vertIdx], uvZW[vertIdx]);
-
- Vector3 worldPos = transform.TransformPoint(vertices[vertIdx]);
-
+ if (i >= uvXY.Length || i >= uvZW.Length) break;
+
+ Vector3 worldPos = transform.TransformPoint(vertices[i]);
+ Quaternion quat = GetQuaternionFromUV(uvXY[i], uvZW[i]);
+
+ // Draw forward direction
+ Gizmos.color = forwardColor;
Vector3 forward = transform.TransformDirection(quat * Vector3.forward);
- DrawArrow(worldPos, forward, forwardColor, orientationVectorLength);
-
- if (showAllAxes)
- {
- Vector3 right = transform.TransformDirection(quat * Vector3.right);
- Vector3 up = transform.TransformDirection(quat * Vector3.up);
- DrawArrow(worldPos, right, rightColor, orientationVectorLength * 0.8f);
- DrawArrow(worldPos, up, upColor, orientationVectorLength * 0.8f);
- }
+ DrawArrow(worldPos, forward, orientationScale);
}
}
-
- void DrawArrow(Vector3 origin, Vector3 direction, Color color, float length)
+
+ void DrawVector(Vector3 origin, Vector3 direction, float scale)
+ {
+ Vector3 end = origin + direction * scale;
+ Gizmos.DrawLine(origin, end);
+ Gizmos.DrawSphere(end, 0.02f);
+ }
+
+ void DrawArrow(Vector3 origin, Vector3 direction, float length)
{
- Gizmos.color = color;
-
Vector3 end = origin + direction * length;
Gizmos.DrawLine(origin, end);
-
+
+ // Simple arrowhead
Vector3 right = Vector3.Cross(direction, Vector3.up).normalized;
if (right.magnitude < 0.01f)
right = Vector3.Cross(direction, Vector3.right).normalized;
-
+
Vector3 arrowBack = -direction * length * 0.2f;
Vector3 arrowSide = right * length * 0.1f;
-
+
Gizmos.DrawLine(end, end + arrowBack + arrowSide);
Gizmos.DrawLine(end, end + arrowBack - arrowSide);
-
- Gizmos.DrawSphere(origin, 0.05f);
}
-
- n (-1 to 1), aVectorontains scale factor.
- /// </summary>
-
+
+ Quaternion GetQuaternionFromUV(Vector2 xy, Vector2 zw)
+ {
+ return new Quaternion(xy.x, xy.y, zw.x, zw.y).normalized;
+ }
+
+ Vector3 DecodeVectorFromColor(Color color)
+ {
return new Vector3(
- (color.r * 2.0f - 1.0f),
- (color.g * 2.0f - 1.0f),
- (color.b * 2.0f - 1.0f)) / color.a;
+ color.r * 2.0f - 1.0f,
+ color.g * 2.0f - 1.0f,
+ color.b * 2.0f - 1.0f) / color.a;
}
-
+
Vector2[] GetUVData(Mesh mesh, int channel)
{
switch (channel)