From 62cb40312564dad2b262f6dd9e4c207b4aeb3fa8 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Sun, 29 May 2016 00:10:42 -0300 Subject: [PATCH] Arch: added 3DS importer --- src/Mod/Arch/CMakeLists.txt | 14 + src/Mod/Arch/Dice3DS/__init__.py | 3 + src/Mod/Arch/Dice3DS/dom3ds.py | 1908 ++++++++++++++++++++++++++++++ src/Mod/Arch/Dice3DS/util.py | 272 +++++ src/Mod/Arch/Init.py | 1 + src/Mod/Arch/import3DS.py | 105 ++ 6 files changed, 2303 insertions(+) create mode 100644 src/Mod/Arch/Dice3DS/__init__.py create mode 100644 src/Mod/Arch/Dice3DS/dom3ds.py create mode 100644 src/Mod/Arch/Dice3DS/util.py create mode 100644 src/Mod/Arch/import3DS.py diff --git a/src/Mod/Arch/CMakeLists.txt b/src/Mod/Arch/CMakeLists.txt index a60d31657..6bee6d75b 100644 --- a/src/Mod/Arch/CMakeLists.txt +++ b/src/Mod/Arch/CMakeLists.txt @@ -35,6 +35,13 @@ SET(Arch_SRCS ArchMaterial.py ArchSchedule.py ArchProfile.py + import3DS.py +) + +SET(Dice3DS_SRCS + Dice3DS/__init__.py + Dice3DS/util.py + Dice3DS/dom3ds.py ) SET(Arch_presets @@ -48,6 +55,7 @@ ADD_CUSTOM_TARGET(Arch ALL ) fc_copy_sources(Arch "${CMAKE_BINARY_DIR}/Mod/Arch" ${Arch_SRCS}) +fc_copy_sources(Arch "${CMAKE_BINARY_DIR}/Mod/Arch/Dice3DS" ${Dice3DS_SRCS}) fc_target_copy_resource(Arch ${CMAKE_SOURCE_DIR}/src/Mod/Arch @@ -69,6 +77,12 @@ INSTALL( DESTINATION Mod/Arch ) +INSTALL( + FILES + ${Dice3DS_SRCS} + DESTINATION Mod/Arch/Dice3DS +) + INSTALL( DIRECTORY Presets diff --git a/src/Mod/Arch/Dice3DS/__init__.py b/src/Mod/Arch/Dice3DS/__init__.py new file mode 100644 index 000000000..35c2a63e1 --- /dev/null +++ b/src/Mod/Arch/Dice3DS/__init__.py @@ -0,0 +1,3 @@ +# __init__.py +__all__ = [ 'dom3ds', 'util' ] +version = (0,13) diff --git a/src/Mod/Arch/Dice3DS/dom3ds.py b/src/Mod/Arch/Dice3DS/dom3ds.py new file mode 100644 index 000000000..bce472fdf --- /dev/null +++ b/src/Mod/Arch/Dice3DS/dom3ds.py @@ -0,0 +1,1908 @@ +# dom3ds.py + +"""Slice and dice 3DS files. + +Provides for reading, writing, and manipulating 3DS files. It's +called dom3ds because it's reminiscent of XML-DOM: it converts the 3DS +file into a hierarchy of objects, in much the same way XML-DOM +converts an XML file into a hierarchy of objects called the Document +Object Model. The dom3ds module creates an object for each chunk in +the 3DS file, which can be accessed hierarchially as attributes. + +For example, once a 3DS file is loaded, you could access the smoothing +data of the second object like this: + + dom.mdata.objects[2].ntri.faces.smoothing.array + +""" + +import os, sys, struct + +import numpy + + +# Exceptions + +class File3dsFormatError(Exception): + "Exception in the 3DS file format" + def __init__(self,msg,tight=False): + self.msg = msg + self.tight = tight + def __str__(self): + return repr(self.msg) + +class FBufError(Exception): + "Exception when reading from FileLikeBuffers" + + +# Numeric/string conversion functions, where the string uses +# little-endian regardless of host byte order + +def string_to_array(s,typ): + """Convert data block from a 3DS file to a numpy array.""" + a = numpy.fromstring(s,typ) + if sys.byteorder == 'big': + a.byteswap(True) + return a + +def array_to_string(a): + """Convert a numpy array to data block for a 3DS file.""" + if sys.byteorder == 'big': + a = a.byteswap() + return a.tostring() + +def array_to_string_destructive(a): + """Destructively convert a numpy array to data block for a 3DS file.""" + if sys.byteorder == 'big': + a.byteswap(True) + return a.tostring() + + +# Utility classes + +class FileLikeBuffer(object): + """Treat a range of a string as a quasi-file-like object. + + This is a quasi-file like object that represents a certain + range of a string. It behaves kind of like a file object, + but has some specialized methods. + + """ + + def __init__(self,s,start=0,end=None): + self.buf = s + self.start = start + if end is None: + self.end = len(s) + else: + if end > len(s): + raise FBufError("End of fbuf object set past end of buffer") + self.end = end + assert 0 <= start <= self.end, (start,self.end) + self.index = start + def tell(self): + return self.index + def seek(self,pos): + self.index = pos + if self.index < 0 or self.index > self.end: + raise FBufError("Invalid position in fbuf") + def advance(self,n): + self.index += n + if self.index > self.end: + raise FBufError("End of fbuf reached") + def read(self,length): + r = self.buf[self.index:self.index+length] + self.advance(length) + return r + def read_fbuf(self,length): + r = FileLikeBuffer(self.buf,self.index,self.index+length) + self.advance(length) + return r + def read_to_nul(self): + try: + i = self.buf.index('\0',self.index,self.end) + except IndexError: + raise FBufError("End of fbuf reached in string") + r = self.buf[self.index:i] + self.index = i+1 + return r + def read_rest(self): + r = self.buf[self.index:self.end] + self.index = self.end + return r + def room_for_chunks(self): + return self.index+6 <= self.end + + +# Utility functions + +def _decls_to_list(decls): + decllist = [ tuple(x.strip().split()) for x in decls.split(',') ] + return decllist + +def _decls_to_vars(decls): + if not decls: + return () + vars = [ x.strip().split(None,1)[1] for x in decls.split(',') ] + return vars + + +# Chunk metaclasses + +class ChunkMetaclass(type): + """Metaclass of chunk types. + + This class takes some static data and defines how to read in + the chunks, and preprocesses it. It also creates a dictionary + of chunk classes keyed by tag. + + """ + + chunk_taghash = {} + chunk_labelhash = {} + + def __new__(metatype,name,bases,clsdict): + vars = [] + vars.extend(_decls_to_vars(clsdict.get('struct',''))) + vars.extend(_decls_to_vars(clsdict.get('single',''))) + vars.extend(_decls_to_vars(clsdict.get('multiple',''))) + if clsdict.get('freechunks',False): + vars.append('subchunks') + clsdict.setdefault('__slots__',[]).extend(vars) + + return type.__new__(metatype,name,bases,clsdict) + + def __init__(self,name,bases,clsdict): + if hasattr(self,'struct'): + self.struct_fields = _decls_to_list(self.struct) + else: + self.struct_fields = [] + + self.single_types = {} + self.single_order = [] + if hasattr(self,'single'): + attrs = {} + for typ,attr in _decls_to_list(self.single): + self.single_types[typ] = attr + if attr not in attrs: + attrs[attr] = 1 + self.single_order.append(attr) + + self.multiple_types = {} + self.multiple_order = [] + if hasattr(self,'multiple'): + attrs = {} + for typ,attr in _decls_to_list(self.multiple): + self.multiple_types[typ] = attr + if attr not in attrs: + attrs[attr] = 1 + self.multiple_order.append(attr) + + if hasattr(self,'keyframe'): + self.keyframe_fields = _decls_to_list(self.keyframe) + slots = ['frameno','flags','tension', + 'continuity','bias','ease_to','ease_from'] + for typ,attr in self.keyframe_fields: + slots.append(attr) + kfdict = { '__slots__': slots } + kfbases = (object,) + kfname = "%s.Key" % name + self.Key = type(kfname,kfbases,kfdict) + else: + self.keyframe_fields = [] + + if hasattr(self,'tag'): + self.chunk_taghash[self.tag] = self + self.chunk_labelhash[name] = self + if not hasattr(self,'label'): + self.label = name + + type.__init__(self,name,bases,clsdict) + + + +class UndefinedChunkMetaclass(ChunkMetaclass): + """Metaclass of undefined chunks. + + Automatically inserts a data attribute into __slots__ to hold + the chunk data. + + """ + + def __new__(metatype,name,bases,clsdict): + if '__slots__' in clsdict: + if 'data' not in clsdict['__slots__']: + clsdict['__slots__'].append('data') + else: + clsdict['__slots__'] = ['data'] + return ChunkMetaclass.__new__(metatype,name,bases,clsdict) + + + +# Base class of chunks. + +class ChunkBase(object): + """Base class of chunks. + + The methods of this class are mostly for input/output. When + manipulating a 3ds model, one would access subchunks as + attributes. + + """ + + __metaclass__ = ChunkMetaclass + + # Defaults for some parameters + + freechunks = True + swallow = False + + # Define constructor to accept keyword arguments, as a convenience + # Init chunk arguments to nothing + + def __init__(self,**kwargs): + """Construct a chunk. + + This is an abstract base constructor; normally you'd + construct a subclass of this. As a convenience, you + can set attributes via keyword arguments when creating + a chunk. + + """ + + if self.freechunks: + self.subchunks = [] + for v in self.single_order: + setattr(self,v,None) + for v in self.multiple_order: + setattr(self,v,[]) + for attr,value in kwargs.items(): + setattr(self,attr,value) + + # -------- SECTION 1: INITIALIZATION FROM CHUNK DATA -------- + + # These get a specific object from the fbuf + + @staticmethod + def get_byte(fbuf,flags): + return ord(fbuf.read(1)) + + @staticmethod + def get_short(fbuf,flags): + return struct.unpack("\n%s (0x%04X)\n' + % (cls.label,cls.label,cls.tag)) + flo.write('
\n') + + i = 0 + + for typ,attr in cls.struct_fields: + flo.write('%s %s
\n' % (typ,attr)) + i += 1 + + tagorder = [] + + d = {} + for typ,attr in cls.single_types.items(): + if attr not in d: + d[attr] = [] + d[attr].append(typ) + for attr in cls.single_order: + typs = d[attr] + for typ in typs: + tagorder.append(typ) + flo.write('%s %s
\n' + % (typ,typ,attr)) + i += 1 + + d = {} + for typ,attr in cls.multiple_types.items(): + if attr not in d: + d[attr] = [] + d[attr].append(typ) + for attr in cls.multiple_order: + typs = d[attr] + for typ in typs: + tagorder.append(typ) + flo.write('%s[] %s
\n' + % (typ,typ,attr)) + i += 1 + + if not i: + flo.write('No fields
\n') + + flo.write(' 
\n') + done[cls.label] = 1 + + for typ in tagorder: + type(cls).chunk_labelhash[typ].document_html(flo,done) + + document_html = classmethod(document_html) + + +# Chunks we don't know anything about, maybe not even the tag + +class UndefinedChunk(ChunkBase): + __metaclass__ = UndefinedChunkMetaclass + def read(self,fbuf,flags): + self.data = fbuf.read_rest() + def dump(self,flo,indent,flags): + self.dump_header(flo,flags) + def write(self): + return self.data + + +class UnknownChunk(UndefinedChunk): + __slots__ = ['tag'] + label = "UNKNOWN" + def __init__(self,tag): + self.tag = tag + + +class ErrorChunk(UndefinedChunk): + __slots__ = ['intended_tag','intended_label','error_msg'] + label = "ERROR" + tag = 0xEEEE + def __init__(self,intended_tag=None,intended_label=None,error_msg=None): + self.intended_tag = intended_tag + self.intended_label = intended_label + self.error_msg = error_msg + def dump(self,flo,indent,flags): + super(ErrorChunk,self).dump(flo,indent,flags) + if self.intended_tag is not None: + flo.write("%s intended_tag = 0x%04X\n" + % (indent,self.intended_tag)) + if self.intended_label is not None: + self.out_attr("intended_label",flo,indent+" ",flags) + if self.error_msg is not None: + self.out_attr("error_msg",flo,indent+" ",flags) + def write(self): + raise TypeError("Attempt to write an Error Chunk") + + +# Chunks with common sets of attributes + +class OneColorChunk(ChunkBase): + single = ("COLOR_F color, COLOR_24 color," + "LIN_COLOR_F lincolor, LIN_COLOR_24 lincolor") + +class OnePercentageChunk(ChunkBase): + single = ("INT_PERCENTAGE pct, FLOAT_PERCENTAGE pct") + +class OneShortValueChunk(ChunkBase): + struct = "short value" + +class OneFloatValueChunk(ChunkBase): + struct = "float value" + +class Color24Chunk(ChunkBase): + struct = "byte red, byte green, byte blue" + +class TextureChunk(ChunkBase): + single = ("INT_PERCENTAGE pct," + "MAT_MAPNAME filename," + "MAT_MAP_TILING tiling," + "MAT_MAP_TEXBLUR blur," + "MAT_MAP_USCALE uscale," + "MAT_MAP_VSCALE vscale," + "MAT_MAP_UOFFSET uoffset," + "MAT_MAP_VOFFSET voffset," + "MAT_MAP_ANG angle," + "MAT_MAP_COL1 color1," + "MAT_MAP_COL2 color2," + "MAT_MAP_RCOL rtint," + "MAT_MAP_GCOL gtint," + "MAT_MAP_BCOL btint") + +class TextureMaskChunk(ChunkBase): + single = ("INT_PERCENTAGE pct," + "MAT_MAPNAME filename," + "MAT_MAP_TILING tiling," + "MAT_MAP_TEXBLUR blur," + "MAT_MAP_USCALE uscale," + "MAT_MAP_VSCALE vscale," + "MAT_MAP_UOFFSET uoffset," + "MAT_MAP_VOFFSET voffset," + "MAT_MAP_ANG angle") + +class ArrayChunk(ChunkBase): + __slots__ = ['array'] + def dump_array(self,flo,indent,flags): + flo.write("%s%s = \n" + % (indent,"array",repr(self.array.shape))) + +class MatrixChunk(ArrayChunk): + def read_array(self,fbuf,flags): + size = 48 + a = string_to_array(fbuf.read(size),numpy.float32) + a = numpy.reshape(a,(4,3)) + m = numpy.zeros((4,4),numpy.float32) + m[:,0:3] = a + m[3,3] = 1.0 + self.array = numpy.array(numpy.transpose(m)) + def dump_array(self,flo,indent,flags): + super(MatrixChunk,self).dump_array(flo,indent,flags) + if flags['arraylines'] == 0: + return + for i in xrange(4): + flo.write("%s %12.4g%12.4g%12.4g%12.4g\n" + % (indent,self.array[i,0],self.array[i,1], + self.array[i,2],self.array[i,3])) + def write_array(self): + a = self.array[0:3,:] + a = numpy.array(numpy.transpose(a)).astype(numpy.float32) + return array_to_string_destructive(a) + +class TrackChunk(ChunkBase): + __slots__ = ['keys'] + struct = "short flags, long unused1, long unused2, long nkeys" + def read_array(self,fbuf,flags): + self.keys = [] + for i in xrange(self.nkeys): + kf = self.Key() + kf.frameno = self.get_long(fbuf,flags) + kf.flags = self.get_short(fbuf,flags) + if kf.flags & 1: + kf.tension = self.get_float(fbuf,flags) + if kf.flags & 2: + kf.continuity = self.get_float(fbuf,flags) + if kf.flags & 4: + kf.bias = self.get_float(fbuf,flags) + if kf.flags & 8: + kf.ease_to = self.get_float(fbuf,flags) + if kf.flags & 16: + kf.ease_from = self.get_float(fbuf,flags) + for typ,attr in self.keyframe_fields: + setattr(kf,attr,getattr(self,"get_"+typ)(fbuf,flags)) + self.keys.append(kf) + def write_array(self): + s = [] + for kf in self.keys: + s.append(self.set_long(kf.frameno)) + s.append(self.set_short(kf.flags)) + if kf.flags & 1: + s.append(self.set_float(kf.tension)) + if kf.flags & 2: + s.append(self.set_float(kf.continuity)) + if kf.flags & 4: + s.append(self.set_float(kf.bias)) + if kf.flags & 8: + s.append(self.set_float(kf.ease_to)) + if kf.flags & 16: + s.append(self.set_float(kf.ease_from)) + for typ,attr in self.keyframe_fields: + s.append(getattr(self,"set_"+typ)(getattr(kf,attr))) + return "".join(s) + def dump_array(self,flo,indent,flags): + super(TrackChunk,self).dump_array(flo,indent,flags) + if flags['arraylines'] == 0: + return + if flags['arraylines'] < 0: + n = self.nkeys + else: + n = min(flags['arraylines'],self.nkeys) + indent2 = indent+" " + for i in xrange(n): + kf = self.keys[i] + flo.write("%skeys[0] = %s.Key\n" % (indent,self.label)) + flo.write("%s frameno = %r\n" % (indent,kf.frameno)) + flo.write("%s flags = %r\n" % (indent,kf.flags)) + if kf.flags & 1: + flo.write("%s tension = %r\n" % (indent,kf.tension)) + if kf.flags & 2: + flo.write("%s continuity = %r\n" % (indent,kf.continuity)) + if kf.flags & 4: + flo.write("%s bias = %r\n" % (indent,kf.bias)) + if kf.flags & 8: + flo.write("%s ease_to = %r\n" % (indent,kf.ease_to)) + if kf.flags & 16: + flo.write("%s ease_from = %r\n" % (indent,kf.ease_from)) + for typ,attr in self.keyframe_fields: + flo.write("%s %s = %r\n" % (indent,attr,getattr(kf,attr))) + if n < self.nkeys: + flo.write("%s ...\n" % indent) + + +# +# Magic and Version Chunks +# + +class M3DMAGIC(ChunkBase): + tag = 0x4D4D + single = "M3D_VERSION version, MDATA mdata, KFDATA kfdata" + +class M3D_VERSION(ChunkBase): + tag = 0x0002 + struct = "long number" + +class MDATA(ChunkBase): + tag = 0x3D3D + single = ("MESH_VERSION version," + "MASTER_SCALE scale," + "VIEWPORT_LAYOUT layout," + "LO_SHADOW_BIAS lo_shadow_bias," + "HI_SHADOW_BIAS hi_shadow_bias," + "SHADOW_MAP_SIZE shadow_map_size," + "SHADOW_SAMPLES shadow_samples," + "SHADOW_RANGE shadow_range," + "SHADOW_FILTER shadow_filter," + #"RAY_BIAS ray_bias," + "O_CONSTS o_consts," + "AMBIENT_LIGHT ambient_light," + "SOLID_BGND solid_background," + "BIT_MAP bitmap," + "V_GRADIENT v_gradient," + "USE_BIT_MAP use_bitmap," + "USE_SOLID_BGND use_solid_background," + "USE_V_GRADIENT use_v_gradient," + "FOG fog," + "LAYER_FOG layer_fog," + "DISTANCE_CUE distance_cue," + "USE_FOG use_fog," + "USE_LAYER_FOG use_layer_fog," + "USE_DISTANCE_CUE use_distance_cue," + "DEFAULT_VIEW default_view," + "MARKER marker") + multiple = "MAT_ENTRY materials, NAMED_OBJECT objects" + +class MESH_VERSION(ChunkBase): + tag = 0x3D3E + struct = "long number" + +class SMAGIC(UndefinedChunk): tag = 0x2D2D +class LMAGIC(UndefinedChunk): tag = 0x2D3D +class MLIBMAGIC(UndefinedChunk): tag = 0x2DAA +class PRJMAGIC(UndefinedChunk): tag = 0x3D2C +class MATMAGIC(UndefinedChunk): tag = 0x3DFF + +# +# Miscellaneous, Global data +# + +class MARKER(ChunkBase): + tag = 0x0001 + struct = "long sum" + +class COLOR_F(ChunkBase): + tag = 0x0010 + struct = "float red, float green, float blue" + +class COLOR_24(Color24Chunk): + tag = 0x0011 + +class LIN_COLOR_24(Color24Chunk): + tag = 0x0012 + +class LIN_COLOR_F(ChunkBase): + tag = 0x0013 + struct = "float red, float green, float blue" + +class INT_PERCENTAGE(OneShortValueChunk): + tag = 0x0030 + +class FLOAT_PERCENTAGE(OneFloatValueChunk): + tag = 0x0031 + +class MASTER_SCALE(OneFloatValueChunk): + tag = 0x0100 + + +# +# Coloring, Lighting, Atmosphers Stuff +# + +class BIT_MAP(ChunkBase): + tag = 0x1100 + struct = 'string filename' + swallow = True + +class USE_BIT_MAP(ChunkBase): + tag = 0x1101 + +class SOLID_BGND(OneColorChunk): + tag = 0x1200 + +class USE_SOLID_BGND(ChunkBase): + tag = 0x1201 + +class V_GRADIENT(ChunkBase): + tag = 0x1300 + struct = "float midpoint" + multiple = ("COLOR_F color, LIN_COLOR_F lincolor," + "COLOR_24 color, LIN_COLOR_24 lincolor") + +class USE_V_GRADIENT(ChunkBase): + tag = 0x1301 + +class LO_SHADOW_BIAS(OneFloatValueChunk): + tag = 0x1400 + +class HI_SHADOW_BIAS(OneFloatValueChunk): + tag = 0x1410 + +class SHADOW_MAP_SIZE(OneShortValueChunk): + tag = 0x1420 + +class SHADOW_SAMPLES(OneShortValueChunk): + tag = 0x1430 + +class SHADOW_RANGE(OneShortValueChunk): + tag = 0x1440 + +class SHADOW_FILTER(OneFloatValueChunk): + tag = 0x1450 + +class RAW_BIAS(OneFloatValueChunk): + tag = 0x1460 + +class O_CONSTS(ChunkBase): + tag = 0x1500 + struct = "float plane_x, float plane_y, float plane_z" + +class AMBIENT_LIGHT(OneColorChunk): + tag = 0x2100 + + +class FOG(ChunkBase): + tag = 0x2200 + struct = ("float near_plane, float near_density," + "float far_plane, float far_density") + single = "COLOR_F color, FOG_BGND fog_background" + +class USE_FOG(ChunkBase): + tag = 0x2201 + +class FOG_BGND(ChunkBase): + tag = 0x2210 + +class DISTANCE_CUE(ChunkBase): + tag = 0x2300 + struct = ("float near_plane, float near_density," + "float far_plane, float far_density") + single = "DCUE_BGND dcue_background" + +class USE_DISTANCE_CUE(ChunkBase): + tag = 0x2301 + +class LAYER_FOG(ChunkBase): + tag = 0x2302 + struct = ("float fog_z_from, float fog_z_to," + "float fog_density, long fog_type") + single = "COLOR_F color" + +class USE_LAYER_FOG(ChunkBase): + tag = 0x2303 + +class DCUE_BGND(ChunkBase): + tag = 0x2310 + +# +# View Data +# + +class DEFAULT_VIEW(ChunkBase): + tag = 0x3000 + single = ('VIEW_TOP view, VIEW_BOTTOM view, VIEW_LEFT view,' + 'VIEW_RIGHT view, VIEW_FRONT view, VIEW_BACK view,' + 'VIEW_USER view, VIEW_CAMERA camera') + +class ViewChunk(ChunkBase): + struct = ("float target_x, float target_y," + "float target_z, float view_width") + +class VIEW_TOP(ViewChunk): + tag = 0x3010 + +class VIEW_BOTTOM(ViewChunk): + tag = 0x3020 + +class VIEW_LEFT(ViewChunk): + tag = 0x3030 + +class VIEW_RIGHT(ViewChunk): + tag = 0x3040 + +class VIEW_FRONT(ViewChunk): + tag = 0x3050 + +class VIEW_BACK(ViewChunk): + tag = 0x3060 + +class VIEW_USER(ChunkBase): + tag = 0x3070 + struct = ("float target_x, float target_y," + "float target_z, float view_width," + "float horiz_angle, float vert_angle," + "float back_angle") + +class VIEW_CAMERA(ChunkBase): + tag = 0x3080 + struct = "string name" + swallow = True + +class VIEW_WINDOW(UndefinedChunk): + tag = 0x3090 + + +# +# Objects +# + +class NAMED_OBJECT(ChunkBase): + tag = 0x4000 + struct = "string name" + single = ("N_TRI_OBJECT obj," + "N_DIRECT_LIGHT obj," + "N_CAMERA obj," + "OBJ_HIDDEN hidden," + "OBJ_VIS_LOFTER vis_lofter," + "OBJ_DOESNT_CAST doesnt_cast," + "OBJ_MATTE matte," + "OBJ_DONT_RCVSHADOW dont_rcvshadow," + "OBJ_FAST fast," + "OBJ_PROCEDURAL procedural," + "OBJ_FROZEN frozen") + +class OBJ_HIDDEN(ChunkBase): + tag = 0x4010 + +class OBJ_VIS_LOFTER(ChunkBase): + tag = 0x4011 + +class OBJ_DOESNT_CAST(ChunkBase): + tag = 0x4012 + +class OBJ_MATTE(ChunkBase): + tag = 0x4013 + +class OBJ_FAST(ChunkBase): + tag = 0x4014 + +class OBJ_PROCEDURAL(ChunkBase): + tag = 0x4015 + +class OBJ_FROZEN(ChunkBase): + tag = 0x4016 + +class OBJ_DONT_RCVSHADOW(ChunkBase): + tag = 0x4017 + +class N_TRI_OBJECT(ChunkBase): + tag = 0x4100 + single = ("POINT_ARRAY points," + "POINT_FLAG_ARRAY flags," + "FACE_ARRAY faces," + "TEX_VERTS texverts," + "MESH_MATRIX matrix," + "MESH_COLOR color," + "MESH_TEXTURE_INFO texinfo," + "PROC_NAME proc_name," + "PROC_DATA proc_data") + multiple = "MSH_MAT_GROUP matlist" + +class POINT_ARRAY(ArrayChunk): + tag = 0x4110 + struct = "short npoints" + def read_array(self,fbuf,flags): + size = 12*self.npoints + a = string_to_array(fbuf.read(size),numpy.float32) + a = numpy.reshape(a,(self.npoints,3)) + self.array = numpy.array(a) + def dump_array(self,flo,indent,flags): + super(POINT_ARRAY,self).dump_array(flo,indent,flags) + if flags['arraylines'] == 0: + return + if flags['arraylines'] < 0: + n = self.npoints + else: + n = min(flags['arraylines'],self.npoints) + for i in xrange(n): + flo.write("%s %12.4g%12.4g%12.4g\n" + % (indent,self.array[i,0],self.array[i,1], + self.array[i,2])) + if n < self.npoints: + flo.write("%s ...\n" % indent) + def write_array(self): + s = numpy.array(self.array).astype(numpy.float32) + return array_to_string_destructive(s) + swallow = True + +class POINT_FLAG_ARRAY(ArrayChunk): + tag = 0x4111 + struct = "short npoints" + def read_array(self,fbuf,flags): + size = 2*self.npoints + self.array = string_to_array(fbuf.read(size),numpy.uint16) + def write_array(self): + s = numpy.array(self.array).astype(numpy.uint16) + return array_to_string_destructive(s) + swallow = True + +class FACE_ARRAY(ArrayChunk): + tag = 0x4120 + struct = "short nfaces" + multiple = 'MSH_MAT_GROUP materials' + single = 'SMOOTH_GROUP smoothing, MSH_BOXMAP box' + def read_array(self,fbuf,flags): + size = 8*self.nfaces + a = string_to_array(fbuf.read(size),numpy.uint16) + a = numpy.reshape(a,(self.nfaces,4)) + self.array = numpy.array(a) + def dump_array(self,flo,indent,flags): + super(FACE_ARRAY,self).dump_array(flo,indent,flags) + if flags['arraylines'] == 0: + return + if flags['arraylines'] < 0: + n = self.nfaces + else: + n = min(flags['arraylines'],self.nfaces) + for i in xrange(n): + flo.write("%s %10d%10d%10d%10d\n" + % (indent,self.array[i,0],self.array[i,1], + self.array[i,2],self.array[i,3])) + if n < self.nfaces: + flo.write("%s ...\n" % indent) + def write_array(self): + s = numpy.array(self.array).astype(numpy.uint16) + return array_to_string_destructive(s) + +class MSH_MAT_GROUP(ArrayChunk): + tag = 0x4130 + struct = "string name, short mfaces" + def read_array(self,fbuf,flags): + size = 2*self.mfaces + self.array = string_to_array(fbuf.read(size),numpy.uint16) + def dump_array(self,flo,indent,flags): + super(MSH_MAT_GROUP,self).dump_array(flo,indent,flags) + if flags['arraylines'] == 0: + return + if flags['arraylines'] < 0: + n = self.mfaces + else: + n = min(flags['arraylines'],self.mfaces) + for i in xrange(n): + flo.write("%s %10d\n" % (indent,self.array[i])) + if n < self.mfaces: + flo.write("%s ...\n" % indent) + def write_array(self): + s = numpy.array(self.array).astype(numpy.uint16) + return array_to_string_destructive(s) + swallow = True + +class TEX_VERTS(ArrayChunk): + tag = 0x4140 + struct = "short npoints" + def read_array(self,fbuf,flags): + size = 8*self.npoints + a = string_to_array(fbuf.read(size),numpy.float32) + a = numpy.reshape(a,(self.npoints,2)) + self.array = numpy.array(a) + def dump_array(self,flo,indent,flags): + super(TEX_VERTS,self).dump_array(flo,indent,flags) + if flags['arraylines'] == 0: + return + if flags['arraylines'] < 0: + n = self.npoints + else: + n = min(flags['arraylines'],self.npoints) + for i in xrange(n): + flo.write("%s %12.4g%12.4g\n" + % (indent,self.array[i,0],self.array[i,1])) + if n < self.npoints: + flo.write("%s ...\n" % indent) + def write_array(self): + s = numpy.array(self.array).astype(numpy.float32) + return array_to_string_destructive(s) + swallow = True + +class SMOOTH_GROUP(ArrayChunk): + tag = 0x4150 + def read_array(self,fbuf,flags): + self.array = string_to_array(fbuf.read_rest(),numpy.uint32) + def dump_array(self,flo,indent,flags): + super(SMOOTH_GROUP,self).dump_array(flo,indent,flags) + if flags['arraylines'] == 0: + return + if flags['arraylines'] < 0: + n = len(self.array) + else: + n = min(flags['arraylines'],len(self.array)) + for i in xrange(n): + flo.write("%s %10d\n" % (indent,self.array[i])) + if n < len(self.array): + flo.write("%s ...\n" % indent) + def write_array(self): + s = numpy.array(self.array).astype(numpy.uint32) + return array_to_string_destructive(s) + swallow = True + +class MESH_MATRIX(MatrixChunk): + tag = 0x4160 + +class MESH_COLOR(ChunkBase): + tag = 0x4165 + struct = "byte color_index" + +class MESH_TEXTURE_INFO(MatrixChunk): + tag = 0x4170 + __slots__ = [ 'icon_width', 'icon_height', 'cyl_height' ] + struct = ("float x_tiling, float y_tiling, float icon_x," + "float icon_y, float icon_z, float scaling") + def read(self,fbuf,flags): + self.read_struct(fbuf,flags) + self.read_array(fbuf,flags) + self.icon_width = self.get_float(fbuf,flags) + self.icon_height = self.get_float(fbuf,flags) + self.cyl_height = self.get_float(fbuf,flags) + self.read_chunks(fbuf,flags) + def dump(self,flo,indent,flags): + indent += ' ' + self.dump_header(flo,flags) + self.dump_struct(flo,indent,flags) + self.dump_array(flo,indent,flags) + self.out_attr('icon_width',flo,indent,flags) + self.out_attr('icon_height',flo,indent,flags) + self.out_attr('cyl_height',flo,indent,flags) + self.dump_chunks(flo,indent,flags) + def write(self): + s = [] + s.append(self.write_struct()) + s.append(self.write_array()) + s.append(self.out_float(self.icon_width)) + s.append(self.out_float(self.icon_height)) + s.append(self.out_float(self.cyl_height)) + s.append(self.write_chunks()) + return ''.join(s) + +class PROC_NAME(ChunkBase): + tag = 0x4181 + struct = "string value" + swallow = True + +class PROC_DATA(UndefinedChunk): + tag = 0x4181 + struct = "string value" + swallow = True + +class MSH_BOXMAP(ChunkBase): + tag = 0x4190 + struct = ("string front, string back, string left," + "string right, string top, string bottom") + swallow = True + + +class N_DIRECT_LIGHT(ChunkBase): + tag = 0x4600 + struct = "float light_x, float light_y, float light_z" + single = ("COLOR_F color, COLOR_24 color," + "DL_OFF switch," + "DL_OUTER_RANGE outer_range," + "DL_INNER_RANGE inner_range," + "DL_MULTIPLIER multiplier," + "DL_SPOTLIGHT spotlight," + "DL_ATTENUATE attenuate") + multiple = "DL_EXCLUDE excludes" + +class DL_SPOTLIGHT(ChunkBase): + tag = 0x4610 + struct = ("float spot_x, float spot_y, float spot_z," + "float hotspot_angle, float falloff_angle") + single = ("DL_SPOT_ROLL roll_angle," + "DL_SHADOWED shadowed," + "DL_LOCAL_SHADOW2 local_shadow," + "DL_SEE_CONE see_cone," + "DL_SPOT_RECTANGULAR rectangular," + "DL_SPOT_ASPECT aspect," + "DL_SPOT_PROJECTOR projector," + "DL_SPOT_OVERSHOOT overshoot," + "DL_RAY_BIAS bias," + "DL_RAYSHADE rayshade") + +class DL_OFF(ChunkBase): + tag = 0x4620 + +class DL_ATTENUATE(ChunkBase): + tag = 0x4625 + +class DL_RAYSHADE(ChunkBase): + tag = 0x4627 + +class DL_SHADOWED(ChunkBase): + tag = 0x4630 + +class DL_LOCAL_SHADOW2(ChunkBase): + tag = 0x4630 + struct = "float low_bias, float filter, short mapsize" + +class DL_SEE_CONE(ChunkBase): + tag = 0x4650 + +class DL_SPOT_RECTANGULAR(ChunkBase): + tag = 0x4651 + +class DL_SPOT_OVERSHOOT(ChunkBase): + tag = 0x4652 + +class DL_SPOT_PROJECTOR(ChunkBase): + tag = 0x4653 + struct = "string filename" + swallow = True + +class DL_EXCLUDE(ChunkBase): + tag = 0x4654 + struct = "string value" + swallow = True + +class DL_SPOT_ROLL(OneFloatValueChunk): + tag = 0x4656 + +class DL_SPOT_ASPECT(OneFloatValueChunk): + tag = 0x4657 + +class DL_RAY_BIAS(OneFloatValueChunk): + tag = 0x4658 + +class DL_INNER_RANGE(OneFloatValueChunk): + tag = 0x4659 + +class DL_OUTER_RANGE(OneFloatValueChunk): + tag = 0x465A + +class DL_MULTIPLIER(OneFloatValueChunk): + tag = 0x465B + + +class N_CAMERA(ChunkBase): + tag = 0x4700 + struct = ("float camera_x, float camera_y, float camera_z," + "float target_x, float target_y, float target_z," + "float bank_angle, float focal_length") + single = "CAM_SEE_CONE see_cone, CAM_RANGES ranges" + +class CAM_SEE_CONE(ChunkBase): + tag = 0x4710 + +class CAM_RANGES(ChunkBase): + tag = 0x4720 + struct = "float near, float far" + +# +# Viewport Data +# + +class VIEWPORT_LAYOUT(ChunkBase): + tag = 0x7001 + struct = ('short form, short top, short ready,' + 'short wstate, short swapws, short swapport,' + 'short swapcur') + single = 'VIEWPORT_SIZE size' + multiple = 'VIEWPORT_DATA data, VIEWPORT_DATA_3 data' + +class ViewportDataChunk(ChunkBase): + struct = ('short flags, short axis_lockout, short win_x,' + 'short win_y, short win_w, short win_h,' + 'short win_view, float zoom, float worldcenter_x,' + 'float worldcenter_y, float worldcenter_z,' + 'float horiz_ang, float vert_ang,' + 'string cameraname') + swallow = True + +class VIEWPORT_DATA(ViewportDataChunk): + tag = 0x7011 + +class VIEWPORT_DATA_3(ViewportDataChunk): + tag = 0x7012 + +class VIEWPORT_SIZE(ChunkBase): + tag = 0x7020 + struct = 'short x, short y, short w, short h' + +class NETWORK_VIEW(UndefinedChunk): + tag = 0x7030 + +# +# Material Data +# + +class MAT_NAME(ChunkBase): + tag = 0xA000 + struct = 'string value' + swallow = True + +class MAT_AMBIENT(OneColorChunk): + tag = 0xA010 + +class MAT_DIFFUSE(OneColorChunk): + tag = 0xA020 + +class MAT_SPECULAR(OneColorChunk): + tag = 0xA030 + +class MAT_SHININESS(OnePercentageChunk): + tag = 0xA040 + +class MAT_SHIN2PCT(OnePercentageChunk): + tag = 0xA041 + +class MAT_SHIN3PCT(OnePercentageChunk): + tag = 0xA042 + +class MAT_TRANSPARENCY(OnePercentageChunk): + tag = 0xA050 + +class MAT_XPFALL(OnePercentageChunk): + tag = 0xA052 + +class MAT_REFBLUR(OnePercentageChunk): + tag = 0xA053 + +class MAT_SELF_ILLUM(ChunkBase): + tag = 0xA080 + +class MAT_TWO_SIDE(ChunkBase): + tag = 0xA081 + +class MAT_DECAL(ChunkBase): + tag = 0xA082 + +class MAT_ADDITIVE(ChunkBase): + tag = 0xA083 + +class MAT_SELF_ILPCT(OnePercentageChunk): + tag = 0xA084 + +class MAT_WIRE(ChunkBase): + tag = 0xA085 + +class MAT_SUPERSMP(UndefinedChunk): + tag = 0xA086 + +class MAT_WIRESIZE(OneFloatValueChunk): + tag = 0xA087 + +class MAT_FACEMAP(ChunkBase): + tag = 0xA088 + +class MAT_XPFALLIN(UndefinedChunk): + tag = 0xA08A + +class MAT_PHONGSOFT(ChunkBase): + tag = 0xA08C + +class MAT_WIREABS(ChunkBase): + tag = 0xA08E + +class MAT_SHADING(OneShortValueChunk): + tag = 0xA100 + +class MAT_TEXMAP(TextureChunk): + tag = 0xA200 + +class MAT_SPECMAP(TextureChunk): + tag = 0xA204 + +class MAT_OPACMAP(TextureMaskChunk): + tag = 0xA210 + +class MAT_REFLMAP(ChunkBase): + tag = 0xA220 + single = "INT_PERCENTAGE pct, MAT_MAPNAME filename" + +class MAT_BUMPMAP(TextureMaskChunk): + tag = 0xA230 + +class MAT_USE_XPFALL(ChunkBase): + tag = 0xA240 + +class MAT_USE_REFBLUR(ChunkBase): + tag = 0xA250 + +class MAT_BUMP_PERCENT(ChunkBase): + tag = 0xA252 + struct = "short value" + +class MAT_MAPNAME(ChunkBase): + tag = 0xA300 + struct = "string value" + swallow = True + +class MAT_ACUBIC(ChunkBase): + tag = 0xA310 + struct = ("byte shade, byte antialias, short rflags," + "long mapsize, long frame") + +class MAT_TEX2MAP(TextureChunk): + tag = 0xA33A + +class MAT_SHINMAP(TextureMaskChunk): + tag = 0xA33C + +class MAT_SELFIMAP(TextureChunk): + tag = 0xA33D + +class MAT_TEXMASK(TextureMaskChunk): + tag = 0xA33E + +class MAT_TEXT2MASK(TextureMaskChunk): + tag = 0xA340 + +class MAT_OPACMASK(TextureMaskChunk): + tag = 0xA342 + +class MAT_BUMPMASK(TextureMaskChunk): + tag = 0xA344 + +class MAT_SHINMASK(TextureMaskChunk): + tag = 0xA346 + +class MAT_SPECMASK(TextureMaskChunk): + tag = 0xA348 + +class MAT_SELFIMASK(TextureChunk): + tag = 0xA34A + +class MAT_REFLMASK(TextureChunk): + tag = 0xA34C + +class MAT_MAP_TILING(OneShortValueChunk): + tag = 0xA351 + +class MAT_MAP_TEXBLUR(OneFloatValueChunk): + tag = 0xA353 + +class MAT_MAP_USCALE(OneFloatValueChunk): + tag = 0xA354 + +class MAT_MAP_VSCALE(OneFloatValueChunk): + tag = 0xA356 + +class MAT_MAP_UOFFSET(OneFloatValueChunk): + tag = 0xA358 + +class MAT_MAP_VOFFSET(OneFloatValueChunk): + tag = 0xA35A + +class MAT_MAP_ANG(OneFloatValueChunk): + tag = 0xA35C + +class MAT_MAP_COL1(Color24Chunk): + tag = 0xA360 + +class MAT_MAP_COL2(Color24Chunk): + tag = 0xA362 + +class MAT_MAP_RCOL(Color24Chunk): + tag = 0xA364 + +class MAT_MAP_GCOL(Color24Chunk): + tag = 0xA366 + +class MAT_MAP_BCOL(Color24Chunk): + tag = 0xA368 + +class MAT_ENTRY(ChunkBase): + tag = 0xAFFF + single = ("MAT_NAME name," + "MAT_AMBIENT ambient," + "MAT_DIFFUSE diffuse," + "MAT_SPECULAR specular," + "MAT_SHININESS shininess," + "MAT_SHIN2PCT shin2pct," + "MAT_TRANSPARENCY transparency," + "MAT_XPFALL xpfall," + "MAT_REFBLUR refblur," + "MAT_SELF_ILLUM self_illum," + "MAT_SHADING shading," + "MAT_SELF_ILPCT self_ilpct," + "MAT_USE_XPFALL use_xpfall," + "MAT_USE_REFBLUR use_refblur," + "MAT_TWO_SIDE two_side," + "MAT_ADDITIVE additive," + "MAT_WIRE wire," + "MAT_FACEMAP facemap," + "MAT_PHONGSOFT phongsoft," + "MAT_WIRESIZE wiresize," + "MAT_DECAL decal," + "MAT_TEXMAP texmap," + "MAT_SXP_TEXT_DATA sxp_text_data," + "MAT_TEXMASK texmask," + "MAT_SXP_TEXT_MASKDATA sxp_text_maskdata," + "MAT_TEX2MAP tex2map," + "MAT_SXP_TEXT2_DATA sxp_text2_data," + "MAT_TEXT2MASK text2mask," + "MAT_SXP_TEXT2_MASKDATA sxp_text2_maskdata," + "MAT_OPACMAP opacmap," + "MAT_SXP_OPAC_DATA sxp_opac_data," + "MAT_OPACMASK opac_mask," + "MAT_SXP_OPAC_MASKDATA sxp_opac_maskdata," + "MAT_BUMPMAP bumpmap," + "MAT_SXP_BUMP_DATA sxp_bump_data," + "MAT_BUMPMASK bumpmask," + "MAT_SXP_BUMP_MASKDATA sxp_bump_maskdata," + "MAT_SPECMAP specmap," + "MAT_SXP_SPEC_DATA sxp_spec_data," + "MAT_SPECMASK specmask," + "MAT_SXP_SPEC_MASKDATA sxp_spec_maskdata," + "MAT_SHINMAP shinmap," + "MAT_SXP_SHIN_DATA sxp_shin_data," + "MAT_SHINMASK shinmask," + "MAT_SXP_SHIN_MASKDATA sxp_shin_maskdata," + "MAT_SELFIMAP selfimap," + "MAT_SXP_SELFI_DATA sxp_selfi_data," + "MAT_SELFIMASK selfimask," + "MAT_SXP_SELFI_MASKDATA sxp_selfi_maskdata," + "MAT_REFLMAP reflmap," + "MAT_REFLMASK reflmask," + "MAT_SXP_REFL_MASKDATA sxp_refl_maskdata," + "MAT_ACUBIC acubic") + +class MAT_SXP_TEXT_DATA(UndefinedChunk): tag = 0xA320 +class MAT_SXP_TEXT2_DATA(UndefinedChunk): tag = 0xA321 +class MAT_SXP_OPAC_DATA(UndefinedChunk): tag = 0xA322 +class MAT_SXP_BUMP_DATA(UndefinedChunk): tag = 0xA324 +class MAT_SXP_SPEC_DATA(UndefinedChunk): tag = 0xA325 +class MAT_SXP_SHIN_DATA(UndefinedChunk): tag = 0xA326 +class MAT_SXP_SELFI_DATA(UndefinedChunk): tag = 0xA328 +class MAT_SXP_TEXT_MASKDATA(UndefinedChunk): tag = 0xA32A +class MAT_SXP_TEXT2_MASKDATA(UndefinedChunk): tag = 0xA32C +class MAT_SXP_OPAC_MASKDATA(UndefinedChunk): tag = 0xA32E +class MAT_SXP_BUMP_MASKDATA(UndefinedChunk): tag = 0xA330 +class MAT_SXP_SPEC_MASKDATA(UndefinedChunk): tag = 0xA332 +class MAT_SXP_SHIN_MASKDATA(UndefinedChunk): tag = 0xA334 +class MAT_SXP_SELFI_MASKDATA(UndefinedChunk): tag = 0xA336 +class MAT_SXP_REFL_MASKDATA(UndefinedChunk): tag = 0xA338 + +# +# Keyframe Section +# + +class KFDATA(ChunkBase): + tag = 0xB000 + single = "KFHDR kfhdr, KFSEG kfseg, KFCURTIME kfcurtime" + multiple = ("OBJECT_NODE_TAG object_nodes," + "CAMERA_NODE_TAG camera_nodes," + "TARGET_NODE_TAG target_nodes," + "LIGHT_NODE_TAG light_nodes," + "SPOTLIGHT_NODE_TAG spotlight_nodes," + "L_TARGET_NODE_TAG l_target_nodes," + "AMBIENT_NODE_TAG ambient_nodes") + +class KFHDR(ChunkBase): + tag = 0xB00A + struct = "short revision, string filename, long anim_length" + +class KFSEG(ChunkBase): + tag = 0xB008 + struct = "long first_frame, long last_frame" + +class KFCURTIME(ChunkBase): + tag = 0xB009 + struct = "long current_frame" + +class AMBIENT_NODE_TAG(ChunkBase): + tag = 0xB001 + single = "NODE_ID node_id, NODE_HDR node_hdr, COL_TRACK_TAG col_track" + +class OBJECT_NODE_TAG(ChunkBase): + tag = 0xB002 + single = ("NODE_ID node_id," + "NODE_HDR node_hdr," + "PIVOT pivot," + "INSTANCE_NAME instance_name," + "BOUNDBOX bounding_box," + "POS_TRACK_TAG pos_track," + "ROT_TRACK_TAG rot_track," + "SCL_TRACK_TAG scl_track," + "MORPH_TRACK_TAG morph_track," + "HIDE_TRACK_TAG hide_track," + "MORPH_SMOOTH morph_smooth") + + +class CAMERA_NODE_TAG(ChunkBase): + tag = 0xB003 + single = ("NODE_ID node_id," + "NODE_HDR node_hdr," + "POS_TRACK_TAG pos_track," + "FOV_TRACK_TAG fov_track," + "ROLL_TRACK_TAG roll_track") + +class TARGET_NODE_TAG(ChunkBase): + tag = 0xB004 + single = ("NODE_ID node_id," + "NODE_HDR node_hdr," + "POS_TRACK_TAG pos_track") + + +class LIGHT_NODE_TAG(ChunkBase): + tag = 0xB005 + single = ("NODE_ID node_id," + "NODE_HDR node_hdr," + "POS_TRACK_TAG pos_track," + "COL_TRACK_TAG col_track") + +class SPOTLIGHT_NODE_TAG(ChunkBase): + tag = 0xB007 + single = ("NODE_ID node_id," + "NODE_HDR node_hdr," + "POS_TRACK_TAG pos_track," + "COL_TRACK_TAG col_track," + "HOT_TRACK_TAG hot_track," + "FALL_TRACK_TAG fall_track," + "ROLL_TRACK_TAG roll_track") + +class L_TARGET_NODE_TAG(ChunkBase): + tag = 0xB006 + single = ("NODE_ID node_id," + "NODE_HDR node_hdr," + "POS_TRACK_TAG pos_track," + "COL_TRACK_TAG col_track") + +class NODE_ID(ChunkBase): + tag = 0xB030 + struct = "short id" + +class NODE_HDR(ChunkBase): + tag = 0xB010 + struct = "string name, short flags1, short flags2, short parent" + +class PIVOT(ChunkBase): + tag = 0xB013 + struct = "float pivot_x, float pivot_y, float pivot_z" + +class INSTANCE_NAME(ChunkBase): + tag = 0xB011 + struct = "string name" + +class MORPH_SMOOTH(ChunkBase): + tag = 0xB015 + struct = "float smooth_angle" + +class BOUNDBOX(ChunkBase): + tag = 0xB014 + struct = ("float min_x, float min_y, float min_z," + "float max_x, float max_y, float max_z") + +class POS_TRACK_TAG(TrackChunk): + tag = 0xB020 + keyframe = "float pos_x, float pos_y, float pos_z" + +class COL_TRACK_TAG(TrackChunk): + tag = 0xB025 + keyframe = "float red, float green, float blue" + +class ROT_TRACK_TAG(TrackChunk): + tag = 0xB021 + keyframe = "float angle, float axis_x, float axis_y, float axis_z" + +class SCL_TRACK_TAG(TrackChunk): + tag = 0xB022 + keyframe = "float scl_x, float scl_y, float scl_z" + +class MORPH_TRACK_TAG(TrackChunk): + tag = 0xB026 + keyframe = "string name" + +class FOV_TRACK_TAG(TrackChunk): + tag = 0xB023 + keyframe = "float angle" + +class ROLL_TRACK_TAG(TrackChunk): + tag = 0xB024 + keyframe = "float angle" + +class HOT_TRACK_TAG(TrackChunk): + tag = 0xB027 + keyframe = "float angle" + +class FALL_TRACK_TAG(TrackChunk): + tag = 0xB028 + keyframe = "float angle" + +class HIDE_TRACK_TAG(UndefinedChunk): + tag = 0xB029 + +# +# Misc +# + +class DUMMY(ChunkBase): tag = 0xFFFF + +# +# Obsolute Chunks +# + +class VIEWPORT_LAYOUT_OLD(UndefinedChunk): tag = 0x7000 +class VIEWPORT_DATA_OLD(UndefinedChunk): tag = 0x7010 +class OLD_MAT_GROUP(UndefinedChunk): tag = 0x4131 +class MAT_MAP_TILING_OLD(UndefinedChunk): tag = 0xA350 +class MAT_MAP_TEXBLUR_OLD(UndefinedChunk): tag = 0xA352 + + +# +# Functions to operate on chunks +# + +def read_3ds_mem(membuf,check_magic=True,tight=False,recover=True): + """Create a 3DS DOM from a memory buffer. + + dom = read_3ds_mem(buffer,check_magic=True,tight=False, + recover=True) + + buffer: is an image of the 3DS file in memory. It could be + a string, an mmaped file, or something else. + + check_magic: If true, this function checks that the top level + chunk is the 3DS magic chunk (0x4D4D), and raises an exception + if it is not. + + tight: Whether to use tighter error checking. Try disabling + if getting 3DS format errors. + + recover: Whether to emit an Error chunk when an error is found; + otherwise raise an exception. + + """ + + if check_magic: + tag,length = struct.unpack(" sinB2 + sinC2, numpy.pi - angA, angA) + angB = numpy.where(sinB2 > sinA2 + sinC2, numpy.pi - angB, angB) + angC = numpy.where(sinC2 > sinA2 + sinB2, numpy.pi - angC, angC) + rnorms[0::3][ndg] = p*(angA/lp)[:,numpy.newaxis] + rnorms[1::3][ndg] = p*(angB/lp)[:,numpy.newaxis] + rnorms[2::3][ndg] = p*(angC/lp)[:,numpy.newaxis] + + # normalize vectors according to passed in smoothing group + + lex = numpy.lexsort(numpy.transpose(points)) + brs = numpy.nonzero( + numpy.any(points[lex[1:],:]-points[lex[:-1],:],axis=1))[0]+1 + lslice = numpy.empty((len(brs)+1,),numpy.int) + lslice[0] = 0 + lslice[1:] = brs + rslice = numpy.empty((len(brs)+1,),numpy.int) + rslice[:-1] = brs + rslice[-1] = 3*m + for i in xrange(len(brs)+1): + rgroup = lex[lslice[i]:rslice[i]] + xgroup = exarray[rgroup] + normpat = numpy.logical_or( + numpy.bitwise_and.outer(xgroup,xgroup), + numpy.eye(len(xgroup))) + fnorms[rgroup,:] = numpy.dot(normpat,rnorms[rgroup,:]) + q = numpy.sum(fnorms*fnorms,axis=1) + qnz = numpy.nonzero(q)[0] + lq = 1.0 / numpy.sqrt(q[qnz]) + fnt = numpy.transpose(fnorms) + fnt[:,qnz] *= lq + + # we're done + + return points, numpy.asarray(fnorms,numpy.float32) diff --git a/src/Mod/Arch/Init.py b/src/Mod/Arch/Init.py index a1a994dc6..46ebd63bb 100644 --- a/src/Mod/Arch/Init.py +++ b/src/Mod/Arch/Init.py @@ -28,3 +28,4 @@ FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)","importOBJ") FreeCAD.addExportType("WebGL file (*.html)","importWebGL") FreeCAD.addImportType("Collada (*.dae)","importDAE") FreeCAD.addExportType("Collada (*.dae)","importDAE") +FreeCAD.addImportType("3D Studio mesh (*.3ds)","import3DS") diff --git a/src/Mod/Arch/import3DS.py b/src/Mod/Arch/import3DS.py new file mode 100644 index 000000000..b63875de4 --- /dev/null +++ b/src/Mod/Arch/import3DS.py @@ -0,0 +1,105 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2016 Yorik van Havre * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import os,FreeCAD,Mesh + +__title__="FreeCAD 3DS importer" +__author__ = "Yorik van Havre" +__url__ = "http://www.freecadweb.org" + +DEBUG = True + + +def check3DS(): + "checks if collada if available" + global dom3ds + dom3ds = None + try: + from Dice3DS import dom3ds + except ImportError: + FreeCAD.Console.PrintError("Dice3DS not found, 3DS support is disabled.\n") + return False + else: + return True + + +def open(filename): + "called when freecad wants to open a file" + if not check3DS(): + return + docname = (os.path.splitext(os.path.basename(filename))[0]).encode("utf8") + doc = FreeCAD.newDocument(docname) + doc.Label = decode(docname) + FreeCAD.ActiveDocument = doc + read(filename) + return doc + + +def insert(filename,docname): + "called when freecad wants to import a file" + if not check3DS(): + return + try: + doc = FreeCAD.getDocument(docname) + except NameError: + doc = FreeCAD.newDocument(docname) + FreeCAD.ActiveDocument = doc + read(filename) + return doc + + +def decode(name): + "decodes encoded strings" + try: + decodedName = (name.decode("utf8")) + except UnicodeDecodeError: + try: + decodedName = (name.decode("latin1")) + except UnicodeDecodeError: + FreeCAD.Console.PrintError(translate("Arch","Error: Couldn't determine character encoding")) + decodedName = name + return decodedName + + +def read(filename): + dom = dom3ds.read_3ds_file(filename,tight=False) + + for j,d_nobj in enumerate(dom.mdata.objects): + if type(d_nobj.obj) != dom3ds.N_TRI_OBJECT: + continue + verts = [] + if d_nobj.obj.points: + for d_point in d_nobj.obj.points.array: + verts.append([d_point[0],d_point[1],d_point[2]]) + meshdata = [] + for d_face in d_nobj.obj.faces.array: + meshdata.append([verts[int(d_face[i])] for i in xrange(3)]) + m = [tuple(r) for r in d_nobj.obj.matrix.array] + m = m[0] + m[1] + m[2] + m[3] + placement = FreeCAD.Placement(FreeCAD.Matrix(*m)) + mesh = Mesh.Mesh(meshdata) + obj = FreeCAD.ActiveDocument.addObject("Mesh::Feature","Mesh") + obj.Mesh = mesh + obj.Placement = placement + else: + print "Skipping object without vertices array: ",d_nobj.obj +