1 # SPDX-License-Identifier: GPL-2.0-or-later
2 # Copyright 2005 Bob Holcomb
10 from bpy_extras
.node_shader_utils
import PrincipledBSDFWrapper
15 ######################################################
17 ######################################################
19 # Some of the chunks that we will see
20 # ----- Primary Chunk, at the beginning of each file
24 OBJECTINFO
= 0x3D3D # This gives the version of the mesh and is found right before the material and object information
25 VERSION
= 0x0002 # This gives the version of the .3ds file
26 AMBIENTLIGHT
= 0x2100 # The color of the ambient light
27 EDITKEYFRAME
= 0xB000 # This is the header for all of the key frame info
29 # ------ Data Chunks, used for various attributes
30 COLOR_F
= 0x0010 # color defined as 3 floats
31 COLOR_24
= 0x0011 # color defined as 3 bytes
32 LIN_COLOR_24
= 0x0012 # linear byte color
33 LIN_COLOR_F
= 0x0013 # linear float color
34 PCT_SHORT
= 0x30 # percentage short
35 PCT_FLOAT
= 0x31 # percentage float
37 # ------ sub defines of OBJECTINFO
38 MATERIAL
= 0xAFFF # This stored the texture info
39 OBJECT
= 0x4000 # This stores the faces, vertices, etc...
41 # >------ sub defines of MATERIAL
42 # ------ sub defines of MATERIAL_BLOCK
43 MAT_NAME
= 0xA000 # This holds the material name
44 MAT_AMBIENT
= 0xA010 # Ambient color of the object/material
45 MAT_DIFFUSE
= 0xA020 # This holds the color of the object/material
46 MAT_SPECULAR
= 0xA030 # Specular color of the object/material
47 MAT_SHINESS
= 0xA040 # Roughness of the object/material (percent)
48 MAT_SHIN2
= 0xA041 # Shininess of the object/material (percent)
49 MAT_SHIN3
= 0xA042 # Reflection of the object/material (percent)
50 MAT_TRANSPARENCY
= 0xA050 # Transparency value of material (percent)
51 MAT_SELF_ILLUM
= 0xA080 # Self Illumination value of material
52 MAT_SELF_ILPCT
= 0xA084 # Self illumination strength (percent)
53 MAT_WIRE
= 0xA085 # Only render's wireframe
54 MAT_SHADING
= 0xA100 # Material shading method
55 MAT_TEXTURE_MAP
= 0xA200 # This is a header for a new texture map
56 MAT_SPECULAR_MAP
= 0xA204 # This is a header for a new specular map
57 MAT_OPACITY_MAP
= 0xA210 # This is a header for a new opacity map
58 MAT_REFLECTION_MAP
= 0xA220 # This is a header for a new reflection map
59 MAT_BUMP_MAP
= 0xA230 # This is a header for a new bump map
60 MAT_BUMP_PERCENT
= 0xA252 # Normalmap strength (percent)
61 MAT_TEX2_MAP
= 0xA33A # This is a header for a secondary texture
62 MAT_SHIN_MAP
= 0xA33C # This is a header for a new roughness map
63 MAT_SELFI_MAP
= 0xA33D # This is a header for a new emission map
64 MAT_MAP_FILEPATH
= 0xA300 # This holds the file name of the texture
65 MAT_MAP_TILING
= 0xA351 # 2nd bit (from LSB) is mirror UV flag
66 MAT_MAP_USCALE
= 0xA354 # U axis scaling
67 MAT_MAP_VSCALE
= 0xA356 # V axis scaling
68 MAT_MAP_UOFFSET
= 0xA358 # U axis offset
69 MAT_MAP_VOFFSET
= 0xA35A # V axis offset
70 MAT_MAP_ANG
= 0xA35C # UV rotation around the z-axis in rad
71 MAT_MAP_COL1
= 0xA360 # Map Color1
72 MAT_MAP_COL2
= 0xA362 # Map Color2
73 MAT_MAP_RCOL
= 0xA364 # Red mapping
74 MAT_MAP_GCOL
= 0xA366 # Green mapping
75 MAT_MAP_BCOL
= 0xA368 # Blue mapping
77 # >------ sub defines of OBJECT
78 OBJECT_MESH
= 0x4100 # This lets us know that we are reading a new object
79 OBJECT_LIGHT
= 0x4600 # This lets un know we are reading a light object
80 OBJECT_LIGHT_SPOT
= 0x4610 # The light is a spotloght.
81 OBJECT_LIGHT_OFF
= 0x4620 # The light off.
82 OBJECT_LIGHT_ATTENUATE
= 0x4625
83 OBJECT_LIGHT_RAYSHADE
= 0x4627
84 OBJECT_LIGHT_SHADOWED
= 0x4630
85 OBJECT_LIGHT_LOCAL_SHADOW
= 0x4640
86 OBJECT_LIGHT_LOCAL_SHADOW2
= 0x4641
87 OBJECT_LIGHT_SEE_CONE
= 0x4650
88 OBJECT_LIGHT_SPOT_RECTANGULAR
= 0x4651
89 OBJECT_LIGHT_SPOT_OVERSHOOT
= 0x4652
90 OBJECT_LIGHT_SPOT_PROJECTOR
= 0x4653
91 OBJECT_LIGHT_EXCLUDE
= 0x4654
92 OBJECT_LIGHT_RANGE
= 0x4655
93 OBJECT_LIGHT_ROLL
= 0x4656
94 OBJECT_LIGHT_SPOT_ASPECT
= 0x4657
95 OBJECT_LIGHT_RAY_BIAS
= 0x4658
96 OBJECT_LIGHT_INNER_RANGE
= 0x4659
97 OBJECT_LIGHT_OUTER_RANGE
= 0x465A
98 OBJECT_LIGHT_MULTIPLIER
= 0x465B
99 OBJECT_LIGHT_AMBIENT_LIGHT
= 0x4680
101 OBJECT_CAMERA
= 0x4700 # This lets un know we are reading a camera object
103 # >------ sub defines of CAMERA
104 OBJECT_CAM_RANGES
= 0x4720 # The camera range values
106 # >------ sub defines of OBJECT_MESH
107 OBJECT_VERTICES
= 0x4110 # The objects vertices
108 OBJECT_VERTFLAGS
= 0x4111 # The objects vertex flags
109 OBJECT_FACES
= 0x4120 # The objects faces
110 OBJECT_MATERIAL
= 0x4130 # This is found if the object has a material, either texture map or color
111 OBJECT_UV
= 0x4140 # The UV texture coordinates
112 OBJECT_SMOOTH
= 0x4150 # The Object smooth groups
113 OBJECT_TRANS_MATRIX
= 0x4160 # The Object Matrix
115 # >------ sub defines of EDITKEYFRAME
116 KFDATA_AMBIENT
= 0xB001
117 KFDATA_OBJECT
= 0xB002
118 KFDATA_CAMERA
= 0xB003
119 KFDATA_TARGET
= 0xB004
120 KFDATA_LIGHT
= 0xB005
121 KFDATA_L_TARGET
= 0xB006
122 KFDATA_SPOTLIGHT
= 0xB007
123 KFDATA_KFSEG
= 0xB008
124 # KFDATA_CURTIME = 0xB009
125 # KFDATA_KFHDR = 0xB00A
126 # >------ sub defines of KEYFRAME_NODE
127 OBJECT_NODE_HDR
= 0xB010
128 OBJECT_INSTANCE_NAME
= 0xB011
129 # OBJECT_PRESCALE = 0xB012
130 OBJECT_PIVOT
= 0xB013
131 # OBJECT_BOUNDBOX = 0xB014
132 MORPH_SMOOTH
= 0xB015
133 POS_TRACK_TAG
= 0xB020
134 ROT_TRACK_TAG
= 0xB021
135 SCL_TRACK_TAG
= 0xB022
136 FOV_TRACK_TAG
= 0xB023
137 ROLL_TRACK_TAG
= 0xB024
138 COL_TRACK_TAG
= 0xB025
139 # MORPH_TRACK_TAG = 0xB026
140 # HOTSPOT_TRACK_TAG = 0xB027
141 # FALLOFF_TRACK_TAG = 0xB028
142 # HIDE_TRACK_TAG = 0xB029
143 # OBJECT_NODE_ID = 0xB030
150 object_dictionary
= {}
160 # we don't read in the bytes_read, we compute that
161 binary_format
= "<HI"
169 print('ID: ', self
.ID
)
170 print('ID in hex: ', hex(self
.ID
))
171 print('length: ', self
.length
)
172 print('bytes_read: ', self
.bytes_read
)
175 def read_chunk(file, chunk
):
176 temp_data
= file.read(struct
.calcsize(chunk
.binary_format
))
177 data
= struct
.unpack(chunk
.binary_format
, temp_data
)
179 chunk
.length
= data
[1]
180 # update the bytes read function
187 def read_string(file):
188 # read in the characters till we get a null character
195 # print('string: ', s)
197 # Remove the null character from the string
198 # print("read string", s)
199 return str(b
''.join(s
), "utf-8", "replace"), len(s
) + 1
201 ######################################################
203 ######################################################
206 def process_next_object_chunk(file, previous_chunk
):
209 while (previous_chunk
.bytes_read
< previous_chunk
.length
):
210 # read the next chunk
211 read_chunk(file, new_chunk
)
214 def skip_to_end(file, skip_chunk
):
215 buffer_size
= skip_chunk
.length
- skip_chunk
.bytes_read
216 binary_format
= "%ic" % buffer_size
217 file.read(struct
.calcsize(binary_format
))
218 skip_chunk
.bytes_read
+= buffer_size
221 def add_texture_to_material(image
, contextWrapper
, pct
, extend
, alpha
, scale
, offset
, angle
, tintcolor
, mapto
):
222 shader
= contextWrapper
.node_principled_bsdf
223 nodetree
= contextWrapper
.material
.node_tree
224 shader
.location
= (-300, 0)
225 nodes
= nodetree
.nodes
226 links
= nodetree
.links
229 mixer
= nodes
.new(type='ShaderNodeMixRGB')
230 mixer
.label
= "Mixer"
231 mixer
.inputs
[0].default_value
= pct
/ 100
232 mixer
.inputs
[1].default_value
= (
233 tintcolor
[:3] + [1] if tintcolor
else
234 shader
.inputs
['Base Color'].default_value
[:]
236 contextWrapper
._grid
_to
_location
(1, 2, dst_node
=mixer
, ref_node
=shader
)
237 img_wrap
= contextWrapper
.base_color_texture
238 links
.new(img_wrap
.node_image
.outputs
['Color'], mixer
.inputs
[2])
239 links
.new(mixer
.outputs
['Color'], shader
.inputs
['Base Color'])
240 elif mapto
== 'SPECULARITY':
241 img_wrap
= contextWrapper
.specular_texture
242 elif mapto
== 'ALPHA':
243 shader
.location
= (0, -300)
244 img_wrap
= contextWrapper
.alpha_texture
245 elif mapto
== 'METALLIC':
246 shader
.location
= (300, 300)
247 img_wrap
= contextWrapper
.metallic_texture
248 elif mapto
== 'ROUGHNESS':
249 shader
.location
= (300, 0)
250 img_wrap
= contextWrapper
.roughness_texture
251 elif mapto
== 'EMISSION':
252 shader
.location
= (-300, -600)
253 img_wrap
= contextWrapper
.emission_color_texture
254 elif mapto
== 'NORMAL':
255 shader
.location
= (300, 300)
256 img_wrap
= contextWrapper
.normalmap_texture
257 elif mapto
== 'TEXTURE':
258 img_wrap
= nodes
.new(type='ShaderNodeTexImage')
259 img_wrap
.label
= image
.name
260 contextWrapper
._grid
_to
_location
(0, 2, dst_node
=img_wrap
, ref_node
=shader
)
262 if node
.label
== 'Mixer':
263 spare
= node
.inputs
[1] if node
.inputs
[1].is_linked
is False else node
.inputs
[2]
264 socket
= spare
if spare
.is_linked
is False else node
.inputs
[0]
265 links
.new(img_wrap
.outputs
['Color'], socket
)
266 if node
.type == 'TEX_COORD':
267 links
.new(node
.outputs
['UV'], img_wrap
.inputs
['Vector'])
268 if shader
.inputs
['Base Color'].is_linked
is False:
269 links
.new(img_wrap
.outputs
['Color'], shader
.inputs
['Base Color'])
271 img_wrap
.image
= image
272 img_wrap
.extension
= 'REPEAT'
274 if mapto
!= 'TEXTURE':
275 img_wrap
.scale
= scale
276 img_wrap
.translation
= offset
277 img_wrap
.rotation
[2] = angle
279 if extend
== 'mirror':
280 # 3DS mirror flag can be emulated by these settings (at least so it seems)
281 # TODO: bring back mirror
283 # texture.repeat_x = texture.repeat_y = 2
284 # texture.use_mirror_x = texture.use_mirror_y = True
285 elif extend
== 'decal':
286 # 3DS' decal mode maps best to Blenders EXTEND
287 img_wrap
.extension
= 'EXTEND'
288 elif extend
== 'noWrap':
289 img_wrap
.extension
= 'CLIP'
292 if link
.from_node
.type == 'TEX_IMAGE' and link
.to_node
.type == 'MIX_RGB':
293 tex
= link
.from_node
.image
.name
294 own_node
= img_wrap
.node_image
295 own_map
= img_wrap
.node_mapping
296 if tex
== image
.name
:
297 links
.new(link
.from_node
.outputs
['Alpha'], img_wrap
.socket_dst
)
298 nodes
.remove(own_map
)
299 nodes
.remove(own_node
)
300 for imgs
in bpy
.data
.images
:
301 if imgs
.name
[-3:].isdigit():
303 bpy
.data
.images
.remove(imgs
)
305 links
.new(img_wrap
.node_image
.outputs
['Alpha'], img_wrap
.socket_dst
)
307 shader
.location
= (300, 300)
308 contextWrapper
._grid
_to
_location
(1, 0, dst_node
=contextWrapper
.node_out
, ref_node
=shader
)
311 def process_next_chunk(context
, file, previous_chunk
, imported_objects
, IMAGE_SEARCH
, WORLD_MATRIX
, KEYFRAME
):
312 from bpy_extras
.image_utils
import load_image
317 contextMaterial
= None
318 contextWrapper
= None
320 contextMesh_vertls
= None
321 contextMesh_facels
= None
322 contextMesh_flag
= None
323 contextMeshMaterials
= []
324 contextMesh_smooth
= None
330 # Localspace variable names, faster.
331 SZ_FLOAT
= struct
.calcsize('f')
332 SZ_2FLOAT
= struct
.calcsize('2f')
333 SZ_3FLOAT
= struct
.calcsize('3f')
334 SZ_4FLOAT
= struct
.calcsize('4f')
335 SZ_U_INT
= struct
.calcsize('I')
336 SZ_U_SHORT
= struct
.calcsize('H')
337 SZ_4U_SHORT
= struct
.calcsize('4H')
338 SZ_4x3MAT
= struct
.calcsize('ffffffffffff')
340 object_list
= [] # for hierarchy
341 object_parent
= [] # index of parent in hierarchy, 0xFFFF = no parent
342 pivot_list
= [] # pivots with hierarchy handling
346 myContextMesh_vertls
,
347 myContextMesh_facels
,
349 myContextMeshMaterials
,
350 myContextMesh_smooth
,
353 bmesh
= bpy
.data
.meshes
.new(contextObName
)
355 if myContextMesh_facels
is None:
356 myContextMesh_facels
= []
358 if myContextMesh_vertls
:
360 bmesh
.vertices
.add(len(myContextMesh_vertls
) // 3)
361 bmesh
.vertices
.foreach_set("co", myContextMesh_vertls
)
363 nbr_faces
= len(myContextMesh_facels
)
364 bmesh
.polygons
.add(nbr_faces
)
365 bmesh
.loops
.add(nbr_faces
* 3)
366 eekadoodle_faces
= []
367 for v1
, v2
, v3
in myContextMesh_facels
:
368 eekadoodle_faces
.extend((v3
, v1
, v2
) if v3
== 0 else (v1
, v2
, v3
))
369 bmesh
.polygons
.foreach_set("loop_start", range(0, nbr_faces
* 3, 3))
370 bmesh
.loops
.foreach_set("vertex_index", eekadoodle_faces
)
372 if bmesh
.polygons
and contextMeshUV
:
373 bmesh
.uv_layers
.new()
374 uv_faces
= bmesh
.uv_layers
.active
.data
[:]
378 for mat_idx
, (matName
, faces
) in enumerate(myContextMeshMaterials
):
382 bmat
= MATDICT
.get(matName
)
383 # in rare cases no materials defined.
385 bmesh
.materials
.append(bmat
) # can be None
387 bmesh
.polygons
[fidx
].material_index
= mat_idx
390 uvl
= bmesh
.uv_layers
.active
.data
[:]
391 for fidx
, pl
in enumerate(bmesh
.polygons
):
392 face
= myContextMesh_facels
[fidx
]
397 v1
, v2
, v3
= v3
, v1
, v2
399 uvl
[pl
.loop_start
].uv
= contextMeshUV
[v1
* 2: (v1
* 2) + 2]
400 uvl
[pl
.loop_start
+ 1].uv
= contextMeshUV
[v2
* 2: (v2
* 2) + 2]
401 uvl
[pl
.loop_start
+ 2].uv
= contextMeshUV
[v3
* 2: (v3
* 2) + 2]
407 ob
= bpy
.data
.objects
.new(contextObName
, bmesh
)
408 object_dictionary
[contextObName
] = ob
409 context
.view_layer
.active_layer_collection
.collection
.objects
.link(ob
)
410 imported_objects
.append(ob
)
412 if myContextMesh_flag
:
413 # Bit 0 (0x1) sets edge CA visible, Bit 1 (0x2) sets edge BC visible and Bit 2 (0x4) sets edge AB visible
414 # In Blender we use sharp edges for those flags
415 for f
, pl
in enumerate(bmesh
.polygons
):
416 face
= myContextMesh_facels
[f
]
417 faceflag
= myContextMesh_flag
[f
]
418 edge_ab
= bmesh
.edges
[bmesh
.loops
[pl
.loop_start
].edge_index
]
419 edge_bc
= bmesh
.edges
[bmesh
.loops
[pl
.loop_start
+ 1].edge_index
]
420 edge_ca
= bmesh
.edges
[bmesh
.loops
[pl
.loop_start
+ 2].edge_index
]
422 edge_ab
, edge_bc
, edge_ca
= edge_ca
, edge_ab
, edge_bc
424 edge_ca
.use_edge_sharp
= True
426 edge_bc
.use_edge_sharp
= True
428 edge_ca
.use_edge_sharp
= True
429 edge_bc
.use_edge_sharp
= True
431 edge_ab
.use_edge_sharp
= True
433 edge_ca
.use_edge_sharp
= True
434 edge_ab
.use_edge_sharp
= True
436 edge_bc
.use_edge_sharp
= True
437 edge_ab
.use_edge_sharp
= True
439 edge_bc
.use_edge_sharp
= True
440 edge_ab
.use_edge_sharp
= True
441 edge_ca
.use_edge_sharp
= True
443 if myContextMesh_smooth
:
444 for f
, pl
in enumerate(bmesh
.polygons
):
445 smoothface
= myContextMesh_smooth
[f
]
447 bmesh
.polygons
[f
].use_smooth
= True
451 ob
.matrix_world
= contextMatrix
453 ob
.matrix_local
= contextMatrix
454 object_matrix
[ob
] = contextMatrix
.copy()
460 CreateBlenderObject
= False
461 CreateLightObject
= False
462 CreateCameraObject
= False
464 def read_float_color(temp_chunk
):
465 temp_data
= file.read(SZ_3FLOAT
)
466 temp_chunk
.bytes_read
+= SZ_3FLOAT
467 return [float(col
) for col
in struct
.unpack('<3f', temp_data
)]
469 def read_float(temp_chunk
):
470 temp_data
= file.read(SZ_FLOAT
)
471 temp_chunk
.bytes_read
+= SZ_FLOAT
472 return struct
.unpack('<f', temp_data
)[0]
474 def read_short(temp_chunk
):
475 temp_data
= file.read(SZ_U_SHORT
)
476 temp_chunk
.bytes_read
+= SZ_U_SHORT
477 return struct
.unpack('<H', temp_data
)[0]
479 def read_byte_color(temp_chunk
):
480 temp_data
= file.read(struct
.calcsize('3B'))
481 temp_chunk
.bytes_read
+= 3
482 return [float(col
) / 255 for col
in struct
.unpack('<3B', temp_data
)]
484 def read_texture(new_chunk
, temp_chunk
, name
, mapto
):
485 uscale
, vscale
, uoffset
, voffset
, angle
= 1.0, 1.0, 0.0, 0.0, 0.0
486 contextWrapper
.use_nodes
= True
492 contextWrapper
.emission_color
= contextMaterial
.line_color
[:3]
493 contextWrapper
.emission_strength
= contextMaterial
.line_priority
/ 100
494 contextWrapper
.base_color
= contextMaterial
.diffuse_color
[:3]
495 contextWrapper
.specular
= contextMaterial
.specular_intensity
496 contextWrapper
.roughness
= contextMaterial
.roughness
497 contextWrapper
.metallic
= contextMaterial
.metallic
498 contextWrapper
.alpha
= contextMaterial
.diffuse_color
[3]
500 while (new_chunk
.bytes_read
< new_chunk
.length
):
501 read_chunk(file, temp_chunk
)
502 if temp_chunk
.ID
== PCT_SHORT
:
503 pct
= read_short(temp_chunk
)
505 elif temp_chunk
.ID
== MAT_MAP_FILEPATH
:
506 texture_name
, read_str_len
= read_string(file)
507 img
= load_image(texture_name
, dirname
, place_holder
=False, recursive
=IMAGE_SEARCH
, check_existing
=True)
508 temp_chunk
.bytes_read
+= read_str_len
# plus one for the null character that gets removed
510 elif temp_chunk
.ID
== MAT_MAP_USCALE
:
511 uscale
= read_float(temp_chunk
)
512 elif temp_chunk
.ID
== MAT_MAP_VSCALE
:
513 vscale
= read_float(temp_chunk
)
514 elif temp_chunk
.ID
== MAT_MAP_UOFFSET
:
515 uoffset
= read_float(temp_chunk
)
516 elif temp_chunk
.ID
== MAT_MAP_VOFFSET
:
517 voffset
= read_float(temp_chunk
)
519 elif temp_chunk
.ID
== MAT_MAP_TILING
:
520 tiling
= read_short(temp_chunk
)
540 elif temp_chunk
.ID
== MAT_MAP_ANG
:
541 angle
= read_float(temp_chunk
)
542 print("\nwarning: UV angle mapped to z-rotation")
544 elif temp_chunk
.ID
== MAT_MAP_COL1
:
545 tintcolor
= read_byte_color(temp_chunk
)
547 skip_to_end(file, temp_chunk
)
548 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
550 # add the map to the material in the right channel
552 add_texture_to_material(img
, contextWrapper
, pct
, extend
, alpha
, (uscale
, vscale
, 1),
553 (uoffset
, voffset
, 0), angle
, tintcolor
, mapto
)
555 def read_track_data(temp_chunk
):
556 new_chunk
.bytes_read
+= SZ_U_SHORT
* 5
557 temp_data
= file.read(SZ_U_SHORT
* 5)
558 temp_data
= file.read(SZ_U_INT
)
559 nkeys
= struct
.unpack('<I', temp_data
)[0]
560 new_chunk
.bytes_read
+= SZ_U_INT
561 for i
in range(nkeys
):
562 temp_data
= file.read(SZ_U_INT
)
563 nframe
= struct
.unpack('<I', temp_data
)[0]
564 new_chunk
.bytes_read
+= SZ_U_INT
565 temp_data
= file.read(SZ_U_SHORT
)
566 nflags
= struct
.unpack('<H', temp_data
)[0]
567 new_chunk
.bytes_read
+= SZ_U_SHORT
569 temp_data
= file.read(SZ_FLOAT
)
570 new_chunk
.bytes_read
+= SZ_FLOAT
571 temp_data
= file.read(SZ_3FLOAT
)
572 data
= struct
.unpack('<3f', temp_data
)
573 new_chunk
.bytes_read
+= SZ_3FLOAT
574 if nkeys
> 1: # Read keyframe data
575 for i
in range(nkeys
- 1):
576 temp_data
= file.read(SZ_U_INT
)
577 kframe
= struct
.unpack('<I', temp_data
)[0]
578 new_chunk
.bytes_read
+= SZ_U_INT
579 temp_data
= file.read(SZ_U_SHORT
)
580 kflags
= struct
.unpack('<H', temp_data
)[0]
581 new_chunk
.bytes_read
+= SZ_U_SHORT
582 temp_data
= file.read(SZ_3FLOAT
)
583 kdata
= struct
.unpack('<3f', temp_data
)
584 new_chunk
.bytes_read
+= SZ_3FLOAT
588 def read_track_angle(temp_chunk
):
589 new_chunk
.bytes_read
+= SZ_U_SHORT
* 5
590 temp_data
= file.read(SZ_U_SHORT
* 5)
591 temp_data
= file.read(SZ_U_INT
)
592 nkeys
= struct
.unpack('<I', temp_data
)[0]
593 new_chunk
.bytes_read
+= SZ_U_INT
594 for i
in range(nkeys
):
595 temp_data
= file.read(SZ_U_INT
)
596 nframe
= struct
.unpack('<I', temp_data
)[0]
597 new_chunk
.bytes_read
+= SZ_U_INT
598 temp_data
= file.read(SZ_U_SHORT
)
599 nflags
= struct
.unpack('<H', temp_data
)[0]
600 new_chunk
.bytes_read
+= SZ_U_SHORT
602 temp_data
= file.read(SZ_FLOAT
)
603 new_chunk
.bytes_read
+= SZ_FLOAT
604 temp_data
= file.read(SZ_FLOAT
)
605 angle
= struct
.unpack('<f', temp_data
)[0]
606 new_chunk
.bytes_read
+= SZ_FLOAT
607 if nkeys
> 1: # Read keyframe data
608 for i
in range(nkeys
- 1):
609 temp_data
= file.read(SZ_U_INT
)
610 kframe
= struct
.unpack('<I', temp_data
)[0]
611 new_chunk
.bytes_read
+= SZ_U_INT
612 temp_data
= file.read(SZ_U_SHORT
)
613 kflags
= struct
.unpack('<H', temp_data
)[0]
614 new_chunk
.bytes_read
+= SZ_U_SHORT
615 temp_data
= file.read(SZ_FLOAT
)
616 kangle
= struct
.unpack('<f', temp_data
)[0]
617 new_chunk
.bytes_read
+= SZ_FLOAT
619 return math
.radians(angle
)
621 dirname
= os
.path
.dirname(file.name
)
623 # loop through all the data for this chunk (previous chunk) and see what it is
624 while (previous_chunk
.bytes_read
< previous_chunk
.length
):
625 read_chunk(file, new_chunk
)
627 # is it a Version chunk?
628 if new_chunk
.ID
== VERSION
:
629 # read in the version of the file
630 temp_data
= file.read(SZ_U_INT
)
631 version
= struct
.unpack('<I', temp_data
)[0]
632 new_chunk
.bytes_read
+= 4 # read the 4 bytes for the version number
633 # this loader works with version 3 and below, but may not with 4 and above
635 print('\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version
)
637 # is it an ambient light chunk?
638 elif new_chunk
.ID
== AMBIENTLIGHT
:
639 path
, filename
= os
.path
.split(file.name
)
640 realname
, ext
= os
.path
.splitext(filename
)
641 world
= bpy
.data
.worlds
.new("Ambient: " + realname
)
642 world
.light_settings
.use_ambient_occlusion
= True
643 context
.scene
.world
= world
644 read_chunk(file, temp_chunk
)
645 if temp_chunk
.ID
== COLOR_F
:
646 context
.scene
.world
.color
[:] = read_float_color(temp_chunk
)
647 elif temp_chunk
.ID
== LIN_COLOR_F
:
648 context
.scene
.world
.color
[:] = read_float_color(temp_chunk
)
650 skip_to_end(file, temp_chunk
)
651 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
653 # is it an object info chunk?
654 elif new_chunk
.ID
== OBJECTINFO
:
655 process_next_chunk(context
, file, new_chunk
, imported_objects
, IMAGE_SEARCH
, WORLD_MATRIX
, KEYFRAME
)
657 # keep track of how much we read in the main chunk
658 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
660 # is it an object chunk?
661 elif new_chunk
.ID
== OBJECT
:
663 if CreateBlenderObject
:
669 contextMeshMaterials
,
673 contextMesh_vertls
= []
674 contextMesh_facels
= []
675 contextMeshMaterials
= []
676 contextMesh_flag
= None
677 contextMesh_smooth
= None
682 CreateBlenderObject
= True
683 contextObName
, read_str_len
= read_string(file)
684 new_chunk
.bytes_read
+= read_str_len
686 # is it a material chunk?
687 elif new_chunk
.ID
== MATERIAL
:
688 contextMaterial
= bpy
.data
.materials
.new('Material')
689 contextWrapper
= PrincipledBSDFWrapper(contextMaterial
, is_readonly
=False, use_nodes
=False)
691 elif new_chunk
.ID
== MAT_NAME
:
692 material_name
, read_str_len
= read_string(file)
694 # plus one for the null character that ended the string
695 new_chunk
.bytes_read
+= read_str_len
696 contextMaterial
.name
= material_name
.rstrip() # remove trailing whitespace
697 MATDICT
[material_name
] = contextMaterial
699 elif new_chunk
.ID
== MAT_AMBIENT
:
700 read_chunk(file, temp_chunk
)
701 # only available color is emission color
702 if temp_chunk
.ID
== COLOR_F
:
703 contextMaterial
.line_color
[:3] = read_float_color(temp_chunk
)
704 elif temp_chunk
.ID
== COLOR_24
:
705 contextMaterial
.line_color
[:3] = read_byte_color(temp_chunk
)
707 skip_to_end(file, temp_chunk
)
708 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
710 elif new_chunk
.ID
== MAT_DIFFUSE
:
711 read_chunk(file, temp_chunk
)
712 if temp_chunk
.ID
== COLOR_F
:
713 contextMaterial
.diffuse_color
[:3] = read_float_color(temp_chunk
)
714 elif temp_chunk
.ID
== COLOR_24
:
715 contextMaterial
.diffuse_color
[:3] = read_byte_color(temp_chunk
)
717 skip_to_end(file, temp_chunk
)
718 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
720 elif new_chunk
.ID
== MAT_SPECULAR
:
721 read_chunk(file, temp_chunk
)
722 # Specular color is available
723 if temp_chunk
.ID
== COLOR_F
:
724 contextMaterial
.specular_color
= read_float_color(temp_chunk
)
725 elif temp_chunk
.ID
== COLOR_24
:
726 contextMaterial
.specular_color
= read_byte_color(temp_chunk
)
728 skip_to_end(file, temp_chunk
)
729 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
731 elif new_chunk
.ID
== MAT_SHINESS
:
732 read_chunk(file, temp_chunk
)
733 if temp_chunk
.ID
== PCT_SHORT
:
734 temp_data
= file.read(SZ_U_SHORT
)
735 temp_chunk
.bytes_read
+= SZ_U_SHORT
736 contextMaterial
.roughness
= 1 - (float(struct
.unpack('<H', temp_data
)[0]) / 100)
737 elif temp_chunk
.ID
== PCT_FLOAT
:
738 temp_data
= file.read(SZ_FLOAT
)
739 temp_chunk
.bytes_read
+= SZ_FLOAT
740 contextMaterial
.roughness
= 1 - float(struct
.unpack('f', temp_data
)[0])
741 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
743 elif new_chunk
.ID
== MAT_SHIN2
:
744 read_chunk(file, temp_chunk
)
745 if temp_chunk
.ID
== PCT_SHORT
:
746 temp_data
= file.read(SZ_U_SHORT
)
747 temp_chunk
.bytes_read
+= SZ_U_SHORT
748 contextMaterial
.specular_intensity
= (float(struct
.unpack('<H', temp_data
)[0]) / 100)
749 elif temp_chunk
.ID
== PCT_FLOAT
:
750 temp_data
= file.read(SZ_FLOAT
)
751 temp_chunk
.bytes_read
+= SZ_FLOAT
752 contextMaterial
.specular_intensity
= float(struct
.unpack('f', temp_data
)[0])
753 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
755 elif new_chunk
.ID
== MAT_SHIN3
:
756 read_chunk(file, temp_chunk
)
757 if temp_chunk
.ID
== PCT_SHORT
:
758 temp_data
= file.read(SZ_U_SHORT
)
759 temp_chunk
.bytes_read
+= SZ_U_SHORT
760 contextMaterial
.metallic
= (float(struct
.unpack('<H', temp_data
)[0]) / 100)
761 elif temp_chunk
.ID
== PCT_FLOAT
:
762 temp_data
= file.read(SZ_FLOAT
)
763 temp_chunk
.bytes_read
+= SZ_FLOAT
764 contextMaterial
.metallic
= float(struct
.unpack('f', temp_data
)[0])
765 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
767 elif new_chunk
.ID
== MAT_TRANSPARENCY
:
768 read_chunk(file, temp_chunk
)
769 if temp_chunk
.ID
== PCT_SHORT
:
770 temp_data
= file.read(SZ_U_SHORT
)
771 temp_chunk
.bytes_read
+= SZ_U_SHORT
772 contextMaterial
.diffuse_color
[3] = 1 - (float(struct
.unpack('<H', temp_data
)[0]) / 100)
773 elif temp_chunk
.ID
== PCT_FLOAT
:
774 temp_data
= file.read(SZ_FLOAT
)
775 temp_chunk
.bytes_read
+= SZ_FLOAT
776 contextMaterial
.diffuse_color
[3] = 1 - float(struct
.unpack('f', temp_data
)[0])
778 print("Cannot read material transparency")
779 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
781 elif new_chunk
.ID
== MAT_SELF_ILPCT
:
782 read_chunk(file, temp_chunk
)
783 if temp_chunk
.ID
== PCT_SHORT
:
784 temp_data
= file.read(SZ_U_SHORT
)
785 temp_chunk
.bytes_read
+= SZ_U_SHORT
786 contextMaterial
.line_priority
= int(struct
.unpack('H', temp_data
)[0])
787 elif temp_chunk
.ID
== PCT_FLOAT
:
788 temp_data
= file.read(SZ_FLOAT
)
789 temp_chunk
.bytes_read
+= SZ_FLOAT
790 contextMaterial
.line_priority
= (float(struct
.unpack('f', temp_data
)[0]) * 100)
791 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
793 elif new_chunk
.ID
== MAT_SHADING
:
794 shading
= read_short(new_chunk
)
796 contextWrapper
.use_nodes
= True
797 contextWrapper
.emission_color
= contextMaterial
.line_color
[:3]
798 contextWrapper
.emission_strength
= contextMaterial
.line_priority
/ 100
799 contextWrapper
.base_color
= contextMaterial
.diffuse_color
[:3]
800 contextWrapper
.specular
= contextMaterial
.specular_intensity
801 contextWrapper
.roughness
= contextMaterial
.roughness
802 contextWrapper
.metallic
= contextMaterial
.metallic
803 contextWrapper
.alpha
= contextMaterial
.diffuse_color
[3]
804 contextWrapper
.use_nodes
= False
806 contextWrapper
.use_nodes
= True
808 elif new_chunk
.ID
== MAT_TEXTURE_MAP
:
809 read_texture(new_chunk
, temp_chunk
, "Diffuse", "COLOR")
811 elif new_chunk
.ID
== MAT_SPECULAR_MAP
:
812 read_texture(new_chunk
, temp_chunk
, "Specular", "SPECULARITY")
814 elif new_chunk
.ID
== MAT_OPACITY_MAP
:
815 contextMaterial
.blend_method
= 'BLEND'
816 read_texture(new_chunk
, temp_chunk
, "Opacity", "ALPHA")
818 elif new_chunk
.ID
== MAT_REFLECTION_MAP
:
819 read_texture(new_chunk
, temp_chunk
, "Reflect", "METALLIC")
821 elif new_chunk
.ID
== MAT_BUMP_MAP
:
822 read_texture(new_chunk
, temp_chunk
, "Bump", "NORMAL")
824 elif new_chunk
.ID
== MAT_BUMP_PERCENT
:
825 read_chunk(file, temp_chunk
)
826 if temp_chunk
.ID
== PCT_SHORT
:
827 temp_data
= file.read(SZ_U_SHORT
)
828 temp_chunk
.bytes_read
+= SZ_U_SHORT
829 contextWrapper
.normalmap_strength
= (float(struct
.unpack('<H', temp_data
)[0]) / 100)
830 elif temp_chunk
.ID
== PCT_FLOAT
:
831 temp_data
= file.read(SZ_FLOAT
)
832 temp_chunk
.bytes_read
+= SZ_FLOAT
833 contextWrapper
.normalmap_strength
= float(struct
.unpack('f', temp_data
)[0])
835 skip_to_end(file, temp_chunk
)
836 new_chunk
.bytes_read
+= temp_chunk
.bytes_read
838 elif new_chunk
.ID
== MAT_SHIN_MAP
:
839 read_texture(new_chunk
, temp_chunk
, "Shininess", "ROUGHNESS")
841 elif new_chunk
.ID
== MAT_SELFI_MAP
:
842 read_texture(new_chunk
, temp_chunk
, "Emit", "EMISSION")
844 elif new_chunk
.ID
== MAT_TEX2_MAP
:
845 read_texture(new_chunk
, temp_chunk
, "Tex", "TEXTURE")
848 elif new_chunk
.ID
== OBJECT_MESH
:
851 elif new_chunk
.ID
== OBJECT_VERTICES
:
852 """Worldspace vertex locations"""
853 temp_data
= file.read(SZ_U_SHORT
)
854 num_verts
= struct
.unpack('<H', temp_data
)[0]
855 new_chunk
.bytes_read
+= 2
856 contextMesh_vertls
= struct
.unpack('<%df' % (num_verts
* 3), file.read(SZ_3FLOAT
* num_verts
))
857 new_chunk
.bytes_read
+= SZ_3FLOAT
* num_verts
858 # dummyvert is not used atm!
860 elif new_chunk
.ID
== OBJECT_FACES
:
861 temp_data
= file.read(SZ_U_SHORT
)
862 num_faces
= struct
.unpack('<H', temp_data
)[0]
863 new_chunk
.bytes_read
+= 2
864 temp_data
= file.read(SZ_4U_SHORT
* num_faces
)
865 new_chunk
.bytes_read
+= SZ_4U_SHORT
* num_faces
# 4 short ints x 2 bytes each
866 contextMesh_facels
= struct
.unpack('<%dH' % (num_faces
* 4), temp_data
)
867 contextMesh_flag
= [contextMesh_facels
[i
] for i
in range(3, (num_faces
* 4) + 3, 4)]
868 contextMesh_facels
= [contextMesh_facels
[i
- 3:i
] for i
in range(3, (num_faces
* 4) + 3, 4)]
870 elif new_chunk
.ID
== OBJECT_MATERIAL
:
871 material_name
, read_str_len
= read_string(file)
872 new_chunk
.bytes_read
+= read_str_len
# remove 1 null character.
873 temp_data
= file.read(SZ_U_SHORT
)
874 num_faces_using_mat
= struct
.unpack('<H', temp_data
)[0]
875 new_chunk
.bytes_read
+= SZ_U_SHORT
876 temp_data
= file.read(SZ_U_SHORT
* num_faces_using_mat
)
877 new_chunk
.bytes_read
+= SZ_U_SHORT
* num_faces_using_mat
878 temp_data
= struct
.unpack("<%dH" % (num_faces_using_mat
), temp_data
)
879 contextMeshMaterials
.append((material_name
, temp_data
))
880 # look up the material in all the materials
882 elif new_chunk
.ID
== OBJECT_SMOOTH
:
883 temp_data
= file.read(SZ_U_INT
* num_faces
)
884 smoothgroup
= struct
.unpack('<%dI' % (num_faces
), temp_data
)
885 new_chunk
.bytes_read
+= SZ_U_INT
* num_faces
886 contextMesh_smooth
= smoothgroup
888 elif new_chunk
.ID
== OBJECT_UV
:
889 temp_data
= file.read(SZ_U_SHORT
)
890 num_uv
= struct
.unpack('<H', temp_data
)[0]
891 new_chunk
.bytes_read
+= 2
892 temp_data
= file.read(SZ_2FLOAT
* num_uv
)
893 new_chunk
.bytes_read
+= SZ_2FLOAT
* num_uv
894 contextMeshUV
= struct
.unpack('<%df' % (num_uv
* 2), temp_data
)
896 elif new_chunk
.ID
== OBJECT_TRANS_MATRIX
:
897 # How do we know the matrix size? 54 == 4x4 48 == 4x3
898 temp_data
= file.read(SZ_4x3MAT
)
899 data
= list(struct
.unpack('<ffffffffffff', temp_data
))
900 new_chunk
.bytes_read
+= SZ_4x3MAT
901 contextMatrix
= mathutils
.Matrix(
902 (data
[:3] + [0], data
[3:6] + [0], data
[6:9] + [0], data
[9:] + [1])).transposed()
904 elif contextObName
and new_chunk
.ID
== OBJECT_LIGHT
: # Basic lamp support.
905 # no lamp in dict that would be confusing
906 # ...why not? just set CreateBlenderObject to False
907 newLamp
= bpy
.data
.lights
.new("Lamp", 'POINT')
908 contextLamp
= bpy
.data
.objects
.new(contextObName
, newLamp
)
909 context
.view_layer
.active_layer_collection
.collection
.objects
.link(contextLamp
)
910 imported_objects
.append(contextLamp
)
911 object_dictionary
[contextObName
] = contextLamp
912 temp_data
= file.read(SZ_3FLOAT
)
913 contextLamp
.location
= struct
.unpack('<3f', temp_data
)
914 new_chunk
.bytes_read
+= SZ_3FLOAT
915 contextMatrix
= None # Reset matrix
916 CreateBlenderObject
= False
917 CreateLightObject
= True
919 elif CreateLightObject
and new_chunk
.ID
== COLOR_F
: # color
920 temp_data
= file.read(SZ_3FLOAT
)
921 contextLamp
.data
.color
= struct
.unpack('<3f', temp_data
)
922 new_chunk
.bytes_read
+= SZ_3FLOAT
923 elif CreateLightObject
and new_chunk
.ID
== OBJECT_LIGHT_MULTIPLIER
: # intensity
924 temp_data
= file.read(SZ_FLOAT
)
925 contextLamp
.data
.energy
= (float(struct
.unpack('f', temp_data
)[0]) * 1000)
926 new_chunk
.bytes_read
+= SZ_FLOAT
928 elif CreateLightObject
and new_chunk
.ID
== OBJECT_LIGHT_SPOT
: # spotlight
929 temp_data
= file.read(SZ_3FLOAT
)
930 contextLamp
.data
.type = 'SPOT'
931 spot
= mathutils
.Vector(struct
.unpack('<3f', temp_data
))
932 aim
= contextLamp
.location
+ spot
933 hypo
= math
.copysign(math
.sqrt(pow(aim
[1], 2) + pow(aim
[0], 2)), aim
[1])
934 track
= math
.copysign(math
.sqrt(pow(hypo
, 2) + pow(spot
[2], 2)), aim
[1])
935 angle
= math
.radians(90) - math
.copysign(math
.acos(hypo
/ track
), aim
[2])
936 contextLamp
.rotation_euler
[0] = -1 * math
.copysign(angle
, aim
[1])
937 contextLamp
.rotation_euler
[2] = -1 * (math
.radians(90) - math
.acos(aim
[0] / hypo
))
938 new_chunk
.bytes_read
+= SZ_3FLOAT
939 temp_data
= file.read(SZ_FLOAT
) # hotspot
940 hotspot
= float(struct
.unpack('f', temp_data
)[0])
941 new_chunk
.bytes_read
+= SZ_FLOAT
942 temp_data
= file.read(SZ_FLOAT
) # angle
943 beam_angle
= float(struct
.unpack('f', temp_data
)[0])
944 contextLamp
.data
.spot_size
= math
.radians(beam_angle
)
945 contextLamp
.data
.spot_blend
= (1.0 - (hotspot
/ beam_angle
)) * 2
946 new_chunk
.bytes_read
+= SZ_FLOAT
947 elif CreateLightObject
and new_chunk
.ID
== OBJECT_LIGHT_ROLL
: # roll
948 temp_data
= file.read(SZ_FLOAT
)
949 contextLamp
.rotation_euler
[1] = float(struct
.unpack('f', temp_data
)[0])
950 new_chunk
.bytes_read
+= SZ_FLOAT
952 elif contextObName
and new_chunk
.ID
== OBJECT_CAMERA
and CreateCameraObject
is False: # Basic camera support
953 camera
= bpy
.data
.cameras
.new("Camera")
954 contextCamera
= bpy
.data
.objects
.new(contextObName
, camera
)
955 context
.view_layer
.active_layer_collection
.collection
.objects
.link(contextCamera
)
956 imported_objects
.append(contextCamera
)
957 object_dictionary
[contextObName
] = contextCamera
958 temp_data
= file.read(SZ_3FLOAT
)
959 contextCamera
.location
= struct
.unpack('<3f', temp_data
)
960 new_chunk
.bytes_read
+= SZ_3FLOAT
961 temp_data
= file.read(SZ_3FLOAT
)
962 target
= mathutils
.Vector(struct
.unpack('<3f', temp_data
))
963 cam
= contextCamera
.location
+ target
964 focus
= math
.copysign(math
.sqrt(pow(cam
[1], 2) + pow(cam
[0], 2)), cam
[1])
965 new_chunk
.bytes_read
+= SZ_3FLOAT
966 temp_data
= file.read(SZ_FLOAT
) # triangulating camera angles
967 direction
= math
.copysign(math
.sqrt(pow(focus
, 2) + pow(target
[2], 2)), cam
[1])
968 pitch
= math
.radians(90) - math
.copysign(math
.acos(focus
/ direction
), cam
[2])
969 contextCamera
.rotation_euler
[0] = -1 * math
.copysign(pitch
, cam
[1])
970 contextCamera
.rotation_euler
[1] = float(struct
.unpack('f', temp_data
)[0])
971 contextCamera
.rotation_euler
[2] = -1 * (math
.radians(90) - math
.acos(cam
[0] / focus
))
972 new_chunk
.bytes_read
+= SZ_FLOAT
973 temp_data
= file.read(SZ_FLOAT
)
974 contextCamera
.data
.lens
= float(struct
.unpack('f', temp_data
)[0])
975 new_chunk
.bytes_read
+= SZ_FLOAT
976 contextMatrix
= None # Reset matrix
977 CreateBlenderObject
= False
978 CreateCameraObject
= True
980 elif new_chunk
.ID
== EDITKEYFRAME
:
983 elif KEYFRAME
and new_chunk
.ID
== KFDATA_KFSEG
:
984 temp_data
= file.read(SZ_U_INT
)
985 start
= struct
.unpack('<I', temp_data
)[0]
986 new_chunk
.bytes_read
+= 4
987 context
.scene
.frame_start
= start
988 temp_data
= file.read(SZ_U_INT
)
989 stop
= struct
.unpack('<I', temp_data
)[0]
990 new_chunk
.bytes_read
+= 4
991 context
.scene
.frame_end
= stop
993 # including these here means their EK_OB_NODE_HEADER are scanned
994 # another object is being processed
995 elif new_chunk
.ID
in {KFDATA_OBJECT
, KFDATA_AMBIENT
, KFDATA_CAMERA
, KFDATA_OBJECT
, KFDATA_TARGET
, KFDATA_LIGHT
, KFDATA_L_TARGET
, }:
998 elif new_chunk
.ID
== OBJECT_NODE_HDR
:
999 object_name
, read_str_len
= read_string(file)
1000 new_chunk
.bytes_read
+= read_str_len
1001 temp_data
= file.read(SZ_U_SHORT
* 2)
1002 new_chunk
.bytes_read
+= 4
1003 temp_data
= file.read(SZ_U_SHORT
)
1004 hierarchy
= struct
.unpack('<H', temp_data
)[0]
1005 new_chunk
.bytes_read
+= 2
1006 child
= object_dictionary
.get(object_name
)
1007 colortrack
= 'LIGHT'
1009 if object_name
== '$AMBIENT$':
1010 child
= context
.scene
.world
1011 child
.use_nodes
= True
1012 colortrack
= 'AMBIENT'
1014 child
= bpy
.data
.objects
.new(object_name
, None) # Create an empty object
1015 context
.view_layer
.active_layer_collection
.collection
.objects
.link(child
)
1016 imported_objects
.append(child
)
1018 if object_name
!= '$AMBIENT$':
1019 object_list
.append(child
)
1020 object_parent
.append(hierarchy
)
1021 pivot_list
.append(mathutils
.Vector((0.0, 0.0, 0.0)))
1023 elif new_chunk
.ID
== OBJECT_INSTANCE_NAME
:
1024 object_name
, read_str_len
= read_string(file)
1025 if child
.name
== '$$$DUMMY':
1026 child
.name
= object_name
1028 child
.name
+= "." + object_name
1029 object_dictionary
[object_name
] = child
1030 new_chunk
.bytes_read
+= read_str_len
1032 elif new_chunk
.ID
== OBJECT_PIVOT
: # Pivot
1033 temp_data
= file.read(SZ_3FLOAT
)
1034 pivot
= struct
.unpack('<3f', temp_data
)
1035 new_chunk
.bytes_read
+= SZ_3FLOAT
1036 pivot_list
[len(pivot_list
) - 1] = mathutils
.Vector(pivot
)
1038 elif new_chunk
.ID
== MORPH_SMOOTH
and child
.type == 'MESH': # Smooth angle
1039 child
.data
.use_auto_smooth
= True
1040 temp_data
= file.read(SZ_FLOAT
)
1041 smooth_angle
= struct
.unpack('<f', temp_data
)[0]
1042 new_chunk
.bytes_read
+= SZ_FLOAT
1043 child
.data
.auto_smooth_angle
= math
.radians(smooth_angle
)
1045 elif KEYFRAME
and new_chunk
.ID
== COL_TRACK_TAG
and colortrack
== 'AMBIENT': # Ambient
1046 child
.node_tree
.nodes
['Background'].inputs
[0].default_value
[:3] = read_track_data(temp_chunk
)
1048 elif KEYFRAME
and new_chunk
.ID
== POS_TRACK_TAG
: # Translation
1049 child
.location
= read_track_data(temp_chunk
)
1051 elif KEYFRAME
and new_chunk
.ID
== ROT_TRACK_TAG
and child
.type == 'MESH': # Rotation
1052 new_chunk
.bytes_read
+= SZ_U_SHORT
* 5
1053 temp_data
= file.read(SZ_U_SHORT
* 5)
1054 temp_data
= file.read(SZ_U_INT
)
1055 nkeys
= struct
.unpack('<I', temp_data
)[0]
1056 new_chunk
.bytes_read
+= SZ_U_INT
1057 for i
in range(nkeys
):
1058 temp_data
= file.read(SZ_U_INT
)
1059 nframe
= struct
.unpack('<I', temp_data
)[0]
1060 new_chunk
.bytes_read
+= SZ_U_INT
1061 temp_data
= file.read(SZ_U_SHORT
)
1062 nflags
= struct
.unpack('<H', temp_data
)[0]
1063 new_chunk
.bytes_read
+= SZ_U_SHORT
1064 if nflags
> 0: # Check for spline term values
1065 temp_data
= file.read(SZ_FLOAT
)
1066 new_chunk
.bytes_read
+= SZ_FLOAT
1067 temp_data
= file.read(SZ_4FLOAT
)
1068 rad
, axis_x
, axis_y
, axis_z
= struct
.unpack("<4f", temp_data
)
1069 new_chunk
.bytes_read
+= SZ_4FLOAT
1070 if nkeys
> 1: # Read keyframe data
1071 for i
in range(nkeys
- 1):
1072 temp_data
= file.read(SZ_U_INT
)
1073 kframe
= struct
.unpack('<I', temp_data
)[0]
1074 new_chunk
.bytes_read
+= SZ_U_INT
1075 temp_data
= file.read(SZ_U_SHORT
)
1076 kflags
= struct
.unpack('<H', temp_data
)[0]
1077 new_chunk
.bytes_read
+= SZ_U_SHORT
1078 temp_data
= file.read(SZ_4FLOAT
)
1079 rotation
= struct
.unpack('<4f', temp_data
)
1080 new_chunk
.bytes_read
+= SZ_4FLOAT
1082 child
.rotation_euler
= mathutils
.Quaternion(
1083 (axis_x
, axis_y
, axis_z
), -rad
).to_euler() # why negative?
1085 elif KEYFRAME
and new_chunk
.ID
== SCL_TRACK_TAG
and child
.type == 'MESH': # Scale
1086 child
.scale
= read_track_data(temp_chunk
)
1088 elif KEYFRAME
and new_chunk
.ID
== COL_TRACK_TAG
and colortrack
== 'LIGHT': # Color
1089 child
.data
.color
= read_track_data(temp_chunk
)
1091 elif KEYFRAME
and new_chunk
.ID
== FOV_TRACK_TAG
and child
.type == 'CAMERA': # Field of view
1092 child
.data
.angle
= read_track_angle(temp_chunk
)
1094 elif KEYFRAME
and new_chunk
.ID
== ROLL_TRACK_TAG
and child
.type == 'CAMERA': # Roll angle
1095 child
.rotation_euler
[1] = read_track_angle(temp_chunk
)
1098 buffer_size
= new_chunk
.length
- new_chunk
.bytes_read
1099 binary_format
= "%ic" % buffer_size
1100 temp_data
= file.read(struct
.calcsize(binary_format
))
1101 new_chunk
.bytes_read
+= buffer_size
1103 # update the previous chunk bytes read
1104 previous_chunk
.bytes_read
+= new_chunk
.bytes_read
1107 # There will be a number of objects still not added
1108 if CreateBlenderObject
:
1109 if CreateLightObject
or CreateCameraObject
:
1117 contextMeshMaterials
,
1122 # Assign parents to objects
1123 # check _if_ we need to assign first because doing so recalcs the depsgraph
1124 for ind
, ob
in enumerate(object_list
):
1125 parent
= object_parent
[ind
]
1126 if parent
== ROOT_OBJECT
:
1127 if ob
.parent
is not None:
1130 if ob
.parent
!= object_list
[parent
]:
1131 if ob
== object_list
[parent
]:
1132 print(' warning: Cannot assign self to parent ', ob
)
1134 ob
.parent
= object_list
[parent
]
1136 # pivot_list[ind] += pivot_list[parent] # XXX, not sure this is correct, should parent space matrix be applied before combining?
1138 for ind
, ob
in enumerate(object_list
):
1139 if ob
.type == 'MESH':
1140 pivot
= pivot_list
[ind
]
1141 pivot_matrix
= object_matrix
.get(ob
, mathutils
.Matrix()) # unlikely to fail
1142 pivot_matrix
= mathutils
.Matrix
.Translation(-1 * pivot
)
1143 # pivot_matrix = mathutils.Matrix.Translation(pivot_matrix.to_3x3() @ -pivot)
1144 ob
.data
.transform(pivot_matrix
)
1147 def load_3ds(filepath
,
1149 IMPORT_CONSTRAIN_BOUNDS
=10.0,
1154 global_matrix
=None):
1157 # if BPyMessages.Error_NoFile(filepath):
1160 print("importing 3DS: %r..." % (filepath
), end
="")
1162 if bpy
.ops
.object.select_all
.poll():
1163 bpy
.ops
.object.select_all(action
='DESELECT')
1166 # time1 = Blender.sys.time()
1168 current_chunk
= Chunk()
1170 file = open(filepath
, 'rb')
1173 # print 'reading the first chunk'
1174 read_chunk(file, current_chunk
)
1175 if current_chunk
.ID
!= PRIMARY
:
1176 print('\tFatal Error: Not a valid 3ds file: %r' % filepath
)
1180 if IMPORT_CONSTRAIN_BOUNDS
:
1181 BOUNDS_3DS
[:] = [1 << 30, 1 << 30, 1 << 30, -1 << 30, -1 << 30, -1 << 30]
1187 # fixme, make unglobal, clear in case
1188 object_dictionary
.clear()
1189 object_matrix
.clear()
1193 imported_objects
= [] # Fill this list with objects
1194 process_next_chunk(context
, file, current_chunk
, imported_objects
, IMAGE_SEARCH
, WORLD_MATRIX
, KEYFRAME
)
1196 # fixme, make unglobal
1197 object_dictionary
.clear()
1198 object_matrix
.clear()
1200 # Link the objects into this scene.
1201 # Layers = scn.Layers
1203 # REMOVE DUMMYVERT, - remove this in the next release when blenders internal are fixed.
1206 for ob
in imported_objects
:
1207 if ob
.type == 'MESH':
1209 me
.transform(ob
.matrix_local
.inverted())
1211 # print(imported_objects)
1213 for ob
in imported_objects
:
1214 if ob
.type == 'MESH' and ob
.parent
is None:
1215 ob
.matrix_world
= ob
.matrix_world
@ global_matrix
1217 for ob
in imported_objects
:
1219 if not APPLY_MATRIX
: # Reset transform
1220 bpy
.ops
.object.rotation_clear()
1221 bpy
.ops
.object.location_clear()
1225 if IMPORT_AS_INSTANCE:
1226 name = filepath.split('\\')[-1].split('/')[-1]
1227 # Create a group for this import.
1228 group_scn = Scene.New(name)
1229 for ob in imported_objects:
1230 group_scn.link(ob) # dont worry about the layers
1232 grp = Blender.Group.New(name)
1233 grp.objects = imported_objects
1235 grp_ob = Object.New('Empty', name)
1236 grp_ob.enableDupGroup = True
1237 grp_ob.DupGroup = grp
1239 grp_ob.Layers = Layers
1242 # Select all imported objects.
1243 for ob in imported_objects:
1249 context
.view_layer
.update()
1251 axis_min
= [1000000000] * 3
1252 axis_max
= [-1000000000] * 3
1253 global_clamp_size
= IMPORT_CONSTRAIN_BOUNDS
1254 if global_clamp_size
!= 0.0:
1255 # Get all object bounds
1256 for ob
in imported_objects
:
1257 for v
in ob
.bound_box
:
1258 for axis
, value
in enumerate(v
):
1259 if axis_min
[axis
] > value
:
1260 axis_min
[axis
] = value
1261 if axis_max
[axis
] < value
:
1262 axis_max
[axis
] = value
1265 max_axis
= max(axis_max
[0] - axis_min
[0],
1266 axis_max
[1] - axis_min
[1],
1267 axis_max
[2] - axis_min
[2])
1270 while global_clamp_size
< max_axis
* scale
:
1271 scale
= scale
/ 10.0
1273 scale_mat
= mathutils
.Matrix
.Scale(scale
, 4)
1275 for obj
in imported_objects
:
1276 if obj
.parent
is None:
1277 obj
.matrix_world
= scale_mat
@ obj
.matrix_world
1279 # Select all new objects.
1280 print(" done in %.4f sec." % (time
.time() - time1
))
1288 use_image_search
=True,
1289 use_world_matrix
=False,
1291 use_apply_transform
=True,
1297 IMPORT_CONSTRAIN_BOUNDS
=constrain_size
,
1298 IMAGE_SEARCH
=use_image_search
,
1299 WORLD_MATRIX
=use_world_matrix
,
1300 KEYFRAME
=read_keyframe
,
1301 APPLY_MATRIX
=use_apply_transform
,
1302 global_matrix
=global_matrix
,