From f34bb42dcb6e7257d3b74b6fb90faedbda4a6243 Mon Sep 17 00:00:00 2001 From: Salad Dais Date: Sun, 17 Jul 2022 00:45:20 +0000 Subject: [PATCH] TextureEntry -> TextureEntryCollection, improve .realize() The "TextureEntry" name from the message template is kind of a misnomer, the field actually includes multiple TextureEntries. --- addon_examples/monochrome.py | 4 ++-- addon_examples/pixel_artist.py | 4 ++-- hippolyzer/lib/base/objects.py | 4 ++-- hippolyzer/lib/base/templates.py | 41 +++++++++++++++++++++----------- tests/proxy/test_templates.py | 11 +++++---- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/addon_examples/monochrome.py b/addon_examples/monochrome.py index 6a78c48..377157f 100644 --- a/addon_examples/monochrome.py +++ b/addon_examples/monochrome.py @@ -27,7 +27,7 @@ from mitmproxy.http import HTTPFlow from hippolyzer.lib.base.datatypes import UUID from hippolyzer.lib.base.jp2_utils import BufferedJp2k from hippolyzer.lib.base.multiprocessing_utils import ParentProcessWatcher -from hippolyzer.lib.base.templates import TextureEntry +from hippolyzer.lib.base.templates import TextureEntryCollection from hippolyzer.lib.proxy.addon_utils import AssetAliasTracker, BaseAddon, GlobalProperty, AddonProcess from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow from hippolyzer.lib.base.message.message import Message @@ -148,7 +148,7 @@ class MonochromeAddon(BaseAddon): message["RegionInfo"][field_name] = tracker.get_alias_uuid(val) @staticmethod - def _make_te_monochrome(tracker: AssetAliasTracker, parsed_te: TextureEntry): + def _make_te_monochrome(tracker: AssetAliasTracker, parsed_te: TextureEntryCollection): # Need a deepcopy because TEs are owned by the ObjectManager # and we don't want to change the canonical view. parsed_te = copy.deepcopy(parsed_te) diff --git a/addon_examples/pixel_artist.py b/addon_examples/pixel_artist.py index d5963f4..b4a28af 100644 --- a/addon_examples/pixel_artist.py +++ b/addon_examples/pixel_artist.py @@ -14,7 +14,7 @@ from PySide6.QtGui import QImage from hippolyzer.lib.base.datatypes import UUID, Vector3, Quaternion from hippolyzer.lib.base.helpers import to_chunks from hippolyzer.lib.base.message.message import Block, Message -from hippolyzer.lib.base.templates import ObjectUpdateFlags, PCode, MCode, MultipleObjectUpdateFlags, TextureEntry +from hippolyzer.lib.base.templates import ObjectUpdateFlags, PCode, MCode, MultipleObjectUpdateFlags, TextureEntryCollection from hippolyzer.lib.client.object_manager import ObjectEvent, UpdateType from hippolyzer.lib.proxy.addon_utils import BaseAddon from hippolyzer.lib.proxy.addons import AddonManager @@ -124,7 +124,7 @@ class PixelArtistAddon(BaseAddon): y = i // width obj = created_prims[prim_idx] # Set a blank texture on all faces - te = TextureEntry() + te = TextureEntryCollection() te.Textures[None] = UUID('5748decc-f629-461c-9a36-a35a221fe21f') # Set the prim color to the color from the pixel te.Color[None] = pixel_color diff --git a/hippolyzer/lib/base/objects.py b/hippolyzer/lib/base/objects.py index 569c740..2b0ae94 100644 --- a/hippolyzer/lib/base/objects.py +++ b/hippolyzer/lib/base/objects.py @@ -71,7 +71,7 @@ class Object(recordclass.datatuple): # type: ignore ProfileBegin: Optional[int] = None ProfileEnd: Optional[int] = None ProfileHollow: Optional[int] = None - TextureEntry: Optional[tmpls.TextureEntry] = None + TextureEntry: Optional[tmpls.TextureEntryCollection] = None TextureAnim: Optional[tmpls.TextureAnim] = None NameValue: Optional[Any] = None Data: Optional[Any] = None @@ -294,7 +294,7 @@ def normalize_object_update_compressed_data(data: bytes): compressed["SoundRadius"] = 0.0 compressed["Sound"] = UUID() if compressed["TextureEntry"] is None: - compressed["TextureEntry"] = tmpls.TextureEntry() + compressed["TextureEntry"] = tmpls.TextureEntryCollection() object_data = { "PSBlock": ps_block.value, diff --git a/hippolyzer/lib/base/templates.py b/hippolyzer/lib/base/templates.py index f4d2da9..3980151 100644 --- a/hippolyzer/lib/base/templates.py +++ b/hippolyzer/lib/base/templates.py @@ -1063,6 +1063,23 @@ class PackedTERotation(se.QuantizedFloat): @dataclasses.dataclass class TextureEntry: + """Representation of a TE for a single face. Not sent over the wire.""" + Textures: UUID = UUID('89556747-24cb-43ed-920b-47caed15465f') + Color: bytes = b"\xff\xff\xff\xff" + ScalesS: float = 1.0 + ScalesT: float = 1.0 + OffsetsS: float = 0.0 + OffsetsT: float = 0.0 + # In radians + Rotation: float = 0.0 + MediaFlags: Optional[MediaFlags] = None + BasicMaterials: Optional[BasicMaterials] = None + Glow: int = 0 + Materials: UUID = UUID.ZERO + + +@dataclasses.dataclass +class TextureEntryCollection: Textures: Dict[_TE_FIELD_KEY, UUID] = _te_field( # Plywood texture se.UUID, first=True, default=UUID('89556747-24cb-43ed-920b-47caed15465f')) @@ -1080,6 +1097,7 @@ class TextureEntry: MEDIA_FLAGS, default_factory=lambda: MediaFlags(WebPage=False, TexGen=TexGen.DEFAULT, _Unused=0), ) + # TODO: dequantize Glow: Dict[_TE_FIELD_KEY, int] = _te_field(se.U8, default=0) Materials: Dict[_TE_FIELD_KEY, UUID] = _te_field(se.UUID, optional=True, default=UUID.ZERO) @@ -1087,21 +1105,16 @@ class TextureEntry: """Return `self` regardless of whether this is lazy wrapped object or not""" return self - def realize(self, num_faces: int): + def realize(self, num_faces: int) -> List[TextureEntry]: """ Turn the "default" vs "exception cases" wire format TE representation to per-face lookups - - Makes it easier to just index into a list of offsets with a face number. - Returns something like: - { - "OffsetsS": [0.5, 0.2, ...], - ... - } + Makes it easier to get all TE details associated with a specific face """ - as_dict = {} + as_dicts = [dict() for _ in range(num_faces)] for key, vals in dataclasses.asdict(self).items(): - # Fill all of the faces in this key with the default value stored in the TE - key_arr = as_dict[key] = [vals[None]] * num_faces + # Fill give all faces the default value for this key + for te in as_dicts: + te[key] = vals[None] # Walk over the exception cases and replace the default value for face_nums, val in vals.items(): # Default case already handled @@ -1110,11 +1123,11 @@ class TextureEntry: for face_num in face_nums: if face_num >= num_faces: raise ValueError(f"Bad value for num_faces? {face_num} >= {num_faces}") - key_arr[face_num] = val - return as_dict + as_dicts[face_num][key] = val + return [TextureEntry(**x) for x in as_dicts] -TE_SERIALIZER = se.Dataclass(TextureEntry) +TE_SERIALIZER = se.Dataclass(TextureEntryCollection) @se.subfield_serializer("ObjectUpdate", "ObjectData", "TextureEntry") diff --git a/tests/proxy/test_templates.py b/tests/proxy/test_templates.py index 6a7afba..dd54a79 100644 --- a/tests/proxy/test_templates.py +++ b/tests/proxy/test_templates.py @@ -4,7 +4,7 @@ import unittest import hippolyzer.lib.base.serialization as se from hippolyzer.lib.base.datatypes import UUID from hippolyzer.lib.base.message.message_formatting import HumanMessageSerializer -from hippolyzer.lib.base.templates import TextureEntrySubfieldSerializer, TEFaceBitfield, TextureEntry, PackedTERotation +from hippolyzer.lib.base.templates import TextureEntrySubfieldSerializer, TEFaceBitfield, TextureEntryCollection, PackedTERotation EXAMPLE_TE = b'\x89UgG$\xcbC\xed\x92\x0bG\xca\xed\x15F_\x08\xca*\x98:\x18\x02,\r\xf4\x1e\xc6\xf5\x91\x01]\x83\x014' \ b'\x00\x90i+\x10\x80\xa1\xaa\xa2g\x11o\xa8]\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00\x80?' \ @@ -19,9 +19,10 @@ class TemplateTests(unittest.TestCase): self.assertEqual(EXAMPLE_TE, serialized) def test_realize_te(self): - deserialized: TextureEntry = TextureEntrySubfieldSerializer.deserialize(None, EXAMPLE_TE) - realized = deserialized.realize(num_faces=4) - self.assertEqual(UUID('ca2a983a-1802-2c0d-f41e-c6f591015d83'), realized["Textures"][3]) + deserialized: TextureEntryCollection = TextureEntrySubfieldSerializer.deserialize(None, EXAMPLE_TE) + realized = deserialized.realize(4) + self.assertEqual(UUID('ca2a983a-1802-2c0d-f41e-c6f591015d83'), realized[3].Textures) + self.assertEqual(UUID('89556747-24cb-43ed-920b-47caed15465f'), realized[1].Textures) with self.assertRaises(ValueError): deserialized.realize(3) @@ -72,7 +73,7 @@ class TemplateTests(unittest.TestCase): self.assertEqual(pod_te, deser) def test_textureentry_defaults(self): - te = TextureEntry() + te = TextureEntryCollection() self.assertEqual(UUID('89556747-24cb-43ed-920b-47caed15465f'), te.Textures[None]) def test_textureentry_rotation_packing(self):