TextureEntry -> TextureEntryCollection, improve .realize()

The "TextureEntry" name from the message template is kind of a
misnomer, the field actually includes multiple TextureEntries.
This commit is contained in:
Salad Dais
2022-07-17 00:45:20 +00:00
parent 59ec99809a
commit f34bb42dcb
5 changed files with 39 additions and 25 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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,

View File

@@ -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")

View File

@@ -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):