io_scene_3ds: Added unit measure convert option #104768
@ -55,6 +55,11 @@ class Import3DS(bpy.types.Operator, ImportHelper):
|
||||
soft_min=0.0, soft_max=1000.0,
|
||||
default=10.0,
|
||||
)
|
||||
convert_measure: BoolProperty(
|
||||
name="Convert Measure",
|
||||
description="Convert from millimeters to meters",
|
||||
default=False,
|
||||
)
|
||||
use_image_search: BoolProperty(
|
||||
name="Image Search",
|
||||
description="Search subdirectories for any associated images "
|
||||
@ -106,6 +111,13 @@ class Export3DS(bpy.types.Operator, ExportHelper):
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
scale_factor: FloatProperty(
|
||||
name="Scale",
|
||||
description="Scale factor for all objects",
|
||||
min=0.0, max=100000.0,
|
||||
soft_min=0.0, soft_max=100000.0,
|
||||
default=1.0,
|
||||
)
|
||||
use_selection: BoolProperty(
|
||||
name="Selection Only",
|
||||
description="Export selected objects only",
|
||||
|
@ -1086,6 +1086,7 @@ def make_kfdata(revision, start=0, stop=100, curtime=0):
|
||||
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, fov, hotspot or falloff track."""
|
||||
ob_distance = mathutils.Matrix.Diagonal(ob_size)
|
||||
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:
|
||||
@ -1110,9 +1111,10 @@ def make_track_chunk(ID, ob, ob_pos, ob_rot, ob_size):
|
||||
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_distance @ 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)))
|
||||
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):
|
||||
@ -1373,8 +1375,7 @@ def make_target_node(ob, translation, rotation, scale, name_id):
|
||||
# Calculate target position
|
||||
ob_pos = translation[name]
|
||||
ob_rot = rotation[name]
|
||||
ob_size = scale[name]
|
||||
|
||||
ob_size = mathutils.Matrix.Diagonal(scale[name])
|
||||
target_pos = calc_target(ob_pos, ob_rot.x, ob_rot.z)
|
||||
|
||||
# Add track chunks for target position
|
||||
@ -1404,7 +1405,8 @@ def make_target_node(ob, translation, rotation, scale, name_id):
|
||||
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_pos = calc_target(mathutils.Vector((loc_x, loc_y, loc_z)), rot_x, rot_z)
|
||||
target_distance = ob_size @ 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))
|
||||
@ -1487,9 +1489,11 @@ def make_ambient_node(world):
|
||||
# EXPORT #
|
||||
##########
|
||||
|
||||
def save(operator, context, filepath="", use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None):
|
||||
def save(operator, context, filepath="", scale_factor=1.0, use_selection=False, use_hierarchy=False, write_keyframe=False, global_matrix=None):
|
||||
|
||||
"""Save the Blender scene to a 3ds file."""
|
||||
mtx_scale = mathutils.Matrix.Scale(scale_factor, 4)
|
||||
|
||||
# Time the export
|
||||
duration = time.time()
|
||||
context.window.cursor_set('WAIT')
|
||||
@ -1575,6 +1579,7 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals
|
||||
if data:
|
||||
matrix = global_matrix @ mtx
|
||||
data.transform(matrix)
|
||||
data.transform(mtx_scale)
|
||||
mesh_objects.append((ob_derived, data, matrix))
|
||||
ma_ls = data.materials
|
||||
ma_ls_len = len(ma_ls)
|
||||
@ -1623,29 +1628,29 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals
|
||||
name_id = {}
|
||||
|
||||
for ob, data, matrix in mesh_objects:
|
||||
translation[ob.name] = ob.location
|
||||
translation[ob.name] = mtx_scale @ ob.location.copy()
|
||||
rotation[ob.name] = ob.rotation_euler
|
||||
scale[ob.name] = ob.scale
|
||||
name_id[ob.name] = len(name_id)
|
||||
object_id[ob.name] = len(object_id)
|
||||
|
||||
for ob in empty_objects:
|
||||
translation[ob.name] = ob.location
|
||||
translation[ob.name] = mtx_scale @ ob.location.copy()
|
||||
rotation[ob.name] = ob.rotation_euler
|
||||
scale[ob.name] = ob.scale
|
||||
name_id[ob.name] = len(name_id)
|
||||
|
||||
for ob in light_objects:
|
||||
translation[ob.name] = ob.location
|
||||
translation[ob.name] = mtx_scale @ ob.location.copy()
|
||||
rotation[ob.name] = ob.rotation_euler
|
||||
scale[ob.name] = ob.scale
|
||||
scale[ob.name] = mtx_scale.to_scale()
|
||||
name_id[ob.name] = len(name_id)
|
||||
object_id[ob.name] = len(object_id)
|
||||
|
||||
for ob in camera_objects:
|
||||
translation[ob.name] = ob.location
|
||||
translation[ob.name] = mtx_scale @ ob.location.copy()
|
||||
rotation[ob.name] = ob.rotation_euler
|
||||
scale[ob.name] = ob.scale
|
||||
scale[ob.name] = mtx_scale.to_scale()
|
||||
name_id[ob.name] = len(name_id)
|
||||
object_id[ob.name] = len(object_id)
|
||||
|
||||
@ -1695,9 +1700,10 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals
|
||||
object_chunk = _3ds_chunk(OBJECT)
|
||||
obj_light_chunk = _3ds_chunk(OBJECT_LIGHT)
|
||||
color_float_chunk = _3ds_chunk(RGB)
|
||||
light_distance = mtx_scale @ ob.location.copy()
|
||||
light_energy_factor = _3ds_chunk(LIGHT_MULTIPLIER)
|
||||
object_chunk.add_variable("light", _3ds_string(sane_name(ob.name)))
|
||||
obj_light_chunk.add_variable("location", _3ds_point_3d(ob.location))
|
||||
obj_light_chunk.add_variable("location", _3ds_point_3d(light_distance))
|
||||
color_float_chunk.add_variable("color", _3ds_float_color(ob.data.color))
|
||||
light_energy_factor.add_variable("energy", _3ds_float(ob.data.energy * 0.001))
|
||||
obj_light_chunk.add_subchunk(color_float_chunk)
|
||||
@ -1706,7 +1712,7 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals
|
||||
if ob.data.type == 'SPOT':
|
||||
cone_angle = math.degrees(ob.data.spot_size)
|
||||
hot_spot = cone_angle - (ob.data.spot_blend * math.floor(cone_angle))
|
||||
spot_pos = calc_target(ob.location, ob.rotation_euler.x, ob.rotation_euler.z)
|
||||
spot_pos = calc_target(light_distance, ob.rotation_euler.x, ob.rotation_euler.z)
|
||||
spotlight_chunk = _3ds_chunk(LIGHT_SPOTLIGHT)
|
||||
spot_roll_chunk = _3ds_chunk(LIGHT_SPOT_ROLL)
|
||||
spotlight_chunk.add_variable("target", _3ds_point_3d(spot_pos))
|
||||
@ -1758,9 +1764,10 @@ def save(operator, context, filepath="", use_selection=False, use_hierarchy=Fals
|
||||
for ob in camera_objects:
|
||||
object_chunk = _3ds_chunk(OBJECT)
|
||||
camera_chunk = _3ds_chunk(OBJECT_CAMERA)
|
||||
camera_target = calc_target(ob.location, ob.rotation_euler.x, ob.rotation_euler.z)
|
||||
camera_distance = mtx_scale @ ob.location.copy()
|
||||
camera_target = calc_target(camera_distance, ob.rotation_euler.x, ob.rotation_euler.z)
|
||||
object_chunk.add_variable("camera", _3ds_string(sane_name(ob.name)))
|
||||
camera_chunk.add_variable("location", _3ds_point_3d(ob.location))
|
||||
camera_chunk.add_variable("location", _3ds_point_3d(camera_distance))
|
||||
camera_chunk.add_variable("target", _3ds_point_3d(camera_target))
|
||||
camera_chunk.add_variable("roll", _3ds_float(round(ob.rotation_euler.y, 6)))
|
||||
camera_chunk.add_variable("lens", _3ds_float(ob.data.lens))
|
||||
|
@ -325,7 +325,8 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of
|
||||
childs_list = []
|
||||
parent_list = []
|
||||
|
||||
def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE):
|
||||
def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN,
|
||||
IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE):
|
||||
|
||||
contextObName = None
|
||||
contextLamp = None
|
||||
@ -362,15 +363,9 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
|
||||
pivot_list = [] # pivots with hierarchy handling
|
||||
trackposition = {} # keep track to position for target calculation
|
||||
|
||||
def putContextMesh(
|
||||
context,
|
||||
myContextMesh_vertls,
|
||||
myContextMesh_facels,
|
||||
myContextMesh_flag,
|
||||
myContextMeshMaterials,
|
||||
myContextMesh_smooth,
|
||||
WORLD_MATRIX,
|
||||
):
|
||||
def putContextMesh(context, myContextMesh_vertls, myContextMesh_facels, myContextMesh_flag,
|
||||
myContextMeshMaterials, myContextMesh_smooth, WORLD_MATRIX):
|
||||
|
||||
bmesh = bpy.data.meshes.new(contextObName)
|
||||
|
||||
if myContextMesh_facels is None:
|
||||
@ -431,8 +426,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
|
||||
imported_objects.append(ob)
|
||||
|
||||
if myContextMesh_flag:
|
||||
"""Bit 0 (0x1) sets edge CA visible, Bit 1 (0x2) sets edge BC visible and Bit 2 (0x4) sets edge AB visible
|
||||
In Blender we use sharp edges for those flags"""
|
||||
"""Bit 0 (0x1) sets edge CA visible, Bit 1 (0x2) sets edge BC visible and
|
||||
Bit 2 (0x4) sets edge AB visible. In Blender we use sharp edges for those flags."""
|
||||
for f, pl in enumerate(bmesh.polygons):
|
||||
face = myContextMesh_facels[f]
|
||||
faceflag = myContextMesh_flag[f]
|
||||
@ -541,7 +536,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
|
||||
0x40 activates alpha source, 0x80 activates tinting, 0x100 ignores alpha, 0x200 activates RGB tint.
|
||||
Bits 0x80, 0x100, and 0x200 are only used with TEXMAP, TEX2MAP, and SPECMAP chunks.
|
||||
0x40, when used with a TEXMAP, TEX2MAP, or SPECMAP chunk must be accompanied with a tint bit,
|
||||
either 0x100 or 0x200, tintcolor will be processed if colorchunks are present"""
|
||||
either 0x100 or 0x200, tintcolor will be processed if colorchunks are present."""
|
||||
tiling = read_short(temp_chunk)
|
||||
if tiling & 0x1:
|
||||
extend = 'decal'
|
||||
@ -620,7 +615,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
|
||||
|
||||
def read_track_data(track_chunk):
|
||||
"""Trackflags 0x1, 0x2 and 0x3 are for looping. 0x8, 0x10 and 0x20
|
||||
locks the XYZ axes. 0x100, 0x200 and 0x400 unlinks the XYZ axes"""
|
||||
locks the XYZ axes. 0x100, 0x200 and 0x400 unlinks the XYZ axes."""
|
||||
tflags = read_short(track_chunk)
|
||||
contextTrack_flag = tflags
|
||||
temp_data = file.read(SZ_U_INT * 2)
|
||||
@ -687,7 +682,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
|
||||
|
||||
# is it an object info chunk?
|
||||
elif new_chunk.ID == OBJECTINFO:
|
||||
process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE)
|
||||
process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN,
|
||||
IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE)
|
||||
|
||||
# keep track of how much we read in the main chunk
|
||||
new_chunk.bytes_read += temp_chunk.bytes_read
|
||||
@ -1105,6 +1101,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
|
||||
for keydata in keyframe_data.items():
|
||||
trackposition[keydata[0]] = keydata[1] # Keep track to position for target calculation
|
||||
child.location = apply_constrain(keydata[1]) if hierarchy == ROOT_OBJECT else mathutils.Vector(keydata[1])
|
||||
if MEASURE:
|
||||
child.location = child.location * 0.001
|
||||
if hierarchy == ROOT_OBJECT:
|
||||
child.location.rotate(CONVERSE)
|
||||
if not contextTrack_flag & 0x100: # Flag 0x100 unlinks X axis
|
||||
@ -1131,6 +1129,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
|
||||
scale = mathutils.Vector.Fill(3, (CONSTRAIN * 0.1)) if CONSTRAIN != 0.0 else child.scale
|
||||
transformation = mathutils.Matrix.LocRotScale(locate, rotate, scale)
|
||||
child.matrix_world = transformation
|
||||
if MEASURE:
|
||||
child.matrix_world = mathutils.Matrix.Scale(0.001,4) @ child.matrix_world
|
||||
if hierarchy == ROOT_OBJECT:
|
||||
child.matrix_world = CONVERSE @ child.matrix_world
|
||||
child.keyframe_insert(data_path="rotation_euler", index=0, frame=keydata[0])
|
||||
@ -1305,7 +1305,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
|
||||
# IMPORT #
|
||||
##########
|
||||
|
||||
def load_3ds(filepath, context, CONSTRAIN=10.0, IMAGE_SEARCH=True, WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None):
|
||||
def load_3ds(filepath, context, CONSTRAIN=10.0, MEASURE=False, IMAGE_SEARCH=True,
|
||||
WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None):
|
||||
|
||||
print("importing 3DS: %r..." % (filepath), end="")
|
||||
|
||||
@ -1335,7 +1336,8 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, IMAGE_SEARCH=True, WORLD_MATRIX=
|
||||
scn = context.scene
|
||||
|
||||
imported_objects = [] # Fill this list with objects
|
||||
process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE)
|
||||
process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN,
|
||||
IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE)
|
||||
|
||||
# fixme, make unglobal
|
||||
object_dictionary.clear()
|
||||
@ -1344,8 +1346,13 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, IMAGE_SEARCH=True, WORLD_MATRIX=
|
||||
if APPLY_MATRIX:
|
||||
for ob in imported_objects:
|
||||
if ob.type == 'MESH':
|
||||
me = ob.data
|
||||
me.transform(ob.matrix_local.inverted())
|
||||
ob.data.transform(ob.matrix_local.inverted())
|
||||
|
||||
if MEASURE:
|
||||
unit_mtx = mathutils.Matrix.Scale(0.001,4)
|
||||
for ob in imported_objects:
|
||||
if ob.type == 'MESH':
|
||||
ob.data.transform(unit_mtx)
|
||||
|
||||
if CONVERSE and not KEYFRAME:
|
||||
for ob in imported_objects:
|
||||
@ -1424,25 +1431,16 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, IMAGE_SEARCH=True, WORLD_MATRIX=
|
||||
file.close()
|
||||
|
||||
|
||||
def load(operator,
|
||||
context,
|
||||
filepath="",
|
||||
constrain_size=0.0,
|
||||
use_image_search=True,
|
||||
use_world_matrix=False,
|
||||
read_keyframe=True,
|
||||
use_apply_transform=True,
|
||||
global_matrix=None,
|
||||
def load(operator, context, filepath="", constrain_size=0.0,
|
||||
convert_measure=False, use_image_search=True,
|
||||
use_world_matrix=False, read_keyframe=True,
|
||||
use_apply_transform=True, global_matrix=None,
|
||||
):
|
||||
|
||||
load_3ds(filepath,
|
||||
context,
|
||||
CONSTRAIN=constrain_size,
|
||||
IMAGE_SEARCH=use_image_search,
|
||||
WORLD_MATRIX=use_world_matrix,
|
||||
KEYFRAME=read_keyframe,
|
||||
APPLY_MATRIX=use_apply_transform,
|
||||
CONVERSE=global_matrix,
|
||||
load_3ds(filepath, context, CONSTRAIN=constrain_size,
|
||||
MEASURE=convert_measure, IMAGE_SEARCH=use_image_search,
|
||||
WORLD_MATRIX=use_world_matrix, KEYFRAME=read_keyframe,
|
||||
APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix,
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
return {'FINISHED'}
|
||||
|
Loading…
Reference in New Issue
Block a user