|
@@ -1434,22 +1434,66 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- // parse nodes in FBXTree.Objects.subNodes.Model and generate a THREE.Group
|
|
|
+ // create the main THREE.Group() to be returned by the loader
|
|
|
function parseScene( FBXTree, connections, deformers, geometryMap, materialMap ) {
|
|
|
|
|
|
var sceneGraph = new THREE.Group();
|
|
|
|
|
|
- var ModelNode = FBXTree.Objects.subNodes.Model;
|
|
|
+ var modelMap = parseModels( FBXTree, deformers, geometryMap, materialMap, connections );
|
|
|
+
|
|
|
+ var modelNodes = FBXTree.Objects.subNodes.Model;
|
|
|
+
|
|
|
+ modelMap.forEach( function ( model ) {
|
|
|
+
|
|
|
+ var modelNode = modelNodes[ model.FBX_ID ];
|
|
|
+
|
|
|
+ setModelTransforms( FBXTree, model, modelNode, connections, sceneGraph );
|
|
|
+
|
|
|
+ var conns = connections.get( model.FBX_ID );
|
|
|
+ for ( var parentIndex = 0; parentIndex < conns.parents.length; parentIndex ++ ) {
|
|
|
+
|
|
|
+ var modelArray = Array.from( modelMap.values() );
|
|
|
+ var pIndex = findIndex( modelArray, function ( mod ) {
|
|
|
+
|
|
|
+ return mod.FBX_ID === conns.parents[ parentIndex ].ID;
|
|
|
+
|
|
|
+ } );
|
|
|
+ if ( pIndex > - 1 ) {
|
|
|
+
|
|
|
+ modelArray[ pIndex ].add( model );
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ if ( model.parent === null ) {
|
|
|
+
|
|
|
+ sceneGraph.add( model );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } );
|
|
|
|
|
|
- var modelArray = [];
|
|
|
+ bindSkeleton( FBXTree, deformers, geometryMap, modelMap, connections, sceneGraph );
|
|
|
+
|
|
|
+ addAnimations( FBXTree, connections, sceneGraph );
|
|
|
+
|
|
|
+ createAmbientLight( FBXTree, sceneGraph );
|
|
|
+
|
|
|
+ return sceneGraph;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // parse nodes in FBXTree.Objects.subNodes.Model
|
|
|
+ function parseModels( FBXTree, deformers, geometryMap, materialMap, connections ) {
|
|
|
|
|
|
var modelMap = new Map();
|
|
|
+ var modelNodes = FBXTree.Objects.subNodes.Model;
|
|
|
|
|
|
- for ( var nodeID in ModelNode ) {
|
|
|
+ for ( var nodeID in modelNodes ) {
|
|
|
|
|
|
var id = parseInt( nodeID );
|
|
|
- var node = ModelNode[ nodeID ];
|
|
|
+ var node = modelNodes[ nodeID ];
|
|
|
var conns = connections.get( id );
|
|
|
var model = null;
|
|
|
|
|
@@ -1481,433 +1525,457 @@
|
|
|
|
|
|
switch ( node.attrType ) {
|
|
|
|
|
|
- // create a THREE.PerspectiveCamera or THREE.OrthographicCamera
|
|
|
case 'Camera':
|
|
|
+ model = createCamera( FBXTree, conns );
|
|
|
+ break;
|
|
|
+ case 'Light':
|
|
|
+ model = createLight( FBXTree, conns );
|
|
|
+ break;
|
|
|
+ case 'Mesh':
|
|
|
+ model = createMesh( FBXTree, conns, geometryMap, materialMap );
|
|
|
+ break;
|
|
|
+ case 'NurbsCurve':
|
|
|
+ model = createCurve( conns, geometryMap );
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ model = new THREE.Group();
|
|
|
+ break;
|
|
|
|
|
|
- var cameraAttribute;
|
|
|
-
|
|
|
- for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
+ }
|
|
|
|
|
|
- var childID = conns.children[ childrenIndex ].ID;
|
|
|
+ }
|
|
|
|
|
|
- var attr = FBXTree.Objects.subNodes.NodeAttribute[ childID ];
|
|
|
+ model.name = THREE.PropertyBinding.sanitizeNodeName( node.attrName );
|
|
|
+ model.FBX_ID = id;
|
|
|
|
|
|
- if ( attr !== undefined && attr.properties !== undefined ) {
|
|
|
+ modelMap.set( id, model );
|
|
|
|
|
|
- cameraAttribute = attr.properties;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ return modelMap;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- if ( cameraAttribute === undefined ) {
|
|
|
+ // create a THREE.PerspectiveCamera or THREE.OrthographicCamera
|
|
|
+ function createCamera( FBXTree, conns ) {
|
|
|
|
|
|
- model = new THREE.Object3D();
|
|
|
+ var model;
|
|
|
+ var cameraAttribute;
|
|
|
|
|
|
- } else {
|
|
|
+ for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
|
|
|
- var type = 0;
|
|
|
- if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) {
|
|
|
+ var childID = conns.children[ childrenIndex ].ID;
|
|
|
|
|
|
- type = 1;
|
|
|
+ var attr = FBXTree.Objects.subNodes.NodeAttribute[ childID ];
|
|
|
|
|
|
- }
|
|
|
+ if ( attr !== undefined && attr.properties !== undefined ) {
|
|
|
|
|
|
- var nearClippingPlane = 1;
|
|
|
- if ( cameraAttribute.NearPlane !== undefined ) {
|
|
|
+ cameraAttribute = attr.properties;
|
|
|
|
|
|
- nearClippingPlane = cameraAttribute.NearPlane.value / 1000;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- var farClippingPlane = 1000;
|
|
|
- if ( cameraAttribute.FarPlane !== undefined ) {
|
|
|
+ if ( cameraAttribute === undefined ) {
|
|
|
|
|
|
- farClippingPlane = cameraAttribute.FarPlane.value / 1000;
|
|
|
+ model = new THREE.Object3D();
|
|
|
|
|
|
- }
|
|
|
+ } else {
|
|
|
|
|
|
+ var type = 0;
|
|
|
+ if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) {
|
|
|
|
|
|
- var width = window.innerWidth;
|
|
|
- var height = window.innerHeight;
|
|
|
+ type = 1;
|
|
|
|
|
|
- if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) {
|
|
|
+ }
|
|
|
|
|
|
- width = cameraAttribute.AspectWidth.value;
|
|
|
- height = cameraAttribute.AspectHeight.value;
|
|
|
+ var nearClippingPlane = 1;
|
|
|
+ if ( cameraAttribute.NearPlane !== undefined ) {
|
|
|
|
|
|
- }
|
|
|
+ nearClippingPlane = cameraAttribute.NearPlane.value / 1000;
|
|
|
|
|
|
- var aspect = width / height;
|
|
|
+ }
|
|
|
|
|
|
- var fov = 45;
|
|
|
- if ( cameraAttribute.FieldOfView !== undefined ) {
|
|
|
+ var farClippingPlane = 1000;
|
|
|
+ if ( cameraAttribute.FarPlane !== undefined ) {
|
|
|
|
|
|
- fov = cameraAttribute.FieldOfView.value;
|
|
|
+ farClippingPlane = cameraAttribute.FarPlane.value / 1000;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- switch ( type ) {
|
|
|
|
|
|
- case 0: // Perspective
|
|
|
- model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane );
|
|
|
- break;
|
|
|
+ var width = window.innerWidth;
|
|
|
+ var height = window.innerHeight;
|
|
|
|
|
|
- case 1: // Orthographic
|
|
|
- model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane );
|
|
|
- break;
|
|
|
+ if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) {
|
|
|
|
|
|
- default:
|
|
|
- console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' );
|
|
|
- model = new THREE.Object3D();
|
|
|
- break;
|
|
|
+ width = cameraAttribute.AspectWidth.value;
|
|
|
+ height = cameraAttribute.AspectHeight.value;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ var aspect = width / height;
|
|
|
|
|
|
- break;
|
|
|
+ var fov = 45;
|
|
|
+ if ( cameraAttribute.FieldOfView !== undefined ) {
|
|
|
|
|
|
+ fov = cameraAttribute.FieldOfView.value;
|
|
|
|
|
|
- // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight
|
|
|
- case 'Light':
|
|
|
+ }
|
|
|
|
|
|
- var lightAttribute;
|
|
|
+ switch ( type ) {
|
|
|
|
|
|
- for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
+ case 0: // Perspective
|
|
|
+ model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane );
|
|
|
+ break;
|
|
|
|
|
|
- var childID = conns.children[ childrenIndex ].ID;
|
|
|
+ case 1: // Orthographic
|
|
|
+ model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane );
|
|
|
+ break;
|
|
|
|
|
|
- var attr = FBXTree.Objects.subNodes.NodeAttribute[ childID ];
|
|
|
+ default:
|
|
|
+ console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' );
|
|
|
+ model = new THREE.Object3D();
|
|
|
+ break;
|
|
|
|
|
|
- if ( attr !== undefined && attr.properties !== undefined ) {
|
|
|
+ }
|
|
|
|
|
|
- lightAttribute = attr.properties;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ return model;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- if ( lightAttribute === undefined ) {
|
|
|
+ // Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight
|
|
|
+ function createLight( FBXTree, conns ) {
|
|
|
|
|
|
- model = new THREE.Object3D();
|
|
|
+ var model;
|
|
|
+ var lightAttribute;
|
|
|
|
|
|
- } else {
|
|
|
+ for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
|
|
|
- var type;
|
|
|
+ var childID = conns.children[ childrenIndex ].ID;
|
|
|
|
|
|
- // LightType can be undefined for Point lights
|
|
|
- if ( lightAttribute.LightType === undefined ) {
|
|
|
+ var attr = FBXTree.Objects.subNodes.NodeAttribute[ childID ];
|
|
|
|
|
|
- type = 0;
|
|
|
+ if ( attr !== undefined && attr.properties !== undefined ) {
|
|
|
|
|
|
- } else {
|
|
|
+ lightAttribute = attr.properties;
|
|
|
|
|
|
- type = lightAttribute.LightType.value;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- var color = 0xffffff;
|
|
|
+ if ( lightAttribute === undefined ) {
|
|
|
|
|
|
- if ( lightAttribute.Color !== undefined ) {
|
|
|
+ model = new THREE.Object3D();
|
|
|
|
|
|
- color = parseColor( lightAttribute.Color );
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ var type;
|
|
|
|
|
|
- var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100;
|
|
|
+ // LightType can be undefined for Point lights
|
|
|
+ if ( lightAttribute.LightType === undefined ) {
|
|
|
|
|
|
- // light disabled
|
|
|
- if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) {
|
|
|
+ type = 0;
|
|
|
|
|
|
- intensity = 0;
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ type = lightAttribute.LightType.value;
|
|
|
|
|
|
- var distance = 0;
|
|
|
- if ( lightAttribute.FarAttenuationEnd !== undefined ) {
|
|
|
+ }
|
|
|
|
|
|
- if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) {
|
|
|
+ var color = 0xffffff;
|
|
|
|
|
|
- distance = 0;
|
|
|
+ if ( lightAttribute.Color !== undefined ) {
|
|
|
|
|
|
- } else {
|
|
|
+ color = parseColor( lightAttribute.Color );
|
|
|
|
|
|
- distance = lightAttribute.FarAttenuationEnd.value / 1000;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ var intensity = ( lightAttribute.Intensity === undefined ) ? 1 : lightAttribute.Intensity.value / 100;
|
|
|
|
|
|
- }
|
|
|
+ // light disabled
|
|
|
+ if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) {
|
|
|
|
|
|
- // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd?
|
|
|
- var decay = 1;
|
|
|
+ intensity = 0;
|
|
|
|
|
|
- switch ( type ) {
|
|
|
+ }
|
|
|
|
|
|
- case 0: // Point
|
|
|
- model = new THREE.PointLight( color, intensity, distance, decay );
|
|
|
- break;
|
|
|
+ var distance = 0;
|
|
|
+ if ( lightAttribute.FarAttenuationEnd !== undefined ) {
|
|
|
|
|
|
- case 1: // Directional
|
|
|
- model = new THREE.DirectionalLight( color, intensity );
|
|
|
- break;
|
|
|
+ if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) {
|
|
|
|
|
|
- case 2: // Spot
|
|
|
- var angle = Math.PI / 3;
|
|
|
+ distance = 0;
|
|
|
|
|
|
- if ( lightAttribute.InnerAngle !== undefined ) {
|
|
|
+ } else {
|
|
|
|
|
|
- angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value );
|
|
|
+ distance = lightAttribute.FarAttenuationEnd.value / 1000;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- var penumbra = 0;
|
|
|
- if ( lightAttribute.OuterAngle !== undefined ) {
|
|
|
+ }
|
|
|
|
|
|
- // TODO: this is not correct - FBX calculates outer and inner angle in degrees
|
|
|
- // with OuterAngle > InnerAngle && OuterAngle <= Math.PI
|
|
|
- // while three.js uses a penumbra between (0, 1) to attenuate the inner angle
|
|
|
- penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value );
|
|
|
- penumbra = Math.max( penumbra, 1 );
|
|
|
+ // TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd?
|
|
|
+ var decay = 1;
|
|
|
|
|
|
- }
|
|
|
+ switch ( type ) {
|
|
|
|
|
|
- model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay );
|
|
|
- break;
|
|
|
+ case 0: // Point
|
|
|
+ model = new THREE.PointLight( color, intensity, distance, decay );
|
|
|
+ break;
|
|
|
|
|
|
- default:
|
|
|
- console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' );
|
|
|
- model = new THREE.PointLight( color, intensity );
|
|
|
- break;
|
|
|
+ case 1: // Directional
|
|
|
+ model = new THREE.DirectionalLight( color, intensity );
|
|
|
+ break;
|
|
|
|
|
|
- }
|
|
|
+ case 2: // Spot
|
|
|
+ var angle = Math.PI / 3;
|
|
|
|
|
|
- if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) {
|
|
|
+ if ( lightAttribute.InnerAngle !== undefined ) {
|
|
|
|
|
|
- model.castShadow = true;
|
|
|
+ angle = THREE.Math.degToRad( lightAttribute.InnerAngle.value );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ var penumbra = 0;
|
|
|
+ if ( lightAttribute.OuterAngle !== undefined ) {
|
|
|
|
|
|
- break;
|
|
|
+ // TODO: this is not correct - FBX calculates outer and inner angle in degrees
|
|
|
+ // with OuterAngle > InnerAngle && OuterAngle <= Math.PI
|
|
|
+ // while three.js uses a penumbra between (0, 1) to attenuate the inner angle
|
|
|
+ penumbra = THREE.Math.degToRad( lightAttribute.OuterAngle.value );
|
|
|
+ penumbra = Math.max( penumbra, 1 );
|
|
|
|
|
|
- case 'Mesh':
|
|
|
+ }
|
|
|
|
|
|
- var geometry = null;
|
|
|
- var material = null;
|
|
|
- var materials = [];
|
|
|
+ model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay );
|
|
|
+ break;
|
|
|
|
|
|
- for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
+ default:
|
|
|
+ console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' );
|
|
|
+ model = new THREE.PointLight( color, intensity );
|
|
|
+ break;
|
|
|
|
|
|
- var child = conns.children[ childrenIndex ];
|
|
|
+ }
|
|
|
|
|
|
- if ( geometryMap.has( child.ID ) ) {
|
|
|
+ if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) {
|
|
|
|
|
|
- geometry = geometryMap.get( child.ID );
|
|
|
+ model.castShadow = true;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- if ( materialMap.has( child.ID ) ) {
|
|
|
+ }
|
|
|
|
|
|
- materials.push( materialMap.get( child.ID ) );
|
|
|
+ return model;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
- if ( materials.length > 1 ) {
|
|
|
+ function createMesh( FBXTree, conns, geometryMap, materialMap ) {
|
|
|
|
|
|
- material = materials;
|
|
|
+ var model;
|
|
|
+ var geometry = null;
|
|
|
+ var material = null;
|
|
|
+ var materials = [];
|
|
|
|
|
|
- } else if ( materials.length > 0 ) {
|
|
|
+ for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
|
|
|
- material = materials[ 0 ];
|
|
|
+ var child = conns.children[ childrenIndex ];
|
|
|
|
|
|
- } else {
|
|
|
+ if ( geometryMap.has( child.ID ) ) {
|
|
|
|
|
|
- material = new THREE.MeshPhongMaterial( { color: 0xcccccc } );
|
|
|
- materials.push( material );
|
|
|
+ geometry = geometryMap.get( child.ID );
|
|
|
|
|
|
- }
|
|
|
- if ( 'color' in geometry.attributes ) {
|
|
|
+ }
|
|
|
|
|
|
- for ( var materialIndex = 0, numMaterials = materials.length; materialIndex < numMaterials; ++ materialIndex ) {
|
|
|
+ if ( materialMap.has( child.ID ) ) {
|
|
|
|
|
|
- materials[ materialIndex ].vertexColors = THREE.VertexColors;
|
|
|
+ materials.push( materialMap.get( child.ID ) );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
- if ( geometry.FBX_Deformer ) {
|
|
|
+ }
|
|
|
+ if ( materials.length > 1 ) {
|
|
|
|
|
|
- for ( var materialsIndex = 0, materialsLength = materials.length; materialsIndex < materialsLength; ++ materialsIndex ) {
|
|
|
+ material = materials;
|
|
|
|
|
|
- materials[ materialsIndex ].skinning = true;
|
|
|
+ } else if ( materials.length > 0 ) {
|
|
|
|
|
|
- }
|
|
|
- model = new THREE.SkinnedMesh( geometry, material );
|
|
|
+ material = materials[ 0 ];
|
|
|
|
|
|
- } else {
|
|
|
+ } else {
|
|
|
|
|
|
- model = new THREE.Mesh( geometry, material );
|
|
|
+ material = new THREE.MeshPhongMaterial( { color: 0xcccccc } );
|
|
|
+ materials.push( material );
|
|
|
|
|
|
- }
|
|
|
- break;
|
|
|
+ }
|
|
|
+ if ( 'color' in geometry.attributes ) {
|
|
|
|
|
|
- case 'NurbsCurve':
|
|
|
- var geometry = null;
|
|
|
+ for ( var materialIndex = 0, numMaterials = materials.length; materialIndex < numMaterials; ++ materialIndex ) {
|
|
|
|
|
|
- for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
+ materials[ materialIndex ].vertexColors = THREE.VertexColors;
|
|
|
|
|
|
- var child = conns.children[ childrenIndex ];
|
|
|
+ }
|
|
|
|
|
|
- if ( geometryMap.has( child.ID ) ) {
|
|
|
+ }
|
|
|
+ if ( geometry.FBX_Deformer ) {
|
|
|
|
|
|
- geometry = geometryMap.get( child.ID );
|
|
|
+ for ( var materialsIndex = 0, materialsLength = materials.length; materialsIndex < materialsLength; ++ materialsIndex ) {
|
|
|
|
|
|
- }
|
|
|
+ materials[ materialsIndex ].skinning = true;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // FBX does not list materials for Nurbs lines, so we'll just put our own in here.
|
|
|
- material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 5 } );
|
|
|
- model = new THREE.Line( geometry, material );
|
|
|
- break;
|
|
|
+ model = new THREE.SkinnedMesh( geometry, material );
|
|
|
|
|
|
- default:
|
|
|
- model = new THREE.Group();
|
|
|
- break;
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ model = new THREE.Mesh( geometry, material );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- model.name = THREE.PropertyBinding.sanitizeNodeName( node.attrName );
|
|
|
- model.FBX_ID = id;
|
|
|
+ return model;
|
|
|
|
|
|
- modelArray.push( model );
|
|
|
- modelMap.set( id, model );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ function createCurve( conns, geometryMap ) {
|
|
|
|
|
|
- for ( var modelArrayIndex = 0, modelArrayLength = modelArray.length; modelArrayIndex < modelArrayLength; ++ modelArrayIndex ) {
|
|
|
+ var geometry = null;
|
|
|
|
|
|
- var model = modelArray[ modelArrayIndex ];
|
|
|
+ for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
|
|
|
- var node = ModelNode[ model.FBX_ID ];
|
|
|
+ var child = conns.children[ childrenIndex ];
|
|
|
|
|
|
- if ( 'Lcl_Translation' in node.properties ) {
|
|
|
+ if ( geometryMap.has( child.ID ) ) {
|
|
|
|
|
|
- model.position.fromArray( node.properties.Lcl_Translation.value );
|
|
|
+ geometry = geometryMap.get( child.ID );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( 'Lcl_Rotation' in node.properties ) {
|
|
|
+ }
|
|
|
|
|
|
- var rotation = node.properties.Lcl_Rotation.value.map( THREE.Math.degToRad );
|
|
|
- rotation.push( 'ZYX' );
|
|
|
- model.rotation.fromArray( rotation );
|
|
|
+ // FBX does not list materials for Nurbs lines, so we'll just put our own in here.
|
|
|
+ var material = new THREE.LineBasicMaterial( { color: 0x3300ff, linewidth: 1 } );
|
|
|
+ return new THREE.Line( geometry, material );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse ambient color in FBXTree.GlobalSettings.properties - if it's not set to black (default), create an ambient light
|
|
|
+ function createAmbientLight( FBXTree, sceneGraph ) {
|
|
|
+
|
|
|
+ if ( 'GlobalSettings' in FBXTree && 'AmbientColor' in FBXTree.GlobalSettings.properties ) {
|
|
|
|
|
|
- if ( 'Lcl_Scaling' in node.properties ) {
|
|
|
+ var ambientColor = FBXTree.GlobalSettings.properties.AmbientColor.value;
|
|
|
+ var r = ambientColor[ 0 ];
|
|
|
+ var g = ambientColor[ 1 ];
|
|
|
+ var b = ambientColor[ 2 ];
|
|
|
|
|
|
- model.scale.fromArray( node.properties.Lcl_Scaling.value );
|
|
|
+ if ( r !== 0 || g !== 0 || b !== 0 ) {
|
|
|
+
|
|
|
+ var color = new THREE.Color( r, g, b );
|
|
|
+ sceneGraph.add( new THREE.AmbientLight( color, 1 ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( 'PreRotation' in node.properties ) {
|
|
|
+ }
|
|
|
|
|
|
- var array = node.properties.PreRotation.value.map( THREE.Math.degToRad );
|
|
|
- array[ 3 ] = 'ZYX';
|
|
|
+ }
|
|
|
|
|
|
- var preRotations = new THREE.Euler().fromArray( array );
|
|
|
+ // parse the model node for transform details and apply them to the model
|
|
|
+ function setModelTransforms( FBXTree, model, modelNode, connections, sceneGraph ) {
|
|
|
|
|
|
- preRotations = new THREE.Quaternion().setFromEuler( preRotations );
|
|
|
- var currentRotation = new THREE.Quaternion().setFromEuler( model.rotation );
|
|
|
- preRotations.multiply( currentRotation );
|
|
|
- model.rotation.setFromQuaternion( preRotations, 'ZYX' );
|
|
|
+ if ( 'Lcl_Translation' in modelNode.properties ) {
|
|
|
|
|
|
- }
|
|
|
+ model.position.fromArray( modelNode.properties.Lcl_Translation.value );
|
|
|
|
|
|
- // allow transformed pivots - see https://github.com/mrdoob/three.js/issues/11895
|
|
|
- if ( 'GeometricTranslation' in node.properties ) {
|
|
|
+ }
|
|
|
|
|
|
- var array = node.properties.GeometricTranslation.value;
|
|
|
+ if ( 'Lcl_Rotation' in modelNode.properties ) {
|
|
|
|
|
|
- model.traverse( function ( child ) {
|
|
|
+ var rotation = modelNode.properties.Lcl_Rotation.value.map( THREE.Math.degToRad );
|
|
|
+ rotation.push( 'ZYX' );
|
|
|
+ model.rotation.fromArray( rotation );
|
|
|
|
|
|
- if ( child.geometry ) {
|
|
|
+ }
|
|
|
|
|
|
- child.geometry.translate( array[ 0 ], array[ 1 ], array[ 2 ] );
|
|
|
+ if ( 'Lcl_Scaling' in modelNode.properties ) {
|
|
|
|
|
|
- }
|
|
|
+ model.scale.fromArray( modelNode.properties.Lcl_Scaling.value );
|
|
|
|
|
|
- } );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ if ( 'PreRotation' in modelNode.properties ) {
|
|
|
|
|
|
- if ( 'LookAtProperty' in node.properties ) {
|
|
|
+ var array = modelNode.properties.PreRotation.value.map( THREE.Math.degToRad );
|
|
|
+ array[ 3 ] = 'ZYX';
|
|
|
|
|
|
- var conns = connections.get( model.FBX_ID );
|
|
|
+ var preRotations = new THREE.Euler().fromArray( array );
|
|
|
|
|
|
- for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
+ preRotations = new THREE.Quaternion().setFromEuler( preRotations );
|
|
|
+ var currentRotation = new THREE.Quaternion().setFromEuler( model.rotation );
|
|
|
+ preRotations.multiply( currentRotation );
|
|
|
+ model.rotation.setFromQuaternion( preRotations, 'ZYX' );
|
|
|
|
|
|
- var child = conns.children[ childrenIndex ];
|
|
|
+ }
|
|
|
|
|
|
- if ( child.relationship === 'LookAtProperty' ) {
|
|
|
+ // allow transformed pivots - see https://github.com/mrdoob/three.js/issues/11895
|
|
|
+ if ( 'GeometricTranslation' in modelNode.properties ) {
|
|
|
|
|
|
- var lookAtTarget = FBXTree.Objects.subNodes.Model[ child.ID ];
|
|
|
+ var array = modelNode.properties.GeometricTranslation.value;
|
|
|
|
|
|
- if ( 'Lcl_Translation' in lookAtTarget.properties ) {
|
|
|
+ model.traverse( function ( child ) {
|
|
|
|
|
|
- var pos = lookAtTarget.properties.Lcl_Translation.value;
|
|
|
+ if ( child.geometry ) {
|
|
|
|
|
|
- // DirectionalLight, SpotLight
|
|
|
- if ( model.target !== undefined ) {
|
|
|
+ child.geometry.translate( array[ 0 ], array[ 1 ], array[ 2 ] );
|
|
|
|
|
|
- model.target.position.set( pos[ 0 ], pos[ 1 ], pos[ 2 ] );
|
|
|
- sceneGraph.add( model.target );
|
|
|
+ }
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- } else { // Cameras and other Object3Ds
|
|
|
+ }
|
|
|
|
|
|
- model.lookAt( new THREE.Vector3( pos[ 0 ], pos[ 1 ], pos[ 2 ] ) );
|
|
|
+ if ( 'LookAtProperty' in modelNode.properties ) {
|
|
|
|
|
|
- }
|
|
|
+ var conns = connections.get( model.FBX_ID );
|
|
|
|
|
|
- }
|
|
|
+ for ( var childrenIndex = 0, childrenLength = conns.children.length; childrenIndex < childrenLength; ++ childrenIndex ) {
|
|
|
|
|
|
- }
|
|
|
+ var child = conns.children[ childrenIndex ];
|
|
|
|
|
|
- }
|
|
|
+ if ( child.relationship === 'LookAtProperty' ) {
|
|
|
|
|
|
- }
|
|
|
+ var lookAtTarget = FBXTree.Objects.subNodes.Model[ child.ID ];
|
|
|
|
|
|
- var conns = connections.get( model.FBX_ID );
|
|
|
- for ( var parentIndex = 0; parentIndex < conns.parents.length; parentIndex ++ ) {
|
|
|
+ if ( 'Lcl_Translation' in lookAtTarget.properties ) {
|
|
|
|
|
|
- var pIndex = findIndex( modelArray, function ( mod ) {
|
|
|
+ var pos = lookAtTarget.properties.Lcl_Translation.value;
|
|
|
|
|
|
- return mod.FBX_ID === conns.parents[ parentIndex ].ID;
|
|
|
+ // DirectionalLight, SpotLight
|
|
|
+ if ( model.target !== undefined ) {
|
|
|
|
|
|
- } );
|
|
|
- if ( pIndex > - 1 ) {
|
|
|
+ model.target.position.set( pos[ 0 ], pos[ 1 ], pos[ 2 ] );
|
|
|
+ sceneGraph.add( model.target );
|
|
|
|
|
|
- modelArray[ pIndex ].add( model );
|
|
|
- break;
|
|
|
|
|
|
- }
|
|
|
+ } else { // Cameras and other Object3Ds
|
|
|
|
|
|
- }
|
|
|
- if ( model.parent === null ) {
|
|
|
+ model.lookAt( new THREE.Vector3( pos[ 0 ], pos[ 1 ], pos[ 2 ] ) );
|
|
|
|
|
|
- sceneGraph.add( model );
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
+ }
|
|
|
+
|
|
|
+ function bindSkeleton( FBXTree, deformers, geometryMap, modelMap, connections, sceneGraph ) {
|
|
|
|
|
|
// Now with the bones created, we can update the skeletons and bind them to the skinned meshes.
|
|
|
sceneGraph.updateMatrixWorld( true );
|
|
@@ -2005,32 +2073,10 @@
|
|
|
// to attach animations to, since FBX treats animations as animations for the entire scene,
|
|
|
// not just for individual objects.
|
|
|
sceneGraph.skeleton = {
|
|
|
- bones: modelArray
|
|
|
- };
|
|
|
-
|
|
|
- var animations = parseAnimations( FBXTree, connections, sceneGraph );
|
|
|
-
|
|
|
- addAnimations( sceneGraph, animations );
|
|
|
-
|
|
|
-
|
|
|
- // Parse ambient color - if it's not set to black (default), create an ambient light
|
|
|
- if ( 'GlobalSettings' in FBXTree && 'AmbientColor' in FBXTree.GlobalSettings.properties ) {
|
|
|
-
|
|
|
- var ambientColor = FBXTree.GlobalSettings.properties.AmbientColor.value;
|
|
|
- var r = ambientColor[ 0 ];
|
|
|
- var g = ambientColor[ 1 ];
|
|
|
- var b = ambientColor[ 2 ];
|
|
|
-
|
|
|
- if ( r !== 0 || g !== 0 || b !== 0 ) {
|
|
|
-
|
|
|
- var color = new THREE.Color( r, g, b );
|
|
|
- sceneGraph.add( new THREE.AmbientLight( color, 1 ) );
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
+ bones: Array.from( modelMap.values() ),
|
|
|
|
|
|
- return sceneGraph;
|
|
|
+ };
|
|
|
|
|
|
}
|
|
|
|
|
@@ -2478,11 +2524,13 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- function addAnimations( group, animations ) {
|
|
|
+ function addAnimations( FBXTree, connections, sceneGraph ) {
|
|
|
+
|
|
|
+ var animations = parseAnimations( FBXTree, connections, sceneGraph );
|
|
|
|
|
|
- if ( group.animations === undefined ) {
|
|
|
+ if ( sceneGraph.animations === undefined ) {
|
|
|
|
|
|
- group.animations = [];
|
|
|
+ sceneGraph.animations = [];
|
|
|
|
|
|
}
|
|
|
|
|
@@ -2499,7 +2547,7 @@
|
|
|
hierarchy: []
|
|
|
};
|
|
|
|
|
|
- var bones = group.skeleton.bones;
|
|
|
+ var bones = sceneGraph.skeleton.bones;
|
|
|
|
|
|
for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
|
|
|
|
|
@@ -2540,7 +2588,7 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- group.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
|
|
|
+ sceneGraph.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
|
|
|
|
|
|
}
|
|
|
|