diff --git a/gltflib/models/attributes.py b/gltflib/models/attributes.py index 56b4ccd..95ff9df 100644 --- a/gltflib/models/attributes.py +++ b/gltflib/models/attributes.py @@ -1,20 +1,36 @@ -from dataclasses import dataclass -from dataclasses_json import dataclass_json -from typing import Optional +import copy +import json - -@dataclass_json -@dataclass +# Attributes is a special case, it can have custom attrs class Attributes: - """ - Helper type for describing the attributes of a primitive. Each property corresponds to mesh attribute semantic and - each value is the index of the accessor containing the attribute's data. - """ - POSITION: Optional[int] = None - NORMAL: Optional[int] = None - TANGENT: Optional[int] = None - TEXCOORD_0: Optional[int] = None - TEXCOORD_1: Optional[int] = None - COLOR_0: Optional[int] = None - JOINTS_0: Optional[int] = None - WEIGHTS_0: Optional[int] = None + def __init__( + self, + POSITION=None, + NORMAL=None, + TANGENT=None, + TEXCOORD_0=None, + TEXCOORD_1=None, + COLOR_0: int = None, + JOINTS_0: int = None, + WEIGHTS_0=None, + *args, + **kwargs + ): + self.POSITION = POSITION + self.NORMAL = NORMAL + self.TANGENT = TANGENT + self.TEXCOORD_0 = TEXCOORD_0 + self.TEXCOORD_1 = TEXCOORD_1 + self.COLOR_0 = COLOR_0 + self.JOINTS_0 = JOINTS_0 + self.WEIGHTS_0 = WEIGHTS_0 + for key, value in kwargs.items(): + setattr(self, key, value) + + def __repr__(self): + return self.__class__.__qualname__ + '(' + ', '.join([f"{f}={v}" for f, v in self.__dict__.items()]) + ')' + + def to_json(self, *args, **kwargs): + data = copy.deepcopy(self.__dict__) + return json.dumps(data) + diff --git a/gltflib/models/gltf_model.py b/gltflib/models/gltf_model.py index a1f1917..21bcedf 100644 --- a/gltflib/models/gltf_model.py +++ b/gltflib/models/gltf_model.py @@ -1,7 +1,8 @@ import json from dataclasses import dataclass, asdict from dataclasses_json import DataClassJsonMixin -from typing import List, Optional +from dataclasses_json.core import _decode_dataclass +from typing import List, Optional, Type from ..utils import del_none from .accessor import Accessor from .animation import Animation @@ -18,6 +19,7 @@ from .scene import Scene from .skin import Skin from .texture import Texture +from .attributes import Attributes @dataclass @@ -43,3 +45,37 @@ class GLTFModel(DataClassJsonMixin, BaseModel): def to_json(self, **kwargs): data = del_none(asdict(self)) return json.dumps(data, **kwargs) + + @classmethod + def from_json( + cls: Type['GLTFModel'], + s: str, + *, + parse_float=None, + parse_int=None, + parse_constant=None, + infer_missing=False, + **kw + ) -> 'GLTFModel': + + init_kwargs = json.loads( + s, + parse_float=parse_float, + parse_int=parse_int, + parse_constant=parse_constant, + **kw + ) + + result = _decode_dataclass(cls, init_kwargs, infer_missing) + for mesh in result.meshes: + for primitive in mesh.primitives: + raw_attributes = primitive.attributes + if raw_attributes: + attributes = Attributes(**raw_attributes) + primitive.attributes = attributes + + raw_targets = primitive.targets + if raw_targets: + primitive.targets = [Attributes(**target) for target in raw_targets] + + return result