~ubuntu-branches/ubuntu/trusty/blender/trusty

« back to all changes in this revision

Viewing changes to release/scripts/addons/io_scene_ms3d/ms3d_spec.py

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2013-03-06 12:08:47 UTC
  • mfrom: (1.5.1) (14.1.8 experimental)
  • Revision ID: package-import@ubuntu.com-20130306120847-frjfaryb2zrotwcg
Tags: 2.66a-1ubuntu1
* Resynchronize with Debian (LP: #1076930, #1089256, #1052743, #999024,
  #1122888, #1147084)
* debian/control:
  - Lower build-depends on libavcodec-dev since we're not
    doing the libav9 transition in Ubuntu yet

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# ##### BEGIN GPL LICENSE BLOCK #####
 
2
#
 
3
#  This program is free software; you can redistribute it and/or
 
4
#  modify it under the terms of the GNU General Public License
 
5
#  as published by the Free Software Foundation; either version 2
 
6
#  of the License, or (at your option) any later version.
 
7
#
 
8
#  This program is distributed in the hope that it will be useful,
 
9
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
#  GNU General Public License for more details.
 
12
#
 
13
#  You should have received a copy of the GNU General Public License
 
14
#  along with this program; if not, write to the Free Software Foundation,
 
15
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
16
#
 
17
# ##### END GPL LICENSE BLOCK #####
 
18
 
 
19
# <pep8 compliant>
 
20
 
 
21
###############################################################################
 
22
#234567890123456789012345678901234567890123456789012345678901234567890123456789
 
23
#--------1---------2---------3---------4---------5---------6---------7---------
 
24
 
 
25
 
 
26
# ##### BEGIN COPYRIGHT BLOCK #####
 
27
#
 
28
# initial script copyright (c)2011-2013 Alexander Nussbaumer
 
29
#
 
30
# ##### END COPYRIGHT BLOCK #####
 
31
 
 
32
 
 
33
from struct import (
 
34
        pack,
 
35
        unpack,
 
36
        )
 
37
from sys import (
 
38
        exc_info,
 
39
        )
 
40
from codecs import (
 
41
        register_error,
 
42
        )
 
43
 
 
44
###############################################################################
 
45
#
 
46
# MilkShape 3D 1.8.5 File Format Specification
 
47
#
 
48
# all specifications were taken from SDK 1.8.5
 
49
#
 
50
# some additional specifications were taken from
 
51
# MilkShape 3D Viewer v2.0 (Nov 06 2007) - msMesh.h
 
52
#
 
53
 
 
54
 
 
55
###############################################################################
 
56
#
 
57
# sizes
 
58
#
 
59
 
 
60
class Ms3dSpec:
 
61
    ###########################################################################
 
62
    #
 
63
    # max values
 
64
    #
 
65
    MAX_VERTICES = 65534 # 0..65533; note: (65534???, 65535???)
 
66
    MAX_TRIANGLES = 65534 # 0..65533; note: (65534???, 65535???)
 
67
    MAX_GROUPS = 255 # 1..255; note: (0 default group)
 
68
    MAX_MATERIALS = 128 # 0..127; note: (-1 no material)
 
69
    MAX_JOINTS = 128 # 0..127; note: (-1 no joint)
 
70
    MAX_SMOOTH_GROUP = 32 # 0..32; note: (0 no smoothing group)
 
71
    MAX_TEXTURE_FILENAME_SIZE = 128
 
72
 
 
73
    ###########################################################################
 
74
    #
 
75
    # flags
 
76
    #
 
77
    FLAG_NONE = 0
 
78
    FLAG_SELECTED = 1
 
79
    FLAG_HIDDEN = 2
 
80
    FLAG_SELECTED2 = 4
 
81
    FLAG_DIRTY = 8
 
82
    FLAG_ISKEY = 16 # additional spec from [2]
 
83
    FLAG_NEWLYCREATED = 32 # additional spec from [2]
 
84
    FLAG_MARKED = 64 # additional spec from [2]
 
85
 
 
86
    FLAG_TEXTURE_NONE = 0x00
 
87
    FLAG_TEXTURE_COMBINE_ALPHA = 0x20
 
88
    FLAG_TEXTURE_HAS_ALPHA = 0x40
 
89
    FLAG_TEXTURE_SPHERE_MAP = 0x80
 
90
 
 
91
    MODE_TRANSPARENCY_SIMPLE = 0
 
92
    MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF = 1
 
93
    MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES = 2
 
94
 
 
95
 
 
96
    ###########################################################################
 
97
    #
 
98
    # values
 
99
    #
 
100
    HEADER = "MS3D000000"
 
101
 
 
102
 
 
103
    ## TEST_STR = 'START@€@µ@²@³@©@®@¶@ÿ@A@END.bmp'
 
104
    ## TEST_RAW = b'START@\x80@\xb5@\xb2@\xb3@\xa9@\xae@\xb6@\xff@A@END.bmp\x00'
 
105
    ##
 
106
    STRING_MS3D_REPLACE = 'use_ms3d_replace'
 
107
    STRING_ENCODING = "ascii" # wrong encoding (too limited), but there is an UnicodeEncodeError issue, that prevent using the correct one for the moment
 
108
    ##STRING_ENCODING = "cp437" # US, wrong encoding and shows UnicodeEncodeError
 
109
    ##STRING_ENCODING = "cp858" # Europe + €, wrong encoding and shows UnicodeEncodeError
 
110
    ##STRING_ENCODING = "cp1252" # WIN EU, this would be the better codepage, but shows UnicodeEncodeError, on print on system console and writing to file
 
111
    STRING_ERROR = STRING_MS3D_REPLACE
 
112
    ##STRING_ERROR = 'replace'
 
113
    ##STRING_ERROR = 'ignore'
 
114
    ##STRING_ERROR = 'surrogateescape'
 
115
    STRING_TERMINATION = b'\x00'
 
116
    STRING_REPLACE = u'_'
 
117
 
 
118
 
 
119
    ###########################################################################
 
120
    #
 
121
    # min, max, default values
 
122
    #
 
123
    NONE_VERTEX_BONE_ID = -1
 
124
    NONE_GROUP_MATERIAL_INDEX = -1
 
125
 
 
126
    DEFAULT_HEADER = HEADER
 
127
    DEFAULT_HEADER_VERSION = 4
 
128
    DEFAULT_VERTEX_BONE_ID = NONE_VERTEX_BONE_ID
 
129
    DEFAULT_TRIANGLE_SMOOTHING_GROUP = 0
 
130
    DEFAULT_TRIANGLE_GROUP = 0
 
131
    DEFAULT_MATERIAL_MODE = FLAG_TEXTURE_NONE
 
132
    DEFAULT_GROUP_MATERIAL_INDEX = NONE_GROUP_MATERIAL_INDEX
 
133
    DEFAULT_MODEL_JOINT_SIZE = 1.0
 
134
    DEFAULT_MODEL_TRANSPARENCY_MODE = MODE_TRANSPARENCY_SIMPLE
 
135
    DEFAULT_MODEL_ANIMATION_FPS = 25.0
 
136
    DEFAULT_MODEL_SUB_VERSION_COMMENTS = 1
 
137
    DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA = 2
 
138
    DEFAULT_MODEL_SUB_VERSION_JOINT_EXTRA = 1
 
139
    DEFAULT_MODEL_SUB_VERSION_MODEL_EXTRA = 1
 
140
    DEFAULT_FLAGS = FLAG_NONE
 
141
    MAX_MATERIAL_SHININESS = 128
 
142
 
 
143
    # blender default / OpenGL default
 
144
    DEFAULT_MATERIAL_AMBIENT = (0.2, 0.2, 0.2, 1.0)
 
145
    DEFAULT_MATERIAL_DIFFUSE = (0.8, 0.8, 0.8, 1.0)
 
146
    DEFAULT_MATERIAL_SPECULAR = (1.0, 1.0, 1.0, 1.0)
 
147
    DEFAULT_MATERIAL_EMISSIVE = (0.0, 0.0, 0.0, 1.0)
 
148
    DEFAULT_MATERIAL_SHININESS = 12.5
 
149
 
 
150
    DEFAULT_JOINT_COLOR = (0.8, 0.8, 0.8)
 
151
 
 
152
###############################################################################
 
153
#
 
154
# helper class for basic raw io
 
155
#
 
156
class Ms3dIo:
 
157
    # sizes for IO
 
158
    SIZE_BYTE = 1
 
159
    SIZE_SBYTE = 1
 
160
    SIZE_WORD = 2
 
161
    SIZE_DWORD = 4
 
162
    SIZE_FLOAT = 4
 
163
    LENGTH_ID = 10
 
164
    LENGTH_NAME = 32
 
165
    LENGTH_FILENAME = 128
 
166
 
 
167
    PRECISION = 4
 
168
 
 
169
    @staticmethod
 
170
    def read_byte(raw_io):
 
171
        """ read a single byte from raw_io """
 
172
        buffer = raw_io.read(Ms3dIo.SIZE_BYTE)
 
173
        if not buffer:
 
174
            raise EOFError()
 
175
        value = unpack('<B', buffer)[0]
 
176
        return value
 
177
 
 
178
    @staticmethod
 
179
    def write_byte(raw_io, value):
 
180
        """ write a single byte to raw_io """
 
181
        raw_io.write(pack('<B', value))
 
182
 
 
183
    @staticmethod
 
184
    def read_sbyte(raw_io):
 
185
        """ read a single signed byte from raw_io """
 
186
        buffer = raw_io.read(Ms3dIo.SIZE_BYTE)
 
187
        if not buffer:
 
188
            raise EOFError()
 
189
        value = unpack('<b', buffer)[0]
 
190
        return value
 
191
 
 
192
    @staticmethod
 
193
    def write_sbyte(raw_io, value):
 
194
        """ write a single signed byte to raw_io """
 
195
        raw_io.write(pack('<b', value))
 
196
 
 
197
    @staticmethod
 
198
    def read_word(raw_io):
 
199
        """ read a word from raw_io """
 
200
        buffer = raw_io.read(Ms3dIo.SIZE_WORD)
 
201
        if not buffer:
 
202
            raise EOFError()
 
203
        value = unpack('<H', buffer)[0]
 
204
        return value
 
205
 
 
206
    @staticmethod
 
207
    def write_word(raw_io, value):
 
208
        """ write a word to raw_io """
 
209
        raw_io.write(pack('<H', value))
 
210
 
 
211
    @staticmethod
 
212
    def read_dword(raw_io):
 
213
        """ read a double word from raw_io """
 
214
        buffer = raw_io.read(Ms3dIo.SIZE_DWORD)
 
215
        if not buffer:
 
216
            raise EOFError()
 
217
        value = unpack('<I', buffer)[0]
 
218
        return value
 
219
 
 
220
    @staticmethod
 
221
    def write_dword(raw_io, value):
 
222
        """ write a double word to raw_io """
 
223
        raw_io.write(pack('<I', value))
 
224
 
 
225
    @staticmethod
 
226
    def read_float(raw_io):
 
227
        """ read a float from raw_io """
 
228
        buffer = raw_io.read(Ms3dIo.SIZE_FLOAT)
 
229
        if not buffer:
 
230
            raise EOFError()
 
231
        value = unpack('<f', buffer)[0]
 
232
        return value
 
233
 
 
234
    @staticmethod
 
235
    def write_float(raw_io, value):
 
236
        """ write a float to raw_io """
 
237
        raw_io.write(pack('<f', value))
 
238
 
 
239
    @staticmethod
 
240
    def read_array(raw_io, itemReader, count):
 
241
        """ read an array[count] of objects from raw_io, by using a itemReader """
 
242
        value = []
 
243
        for i in range(count):
 
244
            itemValue = itemReader(raw_io)
 
245
            value.append(itemValue)
 
246
        return tuple(value)
 
247
 
 
248
    @staticmethod
 
249
    def write_array(raw_io, itemWriter, count, value):
 
250
        """ write an array[count] of objects to raw_io, by using a itemWriter """
 
251
        for i in range(count):
 
252
            itemValue = value[i]
 
253
            itemWriter(raw_io, itemValue)
 
254
 
 
255
    @staticmethod
 
256
    def read_array2(raw_io, itemReader, count, count2):
 
257
        """ read an array[count][count2] of objects from raw_io,
 
258
            by using a itemReader """
 
259
        value = []
 
260
        for i in range(count):
 
261
            itemValue = Ms3dIo.read_array(raw_io, itemReader, count2)
 
262
            value.append(tuple(itemValue))
 
263
        return value
 
264
 
 
265
    @staticmethod
 
266
    def write_array2(raw_io, itemWriter, count, count2, value):
 
267
        """ write an array[count][count2] of objects to raw_io,
 
268
            by using a itemWriter """
 
269
        for i in range(count):
 
270
            itemValue = value[i]
 
271
            Ms3dIo.write_array(raw_io, itemWriter, count2, itemValue)
 
272
 
 
273
 
 
274
    @staticmethod
 
275
    def ms3d_replace(exc):
 
276
        """ http://www.python.org/dev/peps/pep-0293/ """
 
277
        if isinstance(exc, UnicodeEncodeError):
 
278
            return ((exc.end-exc.start)*Ms3dSpec.STRING_REPLACE, exc.end)
 
279
        elif isinstance(exc, UnicodeDecodeError):
 
280
            return (Ms3dSpec.STRING_REPLACE, exc.end)
 
281
        elif isinstance(exc, UnicodeTranslateError):
 
282
            return ((exc.end-exc.start)*Ms3dSpec.STRING_REPLACE, exc.end)
 
283
        else:
 
284
            raise TypeError("can't handle %s" % exc.__name__)
 
285
 
 
286
    @staticmethod
 
287
    def read_string(raw_io, length):
 
288
        """ read a string of a specific length from raw_io """
 
289
        buffer = raw_io.read(length)
 
290
        if not buffer:
 
291
            raise EOFError()
 
292
        eol = buffer.find(Ms3dSpec.STRING_TERMINATION)
 
293
        register_error(Ms3dSpec.STRING_MS3D_REPLACE, Ms3dIo.ms3d_replace)
 
294
        s = buffer[:eol].decode(encoding=Ms3dSpec.STRING_ENCODING, errors=Ms3dSpec.STRING_ERROR)
 
295
        return s
 
296
 
 
297
    @staticmethod
 
298
    def write_string(raw_io, length, value):
 
299
        """ write a string of a specific length to raw_io """
 
300
        register_error(Ms3dSpec.STRING_MS3D_REPLACE, Ms3dIo.ms3d_replace)
 
301
        buffer = value.encode(encoding=Ms3dSpec.STRING_ENCODING, errors=Ms3dSpec.STRING_ERROR)
 
302
        if not buffer:
 
303
            buffer = bytes()
 
304
        raw_io.write(pack('<{}s'.format(length), buffer))
 
305
        return
 
306
 
 
307
 
 
308
###############################################################################
 
309
#
 
310
# multi complex types
 
311
#
 
312
 
 
313
###############################################################################
 
314
class Ms3dHeader:
 
315
    """ Ms3dHeader """
 
316
    __slots__ = (
 
317
            'id',
 
318
            'version',
 
319
            )
 
320
 
 
321
    def __init__(
 
322
            self,
 
323
            default_id=Ms3dSpec.DEFAULT_HEADER,
 
324
            default_version=Ms3dSpec.DEFAULT_HEADER_VERSION
 
325
            ):
 
326
        self.id = default_id
 
327
        self.version = default_version
 
328
 
 
329
    def __repr__(self):
 
330
        return "\n<id='{}', version={}>".format(
 
331
            self.id,
 
332
            self.version
 
333
            )
 
334
 
 
335
    def __hash__(self):
 
336
        return hash(self.id) ^ hash(self.version)
 
337
 
 
338
    def __eq__(self, other):
 
339
        return ((self is not None) and (other is not None)
 
340
                and (self.id == other.id)
 
341
                and (self.version == other.version))
 
342
 
 
343
    def read(self, raw_io):
 
344
        self.id = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_ID)
 
345
        self.version = Ms3dIo.read_dword(raw_io)
 
346
        return self
 
347
 
 
348
    def write(self, raw_io):
 
349
        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_ID, self.id)
 
350
        Ms3dIo.write_dword(raw_io, self.version)
 
351
 
 
352
 
 
353
###############################################################################
 
354
class Ms3dVertex:
 
355
    """ Ms3dVertex """
 
356
    """
 
357
    __slots__ was taking out,
 
358
    to be able to inject additional attributes during runtime
 
359
    __slots__ = (
 
360
            'flags',
 
361
            'bone_id',
 
362
            'reference_count',
 
363
            '_vertex',
 
364
            '_vertex_ex_object', # Ms3dVertexEx
 
365
            )
 
366
    """
 
367
 
 
368
    def __init__(
 
369
            self,
 
370
            default_flags=Ms3dSpec.DEFAULT_FLAGS,
 
371
            default_vertex=(0.0, 0.0, 0.0),
 
372
            default_bone_id=Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
 
373
            default_reference_count=0,
 
374
            default_vertex_ex_object=None, # Ms3dVertexEx
 
375
            ):
 
376
        self.flags = default_flags
 
377
        self._vertex = default_vertex
 
378
        self.bone_id = default_bone_id
 
379
        self.reference_count = default_reference_count
 
380
 
 
381
        if default_vertex_ex_object is None:
 
382
            default_vertex_ex_object = Ms3dVertexEx2()
 
383
            # Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA = 2
 
384
        self._vertex_ex_object = default_vertex_ex_object
 
385
        # Ms3dVertexEx
 
386
 
 
387
    def __repr__(self):
 
388
        return "\n<flags={}, vertex=({:.{p}f}, {:.{p}f}, {:.{p}f}), bone_id={},"\
 
389
                " reference_count={}>".format(
 
390
                self.flags,
 
391
                self._vertex[0],
 
392
                self._vertex[1],
 
393
                self._vertex[2],
 
394
                self.bone_id,
 
395
                self.reference_count,
 
396
                p=Ms3dIo.PRECISION
 
397
                )
 
398
 
 
399
    def __hash__(self):
 
400
        return (hash(self.vertex)
 
401
                #^ hash(self.flags)
 
402
                #^ hash(self.bone_id)
 
403
                #^ hash(self.reference_count)
 
404
                )
 
405
 
 
406
    def __eq__(self, other):
 
407
        return ((self.vertex == other.vertex)
 
408
                #and (self.flags == other.flags)
 
409
                #and (self.bone_id == other.bone_id)
 
410
                #and (self.reference_count == other.reference_count)
 
411
                )
 
412
 
 
413
 
 
414
    @property
 
415
    def vertex(self):
 
416
        return self._vertex
 
417
 
 
418
    @property
 
419
    def vertex_ex_object(self):
 
420
        return self._vertex_ex_object
 
421
 
 
422
 
 
423
    def read(self, raw_io):
 
424
        self.flags = Ms3dIo.read_byte(raw_io)
 
425
        self._vertex = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
 
426
        self.bone_id = Ms3dIo.read_sbyte(raw_io)
 
427
        self.reference_count = Ms3dIo.read_byte(raw_io)
 
428
        return self
 
429
 
 
430
    def write(self, raw_io):
 
431
        Ms3dIo.write_byte(raw_io, self.flags)
 
432
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.vertex)
 
433
        Ms3dIo.write_sbyte(raw_io, self.bone_id)
 
434
        Ms3dIo.write_byte(raw_io, self.reference_count)
 
435
 
 
436
 
 
437
###############################################################################
 
438
class Ms3dTriangle:
 
439
    """ Ms3dTriangle """
 
440
    """
 
441
    __slots__ was taking out,
 
442
    to be able to inject additional attributes during runtime
 
443
    __slots__ = (
 
444
            'flags',
 
445
            'smoothing_group',
 
446
            'group_index',
 
447
            '_vertex_indices',
 
448
            '_vertex_normals',
 
449
            '_s',
 
450
            '_t',
 
451
            )
 
452
    """
 
453
 
 
454
    def __init__(
 
455
            self,
 
456
            default_flags=Ms3dSpec.DEFAULT_FLAGS,
 
457
            default_vertex_indices=(0, 0, 0),
 
458
            default_vertex_normals=(
 
459
                    (0.0, 0.0, 0.0),
 
460
                    (0.0, 0.0, 0.0),
 
461
                    (0.0, 0.0, 0.0)),
 
462
            default_s=(0.0, 0.0, 0.0),
 
463
            default_t=(0.0, 0.0, 0.0),
 
464
            default_smoothing_group=Ms3dSpec.DEFAULT_TRIANGLE_SMOOTHING_GROUP,
 
465
            default_group_index=Ms3dSpec.DEFAULT_TRIANGLE_GROUP
 
466
            ):
 
467
        self.flags = default_flags
 
468
        self._vertex_indices = default_vertex_indices
 
469
        self._vertex_normals = default_vertex_normals
 
470
        self._s = default_s
 
471
        self._t = default_t
 
472
        self.smoothing_group = default_smoothing_group
 
473
        self.group_index = default_group_index
 
474
 
 
475
    def __repr__(self):
 
476
        return "\n<flags={}, vertex_indices={}, vertex_normals=(({:.{p}f}, "\
 
477
                "{:.{p}f}, {:.{p}f}), ({:.{p}f}, {:.{p}f}, {:.{p}f}), ({:.{p}f}, "\
 
478
                "{:.{p}f}, {:.{p}f})), s=({:.{p}f}, {:.{p}f}, {:.{p}f}), "\
 
479
                "t=({:.{p}f}, {:.{p}f}, {:.{p}f}), smoothing_group={}, "\
 
480
                "group_index={}>".format(
 
481
                self.flags,
 
482
                self.vertex_indices,
 
483
                self.vertex_normals[0][0],
 
484
                self.vertex_normals[0][1],
 
485
                self.vertex_normals[0][2],
 
486
                self.vertex_normals[1][0],
 
487
                self.vertex_normals[1][1],
 
488
                self.vertex_normals[1][2],
 
489
                self.vertex_normals[2][0],
 
490
                self.vertex_normals[2][1],
 
491
                self.vertex_normals[2][2],
 
492
                self.s[0],
 
493
                self.s[1],
 
494
                self.s[2],
 
495
                self.t[0],
 
496
                self.t[1],
 
497
                self.t[2],
 
498
                self.smoothing_group,
 
499
                self.group_index,
 
500
                p=Ms3dIo.PRECISION
 
501
                )
 
502
 
 
503
 
 
504
    @property
 
505
    def vertex_indices(self):
 
506
        return self._vertex_indices
 
507
 
 
508
    @property
 
509
    def vertex_normals(self):
 
510
        return self._vertex_normals
 
511
 
 
512
    @property
 
513
    def s(self):
 
514
        return self._s
 
515
 
 
516
    @property
 
517
    def t(self):
 
518
        return self._t
 
519
 
 
520
 
 
521
    def read(self, raw_io):
 
522
        self.flags = Ms3dIo.read_word(raw_io)
 
523
        self._vertex_indices = Ms3dIo.read_array(raw_io, Ms3dIo.read_word, 3)
 
524
        self._vertex_normals = Ms3dIo.read_array2(raw_io, Ms3dIo.read_float, 3, 3)
 
525
        self._s = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
 
526
        self._t = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
 
527
        self.smoothing_group = Ms3dIo.read_byte(raw_io)
 
528
        self.group_index = Ms3dIo.read_byte(raw_io)
 
529
        return self
 
530
 
 
531
    def write(self, raw_io):
 
532
        Ms3dIo.write_word(raw_io, self.flags)
 
533
        Ms3dIo.write_array(raw_io, Ms3dIo.write_word, 3, self.vertex_indices)
 
534
        Ms3dIo.write_array2(raw_io, Ms3dIo.write_float, 3, 3, self.vertex_normals)
 
535
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.s)
 
536
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.t)
 
537
        Ms3dIo.write_byte(raw_io, self.smoothing_group)
 
538
        Ms3dIo.write_byte(raw_io, self.group_index)
 
539
 
 
540
 
 
541
###############################################################################
 
542
class Ms3dGroup:
 
543
    """ Ms3dGroup """
 
544
    """
 
545
    __slots__ was taking out,
 
546
    to be able to inject additional attributes during runtime
 
547
    __slots__ = (
 
548
            'flags',
 
549
            'name',
 
550
            'material_index',
 
551
            '_triangle_indices',
 
552
            '_comment_object', # Ms3dComment
 
553
            )
 
554
    """
 
555
 
 
556
    def __init__(
 
557
            self,
 
558
            default_flags=Ms3dSpec.DEFAULT_FLAGS,
 
559
            default_name="",
 
560
            default_triangle_indices=None,
 
561
            default_material_index=Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX,
 
562
            default_comment_object=None, # Ms3dComment
 
563
            ):
 
564
        if (default_name is None):
 
565
            default_name = ""
 
566
 
 
567
        if (default_triangle_indices is None):
 
568
            default_triangle_indices = []
 
569
 
 
570
        self.flags = default_flags
 
571
        self.name = default_name
 
572
        self._triangle_indices = default_triangle_indices
 
573
        self.material_index = default_material_index
 
574
 
 
575
        if default_comment_object is None:
 
576
            default_comment_object = Ms3dCommentEx()
 
577
        self._comment_object = default_comment_object # Ms3dComment
 
578
 
 
579
    def __repr__(self):
 
580
        return "\n<flags={}, name='{}', number_triangles={},"\
 
581
                " triangle_indices={}, material_index={}>".format(
 
582
                self.flags,
 
583
                self.name,
 
584
                self.number_triangles,
 
585
                self.triangle_indices,
 
586
                self.material_index
 
587
                )
 
588
 
 
589
 
 
590
    @property
 
591
    def number_triangles(self):
 
592
        if self.triangle_indices is None:
 
593
            return 0
 
594
        return len(self.triangle_indices)
 
595
 
 
596
    @property
 
597
    def triangle_indices(self):
 
598
        return self._triangle_indices
 
599
 
 
600
    @property
 
601
    def comment_object(self):
 
602
        return self._comment_object
 
603
 
 
604
 
 
605
    def read(self, raw_io):
 
606
        self.flags = Ms3dIo.read_byte(raw_io)
 
607
        self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME)
 
608
        _number_triangles = Ms3dIo.read_word(raw_io)
 
609
        self._triangle_indices = Ms3dIo.read_array(
 
610
                raw_io, Ms3dIo.read_word, _number_triangles)
 
611
        self.material_index = Ms3dIo.read_sbyte(raw_io)
 
612
        return self
 
613
 
 
614
    def write(self, raw_io):
 
615
        Ms3dIo.write_byte(raw_io, self.flags)
 
616
        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name)
 
617
        Ms3dIo.write_word(raw_io, self.number_triangles)
 
618
        Ms3dIo.write_array(
 
619
                raw_io, Ms3dIo.write_word, self.number_triangles,
 
620
                self.triangle_indices)
 
621
        Ms3dIo.write_sbyte(raw_io, self.material_index)
 
622
 
 
623
 
 
624
###############################################################################
 
625
class Ms3dMaterial:
 
626
    """ Ms3dMaterial """
 
627
    """
 
628
    __slots__ was taking out,
 
629
    to be able to inject additional attributes during runtime
 
630
    __slots__ = (
 
631
            'name',
 
632
            'shininess',
 
633
            'transparency',
 
634
            'mode',
 
635
            'texture',
 
636
            'alphamap',
 
637
            '_ambient',
 
638
            '_diffuse',
 
639
            '_specular',
 
640
            '_emissive',
 
641
            '_comment_object', # Ms3dComment
 
642
            )
 
643
    """
 
644
 
 
645
    def __init__(
 
646
            self,
 
647
            default_name="",
 
648
            default_ambient=list(Ms3dSpec.DEFAULT_MATERIAL_AMBIENT),
 
649
            default_diffuse=list(Ms3dSpec.DEFAULT_MATERIAL_DIFFUSE),
 
650
            default_specular=list(Ms3dSpec.DEFAULT_MATERIAL_SPECULAR),
 
651
            default_emissive=list(Ms3dSpec.DEFAULT_MATERIAL_EMISSIVE),
 
652
            default_shininess=Ms3dSpec.DEFAULT_MATERIAL_SHININESS,
 
653
            default_transparency=0.0,
 
654
            default_mode=Ms3dSpec.DEFAULT_MATERIAL_MODE,
 
655
            default_texture="",
 
656
            default_alphamap="",
 
657
            default_comment_object=None, # Ms3dComment
 
658
            ):
 
659
        if (default_name is None):
 
660
            default_name = ""
 
661
 
 
662
        if (default_texture is None):
 
663
            default_texture = ""
 
664
 
 
665
        if (default_alphamap is None):
 
666
            default_alphamap = ""
 
667
 
 
668
        self.name = default_name
 
669
        self._ambient = default_ambient
 
670
        self._diffuse = default_diffuse
 
671
        self._specular = default_specular
 
672
        self._emissive = default_emissive
 
673
        self.shininess = default_shininess
 
674
        self.transparency = default_transparency
 
675
        self.mode = default_mode
 
676
        self.texture = default_texture
 
677
        self.alphamap = default_alphamap
 
678
 
 
679
        if default_comment_object is None:
 
680
            default_comment_object = Ms3dCommentEx()
 
681
        self._comment_object = default_comment_object # Ms3dComment
 
682
 
 
683
    def __repr__(self):
 
684
        return "\n<name='{}', ambient=({:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), "\
 
685
                "diffuse=({:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), specular=("\
 
686
                "{:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), emissive=({:.{p}f}, "\
 
687
                "{:.{p}f}, {:.{p}f}, {:.{p}f}), shininess={:.{p}f}, transparency="\
 
688
                "{:.{p}f}, mode={}, texture='{}', alphamap='{}'>".format(
 
689
                self.name,
 
690
                self.ambient[0],
 
691
                self.ambient[1],
 
692
                self.ambient[2],
 
693
                self.ambient[3],
 
694
                self.diffuse[0],
 
695
                self.diffuse[1],
 
696
                self.diffuse[2],
 
697
                self.diffuse[3],
 
698
                self.specular[0],
 
699
                self.specular[1],
 
700
                self.specular[2],
 
701
                self.specular[3],
 
702
                self.emissive[0],
 
703
                self.emissive[1],
 
704
                self.emissive[2],
 
705
                self.emissive[3],
 
706
                self.shininess,
 
707
                self.transparency,
 
708
                self.mode,
 
709
                self.texture,
 
710
                self.alphamap,
 
711
                p=Ms3dIo.PRECISION
 
712
                )
 
713
 
 
714
    def __hash__(self):
 
715
        return (hash(self.name)
 
716
 
 
717
                ^ hash(self.ambient)
 
718
                ^ hash(self.diffuse)
 
719
                ^ hash(self.specular)
 
720
                ^ hash(self.emissive)
 
721
 
 
722
                ^ hash(self.shininess)
 
723
                ^ hash(self.transparency)
 
724
                ^ hash(self.mode)
 
725
 
 
726
                ^ hash(self.texture)
 
727
                ^ hash(self.alphamap)
 
728
                )
 
729
 
 
730
    def __eq__(self, other):
 
731
        return ((self.name == other.name)
 
732
 
 
733
                and (self.ambient == other.ambient)
 
734
                and (self.diffuse == other.diffuse)
 
735
                and (self.specular == other.specular)
 
736
                and (self.emissive == other.emissive)
 
737
 
 
738
                and (self.shininess == other.shininess)
 
739
                and (self.transparency == other.transparency)
 
740
                and (self.mode == other.mode)
 
741
 
 
742
                #and (self.texture == other.texture)
 
743
                #and (self.alphamap == other.alphamap)
 
744
                )
 
745
 
 
746
 
 
747
    @property
 
748
    def ambient(self):
 
749
        return self._ambient
 
750
 
 
751
    @property
 
752
    def diffuse(self):
 
753
        return self._diffuse
 
754
 
 
755
    @property
 
756
    def specular(self):
 
757
        return self._specular
 
758
 
 
759
    @property
 
760
    def emissive(self):
 
761
        return self._emissive
 
762
 
 
763
    @property
 
764
    def comment_object(self):
 
765
        return self._comment_object
 
766
 
 
767
 
 
768
    def read(self, raw_io):
 
769
        self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME)
 
770
        self._ambient = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4)
 
771
        self._diffuse = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4)
 
772
        self._specular = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4)
 
773
        self._emissive = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4)
 
774
        self.shininess = Ms3dIo.read_float(raw_io)
 
775
        self.transparency = Ms3dIo.read_float(raw_io)
 
776
        self.mode = Ms3dIo.read_byte(raw_io)
 
777
        self.texture = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_FILENAME)
 
778
        self.alphamap = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_FILENAME)
 
779
        return self
 
780
 
 
781
    def write(self, raw_io):
 
782
        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name)
 
783
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.ambient)
 
784
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.diffuse)
 
785
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.specular)
 
786
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.emissive)
 
787
        Ms3dIo.write_float(raw_io, self.shininess)
 
788
        Ms3dIo.write_float(raw_io, self.transparency)
 
789
        Ms3dIo.write_byte(raw_io, self.mode)
 
790
        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_FILENAME, self.texture)
 
791
        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_FILENAME, self.alphamap)
 
792
 
 
793
 
 
794
###############################################################################
 
795
class Ms3dRotationKeyframe:
 
796
    """ Ms3dRotationKeyframe """
 
797
    __slots__ = (
 
798
            'time',
 
799
            '_rotation',
 
800
            )
 
801
 
 
802
    def __init__(
 
803
            self,
 
804
            default_time=0.0,
 
805
            default_rotation=(0.0, 0.0, 0.0)
 
806
            ):
 
807
        self.time = default_time
 
808
        self._rotation = default_rotation
 
809
 
 
810
    def __repr__(self):
 
811
        return "\n<time={:.{p}f}, rotation=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format(
 
812
                self.time,
 
813
                self.rotation[0],
 
814
                self.rotation[1],
 
815
                self.rotation[2],
 
816
                p=Ms3dIo.PRECISION
 
817
                )
 
818
 
 
819
 
 
820
    @property
 
821
    def rotation(self):
 
822
        return self._rotation
 
823
 
 
824
 
 
825
    def read(self, raw_io):
 
826
        self.time = Ms3dIo.read_float(raw_io)
 
827
        self._rotation = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
 
828
        return self
 
829
 
 
830
    def write(self, raw_io):
 
831
        Ms3dIo.write_float(raw_io, self.time)
 
832
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.rotation)
 
833
 
 
834
 
 
835
###############################################################################
 
836
class Ms3dTranslationKeyframe:
 
837
    """ Ms3dTranslationKeyframe """
 
838
    __slots__ = (
 
839
            'time',
 
840
            '_position',
 
841
            )
 
842
 
 
843
    def __init__(
 
844
            self,
 
845
            default_time=0.0,
 
846
            default_position=(0.0, 0.0, 0.0)
 
847
            ):
 
848
        self.time = default_time
 
849
        self._position = default_position
 
850
 
 
851
    def __repr__(self):
 
852
        return "\n<time={:.{p}f}, position=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format(
 
853
                self.time,
 
854
                self.position[0],
 
855
                self.position[1],
 
856
                self.position[2],
 
857
                p=Ms3dIo.PRECISION
 
858
                )
 
859
 
 
860
 
 
861
    @property
 
862
    def position(self):
 
863
        return self._position
 
864
 
 
865
 
 
866
    def read(self, raw_io):
 
867
        self.time = Ms3dIo.read_float(raw_io)
 
868
        self._position = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
 
869
        return self
 
870
 
 
871
    def write(self, raw_io):
 
872
        Ms3dIo.write_float(raw_io, self.time)
 
873
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.position)
 
874
 
 
875
 
 
876
###############################################################################
 
877
class Ms3dJoint:
 
878
    """ Ms3dJoint """
 
879
    """
 
880
    __slots__ was taking out,
 
881
    to be able to inject additional attributes during runtime
 
882
    __slots__ = (
 
883
            'flags',
 
884
            'name',
 
885
            'parent_name',
 
886
            '_rotation',
 
887
            '_position',
 
888
            '_rotation_keyframes',
 
889
            '_translation_keyframes',
 
890
            '_joint_ex_object', # Ms3dJointEx
 
891
            '_comment_object', # Ms3dComment
 
892
            )
 
893
    """
 
894
 
 
895
    def __init__(
 
896
            self,
 
897
            default_flags=Ms3dSpec.DEFAULT_FLAGS,
 
898
            default_name="",
 
899
            default_parent_name="",
 
900
            default_rotation=(0.0, 0.0, 0.0),
 
901
            default_position=(0.0, 0.0, 0.0),
 
902
            default_rotation_keyframes=None,
 
903
            default_translation_keyframes=None,
 
904
            default_joint_ex_object=None, # Ms3dJointEx
 
905
            default_comment_object=None, # Ms3dComment
 
906
            ):
 
907
        if (default_name is None):
 
908
            default_name = ""
 
909
 
 
910
        if (default_parent_name is None):
 
911
            default_parent_name = ""
 
912
 
 
913
        if (default_rotation_keyframes is None):
 
914
            default_rotation_keyframes = [] #Ms3dRotationKeyframe()
 
915
 
 
916
        if (default_translation_keyframes is None):
 
917
            default_translation_keyframes = [] #Ms3dTranslationKeyframe()
 
918
 
 
919
        self.flags = default_flags
 
920
        self.name = default_name
 
921
        self.parent_name = default_parent_name
 
922
        self._rotation = default_rotation
 
923
        self._position = default_position
 
924
        self._rotation_keyframes = default_rotation_keyframes
 
925
        self._translation_keyframes = default_translation_keyframes
 
926
 
 
927
        if default_comment_object is None:
 
928
            default_comment_object = Ms3dCommentEx()
 
929
        self._comment_object = default_comment_object # Ms3dComment
 
930
 
 
931
        if default_joint_ex_object is None:
 
932
            default_joint_ex_object = Ms3dJointEx()
 
933
        self._joint_ex_object = default_joint_ex_object # Ms3dJointEx
 
934
 
 
935
    def __repr__(self):
 
936
        return "\n<flags={}, name='{}', parent_name='{}', rotation=({:.{p}f}, "\
 
937
                "{:.{p}f}, {:.{p}f}), position=({:.{p}f}, {:.{p}f}, {:.{p}f}), "\
 
938
                "number_rotation_keyframes={}, number_translation_keyframes={},"\
 
939
                " rotation_key_frames={}, translation_key_frames={}>".format(
 
940
                self.flags,
 
941
                self.name,
 
942
                self.parent_name,
 
943
                self.rotation[0],
 
944
                self.rotation[1],
 
945
                self.rotation[2],
 
946
                self.position[0],
 
947
                self.position[1],
 
948
                self.position[2],
 
949
                self.number_rotation_keyframes,
 
950
                self.number_translation_keyframes,
 
951
                self.rotation_key_frames,
 
952
                self.translation_key_frames,
 
953
                p=Ms3dIo.PRECISION
 
954
                )
 
955
 
 
956
 
 
957
    @property
 
958
    def rotation(self):
 
959
        return self._rotation
 
960
 
 
961
    @property
 
962
    def position(self):
 
963
        return self._position
 
964
 
 
965
    @property
 
966
    def number_rotation_keyframes(self):
 
967
        if self.rotation_key_frames is None:
 
968
            return 0
 
969
        return len(self.rotation_key_frames)
 
970
 
 
971
    @property
 
972
    def number_translation_keyframes(self):
 
973
        if self.translation_key_frames is None:
 
974
            return 0
 
975
        return len(self.translation_key_frames)
 
976
 
 
977
    @property
 
978
    def rotation_key_frames(self):
 
979
        return self._rotation_keyframes
 
980
 
 
981
    @property
 
982
    def translation_key_frames(self):
 
983
        return self._translation_keyframes
 
984
 
 
985
 
 
986
    @property
 
987
    def joint_ex_object(self):
 
988
        return self._joint_ex_object
 
989
 
 
990
 
 
991
    @property
 
992
    def comment_object(self):
 
993
        return self._comment_object
 
994
 
 
995
 
 
996
    def read(self, raw_io):
 
997
        self.flags = Ms3dIo.read_byte(raw_io)
 
998
        self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME)
 
999
        self.parent_name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME)
 
1000
        self._rotation = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
 
1001
        self._position = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
 
1002
        _number_rotation_keyframes = Ms3dIo.read_word(raw_io)
 
1003
        _number_translation_keyframes = Ms3dIo.read_word(raw_io)
 
1004
        self._rotation_keyframes = []
 
1005
        for i in range(_number_rotation_keyframes):
 
1006
            self.rotation_key_frames.append(Ms3dRotationKeyframe().read(raw_io))
 
1007
        self._translation_keyframes = []
 
1008
        for i in range(_number_translation_keyframes):
 
1009
            self.translation_key_frames.append(
 
1010
                    Ms3dTranslationKeyframe().read(raw_io))
 
1011
        return self
 
1012
 
 
1013
    def write(self, raw_io):
 
1014
        Ms3dIo.write_byte(raw_io, self.flags)
 
1015
        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name)
 
1016
        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.parent_name)
 
1017
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.rotation)
 
1018
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.position)
 
1019
        Ms3dIo.write_word(raw_io, self.number_rotation_keyframes)
 
1020
        Ms3dIo.write_word(raw_io, self.number_translation_keyframes)
 
1021
        for i in range(self.number_rotation_keyframes):
 
1022
            self.rotation_key_frames[i].write(raw_io)
 
1023
        for i in range(self.number_translation_keyframes):
 
1024
            self.translation_key_frames[i].write(raw_io)
 
1025
 
 
1026
 
 
1027
###############################################################################
 
1028
class Ms3dCommentEx:
 
1029
    """ Ms3dCommentEx """
 
1030
    __slots__ = (
 
1031
            'index',
 
1032
            'comment',
 
1033
            )
 
1034
 
 
1035
    def __init__(
 
1036
            self,
 
1037
            default_index=0,
 
1038
            default_comment=""
 
1039
            ):
 
1040
        if (default_comment is None):
 
1041
            default_comment = ""
 
1042
 
 
1043
        self.index = default_index
 
1044
        self.comment = default_comment
 
1045
 
 
1046
    def __repr__(self):
 
1047
        return "\n<index={}, comment_length={}, comment='{}'>".format(
 
1048
                self.index,
 
1049
                self.comment_length,
 
1050
                self.comment
 
1051
                )
 
1052
 
 
1053
 
 
1054
    @property
 
1055
    def comment_length(self):
 
1056
        if self.comment is None:
 
1057
            return 0
 
1058
        return len(self.comment)
 
1059
 
 
1060
 
 
1061
    def read(self, raw_io):
 
1062
        self.index = Ms3dIo.read_dword(raw_io)
 
1063
        _comment_length = Ms3dIo.read_dword(raw_io)
 
1064
        self.comment = Ms3dIo.read_string(raw_io, _comment_length)
 
1065
        return self
 
1066
 
 
1067
    def write(self, raw_io):
 
1068
        Ms3dIo.write_dword(raw_io, self.index)
 
1069
        Ms3dIo.write_dword(raw_io, self.comment_length)
 
1070
        Ms3dIo.write_string(raw_io, self.comment_length, self.comment)
 
1071
 
 
1072
 
 
1073
###############################################################################
 
1074
class Ms3dComment:
 
1075
    """ Ms3dComment """
 
1076
    __slots__ = (
 
1077
            'comment',
 
1078
            )
 
1079
 
 
1080
    def __init__(
 
1081
            self,
 
1082
            default_comment=""
 
1083
            ):
 
1084
        if (default_comment is None):
 
1085
            default_comment = ""
 
1086
 
 
1087
        self.comment = default_comment
 
1088
 
 
1089
    def __repr__(self):
 
1090
        return "\n<comment_length={}, comment='{}'>".format(
 
1091
                self.comment_length,
 
1092
                self.comment
 
1093
                )
 
1094
 
 
1095
 
 
1096
    @property
 
1097
    def comment_length(self):
 
1098
        if self.comment is None:
 
1099
            return 0
 
1100
        return len(self.comment)
 
1101
 
 
1102
 
 
1103
    def read(self, raw_io):
 
1104
        _comment_length = Ms3dIo.read_dword(raw_io)
 
1105
        self.comment = Ms3dIo.read_string(raw_io, _comment_length)
 
1106
        return self
 
1107
 
 
1108
    def write(self, raw_io):
 
1109
        Ms3dIo.write_dword(raw_io, self.comment_length)
 
1110
        Ms3dIo.write_string(raw_io, self.comment_length, self.comment)
 
1111
 
 
1112
 
 
1113
###############################################################################
 
1114
class Ms3dVertexEx1:
 
1115
    """ Ms3dVertexEx1 """
 
1116
    __slots__ = (
 
1117
            '_bone_ids',
 
1118
            '_weights',
 
1119
            )
 
1120
 
 
1121
    def __init__(
 
1122
            self,
 
1123
            default_bone_ids=(
 
1124
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
 
1125
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
 
1126
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID),
 
1127
            default_weights=(100, 0, 0)
 
1128
            ):
 
1129
        self._bone_ids = default_bone_ids
 
1130
        self._weights = default_weights
 
1131
 
 
1132
    def __repr__(self):
 
1133
        return "\n<bone_ids={}, weights={}>".format(
 
1134
                self.bone_ids,
 
1135
                self.weights
 
1136
                )
 
1137
 
 
1138
 
 
1139
    @property
 
1140
    def bone_ids(self):
 
1141
        return self._bone_ids
 
1142
 
 
1143
    @property
 
1144
    def weights(self):
 
1145
        return self._weights
 
1146
 
 
1147
 
 
1148
    @property
 
1149
    def weight_bone_id(self):
 
1150
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1151
            return self._weights
 
1152
        return 100
 
1153
 
 
1154
    @property
 
1155
    def weight_bone_id0(self):
 
1156
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1157
            return self._weights[0]
 
1158
        return 0
 
1159
 
 
1160
    @property
 
1161
    def weight_bone_id1(self):
 
1162
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1163
            return self._weights[1]
 
1164
        return 0
 
1165
 
 
1166
    @property
 
1167
    def weight_bone_id2(self):
 
1168
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1169
            return 100 - (self._weights[0] + self._weights[1] \
 
1170
                    + self._weights[2])
 
1171
        return 0
 
1172
 
 
1173
 
 
1174
    def read(self, raw_io):
 
1175
        self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3)
 
1176
        self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3)
 
1177
        return self
 
1178
 
 
1179
    def write(self, raw_io):
 
1180
        Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids)
 
1181
        Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights)
 
1182
 
 
1183
 
 
1184
###############################################################################
 
1185
class Ms3dVertexEx2:
 
1186
    """ Ms3dVertexEx2 """
 
1187
    __slots__ = (
 
1188
            'extra',
 
1189
            '_bone_ids',
 
1190
            '_weights',
 
1191
            )
 
1192
 
 
1193
    def __init__(
 
1194
            self,
 
1195
            default_bone_ids=(
 
1196
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
 
1197
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
 
1198
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID),
 
1199
            default_weights=(100, 0, 0),
 
1200
            default_extra=0
 
1201
            ):
 
1202
        self._bone_ids = default_bone_ids
 
1203
        self._weights = default_weights
 
1204
        self.extra = default_extra
 
1205
 
 
1206
    def __repr__(self):
 
1207
        return "\n<bone_ids={}, weights={}, extra={}>".format(
 
1208
                self.bone_ids,
 
1209
                self.weights,
 
1210
                self.extra
 
1211
                )
 
1212
 
 
1213
 
 
1214
    @property
 
1215
    def bone_ids(self):
 
1216
        return self._bone_ids
 
1217
 
 
1218
    @property
 
1219
    def weights(self):
 
1220
        return self._weights
 
1221
 
 
1222
 
 
1223
    @property
 
1224
    def weight_bone_id(self):
 
1225
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1226
            return self._weights
 
1227
        return 100
 
1228
 
 
1229
    @property
 
1230
    def weight_bone_id0(self):
 
1231
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1232
            return self._weights[0]
 
1233
        return 0
 
1234
 
 
1235
    @property
 
1236
    def weight_bone_id1(self):
 
1237
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1238
            return self._weights[1]
 
1239
        return 0
 
1240
 
 
1241
    @property
 
1242
    def weight_bone_id2(self):
 
1243
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1244
            return 100 - (self._weights[0] + self._weights[1] \
 
1245
                    + self._weights[2])
 
1246
        return 0
 
1247
 
 
1248
 
 
1249
    def read(self, raw_io):
 
1250
        self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3)
 
1251
        self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3)
 
1252
        self.extra = Ms3dIo.read_dword(raw_io)
 
1253
        return self
 
1254
 
 
1255
    def write(self, raw_io):
 
1256
        Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids)
 
1257
        Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights)
 
1258
        Ms3dIo.write_dword(raw_io, self.extra)
 
1259
 
 
1260
 
 
1261
###############################################################################
 
1262
class Ms3dVertexEx3:
 
1263
    """ Ms3dVertexEx3 """
 
1264
    #char bone_ids[3]; // index of joint or -1, if -1, then that weight is
 
1265
    #    ignored, since subVersion 1
 
1266
    #byte weights[3]; // vertex weight ranging from 0 - 100, last weight is
 
1267
    #    computed by 1.0 - sum(all weights), since subVersion 1
 
1268
    #// weight[0] is the weight for bone_id in Ms3dVertex
 
1269
    #// weight[1] is the weight for bone_ids[0]
 
1270
    #// weight[2] is the weight for bone_ids[1]
 
1271
    #// 1.0f - weight[0] - weight[1] - weight[2] is the weight for bone_ids[2]
 
1272
    #unsigned int extra; // vertex extra, which can be used as color or
 
1273
    #    anything else, since subVersion 2
 
1274
    __slots__ = (
 
1275
            'extra',
 
1276
            '_bone_ids',
 
1277
            '_weights',
 
1278
            )
 
1279
 
 
1280
    def __init__(
 
1281
            self,
 
1282
            default_bone_ids=(
 
1283
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
 
1284
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
 
1285
                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID),
 
1286
            default_weights=(100, 0, 0),
 
1287
            default_extra=0
 
1288
            ):
 
1289
        self._bone_ids = default_bone_ids
 
1290
        self._weights = default_weights
 
1291
        self.extra = default_extra
 
1292
 
 
1293
    def __repr__(self):
 
1294
        return "\n<bone_ids={}, weights={}, extra={}>".format(
 
1295
                self.bone_ids,
 
1296
                self.weights,
 
1297
                self.extra
 
1298
                )
 
1299
 
 
1300
 
 
1301
    @property
 
1302
    def bone_ids(self):
 
1303
        return self._bone_ids
 
1304
 
 
1305
    @property
 
1306
    def weights(self):
 
1307
        return self._weights
 
1308
 
 
1309
 
 
1310
    @property
 
1311
    def weight_bone_id(self):
 
1312
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1313
            return self._weights
 
1314
        return 100
 
1315
 
 
1316
    @property
 
1317
    def weight_bone_id0(self):
 
1318
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1319
            return self._weights[0]
 
1320
        return 0
 
1321
 
 
1322
    @property
 
1323
    def weight_bone_id1(self):
 
1324
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1325
            return self._weights[1]
 
1326
        return 0
 
1327
 
 
1328
    @property
 
1329
    def weight_bone_id2(self):
 
1330
        if self._weights[0] or self._weights[1] or self._weights[2]:
 
1331
            return 100 - (self._weights[0] + self._weights[1] \
 
1332
                    + self._weights[2])
 
1333
        return 0
 
1334
 
 
1335
 
 
1336
    def read(self, raw_io):
 
1337
        self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3)
 
1338
        self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3)
 
1339
        self.extra = Ms3dIo.read_dword(raw_io)
 
1340
        return self
 
1341
 
 
1342
    def write(self, raw_io):
 
1343
        Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids)
 
1344
        Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights)
 
1345
        Ms3dIo.write_dword(raw_io, self.extra)
 
1346
 
 
1347
 
 
1348
###############################################################################
 
1349
class Ms3dJointEx:
 
1350
    """ Ms3dJointEx """
 
1351
    __slots__ = (
 
1352
            '_color',
 
1353
            )
 
1354
 
 
1355
    def __init__(
 
1356
            self,
 
1357
            default_color=Ms3dSpec.DEFAULT_JOINT_COLOR
 
1358
            ):
 
1359
        self._color = default_color
 
1360
 
 
1361
    def __repr__(self):
 
1362
        return "\n<color=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format(
 
1363
                self.color[0],
 
1364
                self.color[1],
 
1365
                self.color[2],
 
1366
                p=Ms3dIo.PRECISION
 
1367
                )
 
1368
 
 
1369
 
 
1370
    @property
 
1371
    def color(self):
 
1372
        return self._color
 
1373
 
 
1374
 
 
1375
    def read(self, raw_io):
 
1376
        self._color = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
 
1377
        return self
 
1378
 
 
1379
    def write(self, raw_io):
 
1380
        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.color)
 
1381
 
 
1382
 
 
1383
###############################################################################
 
1384
class Ms3dModelEx:
 
1385
    """ Ms3dModelEx """
 
1386
    __slots__ = (
 
1387
            'joint_size',
 
1388
            'transparency_mode',
 
1389
            'alpha_ref',
 
1390
            )
 
1391
 
 
1392
    def __init__(
 
1393
            self,
 
1394
            default_joint_size=Ms3dSpec.DEFAULT_MODEL_JOINT_SIZE,
 
1395
            default_transparency_mode\
 
1396
                    =Ms3dSpec.DEFAULT_MODEL_TRANSPARENCY_MODE,
 
1397
            default_alpha_ref=0.0
 
1398
            ):
 
1399
        self.joint_size = default_joint_size
 
1400
        self.transparency_mode = default_transparency_mode
 
1401
        self.alpha_ref = default_alpha_ref
 
1402
 
 
1403
    def __repr__(self):
 
1404
        return "\n<joint_size={:.{p}f}, transparency_mode={}, alpha_ref={:.{p}f}>".format(
 
1405
                self.joint_size,
 
1406
                self.transparency_mode,
 
1407
                self.alpha_ref,
 
1408
                p=Ms3dIo.PRECISION
 
1409
                )
 
1410
 
 
1411
    def read(self, raw_io):
 
1412
        self.joint_size = Ms3dIo.read_float(raw_io)
 
1413
        self.transparency_mode = Ms3dIo.read_dword(raw_io)
 
1414
        self.alpha_ref = Ms3dIo.read_float(raw_io)
 
1415
        return self
 
1416
 
 
1417
    def write(self, raw_io):
 
1418
        Ms3dIo.write_float(raw_io, self.joint_size)
 
1419
        Ms3dIo.write_dword(raw_io, self.transparency_mode)
 
1420
        Ms3dIo.write_float(raw_io, self.alpha_ref)
 
1421
 
 
1422
 
 
1423
###############################################################################
 
1424
#
 
1425
# file format
 
1426
#
 
1427
###############################################################################
 
1428
class Ms3dModel:
 
1429
    """ Ms3dModel """
 
1430
    __slot__ = (
 
1431
            'header',
 
1432
            'animation_fps',
 
1433
            'current_time',
 
1434
            'number_total_frames',
 
1435
            'sub_version_comments',
 
1436
            'sub_version_vertex_extra',
 
1437
            'sub_version_joint_extra',
 
1438
            'sub_version_model_extra',
 
1439
            'name',
 
1440
            '_vertices',
 
1441
            '_triangles',
 
1442
            '_groups',
 
1443
            '_materials',
 
1444
            '_joints',
 
1445
            '_has_model_comment',
 
1446
            '_comment_object', # Ms3dComment
 
1447
            '_model_ex_object', # Ms3dModelEx
 
1448
            )
 
1449
 
 
1450
    def __init__(
 
1451
            self,
 
1452
            default_name=""
 
1453
            ):
 
1454
        if (default_name is None):
 
1455
            default_name = ""
 
1456
 
 
1457
        self.name = default_name
 
1458
 
 
1459
        self.animation_fps = Ms3dSpec.DEFAULT_MODEL_ANIMATION_FPS
 
1460
        self.current_time = 0.0
 
1461
        self.number_total_frames = 0
 
1462
        self.sub_version_comments \
 
1463
                = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_COMMENTS
 
1464
        self.sub_version_vertex_extra \
 
1465
                = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA
 
1466
        self.sub_version_joint_extra \
 
1467
                = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_JOINT_EXTRA
 
1468
        self.sub_version_model_extra \
 
1469
                = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_MODEL_EXTRA
 
1470
 
 
1471
        self._vertices = [] #Ms3dVertex()
 
1472
        self._triangles = [] #Ms3dTriangle()
 
1473
        self._groups = [] #Ms3dGroup()
 
1474
        self._materials = [] #Ms3dMaterial()
 
1475
        self._joints = [] #Ms3dJoint()
 
1476
 
 
1477
        self.header = Ms3dHeader()
 
1478
        self._model_ex_object = Ms3dModelEx()
 
1479
        self._comment_object = None #Ms3dComment()
 
1480
 
 
1481
 
 
1482
    @property
 
1483
    def number_vertices(self):
 
1484
        if self.vertices is None:
 
1485
            return 0
 
1486
        return len(self.vertices)
 
1487
 
 
1488
    @property
 
1489
    def vertices(self):
 
1490
        return self._vertices
 
1491
 
 
1492
 
 
1493
    @property
 
1494
    def number_triangles(self):
 
1495
        if self.triangles is None:
 
1496
            return 0
 
1497
        return len(self.triangles)
 
1498
 
 
1499
    @property
 
1500
    def triangles(self):
 
1501
        return self._triangles
 
1502
 
 
1503
 
 
1504
    @property
 
1505
    def number_groups(self):
 
1506
        if self.groups is None:
 
1507
            return 0
 
1508
        return len(self.groups)
 
1509
 
 
1510
    @property
 
1511
    def groups(self):
 
1512
        return self._groups
 
1513
 
 
1514
 
 
1515
    @property
 
1516
    def number_materials(self):
 
1517
        if self.materials is None:
 
1518
            return 0
 
1519
        return len(self.materials)
 
1520
 
 
1521
    @property
 
1522
    def materials(self):
 
1523
        return self._materials
 
1524
 
 
1525
 
 
1526
    @property
 
1527
    def number_joints(self):
 
1528
        if self.joints is None:
 
1529
            return 0
 
1530
        return len(self.joints)
 
1531
 
 
1532
    @property
 
1533
    def joints(self):
 
1534
        return self._joints
 
1535
 
 
1536
 
 
1537
    @property
 
1538
    def number_group_comments(self):
 
1539
        if self.groups is None:
 
1540
            return 0
 
1541
        number = 0
 
1542
        for item in self.groups:
 
1543
            if item.comment_object is not None and item.comment_object.comment:
 
1544
                number += 1
 
1545
        return number
 
1546
 
 
1547
    @property
 
1548
    def group_comments(self):
 
1549
        if self.groups is None:
 
1550
            return None
 
1551
        items = []
 
1552
        for item in self.groups:
 
1553
            if item.comment_object is not None and item.comment_object.comment:
 
1554
                items.append(item)
 
1555
        return items
 
1556
 
 
1557
 
 
1558
    @property
 
1559
    def number_material_comments(self):
 
1560
        if self.materials is None:
 
1561
            return 0
 
1562
        number = 0
 
1563
        for item in self.materials:
 
1564
            if item.comment_object is not None and item.comment_object.comment:
 
1565
                number += 1
 
1566
        return number
 
1567
 
 
1568
    @property
 
1569
    def material_comments(self):
 
1570
        if self.materials is None:
 
1571
            return None
 
1572
        items = []
 
1573
        for item in self.materials:
 
1574
            if item.comment_object is not None and item.comment_object.comment:
 
1575
                items.append(item)
 
1576
        return items
 
1577
 
 
1578
 
 
1579
    @property
 
1580
    def number_joint_comments(self):
 
1581
        if self.joints is None:
 
1582
            return 0
 
1583
        number = 0
 
1584
        for item in self.joints:
 
1585
            if item.comment_object is not None and item.comment_object.comment:
 
1586
                number += 1
 
1587
        return number
 
1588
 
 
1589
    @property
 
1590
    def joint_comments(self):
 
1591
        if self.joints is None:
 
1592
            return None
 
1593
        items = []
 
1594
        for item in self.joints:
 
1595
            if item.comment_object is not None and item.comment_object.comment:
 
1596
                items.append(item)
 
1597
        return items
 
1598
 
 
1599
 
 
1600
    @property
 
1601
    def has_model_comment(self):
 
1602
        if self.comment_object is not None and self.comment_object.comment:
 
1603
            return 1
 
1604
        return 0
 
1605
 
 
1606
    @property
 
1607
    def comment_object(self):
 
1608
        return self._comment_object
 
1609
 
 
1610
 
 
1611
    @property
 
1612
    def vertex_ex(self):
 
1613
        if not self.sub_version_vertex_extra:
 
1614
            return None
 
1615
        return [item.vertex_ex_object for item in self.vertices]
 
1616
 
 
1617
    @property
 
1618
    def joint_ex(self):
 
1619
        if not self.sub_version_joint_extra:
 
1620
            return None
 
1621
        return [item.joint_ex_object for item in self.joints]
 
1622
 
 
1623
    @property
 
1624
    def model_ex_object(self):
 
1625
        if not self.sub_version_model_extra:
 
1626
            return None
 
1627
        return self._model_ex_object
 
1628
 
 
1629
 
 
1630
    def print_internal(self):
 
1631
        print()
 
1632
        print("##############################################################")
 
1633
        print("## the internal data of Ms3dModel object...")
 
1634
        print("##")
 
1635
 
 
1636
        print("header={}".format(self.header))
 
1637
 
 
1638
        print("number_vertices={}".format(self.number_vertices))
 
1639
        print("vertices=[", end="")
 
1640
        if self.vertices:
 
1641
            for obj in self.vertices:
 
1642
                print("{}".format(obj), end="")
 
1643
        print("]")
 
1644
 
 
1645
        print("number_triangles={}".format(self.number_triangles))
 
1646
        print("triangles=[", end="")
 
1647
        if self.triangles:
 
1648
            for obj in self.triangles:
 
1649
                print("{}".format(obj), end="")
 
1650
        print("]")
 
1651
 
 
1652
        print("number_groups={}".format(self.number_groups))
 
1653
        print("groups=[", end="")
 
1654
        if self.groups:
 
1655
            for obj in self.groups:
 
1656
                print("{}".format(obj), end="")
 
1657
        print("]")
 
1658
 
 
1659
        print("number_materials={}".format(self.number_materials))
 
1660
        print("materials=[", end="")
 
1661
        if self.materials:
 
1662
            for obj in self.materials:
 
1663
                print("{}".format(obj), end="")
 
1664
        print("]")
 
1665
 
 
1666
        print("animation_fps={}".format(self.animation_fps))
 
1667
        print("current_time={}".format(self.current_time))
 
1668
        print("number_total_frames={}".format(self.number_total_frames))
 
1669
 
 
1670
        print("number_joints={}".format(self.number_joints))
 
1671
        print("joints=[", end="")
 
1672
        if self.joints:
 
1673
            for obj in self.joints:
 
1674
                print("{}".format(obj), end="")
 
1675
        print("]")
 
1676
 
 
1677
        print("sub_version_comments={}".format(self.sub_version_comments))
 
1678
 
 
1679
        print("number_group_comments={}".format(self.number_group_comments))
 
1680
        print("group_comments=[", end="")
 
1681
        if self.group_comments:
 
1682
            for obj in self.group_comments:
 
1683
                print("{}".format(obj.comment_object), end="")
 
1684
        print("]")
 
1685
 
 
1686
        print("number_material_comments={}".format(
 
1687
                self.number_material_comments))
 
1688
        print("material_comments=[", end="")
 
1689
        if self.material_comments:
 
1690
            for obj in self.material_comments:
 
1691
                print("{}".format(obj.comment_object), end="")
 
1692
        print("]")
 
1693
 
 
1694
        print("number_joint_comments={}".format(self.number_joint_comments))
 
1695
        print("joint_comments=[", end="")
 
1696
        if self.joint_comments:
 
1697
            for obj in self.joint_comments:
 
1698
                print("{}".format(obj.comment_object), end="")
 
1699
        print("]")
 
1700
 
 
1701
        print("has_model_comment={}".format(self.has_model_comment))
 
1702
        print("model_comment={}".format(self.comment_object))
 
1703
 
 
1704
        print("sub_version_vertex_extra={}".format(
 
1705
                self.sub_version_vertex_extra))
 
1706
        print("vertex_ex=[", end="")
 
1707
        if self.vertex_ex:
 
1708
            for obj in self.vertex_ex:
 
1709
                print("{}".format(obj), end="")
 
1710
        print("]")
 
1711
 
 
1712
        print("sub_version_joint_extra={}".format(
 
1713
                self.sub_version_joint_extra))
 
1714
        print("joint_ex=[", end="")
 
1715
        if self.joint_ex:
 
1716
            for obj in self.joint_ex:
 
1717
                print("{}".format(obj), end="")
 
1718
        print("]")
 
1719
 
 
1720
        print("sub_version_model_extra={}".format(
 
1721
                self.sub_version_model_extra))
 
1722
        print("model_ex={}".format(self.model_ex_object))
 
1723
 
 
1724
        print("##")
 
1725
        print("## ...end")
 
1726
        print("##############################################################")
 
1727
        print()
 
1728
 
 
1729
 
 
1730
    def read(self, raw_io):
 
1731
        """
 
1732
        opens, reads and pars MS3D file.
 
1733
        add content to blender scene
 
1734
        """
 
1735
 
 
1736
        self.header.read(raw_io)
 
1737
        if (self.header != Ms3dHeader()):
 
1738
            print("\nwarning, invalid file header")
 
1739
 
 
1740
        _number_vertices = Ms3dIo.read_word(raw_io)
 
1741
        if (_number_vertices > Ms3dSpec.MAX_VERTICES):
 
1742
            print("\nwarning, invalid count: number_vertices: {}".format(
 
1743
                    _number_vertices))
 
1744
        self._vertices = []
 
1745
        for i in range(_number_vertices):
 
1746
            self.vertices.append(Ms3dVertex().read(raw_io))
 
1747
 
 
1748
        _number_triangles = Ms3dIo.read_word(raw_io)
 
1749
        if (_number_triangles > Ms3dSpec.MAX_TRIANGLES):
 
1750
            print("\nwarning, invalid count: number_triangles: {}".format(
 
1751
                    _number_triangles))
 
1752
        self._triangles = []
 
1753
        for i in range(_number_triangles):
 
1754
            self.triangles.append(Ms3dTriangle().read(raw_io))
 
1755
 
 
1756
        _number_groups = Ms3dIo.read_word(raw_io)
 
1757
        if (_number_groups > Ms3dSpec.MAX_GROUPS):
 
1758
            print("\nwarning, invalid count: number_groups: {}".format(
 
1759
                    _number_groups))
 
1760
        self._groups = []
 
1761
        for i in range(_number_groups):
 
1762
            self.groups.append(Ms3dGroup().read(raw_io))
 
1763
 
 
1764
        _number_materials = Ms3dIo.read_word(raw_io)
 
1765
        if (_number_materials > Ms3dSpec.MAX_MATERIALS):
 
1766
            print("\nwarning, invalid count: number_materials: {}".format(
 
1767
                    _number_materials))
 
1768
        self._materials = []
 
1769
        for i in range(_number_materials):
 
1770
            self.materials.append(Ms3dMaterial().read(raw_io))
 
1771
 
 
1772
        self.animation_fps = Ms3dIo.read_float(raw_io)
 
1773
        self.current_time = Ms3dIo.read_float(raw_io)
 
1774
        self.number_total_frames = Ms3dIo.read_dword(raw_io)
 
1775
 
 
1776
        _progress = set()
 
1777
 
 
1778
        try:
 
1779
            # optional data
 
1780
            # doesn't matter if doesn't existing.
 
1781
 
 
1782
            _number_joints = Ms3dIo.read_word(raw_io)
 
1783
            _progress.add('NUMBER_JOINTS')
 
1784
            if (_number_joints > Ms3dSpec.MAX_JOINTS):
 
1785
                print("\nwarning, invalid count: number_joints: {}".format(
 
1786
                        _number_joints))
 
1787
            self._joints = []
 
1788
            for i in range(_number_joints):
 
1789
                self.joints.append(Ms3dJoint().read(raw_io))
 
1790
            _progress.add('JOINTS')
 
1791
 
 
1792
            self.sub_version_comments = Ms3dIo.read_dword(raw_io)
 
1793
            _progress.add('SUB_VERSION_COMMENTS')
 
1794
            _number_group_comments = Ms3dIo.read_dword(raw_io)
 
1795
            _progress.add('NUMBER_GROUP_COMMENTS')
 
1796
            if (_number_group_comments > Ms3dSpec.MAX_GROUPS):
 
1797
                print("\nwarning, invalid count:"\
 
1798
                        " number_group_comments: {}".format(
 
1799
                        _number_group_comments))
 
1800
            if _number_group_comments > _number_groups:
 
1801
                print("\nwarning, invalid count:"\
 
1802
                        " number_group_comments: {}, number_groups: {}".format(
 
1803
                        _number_group_comments, _number_groups))
 
1804
            for i in range(_number_group_comments):
 
1805
                item = Ms3dCommentEx().read(raw_io)
 
1806
                if item.index >= 0 and item.index < _number_groups:
 
1807
                    self.groups[item.index]._comment_object = item
 
1808
                else:
 
1809
                    print("\nwarning, invalid index:"\
 
1810
                            " group_index: {}, number_groups: {}".format(
 
1811
                            item.index, _number_groups))
 
1812
            _progress.add('GROUP_COMMENTS')
 
1813
 
 
1814
            _number_material_comments = Ms3dIo.read_dword(raw_io)
 
1815
            _progress.add('NUMBER_MATERIAL_COMMENTS')
 
1816
            if (_number_material_comments > Ms3dSpec.MAX_MATERIALS):
 
1817
                print("\nwarning, invalid count:"\
 
1818
                        " number_material_comments: {}".format(
 
1819
                        _number_material_comments))
 
1820
            if _number_material_comments > _number_materials:
 
1821
                print("\nwarning, invalid count:"\
 
1822
                        " number_material_comments:"\
 
1823
                        " {}, number_materials: {}".format(
 
1824
                        _number_material_comments, _number_materials))
 
1825
            for i in range(_number_material_comments):
 
1826
                item = Ms3dCommentEx().read(raw_io)
 
1827
                if item.index >= 0 and item.index < _number_materials:
 
1828
                    self.materials[item.index]._comment_object = item
 
1829
                else:
 
1830
                    print("\nwarning, invalid index:"\
 
1831
                            " material_index: {}, number_materials:"\
 
1832
                            " {}".format(item.index, _number_materials))
 
1833
            _progress.add('MATERIAL_COMMENTS')
 
1834
 
 
1835
            _number_joint_comments = Ms3dIo.read_dword(raw_io)
 
1836
            _progress.add('NUMBER_JOINT_COMMENTS')
 
1837
            if (_number_joint_comments > Ms3dSpec.MAX_JOINTS):
 
1838
                print("\nwarning, invalid count:"\
 
1839
                        " number_joint_comments: {}".format(
 
1840
                        _number_joint_comments))
 
1841
            if _number_joint_comments > _number_joints:
 
1842
                print("\nwarning, invalid count:"\
 
1843
                        " number_joint_comments: {}, number_joints: {}".format(
 
1844
                        _number_joint_comments, _number_joints))
 
1845
            for i in range(_number_joint_comments):
 
1846
                item = Ms3dCommentEx().read(raw_io)
 
1847
                if item.index >= 0 and item.index < _number_joints:
 
1848
                    self.joints[item.index]._comment_object = item
 
1849
                else:
 
1850
                    print("\nwarning, invalid index:"\
 
1851
                            " joint_index: {}, number_joints: {}".format(
 
1852
                            item.index, _number_joints))
 
1853
            _progress.add('JOINT_COMMENTS')
 
1854
 
 
1855
            _has_model_comment = Ms3dIo.read_dword(raw_io)
 
1856
            _progress.add('HAS_MODEL_COMMENTS')
 
1857
            if (_has_model_comment != 0):
 
1858
                self._comment_object = Ms3dComment().read(raw_io)
 
1859
            else:
 
1860
                self._comment_object = None
 
1861
            _progress.add('MODEL_COMMENTS')
 
1862
 
 
1863
            self.sub_version_vertex_extra = Ms3dIo.read_dword(raw_io)
 
1864
            _progress.add('SUB_VERSION_VERTEX_EXTRA')
 
1865
            if self.sub_version_vertex_extra > 0:
 
1866
                length = len(self.joints)
 
1867
                for i in range(_number_vertices):
 
1868
                    if self.sub_version_vertex_extra == 1:
 
1869
                        item = Ms3dVertexEx1()
 
1870
                    elif self.sub_version_vertex_extra == 2:
 
1871
                        item = Ms3dVertexEx2()
 
1872
                    elif self.sub_version_vertex_extra == 3:
 
1873
                        item = Ms3dVertexEx3()
 
1874
                    else:
 
1875
                        print("\nwarning, invalid version:"\
 
1876
                                " sub_version_vertex_extra: {}".format(
 
1877
                                sub_version_vertex_extra))
 
1878
                        continue
 
1879
                    self.vertices[i]._vertex_ex_object = item.read(raw_io)
 
1880
            _progress.add('VERTEX_EXTRA')
 
1881
 
 
1882
            self.sub_version_joint_extra = Ms3dIo.read_dword(raw_io)
 
1883
            _progress.add('SUB_VERSION_JOINT_EXTRA')
 
1884
            if self.sub_version_joint_extra > 0:
 
1885
                for i in range(_number_joints):
 
1886
                    self.joints[i]._joint_ex_object = Ms3dJointEx().read(raw_io)
 
1887
            _progress.add('JOINT_EXTRA')
 
1888
 
 
1889
            self.sub_version_model_extra = Ms3dIo.read_dword(raw_io)
 
1890
            _progress.add('SUB_VERSION_MODEL_EXTRA')
 
1891
            if self.sub_version_model_extra > 0:
 
1892
                self._model_ex_object.read(raw_io)
 
1893
            _progress.add('MODEL_EXTRA')
 
1894
 
 
1895
        except EOFError:
 
1896
            # reached end of optional data.
 
1897
            print("Ms3dModel.read - optional data read: {}".format(_progress))
 
1898
            pass
 
1899
 
 
1900
        except Exception:
 
1901
            type, value, traceback = exc_info()
 
1902
            print("Ms3dModel.read - exception in optional try block,"
 
1903
                    " _progress={0}\n  type: '{1}'\n  value: '{2}'".format(
 
1904
                    _progress, type, value, traceback))
 
1905
 
 
1906
        else:
 
1907
            pass
 
1908
 
 
1909
        # try best to continue far as possible
 
1910
        if not 'JOINTS' in _progress:
 
1911
            _number_joints = 0
 
1912
            self._joints = []
 
1913
 
 
1914
        if not 'GROUP_COMMENTS' in _progress:
 
1915
            self.sub_version_comments = 0
 
1916
            _number_group_comments = 0
 
1917
 
 
1918
        if not 'MATERIAL_COMMENTS' in _progress:
 
1919
            _number_material_comments = 0
 
1920
 
 
1921
        if not 'JOINT_COMMENTS' in _progress:
 
1922
            _number_joint_comments = 0
 
1923
 
 
1924
        if not 'MODEL_COMMENTS' in _progress:
 
1925
            _has_model_comment = 0
 
1926
            self._comment_object = None # Ms3dComment()
 
1927
 
 
1928
        if not 'VERTEX_EXTRA' in _progress:
 
1929
            self.sub_version_vertex_extra = 0
 
1930
 
 
1931
        if not 'JOINT_EXTRA' in _progress:
 
1932
            self.sub_version_joint_extra = 0
 
1933
 
 
1934
        if not 'MODEL_EXTRA' in _progress:
 
1935
            self.sub_version_model_extra = 0
 
1936
            self._model_ex_object = Ms3dModelEx()
 
1937
 
 
1938
        return
 
1939
 
 
1940
 
 
1941
    def write(self, raw_io):
 
1942
        """
 
1943
        add blender scene content to MS3D
 
1944
        creates, writes MS3D file.
 
1945
        """
 
1946
 
 
1947
        self.header.write(raw_io)
 
1948
 
 
1949
        Ms3dIo.write_word(raw_io, self.number_vertices)
 
1950
        for i in range(self.number_vertices):
 
1951
            self.vertices[i].write(raw_io)
 
1952
 
 
1953
        Ms3dIo.write_word(raw_io, self.number_triangles)
 
1954
        for i in range(self.number_triangles):
 
1955
            self.triangles[i].write(raw_io)
 
1956
 
 
1957
        Ms3dIo.write_word(raw_io, self.number_groups)
 
1958
        for i in range(self.number_groups):
 
1959
            self.groups[i].write(raw_io)
 
1960
 
 
1961
        Ms3dIo.write_word(raw_io, self.number_materials)
 
1962
        for i in range(self.number_materials):
 
1963
            self.materials[i].write(raw_io)
 
1964
 
 
1965
        Ms3dIo.write_float(raw_io, self.animation_fps)
 
1966
        Ms3dIo.write_float(raw_io, self.current_time)
 
1967
        Ms3dIo.write_dword(raw_io, self.number_total_frames)
 
1968
 
 
1969
        try:
 
1970
            # optional part
 
1971
            # doesn't matter if it doesn't complete.
 
1972
            Ms3dIo.write_word(raw_io, self.number_joints)
 
1973
            for i in range(self.number_joints):
 
1974
                self.joints[i].write(raw_io)
 
1975
 
 
1976
            Ms3dIo.write_dword(raw_io, self.sub_version_comments)
 
1977
 
 
1978
            Ms3dIo.write_dword(raw_io, self.number_group_comments)
 
1979
            for i in range(self.number_group_comments):
 
1980
                self.group_comments[i].comment_object.write(raw_io)
 
1981
 
 
1982
            Ms3dIo.write_dword(raw_io, self.number_material_comments)
 
1983
            for i in range(self.number_material_comments):
 
1984
                self.material_comments[i].comment_object.write(raw_io)
 
1985
 
 
1986
            Ms3dIo.write_dword(raw_io, self.number_joint_comments)
 
1987
            for i in range(self.number_joint_comments):
 
1988
                self.joint_comments[i].comment_object.write(raw_io)
 
1989
 
 
1990
            Ms3dIo.write_dword(raw_io, self.has_model_comment)
 
1991
            if (self.has_model_comment != 0):
 
1992
                self.comment_object.write(raw_io)
 
1993
 
 
1994
            Ms3dIo.write_dword(raw_io, self.sub_version_vertex_extra)
 
1995
            if (self.sub_version_vertex_extra in {1, 2, 3}):
 
1996
                for i in range(self.number_vertices):
 
1997
                    self.vertex_ex[i].write(raw_io)
 
1998
 
 
1999
            Ms3dIo.write_dword(raw_io, self.sub_version_joint_extra)
 
2000
            for i in range(self.number_joints):
 
2001
                self.joint_ex[i].write(raw_io)
 
2002
 
 
2003
            Ms3dIo.write_dword(raw_io, self.sub_version_model_extra)
 
2004
            self.model_ex_object.write(raw_io)
 
2005
 
 
2006
        except Exception:
 
2007
            type, value, traceback = exc_info()
 
2008
            print("Ms3dModel.write - exception in optional try block"
 
2009
                    "\n  type: '{0}'\n  value: '{1}'".format(
 
2010
                    type, value, traceback))
 
2011
            pass
 
2012
 
 
2013
        else:
 
2014
            pass
 
2015
 
 
2016
        return
 
2017
 
 
2018
 
 
2019
    def is_valid(self):
 
2020
        valid = True
 
2021
        result = []
 
2022
 
 
2023
        format1 = "\n  number of {0}: {1}"
 
2024
        format2 = " limit exceeded! (limit is {0})"
 
2025
 
 
2026
        result.append("MS3D statistics:")
 
2027
        result.append(format1.format("vertices ........",
 
2028
                self.number_vertices))
 
2029
        if (self.number_vertices > Ms3dSpec.MAX_VERTICES):
 
2030
            result.append(format2.format(Ms3dSpec.MAX_VERTICES))
 
2031
            valid &= False
 
2032
 
 
2033
        result.append(format1.format("triangles .......",
 
2034
                self.number_triangles))
 
2035
        if (self.number_triangles > Ms3dSpec.MAX_TRIANGLES):
 
2036
            result.append(format2.format(Ms3dSpec.MAX_TRIANGLES))
 
2037
            valid &= False
 
2038
 
 
2039
        result.append(format1.format("groups ..........",
 
2040
                self.number_groups))
 
2041
        if (self.number_groups > Ms3dSpec.MAX_GROUPS):
 
2042
            result.append(format2.format(Ms3dSpec.MAX_GROUPS))
 
2043
            valid &= False
 
2044
 
 
2045
        result.append(format1.format("materials .......",
 
2046
                self.number_materials))
 
2047
        if (self.number_materials > Ms3dSpec.MAX_MATERIALS):
 
2048
            result.append(format2.format(Ms3dSpec.MAX_MATERIALS))
 
2049
            valid &= False
 
2050
 
 
2051
        result.append(format1.format("joints ..........",
 
2052
                self.number_joints))
 
2053
        if (self.number_joints > Ms3dSpec.MAX_JOINTS):
 
2054
            result.append(format2.format(Ms3dSpec.MAX_JOINTS))
 
2055
            valid &= False
 
2056
 
 
2057
        result.append(format1.format("model comments ..",
 
2058
                self.has_model_comment))
 
2059
        result.append(format1.format("group comments ..",
 
2060
                self.number_group_comments))
 
2061
        result.append(format1.format("material comments",
 
2062
                self.number_material_comments))
 
2063
        result.append(format1.format("joint comments ..",
 
2064
                self.number_joint_comments))
 
2065
 
 
2066
        #if (not valid):
 
2067
        #    result.append("\n\nthe data may be corrupted.")
 
2068
 
 
2069
        return (valid, ("".join(result)))
 
2070
 
 
2071
 
 
2072
###############################################################################
 
2073
#234567890123456789012345678901234567890123456789012345678901234567890123456789
 
2074
#--------1---------2---------3---------4---------5---------6---------7---------
 
2075
# ##### END OF FILE #####