merge branch 'blender-v3.6-release' #104625

Closed
Sebastian Sille wants to merge 22 commits from (deleted):blender-v3.6-release into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
Showing only changes of commit 8c5684587a - Show all commits

View File

@ -260,15 +260,15 @@ class _3ds_point_3d(object):
# Used for writing a track
'''
class _3ds_point_4d(object):
"""Class representing a four-dimensional point for a 3ds file, for instance a quaternion."""
__slots__ = "w", "x", "y", "z"
def __init__(self, point):
__slots__ = "w","x","y","z"
def __init__(self, point=(0.0,0.0,0.0,0.0)):
self.w, self.x, self.y, self.z = point
def get_size(self):
return 4 * SZ_FLOAT
return 4*SZ_FLOAT
def write(self,file):
data=struct.pack('<4f', self.w, self.x, self.y, self.z)
@ -276,6 +276,7 @@ class _3ds_point_4d(object):
def __str__(self):
return '(%f, %f, %f, %f)' % (self.w, self.x, self.y, self.z)
'''
class _3ds_point_uv(object):
@ -1049,18 +1050,17 @@ def make_mesh_chunk(ob, mesh, matrix, materialDict, translation):
return mesh_chunk
#################
# KEYFRAME DATA #
#################
def make_kfdata(revision, start=0, stop=100, curtime=0):
''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
def make_kfdata(start=0, stop=0, curtime=0):
"""Make the basic keyframe data chunk"""
kfdata = _3ds_chunk(KFDATA)
kfhdr = _3ds_chunk(KFDATA_KFHDR)
kfhdr.add_variable("revision", _3ds_ushort(revision))
kfhdr.add_variable("filename", _3ds_string(b'Blender'))
kfhdr.add_variable("animlen", _3ds_uint(stop - start))
kfhdr.add_variable("revision", _3ds_ushort(0))
# Not really sure what filename is used for, but it seems it is usually used
# to identify the program that generated the .3ds:
kfhdr.add_variable("filename", _3ds_string("Blender"))
kfhdr.add_variable("animlen", _3ds_uint(stop-start))
kfseg = _3ds_chunk(KFDATA_KFSEG)
kfseg.add_variable("start", _3ds_uint(start))
@ -1074,387 +1074,102 @@ def make_kfdata(revision, start=0, stop=100, curtime=0):
kfdata.add_subchunk(kfcurtime)
return kfdata
def make_track_chunk(ID, ob, ob_pos, ob_rot, ob_size):
"""Make a chunk for track data. Depending on the ID, this will
construct a position, rotation, scale, roll, color or fov track."""
def make_track_chunk(ID, obj):
"""Make a chunk for track data.
Depending on the ID, this will construct a position, rotation or scale 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:
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 = 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):
position = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'location']
if not position:
position.append(ob_pos)
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(position))
elif ID == ROT_TRACK_TAG: # Rotation
for i, frame in enumerate(kframes):
rotation = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
if not rotation:
rotation.append(ob_rot)
quat = mathutils.Euler(rotation).to_quaternion()
axis_angle = quat.angle, quat.axis[0], quat.axis[1], quat.axis[2]
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(axis_angle))
elif ID == SCL_TRACK_TAG: # Scale
for i, frame in enumerate(kframes):
size = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'scale']
if not size:
size.append(ob_size)
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))
elif ID == ROLL_TRACK_TAG: # Roll
for i, frame in enumerate(kframes):
roll = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
if not roll:
roll.append(ob_rot)
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[1]), 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:
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 = 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.append(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 = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'lens']
if not lens:
lens.append(ob.data.lens)
fov = 2 * math.atan(ob.data.sensor_width/(2*lens[0]))
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
beam_angle = math.degrees(ob.data.spot_size)
for i, frame in enumerate(kframes):
blend = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_blend']
if not blend:
blend.append(ob.data.spot_blend)
hot_spot = beam_angle-(blend[0]*math.floor(beam_angle))
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 = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'spot_size']
if not fall_off:
fall_off.append(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[0]), 4)))
track_chunk.add_variable("track_flags", _3ds_ushort())
track_chunk.add_variable("unknown", _3ds_uint())
track_chunk.add_variable("unknown", _3ds_uint())
track_chunk.add_variable("nkeys", _3ds_uint(1))
# Next section should be repeated for every keyframe, but for now, animation is not actually supported.
track_chunk.add_variable("tcb_frame", _3ds_uint(0))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
if obj.type=='Empty':
if ID==POS_TRACK_TAG:
# position vector:
track_chunk.add_variable("position", _3ds_point_3d(obj.getLocation()))
elif ID==ROT_TRACK_TAG:
# rotation (quaternion, angle first, followed by axis):
q = obj.getEuler().to_quaternion() # XXX, todo!
track_chunk.add_variable("rotation", _3ds_point_4d((q.angle, q.axis[0], q.axis[1], q.axis[2])))
elif ID==SCL_TRACK_TAG:
# scale vector:
track_chunk.add_variable("scale", _3ds_point_3d(obj.getSize()))
else:
track_chunk.add_variable("track_flags", _3ds_ushort(0x40)) # Based on observation default flag is 0x40
track_chunk.add_variable("frame_start", _3ds_uint(0))
track_chunk.add_variable("frame_total", _3ds_uint(0))
track_chunk.add_variable("nkeys", _3ds_uint(1))
# Next section should be repeated for every keyframe, with no animation only one tag is needed
track_chunk.add_variable("tcb_frame", _3ds_uint(0))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
# New method simply inserts the parameters
if ID == POS_TRACK_TAG: # Position vector
track_chunk.add_variable("position", _3ds_point_3d(ob_pos))
elif ID == ROT_TRACK_TAG: # Rotation (angle first [radians], followed by axis)
track_chunk.add_variable("rotation", _3ds_point_4d((ob_rot.angle, ob_rot.axis[0], ob_rot.axis[1], ob_rot.axis[2])))
elif ID == SCL_TRACK_TAG: # Scale vector
track_chunk.add_variable("scale", _3ds_point_3d(ob_size))
elif ID == ROLL_TRACK_TAG: # Roll angle
track_chunk.add_variable("roll", _3ds_float(round(math.degrees(ob.rotation_euler[1]), 4)))
elif ID == COL_TRACK_TAG: # Color values
track_chunk.add_variable("color", _3ds_float_color(ob.data.color))
elif ID == FOV_TRACK_TAG: # Field of view
track_chunk.add_variable("fov", _3ds_float(round(math.degrees(ob.data.angle), 4)))
elif ID == HOTSPOT_TRACK_TAG: # Hotspot
beam_angle = math.degrees(ob.data.spot_size)
track_chunk.add_variable("hotspot", _3ds_float(round(beam_angle-(ob.data.spot_blend*math.floor(beam_angle)), 4)))
elif ID == FALLOFF_TRACK_TAG: # Falloff
track_chunk.add_variable("falloff", _3ds_float(round(math.degrees(ob.data.spot_size), 4)))
# meshes have their transformations applied before
# exporting, so write identity transforms here:
if ID==POS_TRACK_TAG:
# position vector:
track_chunk.add_variable("position", _3ds_point_3d((0.0,0.0,0.0)))
elif ID==ROT_TRACK_TAG:
# rotation (quaternion, angle first, followed by axis):
track_chunk.add_variable("rotation", _3ds_point_4d((0.0, 1.0, 0.0, 0.0)))
elif ID==SCL_TRACK_TAG:
# scale vector:
track_chunk.add_variable("scale", _3ds_point_3d((1.0, 1.0, 1.0)))
return track_chunk
def make_kf_obj_node(obj, name_to_id):
"""Make a node chunk for a Blender object.
Takes the Blender object as a parameter. Object id's are taken from the dictionary name_to_id.
Blender Empty objects are converted to dummy nodes."""
def make_object_node(ob, translation, rotation, scale):
"""Make a node chunk for a Blender object. Takes Blender object as parameter.
Blender Empty objects are converted to dummy nodes."""
name = obj.name
# main object node chunk:
kf_obj_node = _3ds_chunk(OBJECT_NODE_TAG)
# chunk for the object id:
obj_id_chunk = _3ds_chunk(OBJECT_NODE_ID)
# object id is from the name_to_id dictionary:
obj_id_chunk.add_variable("node_id", _3ds_ushort(name_to_id[name]))
name = ob.name
if ob.type == 'CAMERA':
obj_node = _3ds_chunk(CAMERA_NODE_TAG)
elif ob.type == 'LIGHT':
obj_node = _3ds_chunk(LIGHT_NODE_TAG)
if ob.data.type == 'SPOT':
obj_node = _3ds_chunk(SPOT_NODE_TAG)
else: # Main object node chunk
obj_node = _3ds_chunk(OBJECT_NODE_TAG)
# Object node header with object name
# object node header:
obj_node_header_chunk = _3ds_chunk(OBJECT_NODE_HDR)
parent = ob.parent
if ob.type == 'EMPTY': # Forcing to use the real name for empties
# Empties called $$$DUMMY and use OBJECT_INSTANCE_NAME chunk as name
obj_node_header_chunk.add_variable("name", _3ds_string(b"$$$DUMMY"))
obj_node_header_chunk.add_variable("flags1", _3ds_ushort(0x4000))
obj_node_header_chunk.add_variable("flags2", _3ds_ushort(0))
else: # Add flag variables - Based on observation flags1 is usually 0x0040 and 0x4000 for empty objects
# object name:
if obj.type == 'Empty':
# Empties are called "$$$DUMMY" and use the OBJECT_INSTANCE_NAME chunk
# for their name (see below):
obj_node_header_chunk.add_variable("name", _3ds_string("$$$DUMMY"))
else:
# Add the name:
obj_node_header_chunk.add_variable("name", _3ds_string(sane_name(name)))
obj_node_header_chunk.add_variable("flags1", _3ds_ushort(0x0040))
# Add Flag variables (not sure what they do):
obj_node_header_chunk.add_variable("flags1", _3ds_ushort(0))
obj_node_header_chunk.add_variable("flags2", _3ds_ushort(0))
# Flags2 defines bit 0x01 for display path, bit 0x02 use autosmooth, bit 0x04 object frozen,
# bit 0x10 for motion blur, bit 0x20 for material morph and bit 0x40 for mesh morph
if ob.type == 'MESH' and ob.data.use_auto_smooth:
obj_node_header_chunk.add_variable("flags2", _3ds_ushort(0x02))
else:
obj_node_header_chunk.add_variable("flags2", _3ds_ushort(0))
obj_node_header_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT))
'''
# COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
# Check parent-child relationships:
if parent is None or parent.name not in name_to_id:
# If no parent, or parents name is not in dictionary, ID becomes -1:
parent = obj.parent
if (parent is None) or (parent.name not in name_to_id):
# If no parent, or the parents name is not in the name_to_id dictionary,
# parent id becomes -1:
obj_node_header_chunk.add_variable("parent", _3ds_ushort(-1))
else: # Get the parent's ID from the name_to_id dictionary:
else:
# Get the parent's id from the name_to_id dictionary:
obj_node_header_chunk.add_variable("parent", _3ds_ushort(name_to_id[parent.name]))
'''
# Add subchunk for node header
obj_node.add_subchunk(obj_node_header_chunk)
# Add pivot chunk:
obj_pivot_chunk = _3ds_chunk(OBJECT_PIVOT)
obj_pivot_chunk.add_variable("pivot", _3ds_point_3d(obj.getLocation()))
kf_obj_node.add_subchunk(obj_pivot_chunk)
# Empty objects need to have an extra chunk for the instance name
if ob.type == 'EMPTY': # Will use a real object name for empties for now
# add subchunks for object id and node header:
kf_obj_node.add_subchunk(obj_id_chunk)
kf_obj_node.add_subchunk(obj_node_header_chunk)
# Empty objects need to have an extra chunk for the instance name:
if obj.type == 'Empty':
obj_instance_name_chunk = _3ds_chunk(OBJECT_INSTANCE_NAME)
obj_instance_name_chunk.add_variable("name", _3ds_string(sane_name(name)))
obj_node.add_subchunk(obj_instance_name_chunk)
kf_obj_node.add_subchunk(obj_instance_name_chunk)
if ob.type in {'MESH', 'EMPTY'}: # Add a pivot point at the object center
pivot_pos = (translation[name])
obj_pivot_chunk = _3ds_chunk(OBJECT_PIVOT)
obj_pivot_chunk.add_variable("pivot", _3ds_point_3d(pivot_pos))
obj_node.add_subchunk(obj_pivot_chunk)
# Add track chunks for position, rotation and scale:
kf_obj_node.add_subchunk(make_track_chunk(POS_TRACK_TAG, obj))
kf_obj_node.add_subchunk(make_track_chunk(ROT_TRACK_TAG, obj))
kf_obj_node.add_subchunk(make_track_chunk(SCL_TRACK_TAG, obj))
# Create a bounding box from quadrant diagonal
obj_boundbox = _3ds_chunk(OBJECT_BOUNDBOX)
obj_boundbox.add_variable("min", _3ds_point_3d(ob.bound_box[0]))
obj_boundbox.add_variable("max", _3ds_point_3d(ob.bound_box[6]))
obj_node.add_subchunk(obj_boundbox)
# Add smooth angle if autosmooth is used
if ob.type == 'MESH' and ob.data.use_auto_smooth:
obj_morph_smooth = _3ds_chunk(OBJECT_MORPH_SMOOTH)
obj_morph_smooth.add_variable("angle", _3ds_float(round(ob.data.auto_smooth_angle, 6)))
obj_node.add_subchunk(obj_morph_smooth)
# Add track chunks for color, position, rotation and scale
if parent is None:
ob_pos = translation[name]
ob_rot = rotation[name]
ob_size = scale[name]
else: # Calculate child position and rotation of the object center, no scale applied
ob_pos = translation[name] - translation[parent.name]
ob_rot = rotation[name].cross(rotation[parent.name].copy().inverted())
ob_size = (1.0, 1.0, 1.0)
obj_node.add_subchunk(make_track_chunk(POS_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
if ob.type in {'MESH', 'EMPTY'}:
obj_node.add_subchunk(make_track_chunk(ROT_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
obj_node.add_subchunk(make_track_chunk(SCL_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
if ob.type =='CAMERA':
obj_node.add_subchunk(make_track_chunk(FOV_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
obj_node.add_subchunk(make_track_chunk(ROLL_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
if ob.type =='LIGHT':
obj_node.add_subchunk(make_track_chunk(COL_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
if ob.type == 'LIGHT' and ob.data.type == 'SPOT':
obj_node.add_subchunk(make_track_chunk(HOTSPOT_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
obj_node.add_subchunk(make_track_chunk(FALLOFF_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
obj_node.add_subchunk(make_track_chunk(ROLL_TRACK_TAG, ob, ob_pos, ob_rot, ob_size))
return obj_node
def make_target_node(ob, translation, rotation, scale):
"""Make a target chunk for light and camera objects"""
name = ob.name
if ob.type == 'CAMERA': #Add camera target
tar_node = _3ds_chunk(TARGET_NODE_TAG)
elif ob.type == 'LIGHT': # Add spot target
tar_node = _3ds_chunk(LTARGET_NODE_TAG)
# Object node header with object name
tar_node_header_chunk = _3ds_chunk(OBJECT_NODE_HDR)
# Targets get the same name as the object, flags1 is usually 0x0010 and parent ROOT_OBJECT
tar_node_header_chunk.add_variable("name", _3ds_string(sane_name(name)))
tar_node_header_chunk.add_variable("flags1", _3ds_ushort(0x0010))
tar_node_header_chunk.add_variable("flags2", _3ds_ushort(0))
tar_node_header_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT))
# Add subchunk for node header
tar_node.add_subchunk(tar_node_header_chunk)
# Calculate target position
ob_pos = translation[name]
ob_rot = rotation[name].to_euler()
ob_size = scale[name]
diagonal = math.copysign(math.sqrt(pow(ob_pos[0],2)+pow(ob_pos[1],2)), ob_pos[1])
target_x = ob_pos[0]+(ob_pos[1]*math.tan(ob_rot[2]))
target_y = ob_pos[1]+(ob_pos[0]*math.tan(math.radians(90)-ob_rot[2]))
target_z = -1*diagonal*math.tan(math.radians(90)-ob_rot[0])
# Add track chunks for target position
track_chunk = _3ds_chunk(POS_TRACK_TAG)
if ob.animation_data and ob.animation_data.action:
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 = 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):
target_pos = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'location']
target_rot = [fc.evaluate(frame) for fc in fcurves if fc is not None and fc.data_path == 'rotation_euler']
if not target_pos:
target_pos.append(ob_pos)
if not target_rot:
target_rot.insert(0, ob_rot.x)
target_rot.insert(1, ob_rot.y)
target_rot.insert(2, ob_rot.z)
diagonal = math.copysign(math.sqrt(pow(target_pos[0],2)+pow(target_pos[1],2)), target_pos[1])
target_x = target_pos[0]+(target_pos[1]*math.tan(target_rot[2]))
target_y = target_pos[1]+(target_pos[0]*math.tan(math.radians(90)-target_rot[2]))
target_z = -1*diagonal*math.tan(math.radians(90)-target_rot[0])
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_x, target_y, target_z)))
else: # Track header
track_chunk.add_variable("track_flags", _3ds_ushort(0x40)) # Based on observation default flag is 0x40
track_chunk.add_variable("frame_start", _3ds_uint(0))
track_chunk.add_variable("frame_total", _3ds_uint(0))
track_chunk.add_variable("nkeys", _3ds_uint(1))
# Keyframe header
track_chunk.add_variable("tcb_frame", _3ds_uint(0))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("position", _3ds_point_3d((target_x, target_y, target_z)))
tar_node.add_subchunk(track_chunk)
return tar_node
def make_ambient_node(world):
amb_color = world.color
amb_node = _3ds_chunk(AMBIENT_NODE_TAG)
track_chunk = _3ds_chunk(COL_TRACK_TAG)
# Object node header, name is "$AMBIENT$" for ambient nodes
amb_node_header_chunk = _3ds_chunk(OBJECT_NODE_HDR)
amb_node_header_chunk.add_variable("name", _3ds_string(b"$AMBIENT$"))
amb_node_header_chunk.add_variable("flags1", _3ds_ushort(0x4000)) # Flags1 0x4000 for empty objects
amb_node_header_chunk.add_variable("flags2", _3ds_ushort(0))
amb_node_header_chunk.add_variable("parent", _3ds_ushort(ROOT_OBJECT))
amb_node.add_subchunk(amb_node_header_chunk)
if world.animation_data.action:
action = world.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 = 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.append(world.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))
track_chunk.add_variable("frame_start", _3ds_uint(0))
track_chunk.add_variable("frame_total", _3ds_uint(0))
track_chunk.add_variable("nkeys", _3ds_uint(1))
# Keyframe header
track_chunk.add_variable("tcb_frame", _3ds_uint(0))
track_chunk.add_variable("tcb_flags", _3ds_ushort())
track_chunk.add_variable("color", _3ds_float_color(amb_color))
amb_node.add_subchunk(track_chunk)
return amb_node
return kf_obj_node
'''
##########
@ -1475,7 +1190,6 @@ def save(operator,
scene = context.scene
layer = context.view_layer
depsgraph = context.evaluated_depsgraph_get()
world = scene.world
if global_matrix is None:
global_matrix = mathutils.Matrix()
@ -1502,23 +1216,18 @@ def save(operator,
mscale.add_variable("scale", _3ds_float(1))
object_info.add_subchunk(mscale)
# Init main keyframe data chunk
if write_keyframe:
revision = 0x0005
stop = scene.frame_end
start = scene.frame_start
curtime = scene.frame_current
kfdata = make_kfdata(revision, start, stop, curtime)
# Add AMBIENT color
if world is not None:
if scene.world is not None:
ambient_chunk = _3ds_chunk(AMBIENTLIGHT)
ambient_light = _3ds_chunk(RGB)
ambient_light.add_variable("ambient", _3ds_float_color(scene.world.color))
ambient_chunk.add_subchunk(ambient_light)
object_info.add_subchunk(ambient_chunk)
if write_keyframe and world.animation_data:
kfdata.add_subchunk(make_ambient_node(world))
''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
# init main key frame data chunk:
kfdata = make_kfdata()
'''
# Make a list of all materials used in the selected meshes (use dictionary, each material is added once)
materialDict = {}
@ -1593,8 +1302,6 @@ def save(operator,
# Collect translation for transformation matrix
translation = {}
rotation = {}
scale = {}
# Give all objects a unique ID and build a dictionary from object name to object id
# name_to_id = {}
@ -1630,25 +1337,24 @@ def save(operator,
else:
operator.report({'WARNING'}, "Object %r can't be written into a 3DS file")
# Export kf object node
if write_keyframe:
kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale))
''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
# make a kf object node for the object:
kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id))
'''
i += i
# Create chunks for all empties, only requires a kf object node
if write_keyframe:
for ob in empty_objects:
kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale))
# Create chunks for all empties:
''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
for ob in empty_objects:
# Empties only require a kf object node:
kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id))
pass
'''
# Create light object chunks
for ob in light_objects:
object_chunk = _3ds_chunk(OBJECT)
translation[ob.name] = ob.location
rotation[ob.name] = ob.rotation_euler.to_quaternion()
scale[ob.name] = ob.scale
# Add light data subchunks
light_chunk = _3ds_chunk(OBJECT_LIGHT)
color_float_chunk = _3ds_chunk(RGB)
energy_factor = _3ds_chunk(LIGHT_MULTIPLIER)
@ -1685,20 +1391,9 @@ def save(operator,
object_chunk.add_subchunk(light_chunk)
object_info.add_subchunk(object_chunk)
# Export light and spotlight target node
if write_keyframe:
kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale))
if ob.data.type == 'SPOT':
kfdata.add_subchunk(make_target_node(ob, translation, rotation, scale))
# Create camera object chunks
for ob in camera_objects:
object_chunk = _3ds_chunk(OBJECT)
translation[ob.name] = ob.location
rotation[ob.name] = ob.rotation_euler.to_quaternion()
scale[ob.name] = ob.scale
# Add camera data subchunks
camera_chunk = _3ds_chunk(OBJECT_CAMERA)
diagonal = math.copysign(math.sqrt(pow(ob.location[0], 2) + pow(ob.location[1], 2)), ob.location[1])
focus_x = ob.location[0] + (ob.location[1] * math.tan(ob.rotation_euler[2]))
@ -1712,17 +1407,13 @@ def save(operator,
object_chunk.add_subchunk(camera_chunk)
object_info.add_subchunk(object_chunk)
# Export camera and target node
if write_keyframe:
kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale))
kfdata.add_subchunk(make_target_node(ob, translation, rotation, scale))
# Add main object info chunk to primary chunk
# Add main object info chunk to primary chunk:
primary.add_subchunk(object_info)
# Add main keyframe data chunk to primary chunk
if write_keyframe:
primary.add_subchunk(kfdata)
''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
# Add main keyframe data chunk to primary chunk:
primary.add_subchunk(kfdata)
'''
# At this point, the chunk hierarchy is completely built
# Check the size