|
@@ -8,7 +8,9 @@
|
|
|
* Versions lower than this may load but will probably have errors
|
|
|
*
|
|
|
* Needs Support:
|
|
|
- * Morph targets / blend shapes
|
|
|
+ * Morph normals / blend shape normals
|
|
|
+ * Animation tracks for morph targets
|
|
|
+ *
|
|
|
* Euler rotation order
|
|
|
*
|
|
|
* FBX format references:
|
|
@@ -95,9 +97,9 @@
|
|
|
var images = parseImages( FBXTree );
|
|
|
var textures = parseTextures( FBXTree, new THREE.TextureLoader( this.manager ).setPath( resourceDirectory ), images, connections );
|
|
|
var materials = parseMaterials( FBXTree, textures, connections );
|
|
|
- var skeletons = parseDeformers( FBXTree, connections );
|
|
|
- var geometryMap = parseGeometries( FBXTree, connections, skeletons );
|
|
|
- var sceneGraph = parseScene( FBXTree, connections, skeletons, geometryMap, materials );
|
|
|
+ var deformers = parseDeformers( FBXTree, connections );
|
|
|
+ var geometryMap = parseGeometries( FBXTree, connections, deformers );
|
|
|
+ var sceneGraph = parseScene( FBXTree, connections, deformers.skeletons, geometryMap, materials );
|
|
|
|
|
|
return sceneGraph;
|
|
|
|
|
@@ -592,6 +594,7 @@
|
|
|
function parseDeformers( FBXTree, connections ) {
|
|
|
|
|
|
var skeletons = {};
|
|
|
+ var morphTargets = {};
|
|
|
|
|
|
if ( 'Deformer' in FBXTree.Objects ) {
|
|
|
|
|
@@ -601,9 +604,9 @@
|
|
|
|
|
|
var deformerNode = DeformerNodes[ nodeID ];
|
|
|
|
|
|
- if ( deformerNode.attrType === 'Skin' ) {
|
|
|
+ var relationships = connections.get( parseInt( nodeID ) );
|
|
|
|
|
|
- var relationships = connections.get( parseInt( nodeID ) );
|
|
|
+ if ( deformerNode.attrType === 'Skin' ) {
|
|
|
|
|
|
var skeleton = parseSkeleton( relationships, DeformerNodes );
|
|
|
skeleton.ID = nodeID;
|
|
@@ -613,18 +616,37 @@
|
|
|
|
|
|
skeletons[ nodeID ] = skeleton;
|
|
|
|
|
|
+ } else if ( deformerNode.attrType === 'BlendShape' ) {
|
|
|
+
|
|
|
+ var morphTarget = {
|
|
|
+ id: nodeID,
|
|
|
+ };
|
|
|
+
|
|
|
+ morphTarget.rawTargets = parseMorphTargets( relationships, deformerNode, DeformerNodes, connections, FBXTree );
|
|
|
+ morphTarget.id = nodeID;
|
|
|
+
|
|
|
+ if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: morph target attached to more than one geometry is not supported.' );
|
|
|
+ morphTarget.parentGeoID = relationships.parents[ 0 ].ID;
|
|
|
+
|
|
|
+ morphTargets[ nodeID ] = morphTarget;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- return skeletons;
|
|
|
+ return {
|
|
|
+
|
|
|
+ skeletons: skeletons,
|
|
|
+ morphTargets: morphTargets,
|
|
|
+
|
|
|
+ };
|
|
|
|
|
|
}
|
|
|
|
|
|
// Parse single nodes in FBXTree.Objects.Deformer
|
|
|
- // The top level deformer nodes have type 'Skin' and subDeformer nodes have type 'Cluster'
|
|
|
+ // The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster'
|
|
|
// Each skin node represents a skeleton and each cluster node represents a bone
|
|
|
function parseSkeleton( connections, deformerNodes ) {
|
|
|
|
|
@@ -632,25 +654,25 @@
|
|
|
|
|
|
connections.children.forEach( function ( child ) {
|
|
|
|
|
|
- var subDeformerNode = deformerNodes[ child.ID ];
|
|
|
+ var boneNode = deformerNodes[ child.ID ];
|
|
|
|
|
|
- if ( subDeformerNode.attrType !== 'Cluster' ) return;
|
|
|
+ if ( boneNode.attrType !== 'Cluster' ) return;
|
|
|
|
|
|
var rawBone = {
|
|
|
|
|
|
ID: child.ID,
|
|
|
indices: [],
|
|
|
weights: [],
|
|
|
- transform: new THREE.Matrix4().fromArray( subDeformerNode.Transform.a ),
|
|
|
- transformLink: new THREE.Matrix4().fromArray( subDeformerNode.TransformLink.a ),
|
|
|
- linkMode: subDeformerNode.Mode,
|
|
|
+ transform: new THREE.Matrix4().fromArray( boneNode.Transform.a ),
|
|
|
+ transformLink: new THREE.Matrix4().fromArray( boneNode.TransformLink.a ),
|
|
|
+ linkMode: boneNode.Mode,
|
|
|
|
|
|
};
|
|
|
|
|
|
- if ( 'Indexes' in subDeformerNode ) {
|
|
|
+ if ( 'Indexes' in boneNode ) {
|
|
|
|
|
|
- rawBone.indices = subDeformerNode.Indexes.a;
|
|
|
- rawBone.weights = subDeformerNode.Weights.a;
|
|
|
+ rawBone.indices = boneNode.Indexes.a;
|
|
|
+ rawBone.weights = boneNode.Weights.a;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -667,21 +689,76 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel"
|
|
|
+ function parseMorphTargets( relationships, deformerNode, deformerNodes, connections ) {
|
|
|
+
|
|
|
+ var rawMorphTargets = [];
|
|
|
+
|
|
|
+ for ( var i = 0; i < relationships.children.length; i ++ ) {
|
|
|
+
|
|
|
+ if ( i === 8 ) {
|
|
|
+
|
|
|
+ console.warn( 'FBXLoader: maximum of 8 morph targets supported. Ignoring additional targets.' );
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var child = relationships.children[ i ];
|
|
|
+
|
|
|
+ var morphTargetNode = deformerNodes[ child.ID ];
|
|
|
+
|
|
|
+ var rawMorphTarget = {
|
|
|
+
|
|
|
+ name: morphTargetNode.attrName,
|
|
|
+ initialWeight: morphTargetNode.DeformPercent,
|
|
|
+ id: morphTargetNode.id,
|
|
|
+ fullWeights: morphTargetNode.FullWeights.a
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ if ( morphTargetNode.attrType !== 'BlendShapeChannel' ) return;
|
|
|
+
|
|
|
+ var targetRelationships = connections.get( parseInt( child.ID ) );
|
|
|
+
|
|
|
+ targetRelationships.children.forEach( function ( child ) {
|
|
|
+
|
|
|
+ if ( child.relationship === 'DeformPercent' ) {
|
|
|
+
|
|
|
+ // TODO: animation of morph targets is currently unsupported
|
|
|
+ rawMorphTarget.weightCurveID = child.ID;
|
|
|
+ // weightCurve = FBXTree.Objects.AnimationCurveNode[ weightCurveID ];
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ rawMorphTarget.geoID = child.ID;
|
|
|
+ // morphGeo = FBXTree.Objects.Geometry[ geoID ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ rawMorphTargets.push( rawMorphTarget );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return rawMorphTargets;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
// Parse nodes in FBXTree.Objects.Geometry
|
|
|
- function parseGeometries( FBXTree, connections, skeletons ) {
|
|
|
+ function parseGeometries( FBXTree, connections, deformers ) {
|
|
|
|
|
|
var geometryMap = new Map();
|
|
|
|
|
|
if ( 'Geometry' in FBXTree.Objects ) {
|
|
|
|
|
|
- var geometryNodes = FBXTree.Objects.Geometry;
|
|
|
-
|
|
|
+ var geoNodes = FBXTree.Objects.Geometry;
|
|
|
|
|
|
-
|
|
|
- for ( var nodeID in geometryNodes ) {
|
|
|
+ for ( var nodeID in geoNodes ) {
|
|
|
|
|
|
var relationships = connections.get( parseInt( nodeID ) );
|
|
|
- var geo = parseGeometry( FBXTree, relationships, geometryNodes[ nodeID ], skeletons );
|
|
|
+ var geo = parseGeometry( FBXTree, relationships, geoNodes[ nodeID ], deformers );
|
|
|
|
|
|
geometryMap.set( parseInt( nodeID ), geo );
|
|
|
|
|
@@ -694,25 +771,27 @@
|
|
|
}
|
|
|
|
|
|
// Parse single node in FBXTree.Objects.Geometry
|
|
|
- function parseGeometry( FBXTree, relationships, geometryNode, skeletons ) {
|
|
|
+ function parseGeometry( FBXTree, relationships, geoNode, deformers ) {
|
|
|
|
|
|
- switch ( geometryNode.attrType ) {
|
|
|
+ switch ( geoNode.attrType ) {
|
|
|
|
|
|
case 'Mesh':
|
|
|
- return parseMeshGeometry( FBXTree, relationships, geometryNode, skeletons );
|
|
|
+ return parseMeshGeometry( FBXTree, relationships, geoNode, deformers );
|
|
|
break;
|
|
|
|
|
|
case 'NurbsCurve':
|
|
|
- return parseNurbsGeometry( geometryNode );
|
|
|
+ return parseNurbsGeometry( geoNode );
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// Parse single node mesh geometry in FBXTree.Objects.Geometry
|
|
|
- function parseMeshGeometry( FBXTree, relationships, geometryNode, skeletons ) {
|
|
|
+ function parseMeshGeometry( FBXTree, relationships, geoNode, deformers ) {
|
|
|
+
|
|
|
+ var skeletons = deformers.skeletons;
|
|
|
+ var morphTargets = deformers.morphTargets;
|
|
|
|
|
|
var modelNodes = relationships.parents.map( function ( parent ) {
|
|
|
|
|
@@ -731,6 +810,14 @@
|
|
|
|
|
|
}, null );
|
|
|
|
|
|
+ var morphTarget = relationships.children.reduce( function ( morphTarget, child ) {
|
|
|
+
|
|
|
+ if ( morphTargets[ child.ID ] !== undefined ) morphTarget = morphTargets[ child.ID ];
|
|
|
+
|
|
|
+ return morphTarget;
|
|
|
+
|
|
|
+ }, null );
|
|
|
+
|
|
|
var preTransform = new THREE.Matrix4();
|
|
|
|
|
|
// TODO: if there is more than one model associated with the geometry, AND the models have
|
|
@@ -740,7 +827,6 @@
|
|
|
// For now just assume one model and get the preRotations from that
|
|
|
var modelNode = modelNodes[ 0 ];
|
|
|
|
|
|
-
|
|
|
if ( 'GeometricRotation' in modelNode ) {
|
|
|
|
|
|
var array = modelNode.GeometricRotation.value.map( THREE.Math.degToRad );
|
|
@@ -762,68 +848,172 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- return genGeometry( FBXTree, relationships, geometryNode, skeleton, preTransform );
|
|
|
+ return genGeometry( FBXTree, geoNode, skeleton, morphTarget, preTransform );
|
|
|
|
|
|
}
|
|
|
|
|
|
// Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry
|
|
|
- function genGeometry( FBXTree, relationships, geometryNode, skeleton, preTransform ) {
|
|
|
+ function genGeometry( FBXTree, geoNode, skeleton, morphTarget, preTransform ) {
|
|
|
+
|
|
|
+ var geo = new THREE.BufferGeometry();
|
|
|
+ if ( geoNode.attrName ) geo.name = geoNode.attrName;
|
|
|
+
|
|
|
+ var geoInfo = getGeoInfo( geoNode, skeleton );
|
|
|
+
|
|
|
+ var buffers = genBuffers( geoInfo );
|
|
|
+
|
|
|
+ var positionAttribute = new THREE.Float32BufferAttribute( buffers.vertex, 3 );
|
|
|
+
|
|
|
+ preTransform.applyToBufferAttribute( positionAttribute );
|
|
|
+
|
|
|
+ geo.addAttribute( 'position', positionAttribute );
|
|
|
+
|
|
|
+ if ( buffers.colors.length > 0 ) {
|
|
|
+
|
|
|
+ geo.addAttribute( 'color', new THREE.Float32BufferAttribute( buffers.colors, 3 ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( skeleton ) {
|
|
|
+
|
|
|
+ geo.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffers.weightsIndices, 4 ) );
|
|
|
+
|
|
|
+ geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffers.vertexWeights, 4 ) );
|
|
|
+
|
|
|
+ // used later to bind the skeleton to the model
|
|
|
+ geo.FBX_Deformer = skeleton;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( buffers.normal.length > 0 ) {
|
|
|
+
|
|
|
+ var normalAttribute = new THREE.Float32BufferAttribute( buffers.normal, 3 );
|
|
|
+
|
|
|
+ var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform );
|
|
|
+ normalMatrix.applyToBufferAttribute( normalAttribute );
|
|
|
+
|
|
|
+ geo.addAttribute( 'normal', normalAttribute );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ buffers.uvs.forEach( function ( uvBuffer, i ) {
|
|
|
+
|
|
|
+ // subsequent uv buffers are called 'uv1', 'uv2', ...
|
|
|
+ var name = 'uv' + ( i + 1 ).toString();
|
|
|
+
|
|
|
+ // the first uv buffer is just called 'uv'
|
|
|
+ if ( i === 0 ) {
|
|
|
+
|
|
|
+ name = 'uv';
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ geo.addAttribute( name, new THREE.Float32BufferAttribute( buffers.uvs[ i ], 2 ) );
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
|
|
|
+
|
|
|
+ // Convert the material indices of each vertex into rendering groups on the geometry.
|
|
|
+ var prevMaterialIndex = buffers.materialIndex[ 0 ];
|
|
|
+ var startIndex = 0;
|
|
|
+
|
|
|
+ buffers.materialIndex.forEach( function ( currentIndex, i ) {
|
|
|
+
|
|
|
+ if ( currentIndex !== prevMaterialIndex ) {
|
|
|
+
|
|
|
+ geo.addGroup( startIndex, i - startIndex, prevMaterialIndex );
|
|
|
+
|
|
|
+ prevMaterialIndex = currentIndex;
|
|
|
+ startIndex = i;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ // the loop above doesn't add the last group, do that here.
|
|
|
+ if ( geo.groups.length > 0 ) {
|
|
|
|
|
|
- var vertexPositions = ( geometryNode.Vertices !== undefined ) ? geometryNode.Vertices.a : [];
|
|
|
- var vertexIndices = ( geometryNode.PolygonVertexIndex !== undefined ) ? geometryNode.PolygonVertexIndex.a : [];
|
|
|
+ var lastGroup = geo.groups[ geo.groups.length - 1 ];
|
|
|
+ var lastIndex = lastGroup.start + lastGroup.count;
|
|
|
+
|
|
|
+ if ( lastIndex !== buffers.materialIndex.length ) {
|
|
|
+
|
|
|
+ geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // case where there are multiple materials but the whole geometry is only
|
|
|
+ // using one of them
|
|
|
+ if ( geo.groups.length === 0 ) {
|
|
|
+
|
|
|
+ geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- // create arrays to hold the final data used to build the buffergeometry
|
|
|
- var vertexBuffer = [];
|
|
|
- var normalBuffer = [];
|
|
|
- var colorsBuffer = [];
|
|
|
- var uvsBuffer = [];
|
|
|
- var materialIndexBuffer = [];
|
|
|
- var vertexWeightsBuffer = [];
|
|
|
- var weightsIndicesBuffer = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ addMorphTargets( FBXTree, geo, geoNode, morphTarget, preTransform );
|
|
|
+
|
|
|
+ return geo;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function getGeoInfo( geoNode, skeleton ) {
|
|
|
+
|
|
|
+ var geoInfo = {};
|
|
|
|
|
|
- if ( geometryNode.LayerElementColor ) {
|
|
|
+ geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : [];
|
|
|
+ geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : [];
|
|
|
|
|
|
- var colorInfo = getColors( geometryNode.LayerElementColor[ 0 ] );
|
|
|
+ if ( geoNode.LayerElementColor ) {
|
|
|
+
|
|
|
+ geoInfo.color = getColors( geoNode.LayerElementColor[ 0 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( geometryNode.LayerElementMaterial ) {
|
|
|
+ if ( geoNode.LayerElementMaterial ) {
|
|
|
|
|
|
- var materialInfo = getMaterials( geometryNode.LayerElementMaterial[ 0 ] );
|
|
|
+ geoInfo.material = getMaterials( geoNode.LayerElementMaterial[ 0 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( geometryNode.LayerElementNormal ) {
|
|
|
+ if ( geoNode.LayerElementNormal ) {
|
|
|
|
|
|
- var normalInfo = getNormals( geometryNode.LayerElementNormal[ 0 ] );
|
|
|
+ geoInfo.normal = getNormals( geoNode.LayerElementNormal[ 0 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( geometryNode.LayerElementUV ) {
|
|
|
+ if ( geoNode.LayerElementUV ) {
|
|
|
+
|
|
|
+ geoInfo.uv = [];
|
|
|
|
|
|
- var uvInfo = [];
|
|
|
var i = 0;
|
|
|
- while ( geometryNode.LayerElementUV[ i ] ) {
|
|
|
+ while ( geoNode.LayerElementUV[ i ] ) {
|
|
|
|
|
|
- uvInfo.push( getUVs( geometryNode.LayerElementUV[ i ] ) );
|
|
|
+ geoInfo.uv.push( getUVs( geoNode.LayerElementUV[ i ] ) );
|
|
|
i ++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- var weightTable = {};
|
|
|
+ geoInfo.weightTable = {};
|
|
|
|
|
|
if ( skeleton !== null ) {
|
|
|
|
|
|
+ geoInfo.skeleton = skeleton;
|
|
|
+
|
|
|
skeleton.rawBones.forEach( function ( rawBone, i ) {
|
|
|
|
|
|
// loop over the bone's vertex indices and weights
|
|
|
rawBone.indices.forEach( function ( index, j ) {
|
|
|
|
|
|
- if ( weightTable[ index ] === undefined ) weightTable[ index ] = [];
|
|
|
+ if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = [];
|
|
|
|
|
|
- weightTable[ index ].push( {
|
|
|
+ geoInfo.weightTable[ index ].push( {
|
|
|
|
|
|
id: i,
|
|
|
weight: rawBone.weights[ j ],
|
|
@@ -836,19 +1026,35 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
+ return geoInfo;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function genBuffers( geoInfo ) {
|
|
|
+
|
|
|
+ var buffers = {
|
|
|
+ vertex: [],
|
|
|
+ normal: [],
|
|
|
+ colors: [],
|
|
|
+ uvs: [],
|
|
|
+ materialIndex: [],
|
|
|
+ vertexWeights: [],
|
|
|
+ weightsIndices: [],
|
|
|
+ };
|
|
|
+
|
|
|
var polygonIndex = 0;
|
|
|
var faceLength = 0;
|
|
|
var displayedWeightsWarning = false;
|
|
|
|
|
|
// these will hold data for a single face
|
|
|
- var vertexPositionIndexes = [];
|
|
|
+ var facePositionIndexes = [];
|
|
|
var faceNormals = [];
|
|
|
var faceColors = [];
|
|
|
var faceUVs = [];
|
|
|
var faceWeights = [];
|
|
|
var faceWeightIndices = [];
|
|
|
|
|
|
- vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) {
|
|
|
+ geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) {
|
|
|
|
|
|
var endOfFace = false;
|
|
|
|
|
@@ -858,11 +1064,10 @@
|
|
|
// a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5
|
|
|
// }
|
|
|
// Negative numbers mark the end of a face - first face here is 0, 1, 3, -3
|
|
|
- // to find index of last vertex multiply by -1 and subtract 1: -3 * - 1 - 1 = 2
|
|
|
+ // to find index of last vertex bit shift the index: ^ - 1
|
|
|
if ( vertexIndex < 0 ) {
|
|
|
|
|
|
vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1
|
|
|
- vertexIndices[ polygonVertexIndex ] = vertexIndex;
|
|
|
endOfFace = true;
|
|
|
|
|
|
}
|
|
@@ -870,21 +1075,21 @@
|
|
|
var weightIndices = [];
|
|
|
var weights = [];
|
|
|
|
|
|
- vertexPositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 );
|
|
|
+ facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 );
|
|
|
|
|
|
- if ( colorInfo ) {
|
|
|
+ if ( geoInfo.color ) {
|
|
|
|
|
|
- var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, colorInfo );
|
|
|
+ var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color );
|
|
|
|
|
|
faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( skeleton ) {
|
|
|
+ if ( geoInfo.skeleton ) {
|
|
|
|
|
|
- if ( weightTable[ vertexIndex ] !== undefined ) {
|
|
|
+ if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) {
|
|
|
|
|
|
- weightTable[ vertexIndex ].forEach( function ( wt ) {
|
|
|
+ geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) {
|
|
|
|
|
|
weights.push( wt.weight );
|
|
|
weightIndices.push( wt.id );
|
|
@@ -950,23 +1155,23 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( normalInfo ) {
|
|
|
+ if ( geoInfo.normal ) {
|
|
|
|
|
|
- var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, normalInfo );
|
|
|
+ var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal );
|
|
|
|
|
|
faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
|
|
|
+ if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
|
|
|
|
|
|
- var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, materialInfo )[ 0 ];
|
|
|
+ var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ];
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( uvInfo ) {
|
|
|
+ if ( geoInfo.uv ) {
|
|
|
|
|
|
- uvInfo.forEach( function ( uv, i ) {
|
|
|
+ geoInfo.uv.forEach( function ( uv, i ) {
|
|
|
|
|
|
var data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv );
|
|
|
|
|
@@ -985,235 +1190,210 @@
|
|
|
|
|
|
faceLength ++;
|
|
|
|
|
|
- // we have reached the end of a face - it may have 4 sides though
|
|
|
- // in which case the data is split to represent two 3 sided faces
|
|
|
if ( endOfFace ) {
|
|
|
|
|
|
- for ( var i = 2; i < faceLength; i ++ ) {
|
|
|
-
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 0 ] ] );
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 1 ] ] );
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ 2 ] ] );
|
|
|
-
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 ] ] );
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 + 1 ] ] );
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ ( i - 1 ) * 3 + 2 ] ] );
|
|
|
+ genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength );
|
|
|
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 ] ] );
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 + 1 ] ] );
|
|
|
- vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 + 2 ] ] );
|
|
|
-
|
|
|
- if ( skeleton ) {
|
|
|
-
|
|
|
- vertexWeightsBuffer.push( faceWeights[ 0 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ 1 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ 2 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ 3 ] );
|
|
|
+ polygonIndex ++;
|
|
|
+ faceLength = 0;
|
|
|
|
|
|
- vertexWeightsBuffer.push( faceWeights[ ( i - 1 ) * 4 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ ( i - 1 ) * 4 + 1 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ ( i - 1 ) * 4 + 2 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ ( i - 1 ) * 4 + 3 ] );
|
|
|
+ // reset arrays for the next face
|
|
|
+ facePositionIndexes = [];
|
|
|
+ faceNormals = [];
|
|
|
+ faceColors = [];
|
|
|
+ faceUVs = [];
|
|
|
+ faceWeights = [];
|
|
|
+ faceWeightIndices = [];
|
|
|
|
|
|
- vertexWeightsBuffer.push( faceWeights[ i * 4 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ i * 4 + 1 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ i * 4 + 2 ] );
|
|
|
- vertexWeightsBuffer.push( faceWeights[ i * 4 + 3 ] );
|
|
|
+ }
|
|
|
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ 0 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ 1 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ 2 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ 3 ] );
|
|
|
+ } );
|
|
|
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ ( i - 1 ) * 4 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] );
|
|
|
+ return buffers;
|
|
|
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ i * 4 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ i * 4 + 1 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ i * 4 + 2 ] );
|
|
|
- weightsIndicesBuffer.push( faceWeightIndices[ i * 4 + 3 ] );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ // Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris
|
|
|
+ function genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) {
|
|
|
|
|
|
- if ( colorInfo ) {
|
|
|
+ for ( var i = 2; i < faceLength; i ++ ) {
|
|
|
|
|
|
- colorsBuffer.push( faceColors[ 0 ] );
|
|
|
- colorsBuffer.push( faceColors[ 1 ] );
|
|
|
- colorsBuffer.push( faceColors[ 2 ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] );
|
|
|
|
|
|
- colorsBuffer.push( faceColors[ ( i - 1 ) * 3 ] );
|
|
|
- colorsBuffer.push( faceColors[ ( i - 1 ) * 3 + 1 ] );
|
|
|
- colorsBuffer.push( faceColors[ ( i - 1 ) * 3 + 2 ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] );
|
|
|
|
|
|
- colorsBuffer.push( faceColors[ i * 3 ] );
|
|
|
- colorsBuffer.push( faceColors[ i * 3 + 1 ] );
|
|
|
- colorsBuffer.push( faceColors[ i * 3 + 2 ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] );
|
|
|
+ buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] );
|
|
|
|
|
|
- }
|
|
|
+ if ( geoInfo.skeleton ) {
|
|
|
|
|
|
- if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
|
|
|
+ buffers.vertexWeights.push( faceWeights[ 0 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ 1 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ 2 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ 3 ] );
|
|
|
|
|
|
- materialIndexBuffer.push( materialIndex );
|
|
|
- materialIndexBuffer.push( materialIndex );
|
|
|
- materialIndexBuffer.push( materialIndex );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] );
|
|
|
|
|
|
- }
|
|
|
+ buffers.vertexWeights.push( faceWeights[ i * 4 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] );
|
|
|
+ buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] );
|
|
|
|
|
|
- if ( normalInfo ) {
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ 0 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ 1 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ 2 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ 3 ] );
|
|
|
|
|
|
- normalBuffer.push( faceNormals[ 0 ] );
|
|
|
- normalBuffer.push( faceNormals[ 1 ] );
|
|
|
- normalBuffer.push( faceNormals[ 2 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] );
|
|
|
|
|
|
- normalBuffer.push( faceNormals[ ( i - 1 ) * 3 ] );
|
|
|
- normalBuffer.push( faceNormals[ ( i - 1 ) * 3 + 1 ] );
|
|
|
- normalBuffer.push( faceNormals[ ( i - 1 ) * 3 + 2 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] );
|
|
|
+ buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] );
|
|
|
|
|
|
- normalBuffer.push( faceNormals[ i * 3 ] );
|
|
|
- normalBuffer.push( faceNormals[ i * 3 + 1 ] );
|
|
|
- normalBuffer.push( faceNormals[ i * 3 + 2 ] );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ if ( geoInfo.color ) {
|
|
|
|
|
|
- if ( uvInfo ) {
|
|
|
+ buffers.colors.push( faceColors[ 0 ] );
|
|
|
+ buffers.colors.push( faceColors[ 1 ] );
|
|
|
+ buffers.colors.push( faceColors[ 2 ] );
|
|
|
|
|
|
- uvInfo.forEach( function ( uv, j ) {
|
|
|
+ buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] );
|
|
|
+ buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] );
|
|
|
+ buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] );
|
|
|
|
|
|
- if ( uvsBuffer[ j ] === undefined ) uvsBuffer[ j ] = [];
|
|
|
+ buffers.colors.push( faceColors[ i * 3 ] );
|
|
|
+ buffers.colors.push( faceColors[ i * 3 + 1 ] );
|
|
|
+ buffers.colors.push( faceColors[ i * 3 + 2 ] );
|
|
|
|
|
|
- uvsBuffer[ j ].push( faceUVs[ j ][ 0 ] );
|
|
|
- uvsBuffer[ j ].push( faceUVs[ j ][ 1 ] );
|
|
|
+ }
|
|
|
|
|
|
- uvsBuffer[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] );
|
|
|
- uvsBuffer[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] );
|
|
|
+ if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
|
|
|
|
|
|
- uvsBuffer[ j ].push( faceUVs[ j ][ i * 2 ] );
|
|
|
- uvsBuffer[ j ].push( faceUVs[ j ][ i * 2 + 1 ] );
|
|
|
+ buffers.materialIndex.push( materialIndex );
|
|
|
+ buffers.materialIndex.push( materialIndex );
|
|
|
+ buffers.materialIndex.push( materialIndex );
|
|
|
|
|
|
- } );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ if ( geoInfo.normal ) {
|
|
|
|
|
|
- }
|
|
|
+ buffers.normal.push( faceNormals[ 0 ] );
|
|
|
+ buffers.normal.push( faceNormals[ 1 ] );
|
|
|
+ buffers.normal.push( faceNormals[ 2 ] );
|
|
|
|
|
|
- polygonIndex ++;
|
|
|
- faceLength = 0;
|
|
|
+ buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] );
|
|
|
+ buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] );
|
|
|
+ buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] );
|
|
|
|
|
|
- // reset arrays for the next face
|
|
|
- vertexPositionIndexes = [];
|
|
|
- faceNormals = [];
|
|
|
- faceColors = [];
|
|
|
- faceUVs = [];
|
|
|
- faceWeights = [];
|
|
|
- faceWeightIndices = [];
|
|
|
+ buffers.normal.push( faceNormals[ i * 3 ] );
|
|
|
+ buffers.normal.push( faceNormals[ i * 3 + 1 ] );
|
|
|
+ buffers.normal.push( faceNormals[ i * 3 + 2 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- } );
|
|
|
+ if ( geoInfo.uv ) {
|
|
|
|
|
|
- var geo = new THREE.BufferGeometry();
|
|
|
- geo.name = geometryNode.name;
|
|
|
+ geoInfo.uv.forEach( function ( uv, j ) {
|
|
|
|
|
|
- var positionAttribute = new THREE.Float32BufferAttribute( vertexBuffer, 3 );
|
|
|
+ if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = [];
|
|
|
|
|
|
- preTransform.applyToBufferAttribute( positionAttribute );
|
|
|
+ buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] );
|
|
|
+ buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] );
|
|
|
|
|
|
- geo.addAttribute( 'position', positionAttribute );
|
|
|
-
|
|
|
- if ( colorsBuffer.length > 0 ) {
|
|
|
-
|
|
|
- geo.addAttribute( 'color', new THREE.Float32BufferAttribute( colorsBuffer, 3 ) );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( skeleton ) {
|
|
|
+ buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] );
|
|
|
+ buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] );
|
|
|
|
|
|
- geo.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( weightsIndicesBuffer, 4 ) );
|
|
|
+ buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] );
|
|
|
+ buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] );
|
|
|
|
|
|
- geo.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( vertexWeightsBuffer, 4 ) );
|
|
|
+ } );
|
|
|
|
|
|
- // used later to bind the skeleton to the model
|
|
|
- geo.FBX_Deformer = skeleton;
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( normalBuffer.length > 0 ) {
|
|
|
+ }
|
|
|
|
|
|
- var normalAttribute = new THREE.Float32BufferAttribute( normalBuffer, 3 );
|
|
|
+ function addMorphTargets( FBXTree, parentGeo, parentGeoNode, morphTarget, preTransform ) {
|
|
|
|
|
|
- var normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform );
|
|
|
- normalMatrix.applyToBufferAttribute( normalAttribute );
|
|
|
+ if ( morphTarget === null ) return;
|
|
|
|
|
|
- geo.addAttribute( 'normal', normalAttribute );
|
|
|
+ parentGeo.morphAttributes.position = [];
|
|
|
+ parentGeo.morphAttributes.normal = [];
|
|
|
|
|
|
- }
|
|
|
+ morphTarget.rawTargets.forEach( function ( rawTarget ) {
|
|
|
|
|
|
- uvsBuffer.forEach( function ( uvBuffer, i ) {
|
|
|
+ var morphGeoNode = FBXTree.Objects.Geometry[ rawTarget.geoID ];
|
|
|
|
|
|
- // subsequent uv buffers are called 'uv1', 'uv2', ...
|
|
|
- var name = 'uv' + ( i + 1 ).toString();
|
|
|
+ if ( morphGeoNode !== undefined ) {
|
|
|
|
|
|
- // the first uv buffer is just called 'uv'
|
|
|
- if ( i === 0 ) {
|
|
|
-
|
|
|
- name = 'uv';
|
|
|
+ genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform );
|
|
|
|
|
|
}
|
|
|
|
|
|
- geo.addAttribute( name, new THREE.Float32BufferAttribute( uvsBuffer[ i ], 2 ) );
|
|
|
-
|
|
|
} );
|
|
|
|
|
|
- if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
|
|
|
-
|
|
|
- // Convert the material indices of each vertex into rendering groups on the geometry.
|
|
|
- var prevMaterialIndex = materialIndexBuffer[ 0 ];
|
|
|
- var startIndex = 0;
|
|
|
-
|
|
|
- materialIndexBuffer.forEach( function ( currentIndex, i ) {
|
|
|
-
|
|
|
- if ( currentIndex !== prevMaterialIndex ) {
|
|
|
-
|
|
|
- geo.addGroup( startIndex, i - startIndex, prevMaterialIndex );
|
|
|
+ }
|
|
|
|
|
|
- prevMaterialIndex = currentIndex;
|
|
|
- startIndex = i;
|
|
|
+ // a morph geometry node is similar to a standard node, and the node is also contained
|
|
|
+ // in FBXTree.Objects.Geometry, however it can only have attributes for position, normal
|
|
|
+ // and a special attribute Index defining which vertices of the original geometry are affected
|
|
|
+ // Normal and position attributes only have data for the vertices that are affected by the morph
|
|
|
+ function genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform ) {
|
|
|
|
|
|
- }
|
|
|
+ var morphGeo = new THREE.BufferGeometry();
|
|
|
+ if ( morphGeoNode.attrName ) morphGeo.name = morphGeoNode.attrName;
|
|
|
|
|
|
- } );
|
|
|
+ var vertexIndices = ( parentGeoNode.PolygonVertexIndex !== undefined ) ? parentGeoNode.PolygonVertexIndex.a : [];
|
|
|
|
|
|
- // the loop above doesn't add the last group, do that here.
|
|
|
- if ( geo.groups.length > 0 ) {
|
|
|
+ // make a copy of the parent's vertex positions
|
|
|
+ var vertexPositions = ( parentGeoNode.Vertices !== undefined ) ? parentGeoNode.Vertices.a.slice() : [];
|
|
|
|
|
|
- var lastGroup = geo.groups[ geo.groups.length - 1 ];
|
|
|
- var lastIndex = lastGroup.start + lastGroup.count;
|
|
|
+ var morphPositions = ( morphGeoNode.Vertices !== undefined ) ? morphGeoNode.Vertices.a : [];
|
|
|
+ var indices = ( morphGeoNode.Indexes !== undefined ) ? morphGeoNode.Indexes.a : [];
|
|
|
|
|
|
- if ( lastIndex !== materialIndexBuffer.length ) {
|
|
|
+ for ( var i = 0; i < indices.length; i ++ ) {
|
|
|
|
|
|
- geo.addGroup( lastIndex, materialIndexBuffer.length - lastIndex, prevMaterialIndex );
|
|
|
+ var morphIndex = indices[ i ] * 3;
|
|
|
|
|
|
- }
|
|
|
+ // FBX format uses blend shapes rather than morph targets. This can be converted
|
|
|
+ // by additively combining the blend shape positions with the original geometry's positions
|
|
|
+ vertexPositions[ morphIndex ] += morphPositions[ i * 3 ];
|
|
|
+ vertexPositions[ morphIndex + 1 ] += morphPositions[ i * 3 + 1 ];
|
|
|
+ vertexPositions[ morphIndex + 2 ] += morphPositions[ i * 3 + 2 ];
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // case where there are multiple materials but the whole geometry is only
|
|
|
- // using one of them
|
|
|
- if ( geo.groups.length === 0 ) {
|
|
|
+ // TODO: add morph normal support
|
|
|
+ var morphGeoInfo = {
|
|
|
+ vertexIndices: vertexIndices,
|
|
|
+ vertexPositions: vertexPositions,
|
|
|
+ };
|
|
|
|
|
|
- geo.addGroup( 0, materialIndexBuffer.length, materialIndexBuffer[ 0 ] );
|
|
|
+ var morphBuffers = genBuffers( morphGeoInfo );
|
|
|
|
|
|
- }
|
|
|
+ var positionAttribute = new THREE.Float32BufferAttribute( morphBuffers.vertex, 3 );
|
|
|
+ positionAttribute.name = morphGeoNode.attrName;
|
|
|
|
|
|
- }
|
|
|
+ preTransform.applyToBufferAttribute( positionAttribute );
|
|
|
|
|
|
- return geo;
|
|
|
+ parentGeo.morphAttributes.position.push( positionAttribute );
|
|
|
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists
|
|
|
function getNormals( NormalNode ) {
|
|
|
|
|
@@ -1367,7 +1547,7 @@
|
|
|
}
|
|
|
|
|
|
// Generate a NurbGeometry from a node in FBXTree.Objects.Geometry
|
|
|
- function parseNurbsGeometry( geometryNode ) {
|
|
|
+ function parseNurbsGeometry( geoNode ) {
|
|
|
|
|
|
if ( THREE.NURBSCurve === undefined ) {
|
|
|
|
|
@@ -1376,20 +1556,20 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- var order = parseInt( geometryNode.Order );
|
|
|
+ var order = parseInt( geoNode.Order );
|
|
|
|
|
|
if ( isNaN( order ) ) {
|
|
|
|
|
|
- console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geometryNode.Order, geometryNode.id );
|
|
|
+ console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id );
|
|
|
return new THREE.BufferGeometry();
|
|
|
|
|
|
}
|
|
|
|
|
|
var degree = order - 1;
|
|
|
|
|
|
- var knots = geometryNode.KnotVector.a;
|
|
|
+ var knots = geoNode.KnotVector.a;
|
|
|
var controlPoints = [];
|
|
|
- var pointsValues = geometryNode.Points.a;
|
|
|
+ var pointsValues = geoNode.Points.a;
|
|
|
|
|
|
for ( var i = 0, l = pointsValues.length; i < l; i += 4 ) {
|
|
|
|
|
@@ -1399,11 +1579,11 @@
|
|
|
|
|
|
var startKnot, endKnot;
|
|
|
|
|
|
- if ( geometryNode.Form === 'Closed' ) {
|
|
|
+ if ( geoNode.Form === 'Closed' ) {
|
|
|
|
|
|
controlPoints.push( controlPoints[ 0 ] );
|
|
|
|
|
|
- } else if ( geometryNode.Form === 'Periodic' ) {
|
|
|
+ } else if ( geoNode.Form === 'Periodic' ) {
|
|
|
|
|
|
startKnot = degree;
|
|
|
endKnot = knots.length - 1 - startKnot;
|