From dff14c587a3fb06bc98e6fa3504f5881c03e9e5d Mon Sep 17 00:00:00 2001 From: Sebastian Sille <nrgsille@gmx.de> Date: Thu, 24 Sep 2020 11:18:11 +0200 Subject: [PATCH] Signed-off-by: Sebastian Sille <nrgsille@gmx.de> --- io_scene_3ds/__init__.py | 18 +- io_scene_3ds/export_3ds.py | 628 +++++++++++++++++++--------- io_scene_3ds/import_3ds.py | 809 ++++++++++++++++++++++--------------- 3 files changed, 941 insertions(+), 514 deletions(-) diff --git a/io_scene_3ds/__init__.py b/io_scene_3ds/__init__.py index b6f8b4c0..af4aaa56 100644 --- a/io_scene_3ds/__init__.py +++ b/io_scene_3ds/__init__.py @@ -20,9 +20,9 @@ bl_info = { "name": "Autodesk 3DS format", - "author": "Bob Holcomb, Campbell Barton, Andreas Atteneder", - "version": (2, 0, 0), - "blender": (2, 80, 0), + "author": "Bob Holcomb, Campbell Barton, Andreas Atteneder, Sebastian Schrand", + "version": (2, 1, 0), + "blender": (2, 82, 0), "location": "File > Import", "description": "Import 3DS, meshes, uvs, materials, textures, " "cameras & lamps", @@ -86,6 +86,12 @@ class Import3DS(bpy.types.Operator, ImportHelper): default=True, ) + read_keyframe: bpy.props.BoolProperty( + name="Read Keyframe", + description="Read the keyframe data", + default=True, + ) + def execute(self, context): from . import import_3ds @@ -147,8 +153,7 @@ def menu_func_import(self, context): def register(): bpy.utils.register_class(Import3DS) -# TODO: Restore export -# bpy.utils.register_class(Export3DS) + bpy.utils.register_class(Export3DS) bpy.types.TOPBAR_MT_file_import.append(menu_func_import) bpy.types.TOPBAR_MT_file_export.append(menu_func_export) @@ -156,8 +161,7 @@ def register(): def unregister(): bpy.utils.unregister_class(Import3DS) -# TODO: Restore export -# bpy.utils.unregister_class(Export3DS) + bpy.utils.unregister_class(Export3DS) bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) bpy.types.TOPBAR_MT_file_export.remove(menu_func_export) diff --git a/io_scene_3ds/export_3ds.py b/io_scene_3ds/export_3ds.py index a81cd11d..d96d6651 100644 --- a/io_scene_3ds/export_3ds.py +++ b/io_scene_3ds/export_3ds.py @@ -19,13 +19,20 @@ # <pep8 compliant> # Script copyright (C) Bob Holcomb -# Contributors: Campbell Barton, Bob Holcomb, Richard Lärkäng, Damien McGinnes, Mark Stijnman +# Contributors: Campbell Barton, Bob Holcomb, Richard Lärkäng, Damien McGinnes, Mark Stijnman, Sebastian Sille """ Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen) and using information from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. """ +import bpy +import math +import struct +import mathutils +import bpy_extras +from bpy_extras import node_shader_utils + ###################################################### # Data Structures ###################################################### @@ -35,11 +42,13 @@ from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. PRIMARY = 0x4D4D #------ Main Chunks -OBJECTINFO = 0x3D3D # This gives the version of the mesh and is found right before the material and object information VERSION = 0x0002 # This gives the version of the .3ds file KFDATA = 0xB000 # This is the header for all of the key frame info #------ sub defines of OBJECTINFO +OBJECTINFO = 0x3D3D # Main mesh object chunk before the material and object information +MESHVERSION = 0x3D3E # This gives the version of the mesh +AMBIENTLIGHT = 0x2100 # The color of the ambient light MATERIAL = 45055 # 0xAFFF // This stored the texture info OBJECT = 16384 # 0x4000 // This stores the faces, vertices, etc... @@ -47,40 +56,64 @@ OBJECT = 16384 # 0x4000 // This stores the faces, vertices, etc... MATNAME = 0xA000 # This holds the material name MATAMBIENT = 0xA010 # Ambient color of the object/material MATDIFFUSE = 0xA020 # This holds the color of the object/material -MATSPECULAR = 0xA030 # SPecular color of the object/material -MATSHINESS = 0xA040 # ?? +MATSPECULAR = 0xA030 # Specular color of the object/material +MATSHINESS = 0xA040 # Specular intensity of the object/material (percent) +MATSHIN2 = 0xA041 # Reflection of the object/material (percent) +MATSHIN3 = 0xA042 # metallic/mirror of the object/material (percent) +MATTRANS = 0xA050 # Transparency value (100-OpacityValue) (percent) +MATSELFILPCT = 0xA084 # Self illumination strength (percent) +MATSHADING = 0xA100 # Material shading method MAT_DIFFUSEMAP = 0xA200 # This is a header for a new diffuse texture +MAT_SPECMAP = 0xA204 # head for specularity map MAT_OPACMAP = 0xA210 # head for opacity map -MAT_BUMPMAP = 0xA230 # read for normal map -MAT_SPECMAP = 0xA204 # read for specularity map - -#>------ sub defines of MAT_???MAP +MAT_REFLMAP = 0xA220 # head for reflect map +MAT_BUMPMAP = 0xA230 # head for normal map +MAT_BUMP_PERCENT = 0xA252 # Normalmap strength (percent) +MAT_TEX2MAP = 0xA33A # head for secondary texture +MAT_SHINMAP = 0xA33C # head for roughness map +MAT_SELFIMAP = 0xA33D # head for emission map + +#>------ sub defines of MAT_MAP MATMAPFILE = 0xA300 # This holds the file name of a texture - MAT_MAP_TILING = 0xa351 # 2nd bit (from LSB) is mirror UV flag +MAT_MAP_TEXBLUR = 0xA353 # Texture blurring factor MAT_MAP_USCALE = 0xA354 # U axis scaling MAT_MAP_VSCALE = 0xA356 # V axis scaling MAT_MAP_UOFFSET = 0xA358 # U axis offset MAT_MAP_VOFFSET = 0xA35A # V axis offset MAT_MAP_ANG = 0xA35C # UV rotation around the z-axis in rad - -RGB1 = 0x0011 -RGB2 = 0x0012 +MAP_COL1 = 0xA360 # Tint Color1 +MAP_COL2 = 0xA362 # Tint Color2 +MAP_RCOL = 0xA364 # Red tint +MAP_GCOL = 0xA366 # Green tint +MAP_BCOL = 0xA368 # Blue tint + +RGB = 0x0010 # RGB float +RGB1 = 0x0011 # RGB Color1 +RGB2 = 0x0012 # RGB Color2 +PCT = 0x0030 # Percent chunk +MASTERSCALE = 0x0100 # Master scale factor #>------ sub defines of OBJECT OBJECT_MESH = 0x4100 # This lets us know that we are reading a new object -OBJECT_LIGHT = 0x4600 # This lets un know we are reading a light object -OBJECT_CAMERA = 0x4700 # This lets un know we are reading a camera object +OBJECT_LIGHT = 0x4600 # This lets us know we are reading a light object +OBJECT_CAMERA = 0x4700 # This lets us know we are reading a camera object + +#>------ Sub defines of LIGHT +LIGHT_MULTIPLIER = 0x465B # The light energy factor +LIGHT_SPOTLIGHT = 0x4610 # The target of a spotlight +LIGHT_SPOTROLL = 0x4656 # The roll angle of the spot #>------ sub defines of CAMERA -OBJECT_CAM_RANGES = 0x4720 # The camera range values +OBJECT_CAM_RANGES = 0x4720 # The camera range values #>------ sub defines of OBJECT_MESH OBJECT_VERTICES = 0x4110 # The objects vertices OBJECT_FACES = 0x4120 # The objects faces OBJECT_MATERIAL = 0x4130 # This is found if the object has a material, either texture map or color OBJECT_UV = 0x4140 # The UV texture coordinates +OBJECT_SMOOTH = 0x4150 # The objects smooth groups OBJECT_TRANS_MATRIX = 0x4160 # The Object Matrix #>------ sub defines of KFDATA @@ -98,7 +131,6 @@ POS_TRACK_TAG = 0xB020 ROT_TRACK_TAG = 0xB021 SCL_TRACK_TAG = 0xB022 -import struct # So 3ds max can open files, limit names to 12 in length # this is very annoying for filenames! @@ -202,9 +234,9 @@ class _3ds_string(object): file.write(struct.pack(binary_format, self.value)) def __str__(self): - return self.value - + return str(self.value) + class _3ds_point_3d(object): """Class representing a three-dimensional point for a 3ds file.""" __slots__ = "x", "y", "z" @@ -258,6 +290,23 @@ class _3ds_point_uv(object): def __str__(self): return '(%g, %g)' % self.uv + +class _3ds_float_color(object): + """Class representing a rgb float color for a 3ds file.""" + __slots__ = "r", "g", "b" + + def __init__(self, col): + self.r, self.g, self.b = col + + def get_size(self): + return 3 * SZ_FLOAT + + def write(self, file): + file.write(struct.pack('3f', self.r, self.g, self.b)) + + def __str__(self): + return '{%f, %f, %f}' % (self.r, self.g, self.b) + class _3ds_rgb_color(object): """Class representing a (24-bit) rgb color for a 3ds file.""" @@ -436,24 +485,25 @@ class _3ds_chunk(object): # EXPORT ###################################################### -def get_material_image_texslots(material): - # blender utility func. +def get_material_image(material): + """ Get images from paint slots.""" if material: - return [s for s in material.texture_slots if s and s.texture.type == 'IMAGE' and s.texture.image] - - return [] - - """ - images = [] - if material: - for mtex in material.getTextures(): - if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: - image = mtex.tex.image - if image: - images.append(image) # maye want to include info like diffuse, spec here. - return images - """ - + pt = material.paint_active_slot + tex = material.texture_paint_images + if pt < len(tex): + slot = tex[pt] + if slot.type == 'IMAGE': + return slot + +def get_uv_image(ma): + """ Get image from material wrapper.""" + if ma and ma.use_nodes: + ma_wrap = node_shader_utils.PrincipledBSDFWrapper(ma) + ma_tex = ma_wrap.base_color_texture + if ma_tex and ma_tex.image is not None: + return ma_tex.image + else: + return get_material_image(ma) def make_material_subchunk(chunk_id, color): """Make a material subchunk. @@ -469,83 +519,123 @@ def make_material_subchunk(chunk_id, color): #mat_sub.add_subchunk(col2) return mat_sub +def make_percent_subchunk(chunk_id, percent): + """Make a percentage based subchunk.""" + pct_sub = _3ds_chunk(chunk_id) + pcti = _3ds_chunk(PCT) + pcti.add_variable("percent", _3ds_ushort(int(round(percent * 100,0)))) + pct_sub.add_subchunk(pcti) + return pct_sub + +def make_texture_chunk(chunk_id, images): + """Make Material Map texture chunk.""" + # Add texture percentage value (100 = 1.0) + ma_sub = make_percent_subchunk(chunk_id, 1) + has_entry = False -def make_material_texture_chunk(chunk_id, texslots, tess_uv_image=None): - """Make Material Map texture chunk given a seq. of `MaterialTextureSlot`'s - - `tess_uv_image` is optionally used as image source if the slots are - empty. No additional filtering for mapping modes is done, all - slots are written "as is". - """ + def add_image(img): + filename = bpy.path.basename(image.filepath) + ma_sub_file = _3ds_chunk(MATMAPFILE) + ma_sub_file.add_variable("image", _3ds_string(sane_name(filename))) + ma_sub.add_subchunk(ma_sub_file) + + for image in images: + add_image(image) + has_entry = True + + return ma_sub if has_entry else None - mat_sub = _3ds_chunk(chunk_id) +def make_material_texture_chunk(chunk_id, texslots, pct): + """Make Material Map texture chunk given a seq. of `MaterialTextureSlot`'s + Paint slots are optionally used as image source if no nodes are + used. No additional filtering for mapping modes is done, all + slots are written "as is".""" + # Add texture percentage value + mat_sub = make_percent_subchunk(chunk_id, pct) has_entry = False - import bpy - def add_texslot(texslot): - texture = texslot.texture - image = texture.image + image = texslot.image filename = bpy.path.basename(image.filepath) mat_sub_file = _3ds_chunk(MATMAPFILE) mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename))) mat_sub.add_subchunk(mat_sub_file) + for link in texslot.socket_dst.links: + socket = link.from_socket.identifier maptile = 0 # no perfect mapping for mirror modes - 3DS only has uniform mirror w. repeat=2 - if texture.extension == 'REPEAT' and (texture.use_mirror_x and texture.repeat_x > 1) \ - or (texture.use_mirror_y and texture.repeat_y > 1): - maptile |= 0x2 + if texslot.extension == 'EXTEND': + maptile |= 0x1 # CLIP maps to 3DS' decal flag - elif texture.extension == 'CLIP': + elif texslot.extension == 'CLIP': maptile |= 0x10 mat_sub_tile = _3ds_chunk(MAT_MAP_TILING) - mat_sub_tile.add_variable("maptiling", _3ds_ushort(maptile)) + mat_sub_tile.add_variable("tiling", _3ds_ushort(maptile)) mat_sub.add_subchunk(mat_sub_tile) + if socket=='Alpha': + mat_sub_alpha = _3ds_chunk(MAP_TILING) + alphaflag = 0x40 # summed area sampling 0x20 + mat_sub_alpha.add_variable("alpha", _3ds_ushort(alphaflag)) + mat_sub.add_subchunk(mat_sub_alpha) + if texslot.socket_dst.identifier in {'Base Color', 'Specular'}: + mat_sub_tint = _3ds_chunk(MAP_TILING) # RGB tint 0x200 + tint = 0x80 if texslot.image.colorspace_settings.name=='Non-Color' else 0x200 + mat_sub_tint.add_variable("tint", _3ds_ushort(tint)) + mat_sub.add_subchunk(mat_sub_tint) + + mat_sub_texblur = _3ds_chunk(MAT_MAP_TEXBLUR) # Based on observation this is usually 1.0 + mat_sub_texblur.add_variable("maptexblur", _3ds_float(1.0)) + mat_sub.add_subchunk(mat_sub_texblur) + mat_sub_uscale = _3ds_chunk(MAT_MAP_USCALE) - mat_sub_uscale.add_variable("mapuscale", _3ds_float(texslot.scale[0])) + mat_sub_uscale.add_variable("mapuscale", _3ds_float(round(texslot.scale[0], 6))) mat_sub.add_subchunk(mat_sub_uscale) mat_sub_vscale = _3ds_chunk(MAT_MAP_VSCALE) - mat_sub_vscale.add_variable("mapuscale", _3ds_float(texslot.scale[1])) + mat_sub_vscale.add_variable("mapvscale", _3ds_float(round(texslot.scale[1], 6))) mat_sub.add_subchunk(mat_sub_vscale) - + mat_sub_uoffset = _3ds_chunk(MAT_MAP_UOFFSET) - mat_sub_uoffset.add_variable("mapuoffset", _3ds_float(texslot.offset[0])) + mat_sub_uoffset.add_variable("mapuoffset", _3ds_float(round(texslot.translation[0], 6))) mat_sub.add_subchunk(mat_sub_uoffset) mat_sub_voffset = _3ds_chunk(MAT_MAP_VOFFSET) - mat_sub_voffset.add_variable("mapvoffset", _3ds_float(texslot.offset[1])) + mat_sub_voffset.add_variable("mapvoffset", _3ds_float(round(texslot.translation[1], 6))) mat_sub.add_subchunk(mat_sub_voffset) + mat_sub_angle = _3ds_chunk(MAT_MAP_ANG) + mat_sub_angle.add_variable("mapangle", _3ds_float(round(texslot.rotation[2],6))) + mat_sub.add_subchunk(mat_sub_angle) + + if texslot.socket_dst.identifier in {'Base Color', 'Specular'}: + rgb = _3ds_chunk(MAP_COL1) # Add tint color + base = texslot.owner_shader.material.diffuse_color[:3] + spec = texslot.owner_shader.material.specular_color[:] + rgb.add_variable("mapcolor", _3ds_rgb_color(spec if texslot.socket_dst.identifier == 'Specular' else base)) + mat_sub.add_subchunk(rgb) + # store all textures for this mapto in order. This at least is what # the 3DS exporter did so far, afaik most readers will just skip # over 2nd textures. for slot in texslots: - add_texslot(slot) - has_entry = True - - # image from tess. UV face - basically the code above should handle - # this already. No idea why its here so keep it :-) - if tess_uv_image and not has_entry: - has_entry = True - - filename = bpy.path.basename(tess_uv_image.filepath) - mat_sub_file = _3ds_chunk(MATMAPFILE) - mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename))) - mat_sub.add_subchunk(mat_sub_file) + if slot.image is not None: + add_texslot(slot) + has_entry = True return mat_sub if has_entry else None - def make_material_chunk(material, image): - """Make a material chunk out of a blender material.""" + """Make a material chunk out of a blender material. + Shading method is required for 3ds max, 0 for wireframe. + 0x1 for flat, 0x2 for gouraud, 0x3 for phong and 0x4 for metal.""" material_chunk = _3ds_chunk(MATERIAL) name = _3ds_chunk(MATNAME) + shading = _3ds_chunk(MATSHADING) name_str = material.name if material else "None" @@ -556,50 +646,109 @@ def make_material_chunk(material, image): material_chunk.add_subchunk(name) if not material: + shading.add_variable("shading", _3ds_ushort(1)) # Flat shading material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, (0.0, 0.0, 0.0))) material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, (0.8, 0.8, 0.8))) material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, (1.0, 1.0, 1.0))) - - else: - material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, (material.ambient * material.diffuse_color)[:])) - material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.diffuse_color[:])) + material_chunk.add_subchunk(make_percent_subchunk(MATSHINESS, .2)) + material_chunk.add_subchunk(make_percent_subchunk(MATSHIN2, 1)) + material_chunk.add_subchunk(shading) + + elif material and material.use_nodes: + wrap = node_shader_utils.PrincipledBSDFWrapper(material) + shading.add_variable("shading", _3ds_ushort(3)) # Phong shading + material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, wrap.emission_color[:3])) + material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, wrap.base_color[:3])) material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specular_color[:])) + material_chunk.add_subchunk(make_percent_subchunk(MATSHINESS, wrap.roughness)) + material_chunk.add_subchunk(make_percent_subchunk(MATSHIN2, wrap.specular)) + material_chunk.add_subchunk(make_percent_subchunk(MATSHIN3, wrap.metallic)) + material_chunk.add_subchunk(make_percent_subchunk(MATTRANS, 1-wrap.alpha)) + material_chunk.add_subchunk(shading) + + if wrap.base_color_texture: + d_pct = 0.7+sum(wrap.base_color[:])*.1 + color = [wrap.base_color_texture] + matmap = make_material_texture_chunk(MAT_DIFFUSEMAP, color, d_pct) + if matmap: + material_chunk.add_subchunk(matmap) + + if wrap.specular_texture: + spec = [wrap.specular_texture] + s_pct = material.specular_intensity + matmap = make_material_texture_chunk(MAT_SPECMAP, spec, s_pct) + if matmap: + material_chunk.add_subchunk(matmap) - slots = get_material_image_texslots(material) # can be None + if wrap.alpha_texture: + alpha = [wrap.alpha_texture] + a_pct = material.diffuse_color[3] + matmap = make_material_texture_chunk(MAT_OPACMAP, alpha, a_pct) + if matmap: + material_chunk.add_subchunk(matmap) + + if wrap.metallic_texture: + metallic = [wrap.metallic_texture] + m_pct = material.metallic + matmap = make_material_texture_chunk(MAT_REFLMAP, metallic, m_pct) + if matmap: + material_chunk.add_subchunk(matmap) - if slots: + if wrap.normalmap_texture: + normal = [wrap.normalmap_texture] + bump = wrap.normalmap_strength + b_pct = min(bump, 1) + bumpval = min(999, (bump * 100)) # 3ds max bump = 999 + strength = _3ds_chunk(MAT_BUMP_PERCENT) + strength.add_variable("bump_pct", _3ds_ushort(int(bumpval))) + matmap = make_material_texture_chunk(MAT_BUMPMAP, normal, b_pct) + if matmap: + material_chunk.add_subchunk(matmap) + material_chunk.add_subchunk(strength) - spec = [s for s in slots if s.use_map_specular or s.use_map_color_spec] - matmap = make_material_texture_chunk(MAT_SPECMAP, spec) + if wrap.roughness_texture: + roughness = [wrap.roughness_texture] + r_pct = material.roughness + matmap = make_material_texture_chunk(MAT_SHINMAP, roughness, r_pct) if matmap: material_chunk.add_subchunk(matmap) - alpha = [s for s in slots if s.use_map_alpha] - matmap = make_material_texture_chunk(MAT_OPACMAP, alpha) + if wrap.emission_color_texture: + e_pct = sum(wrap.emission_color[:])*.25 + emission = [wrap.emission_color_texture] + matmap = make_material_texture_chunk(MAT_SELFIMAP, emission, e_pct) if matmap: material_chunk.add_subchunk(matmap) - normal = [s for s in slots if s.use_map_normal] - matmap = make_material_texture_chunk(MAT_BUMPMAP, normal) + # make sure no textures are lost. Everything that doesn't fit + # into a channel is exported as secondary texture + diffuse = [] + + for link in wrap.material.node_tree.links: + if link.from_node.type == 'TEX_IMAGE' and link.to_node.type != 'BSDF_PRINCIPLED': + diffuse = [link.from_node.image] if not wrap.normalmap_texture else None + + if diffuse: + matmap = make_texture_chunk(MAT_TEX2MAP, diffuse) if matmap: material_chunk.add_subchunk(matmap) - # make sure no textures are lost. Everything that doesn't fit - # into a channel is exported as diffuse texture with a - # warning. - diffuse = [] - for s in slots: - if s.use_map_color_diffuse: - diffuse.append(s) - elif not (s in normal or s in alpha or s in spec): - print('\nwarning: failed to map texture to 3DS map channel, assuming diffuse') - diffuse.append(s) - - if diffuse: - matmap = make_material_texture_chunk(MAT_DIFFUSEMAP, diffuse, image) - if matmap: - material_chunk.add_subchunk(matmap) + else: + shading.add_variable("shading", _3ds_ushort(2)) # Gouraud shading + material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, material.line_color[:3])) + material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.diffuse_color[:3])) + material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specular_color[:])) + material_chunk.add_subchunk(make_percent_subchunk(MATSHINESS, material.roughness)) + material_chunk.add_subchunk(make_percent_subchunk(MATSHIN2, material.specular_intensity)) + material_chunk.add_subchunk(make_percent_subchunk(MATSHIN3, material.metallic)) + material_chunk.add_subchunk(make_percent_subchunk(MATTRANS, 1-material.diffuse_color[3])) + material_chunk.add_subchunk(shading) + + slots = [get_material_image(material)] # can be None + if image: + material_chunk.add_subchunk(make_texture_chunk(MAT_DIFFUSEMAP, slots)) + return material_chunk @@ -608,52 +757,47 @@ class tri_wrapper(object): Used when converting faces to triangles""" - __slots__ = "vertex_index", "mat", "image", "faceuvs", "offset" + __slots__ = "vertex_index", "ma", "image", "faceuvs", "offset", "group" - def __init__(self, vindex=(0, 0, 0), mat=None, image=None, faceuvs=None): + def __init__(self, vindex=(0, 0, 0), ma=None, image=None, faceuvs=None, group=0): self.vertex_index = vindex - self.mat = mat + self.ma = ma self.image = image self.faceuvs = faceuvs self.offset = [0, 0, 0] # offset indices + self.group = group def extract_triangles(mesh): - """Extract triangles from a mesh. - - If the mesh contains quads, they will be split into triangles.""" + """Extract triangles from a mesh.""" + + mesh.calc_loop_triangles() + (polygroup, count) = mesh.calc_smooth_groups(use_bitflags=True) + tri_list = [] - do_uv = bool(mesh.tessface_uv_textures) + do_uv = bool(mesh.uv_layers) img = None - for i, face in enumerate(mesh.tessfaces): + for i, face in enumerate(mesh.loop_triangles): f_v = face.vertices - uf = mesh.tessface_uv_textures.active.data[i] if do_uv else None + uf = mesh.uv_layers.active.data if do_uv else None if do_uv: - f_uv = uf.uv - img = uf.image if uf else None - if img is not None: - img = img.name + f_uv = [uf[lp].uv for lp in face.loops] + for ma in mesh.materials: + img = get_uv_image(ma) if uf else None + if img is not None: + img = img.name - # if f_v[3] == 0: - if len(f_v) == 3: - new_tri = tri_wrapper((f_v[0], f_v[1], f_v[2]), face.material_index, img) - if (do_uv): - new_tri.faceuvs = uv_key(f_uv[0]), uv_key(f_uv[1]), uv_key(f_uv[2]) - tri_list.append(new_tri) + smoothgroup = polygroup[face.polygon_index] - else: # it's a quad + if len(f_v) == 3: new_tri = tri_wrapper((f_v[0], f_v[1], f_v[2]), face.material_index, img) - new_tri_2 = tri_wrapper((f_v[0], f_v[2], f_v[3]), face.material_index, img) - if (do_uv): new_tri.faceuvs = uv_key(f_uv[0]), uv_key(f_uv[1]), uv_key(f_uv[2]) - new_tri_2.faceuvs = uv_key(f_uv[0]), uv_key(f_uv[2]), uv_key(f_uv[3]) - + new_tri.group = smoothgroup if face.use_smooth else 0 tri_list.append(new_tri) - tri_list.append(new_tri_2) return tri_list @@ -726,17 +870,20 @@ def remove_face_uv(verts, tri_list): def make_faces_chunk(tri_list, mesh, materialDict): """Make a chunk for the faces. - Also adds subchunks assigning materials to all faces.""" + do_smooth = False + use_smooth = [poly.use_smooth for poly in mesh.polygons] + if True in use_smooth: + do_smooth = True materials = mesh.materials if not materials: - mat = None + ma = None face_chunk = _3ds_chunk(OBJECT_FACES) face_list = _3ds_array() - if mesh.tessface_uv_textures: + if mesh.uv_layers: # Gather materials used in this mesh - mat/image pairs unique_mats = {} for i, tri in enumerate(tri_list): @@ -744,30 +891,30 @@ def make_faces_chunk(tri_list, mesh, materialDict): face_list.add(_3ds_face(tri.vertex_index)) if materials: - mat = materials[tri.mat] - if mat: - mat = mat.name + ma = materials[tri.ma] + if ma: + ma = ma.name img = tri.image try: - context_mat_face_array = unique_mats[mat, img][1] + context_face_array = unique_mats[ma, img][1] except: - name_str = mat if mat else "None" + name_str = ma if ma else "None" if img: name_str += img - context_mat_face_array = _3ds_array() - unique_mats[mat, img] = _3ds_string(sane_name(name_str)), context_mat_face_array + context_face_array = _3ds_array() + unique_mats[ma, img] = _3ds_string(sane_name(name_str)), context_face_array - context_mat_face_array.add(_3ds_ushort(i)) - # obj_material_faces[tri.mat].add(_3ds_ushort(i)) + context_face_array.add(_3ds_ushort(i)) + # obj_material_faces[tri.ma].add(_3ds_ushort(i)) face_chunk.add_variable("faces", face_list) - for mat_name, mat_faces in unique_mats.values(): + for ma_name, ma_faces in unique_mats.values(): obj_material_chunk = _3ds_chunk(OBJECT_MATERIAL) - obj_material_chunk.add_variable("name", mat_name) - obj_material_chunk.add_variable("face_list", mat_faces) + obj_material_chunk.add_variable("name", ma_name) + obj_material_chunk.add_variable("face_list", ma_faces) face_chunk.add_subchunk(obj_material_chunk) else: @@ -782,8 +929,8 @@ def make_faces_chunk(tri_list, mesh, materialDict): for i, tri in enumerate(tri_list): face_list.add(_3ds_face(tri.vertex_index)) - if (tri.mat < n_materials): - obj_material_faces[tri.mat].add(_3ds_ushort(i)) + if (tri.ma < n_materials): + obj_material_faces[tri.ma].add(_3ds_ushort(i)) face_chunk.add_variable("faces", face_list) for i in range(n_materials): @@ -792,6 +939,12 @@ def make_faces_chunk(tri_list, mesh, materialDict): obj_material_chunk.add_variable("face_list", obj_material_faces[i]) face_chunk.add_subchunk(obj_material_chunk) + if do_smooth: + obj_smooth_chunk = _3ds_chunk(OBJECT_SMOOTH) + for i, tri in enumerate(tri_list): + obj_smooth_chunk.add_variable("face_" + str(i),_3ds_uint(tri.group)) + face_chunk.add_subchunk(obj_smooth_chunk) + return face_chunk @@ -808,22 +961,22 @@ def make_uv_chunk(uv_array): uv_chunk.add_variable("uv coords", uv_array) return uv_chunk - +''' def make_matrix_4x3_chunk(matrix): matrix_chunk = _3ds_chunk(OBJECT_TRANS_MATRIX) for vec in matrix.col: for f in vec[:3]: matrix_chunk.add_variable("matrix_f", _3ds_float(f)) return matrix_chunk +''' - -def make_mesh_chunk(mesh, matrix, materialDict): +def make_mesh_chunk(ob, mesh, matrix, materialDict, translation): """Make a chunk out of a Blender mesh.""" # Extract the triangles from the mesh: tri_list = extract_triangles(mesh) - if mesh.tessface_uv_textures: + if mesh.uv_layers: # Remove the face UVs and convert it to vertex UV: vert_array, uv_array, tri_list = remove_face_uv(mesh.vertices, tri_list) else: @@ -839,15 +992,40 @@ def make_mesh_chunk(mesh, matrix, materialDict): # add vertex chunk: mesh_chunk.add_subchunk(make_vert_chunk(vert_array)) + # add faces chunk: - mesh_chunk.add_subchunk(make_faces_chunk(tri_list, mesh, materialDict)) # if available, add uv chunk: if uv_array: mesh_chunk.add_subchunk(make_uv_chunk(uv_array)) - mesh_chunk.add_subchunk(make_matrix_4x3_chunk(matrix)) + #mesh_chunk.add_subchunk(make_matrix_4x3_chunk(matrix)) + + # create transformation matrix chunk + matrix_chunk = _3ds_chunk(OBJECT_TRANS_MATRIX) + obj_matrix = matrix.transposed().to_3x3() + + if ob.parent is None: + obj_translate = translation[ob.name] + + else: # Calculate child matrix translation relative to parent + obj_translate = translation[ob.name].cross(-1*translation[ob.parent.name]) + + matrix_chunk.add_variable("xx", _3ds_float(obj_matrix[0].to_tuple(6)[0])) + matrix_chunk.add_variable("xy", _3ds_float(obj_matrix[0].to_tuple(6)[1])) + matrix_chunk.add_variable("xz", _3ds_float(obj_matrix[0].to_tuple(6)[2])) + matrix_chunk.add_variable("yx", _3ds_float(obj_matrix[1].to_tuple(6)[0])) + matrix_chunk.add_variable("yy", _3ds_float(obj_matrix[1].to_tuple(6)[1])) + matrix_chunk.add_variable("yz", _3ds_float(obj_matrix[1].to_tuple(6)[2])) + matrix_chunk.add_variable("zx", _3ds_float(obj_matrix[2].to_tuple(6)[0])) + matrix_chunk.add_variable("zy", _3ds_float(obj_matrix[2].to_tuple(6)[1])) + matrix_chunk.add_variable("zz", _3ds_float(obj_matrix[2].to_tuple(6)[2])) + matrix_chunk.add_variable("tx", _3ds_float(obj_translate.to_tuple(6)[0])) + matrix_chunk.add_variable("ty", _3ds_float(obj_translate.to_tuple(6)[1])) + matrix_chunk.add_variable("tz", _3ds_float(obj_translate.to_tuple(6)[2])) + + mesh_chunk.add_subchunk(matrix_chunk) return mesh_chunk @@ -982,16 +1160,13 @@ def save(operator, global_matrix=None, ): - import bpy - import mathutils - import time from bpy_extras.io_utils import create_derived_objects, free_derived_objects """Save the Blender scene to a 3ds file.""" # Time the export - time1 = time.clock() + duration = time.time() #Blender.Window.WaitCursor(1) if global_matrix is None: @@ -1000,6 +1175,10 @@ def save(operator, if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') + scene = context.scene + layer = context.view_layer + #depsgraph = context.evaluated_depsgraph_get() + # Initialize the main chunk (primary): primary = _3ds_chunk(PRIMARY) # Add version chunk: @@ -1007,8 +1186,24 @@ def save(operator, version_chunk.add_variable("version", _3ds_uint(3)) primary.add_subchunk(version_chunk) - # init main object info chunk: + # Init main object info chunk: object_info = _3ds_chunk(OBJECTINFO) + mesh_version = _3ds_chunk(MESHVERSION) + mesh_version.add_variable("mesh", _3ds_uint(3)) + object_info.add_subchunk(mesh_version) + + # Add MASTERSCALE element + mscale = _3ds_chunk(MASTERSCALE) + mscale.add_variable("scale", _3ds_float(1)) + object_info.add_subchunk(mscale) + + # Add AMBIENT color + if scene.world is not None: + ambient_chunk = _3ds_chunk(AMBIENTLIGHT) + ambient_light = _3ds_chunk(RGB) + ambient_light.add_variable("ambient", _3ds_float_color(scene.world.color)) + ambient_chunk.add_subchunk(ambient_light) + object_info.add_subchunk(ambient_chunk) ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX # init main key frame data chunk: @@ -1020,13 +1215,13 @@ def save(operator, materialDict = {} mesh_objects = [] - scene = context.scene - depsgraph = context.evaluated_depsgraph_get() - if use_selection: - objects = (ob for ob in scene.objects if ob.is_visible(scene) and ob.select) + objects = [ob for ob in scene.objects if not ob.hide_viewport and ob.select_get(view_layer=layer)] else: - objects = (ob for ob in scene.objects if ob.is_visible(scene)) + objects = [ob for ob in scene.objects if not ob.hide_viewport] + + light_objects = [ob for ob in objects if ob.type == 'LIGHT'] + camera_objects = [ob for ob in objects if ob.type == 'CAMERA'] for ob in objects: # get derived objects @@ -1035,73 +1230,75 @@ def save(operator, if derived is None: continue - for ob_derived, mat in derived: + for ob_derived, mtx in derived: if ob.type not in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META'}: continue - ob_derived_eval = ob_derived.evaluated_get(depsgraph) + #ob_derived_eval = ob_derived.evaluated_get(depsgraph) try: - data = ob_derived_eval.to_mesh() + data = ob_derived.to_mesh() except: data = None if data: - matrix = global_matrix * mat + matrix = global_matrix @ mtx data.transform(matrix) mesh_objects.append((ob_derived, data, matrix)) - mat_ls = data.materials - mat_ls_len = len(mat_ls) + ma_ls = data.materials + ma_ls_len = len(ma_ls) # get material/image tuples. - if data.tessface_uv_textures: - if not mat_ls: - mat = mat_name = None - - for f, uf in zip(data.tessfaces, data.tessface_uv_textures.active.data): - if mat_ls: - mat_index = f.material_index - if mat_index >= mat_ls_len: - mat_index = f.mat = 0 - mat = mat_ls[mat_index] - mat_name = None if mat is None else mat.name + if data.uv_layers: + if not ma_ls: + ma = ma_name = None + + for f, uf in zip(data.polygons, data.uv_layers.active.data): + if ma_ls: + ma_index = f.material_index + if ma_index >= ma_ls_len: + ma_index = f.material_index = 0 + ma = ma_ls[ma_index] + ma_name = None if ma is None else ma.name # else there already set to none - img = uf.image + img = get_uv_image(ma) img_name = None if img is None else img.name - materialDict.setdefault((mat_name, img_name), (mat, img)) + materialDict.setdefault((ma_name, img_name), (ma, img)) else: - for mat in mat_ls: - if mat: # material may be None so check its not. - materialDict.setdefault((mat.name, None), (mat, None)) + for ma in ma_ls: + if ma: # material may be None so check its not. + materialDict.setdefault((ma.name, None), (ma, None)) # Why 0 Why! - for f in data.tessfaces: - if f.material_index >= mat_ls_len: + for f in data.polygons: + if f.material_index >= ma_ls_len: f.material_index = 0 - ob_derived_eval.to_mesh_clear() + #ob_derived_eval.to_mesh_clear() if free: free_derived_objects(ob) # Make material chunks for all materials used in the meshes: - for mat_and_image in materialDict.values(): - object_info.add_subchunk(make_material_chunk(mat_and_image[0], mat_and_image[1])) + for ma_image in materialDict.values(): + object_info.add_subchunk(make_material_chunk(ma_image[0], ma_image[1])) # Give all objects a unique ID and build a dictionary from object name to object id: + translation = {} # collect translation for transformation matrix + #name_to_id = {} + for ob, data, matrix in mesh_objects: + translation[ob.name] = ob.location + #name_to_id[ob.name]= len(name_to_id) """ - name_to_id = {} - for ob, data in mesh_objects: - name_to_id[ob.name]= len(name_to_id) #for ob in empty_objects: # name_to_id[ob.name]= len(name_to_id) """ # Create object chunks for all meshes: i = 0 - for ob, blender_mesh, matrix in mesh_objects: + for ob, mesh, matrix in mesh_objects: # create a new object chunk object_chunk = _3ds_chunk(OBJECT) @@ -1109,7 +1306,7 @@ def save(operator, object_chunk.add_variable("name", _3ds_string(sane_name(ob.name))) # make a mesh chunk out of the mesh: - object_chunk.add_subchunk(make_mesh_chunk(blender_mesh, matrix, materialDict)) + object_chunk.add_subchunk(make_mesh_chunk(ob, mesh, matrix, materialDict, translation)) # ensure the mesh has no over sized arrays # skip ones that do!, otherwise we cant write since the array size wont @@ -1124,8 +1321,8 @@ def save(operator, kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id)) ''' - if not blender_mesh.users: - bpy.data.meshes.remove(blender_mesh) + #if not blender_mesh.users: + #bpy.data.meshes.remove(blender_mesh) #blender_mesh.vertices = None i += i @@ -1138,6 +1335,55 @@ def save(operator, pass ''' + # Create light object chunks + for ob in light_objects: + object_chunk = _3ds_chunk(OBJECT) + light_chunk = _3ds_chunk(OBJECT_LIGHT) + color_float_chunk = _3ds_chunk(RGB) + energy_factor = _3ds_chunk(LIGHT_MULTIPLIER) + object_chunk.add_variable("light", _3ds_string(sane_name(ob.name))) + light_chunk.add_variable("location", _3ds_point_3d(ob.location)) + color_float_chunk.add_variable("color", _3ds_float_color(ob.data.color)) + energy_factor.add_variable("energy", _3ds_float(ob.data.energy*.001)) + light_chunk.add_subchunk(color_float_chunk) + light_chunk.add_subchunk(energy_factor) + + if ob.data.type == 'SPOT': + cone_angle = math.degrees(ob.data.spot_size) + hotspot = cone_angle-(ob.data.spot_blend*math.floor(cone_angle)) + hypo = math.copysign(math.sqrt(pow(ob.location[0],2)+pow(ob.location[1],2)), ob.location[1]) + pos_x = ob.location[0]+(ob.location[1]*math.tan(ob.rotation_euler[2])) + pos_y = ob.location[1]+(ob.location[0]*math.tan(math.radians(90)-ob.rotation_euler[2])) + pos_z = hypo*math.tan(math.radians(90)-ob.rotation_euler[0]) + spotlight_chunk = _3ds_chunk(LIGHT_SPOTLIGHT) + spot_roll_chunk = _3ds_chunk(LIGHT_SPOTROLL) + spotlight_chunk.add_variable("target", _3ds_point_3d((pos_x, pos_y, pos_z))) + spotlight_chunk.add_variable("hotspot", _3ds_float(round(hotspot,4))) + spotlight_chunk.add_variable("angle", _3ds_float(round(cone_angle,4))) + spot_roll_chunk.add_variable("roll", _3ds_float(round(ob.rotation_euler[1],6))) + spotlight_chunk.add_subchunk(spot_roll_chunk) + light_chunk.add_subchunk(spotlight_chunk) + + # Add light to object info + object_chunk.add_subchunk(light_chunk) + object_info.add_subchunk(object_chunk) + + # Create camera object chunks + for ob in camera_objects: + object_chunk = _3ds_chunk(OBJECT) + camera_chunk = _3ds_chunk(OBJECT_CAMERA) + diagonal = math.copysign(math.sqrt(pow(ob.location[0],2)+pow(ob.location[1],2)), ob.location[1]) + focus_x = ob.location[0]+(ob.location[1]*math.tan(ob.rotation_euler[2])) + focus_y = ob.location[1]+(ob.location[0]*math.tan(math.radians(90)-ob.rotation_euler[2])) + focus_z = diagonal*math.tan(math.radians(90)-ob.rotation_euler[0]) + object_chunk.add_variable("camera", _3ds_string(sane_name(ob.name))) + camera_chunk.add_variable("location", _3ds_point_3d(ob.location)) + camera_chunk.add_variable("target", _3ds_point_3d((focus_x, focus_y, focus_z))) + camera_chunk.add_variable("roll", _3ds_float(round(ob.rotation_euler[1],6))) + camera_chunk.add_variable("lens", _3ds_float(ob.data.lens)) + object_chunk.add_subchunk(camera_chunk) + object_info.add_subchunk(object_chunk) + # Add main object info chunk to primary chunk: primary.add_subchunk(object_info) @@ -1165,7 +1411,7 @@ def save(operator, # Debugging only: report the exporting time: #Blender.Window.WaitCursor(0) - print("3ds export time: %.2f" % (time.clock() - time1)) + print("3ds export time: %.2f" % (time.time() - duration)) # Debugging only: dump the chunk hierarchy: #primary.dump() diff --git a/io_scene_3ds/import_3ds.py b/io_scene_3ds/import_3ds.py index e0e100e3..636fb105 100644 --- a/io_scene_3ds/import_3ds.py +++ b/io_scene_3ds/import_3ds.py @@ -19,16 +19,15 @@ # <pep8 compliant> # Script copyright (C) Bob Holcomb -# Contributors: Bob Holcomb, Richard L?rk?ng, Damien McGinnes, +# Contributors: Bob Holcomb, Richard L?rk?ng, Damien McGinnes, Sebastian Sille # Campbell Barton, Mario Lapin, Dominique Lorre, Andreas Atteneder import os import time import struct - import bpy +import math import mathutils - from bpy_extras.node_shader_utils import PrincipledBSDFWrapper BOUNDS_3DS = [] @@ -60,9 +59,11 @@ OBJECT = 0x4000 # This stores the faces, vertices, etc... MAT_NAME = 0xA000 # This holds the material name MAT_AMBIENT = 0xA010 # Ambient color of the object/material MAT_DIFFUSE = 0xA020 # This holds the color of the object/material -MAT_SPECULAR = 0xA030 # SPecular color of the object/material -MAT_SHINESS = 0xA040 # ?? -MAT_TRANSPARENCY = 0xA050 # Transparency value of material +MAT_SPECULAR = 0xA030 # Specular color of the object/material +MAT_SHINESS = 0xA040 # Roughness of the object/material (percent) +MAT_SHIN2 = 0xA041 # Shininess of the object/material (percent) +MAT_SHIN3 = 0xA042 # Reflection of the object/material (percent) +MAT_TRANSPARENCY = 0xA050 # Transparency value of material (percent) MAT_SELF_ILLUM = 0xA080 # Self Illumination value of material MAT_WIRE = 0xA085 # Only render's wireframe @@ -71,6 +72,10 @@ MAT_SPECULAR_MAP = 0xA204 # This is a header for a new specular map MAT_OPACITY_MAP = 0xA210 # This is a header for a new opacity map MAT_REFLECTION_MAP = 0xA220 # This is a header for a new reflection map MAT_BUMP_MAP = 0xA230 # This is a header for a new bump map +MAT_BUMP_PERCENT = 0xA252 # Normalmap strength (percent) +MAT_TEX2_MAP = 0xA33A # This is a header for a secondary texture +MAT_SHIN_MAP = 0xA33C # This is a header for a new roughness map +MAT_SELFI_MAP = 0xA33D # This is a header for a new emission map MAT_MAP_FILEPATH = 0xA300 # This holds the file name of the texture MAT_MAP_TILING = 0xa351 # 2nd bit (from LSB) is mirror UV flag @@ -79,7 +84,11 @@ MAT_MAP_VSCALE = 0xA356 # V axis scaling MAT_MAP_UOFFSET = 0xA358 # U axis offset MAT_MAP_VOFFSET = 0xA35A # V axis offset MAT_MAP_ANG = 0xA35C # UV rotation around the z-axis in rad - +MAT_MAP_COL1 = 0xA360 # Map Color1 +MAT_MAP_COL2 = 0xA362 # Map Color2 +MAT_MAP_RCOL = 0xA364 # Red mapping +MAT_MAP_GCOL = 0xA366 # Green mapping +MAT_MAP_BCOL = 0xA368 # Blue mapping MAT_FLOAT_COLOR = 0x0010 # color defined as 3 floats MAT_24BIT_COLOR = 0x0011 # color defined as 3 bytes @@ -117,37 +126,38 @@ OBJECT_VERTICES = 0x4110 # The objects vertices OBJECT_FACES = 0x4120 # The objects faces OBJECT_MATERIAL = 0x4130 # This is found if the object has a material, either texture map or color OBJECT_UV = 0x4140 # The UV texture coordinates +OBJECT_SMOOTH = 0x4150 # The Object smooth groups OBJECT_TRANS_MATRIX = 0x4160 # The Object Matrix #>------ sub defines of EDITKEYFRAME -ED_KEY_AMBIENT_NODE = 0xB001 -ED_KEY_OBJECT_NODE = 0xB002 -ED_KEY_CAMERA_NODE = 0xB003 -ED_KEY_TARGET_NODE = 0xB004 -ED_KEY_LIGHT_NODE = 0xB005 -ED_KEY_L_TARGET_NODE = 0xB006 -ED_KEY_SPOTLIGHT_NODE = 0xB007 -#>------ sub defines of ED_KEY_OBJECT_NODE -# EK_OB_KEYFRAME_SEG = 0xB008 -# EK_OB_KEYFRAME_CURTIME = 0xB009 -# EK_OB_KEYFRAME_HEADER = 0xB00A -EK_OB_NODE_HEADER = 0xB010 -EK_OB_INSTANCE_NAME = 0xB011 -# EK_OB_PRESCALE = 0xB012 -EK_OB_PIVOT = 0xB013 -# EK_OB_BOUNDBOX = 0xB014 -# EK_OB_MORPH_SMOOTH = 0xB015 -EK_OB_POSITION_TRACK = 0xB020 -EK_OB_ROTATION_TRACK = 0xB021 -EK_OB_SCALE_TRACK = 0xB022 -# EK_OB_CAMERA_FOV_TRACK = 0xB023 -# EK_OB_CAMERA_ROLL_TRACK = 0xB024 -# EK_OB_COLOR_TRACK = 0xB025 -# EK_OB_MORPH_TRACK = 0xB026 -# EK_OB_HOTSPOT_TRACK = 0xB027 -# EK_OB_FALLOF_TRACK = 0xB028 -# EK_OB_HIDE_TRACK = 0xB029 -# EK_OB_NODE_ID = 0xB030 +KFDATA_AMBIENT = 0xB001 +KFDATA_OBJECT = 0xB002 +KFDATA_CAMERA = 0xB003 +KFDATA_TARGET = 0xB004 +KFDATA_LIGHT = 0xB005 +KFDATA_L_TARGET = 0xB006 +KFDATA_SPOTLIGHT = 0xB007 +KFDATA_KFSEG = 0xB008 +# KFDATA_CURTIME = 0xB009 +# KFDATA_KFHDR = 0xB00A +#>------ sub defines of KEYFRAME_NODE +OBJECT_NODE_HDR = 0xB010 +OBJECT_INSTANCE_NAME = 0xB011 +# OBJECT_PRESCALE = 0xB012 +OBJECT_PIVOT = 0xB013 +# OBJECT_BOUNDBOX = 0xB014 +# MORPH_SMOOTH = 0xB015 +POS_TRACK_TAG = 0xB020 +ROT_TRACK_TAG = 0xB021 +SCL_TRACK_TAG = 0xB022 +FOV_TRACK_TAG = 0xB023 +ROLL_TRACK_TAG = 0xB024 +COL_TRACK_TAG = 0xB025 +# MORPH_TRACK_TAG = 0xB026 +# HOTSPOT_TRACK_TAG = 0xB027 +# FALLOFF_TRACK_TAG = 0xB028 +# HIDE_TRACK_TAG = 0xB029 +# OBJECT_NODE_ID = 0xB030 ROOT_OBJECT = 0xFFFF @@ -225,78 +235,111 @@ def skip_to_end(file, skip_chunk): skip_chunk.bytes_read += buffer_size -def add_texture_to_material(image, scale, offset, extension, contextMaterialWrapper, mapto): - #print('assigning %s to %s' % (texture, material)) - - if mapto not in {'COLOR', 'SPECULARITY', 'ALPHA', 'NORMAL'}: - print( - "\tError: Cannot map to %r\n\tassuming diffuse color. modify material %r later." % - (mapto, contextMaterialWrapper.material.name) - ) - mapto = "COLOR" +def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, offset, angle, tintcolor, mapto): + shader = contextWrapper.node_principled_bsdf + nodetree = contextWrapper.material.node_tree + shader.location = (-300,0) + nodes = nodetree.nodes + links = nodetree.links if mapto == 'COLOR': - img_wrap = contextMaterialWrapper.base_color_texture + mixer = nodes.new(type='ShaderNodeMixRGB') + mixer.label = "Mixer" + mixer.inputs[0].default_value = pct/100 + mixer.inputs[1].default_value = tintcolor[:3]+[1] if tintcolor else (0,0,0,0) + contextWrapper._grid_to_location(1,2, dst_node=mixer, ref_node=shader) + img_wrap = contextWrapper.base_color_texture + links.new(img_wrap.node_image.outputs['Color'], mixer.inputs[2]) + links.new(mixer.outputs['Color'], shader.inputs['Base Color']) elif mapto == 'SPECULARITY': - # TODO: Not sure if this is correct usage? - img_wrap = contextMaterialWrapper.specular_texture + img_wrap = contextWrapper.specular_texture elif mapto == 'ALPHA': - img_wrap = contextMaterialWrapper.alpha_texture + shader.location = (0,-300) + img_wrap = contextWrapper.alpha_texture + elif mapto == 'METALLIC': + shader.location = (300,300) + img_wrap = contextWrapper.metallic_texture + elif mapto == 'ROUGHNESS': + shader.location = (300,0) + img_wrap = contextWrapper.roughness_texture + elif mapto == 'EMISSION': + shader.location = (-300,-600) + img_wrap = contextWrapper.emission_color_texture elif mapto == 'NORMAL': - img_wrap = contextMaterialWrapper.normalmap_texture + shader.location = (300,300) + img_wrap = contextWrapper.normalmap_texture + elif mapto == 'TEXTURE': + img_wrap = nodes.new(type='ShaderNodeTexImage') + img_wrap.label = image.name + contextWrapper._grid_to_location(0,2, dst_node=img_wrap, ref_node=shader) + for node in nodes: + if node.label == 'Mixer': + spare = node.inputs[1] if node.inputs[1].is_linked is False else node.inputs[2] + socket = spare if spare.is_linked is False else node.inputs[0] + links.new(img_wrap.outputs['Color'], socket) + if node.type == 'TEX_COORD': + links.new(node.outputs['UV'], img_wrap.inputs['Vector']) + if shader.inputs['Base Color'].is_linked is False: + links.new(img_wrap.outputs['Color'], shader.inputs['Base Color']) img_wrap.image = image - - img_wrap.scale = scale - img_wrap.translation = offset - img_wrap.extension = 'REPEAT' - if extension == 'mirror': + + if mapto != 'TEXTURE': + img_wrap.scale = scale + img_wrap.translation = offset + img_wrap.rotation[2] = angle + + if extend == 'mirror': # 3DS mirror flag can be emulated by these settings (at least so it seems) # TODO: bring back mirror pass # texture.repeat_x = texture.repeat_y = 2 # texture.use_mirror_x = texture.use_mirror_y = True - elif extension == 'decal': - # 3DS' decal mode maps best to Blenders CLIP + elif extend == 'decal': + # 3DS' decal mode maps best to Blenders EXTEND + img_wrap.extension = 'EXTEND' + elif extend == 'noWrap': img_wrap.extension = 'CLIP' + if alpha == 'alpha': + links.new(img_wrap.node_image.outputs['Alpha'], img_wrap.socket_dst) + shader.location = (300,300) + contextWrapper._grid_to_location(1,0, dst_node=contextWrapper.node_out, ref_node=shader) -def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEARCH): + +def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEARCH, KEYFRAME): from bpy_extras.image_utils import load_image - #print previous_chunk.bytes_read, 'BYTES READ' contextObName = None - contextLamp = [None, None] # object, Data + contextLamp = None + contextCamera = None contextMaterial = None - contextMaterialWrapper = None - contextMatrix_rot = None # Blender.mathutils.Matrix(); contextMatrix.identity() - #contextMatrix_tx = None # Blender.mathutils.Matrix(); contextMatrix.identity() - contextMesh_vertls = None # flat array: (verts * 3) + contextWrapper = None + contextMatrix = None + contextMesh_vertls = None contextMesh_facels = None - contextMeshMaterials = [] # (matname, [face_idxs]) - contextMeshUV = None # flat array (verts * 2) + contextMeshMaterials = [] + contextMesh_smooth = None + contextMeshUV = None TEXTURE_DICT = {} MATDICT = {} -# TEXMODE = Mesh.FaceModes['TEX'] # Localspace variable names, faster. - STRUCT_SIZE_FLOAT = struct.calcsize('f') - STRUCT_SIZE_2FLOAT = struct.calcsize('2f') - STRUCT_SIZE_3FLOAT = struct.calcsize('3f') - STRUCT_SIZE_4FLOAT = struct.calcsize('4f') - STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize('H') - STRUCT_SIZE_4UNSIGNED_SHORT = struct.calcsize('4H') - STRUCT_SIZE_4x3MAT = struct.calcsize('ffffffffffff') - # STRUCT_SIZE_4x3MAT = calcsize('ffffffffffff') - # print STRUCT_SIZE_4x3MAT, ' STRUCT_SIZE_4x3MAT' - # only init once + SZ_FLOAT = struct.calcsize('f') + SZ_2FLOAT = struct.calcsize('2f') + SZ_3FLOAT = struct.calcsize('3f') + SZ_4FLOAT = struct.calcsize('4f') + SZ_U_SHORT = struct.calcsize('H') + SZ_4U_SHORT = struct.calcsize('4H') + SZ_4x3MAT = struct.calcsize('ffffffffffff') + object_list = [] # for hierarchy object_parent = [] # index of parent in hierarchy, 0xFFFF = no parent pivot_list = [] # pivots with hierarchy handling - def putContextMesh(context, myContextMesh_vertls, myContextMesh_facels, myContextMeshMaterials): + def putContextMesh(context, myContextMesh_vertls, myContextMesh_facels, myContextMeshMaterials, myContextMeshSmooth): bmesh = bpy.data.meshes.new(contextObName) if myContextMesh_facels is None: @@ -370,95 +413,123 @@ def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEA context.view_layer.active_layer_collection.collection.objects.link(ob) importedObjects.append(ob) - if contextMatrix_rot: - ob.matrix_local = contextMatrix_rot - object_matrix[ob] = contextMatrix_rot.copy() + if myContextMesh_smooth: + for f, pl in enumerate(bmesh.polygons): + smoothface = myContextMesh_smooth[f] + if smoothface > 0: + bmesh.polygons[f].use_smooth = True + + if contextMatrix: + ob.matrix_local = contextMatrix + object_matrix[ob] = contextMatrix.copy() #a spare chunk new_chunk = Chunk() temp_chunk = Chunk() CreateBlenderObject = False + CreateLightObject = False + CreateCameraObject = False def read_float_color(temp_chunk): - temp_data = file.read(STRUCT_SIZE_3FLOAT) - temp_chunk.bytes_read += STRUCT_SIZE_3FLOAT + temp_data = file.read(SZ_3FLOAT) + temp_chunk.bytes_read += SZ_3FLOAT return [float(col) for col in struct.unpack('<3f', temp_data)] def read_float(temp_chunk): - temp_data = file.read(STRUCT_SIZE_FLOAT) - temp_chunk.bytes_read += STRUCT_SIZE_FLOAT + temp_data = file.read(SZ_FLOAT) + temp_chunk.bytes_read += SZ_FLOAT return struct.unpack('<f', temp_data)[0] def read_short(temp_chunk): - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) - temp_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT + temp_data = file.read(SZ_U_SHORT) + temp_chunk.bytes_read += SZ_U_SHORT return struct.unpack('<H', temp_data)[0] def read_byte_color(temp_chunk): temp_data = file.read(struct.calcsize('3B')) temp_chunk.bytes_read += 3 - return [float(col) / 255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb + return [float(col) / 255 for col in struct.unpack('<3B', temp_data)] def read_texture(new_chunk, temp_chunk, name, mapto): -# new_texture = bpy.data.textures.new(name, type='IMAGE') - - u_scale, v_scale, u_offset, v_offset = 1.0, 1.0, 0.0, 0.0 - mirror = False - extension = 'wrap' + uscale, vscale, uoffset, voffset, angle = 1.0, 1.0, 0.0, 0.0, 0.0 + contextWrapper.use_nodes = True + tintcolor = None + extend = 'wrap' + alpha = False + pct = 50 + + contextWrapper.emission_color = contextMaterial.line_color[:3] + contextWrapper.base_color = contextMaterial.diffuse_color[:3] + contextWrapper.specular = contextMaterial.specular_intensity + contextWrapper.roughness = contextMaterial.roughness + contextWrapper.metallic = contextMaterial.metallic + contextWrapper.alpha = contextMaterial.diffuse_color[3] + while (new_chunk.bytes_read < new_chunk.length): - #print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length read_chunk(file, temp_chunk) + if temp_chunk.ID == PERCENTAGE_SHORT: + pct = read_short(temp_chunk) - if temp_chunk.ID == MAT_MAP_FILEPATH: + elif temp_chunk.ID == MAT_MAP_FILEPATH: texture_name, read_str_len = read_string(file) - img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname, recursive=IMAGE_SEARCH) temp_chunk.bytes_read += read_str_len # plus one for the null character that gets removed elif temp_chunk.ID == MAT_MAP_USCALE: - u_scale = read_float(temp_chunk) + uscale = read_float(temp_chunk) elif temp_chunk.ID == MAT_MAP_VSCALE: - v_scale = read_float(temp_chunk) - + vscale = read_float(temp_chunk) elif temp_chunk.ID == MAT_MAP_UOFFSET: - u_offset = read_float(temp_chunk) + uoffset = read_float(temp_chunk) elif temp_chunk.ID == MAT_MAP_VOFFSET: - v_offset = read_float(temp_chunk) + voffset = read_float(temp_chunk) elif temp_chunk.ID == MAT_MAP_TILING: tiling = read_short(temp_chunk) - if tiling & 0x2: - extension = 'mirror' + if tiling & 0x1: + extend = 'decal' + elif tiling & 0x2: + extend = 'mirror' + elif tiling & 0x8: + extend = 'invert' elif tiling & 0x10: - extension = 'decal' + extend = 'noWrap' + elif tiling & 0x20: + alpha = 'sat' + elif tiling & 0x40: + alpha = 'alpha' + elif tiling & 0x80: + tint = 'tint' + elif tiling & 0x100: + tint = 'noAlpha' + elif tiling & 0x200: + tint = 'RGBtint' elif temp_chunk.ID == MAT_MAP_ANG: - print("\nwarning: ignoring UV rotation") + angle = read_float(temp_chunk) + print("\nwarning: UV angle mapped to z-rotation") + + elif temp_chunk.ID == MAT_MAP_COL1: + tintcolor = read_byte_color(temp_chunk) skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read # add the map to the material in the right channel if img: - add_texture_to_material(img, (u_scale, v_scale, 1), - (u_offset, v_offset, 0), extension, contextMaterialWrapper, mapto) + add_texture_to_material(img, contextWrapper, pct, extend, alpha, (uscale, vscale, 1), + (uoffset, voffset, 0), angle, tintcolor, mapto) dirname = os.path.dirname(file.name) #loop through all the data for this chunk (previous chunk) and see what it is while (previous_chunk.bytes_read < previous_chunk.length): - #print '\t', previous_chunk.bytes_read, 'keep going' - #read the next chunk - #print 'reading a chunk' read_chunk(file, new_chunk) #is it a Version chunk? if new_chunk.ID == VERSION: - #print 'if new_chunk.ID == VERSION:' - #print 'found a VERSION chunk' #read in the version of the file - #it's an unsigned short (H) temp_data = file.read(struct.calcsize('I')) version = struct.unpack('<I', temp_data)[0] new_chunk.bytes_read += 4 # read the 4 bytes for the version number @@ -468,9 +539,7 @@ def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEA #is it an object info chunk? elif new_chunk.ID == OBJECTINFO: - #print 'elif new_chunk.ID == OBJECTINFO:' - # print 'found an OBJECTINFO chunk' - process_next_chunk(context, file, new_chunk, importedObjects, IMAGE_SEARCH) + process_next_chunk(context, file, new_chunk, importedObjects, IMAGE_SEARCH, KEYFRAME) #keep track of how much we read in the main chunk new_chunk.bytes_read += temp_chunk.bytes_read @@ -479,16 +548,14 @@ def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEA elif new_chunk.ID == OBJECT: if CreateBlenderObject: - putContextMesh(context, contextMesh_vertls, contextMesh_facels, contextMeshMaterials) + putContextMesh(context, contextMesh_vertls, contextMesh_facels, contextMeshMaterials, contextMesh_smooth) contextMesh_vertls = [] contextMesh_facels = [] - - ## preparando para receber o proximo objeto - contextMeshMaterials = [] # matname:[face_idxs] + contextMeshMaterials = [] + contextMesh_smooth = None contextMeshUV = None # Reset matrix - contextMatrix_rot = None - #contextMatrix_tx = None + contextMatrix = None CreateBlenderObject = True contextObName, read_str_len = read_string(file) @@ -496,79 +563,97 @@ def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEA #is it a material chunk? elif new_chunk.ID == MATERIAL: - -# print("read material") - - #print 'elif new_chunk.ID == MATERIAL:' contextMaterial = bpy.data.materials.new('Material') - contextMaterialWrapper = PrincipledBSDFWrapper(contextMaterial, is_readonly=False, use_nodes=True) + contextWrapper = PrincipledBSDFWrapper(contextMaterial, is_readonly=False, use_nodes=False) elif new_chunk.ID == MAT_NAME: - #print 'elif new_chunk.ID == MAT_NAME:' material_name, read_str_len = read_string(file) -# print("material name", material_name) - #plus one for the null character that ended the string new_chunk.bytes_read += read_str_len - contextMaterial.name = material_name.rstrip() # remove trailing whitespace MATDICT[material_name] = contextMaterial elif new_chunk.ID == MAT_AMBIENT: - #print 'elif new_chunk.ID == MAT_AMBIENT:' read_chunk(file, temp_chunk) - # TODO: consider ambient term somehow. maybe add to color -# if temp_chunk.ID == MAT_FLOAT_COLOR: -# contextMaterial.mirror_color = read_float_color(temp_chunk) -# temp_data = file.read(struct.calcsize('3f')) -# temp_chunk.bytes_read += 12 -# contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)] -# elif temp_chunk.ID == MAT_24BIT_COLOR: -# contextMaterial.mirror_color = read_byte_color(temp_chunk) -# temp_data = file.read(struct.calcsize('3B')) -# temp_chunk.bytes_read += 3 -# contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb -# else: - skip_to_end(file, temp_chunk) + # only available color is emission color + if temp_chunk.ID == MAT_FLOAT_COLOR: + contextMaterial.line_color[:3] = read_float_color(temp_chunk) + elif temp_chunk.ID == MAT_24BIT_COLOR: + contextMaterial.line_color[:3] = read_byte_color(temp_chunk) + else: + skip_to_end(file, temp_chunk) new_chunk.bytes_read += temp_chunk.bytes_read elif new_chunk.ID == MAT_DIFFUSE: - #print 'elif new_chunk.ID == MAT_DIFFUSE:' read_chunk(file, temp_chunk) if temp_chunk.ID == MAT_FLOAT_COLOR: - contextMaterialWrapper.base_color = read_float_color(temp_chunk) -# temp_data = file.read(struct.calcsize('3f')) -# temp_chunk.bytes_read += 12 -# contextMaterial.rgbCol = [float(col) for col in struct.unpack('<3f', temp_data)] + contextMaterial.diffuse_color[:3] = read_float_color(temp_chunk) elif temp_chunk.ID == MAT_24BIT_COLOR: - contextMaterialWrapper.base_color = read_byte_color(temp_chunk) -# temp_data = file.read(struct.calcsize('3B')) -# temp_chunk.bytes_read += 3 -# contextMaterial.rgbCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb + contextMaterial.diffuse_color[:3] = read_byte_color(temp_chunk) else: skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read -# print("read material diffuse color", contextMaterial.diffuse_color) + elif new_chunk.ID == MAT_SPECULAR: + read_chunk(file, temp_chunk) + # Specular color is available + if temp_chunk.ID == MAT_FLOAT_COLOR: + contextMaterial.specular_color = read_float_color(temp_chunk) + elif temp_chunk.ID == MAT_24BIT_COLOR: + contextMaterial.specular_color = read_byte_color(temp_chunk) + else: + skip_to_end(file, temp_chunk) + new_chunk.bytes_read += temp_chunk.bytes_read + elif new_chunk.ID == MAT_SHINESS: + read_chunk(file, temp_chunk) + if temp_chunk.ID == PERCENTAGE_SHORT: + temp_data = file.read(SZ_U_SHORT) + temp_chunk.bytes_read += SZ_U_SHORT + contextMaterial.roughness = 1 - (float(struct.unpack('<H', temp_data)[0]) / 100) + elif temp_chunk.ID == PERCENTAGE_FLOAT: + temp_data = file.read(SZ_FLOAT) + temp_chunk.bytes_read += SZ_FLOAT + contextMaterial.roughness = 1 - float(struct.unpack('f', temp_data)[0]) new_chunk.bytes_read += temp_chunk.bytes_read - elif new_chunk.ID == MAT_SPECULAR: - #print 'elif new_chunk.ID == MAT_SPECULAR:' + elif new_chunk.ID == MAT_SHIN2: read_chunk(file, temp_chunk) - # TODO: consider using specular term somehow -# if temp_chunk.ID == MAT_FLOAT_COLOR: -# contextMaterial.specular_color = read_float_color(temp_chunk) -# temp_data = file.read(struct.calcsize('3f')) -# temp_chunk.bytes_read += 12 -# contextMaterial.mirCol = [float(col) for col in struct.unpack('<3f', temp_data)] -# elif temp_chunk.ID == MAT_24BIT_COLOR: -# contextMaterial.specular_color = read_byte_color(temp_chunk) -# temp_data = file.read(struct.calcsize('3B')) -# temp_chunk.bytes_read += 3 -# contextMaterial.mirCol = [float(col)/255 for col in struct.unpack('<3B', temp_data)] # data [0,1,2] == rgb -# else: - skip_to_end(file, temp_chunk) + if temp_chunk.ID == PERCENTAGE_SHORT: + temp_data = file.read(SZ_U_SHORT) + temp_chunk.bytes_read += SZ_U_SHORT + contextMaterial.specular_intensity = (float(struct.unpack('<H', temp_data)[0]) / 100) + elif temp_chunk.ID == PERCENTAGE_FLOAT: + temp_data = file.read(SZ_FLOAT) + temp_chunk.bytes_read += SZ_FLOAT + contextMaterial.specular_intensity = float(struct.unpack('f', temp_data)[0]) + new_chunk.bytes_read += temp_chunk.bytes_read + + elif new_chunk.ID == MAT_SHIN3: + read_chunk(file, temp_chunk) + if temp_chunk.ID == PERCENTAGE_SHORT: + temp_data = file.read(SZ_U_SHORT) + temp_chunk.bytes_read += SZ_U_SHORT + contextMaterial.metallic = (float(struct.unpack('<H', temp_data)[0]) / 100) + elif temp_chunk.ID == PERCENTAGE_FLOAT: + temp_data = file.read(SZ_FLOAT) + temp_chunk.bytes_read += SZ_FLOAT + contextMaterial.metallic = float(struct.unpack('f', temp_data)[0]) + new_chunk.bytes_read += temp_chunk.bytes_read + + elif new_chunk.ID == MAT_TRANSPARENCY: + read_chunk(file, temp_chunk) + if temp_chunk.ID == PERCENTAGE_SHORT: + temp_data = file.read(SZ_U_SHORT) + temp_chunk.bytes_read += SZ_U_SHORT + contextMaterial.diffuse_color[3] = 1 - (float(struct.unpack('<H', temp_data)[0]) / 100) + elif temp_chunk.ID == PERCENTAGE_FLOAT: + temp_data = file.read(SZ_FLOAT) + temp_chunk.bytes_read += SZ_FLOAT + contextMaterial.diffuse_color[3] = 1 - float(struct.unpack('f', temp_data)[0]) + else: + print( "Cannot read material transparency") new_chunk.bytes_read += temp_chunk.bytes_read elif new_chunk.ID == MAT_TEXTURE_MAP: @@ -578,121 +663,158 @@ def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEA read_texture(new_chunk, temp_chunk, "Specular", "SPECULARITY") elif new_chunk.ID == MAT_OPACITY_MAP: + contextMaterial.blend_method = 'BLEND' read_texture(new_chunk, temp_chunk, "Opacity", "ALPHA") + elif new_chunk.ID == MAT_REFLECTION_MAP: + read_texture(new_chunk, temp_chunk, "Reflect", "METALLIC") + elif new_chunk.ID == MAT_BUMP_MAP: read_texture(new_chunk, temp_chunk, "Bump", "NORMAL") - elif new_chunk.ID == MAT_TRANSPARENCY: - #print 'elif new_chunk.ID == MAT_TRANSPARENCY:' - read_chunk(file, temp_chunk) - - if temp_chunk.ID == PERCENTAGE_SHORT: - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) - temp_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT - contextMaterialWrapper.alpha = 1 - (float(struct.unpack('<H', temp_data)[0]) / 100) - elif temp_chunk.ID == PERCENTAGE_FLOAT: - temp_data = file.read(STRUCT_SIZE_FLOAT) - temp_chunk.bytes_read += STRUCT_SIZE_FLOAT - contextMaterialWrapper.alpha = 1 - float(struct.unpack('f', temp_data)[0]) - else: - print( "Cannot read material transparency") - + elif new_chunk.ID == MAT_BUMP_PERCENT: + temp_data = file.read(SZ_U_SHORT) + new_chunk.bytes_read += SZ_U_SHORT + contextWrapper.normalmap_strength = (float(struct.unpack('<H', temp_data)[0]) / 100) new_chunk.bytes_read += temp_chunk.bytes_read - elif new_chunk.ID == OBJECT_LIGHT: # Basic lamp support. + elif new_chunk.ID == MAT_SHIN_MAP: + read_texture(new_chunk, temp_chunk, "Shininess", "ROUGHNESS") - temp_data = file.read(STRUCT_SIZE_3FLOAT) + elif new_chunk.ID == MAT_SELFI_MAP: + read_texture(new_chunk, temp_chunk, "Emit", "EMISSION") - x, y, z = struct.unpack('<3f', temp_data) - new_chunk.bytes_read += STRUCT_SIZE_3FLOAT + elif new_chunk.ID == MAT_TEX2_MAP: + read_texture(new_chunk, temp_chunk, "Tex", "TEXTURE") + elif contextObName and new_chunk.ID == OBJECT_LIGHT: # Basic lamp support. # no lamp in dict that would be confusing - contextLamp[1] = bpy.data.lights.new("Lamp", 'POINT') - contextLamp[0] = ob = bpy.data.objects.new("Lamp", contextLamp[1]) - - context.view_layer.active_layer_collection.collection.objects.link(ob) - importedObjects.append(contextLamp[0]) - - #print 'number of faces: ', num_faces - #print x,y,z - contextLamp[0].location = x, y, z - - # Reset matrix - contextMatrix_rot = None - #contextMatrix_tx = None - #print contextLamp.name, + # ...why not? just set CreateBlenderObject to False + newLamp = bpy.data.lights.new("Lamp", 'POINT') + contextLamp = bpy.data.objects.new(contextObName, newLamp) + context.view_layer.active_layer_collection.collection.objects.link(contextLamp) + importedObjects.append(contextLamp) + temp_data = file.read(SZ_3FLOAT) + contextLamp.location = struct.unpack('<3f', temp_data) + new_chunk.bytes_read += SZ_3FLOAT + contextMatrix = None # Reset matrix + CreateBlenderObject = False + CreateLightObject = True + + elif CreateLightObject and new_chunk.ID == MAT_FLOAT_COLOR: # color + temp_data = file.read(SZ_3FLOAT) + contextLamp.data.color = struct.unpack('<3f', temp_data) + new_chunk.bytes_read += SZ_3FLOAT + elif CreateLightObject and new_chunk.ID == OBJECT_LIGHT_MULTIPLIER: # intensity + temp_data = file.read(SZ_FLOAT) + contextLamp.data.energy = float(struct.unpack('f', temp_data)[0]) + new_chunk.bytes_read += SZ_FLOAT + + elif CreateLightObject and new_chunk.ID == OBJECT_LIGHT_SPOT: # spotlight + temp_data = file.read(SZ_3FLOAT) + contextLamp.data.type = 'SPOT' + spot = mathutils.Vector(struct.unpack('<3f', temp_data)) + aim = contextLamp.location + spot + hypo = math.copysign(math.sqrt(pow(aim[1],2)+pow(aim[0],2)),aim[1]) + track = math.copysign(math.sqrt(pow(hypo,2)+pow(spot[2],2)),aim[1]) + angle = math.radians(90)-math.copysign(math.acos(hypo/track),aim[2]) + contextLamp.rotation_euler[0] = -1*math.copysign(angle, aim[1]) + contextLamp.rotation_euler[2] = -1*(math.radians(90)-math.acos(aim[0]/hypo)) + new_chunk.bytes_read += SZ_3FLOAT + temp_data = file.read(SZ_FLOAT) # hotspot + hotspot = float(struct.unpack('f', temp_data)[0]) + new_chunk.bytes_read += SZ_FLOAT + temp_data = file.read(SZ_FLOAT) # angle + beam_angle = float(struct.unpack('f', temp_data)[0]) + contextLamp.data.spot_size = math.radians(beam_angle) + contextLamp.data.spot_blend = (1.0 - (hotspot/beam_angle))*2 + new_chunk.bytes_read += SZ_FLOAT + elif CreateLightObject and new_chunk.ID == OBJECT_LIGHT_ROLL: # roll + temp_data = file.read(SZ_FLOAT) + contextLamp.rotation_euler[1] = float(struct.unpack('f', temp_data)[0]) + new_chunk.bytes_read += SZ_FLOAT + + elif contextObName and new_chunk.ID == OBJECT_CAMERA and CreateCameraObject is False: # Basic camera support + camera = bpy.data.cameras.new("Camera") + contextCamera = bpy.data.objects.new(contextObName, camera) + context.view_layer.active_layer_collection.collection.objects.link(contextCamera) + imported_objects.append(contextCamera) + temp_data = file.read(SZ_3FLOAT) + contextCamera.location = struct.unpack('<3f', temp_data) + new_chunk.bytes_read += SZ_3FLOAT + temp_data = file.read(SZ_3FLOAT) + target = mathutils.Vector(struct.unpack('<3f', temp_data)) + cam = contextCamera.location + target + focus = math.copysign(math.sqrt(pow(cam[1],2)+pow(cam[0],2)),cam[1]) + new_chunk.bytes_read += SZ_3FLOAT + temp_data = file.read(SZ_FLOAT) # triangulating camera angles + direction = math.copysign(math.sqrt(pow(focus,2)+pow(target[2],2)),cam[1]) + pitch = math.radians(90)-math.copysign(math.acos(focus/direction),cam[2]) + contextCamera.rotation_euler[0] = -1*math.copysign(pitch, cam[1]) + contextCamera.rotation_euler[1] = float(struct.unpack('f', temp_data)[0]) + contextCamera.rotation_euler[2] = -1*(math.radians(90)-math.acos(cam[0]/focus)) + new_chunk.bytes_read += SZ_FLOAT + temp_data = file.read(SZ_FLOAT) + contextCamera.data.lens = (float(struct.unpack('f', temp_data)[0]) * 10) + new_chunk.bytes_read += SZ_FLOAT + contextMatrix = None # Reset matrix + CreateBlenderObject = False + CreateCameraObject = True elif new_chunk.ID == OBJECT_MESH: - # print 'Found an OBJECT_MESH chunk' pass + elif new_chunk.ID == OBJECT_VERTICES: - """ - Worldspace vertex locations - """ - # print 'elif new_chunk.ID == OBJECT_VERTICES:' - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + """Worldspace vertex locations""" + + temp_data = file.read(SZ_U_SHORT) num_verts = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += 2 - - # print 'number of verts: ', num_verts - contextMesh_vertls = struct.unpack('<%df' % (num_verts * 3), file.read(STRUCT_SIZE_3FLOAT * num_verts)) - new_chunk.bytes_read += STRUCT_SIZE_3FLOAT * num_verts + contextMesh_vertls = struct.unpack('<%df' % (num_verts * 3), file.read(SZ_3FLOAT * num_verts)) + new_chunk.bytes_read += SZ_3FLOAT * num_verts # dummyvert is not used atm! - #print 'object verts: bytes read: ', new_chunk.bytes_read - elif new_chunk.ID == OBJECT_FACES: - # print 'elif new_chunk.ID == OBJECT_FACES:' - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + temp_data = file.read(SZ_U_SHORT) num_faces = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += 2 - #print 'number of faces: ', num_faces - - # print '\ngetting a face' - temp_data = file.read(STRUCT_SIZE_4UNSIGNED_SHORT * num_faces) - new_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT * num_faces # 4 short ints x 2 bytes each + temp_data = file.read(SZ_4U_SHORT * num_faces) + new_chunk.bytes_read += SZ_4U_SHORT * num_faces # 4 short ints x 2 bytes each contextMesh_facels = struct.unpack('<%dH' % (num_faces * 4), temp_data) contextMesh_facels = [contextMesh_facels[i - 3:i] for i in range(3, (num_faces * 4) + 3, 4)] elif new_chunk.ID == OBJECT_MATERIAL: - # print 'elif new_chunk.ID == OBJECT_MATERIAL:' material_name, read_str_len = read_string(file) new_chunk.bytes_read += read_str_len # remove 1 null character. - - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + temp_data = file.read(SZ_U_SHORT) num_faces_using_mat = struct.unpack('<H', temp_data)[0] - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT - - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * num_faces_using_mat) - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * num_faces_using_mat - + new_chunk.bytes_read += SZ_U_SHORT + temp_data = file.read(SZ_U_SHORT * num_faces_using_mat) + new_chunk.bytes_read += SZ_U_SHORT * num_faces_using_mat temp_data = struct.unpack("<%dH" % (num_faces_using_mat), temp_data) - contextMeshMaterials.append((material_name, temp_data)) - #look up the material in all the materials + elif new_chunk.ID == OBJECT_SMOOTH: + temp_data = file.read(struct.calcsize('I') * num_faces) + smoothgroup = struct.unpack('<%dI' % (num_faces), temp_data) + new_chunk.bytes_read += struct.calcsize('I') * num_faces + contextMesh_smooth = smoothgroup + elif new_chunk.ID == OBJECT_UV: - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + temp_data = file.read(SZ_U_SHORT) num_uv = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += 2 - - temp_data = file.read(STRUCT_SIZE_2FLOAT * num_uv) - new_chunk.bytes_read += STRUCT_SIZE_2FLOAT * num_uv + temp_data = file.read(SZ_2FLOAT * num_uv) + new_chunk.bytes_read += SZ_2FLOAT * num_uv contextMeshUV = struct.unpack('<%df' % (num_uv * 2), temp_data) elif new_chunk.ID == OBJECT_TRANS_MATRIX: # How do we know the matrix size? 54 == 4x4 48 == 4x3 - temp_data = file.read(STRUCT_SIZE_4x3MAT) + temp_data = file.read(SZ_4x3MAT) data = list(struct.unpack('<ffffffffffff', temp_data)) - new_chunk.bytes_read += STRUCT_SIZE_4x3MAT - - contextMatrix_rot = mathutils.Matrix((data[:3] + [0], - data[3:6] + [0], - data[6:9] + [0], - data[9:] + [1], - )).transposed() + new_chunk.bytes_read += SZ_4x3MAT + contextMatrix = mathutils.Matrix((data[:3]+[0], data[3:6]+[0], data[6:9]+[0], data[9:]+[1])).transposed() elif (new_chunk.ID == MAT_MAP_FILEPATH): texture_name, read_str_len = read_string(file) @@ -702,29 +824,32 @@ def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEA new_chunk.bytes_read += read_str_len # plus one for the null character that gets removed elif new_chunk.ID == EDITKEYFRAME: pass + + elif new_chunk.ID == KFDATA_KFSEG: + temp_data = file.read(struct.calcsize('I')) + start = struct.unpack('<I', temp_data)[0] + new_chunk.bytes_read += 4 + context.scene.frame_start = start + temp_data = file.read(struct.calcsize('I')) + stop = struct.unpack('<I', temp_data)[0] + new_chunk.bytes_read += 4 + context.scene.frame_end = stop # including these here means their EK_OB_NODE_HEADER are scanned - elif new_chunk.ID in {ED_KEY_AMBIENT_NODE, - ED_KEY_OBJECT_NODE, - ED_KEY_CAMERA_NODE, - ED_KEY_TARGET_NODE, - ED_KEY_LIGHT_NODE, - ED_KEY_L_TARGET_NODE, - ED_KEY_SPOTLIGHT_NODE}: # another object is being processed + elif new_chunk.ID in {KFDATA_AMBIENT, KFDATA_CAMERA, KFDATA_OBJECT, KFDATA_TARGET, KFDATA_LIGHT, KFDATA_L_TARGET,}: # another object is being processed child = None - elif new_chunk.ID == EK_OB_NODE_HEADER: + elif new_chunk.ID == OBJECT_NODE_HDR: object_name, read_str_len = read_string(file) new_chunk.bytes_read += read_str_len - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 2) + temp_data = file.read(SZ_U_SHORT * 2) new_chunk.bytes_read += 4 - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + temp_data = file.read(SZ_U_SHORT) hierarchy = struct.unpack('<H', temp_data)[0] new_chunk.bytes_read += 2 - child = object_dictionary.get(object_name) - if child is None: + if child is None and object_name != '$AMBIENT$': child = bpy.data.objects.new(object_name, None) # create an empty object context.view_layer.active_layer_collection.collection.objects.link(child) importedObjects.append(child) @@ -733,95 +858,147 @@ def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEA object_parent.append(hierarchy) pivot_list.append(mathutils.Vector((0.0, 0.0, 0.0))) - elif new_chunk.ID == EK_OB_INSTANCE_NAME: + elif new_chunk.ID == OBJECT_INSTANCE_NAME: object_name, read_str_len = read_string(file) - # child.name = object_name - child.name += "." + object_name + if child.name == '$$$DUMMY': + child.name = object_name + else: child.name += "." + object_name object_dictionary[object_name] = child new_chunk.bytes_read += read_str_len - # print("new instance object:", object_name) - - elif new_chunk.ID == EK_OB_PIVOT: # translation - temp_data = file.read(STRUCT_SIZE_3FLOAT) - pivot = struct.unpack('<3f', temp_data) - new_chunk.bytes_read += STRUCT_SIZE_3FLOAT - pivot_list[len(pivot_list) - 1] = mathutils.Vector(pivot) - - elif new_chunk.ID == EK_OB_POSITION_TRACK: # translation - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 5 - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 5) - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + + elif new_chunk.ID == OBJECT_PIVOT: # pivot + temp_data = file.read(SZ_3FLOAT) + pivot = struct.unpack('<3f', temp_data) + new_chunk.bytes_read += SZ_3FLOAT + pivot_list[len(pivot_list) - 1] = mathutils.Vector(pivot) + + elif KEYFRAME and new_chunk.ID == POS_TRACK_TAG: # translation + new_chunk.bytes_read += SZ_U_SHORT * 5 + temp_data = file.read(SZ_U_SHORT * 5) + temp_data = file.read(SZ_U_SHORT) nkeys = struct.unpack('<H', temp_data)[0] - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 + temp_data = file.read(SZ_U_SHORT) + new_chunk.bytes_read += SZ_U_SHORT * 2 for i in range(nkeys): - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + temp_data = file.read(SZ_U_SHORT) nframe = struct.unpack('<H', temp_data)[0] - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 2) - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 - temp_data = file.read(STRUCT_SIZE_3FLOAT) + new_chunk.bytes_read += SZ_U_SHORT + temp_data = file.read(SZ_U_SHORT * 2) + new_chunk.bytes_read += SZ_U_SHORT * 2 + temp_data = file.read(SZ_3FLOAT) loc = struct.unpack('<3f', temp_data) - new_chunk.bytes_read += STRUCT_SIZE_3FLOAT + new_chunk.bytes_read += SZ_3FLOAT if nframe == 0: child.location = loc - elif new_chunk.ID == EK_OB_ROTATION_TRACK: # rotation - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 5 - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 5) - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + elif KEYFRAME and new_chunk.ID == ROT_TRACK_TAG and child.type == 'MESH': # rotation + new_chunk.bytes_read += SZ_U_SHORT * 5 + temp_data = file.read(SZ_U_SHORT * 5) + temp_data = file.read(SZ_U_SHORT) nkeys = struct.unpack('<H', temp_data)[0] - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 + temp_data = file.read(SZ_U_SHORT) + new_chunk.bytes_read += SZ_U_SHORT * 2 for i in range(nkeys): - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + temp_data = file.read(SZ_U_SHORT) nframe = struct.unpack('<H', temp_data)[0] - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 2) - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 - temp_data = file.read(STRUCT_SIZE_4FLOAT) + new_chunk.bytes_read += SZ_U_SHORT + temp_data = file.read(SZ_U_SHORT * 2) + new_chunk.bytes_read += SZ_U_SHORT * 2 + temp_data = file.read(SZ_4FLOAT) rad, axis_x, axis_y, axis_z = struct.unpack("<4f", temp_data) - new_chunk.bytes_read += STRUCT_SIZE_4FLOAT + new_chunk.bytes_read += SZ_4FLOAT if nframe == 0: child.rotation_euler = mathutils.Quaternion((axis_x, axis_y, axis_z), -rad).to_euler() # why negative? - elif new_chunk.ID == EK_OB_SCALE_TRACK: # translation - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 5 - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 5) - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + elif KEYFRAME and new_chunk.ID == SCL_TRACK_TAG and child.type == 'MESH': # scale + new_chunk.bytes_read += SZ_U_SHORT * 5 + temp_data = file.read(SZ_U_SHORT * 5) + temp_data = file.read(SZ_U_SHORT) nkeys = struct.unpack('<H', temp_data)[0] - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 + temp_data = file.read(SZ_U_SHORT) + new_chunk.bytes_read += SZ_U_SHORT * 2 for i in range(nkeys): - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT) + temp_data = file.read(SZ_U_SHORT) nframe = struct.unpack('<H', temp_data)[0] - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT - temp_data = file.read(STRUCT_SIZE_UNSIGNED_SHORT * 2) - new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT * 2 - temp_data = file.read(STRUCT_SIZE_3FLOAT) + new_chunk.bytes_read += SZ_U_SHORT + temp_data = file.read(SZ_U_SHORT * 2) + new_chunk.bytes_read += SZ_U_SHORT * 2 + temp_data = file.read(SZ_3FLOAT) sca = struct.unpack('<3f', temp_data) - new_chunk.bytes_read += STRUCT_SIZE_3FLOAT + new_chunk.bytes_read += SZ_3FLOAT if nframe == 0: child.scale = sca - else: # (new_chunk.ID!=VERSION or new_chunk.ID!=OBJECTINFO or new_chunk.ID!=OBJECT or new_chunk.ID!=MATERIAL): - # print 'skipping to end of this chunk' - #print("unknown chunk: "+hex(new_chunk.ID)) + elif KEYFRAME and new_chunk.ID == COL_TRACK_TAG and child.type == 'LIGHT': # color + new_chunk.bytes_read += SZ_U_SHORT * 5 + temp_data = file.read(SZ_U_SHORT * 5) + temp_data = file.read(SZ_U_SHORT) + nkeys = struct.unpack('<H', temp_data)[0] + temp_data = file.read(SZ_U_SHORT) + new_chunk.bytes_read += SZ_U_SHORT * 2 + for i in range(nkeys): + temp_data = file.read(SZ_U_SHORT) + nframe = struct.unpack('<H', temp_data)[0] + new_chunk.bytes_read += SZ_U_SHORT + temp_data = file.read(SZ_U_SHORT * 2) + new_chunk.bytes_read += SZ_U_SHORT * 2 + temp_data = file.read(SZ_3FLOAT) + rgb = struct.unpack('<3f', temp_data) + new_chunk.bytes_read += SZ_3FLOAT + if nframe == 0: + child.data.color = rgb + + elif new_chunk.ID == FOV_TRACK_TAG and child.type == 'CAMERA': # Field of view + new_chunk.bytes_read += SZ_U_SHORT * 5 + temp_data = file.read(SZ_U_SHORT * 5) + temp_data = file.read(SZ_U_SHORT) + nkeys = struct.unpack('<H', temp_data)[0] + temp_data = file.read(SZ_U_SHORT) + new_chunk.bytes_read += SZ_U_SHORT * 2 + for i in range(nkeys): + temp_data = file.read(SZ_U_SHORT) + nframe = struct.unpack('<H', temp_data)[0] + new_chunk.bytes_read += SZ_U_SHORT + temp_data = file.read(SZ_U_SHORT * 2) + new_chunk.bytes_read += SZ_U_SHORT * 2 + temp_data = file.read(SZ_FLOAT) + fov = struct.unpack('<f', temp_data)[0] + new_chunk.bytes_read += SZ_FLOAT + if nframe == 0: + child.data.angle = math.radians(fov) + + elif new_chunk.ID == ROLL_TRACK_TAG and child.type == 'CAMERA': # Roll angle + new_chunk.bytes_read += SZ_U_SHORT * 5 + temp_data = file.read(SZ_U_SHORT * 5) + temp_data = file.read(SZ_U_SHORT) + nkeys = struct.unpack('<H', temp_data)[0] + temp_data = file.read(SZ_U_SHORT) + new_chunk.bytes_read += SZ_U_SHORT * 2 + for i in range(nkeys): + temp_data = file.read(SZ_U_SHORT) + nframe = struct.unpack('<H', temp_data)[0] + new_chunk.bytes_read += SZ_U_SHORT + temp_data = file.read(SZ_U_SHORT * 2) + new_chunk.bytes_read += SZ_U_SHORT * 2 + temp_data = file.read(SZ_FLOAT) + roll = struct.unpack('<f', temp_data)[0] + new_chunk.bytes_read += SZ_FLOAT + if nframe == 0: + child.rotation_euler[1] = math.radians(roll) + + else: buffer_size = new_chunk.length - new_chunk.bytes_read binary_format = "%ic" % buffer_size temp_data = file.read(struct.calcsize(binary_format)) new_chunk.bytes_read += buffer_size #update the previous chunk bytes read - # print 'previous_chunk.bytes_read += new_chunk.bytes_read' - # print previous_chunk.bytes_read, new_chunk.bytes_read previous_chunk.bytes_read += new_chunk.bytes_read - ## print 'Bytes left in this chunk: ', previous_chunk.length - previous_chunk.bytes_read # FINISHED LOOP # There will be a number of objects still not added if CreateBlenderObject: - putContextMesh(context, contextMesh_vertls, contextMesh_facels, contextMeshMaterials) + putContextMesh(context, contextMesh_vertls, contextMesh_facels, contextMeshMaterials, contextMesh_smooth) # Assign parents to objects # check _if_ we need to assign first because doing so recalcs the depsgraph @@ -843,7 +1020,8 @@ def process_next_chunk(context, file, previous_chunk, importedObjects, IMAGE_SEA if ob.type == 'MESH': pivot = pivot_list[ind] pivot_matrix = object_matrix.get(ob, mathutils.Matrix()) # unlikely to fail - pivot_matrix = mathutils.Matrix.Translation(pivot_matrix.to_3x3() @ -pivot) + pivot_matrix = mathutils.Matrix.Translation(-1*pivot) + #pivot_matrix = mathutils.Matrix.Translation(pivot_matrix.to_3x3() @ -pivot) ob.data.transform(pivot_matrix) @@ -851,6 +1029,7 @@ def load_3ds(filepath, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, + KEYFRAME=True, APPLY_MATRIX=True, global_matrix=None): # global SCN @@ -891,13 +1070,9 @@ def load_3ds(filepath, object_matrix.clear() scn = context.scene -# scn = bpy.data.scenes.active -# SCN = scn -# SCN_OBJECTS = scn.objects -# SCN_OBJECTS.selected = [] # de select all importedObjects = [] # Fill this list with objects - process_next_chunk(context, file, current_chunk, importedObjects, IMAGE_SEARCH) + process_next_chunk(context, file, current_chunk, importedObjects, IMAGE_SEARCH, KEYFRAME) # fixme, make unglobal object_dictionary.clear() @@ -917,7 +1092,7 @@ def load_3ds(filepath, # print(importedObjects) if global_matrix: for ob in importedObjects: - if ob.parent is None: + if ob.type == 'MESH' and ob.parent is None: ob.matrix_world = ob.matrix_world @ global_matrix for ob in importedObjects: @@ -989,6 +1164,7 @@ def load(operator, filepath="", constrain_size=0.0, use_image_search=True, + read_keyframe=True, use_apply_transform=True, global_matrix=None, ): @@ -997,6 +1173,7 @@ def load(operator, context, IMPORT_CONSTRAIN_BOUNDS=constrain_size, IMAGE_SEARCH=use_image_search, + KEYFRAME=read_keyframe, APPLY_MATRIX=use_apply_transform, global_matrix=global_matrix, ) -- GitLab