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