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


[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) # i8 flags, f32 pos[3], i8 bone id, u8 reference count fmt = f"{F.I8}{F.F32 * 3}{F.I8}{F.U8}" reference_count = 0xFF # ? necessary only for optimization, calculation too expensive for mesh in self.data.scene.meshes: for index, xyz in enumerate(mesh.vertices): bone_id = ( mesh.links_ids.astype(F.I8)[index][0] if self._skeleton_presented else ModelDefaults.ROOT_BONE_ID ) self._writeb(fmt, 0, *xyz, bone_id, reference_count) def _add_triangles(self): # polygons count self._writecount("polygons", self.data.scene.total_polygons, MAX_TRIANGLES) # u16 flags, u16 indices[3] # f32 normals[3][3], f32 uv1 u[3], f32 uv1 v[3] # u8 smoothing group, u8 group index fmt = f"{F.U16 * 4}{F.F32 * 15}{F.U8 * 2}" offset = 0 for index, mesh in enumerate(self.data.scene.meshes): for abc in mesh.polygons: normals = [i for vertex in abc for i in mesh.normals[vertex]] uv = np.concatenate([mesh.uv1[abc][:, 0], mesh.uv1[abc][:, 1]], dtype=F.F32) indices = (abc + offset).astype(F.U16) self._writeb(fmt, 0, *indices, *normals, *uv, 1, index) 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 self._writeb(f"{count}{F.U16}", *np.arange(count, dtype=F.U16) + offset) # indices 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])