2025-12-01

This commit is contained in:
2026-03-17 14:58:51 -06:00
parent 183e865f8b
commit 4b82b57113
6846 changed files with 954887 additions and 162606 deletions
@@ -4,11 +4,14 @@
# SPDX-License-Identifier: GPL-2.0-or-later
__author__ = "Sebastian Sille <nrgsille@gmail.com>"
__version__ = "2.8.3"
__author__ = "Sebastian Sille <getmore@nrgsille.com>"
__version__ = "3.0.1"
__date__ = "24 Sep 2020"
import bpy
from . import import_3ds
from . import export_3ds
from bpy_extras.io_utils import (
ImportHelper,
ExportHelper,
@@ -23,7 +26,11 @@ from bpy.props import (
StringProperty,
CollectionProperty,
)
import bpy
from bpy.types import (
Operator,
FileHandler,
)
if "bpy" in locals():
import importlib
@@ -34,7 +41,7 @@ if "bpy" in locals():
@orientation_helper(axis_forward='Y', axis_up='Z')
class Import3DS(bpy.types.Operator, ImportHelper):
class Import3DS(Operator, ImportHelper):
"""Import from 3DS file format (.3ds)"""
bl_idname = "import_scene.max3ds"
bl_label = 'Import 3DS'
@@ -106,7 +113,6 @@ class Import3DS(bpy.types.Operator, ImportHelper):
import_transform(layout, self)
def execute(self, context):
from . import import_3ds
keywords = self.as_keywords(ignore=("axis_forward",
"axis_up",
"filter_glob",
@@ -129,14 +135,14 @@ def import_include(layout, operator):
line = body.row(align=True)
line.prop(operator, "use_image_search")
line.label(text="", icon='OUTLINER_OB_IMAGE' if operator.use_image_search else 'IMAGE_DATA')
line = body.row(align=True)
line.prop(operator, "use_collection")
line.label(text="", icon='OUTLINER_COLLECTION' if operator.use_collection else 'GROUP')
body.column().prop(operator, "object_filter")
line = body.row(align=True)
line.prop(operator, "use_keyframes")
line.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER')
line = body.row(align=True)
line.prop(operator, "use_collection")
line.label(text="", icon='OUTLINER_COLLECTION' if operator.use_collection else 'GROUP')
line = body.row(align=True)
line.prop(operator, "use_cursor")
line.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR')
@@ -157,7 +163,7 @@ def import_transform(layout, operator):
@orientation_helper(axis_forward='Y', axis_up='Z')
class Export3DS(bpy.types.Operator, ExportHelper):
class Export3DS(Operator, ExportHelper):
"""Export to 3DS file format (.3ds)"""
bl_idname = "export_scene.max3ds"
bl_label = 'Export 3DS'
@@ -183,6 +189,11 @@ class Export3DS(bpy.types.Operator, ExportHelper):
description="Take the scene unit length settings into account",
default=False,
)
use_images: BoolProperty(
name="Images",
description="Export all associated images from the scene",
default=True,
)
use_selection: BoolProperty(
name="Selection",
description="Export selected objects only",
@@ -200,11 +211,16 @@ class Export3DS(bpy.types.Operator, ExportHelper):
description="Object types to export",
default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY', 'OTHER'},
)
use_apply_transform: bpy.props.BoolProperty(
use_apply_transform: BoolProperty(
name="Apply Transform",
description="Apply matrix transform before export",
default=True,
)
use_invisible: BoolProperty(
name="Invisible",
description="Export invisible objects",
default=False,
)
use_keyframes: BoolProperty(
name="Animation",
description="Write the keyframe data",
@@ -237,7 +253,6 @@ class Export3DS(bpy.types.Operator, ExportHelper):
export_transform(layout, self)
def execute(self, context):
from . import export_3ds
keywords = self.as_keywords(ignore=("axis_forward",
"axis_up",
"filter_glob",
@@ -255,10 +270,19 @@ def export_include(layout, operator, browser):
header, body = layout.panel("MAX3DS_export_include", default_closed=False)
header.label(text="Include")
if body:
line = body.row(align=True)
line.prop(operator, "use_images")
line.label(text="", icon='OUTLINER_OB_IMAGE' if operator.use_images else 'IMAGE_DATA')
line = body.row(align=True)
line.prop(operator, "use_invisible")
line.label(text="", icon='HIDE_OFF' if operator.use_invisible else 'HIDE_ON')
line = body.row(align=True)
line.prop(operator, "use_selection")
line.label(text="", icon='RESTRICT_SELECT_OFF' if operator.use_selection else 'RESTRICT_SELECT_ON')
if browser:
line = body.row(align=True)
line.prop(operator, "use_selection")
line.label(text="", icon='RESTRICT_SELECT_OFF' if operator.use_selection else 'RESTRICT_SELECT_ON')
line.prop(operator, "use_collection")
line.label(text="", icon='OUTLINER_COLLECTION' if operator.use_collection else 'GROUP')
body.column().prop(operator, "object_filter")
line = body.row(align=True)
line.prop(operator, "use_keyframes")
@@ -266,10 +290,6 @@ def export_include(layout, operator, browser):
line = body.row(align=True)
line.prop(operator, "use_hierarchy")
line.label(text="", icon='OUTLINER' if operator.use_hierarchy else 'CON_CHILDOF')
if browser:
line = body.row(align=True)
line.prop(operator, "use_collection")
line.label(text="", icon='OUTLINER_COLLECTION' if operator.use_collection else 'GROUP')
line = body.row(align=True)
line.prop(operator, "use_cursor")
line.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR')
@@ -290,7 +310,7 @@ def export_transform(layout, operator):
body.prop(operator, "axis_up")
class IO_FH_max3ds(bpy.types.FileHandler):
class IO_FH_max3ds(FileHandler):
bl_idname = "IO_FH_max3ds"
bl_label = "3DS"
bl_import_operator = "import_scene.max3ds"
@@ -1,7 +1,7 @@
schema_version = "1.0.0"
id = "autodesk_3ds_format"
name = "Autodesk 3D Studio (.3ds)"
version = "2.8.3"
version = "3.0.1"
tagline = "Import-Export 3DS scenes, objects, cameras, lights & animations"
maintainer = "Sebastian Sille"
type = "add-on"
@@ -10,9 +10,9 @@ blender_version_min = "4.2.0"
license = ["SPDX:GPL-3.0-or-later"]
website = "https://projects.blender.org/extensions/io_scene_3ds"
copyright = [
"2024 Bob Holcomb",
"2024 Campbell Barton",
"2024 Sebastian Schrand",
"2005 Bob Holcomb",
"2011 Campbell Barton",
"2020 Sebastian Schrand",
]
[permissions]
files = "Import-Export Autodesk 3DS files"
@@ -8,12 +8,14 @@ Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen) and
from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode.
"""
import os
import bpy
import time
import math
import struct
import mathutils
import bpy_extras
from pathlib import Path
from bpy_extras import node_shader_utils
###################
@@ -538,7 +540,7 @@ def get_material_image(material):
def get_uv_image(ma):
""" Get image from material wrapper."""
if ma and ma.use_nodes:
if ma and hasattr(ma, "use_nodes") and ma.use_nodes:
mat_wrap = node_shader_utils.PrincipledBSDFWrapper(ma)
mat_tex = mat_wrap.base_color_texture
if mat_tex and mat_tex.image is not None:
@@ -547,16 +549,28 @@ def get_uv_image(ma):
return get_material_image(ma)
def save_image(img, path):
""" Save image to destination filepath."""
if img and path is not None:
img_name = Path(img.filepath).name
file_path = os.path.join(path, img_name)
if not os.path.isfile(file_path):
try:
img.save(filepath=file_path)
except Exception as exc:
print("%s - Image %s not exported." % (exc, img_name))
def make_material_subchunk(chunk_id, color):
"""Make a material subchunk.
Used for color subchunks, such as diffuse color or ambient color subchunks."""
mat_sub = _3ds_chunk(chunk_id)
col1 = _3ds_chunk(RGB1)
col1.add_variable("color1", _3ds_rgb_color(color))
col1.add_variable("color1", _3ds_rgb_color(clamp_values(color, 0.0, 1.0)))
mat_sub.add_subchunk(col1)
# Optional
# col2 = _3ds_chunk(RGBI)
# col2.add_variable("color2", _3ds_rgb_color(color))
# col2.add_variable("color2", _3ds_rgb_color(clamp_values(color, 0.0, 1.0)))
# mat_sub.add_subchunk(col2)
return mat_sub
@@ -574,7 +588,6 @@ def make_percent_subchunk(chunk_id, percent):
return pct_sub
def make_mapping_chunks(tex_chunk, scale, translation, rotation):
"""Make Material Map mapping chunks."""
@@ -599,7 +612,7 @@ def make_mapping_chunks(tex_chunk, scale, translation, rotation):
tex_chunk.add_subchunk(tex_map_angle)
def make_texture_chunk(chunk_id, teximages, texmixer, pct, mapnode=None):
def make_texture_chunk(chunk_id, teximages, path=None, mapnode=None, pct=1, texmixer=False):
"""Make Material Map texture chunk."""
# Add texture percentage value (100 = 1.0)
mat_sub = make_percent_subchunk(chunk_id, pct)
@@ -627,7 +640,7 @@ def make_texture_chunk(chunk_id, teximages, texmixer, pct, mapnode=None):
make_mapping_chunks(mat_sub, mapnode.inputs[3].default_value, mapnode.inputs[1].default_value, mapnode.inputs[2].default_value)
if texmixer: # Add tint color
tint = (1.0, 1.0, 1.0)
tint = (0.0, 0.0, 0.0)
tint1 = _3ds_chunk(MAP_COL1)
tint2 = _3ds_chunk(MAP_COL2)
input1 = next((ip.default_value for ip in texmixer.inputs if ip.identifier in {'Color1', 'A_Color'}), tint)
@@ -637,10 +650,13 @@ def make_texture_chunk(chunk_id, teximages, texmixer, pct, mapnode=None):
mat_sub.add_subchunk(tint1)
mat_sub.add_subchunk(tint2)
for tex in teximages:
extend = tex.extension
add_image(tex.image, extend)
has_entry = True
if bool(teximages):
for tex in teximages:
if tex.image is not None:
extend = tex.extension
save_image(tex.image, path)
add_image(tex.image, extend)
has_entry = True
return mat_sub if has_entry else None
@@ -697,7 +713,7 @@ def make_material_texture_chunk(chunk_id, texslots, pct):
make_mapping_chunks(mat_sub, texslot.scale, texslot.translation, texslot.rotation)
if texslot.socket_dst.identifier == 'Specular Tint': # Add tint color
if texslot.socket_dst.identifier in {'Metallic', 'Roughness', 'Specular Tint'}: # Add tint color
tint1 = _3ds_chunk(MAP_COL1)
tint2 = _3ds_chunk(MAP_COL2)
color1 = clamp_values(texslot.node_dst.inputs['Coat Tint'].default_value[:3], 0.0, 1.0)
@@ -717,7 +733,7 @@ def make_material_texture_chunk(chunk_id, texslots, pct):
return mat_sub if has_entry else None
def make_material_chunk(material, image):
def make_material_chunk(material, image, path):
"""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."""
@@ -738,7 +754,7 @@ def make_material_chunk(material, image):
material_chunk.add_subchunk(make_percent_subchunk(MATSHIN2, 0.5))
material_chunk.add_subchunk(shading)
elif material and material.use_nodes:
elif material and material.node_tree:
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]))
@@ -771,12 +787,13 @@ def make_material_chunk(material, image):
primary_map = wrap.base_color_texture.node_mapping
matmap = make_material_texture_chunk(MAT_DIFFUSEMAP, color, c_pct)
if matmap:
save_image(image, path)
material_chunk.add_subchunk(matmap)
primary_tex = True
if mxtex and not primary_tex:
primary_map = next((lk.from_node for lk in mtlks if lk.from_node.type == 'MAPPING' and lk.to_node.name == mxtex[0].name), None)
material_chunk.add_subchunk(make_texture_chunk(MAT_DIFFUSEMAP, mxtex, mixer, mxpct, primary_map))
material_chunk.add_subchunk(make_texture_chunk(MAT_DIFFUSEMAP, mxtex, path, primary_map, mxpct, mixer))
primary_tex = True
if wrap.specular_tint_texture:
@@ -784,6 +801,7 @@ def make_material_chunk(material, image):
s_pct = material.specular_intensity
matmap = make_material_texture_chunk(MAT_SPECMAP, spec, s_pct)
if matmap:
save_image(wrap.specular_tint_texture.image, path)
material_chunk.add_subchunk(matmap)
if wrap.alpha_texture:
@@ -791,6 +809,7 @@ def make_material_chunk(material, image):
a_pct = material.diffuse_color[3]
matmap = make_material_texture_chunk(MAT_OPACMAP, alpha, a_pct)
if matmap:
save_image(wrap.alpha_texture.image, path)
material_chunk.add_subchunk(matmap)
if wrap.metallic_texture:
@@ -798,6 +817,7 @@ def make_material_chunk(material, image):
m_pct = material.metallic
matmap = make_material_texture_chunk(MAT_REFLMAP, metallic, m_pct)
if matmap:
save_image(wrap.metallic_texture.image, path)
material_chunk.add_subchunk(matmap)
if wrap.normalmap_texture:
@@ -806,6 +826,7 @@ def make_material_chunk(material, image):
primary_map = wrap.normalmap_texture.node_mapping
matmap = make_material_texture_chunk(MAT_BUMPMAP, normal, b_pct)
if matmap:
save_image(wrap.normalmap_texture.image, path)
material_chunk.add_subchunk(matmap)
material_chunk.add_subchunk(make_percent_subchunk(MAT_BUMP_PERCENT, b_pct))
@@ -814,6 +835,7 @@ def make_material_chunk(material, image):
r_pct = 1 - material.roughness
matmap = make_material_texture_chunk(MAT_SHINMAP, roughness, r_pct)
if matmap:
save_image(wrap.roughness_texture.image, path)
material_chunk.add_subchunk(matmap)
if wrap.emission_color_texture:
@@ -821,6 +843,7 @@ def make_material_chunk(material, image):
e_pct = wrap.emission_strength
matmap = make_material_texture_chunk(MAT_SELFIMAP, emission, e_pct)
if matmap:
save_image(wrap.emission_color_texture.image, path)
material_chunk.add_subchunk(matmap)
if wrap.transmission_texture:
@@ -828,18 +851,21 @@ def make_material_chunk(material, image):
t_pct = wrap.transmission
matmap = make_material_texture_chunk(MAT_OPACMASK, transmission, t_pct)
if matmap:
save_image(wrap.transmission_texture.image, path)
material_chunk.add_subchunk(matmap)
if wrap.specular_texture:
specular = [wrap.specular_texture]
matmap = make_material_texture_chunk(MAT_SPECMASK, specular, 1)
if matmap:
save_image(wrap.specular_texture.image, path)
material_chunk.add_subchunk(matmap)
if wrap.emission_strength_texture:
luminous = [wrap.emission_strength_texture]
matmap = make_material_texture_chunk(MAT_SELFIMASK, luminous, 1)
if matmap:
save_image(wrap.emission_strength_texture.image, path)
material_chunk.add_subchunk(matmap)
matmap = diffmask = normalmask = shinmask = reflmask = None
@@ -852,15 +878,15 @@ def make_material_chunk(material, image):
sheenmask = link.from_node if link.from_node.type == tximg and link.to_socket.identifier == 'Sheen Weight' else None
coatmask = link.from_node if link.from_node.type == tximg and link.to_socket.identifier == 'Coat Weight' else None
if secondary:
matmap = make_texture_chunk(MAT_TEX2MAP, [secondary], mixer, 1 - mxpct, primary_map)
matmap = make_texture_chunk(MAT_TEX2MAP, [secondary], path, primary_map, 1 - mxpct, mixer)
if texmask:
diffmask = make_texture_chunk(MAT_TEXMASK, [texmask], False, mxpct, primary_map)
diffmask = make_texture_chunk(MAT_TEXMASK, [texmask], path, primary_map, mxpct)
if bumpmask:
normalmask = make_texture_chunk(MAT_BUMPMASK, [bumpmask], False, 1, primary_map)
normalmask = make_texture_chunk(MAT_BUMPMASK, [bumpmask], path, primary_map)
if sheenmask:
shinmask = make_texture_chunk(MAT_SHINMASK, [sheenmask], False, 1)
shinmask = make_texture_chunk(MAT_SHINMASK, [sheenmask], path)
if coatmask:
reflmask = make_texture_chunk(MAT_REFLMASK, [coatmask], False, 1)
reflmask = make_texture_chunk(MAT_REFLMASK, [coatmask], path)
if primary_tex and matmap:
material_chunk.add_subchunk(matmap)
if diffmask:
@@ -886,6 +912,7 @@ def make_material_chunk(material, image):
slots = [get_material_image(material)] # Can be None
if image:
save_image(image, path)
material_chunk.add_subchunk(make_texture_chunk(MAT_DIFFUSEMAP, slots))
return material_chunk
@@ -1198,108 +1225,108 @@ def make_track_chunk(ID, ob, ob_pos, ob_rot, ob_size, ob_mtx):
a position, rotation, scale, roll, color, fov, hotspot or falloff track."""
track_chunk = _3ds_chunk(ID)
if ID in {POS_TRACK_TAG, ROT_TRACK_TAG, SCL_TRACK_TAG, ROLL_TRACK_TAG} and ob.animation_data and ob.animation_data.action:
if (ID in {POS_TRACK_TAG, ROT_TRACK_TAG, SCL_TRACK_TAG, ROLL_TRACK_TAG} and
ob.animation_data and ob.animation_data.action and ob.animation_data.action.fcurves):
action = ob.animation_data.action
if action.fcurves:
fcurves = action.fcurves
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
nkeys += 1
kframes = sorted(set(kframes))
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start)))
track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end)))
track_chunk.add_variable("nkeys", _3ds_uint(nkeys))
fcurves = action.fcurves
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
nkeys += 1
kframes = sorted(set(kframes))
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start)))
track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end)))
track_chunk.add_variable("nkeys", _3ds_uint(nkeys))
if ID == POS_TRACK_TAG: # Position
for i, frame in enumerate(kframes):
pos_track = [fc for fc in fcurves if fc is not None and fc.data_path == 'location']
pos_x = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 0), ob_pos.x)
pos_y = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 1), ob_pos.y)
pos_z = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 2), ob_pos.z)
pos = ob_mtx @ mathutils.Vector((pos_x, pos_y, pos_z))
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("position", _3ds_point_3d((pos.x, pos.y, pos.z)))
if ID == POS_TRACK_TAG: # Position
for i, frame in enumerate(kframes):
pos_track = [fc for fc in fcurves if fc is not None and fc.data_path == 'location']
pos_x = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 0), ob_pos.x)
pos_y = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 1), ob_pos.y)
pos_z = next((tc.evaluate(frame) for tc in pos_track if tc.array_index == 2), ob_pos.z)
pos = ob_mtx @ mathutils.Vector((pos_x, pos_y, pos_z))
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("position", _3ds_point_3d((pos.x, pos.y, pos.z)))
elif ID == ROT_TRACK_TAG: # Rotation
for i, frame in enumerate(kframes):
rot_track = [fc for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
rot_x = next((tc.evaluate(frame) for tc in rot_track if tc.array_index == 0), ob_rot.x)
rot_y = next((tc.evaluate(frame) for tc in rot_track if tc.array_index == 1), ob_rot.y)
rot_z = next((tc.evaluate(frame) for tc in rot_track if tc.array_index == 2), ob_rot.z)
quat = mathutils.Euler((rot_x, rot_y, rot_z)).to_quaternion().inverted()
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("rotation", _3ds_point_4d((quat.angle, quat.axis.x, quat.axis.y, quat.axis.z)))
elif ID == ROT_TRACK_TAG: # Rotation
for i, frame in enumerate(kframes):
rot_track = [fc for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
rot_x = next((tc.evaluate(frame) for tc in rot_track if tc.array_index == 0), ob_rot.x)
rot_y = next((tc.evaluate(frame) for tc in rot_track if tc.array_index == 1), ob_rot.y)
rot_z = next((tc.evaluate(frame) for tc in rot_track if tc.array_index == 2), ob_rot.z)
quat = mathutils.Euler((rot_x, rot_y, rot_z)).to_quaternion().inverted()
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("rotation", _3ds_point_4d((quat.angle, quat.axis.x, quat.axis.y, quat.axis.z)))
elif ID == SCL_TRACK_TAG: # Scale
for i, frame in enumerate(kframes):
scale_track = [fc for fc in fcurves if fc is not None and fc.data_path == 'scale']
size_x = next((tc.evaluate(frame) for tc in scale_track if tc.array_index == 0), ob_size.x)
size_y = next((tc.evaluate(frame) for tc in scale_track if tc.array_index == 1), ob_size.y)
size_z = next((tc.evaluate(frame) for tc in scale_track if tc.array_index == 2), ob_size.z)
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("scale", _3ds_point_3d((size_x, size_y, size_z)))
elif ID == SCL_TRACK_TAG: # Scale
for i, frame in enumerate(kframes):
scale_track = [fc for fc in fcurves if fc is not None and fc.data_path == 'scale']
size_x = next((tc.evaluate(frame) for tc in scale_track if tc.array_index == 0), ob_size.x)
size_y = next((tc.evaluate(frame) for tc in scale_track if tc.array_index == 1), ob_size.y)
size_z = next((tc.evaluate(frame) for tc in scale_track if tc.array_index == 2), ob_size.z)
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("scale", _3ds_point_3d((size_x, size_y, size_z)))
elif ID == ROLL_TRACK_TAG: # Roll
for i, frame in enumerate(kframes):
roll_track = [fc for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
roll = next((tc.evaluate(frame) for tc in roll_track if tc.array_index == 1), ob_rot.y)
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("roll", _3ds_float(round(math.degrees(roll), 4)))
elif ID == ROLL_TRACK_TAG: # Roll
for i, frame in enumerate(kframes):
roll_track = [fc for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
roll = next((tc.evaluate(frame) for tc in roll_track if tc.array_index == 1), ob_rot.y)
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("roll", _3ds_float(round(math.degrees(roll), 4)))
elif ID in {COL_TRACK_TAG, FOV_TRACK_TAG, HOTSPOT_TRACK_TAG, FALLOFF_TRACK_TAG} and ob.data.animation_data and ob.data.animation_data.action:
elif (ID in {COL_TRACK_TAG, FOV_TRACK_TAG, HOTSPOT_TRACK_TAG, FALLOFF_TRACK_TAG} and
ob.data.animation_data and ob.data.animation_data.action and ob.data.animation_data.action.fcurves):
action = ob.data.animation_data.action
if action.fcurves:
fcurves = action.fcurves
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
nkeys += 1
kframes = sorted(set(kframes))
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start)))
track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end)))
track_chunk.add_variable("nkeys", _3ds_uint(nkeys))
fcurves = action.fcurves
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
nkeys += 1
kframes = sorted(set(kframes))
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start)))
track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end)))
track_chunk.add_variable("nkeys", _3ds_uint(nkeys))
if ID == COL_TRACK_TAG: # Color
for i, frame in enumerate(kframes):
color = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'color']
if not color:
color = ob.data.color[:3]
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("color", _3ds_float_color(color))
if ID == COL_TRACK_TAG: # Color
for i, frame in enumerate(kframes):
color = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'color']
if not color:
color = ob.data.color[:3]
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("color", _3ds_float_color(color))
elif ID == FOV_TRACK_TAG: # Field of view
for i, frame in enumerate(kframes):
lens = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'lens'), ob.data.lens)
fov = 2 * math.atan(ob.data.sensor_width / (2 * lens))
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("fov", _3ds_float(round(math.degrees(fov), 4)))
elif ID == FOV_TRACK_TAG: # Field of view
for i, frame in enumerate(kframes):
lens = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'lens'), ob.data.lens)
fov = 2 * math.atan(ob.data.sensor_width / (2 * lens))
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("fov", _3ds_float(round(math.degrees(fov), 4)))
elif ID == HOTSPOT_TRACK_TAG: # Hotspot
for i, frame in enumerate(kframes):
beamsize = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_size'), ob.data.spot_size)
blend = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_blend'), ob.data.spot_blend)
hot_spot = math.degrees(beamsize) - (blend * math.floor(math.degrees(beamsize)))
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("hotspot", _3ds_float(round(hot_spot, 4)))
elif ID == HOTSPOT_TRACK_TAG: # Hotspot
for i, frame in enumerate(kframes):
beamsize = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_size'), ob.data.spot_size)
blend = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_blend'), ob.data.spot_blend)
hot_spot = math.degrees(beamsize) - (blend * math.floor(math.degrees(beamsize)))
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("hotspot", _3ds_float(round(hot_spot, 4)))
elif ID == FALLOFF_TRACK_TAG: # Falloff
for i, frame in enumerate(kframes):
fall_off = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_size'), ob.data.spot_size)
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("falloff", _3ds_float(round(math.degrees(fall_off), 4)))
elif ID == FALLOFF_TRACK_TAG: # Falloff
for i, frame in enumerate(kframes):
fall_off = next((fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_size'), ob.data.spot_size)
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("falloff", _3ds_float(round(math.degrees(fall_off), 4)))
else:
track_chunk.add_variable("track_flags", _3ds_ushort(0x40)) # Based on observation default flag is 0x40
@@ -1489,34 +1516,33 @@ def make_target_node(ob, name_id, transmtx, position, rotation):
# Add track chunks for target position
track_chunk = _3ds_chunk(POS_TRACK_TAG)
if ob.animation_data and ob.animation_data.action:
if ob.animation_data and ob.animation_data.action and ob.animation_data.action.fcurves:
action = ob.animation_data.action
if action.fcurves:
fcurves = action.fcurves
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
nkeys += 1
kframes = sorted(set(kframes))
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start)))
track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end)))
track_chunk.add_variable("nkeys", _3ds_uint(nkeys))
fcurves = action.fcurves
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
nkeys += 1
kframes = sorted(set(kframes))
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start)))
track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end)))
track_chunk.add_variable("nkeys", _3ds_uint(nkeys))
for i, frame in enumerate(kframes):
loc_target = [fc for fc in fcurves if fc is not None and fc.data_path == 'location']
loc_x = next((tc.evaluate(frame) for tc in loc_target if tc.array_index == 0), ob_pos.x)
loc_y = next((tc.evaluate(frame) for tc in loc_target if tc.array_index == 1), ob_pos.y)
loc_z = next((tc.evaluate(frame) for tc in loc_target if tc.array_index == 2), ob_pos.z)
rot_target = [fc for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
rot_x = next((tc.evaluate(frame) for tc in rot_target if tc.array_index == 0), ob_rot.x)
rot_z = next((tc.evaluate(frame) for tc in rot_target if tc.array_index == 2), ob_rot.z)
target_distance = ob_mtx @ mathutils.Vector((loc_x, loc_y, loc_z))
target_pos = calc_target(target_distance, rot_x, rot_z)
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("position", _3ds_point_3d(target_pos))
for i, frame in enumerate(kframes):
loc_target = [fc for fc in fcurves if fc is not None and fc.data_path == 'location']
loc_x = next((tc.evaluate(frame) for tc in loc_target if tc.array_index == 0), ob_pos.x)
loc_y = next((tc.evaluate(frame) for tc in loc_target if tc.array_index == 1), ob_pos.y)
loc_z = next((tc.evaluate(frame) for tc in loc_target if tc.array_index == 2), ob_pos.z)
rot_target = [fc for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
rot_x = next((tc.evaluate(frame) for tc in rot_target if tc.array_index == 0), ob_rot.x)
rot_z = next((tc.evaluate(frame) for tc in rot_target if tc.array_index == 2), ob_rot.z)
target_distance = ob_mtx @ mathutils.Vector((loc_x, loc_y, loc_z))
target_pos = calc_target(target_distance, rot_x, rot_z)
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("position", _3ds_point_3d(target_pos))
else: # Track header
track_chunk.add_variable("track_flags", _3ds_ushort(0x40)) # Based on observation default flag is 0x40
@@ -1553,19 +1579,21 @@ def make_ambient_node(world):
amb_node_header_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT))
amb_node.add_subchunk(amb_node_header_chunk)
if world.use_nodes and world.node_tree.animation_data.action:
if world.node_tree and world.node_tree.animation_data.action and world.node_tree.animation_data.action.fcurves:
ambioutput = 'EMISSION' ,'MIX_SHADER', 'WORLD_OUTPUT'
action = world.node_tree.animation_data.action
links = world.node_tree.links
ambilinks = [lk for lk in links if lk.from_node.type in {'EMISSION', 'RGB'} and lk.to_node.type in ambioutput]
if ambilinks and action.fcurves:
if ambilinks:
fcurves = action.fcurves
fcurves.update()
emission = next((lk.from_socket.node for lk in ambilinks if lk.to_node.type in ambioutput), False)
ambinode = next((lk.from_socket.node for lk in ambilinks if lk.to_node.type == 'EMISSION'), emission)
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
ambipath = ('nodes[\"RGB\"].outputs[0].default_value' if ambinode and ambinode.type == 'RGB' else
'nodes[\"Emission\"].inputs[0].default_value')
if ambinode and ambinode.type == 'RGB':
ambipath = f'nodes[\"{ambinode.name}\"].outputs[0].default_value'
else:
ambipath = 'nodes[\"Emission\"].inputs[0].default_value'
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
@@ -1584,29 +1612,28 @@ def make_ambient_node(world):
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("color", _3ds_float_color(ambient[:3]))
elif world.animation_data.action:
elif world.animation_data.action and world.animation_data.action.fcurves:
action = world.animation_data.action
if action.fcurves:
fcurves = action.fcurves
fcurves.update()
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
nkeys += 1
kframes = sorted(set(kframes))
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start)))
track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end)))
track_chunk.add_variable("nkeys", _3ds_uint(nkeys))
fcurves = action.fcurves
fcurves.update()
kframes = [kf.co[0] for kf in [fc for fc in fcurves if fc is not None][0].keyframe_points]
nkeys = len(kframes)
if not 0 in kframes:
kframes.append(0)
nkeys += 1
kframes = sorted(set(kframes))
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
track_chunk.add_variable("frame_start", _3ds_uint(int(action.frame_start)))
track_chunk.add_variable("frame_total", _3ds_uint(int(action.frame_end)))
track_chunk.add_variable("nkeys", _3ds_uint(nkeys))
for i, frame in enumerate(kframes):
ambient = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'color']
if not ambient:
ambient = amb_color
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("color", _3ds_float_color(ambient))
for i, frame in enumerate(kframes):
ambient = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'color']
if not ambient:
ambient = amb_color
track_chunk.add_variable("tcb_frame", _3ds_uint(int(frame)))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("color", _3ds_float_color(ambient))
else: # Track header
track_chunk.add_variable("track_flags", _3ds_ushort(0x40))
@@ -1627,45 +1654,25 @@ def make_ambient_node(world):
# EXPORT #
##########
def save(operator, context, filepath="", collection="", scale_factor=1.0, use_scene_unit=False,
use_selection=False, object_filter=None, use_apply_transform=True, use_keyframes=True,
use_hierarchy=False, use_collection=False, global_matrix=None, use_cursor=False):
def save_3ds(context, filepath="", collection="", items=[], scale_factor=1.0, global_matrix=None,
use_selection=False, use_apply_transform=True, object_filter=None, use_invisible=False,
use_images=False, use_keyframes=True, use_hierarchy=False, use_cursor=False):
"""Save the Blender scene to a 3ds file."""
print("exporting 3DS: %r..." % (Path(filepath).name), end="")
image_path = Path(filepath).parent if use_images else None
# Time the export
duration = time.time()
context.window.cursor_set('WAIT')
scene = context.scene
layer = context.view_layer
depsgraph = context.evaluated_depsgraph_get()
items = scene.objects
world = scene.world
unit_measure = 1.0
if use_scene_unit:
unit_length = scene.unit_settings.length_unit
if unit_length == 'MILES':
unit_measure = 0.000621371
elif unit_length == 'KILOMETERS':
unit_measure = 0.001
elif unit_length == 'FEET':
unit_measure = 3.280839895
elif unit_length == 'INCHES':
unit_measure = 39.37007874
elif unit_length == 'CENTIMETERS':
unit_measure = 100
elif unit_length == 'MILLIMETERS':
unit_measure = 1000
elif unit_length == 'THOU':
unit_measure = 39370.07874
elif unit_length == 'MICROMETERS':
unit_measure = 1000000
mtx_scale = mathutils.Matrix.Scale((scale_factor),4)
mtx_scale = mathutils.Matrix.Scale((scale_factor * unit_measure),4)
if use_collection:
items = layer.active_layer_collection.collection.all_objects
elif collection:
if collection and not items:
item_collection = bpy.data.collections.get(collection)
if item_collection:
items = item_collection.all_objects
@@ -1704,7 +1711,7 @@ def save(operator, context, filepath="", collection="", scale_factor=1.0, use_sc
free_objects = []
if object_filter is None:
object_filter = {'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY', 'OTHER'}
object_filter = {'MESH', 'LIGHT', 'CAMERA', 'EMPTY', 'OTHER'}
if 'OTHER' in object_filter:
object_filter.remove('OTHER')
@@ -1712,10 +1719,14 @@ def save(operator, context, filepath="", collection="", scale_factor=1.0, use_sc
object_filter.update(OTHERS)
EMPTYS.update(DUMMYS)
if use_selection:
if use_selection and not use_invisible:
objects = [ob for ob in items if ob.type in object_filter and ob.visible_get(view_layer=layer) and ob.select_get(view_layer=layer)]
else:
elif use_selection and use_invisible:
objects = [ob for ob in items if ob.type in object_filter and ob.select_get(view_layer=layer) or not ob.visible_get(view_layer=layer)]
elif not use_selection and not use_invisible:
objects = [ob for ob in items if ob.type in object_filter and ob.visible_get(view_layer=layer)]
else:
objects = [ob for ob in items if ob.type in object_filter]
empty_objects = [ob for ob in objects if ob.type in EMPTYS]
light_objects = [ob for ob in objects if ob.type == 'LIGHT']
@@ -1778,7 +1789,7 @@ def save(operator, context, filepath="", collection="", scale_factor=1.0, use_sc
# Make MATERIAL chunks for all materials used in the meshes
for ma_image in materialDict.values():
object_info.add_subchunk(make_material_chunk(ma_image[0], ma_image[1]))
object_info.add_subchunk(make_material_chunk(ma_image[0], ma_image[1], image_path))
# Add MASTERSCALE element
mscale = _3ds_chunk(MASTERSCALE)
@@ -1800,7 +1811,7 @@ def save(operator, context, filepath="", collection="", scale_factor=1.0, use_sc
object_info.add_subchunk(ambient_chunk)
# Add BACKGROUND and BITMAP
if world.use_nodes:
if world.node_tree and world.node_tree.nodes.get("Background"):
bgtype = 'BACKGROUND'
ntree = world.node_tree.links
background_color_chunk = _3ds_chunk(RGB)
@@ -1817,6 +1828,7 @@ def save(operator, context, filepath="", collection="", scale_factor=1.0, use_sc
background_color_chunk.add_variable("color", _3ds_float_color(bg_color))
background_chunk.add_subchunk(background_color_chunk)
if bg_image and bg_image is not None:
save_image(bg_image, image_path)
background_image = _3ds_chunk(BITMAP)
background_flag = _3ds_chunk(USE_BITMAP)
background_image.add_variable("image", _3ds_string(sane_name(bg_image.name)))
@@ -1962,7 +1974,7 @@ def save(operator, context, filepath="", collection="", scale_factor=1.0, use_sc
if object_chunk.validate():
object_info.add_subchunk(object_chunk)
else:
operator.report({'WARNING'}, "Object %r can't be written into a 3DS file" % ob.name)
print("Object %r can't be written into a 3DS file" % ob.name)
# Export object node
if use_keyframes and not use_hierarchy:
@@ -2041,6 +2053,7 @@ def save(operator, context, filepath="", collection="", scale_factor=1.0, use_sc
bpnode = next((lk.from_node.type for lk in links if lk.from_node.type in bpmix and lk.to_node.type == bshade), bshade)
bitmap = next((lk.from_node.image for lk in links if lk.from_node.type in bptex and lk.to_node.type == bpnode), False)
if bitmap and bitmap is not None:
save_image(bitmap, image_path)
spot_projector_chunk = _3ds_chunk(LIGHT_SPOT_PROJECTOR)
spot_projector_chunk.add_variable("image", _3ds_string(sane_name(bitmap.name)))
spotlight_chunk.add_subchunk(spot_projector_chunk)
@@ -2135,9 +2148,48 @@ def save(operator, context, filepath="", collection="", scale_factor=1.0, use_sc
# Debugging only: report the exporting time
context.window.cursor_set('DEFAULT')
print("3ds export time: %.2f" % (time.time() - duration))
print(" done in %.4f sec." % (time.time() - duration))
# Debugging only: dump the chunk hierarchy
# primary.dump()
def save(operator, context, filepath="", collection="", scale_factor=1.0, use_scene_unit=False, use_images=True,
use_selection=False, object_filter=None, use_apply_transform=True, use_invisible=False, use_keyframes=True,
use_hierarchy=False, use_collection=False, global_matrix=None, use_cursor=False):
unit_measure = 1.0
if use_scene_unit:
unit_length = context.scene.unit_settings.length_unit
if unit_length == 'MILES':
unit_measure = 0.000621371
elif unit_length == 'KILOMETERS':
unit_measure = 0.001
elif unit_length == 'FEET':
unit_measure = 3.280839895
elif unit_length == 'INCHES':
unit_measure = 39.37007874
elif unit_length == 'CENTIMETERS':
unit_measure = 100
elif unit_length == 'MILLIMETERS':
unit_measure = 1000
elif unit_length == 'THOU':
unit_measure = 39370.07874
elif unit_length == 'MICROMETERS':
unit_measure = 1000000
items = context.scene.objects
viewlayer = context.view_layer
master_scale = scale_factor * unit_measure
if use_collection:
items = viewlayer.active_layer_collection.collection.all_objects
elif collection:
item_collection = bpy.data.collections.get(collection)
if item_collection:
items = item_collection.all_objects
save_3ds(context, filepath, collection, items, master_scale, global_matrix, use_selection, use_apply_transform,
object_filter, use_invisible, use_images, use_keyframes, use_hierarchy, use_cursor)
return {'FINISHED'}
@@ -255,7 +255,8 @@ def skip_to_end(file, skip_chunk):
# MATERIALS #
#############
def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, offset, angle, tint1, tint2, mapto):
def add_texture_to_material(image, contextWrapper, pct, extend, alpha,
scale, offset, angle, luma, tint1, tint2, mapto):
shader = contextWrapper.node_principled_bsdf
nodetree = contextWrapper.material.node_tree
shader.location = (-300, 0)
@@ -272,9 +273,10 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of
img_wrap = contextWrapper.base_color_texture
img_wrap.node_mapping.name = 'Diffuse Mapping'
img_wrap.node_image.name = 'Diffuse Texture'
image.alpha_mode = 'CHANNEL_PACKED'
links.new(mixer.outputs[0], shader.inputs['Base Color'])
links.new(img_wrap.node_image.outputs[0], mixer.inputs[2])
if luma == 'alpha':
image.alpha_mode = 'CHANNEL_PACKED'
if tint2 is not None:
mixer.inputs[2].default_value = tint2[:3] + [1]
elif mapto == 'ROUGHNESS':
@@ -285,10 +287,6 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of
elif mapto == 'SPECULARITY':
shader.location = (300, -600)
img_wrap = contextWrapper.specular_tint_texture
if tint1:
img_wrap.node_dst.inputs['Coat Tint'].default_value = tint1[:3] + [1]
if tint2:
img_wrap.node_dst.inputs['Sheen Tint'].default_value = tint2[:3] + [1]
elif mapto == 'ALPHA':
shader.location = (300, 300)
img_wrap = contextWrapper.alpha_texture
@@ -301,6 +299,7 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of
shader.location = (300, 300)
img_wrap = contextWrapper.normalmap_texture
img_wrap.node_mapping.name = 'Normal Mapping'
image.alpha_mode = 'CHANNEL_PACKED'
elif mapto == 'TRANSMISSION':
shader.location = (-600, 900)
img_wrap = contextWrapper.transmission_texture
@@ -357,6 +356,11 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of
img_wrap.scale = scale
img_wrap.translation = offset
img_wrap.rotation[2] = angle
if mapto in {'ROUGHNESS', 'METALLIC', 'SPECULARITY'}:
if tint1:
img_wrap.node_dst.inputs['Coat Tint'].default_value = tint1[:3] + [1]
if tint2:
img_wrap.node_dst.inputs['Sheen Tint'].default_value = tint2[:3] + [1]
if alpha == 'alpha':
own_node = img_wrap.node_image
primary_tex = nodes.get('Diffuse Texture')
@@ -581,8 +585,10 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
def read_texture(new_chunk, temp_chunk, mapto):
uscale, vscale, uoffset, voffset, angle = 1.0, 1.0, 0.0, 0.0, 0.0
contextWrapper.use_nodes = True
if hasattr(contextWrapper, "use_nodes"):
contextWrapper.use_nodes = True
tint1 = tint2 = None
luma = 'normal'
extend = 'wrap'
alpha = False
pct = 100
@@ -606,6 +612,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
elif temp_chunk.ID == MAT_MAP_FILEPATH:
texture_name, read_str_len = read_string(file)
img = load_image(texture_name, dirname, place_holder=False, recursive=IMAGE_SEARCH, check_existing=True)
if img and img.depth in {32, 16}:
luma = 'alpha'
temp_chunk.bytes_read += read_str_len # plus one for the null character that gets removed
elif temp_chunk.ID == MAT_BUMP_PERCENT:
@@ -633,11 +641,11 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
if tiling & 0x40:
alpha = 'alpha'
if tiling & 0x80:
tint = 'tint'
luma = 'tint'
if tiling & 0x100:
tint = 'noAlpha'
luma = 'noAlpha'
if tiling & 0x200:
tint = 'RGBtint'
luma = 'RGBtint'
elif temp_chunk.ID == MAT_MAP_USCALE:
uscale = read_float(temp_chunk)
@@ -660,7 +668,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
# add the map to the material in the right channel
if img:
add_texture_to_material(img, contextWrapper, pct, extend, alpha, (uscale, vscale, 1),
(uoffset, voffset, 0), angle, tint1, tint2, mapto)
(uoffset, voffset, 0), angle, luma, tint1, tint2, mapto)
def apply_constrain(vec):
convector = mathutils.Vector.Fill(3, (CONSTRAIN * 0.1))
@@ -767,7 +775,10 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
contextTransmission = False
contextColor = mathutils.Color((0.8, 0.8, 0.8))
contextMaterial = bpy.data.materials.new('Material')
contextWrapper = PrincipledBSDFWrapper(contextMaterial, is_readonly=False, use_nodes=False)
try:
contextWrapper = PrincipledBSDFWrapper(contextMaterial, is_readonly=False, use_nodes=False)
except:
contextWrapper = PrincipledBSDFWrapper(contextMaterial, is_readonly=False)
elif new_chunk.ID == MAT_NAME:
material_name, read_str_len = read_string(file)
@@ -884,7 +895,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
elif new_chunk.ID == MAT_SHADING:
shading = read_short(new_chunk)
if shading >= 2:
contextWrapper.use_nodes = True
use_nodes = hasattr(contextWrapper, "use_nodes")
if use_nodes:
contextWrapper.use_nodes = True
contextWrapper.base_color = contextColor[:]
contextWrapper.metallic = contextMaterial.metallic
contextWrapper.roughness = contextMaterial.roughness
@@ -895,8 +908,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
contextWrapper.emission_strength = contextMaterial.line_priority / 100
contextWrapper.alpha = contextMaterial.diffuse_color[3] = contextAlpha
contextWrapper.node_principled_bsdf.inputs['Coat Weight'].default_value = contextReflection
contextWrapper.use_nodes = False
if shading >= 3:
if use_nodes:
contextWrapper.use_nodes = False
if use_nodes and shading >= 3:
contextWrapper.use_nodes = True
elif new_chunk.ID == MAT_TEXTURE_MAP:
@@ -981,9 +995,18 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
realname, ext = os.path.splitext(filename)
contextWorld = bpy.data.worlds.new("Background: " + realname)
context.scene.world = contextWorld
contextWorld.use_nodes = True
if hasattr(contextWorld, "use_nodes"):
contextWorld.use_nodes = True
worldnodes = contextWorld.node_tree.nodes
backgroundnode = worldnodes['Background']
backgroundnode = worldnodes.get("Background")
if backgroundnode is None:
worldlinks = contextWorld.node_tree.links
outputnode = worldnodes.get("World Output")
if outputnode is None:
outputnode = worldnodes.new("ShaderNodeOutputWorld")
backgroundnode = worldnodes.new("ShaderNodeBackground")
worldlinks.new(backgroundnode.outputs[0], outputnode.inputs[0])
backgroundnode.location = (10, 300)
read_chunk(file, temp_chunk)
if temp_chunk.ID == COLOR_F:
backgroundcolor = read_float_array(temp_chunk)
@@ -1005,26 +1028,35 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
realname, ext = os.path.splitext(filename)
contextWorld = bpy.data.worlds.new("Bitmap: " + realname)
context.scene.world = contextWorld
contextWorld.use_nodes = True
if hasattr(contextWorld, "use_nodes"):
contextWorld.use_nodes = True
links = contextWorld.node_tree.links
nodes = contextWorld.node_tree.nodes
bitmap_mix = nodes.new(type='ShaderNodeMixRGB')
bitmapnode = nodes.new(type='ShaderNodeTexEnvironment')
bitmapping = nodes.new(type='ShaderNodeMapping')
backgroundnode = nodes.get("Background")
bitmap_mix = nodes.new(type="ShaderNodeMixRGB")
bitmapnode = nodes.new(type="ShaderNodeTexEnvironment")
bitmapping = nodes.new(type="ShaderNodeMapping")
if backgroundnode is None:
outputnode = nodes.get("World Output")
if outputnode is None:
outputnode = nodes.new("ShaderNodeOutputWorld")
backgroundnode = nodes.new("ShaderNodeBackground")
links.new(backgroundnode.outputs[0], outputnode.inputs[0])
bitmap_mix.label = "Background Mix"
bitmapnode.label = "Bitmap: " + bitmap_name
bitmap_mix.inputs[2].default_value = nodes['Background'].inputs[0].default_value
bitmap_mix.inputs[2].default_value = backgroundnode.inputs[0].default_value
bitmapnode.image = load_image(bitmap_name, dirname, place_holder=False, recursive=IMAGE_SEARCH, check_existing=True)
bitmap_mix.inputs[0].default_value = 0.5 if bitmapnode.image is not None else 1.0
coordinate = nodes.get("Texture Coordinate", nodes.new(type='ShaderNodeTexCoord'))
coordinate = nodes.get("Texture Coordinate", nodes.new(type="ShaderNodeTexCoord"))
bitmapnode.location = (-520, 400)
bitmap_mix.location = (-200, 360)
bitmapping.location = (-740, 400)
coordinate.location = (-1340, 400)
links.new(bitmap_mix.outputs[0], nodes['Background'].inputs[0])
backgroundnode.location = (10, 300)
links.new(bitmap_mix.outputs[0], backgroundnode.inputs[0])
links.new(bitmapnode.outputs[0], bitmap_mix.inputs[1])
links.new(bitmapping.outputs[0], bitmapnode.inputs[0])
if not bitmapping.inputs['Vector'].is_linked:
if not bitmapping.inputs["Vector"].is_linked:
links.new(coordinate.outputs[0], bitmapping.inputs[0])
new_chunk.bytes_read += read_str_len
@@ -1035,19 +1067,28 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
realname, ext = os.path.splitext(filename)
contextWorld = bpy.data.worlds.new("Gradient: " + realname)
context.scene.world = contextWorld
contextWorld.use_nodes = True
if hasattr(contextWorld, "use_nodes"):
contextWorld.use_nodes = True
links = contextWorld.node_tree.links
nodes = contextWorld.node_tree.nodes
gradientnode = nodes.new(type='ShaderNodeValToRGB')
layerweight = nodes.new(type='ShaderNodeLayerWeight')
conversion = nodes.new(type='ShaderNodeMath')
normalnode = nodes.new(type='ShaderNodeNormal')
backgroundnode = nodes.get("Background")
gradientnode = nodes.new(type="ShaderNodeValToRGB")
layerweight = nodes.new(type="ShaderNodeLayerWeight")
conversion = nodes.new(type="ShaderNodeMath")
normalnode = nodes.new(type="ShaderNodeNormal")
mappingnode = nodes.get("Mapping", False)
coordinates = nodes.get("Texture Coordinate", False)
backgroundmix = next((wn for wn in nodes if wn.type in {'MIX', 'MIX_RGB'}), False)
if backgroundnode is None:
outputnode = nodes.get("World Output")
if outputnode is None:
outputnode = nodes.new("ShaderNodeOutputWorld")
backgroundnode = nodes.new("ShaderNodeBackground")
links.new(backgroundnode.outputs[0], outputnode.inputs[0])
conversion.location = (-740, -60)
layerweight.location = (-940, 170)
normalnode.location = (-1140, 300)
backgroundnode.location = (10, 300)
gradientnode.location = (-520, -20)
gradientnode.label = "Gradient"
conversion.operation = 'MULTIPLY_ADD'
@@ -1059,14 +1100,14 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
links.new(normalnode.outputs[0], layerweight.inputs[1])
links.new(normalnode.outputs[1], layerweight.inputs[0])
if not coordinates:
coordinates = nodes.new(type='ShaderNodeTexCoord')
coordinates = nodes.new(type="ShaderNodeTexCoord")
coordinates.location = (-1340, 400)
links.new(coordinates.outputs[6], normalnode.inputs[0])
if backgroundmix:
links.new(gradientnode.outputs[0], backgroundmix.inputs[2])
else:
links.new(gradientnode.outputs[0], nodes['Background'].inputs[0])
if mappingnode and not mappingnode.inputs['Vector'].is_linked:
links.new(gradientnode.outputs[0], backgroundnode.inputs[0])
if mappingnode and not mappingnode.inputs["Vector"].is_linked:
links.new(coordinates.outputs[0], mappingnode.inputs[0])
gradientnode.color_ramp.elements.new(read_float(new_chunk))
read_chunk(file, temp_chunk)
@@ -1101,17 +1142,21 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
realname, ext = os.path.splitext(filename)
contextWorld = bpy.data.worlds.new("Fog: " + realname)
context.scene.world = contextWorld
contextWorld.use_nodes = True
if hasattr(contextWorld, "use_nodes"):
contextWorld.use_nodes = True
links = contextWorld.node_tree.links
nodes = contextWorld.node_tree.nodes
fognode = nodes.new(type='ShaderNodeVolumeAbsorption')
fognode = nodes.new(type="ShaderNodeVolumeAbsorption")
fognode.label = "Fog"
fognode.location = (10, 20)
volumemix = nodes.get("Volume", False)
if volumemix:
links.new(fognode.outputs[0], volumemix.inputs[1])
else:
links.new(fognode.outputs[0], nodes['World Output'].inputs[1])
outputnode = nodes.get("World Output")
if outputnode is None:
outputnode = nodes.new("ShaderNodeOutputWorld")
links.new(fognode.outputs[0], outputnode.inputs[1])
contextWorld.mist_settings.use_mist = True
contextWorld.mist_settings.start = read_float(new_chunk)
nearfog = read_float(new_chunk) * 0.01
@@ -1138,23 +1183,30 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
realname, ext = os.path.splitext(filename)
contextWorld = bpy.data.worlds.new("LayerFog: " + realname)
context.scene.world = contextWorld
contextWorld.use_nodes = True
if hasattr(contextWorld, "use_nodes"):
contextWorld.use_nodes = True
links = contextWorld.node_tree.links
nodes = contextWorld.node_tree.nodes
worldout = nodes.get("World Output")
background = nodes.get("Background")
if worldout is None:
worldout = nodes.new("ShaderNodeOutputWorld")
worldfog = worldout.inputs[1]
layerfog = nodes.new(type='ShaderNodeVolumeScatter')
litepath = nodes.get("Light Path", nodes.new('ShaderNodeLightPath'))
if background is None:
background = nodes.new("ShaderNodeBackground")
links.new(background.outputs[0], worldout.inputs[0])
layerfog = nodes.new(type="ShaderNodeVolumeScatter")
litepath = nodes.get("Light Path", nodes.new("ShaderNodeLightPath"))
fognode = nodes.get("Volume Absorption", False)
if fognode:
cuenode = nodes.get("Map Range", False)
mxvolume = nodes.new(type='ShaderNodeMixShader')
mxvolume = nodes.new(type="ShaderNodeMixShader")
mxvolume.label = mxvolume.name = "Volume"
mxvolume.location = (220, 0)
cuesource = cuenode.outputs[0] if cuenode else litepath.outputs[7]
cuesource = cuenode.outputs[0] if cuenode else litepath.outputs["Ray Length"]
cuetarget = cuenode.inputs[3] if cuenode else mxvolume.inputs[0]
links.new(litepath.outputs["Ray Length"], cuetarget)
links.new(fognode.outputs[0], mxvolume.inputs[1])
links.new(litepath.outputs[7], cuetarget)
links.new(cuesource, mxvolume.inputs[0])
links.new(mxvolume.outputs[0], worldfog)
worldfog = mxvolume.inputs[2]
@@ -1162,9 +1214,10 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
layerfog.location = (10, -120)
worldout.location = (440, 160)
litepath.location = (-200, 70)
background.location = (10, 300)
links.new(layerfog.outputs[0], worldfog)
links.new(litepath.outputs[8], layerfog.inputs[2])
links.new(litepath.outputs[0], nodes['Background'].inputs[1])
links.new(litepath.outputs["Ray Depth"], layerfog.inputs[2])
links.new(litepath.outputs[0], background.inputs[1])
contextWorld.mist_settings.use_mist = True
contextWorld.mist_settings.start = read_float(new_chunk)
contextWorld.mist_settings.height = read_float(new_chunk)
@@ -1193,22 +1246,31 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
realname, ext = os.path.splitext(filename)
contextWorld = bpy.data.worlds.new("DistanceCue: " + realname)
context.scene.world = contextWorld
contextWorld.use_nodes = True
if hasattr(contextWorld, "use_nodes"):
contextWorld.use_nodes = True
links = contextWorld.node_tree.links
nodes = contextWorld.node_tree.nodes
distcue_node = nodes.new(type='ShaderNodeMapRange')
camera_data = nodes.new(type='ShaderNodeCameraData')
background = nodes.get("Background")
distcue_node = nodes.new(type="ShaderNodeMapRange")
camera_data = nodes.new(type="ShaderNodeCameraData")
if background is None:
output = nodes.get("World Output")
if output is None:
output = nodes.new("ShaderNodeOutputWorld")
background = nodes.new("ShaderNodeBackground")
links.new(background.outputs[0], output.inputs[0])
distcue_node.label = "Distance Cue"
distcue_node.clamp = False
distcue_mix = nodes.get("Volume", False)
distcuepath = nodes.get("Light Path", False)
if not distcuepath:
distcuepath = nodes.new(type='ShaderNodeLightPath')
distcuepath = nodes.new(type="ShaderNodeLightPath")
background.location = (10, 300)
distcue_node.location = (-940, 10)
distcuepath.location = (-1140, 70)
camera_data.location = (-1340, 166)
raysource = distcuepath.outputs[7] if distcue_mix else distcuepath.outputs[0]
raytarget = distcue_mix.inputs[0] if distcue_mix else nodes['Background'].inputs[1]
raysource = distcuepath.outputs["Ray Length"] if distcue_mix else distcuepath.outputs[0]
raytarget = distcue_mix.inputs[0] if distcue_mix else background.inputs[1]
links.new(camera_data.outputs[1], distcue_node.inputs[1])
links.new(camera_data.outputs[2], distcue_node.inputs[0])
links.new(raysource, distcue_node.inputs[4])
@@ -1347,7 +1409,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
elif CreateLightObject and new_chunk.ID == LIGHT_SPOT_SHADOWED: # Shadow flag
contextLamp.data.use_shadow = True
elif CreateLightObject and new_chunk.ID == LIGHT_LOCAL_SHADOW2: # Shadow parameters
if contextLamp.data.get('shadow_buffer_bias') is not None:
if contextLamp.data.get("shadow_buffer_bias") is not None:
contextLamp.data.shadow_buffer_bias = read_float(new_chunk)
else:
read_float(new_chunk)
@@ -1364,25 +1426,28 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
contextLamp.data.use_nodes = True
nodes = contextLamp.data.node_tree.nodes
links = contextLamp.data.node_tree.links
mix = nodes.new(type='ShaderNodeMixRGB')
rgb = nodes.new(type='ShaderNodeRGB')
mix = nodes.new(type="ShaderNodeMixRGB")
rgb = nodes.new(type="ShaderNodeRGB")
mix.blend_type = 'LINEAR_LIGHT'
mix.label = "Emission Color"
emit = nodes.get("Emission")
emit.label = "Projector"
rgb.name = "RGB"
emit.location = (80, 300)
rgb.location = (-380, 60)
mix.location = (-140, 340)
gobo_name, read_str_len = read_string(file)
new_chunk.bytes_read += read_str_len
projection = nodes.new(type='ShaderNodeTexImage')
promapping = nodes.new(type='ShaderNodeMapping')
protxcoord = nodes.new(type='ShaderNodeTexCoord')
prolitpath = nodes.new(type='ShaderNodeLightPath')
prolitfall = nodes.new(type='ShaderNodeLightFalloff')
liteoutput = nodes.get("Light Output")
projection = nodes.new(type="ShaderNodeTexImage")
promapping = nodes.new(type="ShaderNodeMapping")
protxcoord = nodes.new(type="ShaderNodeTexCoord")
prolitpath = nodes.new(type="ShaderNodeLightPath")
prolitfall = nodes.new(type="ShaderNodeLightFalloff")
projection.label = "Gobo: " + gobo_name
protxcoord.label = "Gobo Coordinate"
promapping.vector_type = 'TEXTURE'
liteoutput.location = (300, 280)
prolitfall.location = (-720, 20)
projection.location = (-480, 440)
promapping.location = (-720, 440)
@@ -1390,11 +1455,11 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
prolitpath.location = (-940, 180)
projection.image = load_image(gobo_name, dirname, place_holder=False, recursive=IMAGE_SEARCH, check_existing=True)
emit.inputs[0].default_value[:3] = mix.inputs[2].default_value[:3] = rgb.outputs[0].default_value[:3] = contextLamp.data.color
links.new(emit.outputs[0], nodes['Light Output'].inputs[0])
links.new(prolitpath.outputs["Ray Length"], prolitfall.inputs[1])
links.new(prolitpath.outputs["Ray Depth"], prolitfall.inputs[0])
links.new(promapping.outputs[0], projection.inputs[0])
links.new(protxcoord.outputs[2], promapping.inputs[0])
links.new(prolitpath.outputs[8], prolitfall.inputs[0])
links.new(prolitpath.outputs[7], prolitfall.inputs[1])
links.new(emit.outputs[0], liteoutput.inputs[0])
links.new(prolitfall.outputs[1], emit.inputs[1])
links.new(prolitfall.outputs[0], mix.inputs[0])
links.new(projection.outputs[0], mix.inputs[1])
@@ -1487,18 +1552,20 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
if child is None:
if CreateWorld and tracking == 'AMBIENT':
child = context.scene.world
child.use_nodes = True
if hasattr(child, "use_nodes"):
child.use_nodes = True
nodetree = child.node_tree
links = nodetree.links
nodes = nodetree.nodes
backlite = nodes.get("Background")
worldout = nodes.get("World Output")
ambilite = nodes.new(type='ShaderNodeRGB')
raymixer = nodes.new(type='ShaderNodeMix')
mathnode = nodes.new(type='ShaderNodeMath')
ambinode = nodes.new(type='ShaderNodeEmission')
mixshade = nodes.new(type='ShaderNodeMixShader')
litefall = nodes.new(type='ShaderNodeLightFalloff')
ambilite = nodes.new(type="ShaderNodeRGB")
raymixer = nodes.new(type="ShaderNodeMix")
mathnode = nodes.new(type="ShaderNodeMath")
ambinode = nodes.new(type="ShaderNodeEmission")
mixshade = nodes.new(type="ShaderNodeMixShader")
litefall = nodes.new(type="ShaderNodeLightFalloff")
ambilite.name = "Ambient"
raymixer.label = "Ambient Mix"
ambilite.label = "Ambient Color"
raymixer.inputs[3].name = "Ambient"
@@ -1506,8 +1573,13 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
mixshade.label = mixshade.name = "Surface"
litepath = nodes.get("Light Path", False)
ambinode.inputs[0].default_value[:3] = child.color
if not litepath:
litepath = nodes.new('ShaderNodeLightPath')
if litepath is None:
litepath = nodes.new("ShaderNodeLightPath")
if worldout is None:
worldout = nodes.new(type="ShaderNodeOutputWorld")
if backlite is None:
backlite = nodes.new(type="ShaderNodeBackground")
backlite.location = (10, 300)
ambinode.location = (10, 160)
worldout.location = (440, 160)
mixshade.location = (220, 280)
@@ -1577,7 +1649,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
for keydata in keyframe_data.items():
child.color = ambilite.outputs[0].default_value[:3] = keydata[1]
child.keyframe_insert(data_path="color", frame=keydata[0])
nodetree.keyframe_insert(data_path="nodes[\"RGB\"].outputs[0].default_value", frame=keydata[0])
nodetree.keyframe_insert(data_path="nodes[\"Ambient\"].outputs[0].default_value", frame=keydata[0])
contextTrack_flag = False
elif KEYFRAME and new_chunk.ID == COL_TRACK_TAG and tracking == 'LIGHT': # Color
@@ -1591,16 +1663,17 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
colornode = tree.nodes.get("RGB", False)
lightfall = tree.nodes.get("Light Falloff", False)
if not colornode:
colornode = tree.nodes.new('ShaderNodeRGB')
colornode = tree.nodes.new("ShaderNodeRGB")
colornode.name = "RGB"
colornode.location = (-380, 60)
tree.links.new(colornode.outputs[0], emitnode.inputs[0])
if not lightfall:
lightfall = tree.nodes.new('ShaderNodeLightFalloff')
lightpath = tree.nodes.new('ShaderNodeLightPath')
lightfall = tree.nodes.new("ShaderNodeLightFalloff")
lightpath = tree.nodes.new("ShaderNodeLightPath")
lightfall.location = (-720, 20)
lightpath.location = (-940, 180)
tree.links.new(lightpath.outputs[8], lightfall.inputs[0])
tree.links.new(lightpath.outputs[7], lightfall.inputs[1])
tree.links.new(lightpath.outputs["Ray Depth"], lightfall.inputs[0])
tree.links.new(lightpath.outputs["Ray Length"], lightfall.inputs[1])
tree.links.new(lightfall.outputs[1], emitnode.inputs[1])
colornode.outputs[0].default_value[:3] = child.data.color
for keydata in keyframe_data.items():
@@ -1677,7 +1750,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
for i in range(nkeys):
nframe = read_long(new_chunk)
nflags = read_short(new_chunk)
for f in range(bin(nflags)[-5:].count('1')):
for f in range(bin(nflags)[-5:].count("1")):
temp_data = file.read(SZ_FLOAT) # Check for spline term values
new_chunk.bytes_read += SZ_FLOAT
temp_data = file.read(SZ_4FLOAT)
@@ -1757,7 +1830,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
else:
buffer_size = new_chunk.length - new_chunk.bytes_read
binary_format = '%ic' % buffer_size
binary_format = "%ic" % buffer_size
temp_data = file.read(struct.calcsize(binary_format))
new_chunk.bytes_read += buffer_size
@@ -1788,7 +1861,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
if ob.name in parent_dictionary.keys():
kids = parent_dictionary.get(ob.name)
for kid in kids:
kid.parent = ob
if kid is not None:
kid.parent = ob
elif not found:
if parent == ROOT_OBJECT:
ob.parent = None
@@ -1809,7 +1883,10 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
trans_matrix = matrix_dictionary.get(ob.name, mathutils.Matrix())
pivot_matrix = mathutils.Matrix.Translation(trans_matrix.to_3x3() @ -pivot)
if APPLY_MATRIX and ob.type == 'MESH':
ob.data.transform(trans_matrix.inverted() @ pivot_matrix)
try:
ob.data.transform(trans_matrix.inverted() @ pivot_matrix)
except:
pass
##########