diff --git a/docs/api/loaders/ImageBitmapLoader.html b/docs/api/loaders/ImageBitmapLoader.html
new file mode 100644
index 0000000000000000000000000000000000000000..5638e2ddffd48e0eeb118190356fdeadfa851395
--- /dev/null
+++ b/docs/api/loaders/ImageBitmapLoader.html
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+ [name]
+
+
+ A loader for loading an [page:Image] as an [link:https://developer.mozilla.org/de/docs/Web/API/ImageBitmap ImageBitmap]. An ImageBitmap provides an asynchronous and resource efficient pathway to prepare textures for rendering in WebGL.
+
+
+
+ Example
+
+
+ [example:webgl_loader_imagebitmap WebGL / loader / ImageBitmap]
+
+
+
+ // instantiate a loader
+ var loader = new THREE.ImageBitmapLoader();
+
+ // load a image resource
+ loader.load(
+ // resource URL
+ 'textures/skyboxsun25degtest.png',
+ // Function when resource is loaded
+ function ( imageBitmap ) {
+ var texture = new THREE.CanvasTexture( imageBitmap );
+ var material = new THREE.MeshBasicMaterial( { map: texture } );
+ },
+ // Function called when download progresses
+ function ( xhr ) {
+ console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
+ },
+ // Function called when download errors
+ function ( xhr ) {
+ console.log( 'An error happened' );
+ }
+ );
+
+
+
+ Constructor
+
+ [name]( [page:LoadingManager manager] )
+
+ [page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
+
+ Creates a new [name].
+
+
+ Properties
+
+ [property:LoadingManager manager]
+
+ The [page:LoadingManager loadingManager] the loader is using. Default is [page:DefaultLoadingManager].
+
+
+ [property:String options]
+ An optional object that sets options for the internally used [link:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap createImageBitmap] factory method. Default is *undefined*.
+
+ [property:String path]
+ The base path from which files will be loaded. See [page:.setPath]. Default is *undefined*.
+
+ Methods
+
+ [method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )
+
+ [page:String url] — the path or URL to the file. This can also be a
+ [link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URI].
+ [page:Function onLoad] — Will be called when load completes. The argument will be the loaded [page:Image image].
+ [page:Function onProgress] — Will be called while load progresses. The argument will be the progress event.
+ [page:Function onError] — Will be called when load errors.
+
+
+ Begin loading from url and return the [page:ImageBitmap image] object that will contain the data.
+
+
+ [method:ImageBitmapLoader setCrossOrigin]()
+ This method exists for compatibility reasons and implements no logic. It ensures that [name] has a similar interface like [page:ImageLoader].
+
+ [method:ImageBitmapLoader setOptions]( [page:Object options] )
+
+ Sets the options object for [link:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap createImageBitmap].
+
+
+ [method:ImageBitmapLoader setPath]( [page:String path] )
+
+ Sets the base path or URL from which to load files. This can be useful if
+ you are loading many images from the same directory.
+
+
+
+ Source
+
+ [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+
+
diff --git a/docs/api/loaders/ImageLoader.html b/docs/api/loaders/ImageLoader.html
index 3100e699b91f21d87d64edeccaa567515b64cd6b..23bb2437cc892ce0b53ed06d75d023b362790d14 100644
--- a/docs/api/loaders/ImageLoader.html
+++ b/docs/api/loaders/ImageLoader.html
@@ -78,12 +78,6 @@
[property:String path]
The base path from which files will be loaded. See [page:.setPath]. Default is *undefined*.
- [property:String withCredentials]
-
- Whether the XMLHttpRequest uses credentials - see [page:.setWithCredentials].
- Default is *undefined*.
-
-
Methods
[method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )
@@ -107,13 +101,6 @@
you are loading many images from the same directory.
- [method:FileLoader setWithCredentials]( [page:Boolean value] )
- Whether the XMLHttpRequest uses credentials such as cookies, authorization headers or
- TLS client certificates. See
- [link:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials XMLHttpRequest.withCredentials].
- Note that this has no effect if you are loading files locally or from the same domain.
-
-
Source
[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
diff --git a/docs/api/textures/Texture.html b/docs/api/textures/Texture.html
index 440c396535632f3785a5650622ef753084eb7f9a..2d38f66e388ad7c1710a34811d096e689e327e52 100644
--- a/docs/api/textures/Texture.html
+++ b/docs/api/textures/Texture.html
@@ -15,7 +15,7 @@
Constructor
- [name]( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy )
+ [name]( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding )
Example
diff --git a/docs/list.js b/docs/list.js
index c8c396fa7ad5b15ac08890e7a80f5e6571ee42f4..0b56008b55e461b0f40a0bfd684151c928155477 100644
--- a/docs/list.js
+++ b/docs/list.js
@@ -224,6 +224,7 @@ var list = {
"DataTextureLoader": "api/loaders/DataTextureLoader",
"FileLoader": "api/loaders/FileLoader",
"FontLoader": "api/loaders/FontLoader",
+ "ImageBitmapLoader": "api/loaders/ImageBitmapLoader",
"ImageLoader": "api/loaders/ImageLoader",
"JSONLoader": "api/loaders/JSONLoader",
"Loader": "api/loaders/Loader",
diff --git a/examples/js/exporters/GLTFExporter.js b/examples/js/exporters/GLTFExporter.js
index 0b70b1f64c48a39bfa092383c0c71b0defd729ee..fa327470ea9b84c2047dd8544820e0809c6e3f66 100644
--- a/examples/js/exporters/GLTFExporter.js
+++ b/examples/js/exporters/GLTFExporter.js
@@ -121,9 +121,9 @@ THREE.GLTFExporter.prototype = {
}
- var buffer = new ArrayBuffer( text.length * 2 ); // 2 bytes per character.
+ var buffer = new ArrayBuffer( text.length );
- var bufferView = new Uint16Array( buffer );
+ var bufferView = new Uint8Array( buffer );
for ( var i = 0; i < text.length; ++ i ) {
@@ -380,6 +380,14 @@ THREE.GLTFExporter.prototype = {
canvas.width = map.image.width;
canvas.height = map.image.height;
var ctx = canvas.getContext( '2d' );
+
+ if ( map.flipY === true ) {
+
+ ctx.translate( 0, map.image.height );
+ ctx.scale( 1, -1 );
+
+ }
+
ctx.drawImage( map.image, 0, 0 );
// @TODO Embed in { bufferView } if options.binary set.
diff --git a/examples/js/loaders/ColladaLoader.js b/examples/js/loaders/ColladaLoader.js
index 1b2f8f20b16d625c0c0b75f3a5d6e8580674c07f..cf8515fc9ad66e7da2972207fe1590ff7e4cacb3 100644
--- a/examples/js/loaders/ColladaLoader.js
+++ b/examples/js/loaders/ColladaLoader.js
@@ -19,7 +19,7 @@ THREE.ColladaLoader.prototype = {
var scope = this;
- var path = THREE.Loader.prototype.extractUrlBase( url );
+ var path = scope.path === undefined ? THREE.Loader.prototype.extractUrlBase( url ) : scope.path;
var loader = new THREE.FileLoader( scope.manager );
loader.load( url, function ( text ) {
@@ -30,6 +30,12 @@ THREE.ColladaLoader.prototype = {
},
+ setPath: function ( value ) {
+
+ this.path = value;
+
+ },
+
options: {
set convertUpAxis( value ) {
diff --git a/examples/js/loaders/FBXLoader.js b/examples/js/loaders/FBXLoader.js
index 018cdbb6c5b223a14f5241c3c3bf2083e94d00d7..0f650c67a58fe5db0e975789887c266b413b685f 100644
--- a/examples/js/loaders/FBXLoader.js
+++ b/examples/js/loaders/FBXLoader.js
@@ -304,29 +304,61 @@
// Parse individual node in FBXTree.Objects.subNodes.Texture
function parseTexture( textureNode, loader, imageMap, connections ) {
- var FBX_ID = textureNode.id;
+ var texture = loadTexture( textureNode, loader, imageMap, connections );
- var name = textureNode.attrName;
+ texture.FBX_ID = textureNode.id;
+
+ texture.name = textureNode.attrName;
+
+ var wrapModeU = textureNode.properties.WrapModeU;
+ var wrapModeV = textureNode.properties.WrapModeV;
+
+ var valueU = wrapModeU !== undefined ? wrapModeU.value : 0;
+ var valueV = wrapModeV !== undefined ? wrapModeV.value : 0;
+
+ // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a
+ // 0: repeat(default), 1: clamp
+
+ texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+ texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+
+ if ( 'Scaling' in textureNode.properties ) {
+
+ var values = textureNode.properties.Scaling.value;
+
+ texture.repeat.x = values[ 0 ];
+ texture.repeat.y = values[ 1 ];
+
+ }
+
+ return texture;
+
+ }
+
+ // load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader
+ function loadTexture( textureNode, loader, imageMap, connections ) {
var fileName;
var filePath = textureNode.properties.FileName;
var relativeFilePath = textureNode.properties.RelativeFilename;
- var children = connections.get( FBX_ID ).children;
+ var children = connections.get( textureNode.id ).children;
+ // embedded texture
if ( children !== undefined && children.length > 0 && imageMap.has( children[ 0 ].ID ) ) {
fileName = imageMap.get( children[ 0 ].ID );
- } else if ( relativeFilePath !== undefined && relativeFilePath[ 0 ] !== '/' && relativeFilePath.match( /^[a-zA-Z]:/ ) === null ) {
-
- // use textureNode.properties.RelativeFilename
- // if it exists and it doesn't seem an absolute path
+ }
+ // check that relative path is not an actually an absolute path and if so use it to load texture
+ else if ( relativeFilePath !== undefined && relativeFilePath[ 0 ] !== '/' && relativeFilePath.match( /^[a-zA-Z]:/ ) === null ) {
fileName = relativeFilePath;
- } else {
+ }
+ // texture specified by absolute path
+ else {
var split = filePath.split( /[\\\/]/ );
@@ -351,29 +383,6 @@
}
var texture = loader.load( fileName );
- texture.name = name;
- texture.FBX_ID = FBX_ID;
-
- var wrapModeU = textureNode.properties.WrapModeU;
- var wrapModeV = textureNode.properties.WrapModeV;
-
- var valueU = wrapModeU !== undefined ? wrapModeU.value : 0;
- var valueV = wrapModeV !== undefined ? wrapModeV.value : 0;
-
- // http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a
- // 0: repeat(default), 1: clamp
-
- texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
- texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-
- if ( 'Scaling' in textureNode.properties ) {
-
- var values = textureNode.properties.Scaling.value;
-
- texture.repeat.x = values[ 0 ];
- texture.repeat.y = values[ 1 ];
-
- }
loader.setPath( currentPath );
@@ -381,7 +390,6 @@
}
-
// Parse nodes in FBXTree.Objects.subNodes.Material
function parseMaterials( FBXTree, textureMap, connections ) {
@@ -392,7 +400,7 @@
var materialNodes = FBXTree.Objects.subNodes.Material;
for ( var nodeID in materialNodes ) {
- var material = parseMaterial( materialNodes[ nodeID ], textureMap, connections );
+ var material = parseMaterial( FBXTree, materialNodes[ nodeID ], textureMap, connections );
if ( material !== null ) materialMap.set( parseInt( nodeID ), material );
}
@@ -406,7 +414,7 @@
// Parse single node in FBXTree.Objects.subNodes.Material
// Materials are connected to texture maps in FBXTree.Objects.subNodes.Textures
// FBX format currently only supports Lambert and Phong shading models
- function parseMaterial( materialNode, textureMap, connections ) {
+ function parseMaterial( FBXTree, materialNode, textureMap, connections ) {
var FBX_ID = materialNode.id;
var name = materialNode.attrName;
@@ -424,7 +432,7 @@
var children = connections.get( FBX_ID ).children;
- var parameters = parseParameters( materialNode.properties, textureMap, children );
+ var parameters = parseParameters( FBXTree, materialNode.properties, textureMap, children, connections );
var material;
@@ -452,7 +460,7 @@
// Parse FBX material and return parameters suitable for a three.js material
// Also parse the texture map and return any textures associated with the material
- function parseParameters( properties, textureMap, childrenRelationships ) {
+ function parseParameters( FBXTree, properties, textureMap, childrenRelationships, connections ) {
var parameters = {};
@@ -520,33 +528,33 @@
break;
case 'DiffuseColor':
- parameters.map = textureMap.get( relationship.ID );
+ parameters.map = getTexture( FBXTree, textureMap, relationship.ID, connections );
break;
case 'DisplacementColor':
- parameters.displacementMap = textureMap.get( relationship.ID );
+ parameters.displacementMap = getTexture( FBXTree, textureMap, relationship.ID, connections );
break;
case 'EmissiveColor':
- parameters.emissiveMap = textureMap.get( relationship.ID );
+ parameters.emissiveMap = getTexture( FBXTree, textureMap, relationship.ID, connections );
break;
case 'NormalMap':
- parameters.normalMap = textureMap.get( relationship.ID );
+ parameters.normalMap = getTexture( FBXTree, textureMap, relationship.ID, connections );
break;
case 'ReflectionColor':
- parameters.envMap = textureMap.get( relationship.ID );
+ parameters.envMap = getTexture( FBXTree, textureMap, relationship.ID, connections );
parameters.envMap.mapping = THREE.EquirectangularReflectionMapping;
break;
case 'SpecularColor':
- parameters.specularMap = textureMap.get( relationship.ID );
+ parameters.specularMap = getTexture( FBXTree, textureMap, relationship.ID, connections );
break;
case 'TransparentColor':
- parameters.alphaMap = textureMap.get( relationship.ID );
+ parameters.alphaMap = getTexture( FBXTree, textureMap, relationship.ID, connections );
parameters.transparent = true;
break;
@@ -566,6 +574,21 @@
}
+ // get a texture from the textureMap for use by a material.
+ function getTexture( FBXTree, textureMap, id, connections ) {
+
+ // if the texture is a layered texture, just use the first layer and issue a warning
+ if ( 'LayeredTexture' in FBXTree.Objects.subNodes && id in FBXTree.Objects.subNodes.LayeredTexture ) {
+
+ console.warn( 'THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.' );
+ id = connections.get( id ).children[ 0 ].ID;
+
+ }
+
+ return textureMap.get( id );
+
+ }
+
// Parse nodes in FBXTree.Objects.subNodes.Deformer
// Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here
// Generates map of Skeleton-like objects for use later when generating and binding skeletons.
@@ -743,7 +766,6 @@
}
-
var weightTable = {};
if ( deformer ) {
@@ -843,7 +865,7 @@
}
- var WIndex = [ 0, 0, 0, 0 ];
+ var wIndex = [ 0, 0, 0, 0 ];
var Weight = [ 0, 0, 0, 0 ];
weights.forEach( function ( weight, weightIndex ) {
@@ -858,8 +880,8 @@
comparedWeightArray[ comparedWeightIndex ] = currentWeight;
currentWeight = comparedWeight;
- var tmp = WIndex[ comparedWeightIndex ];
- WIndex[ comparedWeightIndex ] = currentIndex;
+ var tmp = wIndex[ comparedWeightIndex ];
+ wIndex[ comparedWeightIndex ] = currentIndex;
currentIndex = tmp;
}
@@ -868,7 +890,7 @@
} );
- weightIndices = WIndex;
+ weightIndices = wIndex;
weights = Weight;
}
@@ -898,6 +920,12 @@
}
+ if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
+
+ var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, materialInfo )[ 0 ];
+
+ }
+
if ( uvInfo ) {
for ( var i = 0; i < uvInfo.length; i ++ ) {
@@ -922,7 +950,7 @@
faceLength ++;
// we have reached the end of a face - it may have 4 sides though
- // in which case the data is split into to represent 3 sides faces
+ // in which case the data is split to represent two 3 sided faces
if ( endOfFace ) {
for ( var i = 2; i < faceLength; i ++ ) {
@@ -939,11 +967,7 @@
vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 + 1 ] ] );
vertexBuffer.push( vertexPositions[ vertexPositionIndexes[ i * 3 + 2 ] ] );
- }
-
- if ( deformer ) {
-
- for ( var i = 2; i < faceLength; i ++ ) {
+ if ( deformer ) {
vertexWeightsBuffer.push( faceWeights[ 0 ] );
vertexWeightsBuffer.push( faceWeights[ 1 ] );
@@ -977,11 +1001,31 @@
}
- }
+ if ( colorInfo ) {
+
+ colorsBuffer.push( faceColors[ 0 ] );
+ colorsBuffer.push( faceColors[ 1 ] );
+ colorsBuffer.push( faceColors[ 2 ] );
+
+ colorsBuffer.push( faceColors[ ( i - 1 ) * 3 ] );
+ colorsBuffer.push( faceColors[ ( i - 1 ) * 3 + 1 ] );
+ colorsBuffer.push( faceColors[ ( i - 1 ) * 3 + 2 ] );
+
+ colorsBuffer.push( faceColors[ i * 3 ] );
+ colorsBuffer.push( faceColors[ i * 3 + 1 ] );
+ colorsBuffer.push( faceColors[ i * 3 + 2 ] );
+
+ }
+
+ if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
+
+ materialIndexBuffer.push( materialIndex );
+ materialIndexBuffer.push( materialIndex );
+ materialIndexBuffer.push( materialIndex );
- if ( normalInfo ) {
+ }
- for ( var i = 2; i < faceLength; i ++ ) {
+ if ( normalInfo ) {
normalBuffer.push( faceNormals[ 0 ] );
normalBuffer.push( faceNormals[ 1 ] );
@@ -997,15 +1041,11 @@
}
- }
-
- if ( uvInfo ) {
-
- for ( var j = 0; j < uvInfo.length; j ++ ) {
+ if ( uvInfo ) {
- if ( uvsBuffer[ j ] === undefined ) uvsBuffer[ j ] = [];
+ for ( var j = 0; j < uvInfo.length; j ++ ) {
- for ( var i = 2; i < faceLength; i ++ ) {
+ if ( uvsBuffer[ j ] === undefined ) uvsBuffer[ j ] = [];
uvsBuffer[ j ].push( faceUVs[ j ][ 0 ] );
uvsBuffer[ j ].push( faceUVs[ j ][ 1 ] );
@@ -1022,41 +1062,6 @@
}
- if ( colorInfo ) {
-
- for ( var i = 2; i < faceLength; i ++ ) {
-
-
- colorsBuffer.push( faceColors[ 0 ] );
- colorsBuffer.push( faceColors[ 1 ] );
- colorsBuffer.push( faceColors[ 2 ] );
-
- colorsBuffer.push( faceColors[ ( i - 1 ) * 3 ] );
- colorsBuffer.push( faceColors[ ( i - 1 ) * 3 + 1 ] );
- colorsBuffer.push( faceColors[ ( i - 1 ) * 3 + 2 ] );
-
- colorsBuffer.push( faceColors[ i * 3 ] );
- colorsBuffer.push( faceColors[ i * 3 + 1 ] );
- colorsBuffer.push( faceColors[ i * 3 + 2 ] );
-
- }
-
- }
-
- if ( materialInfo && materialInfo.mappingType !== 'AllSame' ) {
-
- var materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, materialInfo )[ 0 ];
-
- for ( var i = 2; i < faceLength; i ++ ) {
-
- materialIndexBuffer.push( materialIndex );
- materialIndexBuffer.push( materialIndex );
- materialIndexBuffer.push( materialIndex );
-
- }
-
- }
-
polygonIndex ++;
endOfFace = false;
@@ -1165,6 +1170,7 @@
}
+
// Parse normal from FBXTree.Objects.subNodes.Geometry.subNodes.LayerElementNormal if it exists
function getNormals( NormalNode ) {
@@ -1446,22 +1452,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 );
+
+ }
+
+ } );
+
+ bindSkeleton( FBXTree, deformers, geometryMap, modelMap, connections, sceneGraph );
+
+ addAnimations( FBXTree, connections, sceneGraph );
+
+ createAmbientLight( FBXTree, sceneGraph );
- var modelArray = [];
+ 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;
@@ -1493,433 +1543,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 ) {
+
+ var ambientColor = FBXTree.GlobalSettings.properties.AmbientColor.value;
+ var r = ambientColor[ 0 ];
+ var g = ambientColor[ 1 ];
+ var b = ambientColor[ 2 ];
- if ( 'Lcl_Scaling' in node.properties ) {
+ if ( r !== 0 || g !== 0 || b !== 0 ) {
- model.scale.fromArray( node.properties.Lcl_Scaling.value );
+ 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 );
@@ -2017,32 +2091,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;
+ };
}
@@ -2410,7 +2462,7 @@
var model = rawModels[ returnObject.containerID.toString() ];
if ( 'PreRotation' in model.properties ) {
- returnObject.preRotations = parseVector3( model.properties.PreRotation ).multiplyScalar( Math.PI / 180 );
+ returnObject.preRotations = new THREE.Vector3().fromArray( model.properties.PreRotation.value ).multiplyScalar( Math.PI / 180 );
}
break;
@@ -2490,11 +2542,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 = [];
}
@@ -2511,7 +2565,7 @@
hierarchy: []
};
- var bones = group.skeleton.bones;
+ var bones = sceneGraph.skeleton.bones;
for ( var bonesIndex = 0, bonesLength = bones.length; bonesIndex < bonesLength; ++ bonesIndex ) {
@@ -2552,7 +2606,7 @@
}
- group.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
+ sceneGraph.animations.push( THREE.AnimationClip.parseAnimation( animationData, bones ) );
}
diff --git a/examples/webgl_shadowmap_pointlight.html b/examples/webgl_shadowmap_pointlight.html
index d064d58fcab7ee358c3b6ef27e96a96cdd0f1b82..6869f633b290a429faaa2bc8da08cf6065092bad 100644
--- a/examples/webgl_shadowmap_pointlight.html
+++ b/examples/webgl_shadowmap_pointlight.html
@@ -83,8 +83,7 @@
var material = new THREE.MeshPhongMaterial( {
side: THREE.DoubleSide,
alphaMap: texture,
- alphaTest: 0.5,
- transparent: true
+ alphaTest: 0.5
} );
var sphere = new THREE.Mesh( geometry, material );