source: other-projects/playing-in-the-street/summer-2013/trunk/Playing-in-the-Street-WPF/Content/Web/mrdoob-three.js-4862f5f/utils/converters/fbx/convert_to_threejs.py@ 28897

Last change on this file since 28897 was 28897, checked in by davidb, 10 years ago

GUI front-end to server base plus web page content

File size: 66.0 KB
Line 
1# @author zfedoran / http://github.com/zfedoran
2
3import os
4import sys
5import math
6import operator
7
8# #####################################################
9# Globals
10# #####################################################
11option_triangulate = True
12option_textures = True
13option_prefix = True
14option_geometry = False
15option_default_camera = False
16option_default_light = False
17
18converter = None
19global_up_vector = None
20
21# #####################################################
22# Templates
23# #####################################################
24def Vector2String(v, no_brackets = False):
25 if no_brackets:
26 return '%g,%g' % (v[0], v[1])
27 else:
28 return '[ %g, %g ]' % (v[0], v[1])
29
30def Vector3String(v, no_brackets = False):
31 if no_brackets:
32 return '%g,%g,%g' % (v[0], v[1], v[2])
33 else:
34 return '[ %g, %g, %g ]' % (v[0], v[1], v[2])
35
36def ColorString(c, no_brackets = False):
37 if no_brackets:
38 return '%g, %g, %g' % (c[0], c[1], c[2])
39 else:
40 return '[ %g, %g, %g ]' % (c[0], c[1], c[2])
41
42def LabelString(s):
43 return '"%s"' % s
44
45def ArrayString(s):
46 return '[ %s ]' % s
47
48def PaddingString(n):
49 output = ""
50 for i in range(n):
51 output += "\t"
52 return output
53
54def BoolString(value):
55 if value:
56 return "true"
57 return "false"
58
59# #####################################################
60# Helpers
61# #####################################################
62def getObjectName(o, force_prefix = False):
63 if not o:
64 return ""
65 prefix = ""
66 if option_prefix or force_prefix:
67 prefix = "Object_%s_" % o.GetUniqueID()
68 return prefix + o.GetName()
69
70def getGeometryName(g, force_prefix = False):
71 prefix = ""
72 if option_prefix or force_prefix:
73 prefix = "Geometry_%s_" % g.GetUniqueID()
74 return prefix + g.GetName()
75
76def getEmbedName(e, force_prefix = False):
77 prefix = ""
78 if option_prefix or force_prefix:
79 prefix = "Embed_%s_" % e.GetUniqueID()
80 return prefix + e.GetName()
81
82def getMaterialName(m, force_prefix = False):
83 prefix = ""
84 if option_prefix or force_prefix:
85 prefix = "Material_%s_" % m.GetUniqueID()
86 return prefix + m.GetName()
87
88def getTextureName(t, force_prefix = False):
89 texture_file = t.GetFileName()
90 texture_id = os.path.splitext(os.path.basename(texture_file))[0]
91 prefix = ""
92 if option_prefix or force_prefix:
93 prefix = "Texture_%s_" % t.GetUniqueID()
94 return prefix + texture_id
95
96def getFogName(f, force_prefix = False):
97 prefix = ""
98 if option_prefix or force_prefix:
99 prefix = "Fog_%s_" % f.GetUniqueID()
100 return prefix + f.GetName()
101
102def getObjectVisible(n):
103 return BoolString(True)
104
105def getRadians(v):
106 return ((v[0]*math.pi)/180, (v[1]*math.pi)/180, (v[2]*math.pi)/180)
107
108def getHex(c):
109 color = (int(c[0]*255) << 16) + (int(c[1]*255) << 8) + int(c[2]*255)
110 return color
111
112def setBit(value, position, on):
113 if on:
114 mask = 1 << position
115 return (value | mask)
116 else:
117 mask = ~(1 << position)
118 return (value & mask)
119
120def convert_fbx_color(color):
121 return [color.mRed, color.mGreen, color.mBlue, color.mAlpha]
122
123def convert_fbx_vec2(v):
124 return [v[0], v[1]]
125
126def convert_fbx_vec3(v):
127 return [v[0], v[1], v[2]]
128
129def generate_uvs(uv_layers):
130 layers = []
131 for uvs in uv_layers:
132 layer = ",".join(Vector2String(n, True) for n in uvs)
133 layers.append(layer)
134
135 return ",".join("[%s]" % n for n in layers)
136
137def generateMultiLineString(lines, separator, padding):
138 cleanLines = []
139 for i in range(len(lines)):
140 line = lines[i]
141 line = PaddingString(padding) + line
142 cleanLines.append(line)
143 return separator.join(cleanLines)
144
145def get_up_vector(scene):
146 global_settings = scene.GetGlobalSettings()
147 axis_system = global_settings.GetAxisSystem()
148 up_vector = axis_system.GetUpVector()
149 tmp = [0,0,0]
150 tmp[up_vector[0] - 1] = up_vector[1] * 1
151 return FbxVector4(tmp[0], tmp[1], tmp[2], 1)
152
153def generate_bounding_box(vertices):
154 minx = 0
155 miny = 0
156 minz = 0
157 maxx = 0
158 maxy = 0
159 maxz = 0
160
161 for vertex in vertices:
162 if vertex[0] < minx:
163 minx = vertex[0]
164 if vertex[1] < miny:
165 miny = vertex[1]
166 if vertex[2] < minz:
167 minz = vertex[2]
168
169 if vertex[0] > maxx:
170 maxx = vertex[0]
171 if vertex[1] > maxy:
172 maxy = vertex[1]
173 if vertex[2] > maxz:
174 maxz = vertex[2]
175
176 return [minx, miny, minz], [maxx, maxy, maxz]
177
178
179# #####################################################
180# Generate - Triangles
181# #####################################################
182def triangulate_node_hierarchy(node):
183 node_attribute = node.GetNodeAttribute();
184
185 if node_attribute:
186 if node_attribute.GetAttributeType() == FbxNodeAttribute.eMesh or \
187 node_attribute.GetAttributeType() == FbxNodeAttribute.eNurbs or \
188 node_attribute.GetAttributeType() == FbxNodeAttribute.eNurbsSurface or \
189 node_attribute.GetAttributeType() == FbxNodeAttribute.ePatch:
190 converter.TriangulateInPlace(node);
191
192 child_count = node.GetChildCount()
193 for i in range(child_count):
194 triangulate_node_hierarchy(node.GetChild(i))
195
196def triangulate_scene(scene):
197 node = scene.GetRootNode()
198 if node:
199 for i in range(node.GetChildCount()):
200 triangulate_node_hierarchy(node.GetChild(i))
201
202# #####################################################
203# Generate - Material String
204# #####################################################
205def generate_texture_bindings(material_property, texture_list):
206 binding_types = {
207 "DiffuseColor": "map", "DiffuseFactor": "diffuseFactor", "EmissiveColor": "emissiveMap",
208 "EmissiveFactor": "emissiveFactor", "AmbientColor": "ambientMap", "AmbientFactor": "ambientFactor",
209 "SpecularColor": "specularMap", "SpecularFactor": "specularFactor", "ShininessExponent": "shininessExponent",
210 "NormalMap": "normalMap", "Bump": "bumpMap", "TransparentColor": "transparentMap",
211 "TransparencyFactor": "transparentFactor", "ReflectionColor": "reflectionMap",
212 "ReflectionFactor": "reflectionFactor", "DisplacementColor": "displacementMap",
213 "VectorDisplacementColor": "vectorDisplacementMap"
214 }
215
216 if material_property.IsValid():
217 #Here we have to check if it's layeredtextures, or just textures:
218 layered_texture_count = material_property.GetSrcObjectCount(FbxLayeredTexture.ClassId)
219 if layered_texture_count > 0:
220 for j in range(layered_texture_count):
221 layered_texture = material_property.GetSrcObject(FbxLayeredTexture.ClassId, j)
222 texture_count = layered_texture.GetSrcObjectCount(FbxTexture.ClassId)
223 for k in range(texture_count):
224 texture = layered_texture.GetSrcObject(FbxTexture.ClassId,k)
225 if texture:
226 texture_id = getTextureName(texture, True)
227 texture_binding = ' "%s": "%s",' % (binding_types[str(material_property.GetName())], texture_id)
228 texture_list.append(texture_binding)
229 else:
230 # no layered texture simply get on the property
231 texture_count = material_property.GetSrcObjectCount(FbxTexture.ClassId)
232 for j in range(texture_count):
233 texture = material_property.GetSrcObject(FbxTexture.ClassId,j)
234 if texture:
235 texture_id = getTextureName(texture, True)
236 texture_binding = ' "%s": "%s",' % (binding_types[str(material_property.GetName())], texture_id)
237 texture_list.append(texture_binding)
238
239def generate_material_string(material):
240 #Get the implementation to see if it's a hardware shader.
241 implementation = GetImplementation(material, "ImplementationHLSL")
242 implementation_type = "HLSL"
243 if not implementation:
244 implementation = GetImplementation(material, "ImplementationCGFX")
245 implementation_type = "CGFX"
246
247 output = []
248
249 if implementation:
250 # This material is a hardware shader, skip it
251 print("Shader materials are not supported")
252 return ''
253
254 elif material.GetClassId().Is(FbxSurfaceLambert.ClassId):
255
256 ambient = str(getHex(material.Ambient.Get()))
257 diffuse = str(getHex(material.Diffuse.Get()))
258 emissive = str(getHex(material.Emissive.Get()))
259 opacity = 1.0 - material.TransparencyFactor.Get()
260 opacity = 1.0 if opacity == 0 else opacity
261 opacity = str(opacity)
262 transparent = BoolString(False)
263 reflectivity = "1"
264
265 output = [
266
267 '\t' + LabelString( getMaterialName( material ) ) + ': {',
268 ' "type" : "MeshLambertMaterial",',
269 ' "parameters" : {',
270 ' "color" : ' + diffuse + ',',
271 ' "ambient" : ' + ambient + ',',
272 ' "emissive" : ' + emissive + ',',
273 ' "reflectivity" : ' + reflectivity + ',',
274 ' "transparent" : ' + transparent + ',',
275 ' "opacity" : ' + opacity + ',',
276
277 ]
278
279 elif material.GetClassId().Is(FbxSurfacePhong.ClassId):
280
281 ambient = str(getHex(material.Ambient.Get()))
282 diffuse = str(getHex(material.Diffuse.Get()))
283 emissive = str(getHex(material.Emissive.Get()))
284 specular = str(getHex(material.Specular.Get()))
285 opacity = 1.0 - material.TransparencyFactor.Get()
286 opacity = 1.0 if opacity == 0 else opacity
287 opacity = str(opacity)
288 shininess = str(material.Shininess.Get())
289 transparent = BoolString(False)
290 reflectivity = "1"
291 bumpScale = "1"
292
293 output = [
294
295 '\t' + LabelString( getMaterialName( material ) ) + ': {',
296 ' "type" : "MeshPhongMaterial",',
297 ' "parameters" : {',
298 ' "color" : ' + diffuse + ',',
299 ' "ambient" : ' + ambient + ',',
300 ' "emissive" : ' + emissive + ',',
301 ' "specular" : ' + specular + ',',
302 ' "shininess" : ' + shininess + ',',
303 ' "bumpScale" : ' + bumpScale + ',',
304 ' "reflectivity" : ' + reflectivity + ',',
305 ' "transparent" : ' + transparent + ',',
306 ' "opacity" : ' + opacity + ',',
307
308 ]
309
310 else:
311 print("Unknown type of Material")
312 return ''
313
314 if option_textures:
315 texture_list = []
316 texture_count = FbxLayerElement.sTypeTextureCount()
317 for texture_index in range(texture_count):
318 material_property = material.FindProperty(FbxLayerElement.sTextureChannelNames(texture_index))
319 generate_texture_bindings(material_property, texture_list)
320
321 output += texture_list
322
323 wireframe = BoolString(False)
324 wireframeLinewidth = "1"
325
326 output.append(' "wireframe" : ' + wireframe + ',')
327 output.append(' "wireframeLinewidth" : ' + wireframeLinewidth)
328 output.append(' }')
329 output.append('}')
330
331 return generateMultiLineString( output, '\n\t\t', 0 )
332
333def generate_proxy_material_string(node, material_names):
334
335 output = [
336
337 '\t' + LabelString( getMaterialName( node, True ) ) + ': {',
338 ' "type" : "MeshFaceMaterial",',
339 ' "parameters" : {',
340 ' "materials" : ' + ArrayString( ",".join(LabelString(m) for m in material_names) ),
341 ' }',
342 '}'
343
344 ]
345
346 return generateMultiLineString( output, '\n\t\t', 0 )
347
348# #####################################################
349# Parse - Materials
350# #####################################################
351def extract_materials_from_node(node, material_list):
352 name = node.GetName()
353 mesh = node.GetNodeAttribute()
354
355 node = None
356 if mesh:
357 node = mesh.GetNode()
358 if node:
359 material_count = node.GetMaterialCount()
360
361 material_names = []
362 for l in range(mesh.GetLayerCount()):
363 materials = mesh.GetLayer(l).GetMaterials()
364 if materials:
365 if materials.GetReferenceMode() == FbxLayerElement.eIndex:
366 #Materials are in an undefined external table
367 continue
368 for i in range(material_count):
369 material = node.GetMaterial(i)
370 material_names.append(getMaterialName(material))
371 material_string = generate_material_string(material)
372 material_list.append(material_string)
373
374 if material_count > 1:
375 proxy_material = generate_proxy_material_string(node, material_names)
376 material_list.append(proxy_material)
377
378
379def generate_materials_from_hierarchy(node, material_list):
380 if node.GetNodeAttribute() == None:
381 pass
382 else:
383 attribute_type = (node.GetNodeAttribute().GetAttributeType())
384 if attribute_type == FbxNodeAttribute.eMesh:
385 extract_materials_from_node(node, material_list)
386 for i in range(node.GetChildCount()):
387 generate_materials_from_hierarchy(node.GetChild(i), material_list)
388
389def generate_material_list(scene):
390 material_list = []
391 node = scene.GetRootNode()
392 if node:
393 for i in range(node.GetChildCount()):
394 generate_materials_from_hierarchy(node.GetChild(i), material_list)
395 return material_list
396
397# #####################################################
398# Generate - Texture String
399# #####################################################
400def generate_texture_string(texture):
401
402 #TODO: extract more texture properties
403 wrap_u = texture.GetWrapModeU()
404 wrap_v = texture.GetWrapModeV()
405 offset = texture.GetUVTranslation()
406
407 output = [
408
409 '\t' + LabelString( getTextureName( texture, True ) ) + ': {',
410 ' "url" : "' + texture.GetFileName() + '",',
411 ' "repeat" : ' + Vector2String( (1,1) ) + ',',
412 ' "offset" : ' + Vector2String( texture.GetUVTranslation() ) + ',',
413 ' "magFilter" : ' + LabelString( "LinearFilter" ) + ',',
414 ' "minFilter" : ' + LabelString( "LinearMipMapLinearFilter" ) + ',',
415 ' "anisotropy" : ' + BoolString( True ),
416 '}'
417
418 ]
419
420 return generateMultiLineString( output, '\n\t\t', 0 )
421
422# #####################################################
423# Parse - Textures
424# #####################################################
425def extract_material_textures(material_property, texture_list):
426 if material_property.IsValid():
427 #Here we have to check if it's layeredtextures, or just textures:
428 layered_texture_count = material_property.GetSrcObjectCount(FbxLayeredTexture.ClassId)
429 if layered_texture_count > 0:
430 for j in range(layered_texture_count):
431 layered_texture = material_property.GetSrcObject(FbxLayeredTexture.ClassId, j)
432 texture_count = layered_texture.GetSrcObjectCount(FbxTexture.ClassId)
433 for k in range(texture_count):
434 texture = layered_texture.GetSrcObject(FbxTexture.ClassId,k)
435 if texture:
436 texture_string = generate_texture_string(texture)
437 texture_list.append(texture_string)
438 else:
439 # no layered texture simply get on the property
440 texture_count = material_property.GetSrcObjectCount(FbxTexture.ClassId)
441 for j in range(texture_count):
442 texture = material_property.GetSrcObject(FbxTexture.ClassId,j)
443 if texture:
444 texture_string = generate_texture_string(texture)
445 texture_list.append(texture_string)
446
447def extract_textures_from_node(node, texture_list):
448 name = node.GetName()
449 mesh = node.GetNodeAttribute()
450
451 #for all materials attached to this mesh
452 material_count = mesh.GetNode().GetSrcObjectCount(FbxSurfaceMaterial.ClassId)
453 for material_index in range(material_count):
454 material = mesh.GetNode().GetSrcObject(FbxSurfaceMaterial.ClassId, material_index)
455
456 #go through all the possible textures types
457 if material:
458 texture_count = FbxLayerElement.sTypeTextureCount()
459 for texture_index in range(texture_count):
460 material_property = material.FindProperty(FbxLayerElement.sTextureChannelNames(texture_index))
461 extract_material_textures(material_property, texture_list)
462
463def generate_textures_from_hierarchy(node, texture_list):
464 if node.GetNodeAttribute() == None:
465 pass
466 else:
467 attribute_type = (node.GetNodeAttribute().GetAttributeType())
468 if attribute_type == FbxNodeAttribute.eMesh:
469 extract_textures_from_node(node, texture_list)
470 for i in range(node.GetChildCount()):
471 generate_textures_from_hierarchy(node.GetChild(i), texture_list)
472
473def generate_texture_list(scene):
474 if not option_textures:
475 return []
476
477 texture_list = []
478 node = scene.GetRootNode()
479 if node:
480 for i in range(node.GetChildCount()):
481 generate_textures_from_hierarchy(node.GetChild(i), texture_list)
482 return texture_list
483
484# #####################################################
485# Extract - Fbx Mesh data
486# #####################################################
487def extract_fbx_vertex_positions(mesh):
488 control_points_count = mesh.GetControlPointsCount()
489 control_points = mesh.GetControlPoints()
490
491 positions = []
492 for i in range(control_points_count):
493 positions.append(convert_fbx_vec3(control_points[i]))
494
495 node = mesh.GetNode()
496 if node and option_geometry:
497 # FbxMeshes are local to their node, we need the vertices in global space
498 # when scene nodes are not exported
499 transform = node.EvaluateGlobalTransform()
500 transform = FbxMatrix(transform)
501
502 for i in range(len(positions)):
503 v = positions[i]
504 position = FbxVector4(v[0], v[1], v[2])
505 position = transform.MultNormalize(position)
506 positions[i] = convert_fbx_vec3(position)
507
508 return positions
509
510def extract_fbx_vertex_normals(mesh):
511# eNone The mapping is undetermined.
512# eByControlPoint There will be one mapping coordinate for each surface control point/vertex.
513# eByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part.
514# eByPolygon There can be only one mapping coordinate for the whole polygon.
515# eByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements.
516# eAllSame There can be only one mapping coordinate for the whole surface.
517
518 layered_normal_indices = []
519 layered_normal_values = []
520
521 poly_count = mesh.GetPolygonCount()
522 control_points = mesh.GetControlPoints()
523
524 for l in range(mesh.GetLayerCount()):
525 mesh_normals = mesh.GetLayer(l).GetNormals()
526 if not mesh_normals:
527 continue
528
529 normals_array = mesh_normals.GetDirectArray()
530 normals_count = normals_array.GetCount()
531
532 if normals_count == 0:
533 continue
534
535 normal_indices = []
536 normal_values = []
537
538 # values
539 for i in range(normals_count):
540 normal = convert_fbx_vec3(normals_array.GetAt(i))
541 normal_values.append(normal)
542
543 node = mesh.GetNode()
544 if node and option_geometry:
545 # FbxMeshes are local to their node, we need the normals in global space
546 # when scene nodes are not exported
547 transform = node.EvaluateGlobalTransform()
548 transform.SetT(FbxVector4(0,0,0,0))
549 transform = FbxMatrix(transform)
550
551 for i in range(len(normal_values)):
552 n = normal_values[i]
553 normal = FbxVector4(n[0], n[1], n[2])
554 normal = transform.MultNormalize(normal)
555 normal_values[i] = convert_fbx_vec3(normal)
556
557 # indices
558 vertexId = 0
559 for p in range(poly_count):
560 poly_size = mesh.GetPolygonSize(p)
561 poly_normals = []
562
563 for v in range(poly_size):
564 control_point_index = mesh.GetPolygonVertex(p, v)
565
566 if mesh_normals.GetMappingMode() == FbxLayerElement.eByControlPoint:
567 if mesh_normals.GetReferenceMode() == FbxLayerElement.eDirect:
568 poly_normals.append(control_point_index)
569 elif mesh_normals.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
570 index = mesh_normals.GetIndexArray().GetAt(control_point_index)
571 poly_normals.append(index)
572 elif mesh_normals.GetMappingMode() == FbxLayerElement.eByPolygonVertex:
573 if mesh_normals.GetReferenceMode() == FbxLayerElement.eDirect:
574 poly_normals.append(vertexId)
575 elif mesh_normals.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
576 index = mesh_normals.GetIndexArray().GetAt(vertexId)
577 poly_normals.append(index)
578 elif mesh_normals.GetMappingMode() == FbxLayerElement.eByPolygon or \
579 mesh_normals.GetMappingMode() == FbxLayerElement.eAllSame or \
580 mesh_normals.GetMappingMode() == FbxLayerElement.eNone:
581 print("unsupported normal mapping mode for polygon vertex")
582
583 vertexId += 1
584 normal_indices.append(poly_normals)
585
586 layered_normal_values.append(normal_values)
587 layered_normal_indices.append(normal_indices)
588
589 normal_values = []
590 normal_indices = []
591
592 # Three.js only supports one layer of normals
593 if len(layered_normal_values) > 0:
594 normal_values = layered_normal_values[0]
595 normal_indices = layered_normal_indices[0]
596
597 return normal_values, normal_indices
598
599def extract_fbx_vertex_colors(mesh):
600# eNone The mapping is undetermined.
601# eByControlPoint There will be one mapping coordinate for each surface control point/vertex.
602# eByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part.
603# eByPolygon There can be only one mapping coordinate for the whole polygon.
604# eByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements.
605# eAllSame There can be only one mapping coordinate for the whole surface.
606
607 layered_color_indices = []
608 layered_color_values = []
609
610 poly_count = mesh.GetPolygonCount()
611 control_points = mesh.GetControlPoints()
612
613 for l in range(mesh.GetLayerCount()):
614 mesh_colors = mesh.GetLayer(l).GetVertexColors()
615 if not mesh_colors:
616 continue
617
618 colors_array = mesh_colors.GetDirectArray()
619 colors_count = colors_array.GetCount()
620
621 if colors_count == 0:
622 continue
623
624 color_indices = []
625 color_values = []
626
627 # values
628 for i in range(colors_count):
629 color = convert_fbx_color(colors_array.GetAt(i))
630 color_values.append(color)
631
632 # indices
633 vertexId = 0
634 for p in range(poly_count):
635 poly_size = mesh.GetPolygonSize(p)
636 poly_colors = []
637
638 for v in range(poly_size):
639 control_point_index = mesh.GetPolygonVertex(p, v)
640
641 if mesh_colors.GetMappingMode() == FbxLayerElement.eByControlPoint:
642 if mesh_colors.GetReferenceMode() == FbxLayerElement.eDirect:
643 poly_colors.append(control_point_index)
644 elif mesh_colors.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
645 index = mesh_colors.GetIndexArray().GetAt(control_point_index)
646 poly_colors.append(index)
647 elif mesh_colors.GetMappingMode() == FbxLayerElement.eByPolygonVertex:
648 if mesh_colors.GetReferenceMode() == FbxLayerElement.eDirect:
649 poly_colors.append(vertexId)
650 elif mesh_colors.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
651 index = mesh_colors.GetIndexArray().GetAt(vertexId)
652 poly_colors.append(index)
653 elif mesh_colors.GetMappingMode() == FbxLayerElement.eByPolygon or \
654 mesh_colors.GetMappingMode() == FbxLayerElement.eAllSame or \
655 mesh_colors.GetMappingMode() == FbxLayerElement.eNone:
656 print("unsupported color mapping mode for polygon vertex")
657
658 vertexId += 1
659 color_indices.append(poly_colors)
660
661 color_values = []
662 color_indices = []
663
664 # Three.js only supports one layer of colors
665 if len(layered_color_values) > 0:
666 color_values = layered_color_values[0]
667 color_indices = layered_color_indices[0]
668
669 return color_values, color_indices
670
671def extract_fbx_vertex_uvs(mesh):
672# eNone The mapping is undetermined.
673# eByControlPoint There will be one mapping coordinate for each surface control point/vertex.
674# eByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part.
675# eByPolygon There can be only one mapping coordinate for the whole polygon.
676# eByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements.
677# eAllSame There can be only one mapping coordinate for the whole surface.
678
679 layered_uv_indices = []
680 layered_uv_values = []
681
682 poly_count = mesh.GetPolygonCount()
683 control_points = mesh.GetControlPoints()
684
685 for l in range(mesh.GetLayerCount()):
686 mesh_uvs = mesh.GetLayer(l).GetUVs()
687 if not mesh_uvs:
688 continue
689
690 uvs_array = mesh_uvs.GetDirectArray()
691 uvs_count = uvs_array.GetCount()
692
693 if uvs_count == 0:
694 continue
695
696 uv_indices = []
697 uv_values = []
698
699 # values
700 for i in range(uvs_count):
701 uv = convert_fbx_vec2(uvs_array.GetAt(i))
702 uv_values.append(uv)
703
704 # indices
705 vertexId = 0
706 for p in range(poly_count):
707 poly_size = mesh.GetPolygonSize(p)
708 poly_uvs = []
709
710 for v in range(poly_size):
711 control_point_index = mesh.GetPolygonVertex(p, v)
712
713 if mesh_uvs.GetMappingMode() == FbxLayerElement.eByControlPoint:
714 if mesh_uvs.GetReferenceMode() == FbxLayerElement.eDirect:
715 poly_uvs.append(control_point_index)
716 elif mesh_uvs.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
717 index = mesh_uvs.GetIndexArray().GetAt(control_point_index)
718 poly_uvs.append(index)
719 elif mesh_uvs.GetMappingMode() == FbxLayerElement.eByPolygonVertex:
720 uv_texture_index = mesh.GetTextureUVIndex(p, v)
721 if mesh_uvs.GetReferenceMode() == FbxLayerElement.eDirect or \
722 mesh_uvs.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
723 poly_uvs.append(uv_texture_index)
724 elif mesh_uvs.GetMappingMode() == FbxLayerElement.eByPolygon or \
725 mesh_uvs.GetMappingMode() == FbxLayerElement.eAllSame or \
726 mesh_uvs.GetMappingMode() == FbxLayerElement.eNone:
727 print("unsupported uv mapping mode for polygon vertex")
728
729 vertexId += 1
730 uv_indices.append(poly_uvs)
731
732 layered_uv_values.append(uv_values)
733 layered_uv_indices.append(uv_indices)
734
735 return layered_uv_values, layered_uv_indices
736
737# #####################################################
738# Generate - Mesh String (for scene output)
739# #####################################################
740def generate_mesh_string_for_scene_output(node):
741 mesh = node.GetNodeAttribute()
742 mesh_list = [ mesh ]
743
744 vertices, vertex_offsets = process_mesh_vertices(mesh_list)
745 materials, material_offsets = process_mesh_materials(mesh_list)
746
747 normals_to_indices = generate_unique_normals_dictionary(mesh_list)
748 colors_to_indices = generate_unique_colors_dictionary(mesh_list)
749 uvs_to_indices_list = generate_unique_uvs_dictionary_layers(mesh_list)
750
751 normal_values = generate_normals_from_dictionary(normals_to_indices)
752 color_values = generate_colors_from_dictionary(colors_to_indices)
753 uv_values = generate_uvs_from_dictionary_layers(uvs_to_indices_list)
754
755 faces = process_mesh_polygons(mesh_list,
756 normals_to_indices,
757 colors_to_indices,
758 uvs_to_indices_list,
759 vertex_offsets,
760 material_offsets)
761
762 nuvs = []
763 for layer_index, uvs in enumerate(uv_values):
764 nuvs.append(str(len(uvs)))
765
766 nvertices = len(vertices)
767 nnormals = len(normal_values)
768 ncolors = len(color_values)
769 nfaces = len(faces)
770 nuvs = ",".join(nuvs)
771
772 aabb_min, aabb_max = generate_bounding_box(vertices)
773 aabb_min = ",".join(str(f) for f in aabb_min)
774 aabb_max = ",".join(str(f) for f in aabb_max)
775
776 vertices = ",".join(Vector3String(v, True) for v in vertices)
777 normals = ",".join(Vector3String(v, True) for v in normal_values)
778 colors = ",".join(Vector3String(v, True) for v in color_values)
779 faces = ",".join(faces)
780 uvs = generate_uvs(uv_values)
781
782 output = [
783
784 '\t' + LabelString( getEmbedName( node, True ) ) + ' : {',
785 ' "metadata" : {',
786 ' "vertices" : ' + str(nvertices) + ',',
787 ' "normals" : ' + str(nnormals) + ',',
788 ' "colors" : ' + str(ncolors) + ',',
789 ' "faces" : ' + str(nfaces) + ',',
790 ' "uvs" : ' + ArrayString(nuvs),
791 ' },',
792 ' "boundingBox" : {',
793 ' "min" : ' + ArrayString(aabb_min) + ',',
794 ' "max" : ' + ArrayString(aabb_max),
795 ' },',
796 ' "scale" : ' + str( 1 ) + ',',
797 ' "materials" : ' + ArrayString("") + ',',
798 ' "vertices" : ' + ArrayString(vertices) + ',',
799 ' "normals" : ' + ArrayString(normals) + ',',
800 ' "colors" : ' + ArrayString(colors) + ',',
801 ' "uvs" : ' + ArrayString(uvs) + ',',
802 ' "faces" : ' + ArrayString(faces),
803 '}'
804
805 ]
806
807 return generateMultiLineString( output, '\n\t\t', 0 )
808
809# #####################################################
810# Generate - Mesh String (for non-scene output)
811# #####################################################
812def generate_mesh_string_for_non_scene_output(scene):
813 mesh_list = generate_mesh_list(scene)
814
815 vertices, vertex_offsets = process_mesh_vertices(mesh_list)
816 materials, material_offsets = process_mesh_materials(mesh_list)
817
818 normals_to_indices = generate_unique_normals_dictionary(mesh_list)
819 colors_to_indices = generate_unique_colors_dictionary(mesh_list)
820 uvs_to_indices_list = generate_unique_uvs_dictionary_layers(mesh_list)
821
822 normal_values = generate_normals_from_dictionary(normals_to_indices)
823 color_values = generate_colors_from_dictionary(colors_to_indices)
824 uv_values = generate_uvs_from_dictionary_layers(uvs_to_indices_list)
825
826 faces = process_mesh_polygons(mesh_list,
827 normals_to_indices,
828 colors_to_indices,
829 uvs_to_indices_list,
830 vertex_offsets,
831 material_offsets)
832
833 nuvs = []
834 for layer_index, uvs in enumerate(uv_values):
835 nuvs.append(str(len(uvs)))
836
837 nvertices = len(vertices)
838 nnormals = len(normal_values)
839 ncolors = len(color_values)
840 nfaces = len(faces)
841 nuvs = ",".join(nuvs)
842
843 aabb_min, aabb_max = generate_bounding_box(vertices)
844 aabb_min = ",".join(str(f) for f in aabb_min)
845 aabb_max = ",".join(str(f) for f in aabb_max)
846
847 vertices = ",".join(Vector3String(v, True) for v in vertices)
848 normals = ",".join(Vector3String(v, True) for v in normal_values)
849 colors = ",".join(Vector3String(v, True) for v in color_values)
850 faces = ",".join(faces)
851 uvs = generate_uvs(uv_values)
852
853 output = [
854
855 '{',
856 ' "metadata" : {',
857 ' "formatVersion" : 3.2,',
858 ' "type" : "geometry",',
859 ' "generatedBy" : "convert-to-threejs.py"' + ',',
860 ' "vertices" : ' + str(nvertices) + ',',
861 ' "normals" : ' + str(nnormals) + ',',
862 ' "colors" : ' + str(ncolors) + ',',
863 ' "faces" : ' + str(nfaces) + ',',
864 ' "uvs" : ' + ArrayString(nuvs),
865 ' },',
866 ' "boundingBox" : {',
867 ' "min" : ' + ArrayString(aabb_min) + ',',
868 ' "max" : ' + ArrayString(aabb_max),
869 ' },',
870 ' "scale" : ' + str( 1 ) + ',',
871 ' "materials" : ' + ArrayString("") + ',',
872 ' "vertices" : ' + ArrayString(vertices) + ',',
873 ' "normals" : ' + ArrayString(normals) + ',',
874 ' "colors" : ' + ArrayString(colors) + ',',
875 ' "uvs" : ' + ArrayString(uvs) + ',',
876 ' "faces" : ' + ArrayString(faces),
877 '}'
878
879 ]
880
881 return generateMultiLineString( output, '\n', 0 )
882
883# #####################################################
884# Process - Mesh Geometry
885# #####################################################
886def generate_normal_key(normal):
887 return (round(normal[0], 6), round(normal[1], 6), round(normal[2], 6))
888
889def generate_color_key(color):
890 return getHex(color)
891
892def generate_uv_key(uv):
893 return (round(uv[0], 6), round(uv[1], 6))
894
895def append_non_duplicate_uvs(source_uvs, dest_uvs, counts):
896 source_layer_count = len(source_uvs)
897 for layer_index in range(source_layer_count):
898
899 dest_layer_count = len(dest_uvs)
900
901 if dest_layer_count <= layer_index:
902 dest_uv_layer = {}
903 count = 0
904 dest_uvs.append(dest_uv_layer)
905 counts.append(count)
906 else:
907 dest_uv_layer = dest_uvs[layer_index]
908 count = counts[layer_index]
909
910 source_uv_layer = source_uvs[layer_index]
911
912 for uv in source_uv_layer:
913 key = generate_uv_key(uv)
914 if key not in dest_uv_layer:
915 dest_uv_layer[key] = count
916 count += 1
917
918 counts[layer_index] = count
919
920 return counts
921
922def generate_unique_normals_dictionary(mesh_list):
923 normals_dictionary = {}
924 nnormals = 0
925
926 # Merge meshes, remove duplicate data
927 for mesh in mesh_list:
928 node = mesh.GetNode()
929 normal_values, normal_indices = extract_fbx_vertex_normals(mesh)
930
931 if len(normal_values) > 0:
932 for normal in normal_values:
933 key = generate_normal_key(normal)
934 if key not in normals_dictionary:
935 normals_dictionary[key] = nnormals
936 nnormals += 1
937
938 return normals_dictionary
939
940def generate_unique_colors_dictionary(mesh_list):
941 colors_dictionary = {}
942 ncolors = 0
943
944 # Merge meshes, remove duplicate data
945 for mesh in mesh_list:
946 color_values, color_indices = extract_fbx_vertex_colors(mesh)
947
948 if len(color_values) > 0:
949 for color in color_values:
950 key = generate_color_key(color)
951 if key not in colors_dictionary:
952 colors_dictionary[key] = count
953 count += 1
954
955 return colors_dictionary
956
957def generate_unique_uvs_dictionary_layers(mesh_list):
958 uvs_dictionary_layers = []
959 nuvs_list = []
960
961 # Merge meshes, remove duplicate data
962 for mesh in mesh_list:
963 uv_values, uv_indices = extract_fbx_vertex_uvs(mesh)
964
965 if len(uv_values) > 0:
966 nuvs_list = append_non_duplicate_uvs(uv_values, uvs_dictionary_layers, nuvs_list)
967
968 return uvs_dictionary_layers
969
970def generate_normals_from_dictionary(normals_dictionary):
971 normal_values = []
972 for key, index in sorted(normals_dictionary.items(), key = operator.itemgetter(1)):
973 normal_values.append(key)
974
975 return normal_values
976
977def generate_colors_from_dictionary(colors_dictionary):
978 color_values = []
979 for key, index in sorted(colors_dictionary.items(), key = operator.itemgetter(1)):
980 color_values.append(key)
981
982 return color_values
983
984def generate_uvs_from_dictionary_layers(uvs_dictionary_layers):
985 uv_values = []
986 for uvs_dictionary in uvs_dictionary_layers:
987 uv_values_layer = []
988 for key, index in sorted(uvs_dictionary.items(), key = operator.itemgetter(1)):
989 uv_values_layer.append(key)
990 uv_values.append(uv_values_layer)
991
992 return uv_values
993
994def generate_normal_indices_for_poly(poly_index, mesh_normal_values, mesh_normal_indices, normals_to_indices):
995 if len(mesh_normal_indices) <= 0:
996 return []
997
998 poly_normal_indices = mesh_normal_indices[poly_index]
999 poly_size = len(poly_normal_indices)
1000
1001 output_poly_normal_indices = []
1002 for v in range(poly_size):
1003 normal_index = poly_normal_indices[v]
1004 normal_value = mesh_normal_values[normal_index]
1005
1006 key = generate_normal_key(normal_value)
1007
1008 output_index = normals_to_indices[key]
1009 output_poly_normal_indices.append(output_index)
1010
1011 return output_poly_normal_indices
1012
1013def generate_color_indices_for_poly(poly_index, mesh_color_values, mesh_color_indices, colors_to_indices):
1014 if len(mesh_color_indices) <= 0:
1015 return []
1016
1017 poly_color_indices = mesh_color_indices[poly_index]
1018 poly_size = len(poly_color_indices)
1019
1020 output_poly_color_indices = []
1021 for v in range(poly_size):
1022 color_index = poly_color_indices[v]
1023 color_value = mesh_color_values[color_index]
1024
1025 key = generate_color_key(color_value)
1026
1027 output_index = colors_to_indices[key]
1028 output_poly_color_indices.append(output_index)
1029
1030 return output_poly_color_indices
1031
1032def generate_uv_indices_for_poly(poly_index, mesh_uv_values, mesh_uv_indices, uvs_to_indices):
1033 if len(mesh_uv_indices) <= 0:
1034 return []
1035
1036 poly_uv_indices = mesh_uv_indices[poly_index]
1037 poly_size = len(poly_uv_indices)
1038
1039 output_poly_uv_indices = []
1040 for v in range(poly_size):
1041 uv_index = poly_uv_indices[v]
1042 uv_value = mesh_uv_values[uv_index]
1043
1044 key = generate_uv_key(uv_value)
1045
1046 output_index = uvs_to_indices[key]
1047 output_poly_uv_indices.append(output_index)
1048
1049 return output_poly_uv_indices
1050
1051def process_mesh_vertices(mesh_list):
1052 vertex_offset = 0
1053 vertex_offset_list = [0]
1054 vertices = []
1055 for mesh in mesh_list:
1056 node = mesh.GetNode()
1057 mesh_vertices = extract_fbx_vertex_positions(mesh)
1058
1059 vertices.extend(mesh_vertices[:])
1060 vertex_offset += len(mesh_vertices)
1061 vertex_offset_list.append(vertex_offset)
1062
1063 return vertices, vertex_offset_list
1064
1065def process_mesh_materials(mesh_list):
1066 material_offset = 0
1067 material_offset_list = [0]
1068 materials_list = []
1069
1070 #TODO: remove duplicate mesh references
1071 for mesh in mesh_list:
1072 node = mesh.GetNode()
1073
1074 material_count = node.GetMaterialCount()
1075 if material_count > 0:
1076 for l in range(mesh.GetLayerCount()):
1077 materials = mesh.GetLayer(l).GetMaterials()
1078 if materials:
1079 if materials.GetReferenceMode() == FbxLayerElement.eIndex:
1080 #Materials are in an undefined external table
1081 continue
1082
1083 for i in range(material_count):
1084 material = node.GetMaterial(i)
1085 materials_list.append( material )
1086
1087 material_offset += material_count
1088 material_offset_list.append(material_offset)
1089
1090 return materials_list, material_offset_list
1091
1092def process_mesh_polygons(mesh_list, normals_to_indices, colors_to_indices, uvs_to_indices_list, vertex_offset_list, material_offset_list):
1093 faces = []
1094 for mesh_index in range(len(mesh_list)):
1095 mesh = mesh_list[mesh_index]
1096 poly_count = mesh.GetPolygonCount()
1097 control_points = mesh.GetControlPoints()
1098
1099 normal_values, normal_indices = extract_fbx_vertex_normals(mesh)
1100 color_values, color_indices = extract_fbx_vertex_colors(mesh)
1101 uv_values_layers, uv_indices_layers = extract_fbx_vertex_uvs(mesh)
1102
1103 for poly_index in range(poly_count):
1104 poly_size = mesh.GetPolygonSize(poly_index)
1105
1106 face_normals = generate_normal_indices_for_poly(poly_index, normal_values, normal_indices, normals_to_indices)
1107 face_colors = generate_color_indices_for_poly(poly_index, color_values, color_indices, colors_to_indices)
1108
1109 face_uv_layers = []
1110 for l in range(len(uv_indices_layers)):
1111 uv_values = uv_values_layers[l]
1112 uv_indices = uv_indices_layers[l]
1113 face_uv_indices = generate_uv_indices_for_poly(poly_index, uv_values, uv_indices, uvs_to_indices_list[l])
1114 face_uv_layers.append(face_uv_indices)
1115
1116 face_vertices = []
1117 for vertex_index in range(poly_size):
1118 control_point_index = mesh.GetPolygonVertex(poly_index, vertex_index)
1119 face_vertices.append(control_point_index)
1120
1121 #TODO: assign a default material to any mesh without one
1122 if len(material_offset_list) <= mesh_index:
1123 material_offset = 0
1124 else:
1125 material_offset = material_offset_list[mesh_index]
1126
1127 vertex_offset = vertex_offset_list[mesh_index]
1128
1129 face = generate_mesh_face(mesh,
1130 poly_index,
1131 face_vertices,
1132 face_normals,
1133 face_colors,
1134 face_uv_layers,
1135 vertex_offset,
1136 material_offset)
1137
1138 faces.append(face)
1139
1140
1141 return faces
1142
1143def generate_mesh_face(mesh, polygon_index, vertex_indices, normals, colors, uv_layers, vertex_offset, material_offset):
1144 isTriangle = ( len(vertex_indices) == 3 )
1145 nVertices = 3 if isTriangle else 4
1146
1147 hasMaterial = False
1148 for l in range(mesh.GetLayerCount()):
1149 materials = mesh.GetLayer(l).GetMaterials()
1150 if materials:
1151 hasMaterial = True
1152 break
1153
1154 hasFaceUvs = False
1155 hasFaceVertexUvs = len(uv_layers) > 0
1156 hasFaceNormals = False
1157 hasFaceVertexNormals = len(normals) > 0
1158 hasFaceColors = False
1159 hasFaceVertexColors = len(colors) > 0
1160
1161 faceType = 0
1162 faceType = setBit(faceType, 0, not isTriangle)
1163 faceType = setBit(faceType, 1, hasMaterial)
1164 faceType = setBit(faceType, 2, hasFaceUvs)
1165 faceType = setBit(faceType, 3, hasFaceVertexUvs)
1166 faceType = setBit(faceType, 4, hasFaceNormals)
1167 faceType = setBit(faceType, 5, hasFaceVertexNormals)
1168 faceType = setBit(faceType, 6, hasFaceColors)
1169 faceType = setBit(faceType, 7, hasFaceVertexColors)
1170
1171 faceData = []
1172
1173 # order is important, must match order in JSONLoader
1174
1175 # face type
1176 # vertex indices
1177 # material index
1178 # face uvs index
1179 # face vertex uvs indices
1180 # face color index
1181 # face vertex colors indices
1182
1183 faceData.append(faceType)
1184
1185 tmp = []
1186 for i in range(nVertices):
1187 tmp.append(vertex_indices[i])
1188 index = vertex_indices[i] + vertex_offset
1189 faceData.append(index)
1190
1191 if hasMaterial:
1192 material_id = 0
1193 for l in range(mesh.GetLayerCount()):
1194 materials = mesh.GetLayer(l).GetMaterials()
1195 if materials:
1196 material_id = materials.GetIndexArray().GetAt(polygon_index)
1197 break
1198 material_id += material_offset
1199 faceData.append( material_id )
1200
1201 if hasFaceVertexUvs:
1202 for polygon_uvs in uv_layers:
1203 for i in range(nVertices):
1204 index = polygon_uvs[i]
1205 faceData.append(index)
1206
1207 if hasFaceVertexNormals:
1208 for i in range(nVertices):
1209 index = normals[i]
1210 faceData.append(index)
1211
1212 if hasFaceVertexColors:
1213 for i in range(nVertices):
1214 index = colors[i]
1215 faceData.append(index)
1216
1217 return ",".join( map(str, faceData) )
1218
1219
1220# #####################################################
1221# Generate - Mesh List
1222# #####################################################
1223def generate_mesh_list_from_hierarchy(node, mesh_list):
1224 if node.GetNodeAttribute() == None:
1225 pass
1226 else:
1227 attribute_type = (node.GetNodeAttribute().GetAttributeType())
1228 if attribute_type == FbxNodeAttribute.eMesh or \
1229 attribute_type == FbxNodeAttribute.eNurbs or \
1230 attribute_type == FbxNodeAttribute.eNurbsSurface or \
1231 attribute_type == FbxNodeAttribute.ePatch:
1232
1233 if attribute_type != FbxNodeAttribute.eMesh:
1234 converter.TriangulateInPlace(node);
1235
1236 mesh_list.append(node.GetNodeAttribute())
1237
1238 for i in range(node.GetChildCount()):
1239 generate_mesh_list_from_hierarchy(node.GetChild(i), mesh_list)
1240
1241def generate_mesh_list(scene):
1242 mesh_list = []
1243 node = scene.GetRootNode()
1244 if node:
1245 for i in range(node.GetChildCount()):
1246 generate_mesh_list_from_hierarchy(node.GetChild(i), mesh_list)
1247 return mesh_list
1248
1249# #####################################################
1250# Generate - Embeds
1251# #####################################################
1252def generate_embed_list_from_hierarchy(node, embed_list):
1253 if node.GetNodeAttribute() == None:
1254 pass
1255 else:
1256 attribute_type = (node.GetNodeAttribute().GetAttributeType())
1257 if attribute_type == FbxNodeAttribute.eMesh or \
1258 attribute_type == FbxNodeAttribute.eNurbs or \
1259 attribute_type == FbxNodeAttribute.eNurbsSurface or \
1260 attribute_type == FbxNodeAttribute.ePatch:
1261
1262 if attribute_type != FbxNodeAttribute.eMesh:
1263 converter.TriangulateInPlace(node);
1264
1265 embed_string = generate_mesh_string_for_scene_output(node)
1266 embed_list.append(embed_string)
1267
1268 for i in range(node.GetChildCount()):
1269 generate_embed_list_from_hierarchy(node.GetChild(i), embed_list)
1270
1271def generate_embed_list(scene):
1272 embed_list = []
1273 node = scene.GetRootNode()
1274 if node:
1275 for i in range(node.GetChildCount()):
1276 generate_embed_list_from_hierarchy(node.GetChild(i), embed_list)
1277 return embed_list
1278
1279# #####################################################
1280# Generate - Geometries
1281# #####################################################
1282def generate_geometry_string(node):
1283
1284 output = [
1285 '\t' + LabelString( getGeometryName( node, True ) ) + ' : {',
1286 ' "type" : "embedded",',
1287 ' "id" : ' + LabelString( getEmbedName( node, True ) ),
1288 '}'
1289 ]
1290
1291 return generateMultiLineString( output, '\n\t\t', 0 )
1292
1293def generate_geometry_list_from_hierarchy(node, geometry_list):
1294 if node.GetNodeAttribute() == None:
1295 pass
1296 else:
1297 attribute_type = (node.GetNodeAttribute().GetAttributeType())
1298 if attribute_type == FbxNodeAttribute.eMesh:
1299 geometry_string = generate_geometry_string(node)
1300 geometry_list.append(geometry_string)
1301 for i in range(node.GetChildCount()):
1302 generate_geometry_list_from_hierarchy(node.GetChild(i), geometry_list)
1303
1304def generate_geometry_list(scene):
1305 geometry_list = []
1306 node = scene.GetRootNode()
1307 if node:
1308 for i in range(node.GetChildCount()):
1309 generate_geometry_list_from_hierarchy(node.GetChild(i), geometry_list)
1310 return geometry_list
1311
1312# #####################################################
1313# Generate - Camera Names
1314# #####################################################
1315def generate_camera_name_list_from_hierarchy(node, camera_list):
1316 if node.GetNodeAttribute() == None:
1317 pass
1318 else:
1319 attribute_type = (node.GetNodeAttribute().GetAttributeType())
1320 if attribute_type == FbxNodeAttribute.eCamera:
1321 camera_string = getObjectName(node)
1322 camera_list.append(camera_string)
1323 for i in range(node.GetChildCount()):
1324 generate_camera_name_list_from_hierarchy(node.GetChild(i), camera_list)
1325
1326def generate_camera_name_list(scene):
1327 camera_list = []
1328 node = scene.GetRootNode()
1329 if node:
1330 for i in range(node.GetChildCount()):
1331 generate_camera_name_list_from_hierarchy(node.GetChild(i), camera_list)
1332 return camera_list
1333
1334# #####################################################
1335# Generate - Light Object
1336# #####################################################
1337def generate_default_light_string(padding):
1338 direction = (1,1,1)
1339 color = (1,1,1)
1340 intensity = 80.0
1341
1342 output = [
1343
1344 '\t\t' + LabelString( 'default_light' ) + ' : {',
1345 ' "type" : "DirectionalLight",',
1346 ' "color" : ' + str(getHex(color)) + ',',
1347 ' "intensity" : ' + str(intensity/100.0) + ',',
1348 ' "direction" : ' + Vector3String( direction ) + ',',
1349 ' "target" : ' + LabelString( getObjectName( None ) ),
1350 ' }'
1351
1352 ]
1353
1354 return generateMultiLineString( output, '\n\t\t', padding )
1355
1356def generate_light_string(node, padding):
1357 light = node.GetNodeAttribute()
1358 light_types = ["point", "directional", "spot", "area", "volume"]
1359 light_type = light_types[light.LightType.Get()]
1360
1361 transform = node.EvaluateLocalTransform()
1362 position = transform.GetT()
1363
1364 output = []
1365
1366 if light_type == "directional":
1367
1368 # Three.js directional lights emit light from a point in 3d space to a target node or the origin.
1369 # When there is no target, we need to take a point, one unit away from the origin, and move it
1370 # into the right location so that the origin acts like the target
1371
1372 if node.GetTarget():
1373 direction = position
1374 else:
1375 translation = FbxVector4(0,0,0,0)
1376 scale = FbxVector4(1,1,1,1)
1377 rotation = transform.GetR()
1378 matrix = FbxMatrix(translation, rotation, scale)
1379 direction = matrix.MultNormalize(global_up_vector)
1380
1381 output = [
1382
1383 '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
1384 ' "type" : "DirectionalLight",',
1385 ' "color" : ' + str(getHex(light.Color.Get())) + ',',
1386 ' "intensity" : ' + str(light.Intensity.Get()/100.0) + ',',
1387 ' "direction" : ' + Vector3String( direction ) + ',',
1388 ' "target" : ' + LabelString( getObjectName( node.GetTarget() ) ) + ( ',' if node.GetChildCount() > 0 else '' )
1389 ]
1390
1391 elif light_type == "point":
1392
1393 output = [
1394
1395 '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
1396 ' "type" : "PointLight",',
1397 ' "color" : ' + str(getHex(light.Color.Get())) + ',',
1398 ' "intensity" : ' + str(light.Intensity.Get()/100.0) + ',',
1399 ' "position" : ' + Vector3String( position ) + ',',
1400 ' "distance" : ' + str(light.FarAttenuationEnd.Get()) + ( ',' if node.GetChildCount() > 0 else '' )
1401
1402 ]
1403
1404 elif light_type == "spot":
1405
1406 output = [
1407
1408 '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
1409 ' "type" : "SpotLight",',
1410 ' "color" : ' + str(getHex(light.Color.Get())) + ',',
1411 ' "intensity" : ' + str(light.Intensity.Get()/100.0) + ',',
1412 ' "position" : ' + Vector3String( position ) + ',',
1413 ' "distance" : ' + str(light.FarAttenuationEnd.Get()) + ',',
1414 ' "angle" : ' + str((light.OuterAngle.Get()*math.pi)/180) + ',',
1415 ' "exponent" : ' + str(light.DecayType.Get()) + ',',
1416 ' "target" : ' + LabelString( getObjectName( node.GetTarget() ) ) + ( ',' if node.GetChildCount() > 0 else '' )
1417
1418 ]
1419
1420 return generateMultiLineString( output, '\n\t\t', padding )
1421
1422def generate_ambient_light_string(scene):
1423
1424 scene_settings = scene.GetGlobalSettings()
1425 ambient_color = scene_settings.GetAmbientColor()
1426 ambient_color = (ambient_color.mRed, ambient_color.mGreen, ambient_color.mBlue)
1427
1428 if ambient_color[0] == 0 and ambient_color[1] == 0 and ambient_color[2] == 0:
1429 return None
1430
1431 class AmbientLight:
1432 def GetName(self):
1433 return "AmbientLight"
1434
1435 node = AmbientLight()
1436
1437 output = [
1438
1439 '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
1440 ' "type" : "AmbientLight",',
1441 ' "color" : ' + str(getHex(ambient_color)),
1442 '}'
1443
1444 ]
1445
1446 return generateMultiLineString( output, '\n\t\t', 0 )
1447
1448# #####################################################
1449# Generate - Camera Object
1450# #####################################################
1451def generate_default_camera_string(padding):
1452 position = (100, 100, 100)
1453 near = 0.1
1454 far = 1000
1455 fov = 75
1456
1457 output = [
1458
1459 '\t\t' + LabelString( 'default_camera' ) + ' : {',
1460 ' "type" : "PerspectiveCamera",',
1461 ' "fov" : ' + str(fov) + ',',
1462 ' "near" : ' + str(near) + ',',
1463 ' "far" : ' + str(far) + ',',
1464 ' "position" : ' + Vector3String( position ),
1465 ' }'
1466
1467 ]
1468
1469 return generateMultiLineString( output, '\n\t\t', padding )
1470
1471def generate_camera_string(node, padding):
1472 camera = node.GetNodeAttribute()
1473
1474 target_node = node.GetTarget()
1475 target = ""
1476 if target_node:
1477 transform = target.EvaluateLocalTransform()
1478 target = transform.GetT()
1479 else:
1480 target = camera.InterestPosition.Get()
1481
1482 position = camera.Position.Get()
1483
1484 projection_types = [ "perspective", "orthogonal" ]
1485 projection = projection_types[camera.ProjectionType.Get()]
1486
1487 near = camera.NearPlane.Get()
1488 far = camera.FarPlane.Get()
1489
1490 output = []
1491
1492 if projection == "perspective":
1493
1494 aspect = camera.PixelAspectRatio.Get()
1495 fov = camera.FieldOfView.Get()
1496
1497 output = [
1498
1499 '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
1500 ' "type" : "PerspectiveCamera",',
1501 ' "fov" : ' + str(fov) + ',',
1502 ' "aspect" : ' + str(aspect) + ',',
1503 ' "near" : ' + str(near) + ',',
1504 ' "far" : ' + str(far) + ',',
1505 ' "position" : ' + Vector3String( position ) + ( ',' if node.GetChildCount() > 0 else '' )
1506
1507 ]
1508
1509 elif projection == "orthogonal":
1510
1511 left = ""
1512 right = ""
1513 top = ""
1514 bottom = ""
1515
1516 output = [
1517
1518 '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
1519 ' "type" : "OrthographicCamera",',
1520 ' "left" : ' + left + ',',
1521 ' "right" : ' + right + ',',
1522 ' "top" : ' + top + ',',
1523 ' "bottom" : ' + bottom + ',',
1524 ' "near" : ' + str(near) + ',',
1525 ' "far" : ' + str(far) + ',',
1526 ' "position" : ' + Vector3String( position ) + ( ',' if node.GetChildCount() > 0 else '' )
1527
1528 ]
1529
1530 return generateMultiLineString( output, '\n\t\t', padding )
1531
1532# #####################################################
1533# Generate - Mesh Object
1534# #####################################################
1535def generate_mesh_object_string(node, padding):
1536 mesh = node.GetNodeAttribute()
1537 transform = node.EvaluateLocalTransform()
1538 position = transform.GetT()
1539 scale = transform.GetS()
1540 rotation = getRadians(transform.GetR())
1541
1542 material_count = node.GetMaterialCount()
1543 material_name = ""
1544
1545 if material_count > 0:
1546 material_names = []
1547 for l in range(mesh.GetLayerCount()):
1548 materials = mesh.GetLayer(l).GetMaterials()
1549 if materials:
1550 if materials.GetReferenceMode() == FbxLayerElement.eIndex:
1551 #Materials are in an undefined external table
1552 continue
1553 for i in range(material_count):
1554 material = node.GetMaterial(i)
1555 material_names.append( getMaterialName(material) )
1556 #If this mesh has more than one material, use a proxy material
1557 material_name = getMaterialName( node, True) if material_count > 1 else material_names[0]
1558
1559 output = [
1560
1561 '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
1562 ' "geometry" : ' + LabelString( getGeometryName( node, True ) ) + ',',
1563 ' "material" : ' + LabelString( material_name ) + ',',
1564 ' "position" : ' + Vector3String( position ) + ',',
1565 ' "rotation" : ' + Vector3String( rotation ) + ',',
1566 ' "scale" : ' + Vector3String( scale ) + ',',
1567 ' "visible" : ' + getObjectVisible( node ) + ( ',' if node.GetChildCount() > 0 else '' )
1568
1569 ]
1570
1571 return generateMultiLineString( output, '\n\t\t', padding )
1572
1573# #####################################################
1574# Generate - Object
1575# #####################################################
1576def generate_object_string(node, padding):
1577 node_types = ["Unknown", "Null", "Marker", "Skeleton", "Mesh", "Nurbs", "Patch", "Camera",
1578 "CameraStereo", "CameraSwitcher", "Light", "OpticalReference", "OpticalMarker", "NurbsCurve",
1579 "TrimNurbsSurface", "Boundary", "NurbsSurface", "Shape", "LODGroup", "SubDiv", "CachedEffect", "Line"]
1580
1581 transform = node.EvaluateLocalTransform()
1582 position = transform.GetT()
1583 scale = transform.GetS()
1584 rotation = getRadians(transform.GetR())
1585
1586 node_type = ""
1587 if node.GetNodeAttribute() == None:
1588 node_type = "Null"
1589 else:
1590 node_type = node_types[node.GetNodeAttribute().GetAttributeType()]
1591
1592 output = [
1593
1594 '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
1595 ' "fbx_type" : ' + LabelString( node_type ) + ',',
1596 ' "position" : ' + Vector3String( position ) + ',',
1597 ' "rotation" : ' + Vector3String( rotation ) + ',',
1598 ' "scale" : ' + Vector3String( scale ) + ',',
1599 ' "visible" : ' + getObjectVisible( node ) + ( ',' if node.GetChildCount() > 0 else '' )
1600
1601 ]
1602
1603 return generateMultiLineString( output, '\n\t\t', padding )
1604
1605# #####################################################
1606# Parse - Objects
1607# #####################################################
1608def generate_object_hierarchy(node, object_list, pad, siblings_left):
1609 object_count = 0
1610 if node.GetNodeAttribute() == None:
1611 object_string = generate_object_string(node, pad)
1612 object_list.append(object_string)
1613 object_count += 1
1614 else:
1615 attribute_type = (node.GetNodeAttribute().GetAttributeType())
1616 if attribute_type == FbxNodeAttribute.eMesh:
1617 object_string = generate_mesh_object_string(node, pad)
1618 object_list.append(object_string)
1619 object_count += 1
1620 elif attribute_type == FbxNodeAttribute.eLight:
1621 object_string = generate_light_string(node, pad)
1622 object_list.append(object_string)
1623 object_count += 1
1624 elif attribute_type == FbxNodeAttribute.eCamera:
1625 object_string = generate_camera_string(node, pad)
1626 object_list.append(object_string)
1627 object_count += 1
1628 else:
1629 object_string = generate_object_string(node, pad)
1630 object_list.append(object_string)
1631 object_count += 1
1632
1633 if node.GetChildCount() > 0:
1634 object_list.append( PaddingString( pad + 1 ) + '\t\t"children" : {\n' )
1635
1636 for i in range(node.GetChildCount()):
1637 object_count += generate_object_hierarchy(node.GetChild(i), object_list, pad + 2, node.GetChildCount() - i - 1)
1638
1639 object_list.append( PaddingString( pad + 1 ) + '\t\t}' )
1640 object_list.append( PaddingString( pad ) + '\t\t}' + (',\n' if siblings_left > 0 else ''))
1641
1642 return object_count
1643
1644def generate_scene_objects_string(scene):
1645 object_count = 0
1646 object_list = []
1647
1648 ambient_light = generate_ambient_light_string(scene)
1649 if ambient_light:
1650 if scene.GetNodeCount() > 0 or option_default_light or option_default_camera:
1651 ambient_light += (',\n')
1652 object_list.append(ambient_light)
1653 object_count += 1
1654
1655 if option_default_light:
1656 default_light = generate_default_light_string(0)
1657 if scene.GetNodeCount() > 0 or option_default_camera:
1658 default_light += (',\n')
1659 object_list.append(default_light)
1660 object_count += 1
1661
1662 if option_default_camera:
1663 default_camera = generate_default_camera_string(0)
1664 if scene.GetNodeCount() > 0:
1665 default_camera += (',\n')
1666 object_list.append(default_camera)
1667 object_count += 1
1668
1669 node = scene.GetRootNode()
1670 if node:
1671 for i in range(node.GetChildCount()):
1672 object_count += generate_object_hierarchy(node.GetChild(i), object_list, 0, node.GetChildCount() - i - 1)
1673
1674 return "\n".join(object_list), object_count
1675
1676# #####################################################
1677# Parse - Geometry (non-scene output)
1678# #####################################################
1679def extract_geometry(scene, filename):
1680 mesh_string = generate_mesh_string_for_non_scene_output(scene)
1681 return mesh_string
1682
1683# #####################################################
1684# Parse - Scene (scene output)
1685# #####################################################
1686def extract_scene(scene, filename):
1687 global_settings = scene.GetGlobalSettings()
1688 objects, nobjects = generate_scene_objects_string(scene)
1689
1690 textures = generate_texture_list(scene)
1691 materials = generate_material_list(scene)
1692 geometries = generate_geometry_list(scene)
1693 embeds = generate_embed_list(scene)
1694 fogs = []
1695
1696 ntextures = len(textures)
1697 nmaterials = len(materials)
1698 ngeometries = len(geometries)
1699
1700 #TODO: extract actual root/scene data here
1701 position = Vector3String( (0,0,0) )
1702 rotation = Vector3String( (0,0,0) )
1703 scale = Vector3String( (1,1,1) )
1704
1705 camera_names = generate_camera_name_list(scene)
1706 scene_settings = scene.GetGlobalSettings()
1707
1708 #TODO: this might exist as part of the FBX spec
1709 bgcolor = Vector3String( (0.667,0.667,0.667) )
1710 bgalpha = 1
1711
1712 # This does not seem to be any help here
1713 # global_settings.GetDefaultCamera()
1714
1715 defcamera = LabelString(camera_names[0] if len(camera_names) > 0 else "")
1716 if option_default_camera:
1717 defcamera = LabelString('default_camera')
1718
1719 #TODO: extract fog info from scene
1720 deffog = LabelString("")
1721
1722 geometries = generateMultiLineString( geometries, ",\n\n\t", 0 )
1723 materials = generateMultiLineString( materials, ",\n\n\t", 0 )
1724 textures = generateMultiLineString( textures, ",\n\n\t", 0 )
1725 embeds = generateMultiLineString( embeds, ",\n\n\t", 0 )
1726 fogs = generateMultiLineString( fogs, ",\n\n\t", 0 )
1727
1728 output = [
1729
1730 '{',
1731 ' "metadata": {',
1732 ' "formatVersion" : 3.2,',
1733 ' "type" : "scene",',
1734 ' "generatedBy" : "convert-to-threejs.py",',
1735 ' "objects" : ' + str(nobjects) + ',',
1736 ' "geometries" : ' + str(ngeometries) + ',',
1737 ' "materials" : ' + str(nmaterials) + ',',
1738 ' "textures" : ' + str(ntextures),
1739 ' },',
1740
1741 '',
1742 ' "urlBaseType": "relativeToScene",',
1743 '',
1744
1745 ' "objects" :',
1746 ' {',
1747 objects,
1748 ' },',
1749 '',
1750
1751 ' "geometries" :',
1752 ' {',
1753 '\t' + geometries,
1754 ' },',
1755 '',
1756
1757 ' "materials" :',
1758 ' {',
1759 '\t' + materials,
1760 ' },',
1761 '',
1762
1763 ' "textures" :',
1764 ' {',
1765 '\t' + textures,
1766 ' },',
1767 '',
1768
1769 ' "embeds" :',
1770 ' {',
1771 '\t' + embeds,
1772 ' },',
1773 '',
1774
1775 ' "fogs" :',
1776 ' {',
1777 '\t' + fogs,
1778 ' },',
1779 '',
1780
1781 ' "transform" :',
1782 ' {',
1783 ' "position" : ' + position + ',',
1784 ' "rotation" : ' + rotation + ',',
1785 ' "scale" : ' + scale,
1786 ' },',
1787 '',
1788
1789 ' "defaults" :',
1790 ' {',
1791 ' "bgcolor" : ' + str(bgcolor) + ',',
1792 ' "bgalpha" : ' + str(bgalpha) + ',',
1793 ' "camera" : ' + defcamera + ',',
1794 ' "fog" : ' + deffog,
1795 ' }',
1796 '}'
1797
1798 ]
1799
1800 return "\n".join(output)
1801
1802# #####################################################
1803# file helpers
1804# #####################################################
1805def write_file(fname, content):
1806 out = open(fname, "w")
1807 out.write(content)
1808 out.close()
1809
1810# #####################################################
1811# main
1812# #####################################################
1813if __name__ == "__main__":
1814 from optparse import OptionParser
1815
1816 try:
1817 from FbxCommon import *
1818 except ImportError:
1819 import platform
1820 msg = 'Could not locate the python FBX SDK!\n'
1821 msg += 'You need to copy the FBX SDK into your python install folder such as '
1822 if platform.system() == 'Windows' or platform.system() == 'Microsoft':
1823 msg += '"Python26/Lib/site-packages"'
1824 elif platform.system() == 'Linux':
1825 msg += '"/usr/local/lib/python2.6/site-packages"'
1826 elif platform.system() == 'Darwin':
1827 msg += '"/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages"'
1828 msg += ' folder.'
1829 print(msg)
1830 sys.exit(1)
1831
1832 usage = "Usage: %prog [source_file.fbx] [output_file.js] [options]"
1833 parser = OptionParser(usage=usage)
1834
1835 parser.add_option('-t', '--triangulate', action='store_true', dest='triangulate', help="force quad geometry into triangles", default=False)
1836 parser.add_option('-x', '--no-textures', action='store_true', dest='notextures', help="don't include texture references in output file", default=False)
1837 parser.add_option('-p', '--prefix', action='store_true', dest='prefix', help="prefix object names in output file", default=False)
1838 parser.add_option('-g', '--geometry-only', action='store_true', dest='geometry', help="output geometry only", default=False)
1839 parser.add_option('-c', '--default-camera', action='store_true', dest='defcamera', help="include default camera in output scene", default=False)
1840 parser.add_option('-l', '--defualt-light', action='store_true', dest='deflight', help="include default light in output scene", default=False)
1841
1842 (options, args) = parser.parse_args()
1843
1844 option_triangulate = options.triangulate
1845 option_textures = True if not options.notextures else False
1846 option_prefix = options.prefix
1847 option_geometry = options.geometry
1848 option_default_camera = options.defcamera
1849 option_default_light = options.deflight
1850
1851 # Prepare the FBX SDK.
1852 sdk_manager, scene = InitializeSdkObjects()
1853 converter = FbxGeometryConverter(sdk_manager)
1854 global_up_vector = get_up_vector(scene)
1855
1856 # The converter takes an FBX file as an argument.
1857 if len(args) > 1:
1858 print("\nLoading file: %s" % args[0])
1859 result = LoadScene(sdk_manager, scene, args[0])
1860 else:
1861 result = False
1862 print("\nUsage: convert_fbx_to_threejs [source_file.fbx] [output_file.js]\n")
1863
1864 if not result:
1865 print("\nAn error occurred while loading the file...")
1866 else:
1867 if option_triangulate:
1868 print("\nForcing geometry to triangles")
1869 triangulate_scene(scene)
1870
1871 if option_geometry:
1872 output_content = extract_geometry(scene, os.path.basename(args[0]))
1873 else:
1874 output_content = extract_scene(scene, os.path.basename(args[0]))
1875
1876 output_path = os.path.join(os.getcwd(), args[1])
1877 write_file(output_path, output_content)
1878
1879 print("\nExported Three.js file to:\n%s\n" % output_path)
1880
1881 # Destroy all objects created by the FBX SDK.
1882 sdk_manager.Destroy()
1883 sys.exit(0)
Note: See TracBrowser for help on using the repository browser.