|
@@ -0,0 +1,1663 @@
|
|
|
+"""FBX to Three.js converter (based on FBX import example from Autodesk FBX SDK)
|
|
|
+
|
|
|
+Converts FBX scene file into Three.js scene format (one scene file and several ascii model files plus textures).
|
|
|
+
|
|
|
+-------------------------
|
|
|
+How to use this converter
|
|
|
+-------------------------
|
|
|
+
|
|
|
+python convert_fbx_three.py scene.fbx output_folder
|
|
|
+
|
|
|
+------------
|
|
|
+Dependencies
|
|
|
+------------
|
|
|
+
|
|
|
+Requires Autodesk FBX SDK Python bindings.
|
|
|
+
|
|
|
+If your platform is not included in "modules", full SDK can be downloaded from here:
|
|
|
+
|
|
|
+ http://usa.autodesk.com/adsk/servlet/pc/index?siteID=123112&id=6837478
|
|
|
+
|
|
|
+---------------------------
|
|
|
+How to load generated scene
|
|
|
+---------------------------
|
|
|
+
|
|
|
+<script type="text/javascript" src="ThreeExtras.js"></script>
|
|
|
+
|
|
|
+<script type="text/javascript">
|
|
|
+
|
|
|
+ ...
|
|
|
+
|
|
|
+ SCENE_URL = "http://example.com/output_folder/scene.js";
|
|
|
+ SceneUtils.loadScene( SCENE_URL, callback_sync, callback_async, callback_progress );
|
|
|
+
|
|
|
+ ...
|
|
|
+
|
|
|
+ /*
|
|
|
+ callback_sync - called after the scene file is loaded (procedural elements like cubes and spheres or materials without textures are now ready)
|
|
|
+ callback_progress - called after each asynchronous elements gets loaded (textures and model files)
|
|
|
+ callback_async - called after all asynchronous elements (models and textures) are loaded, now whole scene is ready
|
|
|
+
|
|
|
+ Scene is fully created, just needs to be passed to renderer, together with camera.
|
|
|
+ Individual scene elements are also returned for convenient access via hashmap (addressable by their id).
|
|
|
+
|
|
|
+ progress = {
|
|
|
+
|
|
|
+ total_models: X,
|
|
|
+ total_textures: Y,
|
|
|
+ loaded_models: A,
|
|
|
+ loaded_textures: B
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ result = {
|
|
|
+
|
|
|
+ scene: new THREE.Scene(),
|
|
|
+ geometries: { ... },
|
|
|
+ materials: { ... },
|
|
|
+ textures: { ... },
|
|
|
+ objects: { ... },
|
|
|
+ cameras: { ... },
|
|
|
+ lights: { ... },
|
|
|
+ fogs: { ... },
|
|
|
+
|
|
|
+ currentCamera: THREE.Camera( ... )
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ */
|
|
|
+
|
|
|
+ var callback_progress = function( progress, result ) { ... };
|
|
|
+ var callback_sync = function( result ) { ... };
|
|
|
+ var callback_async = function( result ) { ... };
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+
|
|
|
+--------
|
|
|
+Features
|
|
|
+--------
|
|
|
+
|
|
|
+- scene + models + materials + textures
|
|
|
+- multiple UV layers
|
|
|
+
|
|
|
+-------------------
|
|
|
+Current limitations
|
|
|
+-------------------
|
|
|
+
|
|
|
+- only very small subset from FBX is currently supported
|
|
|
+ - static meshes
|
|
|
+ - triangles and quads
|
|
|
+ - max two UV layers
|
|
|
+ - Lambert and Phong materials (partial: Three doesn't support emissive color)
|
|
|
+- no lights yet
|
|
|
+- no cameras yet (default one is created instead)
|
|
|
+
|
|
|
+------
|
|
|
+Author
|
|
|
+------
|
|
|
+AlteredQualia http://alteredqualia.com
|
|
|
+
|
|
|
+"""
|
|
|
+
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import random
|
|
|
+import math
|
|
|
+import pprint
|
|
|
+import shutil
|
|
|
+import os.path
|
|
|
+
|
|
|
+# load platform specific binary modules
|
|
|
+platform_map = {
|
|
|
+"Windows" :
|
|
|
+ {
|
|
|
+ "folder": "win",
|
|
|
+ "versions" : {
|
|
|
+ 2 : "Python26_x86",
|
|
|
+ 3 : "Python31_x86"
|
|
|
+ }
|
|
|
+ },
|
|
|
+"Linux" :
|
|
|
+ {
|
|
|
+ "folder": "linux",
|
|
|
+ "versions" : {
|
|
|
+ 2 : "Python26_x86",
|
|
|
+ 3 : "Python31_x86"
|
|
|
+ }
|
|
|
+ },
|
|
|
+"Darwin" :
|
|
|
+ {
|
|
|
+ "folder": "mac",
|
|
|
+ "versions" : {
|
|
|
+ 2 : "Python26_x86",
|
|
|
+ 3 : "Python31_x86"
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+import platform
|
|
|
+system = platform.system()
|
|
|
+version = sys.version_info[0]
|
|
|
+if system in platform_map:
|
|
|
+ if version in platform_map[system]["versions"]:
|
|
|
+ mod_path = os.path.join(sys.path[0], "modules", platform_map[system]["folder"], platform_map[system]["versions"][version])
|
|
|
+ print mod_path
|
|
|
+ sys.path.append(mod_path)
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Configuration
|
|
|
+# #####################################################
|
|
|
+DEFAULTS = {
|
|
|
+"bgcolor" : [0, 0, 0],
|
|
|
+"bgalpha" : 1.0,
|
|
|
+"camera" :
|
|
|
+ {
|
|
|
+ "name" : "default_camera",
|
|
|
+ "type" : "perspective",
|
|
|
+ "near" : 1,
|
|
|
+ "far" : 10000,
|
|
|
+ "fov" : 60,
|
|
|
+ "aspect": 1.333,
|
|
|
+ "position" : [0, 0, 10],
|
|
|
+ "target" : [0, 0, 0]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+MATERIALS_IN_SCENE = True
|
|
|
+DEBUG_FBX_JSON = False
|
|
|
+
|
|
|
+# default colors for debugging (each material gets one distinct color):
|
|
|
+# white, red, green, blue, yellow, cyan, magenta
|
|
|
+COLORS = [0xeeeeee, 0xee0000, 0x00ee00, 0x0000ee, 0xeeee00, 0x00eeee, 0xee00ee]
|
|
|
+
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Templates - scene
|
|
|
+# #####################################################
|
|
|
+TEMPLATE_SCENE_ASCII = u"""\
|
|
|
+// Converted from: %(fname)s
|
|
|
+// Generated with FBX -> Three.js converter
|
|
|
+// http://github.com/alteredq/three.js/blob/master/utils/exporters/fbx/convert_fbx_three.py
|
|
|
+
|
|
|
+var url_base = %(url_base)s,
|
|
|
+ url_models = url_base + "models/",
|
|
|
+ url_textures = url_base + "textures/";
|
|
|
+
|
|
|
+var scene = {
|
|
|
+%(sections)s
|
|
|
+
|
|
|
+"defaults" :
|
|
|
+{
|
|
|
+ "bgcolor" : %(bgcolor)s,
|
|
|
+ "bgalpha" : %(bgalpha)f,
|
|
|
+ "camera" : %(defcamera)s
|
|
|
+}
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+postMessage( scene );
|
|
|
+
|
|
|
+"""
|
|
|
+
|
|
|
+TEMPLATE_SECTION = """
|
|
|
+"%s" :
|
|
|
+{
|
|
|
+%s
|
|
|
+},
|
|
|
+"""
|
|
|
+
|
|
|
+TEMPLATE_OBJECT = """\
|
|
|
+ %(object_id)s : {
|
|
|
+ "geometry" : %(geometry_id)s,
|
|
|
+ "materials" : [ %(material_id)s ],
|
|
|
+ "position" : %(position)s,
|
|
|
+ "rotation" : %(rotation)s,
|
|
|
+ "scale" : %(scale)s,
|
|
|
+ "visible" : true
|
|
|
+ }"""
|
|
|
+
|
|
|
+TEMPLATE_GEOMETRY = """\
|
|
|
+ %(geometry_id)s : {
|
|
|
+ "type" : "ascii_mesh",
|
|
|
+ "url" : url_models + %(model_file)s
|
|
|
+ }"""
|
|
|
+
|
|
|
+TEMPLATE_TEXTURE = """\
|
|
|
+ %(texture_id)s : {
|
|
|
+ "url": url_textures + %(texture_file)s
|
|
|
+ }"""
|
|
|
+
|
|
|
+TEMPLATE_MATERIAL_SCENE = """\
|
|
|
+ %(material_id)s : {
|
|
|
+ "type": %(type)s,
|
|
|
+ "parameters": { %(parameters)s }
|
|
|
+ }"""
|
|
|
+
|
|
|
+TEMPLATE_CAMERA_PERSPECTIVE = """\
|
|
|
+ %(camera_id)s : {
|
|
|
+ "type" : "perspective",
|
|
|
+ "fov" : %(fov)f,
|
|
|
+ "aspect": %(aspect)f,
|
|
|
+ "near" : %(near)f,
|
|
|
+ "far" : %(far)f,
|
|
|
+ "position": %(position)s,
|
|
|
+ "target" : %(target)s
|
|
|
+ }"""
|
|
|
+
|
|
|
+TEMPLATE_CAMERA_ORTHO = """\
|
|
|
+ %(camera_id)s: {
|
|
|
+ "type" : "ortho",
|
|
|
+ "left" : %(left)f,
|
|
|
+ "right" : %(right)f,
|
|
|
+ "top" : %(top)f,
|
|
|
+ "bottom": %(bottom)f,
|
|
|
+ "near" : %(near)f,
|
|
|
+ "far" : %(far)f,
|
|
|
+ "position": %(position)s,
|
|
|
+ "target" : %(target)s
|
|
|
+ }"""
|
|
|
+
|
|
|
+TEMPLATE_VEC3 = '[ %f, %f, %f ]'
|
|
|
+TEMPLATE_VEC2 = '[ %f, %f ]'
|
|
|
+TEMPLATE_STRING = '"%s"'
|
|
|
+TEMPLATE_HEX = "0x%06x"
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Templates - model
|
|
|
+# #####################################################
|
|
|
+TEMPLATE_MODEL_ASCII = u"""\
|
|
|
+// Converted from: %(fname)s
|
|
|
+// vertices: %(nvertex)d
|
|
|
+// faces: %(nface)d
|
|
|
+// materials: %(nmaterial)d
|
|
|
+//
|
|
|
+// Generated with FBX -> Three.js converter
|
|
|
+// http://github.com/alteredq/three.js/blob/master/utils/exporters/fbx/convert_fbx_three.py
|
|
|
+
|
|
|
+var model = {
|
|
|
+ 'materials': [%(materials)s],
|
|
|
+
|
|
|
+ 'normals': [%(normals)s],
|
|
|
+
|
|
|
+ 'vertices': [%(vertices)s],
|
|
|
+
|
|
|
+ 'uvs': [%(uvs)s],
|
|
|
+ 'uvs2': [%(uvs2)s],
|
|
|
+
|
|
|
+ 'triangles': [%(triangles)s],
|
|
|
+ 'triangles_n': [%(triangles_n)s],
|
|
|
+ 'triangles_uv': [%(triangles_uv)s],
|
|
|
+ 'triangles_n_uv': [%(triangles_n_uv)s],
|
|
|
+
|
|
|
+ 'quads': [%(quads)s],
|
|
|
+ 'quads_n': [%(quads_n)s],
|
|
|
+ 'quads_uv': [%(quads_uv)s],
|
|
|
+ 'quads_n_uv': [%(quads_n_uv)s],
|
|
|
+
|
|
|
+ 'end': (new Date).getTime()
|
|
|
+ }
|
|
|
+
|
|
|
+postMessage( model );
|
|
|
+"""
|
|
|
+
|
|
|
+TEMPLATE_VERTEX = "%f,%f,%f"
|
|
|
+
|
|
|
+TEMPLATE_UV_TRI = "%f,%f,%f,%f,%f,%f"
|
|
|
+TEMPLATE_UV_QUAD = "%f,%f,%f,%f,%f,%f,%f,%f"
|
|
|
+
|
|
|
+TEMPLATE_TRI = "%d,%d,%d,%d"
|
|
|
+TEMPLATE_QUAD = "%d,%d,%d,%d,%d"
|
|
|
+
|
|
|
+TEMPLATE_TRI_UV = "%d,%d,%d,%d,%d,%d,%d"
|
|
|
+TEMPLATE_QUAD_UV = "%d,%d,%d,%d,%d,%d,%d,%d,%d"
|
|
|
+
|
|
|
+TEMPLATE_TRI_N = "%d,%d,%d,%d,%d,%d,%d"
|
|
|
+TEMPLATE_QUAD_N = "%d,%d,%d,%d,%d,%d,%d,%d,%d"
|
|
|
+
|
|
|
+TEMPLATE_TRI_N_UV = "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d"
|
|
|
+TEMPLATE_QUAD_N_UV = "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d"
|
|
|
+
|
|
|
+TEMPLATE_N = "%f,%f,%f"
|
|
|
+TEMPLATE_UV = "%f,%f"
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Templates - misc
|
|
|
+# #####################################################
|
|
|
+TEMPLATE_HTACCESS = """\
|
|
|
+<Files *.js>
|
|
|
+SetOutputFilter DEFLATE
|
|
|
+</Files>
|
|
|
+"""
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Parser - global settings
|
|
|
+# #####################################################
|
|
|
+def extract_color(color):
|
|
|
+ return [color.mRed, color.mGreen, color.mBlue]
|
|
|
+
|
|
|
+def extract_vec2(v):
|
|
|
+ return [v[0], v[1]]
|
|
|
+
|
|
|
+def extract_vec3(v):
|
|
|
+ return [v[0], v[1], v[2]]
|
|
|
+
|
|
|
+def extract_global_settings(settings):
|
|
|
+ settings = {
|
|
|
+ "ambient_color" : extract_color(settings.GetAmbientColor()),
|
|
|
+ "default_camera": settings.GetDefaultCamera().Buffer()
|
|
|
+ }
|
|
|
+
|
|
|
+ return settings
|
|
|
+
|
|
|
+def extract_object_properties(object):
|
|
|
+ info = {}
|
|
|
+
|
|
|
+ property = object.GetFirstProperty()
|
|
|
+ while property.IsValid():
|
|
|
+ name = property.GetName().Buffer()
|
|
|
+
|
|
|
+ value = "UNIDENTIFIED"
|
|
|
+
|
|
|
+ ptype = property.GetPropertyDataType().GetType()
|
|
|
+ if ptype in [eBOOL1, eDOUBLE1, eINTEGER1, eDOUBLE4, eDOUBLE3, eFLOAT1]:
|
|
|
+ value = property.Get()
|
|
|
+ elif ptype == eSTRING:
|
|
|
+ value = property.Get().Buffer()
|
|
|
+
|
|
|
+ info[name] = {
|
|
|
+ "label" : property.GetLabel().Buffer(),
|
|
|
+ "type" : property.GetPropertyDataType().GetName(),
|
|
|
+ "value" : value
|
|
|
+ }
|
|
|
+
|
|
|
+ property = object.GetNextProperty(property)
|
|
|
+
|
|
|
+ return info
|
|
|
+
|
|
|
+def extract_node_generic_info(node):
|
|
|
+ info = []
|
|
|
+
|
|
|
+ node_info = {
|
|
|
+ "name" : node.GetName(),
|
|
|
+ "type" : node.ClassId.GetFbxFileTypeName(),
|
|
|
+ "properties" : extract_object_properties(node)
|
|
|
+ }
|
|
|
+
|
|
|
+ info.append(node_info)
|
|
|
+
|
|
|
+ for i in range(node.GetChildCount()):
|
|
|
+ info += extract_node_generic_info(node.GetChild(i))
|
|
|
+
|
|
|
+ return info
|
|
|
+
|
|
|
+def extract_generic_info(scene):
|
|
|
+ info_list = []
|
|
|
+
|
|
|
+ root_node = scene.GetRootNode()
|
|
|
+ for i in range(root_node.GetChildCount()):
|
|
|
+ child = root_node.GetChild(i)
|
|
|
+ info_list += extract_node_generic_info(child)
|
|
|
+
|
|
|
+ info_dict = {}
|
|
|
+ for item in info_list:
|
|
|
+ name = item["name"]
|
|
|
+ info_dict[name] = item["properties"]
|
|
|
+
|
|
|
+ return info_dict
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Parser - hierarchy
|
|
|
+# #####################################################
|
|
|
+def extract_node_hierarchy(node, depth):
|
|
|
+ hierarchy = { "name" : node.GetName(), "depth" : depth, "children" : [] }
|
|
|
+
|
|
|
+ for i in range(node.GetChildCount()):
|
|
|
+ hierarchy["children"].append(extract_node_hierarchy(node.GetChild(i), depth + 1))
|
|
|
+
|
|
|
+ return hierarchy
|
|
|
+
|
|
|
+def extract_hierarchy(scene):
|
|
|
+ root = scene.GetRootNode()
|
|
|
+
|
|
|
+ hierarchy = { "name" : "root", "depth" : 0, "children" : [] }
|
|
|
+
|
|
|
+ for i in range(root.GetChildCount()):
|
|
|
+ hierarchy["children"].append(extract_node_hierarchy(root.GetChild(i), 1))
|
|
|
+
|
|
|
+ return hierarchy
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Parser - mesh - layers
|
|
|
+# #####################################################
|
|
|
+def extract_layer_groups(layer, poly_count):
|
|
|
+ group = []
|
|
|
+
|
|
|
+ polygroups = layer.GetPolygonGroups()
|
|
|
+ if polygroups and \
|
|
|
+ polygroups.GetMappingMode() == KFbxLayerElement.eBY_POLYGON and \
|
|
|
+ polygroups.GetReferenceMode() == KFbxLayerElement.eINDEX:
|
|
|
+
|
|
|
+ polygroups_index_array = polygroups.GetIndexArray()
|
|
|
+ group = []
|
|
|
+ for p in range(poly_count):
|
|
|
+ group_id = polygroups_index_array.GetAt(p)
|
|
|
+ group.append(group_id)
|
|
|
+
|
|
|
+ return group
|
|
|
+
|
|
|
+def extract_layer_materials(layer, poly_count, mesh):
|
|
|
+ material_index_layer = []
|
|
|
+
|
|
|
+ materials = layer.GetMaterials()
|
|
|
+ if materials:
|
|
|
+ index_array = materials.GetIndexArray()
|
|
|
+ index_array_count = index_array.GetCount()
|
|
|
+ for i in range(index_array_count):
|
|
|
+ material_index_layer.append(index_array.GetAt(i))
|
|
|
+
|
|
|
+ return material_index_layer
|
|
|
+
|
|
|
+def extract_layer_uvs(layer, poly_count, mesh):
|
|
|
+ uv_values = []
|
|
|
+ uv_index_layer = []
|
|
|
+
|
|
|
+ uvs = layer.GetUVs()
|
|
|
+ if uvs:
|
|
|
+ uvs_array = uvs.GetDirectArray()
|
|
|
+ uvs_count = uvs_array.GetCount()
|
|
|
+
|
|
|
+ # values
|
|
|
+ for i in range(uvs_count):
|
|
|
+ uv = extract_vec2(uvs_array.GetAt(i))
|
|
|
+ uv_values.append(uv)
|
|
|
+
|
|
|
+ # indices
|
|
|
+ if uvs.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT \
|
|
|
+ and uvs.GetReferenceMode() == KFbxLayerElement.eDIRECT:
|
|
|
+
|
|
|
+ for p in range(poly_count):
|
|
|
+ tmp = []
|
|
|
+ polygon_size = mesh.GetPolygonSize(p)
|
|
|
+ for v in range(polygon_size):
|
|
|
+ id = mesh.GetPolygonVertex(p, v)
|
|
|
+ tmp.append(id)
|
|
|
+
|
|
|
+ uv_index_layer.append(tmp)
|
|
|
+
|
|
|
+ elif uvs.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT \
|
|
|
+ and uvs.GetReferenceMode() == KFbxLayerElement.eINDEX_TO_DIRECT:
|
|
|
+
|
|
|
+ uvs_index_array = uvs.GetIndexArray()
|
|
|
+ for p in range(poly_count):
|
|
|
+ tmp = []
|
|
|
+ polygon_size = mesh.GetPolygonSize(p)
|
|
|
+ for v in range(polygon_size):
|
|
|
+ control_point_index = mesh.GetPolygonVertex(p, v)
|
|
|
+ id = uvs_index_array.GetAt(control_point_index)
|
|
|
+ tmp.append(id)
|
|
|
+
|
|
|
+ uv_index_layer.append(tmp)
|
|
|
+
|
|
|
+ elif uvs.GetMappingMode() == KFbxLayerElement.eBY_POLYGON_VERTEX \
|
|
|
+ and ( uvs.GetReferenceMode() == KFbxLayerElement.eDIRECT \
|
|
|
+ or uvs.GetReferenceMode() == KFbxLayerElement.eINDEX_TO_DIRECT ):
|
|
|
+
|
|
|
+ for p in range(poly_count):
|
|
|
+ tmp = []
|
|
|
+ polygon_size = mesh.GetPolygonSize(p)
|
|
|
+ for v in range(polygon_size):
|
|
|
+ id = mesh.GetTextureUVIndex(p, v)
|
|
|
+ tmp.append(id)
|
|
|
+
|
|
|
+ uv_index_layer.append(tmp)
|
|
|
+
|
|
|
+ return uv_values, uv_index_layer
|
|
|
+
|
|
|
+def extract_layer_colors(layer, poly_count, mesh):
|
|
|
+ color_values = []
|
|
|
+ color_index_layer = []
|
|
|
+
|
|
|
+ colors = layer.GetVertexColors()
|
|
|
+ if colors:
|
|
|
+ colors_array = colors.GetDirectArray()
|
|
|
+ colors_count = colors_array.GetCount()
|
|
|
+
|
|
|
+ # values
|
|
|
+ tmp = []
|
|
|
+ for i in range(colors_count):
|
|
|
+ color = extract_color(colors_array.GetAt(i))
|
|
|
+ color_values.append(color)
|
|
|
+
|
|
|
+ # indices
|
|
|
+ if colors.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT \
|
|
|
+ and colors.GetReferenceMode() == KFbxLayerElement.eDIRECT:
|
|
|
+
|
|
|
+ for p in range(poly_count):
|
|
|
+ tmp = []
|
|
|
+ polygon_size = mesh.GetPolygonSize(p)
|
|
|
+ for v in range(polygon_size):
|
|
|
+ id = mesh.GetPolygonVertex(p, v)
|
|
|
+ tmp.append(id)
|
|
|
+
|
|
|
+ color_index_layer.append(tmp)
|
|
|
+
|
|
|
+ elif colors.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT \
|
|
|
+ and colors.GetReferenceMode() == KFbxLayerElement.eINDEX_TO_DIRECT:
|
|
|
+
|
|
|
+ for p in range(poly_count):
|
|
|
+ tmp = []
|
|
|
+ polygon_size = mesh.GetPolygonSize(p)
|
|
|
+ for v in range(polygon_size):
|
|
|
+ control_point_index = mesh.GetPolygonVertex(p, v)
|
|
|
+ id = colors.GetIndexArray().GetAt(control_point_index)
|
|
|
+ tmp.append(id)
|
|
|
+
|
|
|
+ color_index_layer.append(tmp)
|
|
|
+
|
|
|
+ elif colors.GetMappingMode() == KFbxLayerElement.eBY_POLYGON_VERTEX \
|
|
|
+ and colors.GetReferenceMode() == KFbxLayerElement.eDIRECT:
|
|
|
+
|
|
|
+ vertex_id = 0
|
|
|
+ for p in range(poly_count):
|
|
|
+ tmp = []
|
|
|
+ polygon_size = mesh.GetPolygonSize(p)
|
|
|
+ for v in range(polygon_size):
|
|
|
+ tmp.append(vertex_id)
|
|
|
+ vertex_id += 1
|
|
|
+
|
|
|
+ color_index_layer.append(tmp)
|
|
|
+
|
|
|
+ elif colors.GetMappingMode() == KFbxLayerElement.eBY_POLYGON_VERTEX \
|
|
|
+ and colors.GetReferenceMode() == KFbxLayerElement.eINDEX_TO_DIRECT:
|
|
|
+
|
|
|
+ colors_index_array = colors.GetIndexArray()
|
|
|
+ vertex_id = 0
|
|
|
+ for p in range(poly_count):
|
|
|
+ tmp = []
|
|
|
+ polygon_size = mesh.GetPolygonSize(p)
|
|
|
+ for v in range(polygon_size):
|
|
|
+ id = colors_index_array.GetAt(vertex_id)
|
|
|
+ tmp.append(id)
|
|
|
+ vertex_id += 1
|
|
|
+
|
|
|
+ color_index_layer.append(tmp)
|
|
|
+
|
|
|
+ return color_values, color_index_layer
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Parser - mesh - polygons
|
|
|
+# #####################################################
|
|
|
+def extract_polygons(mesh):
|
|
|
+ poly_count = mesh.GetPolygonCount()
|
|
|
+ layer_count = mesh.GetLayerCount()
|
|
|
+ control_points = mesh.GetControlPoints()
|
|
|
+
|
|
|
+ layers_groups = []
|
|
|
+ layers_uvs = []
|
|
|
+ layers_colors = []
|
|
|
+
|
|
|
+ indices_uv = []
|
|
|
+ indices_color = []
|
|
|
+ indices_vertex = []
|
|
|
+ indices_material = []
|
|
|
+
|
|
|
+ # per layer data
|
|
|
+ for l in range(layer_count):
|
|
|
+
|
|
|
+ layer = mesh.GetLayer(l)
|
|
|
+
|
|
|
+ # groups
|
|
|
+ group = extract_layer_groups(layer, poly_count)
|
|
|
+ if group:
|
|
|
+ groups_layers.append(group)
|
|
|
+
|
|
|
+ # uvs
|
|
|
+ uv_values, uv_index_layer = extract_layer_uvs(layer, poly_count, mesh)
|
|
|
+
|
|
|
+ if uv_values:
|
|
|
+ layers_uvs.append(uv_values)
|
|
|
+
|
|
|
+ if uv_index_layer:
|
|
|
+ indices_uv.append(uv_index_layer)
|
|
|
+
|
|
|
+ # colors
|
|
|
+ color_values, color_index_layer = extract_layer_colors(layer, poly_count, mesh)
|
|
|
+
|
|
|
+ if color_values:
|
|
|
+ layers_colors.append(color_values)
|
|
|
+
|
|
|
+ if color_index_layer:
|
|
|
+ indices_color.append(color_index_layer)
|
|
|
+
|
|
|
+ # materials
|
|
|
+ material_index_layer = extract_layer_materials(layer, poly_count, mesh)
|
|
|
+ if material_index_layer:
|
|
|
+ indices_material.append(material_index_layer)
|
|
|
+
|
|
|
+ # single layer data
|
|
|
+ for p in range(poly_count):
|
|
|
+ face_vertex_index = []
|
|
|
+ polygon_size = mesh.GetPolygonSize(p)
|
|
|
+ for v in range(polygon_size):
|
|
|
+ control_point_index = mesh.GetPolygonVertex(p, v)
|
|
|
+ face_vertex_index.append(control_point_index)
|
|
|
+
|
|
|
+ indices_vertex.append(face_vertex_index)
|
|
|
+
|
|
|
+
|
|
|
+ polygons = {}
|
|
|
+ conditional_set(polygons, "indices_vertex", indices_vertex)
|
|
|
+ conditional_set(polygons, "indices_uv", indices_uv)
|
|
|
+ conditional_set(polygons, "indices_color", indices_color)
|
|
|
+ conditional_set(polygons, "indices_material", indices_material)
|
|
|
+ conditional_set(polygons, "layers_uvs", layers_uvs)
|
|
|
+ conditional_set(polygons, "layers_colors", layers_colors)
|
|
|
+ conditional_set(polygons, "layers_groups", layers_groups)
|
|
|
+
|
|
|
+ return polygons
|
|
|
+
|
|
|
+def extract_control_points(mesh):
|
|
|
+ control_points_count = mesh.GetControlPointsCount()
|
|
|
+ control_points = mesh.GetControlPoints()
|
|
|
+ layer_count = mesh.GetLayerCount()
|
|
|
+
|
|
|
+ coordinates = []
|
|
|
+ layers_normals = []
|
|
|
+
|
|
|
+ for i in range(control_points_count):
|
|
|
+ coordinates.append( extract_vec3( control_points[i] ) )
|
|
|
+
|
|
|
+ for layer in range(layer_count):
|
|
|
+ normals = mesh.GetLayer(layer).GetNormals()
|
|
|
+ if normals:
|
|
|
+ znormals = []
|
|
|
+ for i in range(control_points_count):
|
|
|
+ if normals.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT:
|
|
|
+ if normals.GetReferenceMode() == KFbxLayerElement.eDIRECT:
|
|
|
+ znormals.append( extract_vec3( normals.GetDirectArray().GetAt(i) ) )
|
|
|
+
|
|
|
+ if znormals:
|
|
|
+ layers_normals.append( znormals )
|
|
|
+
|
|
|
+
|
|
|
+ points = {}
|
|
|
+
|
|
|
+ conditional_set(points, "coordinates", coordinates)
|
|
|
+ conditional_set(points, "normals", layers_normals)
|
|
|
+
|
|
|
+ return points
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Parser - mesh - materials
|
|
|
+# #####################################################
|
|
|
+def extract_materials(mesh):
|
|
|
+ materials_layers = []
|
|
|
+
|
|
|
+ material_count = 0
|
|
|
+ layer_count = mesh.GetLayerCount()
|
|
|
+
|
|
|
+ node = None
|
|
|
+ if mesh:
|
|
|
+ node = mesh.GetNode()
|
|
|
+ if node:
|
|
|
+ material_count = node.GetMaterialCount()
|
|
|
+
|
|
|
+ for layer in range(layer_count):
|
|
|
+ layer_materials = []
|
|
|
+
|
|
|
+ materials = mesh.GetLayer(layer).GetMaterials()
|
|
|
+ if materials:
|
|
|
+ if materials.GetReferenceMode() == KFbxLayerElement.eINDEX:
|
|
|
+ #Materials are in an undefined external table
|
|
|
+ continue
|
|
|
+
|
|
|
+ if material_count > 0:
|
|
|
+
|
|
|
+ for i in range(material_count):
|
|
|
+ material = node.GetMaterial(i)
|
|
|
+
|
|
|
+ zmaterial = {
|
|
|
+ "name" : material.GetName()
|
|
|
+ }
|
|
|
+
|
|
|
+ # Get the implementation to see if it's a hardware shader.
|
|
|
+ implementation = GetImplementation(material, "ImplementationHLSL")
|
|
|
+ implemenation_type = "HLSL"
|
|
|
+ if not implementation:
|
|
|
+ implementation = GetImplementation(material, "ImplementationCGFX")
|
|
|
+ implemenation_type = "CGFX"
|
|
|
+ if implementation:
|
|
|
+ # Now we have a hardware shader, let's read it
|
|
|
+ zmaterial["hardware_shader_type"] = implemenation_type.Buffer()
|
|
|
+ # Skipped parsing of shaders
|
|
|
+
|
|
|
+ elif material.GetClassId().Is(KFbxSurfaceLambert.ClassId):
|
|
|
+ ambient = material.GetAmbientColor()
|
|
|
+ diffuse = material.GetDiffuseColor()
|
|
|
+ emissive = material.GetEmissiveColor()
|
|
|
+
|
|
|
+ zmaterial["ambient"] = [ambient.Get()[0], ambient.Get()[1], ambient.Get()[2]]
|
|
|
+ zmaterial["diffuse"] = [diffuse.Get()[0], diffuse.Get()[1], diffuse.Get()[2]]
|
|
|
+ zmaterial["emissive"] = [emissive.Get()[0], emissive.Get()[1], emissive.Get()[2]]
|
|
|
+
|
|
|
+ zmaterial["opacity"] = material.GetTransparencyFactor().Get()
|
|
|
+
|
|
|
+ elif (material.GetClassId().Is(KFbxSurfacePhong.ClassId)):
|
|
|
+ ambient = material.GetAmbientColor()
|
|
|
+ diffuse = material.GetDiffuseColor()
|
|
|
+ emissive = material.GetEmissiveColor()
|
|
|
+ specular = material.GetSpecularColor()
|
|
|
+
|
|
|
+ zmaterial["ambient"] = [ambient.Get()[0], ambient.Get()[1], ambient.Get()[2]]
|
|
|
+ zmaterial["diffuse"] = [diffuse.Get()[0], diffuse.Get()[1], diffuse.Get()[2]]
|
|
|
+ zmaterial["emissive"] = [emissive.Get()[0], emissive.Get()[1], emissive.Get()[2]]
|
|
|
+ zmaterial["specular"] = [specular.Get()[0], specular.Get()[1], specular.Get()[2]]
|
|
|
+
|
|
|
+ zmaterial["opacity"] = material.GetTransparencyFactor().Get()
|
|
|
+
|
|
|
+ zmaterial["shininess"] = material.GetShininess().Get()
|
|
|
+ zmaterial["reflectivity"] = material.GetReflectionFactor().Get()
|
|
|
+
|
|
|
+ zmaterial["shading_model"] = material.GetShadingModel().Get().Buffer()
|
|
|
+ layer_materials.append(zmaterial)
|
|
|
+
|
|
|
+ if layer_materials:
|
|
|
+ materials_layers.append(layer_materials)
|
|
|
+
|
|
|
+ return materials_layers
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Parser - mesh - textures
|
|
|
+# #####################################################
|
|
|
+def extract_texture_info(texture, blend_mode):
|
|
|
+ mapping_types = [ "Null", "Planar", "Spherical", "Cylindrical", "Box", "Face", "UV", "Environment" ]
|
|
|
+ alpha_sources = [ "None", "RGB Intensity", "Black" ]
|
|
|
+ blend_modes = [ "Translucent", "Add", "Modulate", "Modulate2" ]
|
|
|
+ material_uses = [ "Model Material", "Default Material" ]
|
|
|
+ texture_uses = [ "Standard", "Shadow Map", "Light Map", "Spherical Reflection Map", "Sphere Reflection Map" ]
|
|
|
+ planar_mapping_normals = [ "X", "Y", "Z" ]
|
|
|
+
|
|
|
+ info = {
|
|
|
+ "name" : texture.GetName(),
|
|
|
+ "filename" : texture.GetFileName(),
|
|
|
+ "scale_u" : texture.GetScaleU(),
|
|
|
+ "scale_v" : texture.GetScaleV(),
|
|
|
+ "swap_uv" : texture.GetSwapUV(),
|
|
|
+ "translation_u" : texture.GetTranslationU(),
|
|
|
+ "translation_v" : texture.GetTranslationV(),
|
|
|
+ "rotation_u" : texture.GetRotationU(),
|
|
|
+ "rotation_v" : texture.GetRotationV(),
|
|
|
+ "rotation_w" : texture.GetRotationW(),
|
|
|
+ "mapping" : mapping_types[texture.GetMappingType()],
|
|
|
+ "alpha_source" : alpha_sources[texture.GetAlphaSource()],
|
|
|
+ "cropping_left" : texture.GetCroppingLeft(),
|
|
|
+ "cropping_top" : texture.GetCroppingTop(),
|
|
|
+ "cropping_right" : texture.GetCroppingRight(),
|
|
|
+ "cropping_bottom" : texture.GetCroppingBottom(),
|
|
|
+ "alpha" : texture.GetDefaultAlpha(),
|
|
|
+ "material_use" : material_uses[texture.GetMaterialUse()],
|
|
|
+ "texture_use" : texture_uses[texture.GetTextureUse()]
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if texture.GetMappingType() == KFbxTexture.ePLANAR:
|
|
|
+ info["planar_mapping_normal"] = planar_mapping_normals[texture.GetPlanarMappingNormal()]
|
|
|
+
|
|
|
+ if blend_mode >= 0:
|
|
|
+ info["blend_mode"] = blend_modes[blend_mode]
|
|
|
+
|
|
|
+ return info
|
|
|
+
|
|
|
+def extract_texture_info_by_property(property, material_index):
|
|
|
+ textures = []
|
|
|
+
|
|
|
+ if property.IsValid():
|
|
|
+
|
|
|
+ #Here we have to check if it's layeredtextures, or just textures:
|
|
|
+ layered_texture_count = property.GetSrcObjectCount(KFbxLayeredTexture.ClassId)
|
|
|
+ if layered_texture_count > 0:
|
|
|
+ for j in range(layered_texture_count):
|
|
|
+ ltexture = {
|
|
|
+ "layered_texture" : j,
|
|
|
+ "layers" : []
|
|
|
+ }
|
|
|
+
|
|
|
+ layered_texture = property.GetSrcObject(KFbxLayeredTexture.ClassId, j)
|
|
|
+ texture_count = layered_texture.GetSrcObjectCount(KFbxTexture.ClassId)
|
|
|
+ for k in range(texture_count):
|
|
|
+ ztexture = lLayeredTexture.GetSrcObject(KFbxTexture.ClassId, k)
|
|
|
+ if ztexture:
|
|
|
+ # NOTE the blend mode is ALWAYS on the LayeredTexture and NOT the one on the texture.
|
|
|
+ # Why is that? because one texture can be shared on different layered textures and might
|
|
|
+ # have different blend modes.
|
|
|
+
|
|
|
+ blend_mode = layered_texture.GetTextureBlendMode(k)
|
|
|
+ texture = {
|
|
|
+ "material_index" : material_index,
|
|
|
+ "texture_index" : k,
|
|
|
+ "property_name" : property.GetName().Buffer(),
|
|
|
+ "blend_mode" : blend_mode,
|
|
|
+ "info" : extract_texture_info(ztexture, blend_mode)
|
|
|
+ }
|
|
|
+
|
|
|
+ ltexture["layers"].append(texture)
|
|
|
+
|
|
|
+ textures.append(ltexture)
|
|
|
+
|
|
|
+ # no layered texture simply get on the property
|
|
|
+ else:
|
|
|
+
|
|
|
+ texture_count = property.GetSrcObjectCount(KFbxTexture.ClassId)
|
|
|
+ for j in range(texture_count):
|
|
|
+ ztexture = property.GetSrcObject(KFbxTexture.ClassId, j)
|
|
|
+ if ztexture:
|
|
|
+ texture = {
|
|
|
+ "material_index" : material_index,
|
|
|
+ "info" : extract_texture_info(ztexture, -1)
|
|
|
+ }
|
|
|
+ textures.append(texture)
|
|
|
+
|
|
|
+ return textures
|
|
|
+
|
|
|
+def extract_textures(mesh):
|
|
|
+ textures = {}
|
|
|
+ node = mesh.GetNode()
|
|
|
+ materials_count = node.GetSrcObjectCount(KFbxSurfaceMaterial.ClassId)
|
|
|
+ for material_index in range(materials_count):
|
|
|
+ material = node.GetSrcObject(KFbxSurfaceMaterial.ClassId, material_index)
|
|
|
+
|
|
|
+ #go through all the possible textures
|
|
|
+ if material:
|
|
|
+ for texture_index in range(KFbxLayerElement.LAYERELEMENT_TYPE_TEXTURE_COUNT):
|
|
|
+ property = material.FindProperty(KFbxLayerElement.TEXTURE_CHANNEL_NAMES[texture_index])
|
|
|
+ texture = extract_texture_info_by_property(property, material_index)
|
|
|
+ if texture:
|
|
|
+ textures[property.GetName().Buffer()] = texture
|
|
|
+
|
|
|
+ return textures
|
|
|
+
|
|
|
+def extract_material_mapping(mesh):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_material_connections(mesh):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_link(mesh):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_shape(mesh):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_mesh(node):
|
|
|
+ mesh = node.GetNodeAttribute()
|
|
|
+
|
|
|
+ zmesh = {}
|
|
|
+
|
|
|
+ conditional_set(zmesh, "name", node.GetName())
|
|
|
+ conditional_set(zmesh, "shape", extract_shape(mesh))
|
|
|
+ conditional_set(zmesh, "link", extract_link(mesh))
|
|
|
+ conditional_set(zmesh, "control_points", extract_control_points(mesh))
|
|
|
+ conditional_set(zmesh, "faces", extract_polygons(mesh))
|
|
|
+ conditional_set(zmesh, "textures", extract_textures(mesh))
|
|
|
+ conditional_set(zmesh, "materials", extract_materials(mesh))
|
|
|
+ conditional_set(zmesh, "material_mapping", extract_material_mapping(mesh))
|
|
|
+ conditional_set(zmesh, "material_connections", extract_material_connections(mesh))
|
|
|
+
|
|
|
+ return zmesh
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Parser - nodes (todo)
|
|
|
+# #####################################################
|
|
|
+def extract_marker(node):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_nurb(node):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_patch(node):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_skeleton(node):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_camera(node):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_light(node):
|
|
|
+ return {}
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Parser - nodes (generic)
|
|
|
+# #####################################################
|
|
|
+def extract_target(node):
|
|
|
+ if node.GetTarget():
|
|
|
+ return node.GetTarget().GetName()
|
|
|
+ return ""
|
|
|
+
|
|
|
+def extract_transform(node):
|
|
|
+ translation = node.GetGeometricTranslation(KFbxNode.eSOURCE_SET)
|
|
|
+ rotation = node.GetGeometricRotation(KFbxNode.eSOURCE_SET)
|
|
|
+ scale = node.GetGeometricScaling(KFbxNode.eSOURCE_SET)
|
|
|
+
|
|
|
+ transform = {
|
|
|
+ "translation": [ translation[0], translation[1], translation[2] ],
|
|
|
+ "rotation" : [ rotation[0], rotation[1], rotation[2] ],
|
|
|
+ "scale" : [ scale[0], scale[1], scale[2] ],
|
|
|
+ }
|
|
|
+
|
|
|
+ return transform
|
|
|
+
|
|
|
+def extract_transform_propagation(node):
|
|
|
+ rotation_order = node.GetRotationOrder(KFbxNode.eSOURCE_SET)
|
|
|
+
|
|
|
+ order_map = {
|
|
|
+ eEULER_XYZ : "Euler XYZ",
|
|
|
+ eEULER_XZY : "Euler XZY",
|
|
|
+ eEULER_YZX : "Euler YZX",
|
|
|
+ eEULER_YXZ : "Euler YXZ",
|
|
|
+ eEULER_ZXY : "Euler ZXY",
|
|
|
+ eEULER_ZYX : "Euler ZYX",
|
|
|
+ eSPHERIC_XYZ:"Spheric XYZ"
|
|
|
+ }
|
|
|
+
|
|
|
+ order = "Euler XYZ"
|
|
|
+
|
|
|
+ if rotation_order in order_map:
|
|
|
+ order = order_map[rotation_order]
|
|
|
+
|
|
|
+ # Use the Rotation space only for the limits
|
|
|
+ # (keep using eEULER_XYZ for the rest)
|
|
|
+
|
|
|
+ if node.GetUseRotationSpaceForLimitOnly(KFbxNode.eSOURCE_SET):
|
|
|
+ only_limits = 1
|
|
|
+ else:
|
|
|
+ only_limits = 0
|
|
|
+
|
|
|
+ inherit_type = node.GetTransformationInheritType()
|
|
|
+
|
|
|
+ inherit_map = {
|
|
|
+ eINHERIT_RrSs : "RrSs",
|
|
|
+ eINHERIT_RSrs : "RSrs",
|
|
|
+ eINHERIT_Rrs : "Rrs"
|
|
|
+ }
|
|
|
+
|
|
|
+ if inherit_type in inherit_map:
|
|
|
+ inheritance = inherit_map[inherit_type]
|
|
|
+
|
|
|
+ transform_propagation = {
|
|
|
+ "rotation_order" : order,
|
|
|
+ "only_limits" : only_limits,
|
|
|
+ "inheritance" : inheritance
|
|
|
+ }
|
|
|
+
|
|
|
+ return transform_propagation
|
|
|
+
|
|
|
+def extract_pivots(node):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_user_properties(node):
|
|
|
+ return {}
|
|
|
+
|
|
|
+def extract_node_content(node):
|
|
|
+ nodes = []
|
|
|
+
|
|
|
+ if node.GetNodeAttribute() == None:
|
|
|
+ return "NULL"
|
|
|
+
|
|
|
+ else:
|
|
|
+ attribute_type = node.GetNodeAttribute().GetAttributeType()
|
|
|
+
|
|
|
+ ztype = "undefined"
|
|
|
+ data = {}
|
|
|
+
|
|
|
+ type_map = {
|
|
|
+ KFbxNodeAttribute.eMARKER : ["marker", extract_marker],
|
|
|
+ KFbxNodeAttribute.eSKELETON : ["skeleton", extract_skeleton],
|
|
|
+ KFbxNodeAttribute.eMESH : ["mesh", extract_mesh],
|
|
|
+ KFbxNodeAttribute.eNURB : ["nurb", extract_nurb],
|
|
|
+ KFbxNodeAttribute.ePATCH : ["patch", extract_patch],
|
|
|
+ KFbxNodeAttribute.eCAMERA : ["camera", extract_camera],
|
|
|
+ KFbxNodeAttribute.eLIGHT : ["light", extract_light]
|
|
|
+ }
|
|
|
+
|
|
|
+ if attribute_type in type_map:
|
|
|
+ ztype = type_map[attribute_type][0]
|
|
|
+ data = type_map[attribute_type][1](node)
|
|
|
+
|
|
|
+ content = { }
|
|
|
+
|
|
|
+ conditional_set(content, "type", ztype)
|
|
|
+ conditional_set(content, "data", data)
|
|
|
+ conditional_set(content, "target", extract_target(node))
|
|
|
+ conditional_set(content, "pivots", extract_pivots(node))
|
|
|
+ conditional_set(content, "transform", extract_transform(node))
|
|
|
+ conditional_set(content, "transform_propagation", extract_transform_propagation(node))
|
|
|
+ conditional_set(content, "user_properties", extract_user_properties(node))
|
|
|
+
|
|
|
+ nodes.append(content)
|
|
|
+
|
|
|
+ for i in range(node.GetChildCount()):
|
|
|
+ nodes += extract_node_content(node.GetChild(i))
|
|
|
+
|
|
|
+ return nodes
|
|
|
+
|
|
|
+def extract_nodes(scene):
|
|
|
+ nodes = []
|
|
|
+
|
|
|
+ root = scene.GetRootNode()
|
|
|
+ if root:
|
|
|
+ for i in range(root.GetChildCount()):
|
|
|
+ nodes += extract_node_content(root.GetChild(i))
|
|
|
+
|
|
|
+ return nodes
|
|
|
+
|
|
|
+def filter_mesh(item):
|
|
|
+ return item["type"] == "mesh"
|
|
|
+
|
|
|
+def extract_meshes(scene):
|
|
|
+ nodes = extract_nodes(scene)
|
|
|
+ meshes = filter(filter_mesh, nodes)
|
|
|
+ return meshes
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# JSON extractors
|
|
|
+# #####################################################
|
|
|
+def get_material_texture(material_index, textures, property):
|
|
|
+ result = [t for t in textures.get(property, []) if t["material_index"] == material_index]
|
|
|
+ return result
|
|
|
+
|
|
|
+def collect_textures(data):
|
|
|
+ texture_set = set()
|
|
|
+
|
|
|
+ for mesh in data["meshes"]:
|
|
|
+ for texture_type in mesh["data"]["textures"]:
|
|
|
+ for texture in mesh["data"]["textures"][texture_type]:
|
|
|
+ texture_file = base_filename(texture["info"]["filename"])
|
|
|
+ texture_set.add(texture_file)
|
|
|
+
|
|
|
+ return list(texture_set)
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Generator - model
|
|
|
+# #####################################################
|
|
|
+def generate_vertex(v):
|
|
|
+ return TEMPLATE_VERTEX % (v[0], v[1], v[2])
|
|
|
+
|
|
|
+def generate_triangle(f):
|
|
|
+ v = f['vertex']
|
|
|
+ return TEMPLATE_TRI % (v[0], v[1], v[2],
|
|
|
+ f['material'])
|
|
|
+
|
|
|
+def generate_triangle_uv(f):
|
|
|
+ v = f['vertex']
|
|
|
+ uv = f['uv']
|
|
|
+ return TEMPLATE_TRI_UV % (v[0], v[1], v[2],
|
|
|
+ f['material'],
|
|
|
+ uv[0], uv[1], uv[2])
|
|
|
+
|
|
|
+def generate_triangle_n(f):
|
|
|
+ v = f['vertex']
|
|
|
+ n = f['normal']
|
|
|
+ return TEMPLATE_TRI_N % (v[0], v[1], v[2],
|
|
|
+ f['material'],
|
|
|
+ n[0], n[1], n[2])
|
|
|
+
|
|
|
+def generate_triangle_n_uv(f):
|
|
|
+ v = f['vertex']
|
|
|
+ n = f['normal']
|
|
|
+ uv = f['uv']
|
|
|
+ return TEMPLATE_TRI_N_UV % (v[0], v[1], v[2],
|
|
|
+ f['material'],
|
|
|
+ n[0], n[1], n[2],
|
|
|
+ uv[0], uv[1], uv[2])
|
|
|
+
|
|
|
+def generate_quad(f):
|
|
|
+ vi = f['vertex']
|
|
|
+ return TEMPLATE_QUAD % (vi[0], vi[1], vi[2], vi[3],
|
|
|
+ f['material'])
|
|
|
+
|
|
|
+def generate_quad_uv(f):
|
|
|
+ v = f['vertex']
|
|
|
+ uv = f['uv']
|
|
|
+ return TEMPLATE_QUAD_UV % (v[0], v[1], v[2], v[3],
|
|
|
+ f['material'],
|
|
|
+ uv[0], uv[1], uv[2], uv[3])
|
|
|
+
|
|
|
+def generate_quad_n(f):
|
|
|
+ v = f['vertex']
|
|
|
+ n = f['normal']
|
|
|
+ return TEMPLATE_QUAD_N % (v[0], v[1], v[2], v[3],
|
|
|
+ f['material'],
|
|
|
+ n[0], n[1], n[2], n[3])
|
|
|
+
|
|
|
+def generate_quad_n_uv(f):
|
|
|
+ v = f['vertex']
|
|
|
+ n = f['normal']
|
|
|
+ uv = f['uv']
|
|
|
+ return TEMPLATE_QUAD_N_UV % (v[0], v[1], v[2], v[3],
|
|
|
+ f['material'],
|
|
|
+ n[0], n[1], n[2], n[3],
|
|
|
+ uv[0], uv[1], uv[2], uv[3])
|
|
|
+
|
|
|
+def generate_normal(n):
|
|
|
+ return TEMPLATE_N % (n[0], n[1], n[2])
|
|
|
+
|
|
|
+def generate_uv(uv):
|
|
|
+ return TEMPLATE_UV % (uv[0], uv[1])
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Generator - scene
|
|
|
+# #####################################################
|
|
|
+def generate_vec3(vec):
|
|
|
+ return TEMPLATE_VEC3 % (vec[0], vec[1], vec[2])
|
|
|
+
|
|
|
+def generate_vec2(vec):
|
|
|
+ return TEMPLATE_VEC2 % (vec[0], vec[1])
|
|
|
+
|
|
|
+def generate_hex(number):
|
|
|
+ return TEMPLATE_HEX % number
|
|
|
+
|
|
|
+def generate_string(s):
|
|
|
+ return TEMPLATE_STRING % s
|
|
|
+
|
|
|
+def generate_section(label, content):
|
|
|
+ return TEMPLATE_SECTION % (label, content)
|
|
|
+
|
|
|
+def get_mesh_filename(mesh):
|
|
|
+ object_id = mesh["data"]["name"]
|
|
|
+ filename = "%s.js" % object_id
|
|
|
+ return filename
|
|
|
+
|
|
|
+def generate_material_id_list(materials):
|
|
|
+ chunks = []
|
|
|
+ for layer in materials:
|
|
|
+ for material in layer:
|
|
|
+ chunks.append(material["name"])
|
|
|
+
|
|
|
+ return ",".join(chunks)
|
|
|
+
|
|
|
+def generate_objects(data):
|
|
|
+ chunks = []
|
|
|
+
|
|
|
+ for mesh in data["meshes"]:
|
|
|
+ object_id = mesh["data"]["name"]
|
|
|
+ geometry_id = "geo_%s" % object_id
|
|
|
+
|
|
|
+ material_id = generate_material_id_list(mesh["data"]["materials"])
|
|
|
+
|
|
|
+ position = mesh["transform"]["translation"]
|
|
|
+ rotation = mesh["transform"]["rotation"]
|
|
|
+ scale = mesh["transform"]["scale"]
|
|
|
+
|
|
|
+ # hunt for local transform
|
|
|
+ if object_id in data["generic_info"]:
|
|
|
+ gi = data["generic_info"][object_id]
|
|
|
+ lt = gi.get("Lcl Translation", {})
|
|
|
+ local_translation = lt.get("value", [0,0,0])
|
|
|
+ position[0] += local_translation[0]
|
|
|
+ position[1] += local_translation[1]
|
|
|
+ position[2] += local_translation[2]
|
|
|
+
|
|
|
+ object_string = TEMPLATE_OBJECT % {
|
|
|
+ "object_id" : generate_string(object_id),
|
|
|
+ "geometry_id" : generate_string(geometry_id),
|
|
|
+ "material_id" : generate_string(material_id),
|
|
|
+ "position" : generate_vec3(position),
|
|
|
+ "rotation" : generate_vec3(rotation),
|
|
|
+ "scale" : generate_vec3(scale)
|
|
|
+ }
|
|
|
+ chunks.append(object_string)
|
|
|
+
|
|
|
+ return ",\n\n".join(chunks)
|
|
|
+
|
|
|
+def generate_geometries(data):
|
|
|
+ chunks = []
|
|
|
+
|
|
|
+ for mesh in data["meshes"]:
|
|
|
+ geometry_id = "geo_%s" % mesh["data"]["name"]
|
|
|
+ model_filename = get_mesh_filename(mesh)
|
|
|
+
|
|
|
+ geometry_string = TEMPLATE_GEOMETRY % {
|
|
|
+ "geometry_id" : generate_string(geometry_id),
|
|
|
+ "model_file" : generate_string(model_filename)
|
|
|
+ }
|
|
|
+ chunks.append(geometry_string)
|
|
|
+
|
|
|
+ return ",\n\n".join(chunks)
|
|
|
+
|
|
|
+def generate_textures_scene(data):
|
|
|
+ chunks = []
|
|
|
+ texture_set = set()
|
|
|
+
|
|
|
+ for mesh in data["meshes"]:
|
|
|
+ for texture_type in mesh["data"]["textures"]:
|
|
|
+ for texture in mesh["data"]["textures"][texture_type]:
|
|
|
+ texture_id = texture["info"]["name"]
|
|
|
+
|
|
|
+ if texture_id not in texture_set:
|
|
|
+ texture_set.add(texture_id)
|
|
|
+ texture_file = base_filename(texture["info"]["filename"])
|
|
|
+
|
|
|
+ texture_string = TEMPLATE_TEXTURE % {
|
|
|
+ "texture_id" : generate_string(texture_id),
|
|
|
+ "texture_file" : generate_string(texture_file)
|
|
|
+ }
|
|
|
+ chunks.append(texture_string)
|
|
|
+
|
|
|
+ return ",\n\n".join(chunks)
|
|
|
+
|
|
|
+def generate_materials_scene(data):
|
|
|
+ chunks = []
|
|
|
+
|
|
|
+ type_map = {
|
|
|
+ "Lambert" : "MeshLambertMaterial",
|
|
|
+ "Phong" : "MeshPhongMaterial"
|
|
|
+ }
|
|
|
+
|
|
|
+ for mesh in data["meshes"]:
|
|
|
+ for layer in mesh["data"]["materials"]:
|
|
|
+ for material_index in range(len(layer)):
|
|
|
+ material = layer[material_index]
|
|
|
+ material_id = material["name"]
|
|
|
+ shading = material["shading_model"]
|
|
|
+ material_type = type_map.get(shading, "MeshBasicMaterial")
|
|
|
+
|
|
|
+ parameters = "color: %s" % generate_hex(rgb2int(material["diffuse"]))
|
|
|
+
|
|
|
+ if shading == "Phong":
|
|
|
+ parameters += ", ambient: %s" % generate_hex(rgb2int(material["ambient"]))
|
|
|
+ parameters += ", specular: %s" % generate_hex(rgb2int(material["specular"]))
|
|
|
+ parameters += ", shininess: %f" % material["shininess"]
|
|
|
+
|
|
|
+ # TODO: proper handling of textures
|
|
|
+ color_map = get_material_texture(material_index, mesh["data"]["textures"], "DiffuseColor")
|
|
|
+ light_map = get_material_texture(material_index, mesh["data"]["textures"], "AmbientColor")
|
|
|
+ bump_map = get_material_texture(material_index, mesh["data"]["textures"], "Bump")
|
|
|
+
|
|
|
+ if map:
|
|
|
+ parameters += ", map: %s" % generate_string(color_map[0]["info"]["name"])
|
|
|
+ if light_map:
|
|
|
+ parameters += ", light_map: %s" % generate_string(light_map[0]["info"]["name"])
|
|
|
+ if bump_map:
|
|
|
+ parameters += ", bump_map: %s" % generate_string(bump_map[0]["info"]["name"])
|
|
|
+
|
|
|
+ material_string = TEMPLATE_MATERIAL_SCENE % {
|
|
|
+ "material_id" : generate_string(material_id),
|
|
|
+ "type" : generate_string(material_type),
|
|
|
+ "parameters" : parameters
|
|
|
+ }
|
|
|
+ chunks.append(material_string)
|
|
|
+
|
|
|
+ return ",\n\n".join(chunks)
|
|
|
+
|
|
|
+# TODO
|
|
|
+
|
|
|
+def generate_cameras(data):
|
|
|
+ cameras = data.get("cameras", [])
|
|
|
+ if not cameras:
|
|
|
+ cameras.append(DEFAULTS["camera"])
|
|
|
+
|
|
|
+ chunks = []
|
|
|
+ for camera in cameras:
|
|
|
+ if camera["type"] == "perspective":
|
|
|
+ camera_string = TEMPLATE_CAMERA_PERSPECTIVE % {
|
|
|
+ "camera_id" : generate_string(camera["name"]),
|
|
|
+ "fov" : camera["fov"],
|
|
|
+ "aspect" : camera["aspect"],
|
|
|
+ "near" : camera["near"],
|
|
|
+ "far" : camera["far"],
|
|
|
+ "position" : generate_vec3(camera["position"]),
|
|
|
+ "target" : generate_vec3(camera["target"])
|
|
|
+ }
|
|
|
+ elif camera["type"] == "ortho":
|
|
|
+ camera_string = TEMPLATE_CAMERA_ORTHO % {
|
|
|
+ "camera_id" : generate_string(camera["name"]),
|
|
|
+ "left" : camera["left"],
|
|
|
+ "right" : camera["right"],
|
|
|
+ "top" : camera["top"],
|
|
|
+ "bottom" : camera["bottom"],
|
|
|
+ "near" : camera["near"],
|
|
|
+ "far" : camera["far"],
|
|
|
+ "position" : generate_vec3(camera["position"]),
|
|
|
+ "target" : generate_vec3(camera["target"])
|
|
|
+ }
|
|
|
+
|
|
|
+ chunks.append(camera_string)
|
|
|
+
|
|
|
+ return ",\n\n".join(chunks)
|
|
|
+
|
|
|
+def generate_lights(data):
|
|
|
+ return ""
|
|
|
+
|
|
|
+def generate_ascii_scene(data):
|
|
|
+ objects = generate_objects(data)
|
|
|
+ geometries = generate_geometries(data)
|
|
|
+ textures = generate_textures_scene(data)
|
|
|
+ materials = generate_materials_scene(data)
|
|
|
+ cameras = generate_cameras(data)
|
|
|
+ lights = generate_lights(data)
|
|
|
+
|
|
|
+ sections = [
|
|
|
+ ["objects", objects],
|
|
|
+ ["geometries", geometries],
|
|
|
+ ["textures", textures],
|
|
|
+ ["materials", materials],
|
|
|
+ ["cameras", cameras],
|
|
|
+ ["lights", lights]
|
|
|
+ ]
|
|
|
+
|
|
|
+ chunks = []
|
|
|
+ for label, content in sections:
|
|
|
+ if content:
|
|
|
+ chunks.append(generate_section(label, content))
|
|
|
+
|
|
|
+ sections_string = "\n".join(chunks)
|
|
|
+
|
|
|
+ default_camera = "default_camera"
|
|
|
+
|
|
|
+ parameters = {
|
|
|
+ "fname" : data["source_file"],
|
|
|
+ "url_base" : generate_string(data["base_folder"]),
|
|
|
+ "sections" : sections_string,
|
|
|
+ "bgcolor" : generate_vec3(DEFAULTS["bgcolor"]),
|
|
|
+ "bgalpha" : DEFAULTS["bgalpha"],
|
|
|
+ "defcamera" : generate_string(default_camera)
|
|
|
+ }
|
|
|
+
|
|
|
+ text = TEMPLATE_SCENE_ASCII % parameters
|
|
|
+
|
|
|
+ return text
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Generator - materials
|
|
|
+# #####################################################
|
|
|
+def generate_color(i):
|
|
|
+ """Generate hex color corresponding to integer.
|
|
|
+
|
|
|
+ Colors should have well defined ordering.
|
|
|
+ First N colors are hardcoded, then colors are random
|
|
|
+ (must seed random number generator with deterministic value
|
|
|
+ before getting colors).
|
|
|
+ """
|
|
|
+
|
|
|
+ if i < len(COLORS):
|
|
|
+ return "0x%06x" % COLORS[i]
|
|
|
+ else:
|
|
|
+ return "0x%06x" % int(0xffffff * random.random())
|
|
|
+
|
|
|
+def value2string(v):
|
|
|
+ if type(v)==str and v[0:2] != "0x":
|
|
|
+ return '"%s"' % v
|
|
|
+ return str(v)
|
|
|
+
|
|
|
+def generate_material_model(material, index):
|
|
|
+ m = {
|
|
|
+ 'a_dbg_name' :generate_string(material["name"]),
|
|
|
+ 'a_dbg_index' :index,
|
|
|
+ 'a_dbg_color' :generate_color(index),
|
|
|
+ "shading" :generate_string(material["shading_model"]),
|
|
|
+ "opacity" : material["opacity"]
|
|
|
+ }
|
|
|
+
|
|
|
+ if material["shading_model"] in ["Lambert", "Phong"]:
|
|
|
+ m["col_ambient"] = material["ambient"]
|
|
|
+ m["col_diffuse"] = material["diffuse"]
|
|
|
+ m["col_emissive"] = material["emissive"]
|
|
|
+
|
|
|
+ if material["shading_model"] in ["Phong"]:
|
|
|
+ m["col_specular"] = material["specular"]
|
|
|
+ m["shininess"] = material["shininess"]
|
|
|
+
|
|
|
+ if not MATERIALS_IN_SCENE:
|
|
|
+ conditional_set(m, "map_diffuse", material.get("map_diffuse", 0))
|
|
|
+ conditional_set(m, "map_lightmap", material.get("map_lightmap", 0))
|
|
|
+
|
|
|
+ mtl_raw = ",\n".join(['\t"%s" : %s' % (n, value2string(v)) for n,v in sorted(m.items())])
|
|
|
+ mtl_string = "\t{\n%s\n\t}" % mtl_raw
|
|
|
+
|
|
|
+ return mtl_string
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Generator - models
|
|
|
+# #####################################################
|
|
|
+def generate_ascii_model(data):
|
|
|
+ materials = data["materials"]
|
|
|
+
|
|
|
+ vertices = data["control_points"]["coordinates"]
|
|
|
+
|
|
|
+ normals = []
|
|
|
+ if "normals" in data["control_points"]:
|
|
|
+ normals = data["control_points"]["normals"][0]
|
|
|
+
|
|
|
+ uvs = []
|
|
|
+ uvs2 = []
|
|
|
+ if "layers_uvs" in data["faces"]:
|
|
|
+ n_uvs = len(data["faces"]["layers_uvs"])
|
|
|
+ if n_uvs > 0:
|
|
|
+ uvs = data["faces"]["layers_uvs"][0]
|
|
|
+ if n_uvs > 1:
|
|
|
+ uvs2 = data["faces"]["layers_uvs"][1]
|
|
|
+
|
|
|
+
|
|
|
+ triangles = []
|
|
|
+ triangles_n = []
|
|
|
+ triangles_uv = []
|
|
|
+ triangles_n_uv = []
|
|
|
+
|
|
|
+ quads = []
|
|
|
+ quads_n = []
|
|
|
+ quads_uv = []
|
|
|
+ quads_n_uv = []
|
|
|
+
|
|
|
+ indices_vertex = data["faces"]["indices_vertex"]
|
|
|
+ for vi in range(len(indices_vertex)):
|
|
|
+
|
|
|
+ vertex_index = indices_vertex[vi]
|
|
|
+
|
|
|
+ face = {
|
|
|
+ 'vertex' : vertex_index,
|
|
|
+ 'material' : data["faces"]["indices_material"][0][vi]
|
|
|
+ }
|
|
|
+
|
|
|
+ if normals:
|
|
|
+ face["normal"] = vertex_index
|
|
|
+
|
|
|
+ if uvs and "indices_uv" in data["faces"]:
|
|
|
+ indices_uv = data["faces"]["indices_uv"]
|
|
|
+ face["uv"] = indices_uv[0][vi]
|
|
|
+ if len(indices_uv) > 1:
|
|
|
+ face["uv2"] = indices_uv[1][vi]
|
|
|
+
|
|
|
+
|
|
|
+ if len(vertex_index) == 3:
|
|
|
+ if normals:
|
|
|
+ if uvs:
|
|
|
+ where = triangles_n_uv
|
|
|
+ else:
|
|
|
+ where = triangles_n
|
|
|
+ else:
|
|
|
+ if uvs:
|
|
|
+ where = triangles_uv
|
|
|
+ else:
|
|
|
+ where = triangles
|
|
|
+
|
|
|
+ elif len(vertex_index) == 4:
|
|
|
+ if normals:
|
|
|
+ if uvs:
|
|
|
+ where = quads_n_uv
|
|
|
+ else:
|
|
|
+ where = quads_n
|
|
|
+ else:
|
|
|
+ if uvs:
|
|
|
+ where = quads_uv
|
|
|
+ else:
|
|
|
+ where = quads
|
|
|
+
|
|
|
+ where.append(face)
|
|
|
+
|
|
|
+ nvertex = len(vertices)
|
|
|
+ nface = len(indices_vertex)
|
|
|
+ nmaterial = 0
|
|
|
+
|
|
|
+ text = TEMPLATE_MODEL_ASCII % {
|
|
|
+ "fname" : source_file,
|
|
|
+ "nvertex" : nvertex,
|
|
|
+ "nface" : nface,
|
|
|
+ "nmaterial" : nmaterial,
|
|
|
+
|
|
|
+ "materials" : "".join(generate_material_model(m, i) for i, m in enumerate(materials[0])),
|
|
|
+
|
|
|
+ "normals" : ",".join(generate_normal(n) for n in normals),
|
|
|
+ "vertices" : ",".join(generate_vertex(v) for v in vertices),
|
|
|
+
|
|
|
+ "uvs" : ",".join(generate_uv(u) for u in uvs),
|
|
|
+ "uvs2" : ",".join(generate_uv(u) for u in uvs2),
|
|
|
+
|
|
|
+ "triangles" : ",".join(generate_triangle(f) for f in triangles),
|
|
|
+ "triangles_n" : ",".join(generate_triangle_n(f) for f in triangles_n),
|
|
|
+ "triangles_uv" : ",".join(generate_triangle_uv(f) for f in triangles_uv),
|
|
|
+ "triangles_n_uv": ",".join(generate_triangle_n_uv(f) for f in triangles_n_uv),
|
|
|
+
|
|
|
+ "quads" : ",".join(generate_quad(f) for f in quads),
|
|
|
+ "quads_n" : ",".join(generate_quad_n(f) for f in quads_n),
|
|
|
+ "quads_uv" : ",".join(generate_quad_uv(f) for f in quads_uv),
|
|
|
+ "quads_n_uv" : ",".join(generate_quad_n_uv(f) for f in quads_n_uv)
|
|
|
+ }
|
|
|
+
|
|
|
+ return text
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Helpers
|
|
|
+# #####################################################
|
|
|
+def base_filename(path):
|
|
|
+ return os.path.basename(path)
|
|
|
+
|
|
|
+def rgb2int(rgb):
|
|
|
+ color = (int(rgb[0]*255) << 16) + (int(rgb[1]*255) << 8) + int(rgb[2]*255);
|
|
|
+ return color
|
|
|
+
|
|
|
+def conditional_set(where, label, what):
|
|
|
+ """Set dictionary property only if it exists"""
|
|
|
+
|
|
|
+ if what:
|
|
|
+ where[label] = what
|
|
|
+
|
|
|
+def dump_data(data):
|
|
|
+ """Generate pretty printed view of data."""
|
|
|
+
|
|
|
+ chunks = []
|
|
|
+
|
|
|
+ pp = pprint.PrettyPrinter(indent=2, width=160)
|
|
|
+ if type(data) == list:
|
|
|
+ for d in data:
|
|
|
+ chunks.append(pp.pformat(d))
|
|
|
+ elif type(data) == dict:
|
|
|
+ chunks.append(pp.pformat(data))
|
|
|
+
|
|
|
+ return "\n\n".join(chunks)
|
|
|
+
|
|
|
+def ensure_folder_exist(foldername):
|
|
|
+ """Create folder (with whole path) if it doesn't exist yet."""
|
|
|
+
|
|
|
+ if not os.access(foldername, os.R_OK|os.W_OK|os.X_OK):
|
|
|
+ os.makedirs(foldername)
|
|
|
+
|
|
|
+def abort(message):
|
|
|
+ print message
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+def write_file(fname, content):
|
|
|
+ out = open(fname, "w")
|
|
|
+ out.write(content)
|
|
|
+ out.close()
|
|
|
+
|
|
|
+def copy_files(textures, src_folder, dst_folder):
|
|
|
+ for texture in textures:
|
|
|
+ src_file = os.path.join(src_folder, texture)
|
|
|
+ if os.path.isfile(src_file):
|
|
|
+ shutil.copy(src_file, dst_folder)
|
|
|
+ else:
|
|
|
+ print "WARNING: couldn't find [%s]" % src_file
|
|
|
+
|
|
|
+# #####################################################
|
|
|
+# Main
|
|
|
+# #####################################################
|
|
|
+if __name__ == "__main__":
|
|
|
+
|
|
|
+ try:
|
|
|
+ from FbxCommon import *
|
|
|
+
|
|
|
+ except ImportError:
|
|
|
+
|
|
|
+ import platform
|
|
|
+
|
|
|
+ msg = ""
|
|
|
+
|
|
|
+ if platform.system() == 'Windows' or platform.system() == 'Microsoft':
|
|
|
+ msg = '"Python26/Lib/site-packages"'
|
|
|
+
|
|
|
+ elif platform.system() == 'Linux':
|
|
|
+ msg = '"/usr/local/lib/python2.6/site-packages"'
|
|
|
+
|
|
|
+ elif platform.system() == 'Darwin':
|
|
|
+ msg = '"/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages"'
|
|
|
+
|
|
|
+ abort('You need to copy the content in compatible subfolder under /lib/python<version> into your python install folder such as %s folder.' % msg)
|
|
|
+
|
|
|
+ sdkManager, scene = InitializeSdkObjects()
|
|
|
+
|
|
|
+ if len(sys.argv) < 3:
|
|
|
+ abort("Usage: convert_fbx_three.py [scene.fbx] [scene_folder]")
|
|
|
+
|
|
|
+ source_file = sys.argv[1]
|
|
|
+ output_folder = sys.argv[2]
|
|
|
+
|
|
|
+ junk, base_folder = os.path.split(os.path.normpath(output_folder))
|
|
|
+
|
|
|
+ result = LoadScene(sdkManager, scene, source_file)
|
|
|
+
|
|
|
+ if not result:
|
|
|
+ abort("An error occurred while loading the scene ...")
|
|
|
+
|
|
|
+ random.seed(42) # to get well defined debug color order for materials
|
|
|
+
|
|
|
+ ensure_folder_exist(output_folder)
|
|
|
+ ensure_folder_exist(output_folder+"/models")
|
|
|
+ ensure_folder_exist(output_folder+"/textures")
|
|
|
+
|
|
|
+ meshes = extract_meshes(scene)
|
|
|
+ generic_info = extract_generic_info(scene)
|
|
|
+
|
|
|
+ scene_text = ""
|
|
|
+ data = {
|
|
|
+ "meshes" : meshes,
|
|
|
+ "generic_info": generic_info,
|
|
|
+ "source_file" : source_file,
|
|
|
+ "base_folder" : base_folder+"/"
|
|
|
+ }
|
|
|
+ scene_text += generate_ascii_scene(data)
|
|
|
+
|
|
|
+ if DEBUG_FBX_JSON:
|
|
|
+ scene_text += "/*" + dump_data(meshes) + "\n\n\n" + dump_data(generic_info) + "*/"
|
|
|
+
|
|
|
+ scene_file = os.path.join(output_folder, "scene.js")
|
|
|
+ htaccess_file = os.path.join(output_folder, ".htaccess")
|
|
|
+
|
|
|
+ write_file(scene_file, scene_text)
|
|
|
+ write_file(htaccess_file, TEMPLATE_HTACCESS)
|
|
|
+
|
|
|
+ for mesh in meshes:
|
|
|
+ model_text = generate_ascii_model(mesh["data"])
|
|
|
+ model_file = os.path.join(output_folder, "models", get_mesh_filename(mesh))
|
|
|
+ write_file(model_file, model_text)
|
|
|
+
|
|
|
+ textures_src_folder = os.path.dirname(source_file)
|
|
|
+ textures_dst_folder = os.path.join(output_folder, "textures")
|
|
|
+ copy_files(collect_textures(data), textures_src_folder, textures_dst_folder)
|
|
|
+
|
|
|
+ # Destroy all objects created by the FBX SDK
|
|
|
+
|
|
|
+ sdkManager.Destroy()
|