Mesh serialization clarifications
This commit is contained in:
@@ -24,7 +24,6 @@ from lxml import etree
|
||||
import numpy as np
|
||||
import transformations
|
||||
|
||||
from hippolyzer.lib.base.datatypes import Vector3
|
||||
from hippolyzer.lib.base.helpers import get_resource_filename
|
||||
from hippolyzer.lib.base.serialization import BufferReader
|
||||
from hippolyzer.lib.base.mesh import (
|
||||
@@ -311,39 +310,11 @@ def fix_weird_bind_matrices(skin_seg: SkinSegmentDict) -> None:
|
||||
if not skin_seg['joint_names']:
|
||||
return
|
||||
|
||||
scale_fixup = Vector3(1, 1, 1)
|
||||
angle_fixup = Vector3(0, 0, 0)
|
||||
have_fixups = False
|
||||
|
||||
# Totally non-scientific method of detecting odd bind matrices based on squinting very,
|
||||
# very hard at a random sample of assets.
|
||||
for joint_name, joint_inv in zip(skin_seg['joint_names'], skin_seg['inverse_bind_matrix']):
|
||||
if not joint_name.startswith("m"):
|
||||
# We can't make very good guesses based on collision volume scales and rotations,
|
||||
# skip anything but the "m" joints.
|
||||
continue
|
||||
joint_mat = llsd_to_mat4(joint_inv)
|
||||
joint_scale, _, joint_angle, _, _ = transformations.decompose_matrix(joint_mat)
|
||||
# If the scale component of an mJointName joint isn't roughly <1,1,1>, we likely have
|
||||
# scaling applied to the inverse bind matrices rather than the bind matrix. Figure out
|
||||
# what the fixup should be so that we can reverse it.
|
||||
if abs(3.0 - sum(joint_scale)) > 0.5:
|
||||
scale_fixup = Vector3(1, 1, 1) / Vector3(*joint_scale)
|
||||
have_fixups = True
|
||||
# I wouldn't expect mJointName joints to be rotated at all in their inverse bind matrices.
|
||||
# Is this a rotation that should've been applied to the bind shape matrix instead?
|
||||
# In any event, all joints are likely rotated by this amount, so calculate the inverse.
|
||||
if abs(sum(joint_angle)) > 0.05:
|
||||
angle_fixup = -Vector3(*joint_angle)
|
||||
have_fixups = True
|
||||
|
||||
if have_fixups:
|
||||
LOG.warning(f"Detected weird matrices in mesh! {scale_fixup!r}, {angle_fixup!r}")
|
||||
# The magnitude of the scales in the inverse bind matrices look very strange.
|
||||
# The bind matrix itself is probably messed up as well, try to fix it.
|
||||
# TODO: put this back in, the previous logic was totally wrong-headed.
|
||||
# DON'T MESS WITH INVERSE TRANSLATION!!!! Only bind shape gets its translation scaled.
|
||||
pass
|
||||
# TODO: calculate the correct inverse bind matrix scale & rotations from avatar_skeleton.xml
|
||||
# definitions. If the rotation and scale factors are the same across all inverse bind matrices then
|
||||
# they can be moved over to the bind shape matrix to keep Blender happy.
|
||||
# Maybe add a scaled / rotated empty as a parent for the armature instead?
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -5,6 +5,8 @@ WIP LLMesh -> glTF converter, for testing eventual glTF -> LLMesh conversion log
|
||||
# * Simple tests
|
||||
# * Round-tripping skinning data from Blender-compatible glTF back to LLMesh (maybe through rig retargeting?)
|
||||
# * Panda3D-glTF viewer for LLMesh? The glTFs seem to work fine in Panda3D-glTF's `gltf-viewer`.
|
||||
# * Check if skew and projection components of transform matrices are ignored in practice as the spec requires.
|
||||
# I suppose this would render some real assets impossible to represent with glTF.
|
||||
|
||||
import dataclasses
|
||||
import math
|
||||
|
||||
@@ -281,6 +281,17 @@ class VertexWeights(se.SerializableBase):
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, reader: se.Reader, ctx=None):
|
||||
# NOTE: normally you'd want to do something like arrange this into a nicely
|
||||
# aligned byte array with zero padding so that you could vectorize the decoding.
|
||||
# In cases where having a vertex with no weights is semantically equivalent to
|
||||
# having a vertex _with_ weights of a value of 0.0 that's fine. This isn't the case
|
||||
# in LL's implementation of mesh:
|
||||
#
|
||||
# https://bitbucket.org/lindenlab/viewer/src/d31a83fb946c49a38376ea3b312b5380d0c8c065/indra/llmath/llvolume.cpp#lines-2560:2628
|
||||
#
|
||||
# Consider the difference between handling of b"\x00\x00\x00\xFF" and b"\xFF" with the above logic.
|
||||
# To simplify round-tripping while preserving those semantics, we don't do a vectorized decode.
|
||||
# I had a vectorized numpy version, but those requirements made everything a bit of a mess.
|
||||
influence_list = []
|
||||
for _ in range(cls.INFLUENCE_LIMIT):
|
||||
joint_idx = reader.read_bytes(1)[0]
|
||||
@@ -357,7 +368,7 @@ LOD_SEGMENT_SERIALIZER = SegmentSerializer({
|
||||
se.QuantizedNumPyArray(se.NumPyArray(se.BytesGreedy(), LE_U16, 2), 0.0, 1.0),
|
||||
Vector2,
|
||||
),
|
||||
# Normals have a static domain between -1 and 1, so just use that.
|
||||
# Normals have a static domain between -1 and 1, so we just use that rather than 0.0 - 1.0.
|
||||
"Normal": VecListAdapter(
|
||||
se.QuantizedNumPyArray(se.NumPyArray(se.BytesGreedy(), LE_U16, 3), -1.0, 1.0),
|
||||
Vector3,
|
||||
|
||||
@@ -40,6 +40,8 @@ class TestMesh(unittest.TestCase):
|
||||
writer.write(serializer, reader.read(serializer))
|
||||
second_buf = writer.copy_buffer()
|
||||
self.assertEqual(first_buf, second_buf)
|
||||
# Dates may not round-trip correctly, but length should always be the same
|
||||
self.assertEqual(len(first_buf), len(self.slm_bytes))
|
||||
|
||||
def test_serialize_raw_segments(self):
|
||||
serializer = LLMeshSerializer(include_raw_segments=True)
|
||||
|
||||
Reference in New Issue
Block a user