Ejemplo n.º 1
0
 def __init__(self, buffer: Union[bytearray, bytes, str]):
     self.buffer = buffer
     self.stream = None
     self.version = 800
     self.name = ''
     self.animation_file = ""
     self.extent = Extent()
     self.blend_time = 0
     self.sequences = []
     self.global_sequences = []
     self.materials = []
     self.textures = []
     self.texture_animations = []
     self.geosets = []
     self.geoset_animations = []
     self.bones = []
     self.lights = []
     self.helpers = []
     self.attachments = []
     self.pivot_points = []
     self.particle_emitters = []
     self.particle_emitters2 = []
     self.particle_emitters_popcorn = []
     self.ribbon_emitters = []
     self.cameras = []
     self.event_objects = []
     self.collision_shapes = []
     self.face_effects = []
     self.bind_pose = []
     self.unknown_chunks = []
     self.mdl_num_of_chunk = []
     self.mdl_comment = [""]
Ejemplo n.º 2
0
 def __init__(self):
     self.name = ''
     self.interval = [0, 0]
     self.move_speed = 0
     self.flags = 0
     self.rarity = 0
     self.sync_point = 0
     self.extent = Extent()
Ejemplo n.º 3
0
 def __init__(self):
     self.vertices = None
     self.normals = None
     self.face_type_groups = None
     self.face_groups = []
     self.faces = None
     self.vertex_groups = None
     self.matrix_groups = None
     self.matrix_indices = None
     self.material_id = 0
     self.selection_group = 0
     self.selection_flags = 0
     self.lod = -1
     self.lod_name = ''
     self.extent = Extent()
     self.sequence_extents = []
     self.tangents = []
     self.skin = []
     self.uv_sets = []
Ejemplo n.º 4
0
 def read_mdx(self, stream: BinaryStream, version: int):
     stream.read_uint32()
     stream.skip(4)  # VRTX TAG
     self.vertices = stream.read_float32_array(stream.read_uint32() * 3)
     stream.skip(4)  # NRMS TAG
     self.normals = stream.read_float32_array(stream.read_uint32() * 3)
     stream.skip(4)  # PTYP TAG
     self.face_type_groups = stream.read_uint32_array(stream.read_uint32())
     stream.skip(4)  # PCNT TAG
     self.face_groups = stream.read_uint32_array(stream.read_uint32())
     stream.skip(4)  # PVTX TAG
     self.faces = stream.read_uint16_array(stream.read_uint32())
     stream.skip(4)  # GNDX TAG
     self.vertex_groups = stream.read_uint8_array(stream.read_uint32())
     stream.skip(4)  # MTGC TAG
     self.matrix_groups = stream.read_uint32_array(stream.read_uint32())
     stream.skip(4)  # MATS TAG
     self.matrix_indices = stream.read_uint32_array(stream.read_uint32())
     self.material_id = stream.read_uint32()
     self.selection_group = stream.read_uint32()
     self.selection_flags = stream.read_uint32()
     if version > 800:
         self.lod = stream.read_int32()
         self.lod_name = stream.read(80)
     self.extent.read_mdx(stream)
     for i in range(stream.read_uint32()):
         extent = Extent()
         extent.read_mdx(stream)
         self.sequence_extents.append(extent)
     if version > 800:
         if stream.peek(4) == 'TANG':
             stream.skip(4)
             self.tangents = stream.read_float32_array(
                 stream.read_uint32() * 4)
         if stream.peek(4) == 'SKIN':
             stream.skip(4)
             self.skin = stream.read_uint8_array(stream.read_uint32())
     stream.skip(4)
     for i in range(stream.read_uint32()):
         stream.skip(4)
         self.uv_sets.append(
             stream.read_float32_array(stream.read_uint32() * 2))
Ejemplo n.º 5
0
class Sequence(object):
    def __init__(self):
        self.name = ''
        self.interval = [0, 0]
        self.move_speed = 0
        self.flags = 0
        self.rarity = 0
        self.sync_point = 0
        self.extent = Extent()

    def read_mdx(self, stream: BinaryStream):
        self.name = stream.read(80)
        self.interval = stream.read_uint32_array(2)
        self.move_speed = stream.read_float32()
        self.flags = stream.read_uint32()
        self.rarity = stream.read_float32()
        self.sync_point = stream.read_uint32()
        self.extent.read_mdx(stream)

    def write_mdx(self, stream: BinaryStream):
        stream.write(self.name)
        stream.skip(80 - len(self.name))
        stream.write_uint32_array(self.interval)
        stream.write_float32(self.move_speed)
        stream.write_uint32(self.flags)
        stream.write_float32(self.rarity)
        stream.write_uint32(self.sync_point)
        self.extent.write_mdx(stream)

    def read_mdl(self, stream: TokenStream):
        self.name = stream.read()
        for token in stream.read_block():
            if token == 'Interval':
                self.interval = stream.read_vector(2)
            elif token == 'NonLooping':
                self.flags = 1
            elif token == 'MoveSpeed':
                self.move_speed = stream.read_float()
            elif token == 'Rarity':
                self.rarity = stream.read_float()
            elif token == 'MinimumExtent':
                self.extent.min = stream.read_vector(3)
            elif token == 'MaximumExtent':
                self.extent.max = stream.read_vector(3)
            elif token == 'BoundsRadius':
                self.extent.bounds_radius = stream.read_float()
            else:
                raise TokenStreamError('Sequence', token)

    def write_mdl(self, stream: TokenStream, version=None):
        stream.start_object_block('Anim', self.name)
        stream.write_vector_attrib('Interval', self.interval)
        if self.flags == 1:
            stream.write_flag('NonLooping')
        if self.move_speed != 0:
            stream.write_number_attrib('MoveSpeed', self.move_speed)
        if self.rarity != 0:
            stream.write_number_attrib('Rarity', self.rarity)
        self.extent.write_mdl(stream)
        stream.end_block()
Ejemplo n.º 6
0
class Geoset(object):
    def __init__(self):
        self.vertices = None
        self.normals = None
        self.face_type_groups = None
        self.face_groups = []
        self.faces = None
        self.vertex_groups = None
        self.matrix_groups = None
        self.matrix_indices = None
        self.material_id = 0
        self.selection_group = 0
        self.selection_flags = 0
        self.lod = -1
        self.lod_name = ''
        self.extent = Extent()
        self.sequence_extents = []
        self.tangents = []
        self.skin = []
        self.uv_sets = []

    def read_mdx(self, stream: BinaryStream, version: int):
        stream.read_uint32()
        stream.skip(4)  # VRTX TAG
        self.vertices = stream.read_float32_array(stream.read_uint32() * 3)
        stream.skip(4)  # NRMS TAG
        self.normals = stream.read_float32_array(stream.read_uint32() * 3)
        stream.skip(4)  # PTYP TAG
        self.face_type_groups = stream.read_uint32_array(stream.read_uint32())
        stream.skip(4)  # PCNT TAG
        self.face_groups = stream.read_uint32_array(stream.read_uint32())
        stream.skip(4)  # PVTX TAG
        self.faces = stream.read_uint16_array(stream.read_uint32())
        stream.skip(4)  # GNDX TAG
        self.vertex_groups = stream.read_uint8_array(stream.read_uint32())
        stream.skip(4)  # MTGC TAG
        self.matrix_groups = stream.read_uint32_array(stream.read_uint32())
        stream.skip(4)  # MATS TAG
        self.matrix_indices = stream.read_uint32_array(stream.read_uint32())
        self.material_id = stream.read_uint32()
        self.selection_group = stream.read_uint32()
        self.selection_flags = stream.read_uint32()
        if version > 800:
            self.lod = stream.read_int32()
            self.lod_name = stream.read(80)
        self.extent.read_mdx(stream)
        for i in range(stream.read_uint32()):
            extent = Extent()
            extent.read_mdx(stream)
            self.sequence_extents.append(extent)
        if version > 800:
            if stream.peek(4) == 'TANG':
                stream.skip(4)
                self.tangents = stream.read_float32_array(
                    stream.read_uint32() * 4)
            if stream.peek(4) == 'SKIN':
                stream.skip(4)
                self.skin = stream.read_uint8_array(stream.read_uint32())
        stream.skip(4)
        for i in range(stream.read_uint32()):
            stream.skip(4)
            self.uv_sets.append(
                stream.read_float32_array(stream.read_uint32() * 2))

    def write_mdx(self, stream: BinaryStream, version: int):
        stream.write_uint32(self.get_byte_length(version))
        stream.write('VRTX')
        stream.write_uint32(len(self.vertices) // 3)
        stream.write_float32_array(self.vertices)
        stream.write('NRMS')
        stream.write_uint32(len(self.normals) // 3)
        stream.write_float32_array(self.normals)
        stream.write('PTYP')
        stream.write_uint32(len(self.face_type_groups))
        stream.write_uint32_array(self.face_type_groups)
        stream.write('PCNT')
        stream.write_uint32(len(self.face_groups))
        stream.write_uint32_array(self.face_groups)
        stream.write('PVTX')
        stream.write_uint32(len(self.faces))
        stream.write_uint16_array(self.faces)
        stream.write('GNDX')
        stream.write_uint32(len(self.vertex_groups))
        stream.write_uint8_array(self.vertex_groups)
        stream.write('MTGC')
        stream.write_uint32(len(self.matrix_groups))
        stream.write_uint32_array(self.matrix_groups)
        stream.write('MATS')
        stream.write_uint32(len(self.matrix_indices))
        stream.write_uint32_array(self.matrix_indices)
        stream.write_uint32(self.material_id)
        stream.write_uint32(self.selection_group)
        stream.write_uint32(self.selection_flags)
        if version > 800:
            stream.write_int32(self.lod)
            stream.write(self.lod_name)
            stream.skip(80 - len(self.lod_name))
        self.extent.write_mdx(stream)
        stream.write_uint32(len(self.sequence_extents))
        for sequence_extent in self.sequence_extents:
            sequence_extent.write_mdx(stream)
        if version > 800:
            if len(self.tangents):
                stream.write('TANG')
                stream.write_uint32(len(self.tangents) // 4)
                stream.write_float32_array(self.tangents)
            if len(self.skin):
                stream.write('SKIN')
                stream.write_uint32(len(self.skin))
                stream.write_uint8_array(self.skin)
        stream.write('UVAS')
        stream.write_uint32(len(self.uv_sets))
        for uv_set in self.uv_sets:
            stream.write('UVBS')
            stream.write_uint32(len(uv_set) // 2)
            stream.write_float32_array(uv_set)

    def read_mdl(self, stream: TokenStream):
        for token in stream.read_block():
            if token == 'Vertices':
                self.vertices = stream.read_vectors_block(stream.read_int(), 3)
            elif token == 'Normals':
                self.normals = stream.read_vectors_block(stream.read_int(), 3)
            elif token == 'TVertices':
                self.uv_sets.append(
                    stream.read_vectors_block(stream.read_int(), 2))
            elif token == 'VertexGroup':
                vertex_groups = []
                for vertex_group in stream.read_block():
                    vertex_groups.append(int(vertex_group))
                self.vertex_groups = vertex_groups
            elif token == 'Tangents':
                self.tangents = stream.read_vectors_block(stream.read_int(), 4)
            elif token == 'SkinWeights':
                self.skin = stream.read_vector(stream.read_int() * 8)
            elif token == 'Faces':
                self.face_type_groups = []
                vectors = stream.read_int()
                count = stream.read_int()
                stream.read()
                stream.read()
                self.faces = stream.read_single_vector_block(count)
                self.face_groups = [count]
                stream.read()
            elif token == 'Groups':
                indices = []
                groups = []
                stream.read_int()
                stream.read_int()
                for _ in stream.read_block():
                    size = 0
                    for index in stream.read_block():
                        indices.append(int(index))
                        size += 1
                    groups.append(size)
                self.matrix_indices = indices
                self.matrix_groups = groups
            elif token == 'MinimumExtent':
                self.extent.min = stream.read_vector(3)
            elif token == 'MaximumExtent':
                self.extent.max = stream.read_vector(3)
            elif token == 'BoundsRadius':
                self.extent.bounds_radius = stream.read_float()
            elif token == 'Anim':
                extent = Extent()
                for token in stream.read_block():
                    if token == 'MinimumExtent':
                        extent.min = stream.read_vector(3)
                    elif token == 'MaximumExtent':
                        extent.max = stream.read_vector(3)
                    elif token == 'BoundsRadius':
                        extent.bounds_radius = stream.read_float()
                self.sequence_extents.append(extent)
            elif token == 'MaterialID':
                self.material_id = stream.read_int()
            elif token == 'SelectionGroup':
                self.selection_group = stream.read_int()
            elif token == 'Unselectable':
                self.selection_flags = 4
            elif token == 'LevelOfDetail':
                self.lod = stream.read_int()
            elif token == 'Name':
                self.lod_name = stream.read()
            else:
                raise TokenStreamError('Geoset', token)

    def write_mdl(self, stream: TokenStream, version=None):
        stream.start_block('Geoset', None)
        stream.write_vector_array_block('Vertices', self.vertices, 3)
        stream.write_vector_array_block('Normals', self.normals, 3)
        for uv_set in self.uv_sets:
            stream.write_vector_array_block('TVertices', uv_set, 2)
        if version > 800 or len(self.tangents):
            stream.write_vector_array_block('Tangents', self.tangents, 4)
        if version > 800 or len(self.skin):
            stream.start_block('SkinWeights', len(self.skin) // 8)
            for i in range(0, len(self.skin), 8):
                stream.write_line(
                    f'{", ".join([str(a) for a in self.skin[i:i + 8]])+","}')
        else:
            stream.start_block('VertexGroup', None)
            for i in range(len(self.vertex_groups)):
                stream.write_line(f'{self.vertex_groups[i]},')
        stream.end_block()
        stream.start_block('Faces', [str(1), str(len(self.faces))])
        stream.start_block('Triangles', None)
        stream.write_vector(self.faces)
        stream.end_block()
        stream.end_block()
        stream.start_block(
            'Groups',
            [str(len(self.matrix_groups)),
             str(len(self.matrix_indices))])
        index = 0
        for group_size in self.matrix_groups:
            stream.write_vector_attrib(
                'Matrices', self.matrix_indices[index:index + group_size])
            index += group_size
        stream.end_block()
        self.extent.write_mdl(stream)
        for sequence_extent in self.sequence_extents:
            stream.start_block('Anim', None)
            sequence_extent.write_mdl(stream)
            stream.end_block()
        stream.write_number_attrib('MaterialID', self.material_id)
        stream.write_number_attrib('SelectionGroup', self.selection_group)
        if self.selection_flags == 4:
            stream.write_flag('Unselectable')
        if version > 800:
            stream.write_number_attrib('LevelOfDetail', self.lod)
            if len(self.lod_name):
                stream.write_string_attrib('Name', self.lod_name)
        stream.end_block()

    def get_byte_length(self, version):
        size = (120 + (len(self.vertices) * 4) + (len(self.normals) * 4) +
                (len(self.face_type_groups) * 4) +
                (len(self.face_groups) * 4) + (len(self.faces) * 2) +
                (len(self.vertex_groups) * 1) + (len(self.matrix_groups) * 4) +
                (len(self.matrix_indices) * 4) +
                ((len(self.sequence_extents)) * 28))
        if version > 800:
            size += 84
        if len(self.tangents):
            size += 8 + (4 * len(self.tangents))
        if len(self.skin):
            size += 8 + (1 * len(self.skin))
        for idx, uv_set in enumerate(self.uv_sets):
            size += 8 + (4 * len(self.uv_sets[idx]))
        return size
Ejemplo n.º 7
0
 def read_mdl(self, stream: TokenStream):
     for token in stream.read_block():
         if token == 'Vertices':
             self.vertices = stream.read_vectors_block(stream.read_int(), 3)
         elif token == 'Normals':
             self.normals = stream.read_vectors_block(stream.read_int(), 3)
         elif token == 'TVertices':
             self.uv_sets.append(
                 stream.read_vectors_block(stream.read_int(), 2))
         elif token == 'VertexGroup':
             vertex_groups = []
             for vertex_group in stream.read_block():
                 vertex_groups.append(int(vertex_group))
             self.vertex_groups = vertex_groups
         elif token == 'Tangents':
             self.tangents = stream.read_vectors_block(stream.read_int(), 4)
         elif token == 'SkinWeights':
             self.skin = stream.read_vector(stream.read_int() * 8)
         elif token == 'Faces':
             self.face_type_groups = []
             vectors = stream.read_int()
             count = stream.read_int()
             stream.read()
             stream.read()
             self.faces = stream.read_single_vector_block(count)
             self.face_groups = [count]
             stream.read()
         elif token == 'Groups':
             indices = []
             groups = []
             stream.read_int()
             stream.read_int()
             for _ in stream.read_block():
                 size = 0
                 for index in stream.read_block():
                     indices.append(int(index))
                     size += 1
                 groups.append(size)
             self.matrix_indices = indices
             self.matrix_groups = groups
         elif token == 'MinimumExtent':
             self.extent.min = stream.read_vector(3)
         elif token == 'MaximumExtent':
             self.extent.max = stream.read_vector(3)
         elif token == 'BoundsRadius':
             self.extent.bounds_radius = stream.read_float()
         elif token == 'Anim':
             extent = Extent()
             for token in stream.read_block():
                 if token == 'MinimumExtent':
                     extent.min = stream.read_vector(3)
                 elif token == 'MaximumExtent':
                     extent.max = stream.read_vector(3)
                 elif token == 'BoundsRadius':
                     extent.bounds_radius = stream.read_float()
             self.sequence_extents.append(extent)
         elif token == 'MaterialID':
             self.material_id = stream.read_int()
         elif token == 'SelectionGroup':
             self.selection_group = stream.read_int()
         elif token == 'Unselectable':
             self.selection_flags = 4
         elif token == 'LevelOfDetail':
             self.lod = stream.read_int()
         elif token == 'Name':
             self.lod_name = stream.read()
         else:
             raise TokenStreamError('Geoset', token)
Ejemplo n.º 8
0
class Model(object):
    def __init__(self, buffer: Union[bytearray, bytes, str]):
        self.buffer = buffer
        self.stream = None
        self.version = 800
        self.name = ''
        self.animation_file = ""
        self.extent = Extent()
        self.blend_time = 0
        self.sequences = []
        self.global_sequences = []
        self.materials = []
        self.textures = []
        self.texture_animations = []
        self.geosets = []
        self.geoset_animations = []
        self.bones = []
        self.lights = []
        self.helpers = []
        self.attachments = []
        self.pivot_points = []
        self.particle_emitters = []
        self.particle_emitters2 = []
        self.particle_emitters_popcorn = []
        self.ribbon_emitters = []
        self.cameras = []
        self.event_objects = []
        self.collision_shapes = []
        self.face_effects = []
        self.bind_pose = []
        self.unknown_chunks = []
        self.mdl_num_of_chunk = []
        self.mdl_comment = [""]

    @staticmethod
    def get_current_date_with_time_format():
        data = datetime.now(tzlocal())
        data = str(data)
        date = data[:19]
        time_format = data[26:]
        prefix = 'UTC'
        return f'{date} {prefix}{time_format}'

    def load(self):
        if isinstance(self.buffer, str):
            self.stream = TokenStream(buffer=self.buffer)
            self.load_mdl(self.stream)
        elif isinstance(self.buffer, (bytearray, bytes)):
            self.stream = BinaryStream(self.buffer)
            self.load_mdx()

    def load_mdx(self):
        stream = self.stream
        if stream.read(4) != 'MDLX':
            raise ValueError('Wrong Magic Number')
        tag = ''
        size = ''
        try:
            while stream.remaining > 0:
                tag = stream.read(4)
                size = stream.read_uint32()
                if tag == 'VERS':
                    self.load_version_chunk(stream)
                elif tag == 'MODL':
                    self.load_model_chunk(stream)
                elif tag == 'SEQS':
                    self.load_static_objects(self.sequences, Sequence, stream,
                                             int(size / 132))
                elif tag == 'GLBS':
                    self.load_global_sequence_chunk(stream, size)
                elif tag == 'MTLS':
                    self.load_dynamic_objects(self.materials, Material, stream,
                                              size)
                elif tag == 'TEXS':
                    self.load_static_objects(self.textures, Texture, stream,
                                             int(size / 268))
                elif tag == 'TXAN':
                    self.load_dynamic_objects(self.texture_animations,
                                              TextureAnimation, stream, size)
                elif tag == 'GEOS':
                    self.load_dynamic_objects(self.geosets, Geoset, stream,
                                              size)
                elif tag == 'GEOA':
                    self.load_dynamic_objects(self.geoset_animations,
                                              GeosetAnimation, stream, size)
                elif tag == 'BONE':
                    self.load_dynamic_objects(self.bones, Bone, stream, size)
                elif tag == 'LITE':
                    self.load_dynamic_objects(self.lights, Light, stream, size)
                elif tag == 'HELP':
                    self.load_dynamic_objects(self.helpers, Helper, stream,
                                              size)
                elif tag == 'ATCH':
                    self.load_dynamic_objects(self.attachments, Attachment,
                                              stream, size)
                elif tag == 'PIVT':
                    self.load_pivot_point_chunk(stream, size)
                elif tag == 'PREM':
                    self.load_dynamic_objects(self.particle_emitters,
                                              ParticleEmitter, stream, size)
                elif tag == 'PRE2':
                    self.load_dynamic_objects(self.particle_emitters2,
                                              ParticleEmitter2, stream, size)
                elif tag == 'CORN':
                    self.load_dynamic_objects(self.particle_emitters_popcorn,
                                              ParticleEmitterPopcorn, stream,
                                              size)
                elif tag == 'RIBB':
                    self.load_dynamic_objects(self.ribbon_emitters,
                                              RibbonEmitter, stream, size)
                elif tag == 'CAMS':
                    self.load_dynamic_objects(self.cameras, Camera, stream,
                                              size)
                elif tag == 'EVTS':
                    self.load_dynamic_objects(self.event_objects, EventObject,
                                              stream, size)
                elif tag == 'CLID':
                    self.load_dynamic_objects(self.collision_shapes,
                                              CollisionShape, stream, size)
                elif tag == 'FAFX':
                    self.load_static_objects(self.face_effects, FaceEffect,
                                             stream, int(size / 340))
                elif tag == 'BPOS':
                    self.load_bind_pose_chunk(stream, size)
                else:
                    self.unknown_chunks.append(UnknownChunk(stream, size, tag))
        except Exception as e:
            print(f'MDLX parsing error in {tag}: {e}')

    def load_version_chunk(self, stream):
        self.version = stream.read_uint32()

    def load_model_chunk(self, stream):
        self.name = stream.read(80)
        self.animation_file = stream.read(260)
        self.extent = Extent()
        self.extent.read_mdx(stream)
        self.blend_time = stream.read_uint32()

    def load_static_objects(self, out: Union[list, tuple], class_name,
                            stream: BinaryStream, count: int):
        for i in range(count):
            obj = class_name()
            obj.read_mdx(stream)
            out.append(obj)

    def load_global_sequence_chunk(self, stream: BinaryStream, size: int):
        l = int(size / 4)
        i = 0
        while i < l:
            self.global_sequences.append(stream.read_uint32())
            i += 1

    def load_dynamic_objects(self, out: Union[list, tuple], class_name,
                             stream: BinaryStream, size):
        end = stream.index + size
        while stream.index < end:
            obj = class_name()
            obj.read_mdx(stream, self.version)
            out.append(obj)

    def load_pivot_point_chunk(self, stream: BinaryStream, size: int):
        for i in range(int(size / 12)):
            self.pivot_points.append(stream.read_float32_array(3))

    def load_bind_pose_chunk(self, stream: BinaryStream, size: int):
        for i in range(stream.read_uint32()):
            self.bind_pose.append(stream.read_float32_array(12))

    def save_mdx(self):
        buffer = bytearray(self.get_byte_length())
        stream = BinaryStream(buffer)
        stream.write('MDLX')
        self.save_version_chunk(stream)
        self.save_model_chunk(stream)
        self.save_static_object_chunk(stream, 'SEQS', self.sequences, 132)
        self.save_global_sequence_chunk(stream)
        self.save_dynamic_object_chunk(stream, 'MTLS', self.materials)
        self.save_static_object_chunk(stream, 'TEXS', self.textures, 268)
        self.save_dynamic_object_chunk(stream, 'TXAN', self.texture_animations)
        self.save_dynamic_object_chunk(stream, 'GEOS', self.geosets)
        self.save_dynamic_object_chunk(stream, 'GEOA', self.geoset_animations)
        self.save_dynamic_object_chunk(stream, 'BONE', self.bones)
        self.save_dynamic_object_chunk(stream, 'LITE', self.lights)
        self.save_dynamic_object_chunk(stream, 'HELP', self.helpers)
        self.save_dynamic_object_chunk(stream, 'ATCH', self.attachments)
        self.save_pivot_point_chunk(stream)
        self.save_dynamic_object_chunk(stream, 'PREM', self.particle_emitters)
        self.save_dynamic_object_chunk(stream, 'PRE2', self.particle_emitters2)
        if self.version > 800:
            self.save_dynamic_object_chunk(stream, 'CORN',
                                           self.particle_emitters_popcorn)
        self.save_dynamic_object_chunk(stream, 'RIBB', self.ribbon_emitters)
        self.save_dynamic_object_chunk(stream, 'CAMS', self.cameras)
        self.save_dynamic_object_chunk(stream, 'EVTS', self.event_objects)
        self.save_dynamic_object_chunk(stream, 'CLID', self.collision_shapes)
        if self.version > 800:
            self.save_static_object_chunk(stream, 'FAFX', self.face_effects,
                                          340)
            self.save_bind_pose_chunk(stream)
        for chunk in self.unknown_chunks:
            chunk.write_mdx(stream)
        return stream.buffer_array

    def save_version_chunk(self, stream: BinaryStream):
        stream.write('VERS')
        stream.write_uint32(4)
        stream.write_uint32(self.version)

    def save_model_chunk(self, stream: BinaryStream):
        stream.write('MODL')
        stream.write_uint32(372)
        stream.write(self.name)
        stream.skip(80 - len(self.name))
        stream.write(self.animation_file)
        stream.skip(260 - len(self.animation_file))
        self.extent.write_mdx(stream)
        stream.write_uint32(self.blend_time)

    def save_static_object_chunk(self, stream: BinaryStream, name, objects,
                                 size):
        if len(objects):
            stream.write(name)
            stream.write_uint32(len(objects) * size)
            for obj in objects:
                obj.write_mdx(stream)

    def save_global_sequence_chunk(self, stream: BinaryStream):
        if len(self.global_sequences):
            stream.write('GLBS')
            stream.write_uint32(len(self.global_sequences) * 4)
            for global_sequence in self.global_sequences:
                stream.write_uint32(global_sequence)

    def save_dynamic_object_chunk(self, stream: BinaryStream, name, objects):
        if len(objects):
            stream.write(name)
            stream.write_uint32(self.get_objects_byte_length(objects))
            for obj in objects:
                obj.write_mdx(stream, self.version)

    def save_pivot_point_chunk(self, stream: BinaryStream):
        if len(self.pivot_points):
            stream.write('PIVT')
            stream.write_uint32(len(self.pivot_points) * 12)
            for pivot_point in self.pivot_points:
                stream.write_float32_array(pivot_point)

    def save_bind_pose_chunk(self, stream: BinaryStream):
        if len(self.bind_pose):
            stream.write('BPOS')
            stream.write_uint32(4 + len(self.bind_pose) * 48)
            stream.write_uint32(len(self.bind_pose))
            for matrix in self.bind_pose:
                stream.write_float32_array(matrix)

    def load_mdl(self, stream):
        token = ''
        while token is not None:
            if token is None:
                break
            else:
                token = stream.read_token()
            if token == 'Version':
                self.load_version_block(stream)
            elif token == 'Model':
                self.load_model_block(stream)
            elif token == 'Sequences':
                self.load_numbered_object_block(self.sequences, Sequence,
                                                'Anim', stream)
            elif token == 'GlobalSequences':
                self.load_global_sequence_block(stream)
            elif token == 'Textures':
                self.load_numbered_object_block(self.textures, Texture,
                                                'Bitmap', stream)
            elif token == 'Materials':
                self.load_numbered_object_block(self.materials, Material,
                                                'Material', stream)
            elif token == 'TextureAnims':
                self.load_numbered_object_block(self.texture_animations,
                                                TextureAnimation,
                                                'TVertexAnim', stream)
            elif token == 'Geoset':
                self.load_object(self.geosets, Geoset, stream)
            elif token == 'GeosetAnim':
                self.load_object(self.geoset_animations, GeosetAnimation,
                                 stream)
            elif token == 'Bone':
                self.load_object(self.bones, Bone, stream)
            elif token == 'Light':
                self.load_object(self.lights, Light, stream)
            elif token == 'Helper':
                self.load_object(self.helpers, Helper, stream)
            elif token == 'Attachment':
                self.load_object(self.attachments, Attachment, stream)
            elif token == 'PivotPoints':
                self.load_pivot_point_block(stream)
            elif token == 'ParticleEmitter':
                self.load_object(self.particle_emitters, ParticleEmitter,
                                 stream)
            elif token == 'ParticleEmitter2':
                self.load_object(self.particle_emitters2, ParticleEmitter2,
                                 stream)
            elif token == 'ParticleEmitterPopcorn':
                self.load_object(self.particle_emitters_popcorn,
                                 ParticleEmitterPopcorn, stream)
            elif token == 'RibbonEmitter':
                self.load_object(self.ribbon_emitters, RibbonEmitter, stream)
            elif token == 'Camera':
                self.load_object(self.cameras, Camera, stream)
            elif token == 'EventObject':
                self.load_object(self.event_objects, EventObject, stream)
            elif token == 'CollisionShape':
                self.load_object(self.collision_shapes, CollisionShape, stream)
            elif token == 'FaceFX':
                self.load_object(self.face_effects, FaceEffect, stream)
            elif token == 'BindPose':
                self.load_bind_pose_block(stream)
            elif token is None:
                break
            else:
                raise ValueError(f'Unsupported block: {token}')

    def load_version_block(self, stream: TokenStream):
        for token in stream.read_block():
            if token == 'FormatVersion':
                self.version = stream.read_int()
            else:
                raise TokenStreamError('Version', token)

    def load_model_block(self, stream: TokenStream):
        self.name = stream.read()
        for token in stream.read_block():
            if token.startswith('Num'):
                stream.read()
            elif token == 'BlendTime':
                self.blend_time = stream.read_int()
            elif token == 'MinimumExtent':
                self.extent.min = stream.read_vector(3)
            elif token == 'MaximumExtent':
                self.extent.max = stream.read_vector(3)
            elif token == 'BoundsRadius':
                self.extent.bounds_radius = stream.read_float()
            elif token == 'AnimationFile':
                self.animation_file = stream.read()
            else:
                raise TokenStreamError('Model', token)

    def load_numbered_object_block(self, out, class_name, name,
                                   stream: TokenStream):
        stream.read()
        for token in stream.read_block():
            if token == name:
                obj = class_name()
                obj.read_mdl(stream)
                out.append(obj)
            else:
                raise TokenStreamError(name, token)

    def load_global_sequence_block(self, stream: TokenStream):
        stream.read()
        for token in stream.read_block():
            if token == 'Duration':
                self.global_sequences.append(stream.read_int())
            else:
                raise TokenStreamError('GlobalSequences', token)

    def load_object(self, out, class_name, stream: TokenStream):
        obj = class_name()
        obj.read_mdl(stream)
        out.append(obj)

    def load_pivot_point_block(self, stream: TokenStream):
        count = stream.read_int()
        stream.read()
        for i in range(count):
            self.pivot_points.append(stream.read_vector(3))
        stream.read()

    def load_bind_pose_block(self, stream: TokenStream):
        for token in stream.read_block():
            if token == 'Matrices':
                matrices = stream.read_int()
                stream.read()
                for i in range(matrices):
                    self.bind_pose.append(stream.read_vector(12))
                stream.read()
            else:
                raise TokenStreamError('BindPose', token)

    def save_mdl(self, comment=None):
        stream = TokenStream()
        tmp_file = open('tmp.data', 'a')
        stream.tmp_file = tmp_file
        if comment:
            #  comment[-1:] += Model.get_current_date_with_time_format()
            stream.write_comment(comment)
        self.save_version_block(stream)
        self.save_model_block(stream)
        self.save_static_objects_block(stream, 'Sequences', self.sequences)
        self.save_global_sequence_block(stream)
        self.save_static_objects_block(stream, 'Textures', self.textures)
        self.save_static_objects_block(stream, 'Materials', self.materials)
        self.save_static_objects_block(stream, 'TextureAnims',
                                       self.texture_animations)
        self.save_objects(stream, self.geosets)
        self.save_objects(stream, self.geoset_animations)
        self.save_objects(stream, self.bones)
        self.save_objects(stream, self.lights)
        self.save_objects(stream, self.helpers)
        self.save_objects(stream, self.attachments)
        self.save_pivot_point_block(stream)
        self.save_objects(stream, self.particle_emitters)
        self.save_objects(stream, self.particle_emitters2)
        if self.version > 800:
            self.save_objects(stream, self.particle_emitters_popcorn)
        self.save_objects(stream, self.ribbon_emitters)
        self.save_objects(stream, self.cameras)
        self.save_objects(stream, self.event_objects)
        self.save_objects(stream, self.collision_shapes)
        if self.version > 800:
            self.save_objects(stream, self.face_effects)
            self.save_bind_pose_block(stream)
        stream.tmp_file.close()
        with open('tmp.data', 'r') as f:
            stream.buffer = f.read()
        os.remove('tmp.data')
        return stream.buffer

    def save_version_block(self, stream: TokenStream):
        stream.start_block('Version', None)
        stream.write_number_attrib('FormatVersion', self.version)
        stream.end_block()

    def save_model_block(self, stream: TokenStream):
        stream.start_object_block('Model', self.name)
        self.mdl_num_of_chunk = [
            len(self.geosets),
            len(self.geoset_animations),
            len(self.helpers),
            len(self.lights),
            len(self.bones),
            len(self.attachments),
            len(self.particle_emitters),
            len(self.particle_emitters2),
            len(self.particle_emitters_popcorn),
            len(self.ribbon_emitters),
            len(self.event_objects),
            len(self.face_effects)
        ]
        stream.write_number_attrib('NumGeosets', self.mdl_num_of_chunk[0])
        stream.write_number_attrib('NumGeosetAnims', self.mdl_num_of_chunk[1])
        stream.write_number_attrib('NumHelpers', self.mdl_num_of_chunk[2])
        stream.write_number_attrib('NumLights', self.mdl_num_of_chunk[3])
        stream.write_number_attrib('NumBones', self.mdl_num_of_chunk[4])
        stream.write_number_attrib('NumAttachments', self.mdl_num_of_chunk[5])
        stream.write_number_attrib('NumParticleEmitters',
                                   self.mdl_num_of_chunk[6])
        stream.write_number_attrib('NumParticleEmitters2',
                                   self.mdl_num_of_chunk[7])
        if self.version > 800:
            stream.write_number_attrib('NumParticleEmittersPopcorn',
                                       self.mdl_num_of_chunk[8])
        stream.write_number_attrib('NumRibbonEmitters',
                                   self.mdl_num_of_chunk[9])
        stream.write_number_attrib('NumEvents', self.mdl_num_of_chunk[10])
        if self.version > 800:
            stream.write_number_attrib('NumFaceFX', self.mdl_num_of_chunk[11])
        stream.write_number_attrib('BlendTime', self.blend_time)
        self.extent.write_mdl(stream)
        if len(self.animation_file):
            stream.write_string_attrib('AnimationFile', self.animation_file)
        stream.end_block()

    def save_static_objects_block(self, stream: TokenStream, name, objects):
        if len(objects):
            stream.start_block(name, len(objects))
            for obj in objects:
                obj.write_mdl(stream, version=self.version)
            stream.end_block()

    def save_global_sequence_block(self, stream: TokenStream):
        if len(self.global_sequences):
            stream.start_block('GlobalSequences', len(self.global_sequences))
            for global_sequence in self.global_sequences:
                stream.write_number_attrib('Duration', global_sequence)
            stream.end_block()

    def save_objects(self, stream: TokenStream, objects):
        for obj in objects:
            obj.write_mdl(stream, version=self.version)

    def save_pivot_point_block(self, stream: TokenStream):
        if len(self.pivot_points):
            stream.start_block('PivotPoints', len(self.pivot_points))
            for pivot_point in self.pivot_points:
                stream.write_vector(pivot_point)
            stream.end_block()

    def save_bind_pose_block(self, stream: TokenStream):
        if len(self.bind_pose):
            stream.start_block('BindPose', None)
            stream.start_block('Matrices', len(self.bind_pose))
            for matrix in self.bind_pose:
                stream.write_vector(matrix)
            stream.end_block()
            stream.end_block()

    def get_byte_length(self):
        size = 396
        size += self.get_static_objects_chunk_byte_length(self.sequences, 132)
        size += self.get_static_objects_chunk_byte_length(
            self.global_sequences, 4)
        size += self.get_dynamic_objects_chunk_byte_length(self.materials)
        size += self.get_static_objects_chunk_byte_length(self.textures, 268)
        size += self.get_dynamic_objects_chunk_byte_length(
            self.texture_animations)
        size += self.get_dynamic_objects_chunk_byte_length(self.geosets)
        size += self.get_dynamic_objects_chunk_byte_length(
            self.geoset_animations)
        size += self.get_dynamic_objects_chunk_byte_length(self.bones)
        size += self.get_dynamic_objects_chunk_byte_length(self.lights)
        size += self.get_dynamic_objects_chunk_byte_length(self.helpers)
        size += self.get_dynamic_objects_chunk_byte_length(self.attachments)
        size += self.get_static_objects_chunk_byte_length(
            self.pivot_points, 12)
        size += self.get_dynamic_objects_chunk_byte_length(
            self.particle_emitters)
        size += self.get_dynamic_objects_chunk_byte_length(
            self.particle_emitters2)
        if self.version > 800:
            size += self.get_dynamic_objects_chunk_byte_length(
                self.particle_emitters_popcorn)
        size += self.get_dynamic_objects_chunk_byte_length(
            self.ribbon_emitters)
        size += self.get_dynamic_objects_chunk_byte_length(self.cameras)
        size += self.get_dynamic_objects_chunk_byte_length(self.event_objects)
        size += self.get_dynamic_objects_chunk_byte_length(
            self.collision_shapes)
        if self.version > 800:
            size += self.get_static_objects_chunk_byte_length(
                self.face_effects, 340)
            size += self.get_bind_pose_chunk_byte_length()
        size += self.get_objects_byte_length(self.unknown_chunks)
        return size

    def get_objects_byte_length(self, objects):
        size = 0
        for obj in objects:
            size += obj.get_byte_length(version=self.version)
        return size

    def get_dynamic_objects_chunk_byte_length(self, objects):
        if len(objects):
            return 8 + self.get_objects_byte_length(objects)
        return 0

    def get_static_objects_chunk_byte_length(self, objects, size):
        if len(objects):
            return 8 + len(objects) * size
        return 0

    def get_bind_pose_chunk_byte_length(self):
        if len(self.bind_pose):
            return 12 + len(self.bind_pose) * 48
        return 0
Ejemplo n.º 9
0
 def load_model_chunk(self, stream):
     self.name = stream.read(80)
     self.animation_file = stream.read(260)
     self.extent = Extent()
     self.extent.read_mdx(stream)
     self.blend_time = stream.read_uint32()