Source code for scfile.formats.ms3d.encoder

import numpy as np

from scfile.consts import FileSignature, ModelDefaults
from scfile.core import FileEncoder, ModelContent
from scfile.enums import ByteOrder, F, FileFormat
from scfile.structures.models import transforms as T

from .io import Ms3dFileIO


VERSION = 4
COMMENTS_VERSION = 1
VERTEX_EXTRA_VERSION = 1

MAX_VERTICES = 0xFFFF
MAX_TRIANGLES = 0xFFFF

VERTEX_DTYPE = np.dtype(
    [
        ("flags", "i1"),
        ("position", "<f4", 3),
        ("bone_id", "i1"),
        ("reference_count", "u1"),
    ]
)

TRIANGLE_DTYPE = np.dtype(
    [
        ("flags", "<u2"),
        ("indices", "<u2", 3),
        ("normals", "<f4", (3, 3)),
        ("u", "<f4", 3),
        ("v", "<f4", 3),
        ("smoothing_group", "u1"),
        ("group_index", "u1"),
    ]
)


[docs] class Ms3dEncoder(FileEncoder[ModelContent], Ms3dFileIO): format = FileFormat.MS3D signature = FileSignature.MS3D order = ByteOrder.LITTLE transforms = [T.unique_names, T.skeleton_to_local]
[docs] def serialize(self): self._writeb(F.I32, VERSION) self._add_vertices() self._add_triangles() self._add_groups() self._add_materials() self._add_bones() self._add_comments() self._add_links()
def _add_vertices(self): # vertices count self._writecount("vertices", self.data.scene.total_vertices, MAX_VERTICES) reference_count = 0xFF # ? necessary only for optimization, calculation too expensive for mesh in self.data.scene.meshes: vertices = np.empty(len(mesh.vertices), dtype=VERTEX_DTYPE) vertices["flags"] = 0 vertices["position"] = mesh.vertices vertices["bone_id"] = mesh.links_ids[:, 0] if self._skeleton_presented else ModelDefaults.ROOT_BONE_ID vertices["reference_count"] = reference_count self.write(vertices.tobytes()) def _add_triangles(self): # polygons count self._writecount("polygons", self.data.scene.total_polygons, MAX_TRIANGLES) offset = 0 for index, mesh in enumerate(self.data.scene.meshes): triangles = np.empty(len(mesh.polygons), dtype=TRIANGLE_DTYPE) uv = mesh.uv1[mesh.polygons] triangles["flags"] = 0 triangles["indices"] = mesh.polygons + offset triangles["normals"] = mesh.normals[mesh.polygons] triangles["u"] = uv[:, :, 0] triangles["v"] = uv[:, :, 1] triangles["smoothing_group"] = 1 triangles["group_index"] = index self.write(triangles.tobytes()) offset += len(mesh.vertices) def _add_groups(self): self._writeb(F.U16, len(self.data.scene.meshes)) # groups count offset = 0 for index, mesh in enumerate(self.data.scene.meshes): self._writeb(F.U8, 0) # flags self._writefixedstring(mesh.name) # group name count = len(mesh.polygons) self._writeb(F.U16, count) # triangles count indices = np.arange(offset, offset + count, dtype="<u2") self.write(indices.tobytes()) self._writeb(F.I8, index) # material index offset += count def _add_materials(self): self._writeb(F.U16, len(self.data.scene.meshes)) # materials count # f32 ambient[4], diffuse[4], specular[4], emissive[4] (RGBA) # f32 shininess, f32 transparency, i8 mode fmt = f"{F.F32 * 18}{F.I8}" # rgba templates empty = (0.0, 0.0, 0.0, 1.0) diffuse = (0.8, 0.8, 0.8, 1.0) for mesh in self.data.scene.meshes: self._writefixedstring(mesh.material) # material name self._writeb(fmt, *empty, *diffuse, *empty, *empty, 0.0, 1.0, 1) self._writenull(size=128) # texture self._writenull(size=128) # alphamap def _add_bones(self): # f32 fps, f32 frame, f32 framesCount, u16 bonesCount fmt = f"{F.F32 * 3}{F.U16}" self._writeb(fmt, 24, 1, 30, len(self.data.scene.skeleton.bones)) for bone in self.data.scene.skeleton.bones: self._writeb(F.U8, 0) # flags self._writefixedstring(bone.name) # bone name parent = self.data.scene.skeleton.bones[bone.parent_id] parent_name = parent.name if bone.parent_id != ModelDefaults.ROOT_BONE_ID else "" self._writefixedstring(parent_name) # parent name # f32 bone rotation[3], f32 bone position[3] # u16 keyframes rotations, u16 keyframes transitions fmt = f"{F.F32 * 6}{F.U16 * 2}" qx, qy, qz, qw = bone.quaternion self._writeb(fmt, qx, qy, qz, *bone.position, 0, 0) def _add_comments(self): self._writeb(F.I32, COMMENTS_VERSION) # comments version fmt = F.U32 * 4 # u32 group, u32 material, u32 joints, u32 model self._writeb(fmt, 0, 0, 0, 0) # comments count def _add_links(self): self._writeb(F.I32, VERTEX_EXTRA_VERSION) # vertex extra version # i8 ids[3], u8 weights[3] fmt = f"{F.I8 * 3}{F.U8 * 3}" for mesh in self.data.scene.meshes: links_ids = mesh.links_ids.astype(F.I8) links_weights = (mesh.links_weights * 255).astype(F.U8) for ids, weights in zip(links_ids, links_weights): self._writeb(fmt, *ids[:3], *weights[:3])