2025-12-01
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
##########
|
||||
|
||||
Reference in New Issue
Block a user