|
@@ -15754,6 +15754,88 @@ THREE.Sprite.prototype.clone = function ( object ) {
|
|
|
|
|
|
THREE.Particle = THREE.Sprite;
|
|
|
|
|
|
+// File:src/objects/LensFlare.js
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author mikael emtinger / http://gomo.se/
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ */
|
|
|
+
|
|
|
+THREE.LensFlare = function ( texture, size, distance, blending, color ) {
|
|
|
+
|
|
|
+ THREE.Object3D.call( this );
|
|
|
+
|
|
|
+ this.lensFlares = [];
|
|
|
+
|
|
|
+ this.positionScreen = new THREE.Vector3();
|
|
|
+ this.customUpdateCallback = undefined;
|
|
|
+
|
|
|
+ if( texture !== undefined ) {
|
|
|
+
|
|
|
+ this.add( texture, size, distance, blending, color );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Add: adds another flare
|
|
|
+ */
|
|
|
+
|
|
|
+THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
|
|
|
+
|
|
|
+ if ( size === undefined ) size = - 1;
|
|
|
+ if ( distance === undefined ) distance = 0;
|
|
|
+ if ( opacity === undefined ) opacity = 1;
|
|
|
+ if ( color === undefined ) color = new THREE.Color( 0xffffff );
|
|
|
+ if ( blending === undefined ) blending = THREE.NormalBlending;
|
|
|
+
|
|
|
+ distance = Math.min( distance, Math.max( 0, distance ) );
|
|
|
+
|
|
|
+ this.lensFlares.push( {
|
|
|
+ texture: texture, // THREE.Texture
|
|
|
+ size: size, // size in pixels (-1 = use texture.width)
|
|
|
+ distance: distance, // distance (0-1) from light source (0=at light source)
|
|
|
+ x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back
|
|
|
+ scale: 1, // scale
|
|
|
+ rotation: 1, // rotation
|
|
|
+ opacity: opacity, // opacity
|
|
|
+ color: color, // color
|
|
|
+ blending: blending // blending
|
|
|
+ } );
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Update lens flares update positions on all flares based on the screen position
|
|
|
+ * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
|
|
|
+ */
|
|
|
+
|
|
|
+THREE.LensFlare.prototype.updateLensFlares = function () {
|
|
|
+
|
|
|
+ var f, fl = this.lensFlares.length;
|
|
|
+ var flare;
|
|
|
+ var vecX = - this.positionScreen.x * 2;
|
|
|
+ var vecY = - this.positionScreen.y * 2;
|
|
|
+
|
|
|
+ for( f = 0; f < fl; f ++ ) {
|
|
|
+
|
|
|
+ flare = this.lensFlares[ f ];
|
|
|
+
|
|
|
+ flare.x = this.positionScreen.x + vecX * flare.distance;
|
|
|
+ flare.y = this.positionScreen.y + vecY * flare.distance;
|
|
|
+
|
|
|
+ flare.wantedRotation = flare.x * Math.PI * 0.25;
|
|
|
+ flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
// File:src/scenes/Scene.js
|
|
|
|
|
|
/**
|
|
@@ -17596,6 +17678,9 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
var opaqueObjects = [];
|
|
|
var transparentObjects = [];
|
|
|
|
|
|
+ var sprites = [];
|
|
|
+ var lensFlares = [];
|
|
|
+
|
|
|
// public properties
|
|
|
|
|
|
this.domElement = _canvas;
|
|
@@ -17640,11 +17725,6 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
this.autoScaleCubemaps = true;
|
|
|
|
|
|
- // custom render plugins
|
|
|
-
|
|
|
- this.renderPluginsPre = [];
|
|
|
- this.renderPluginsPost = [];
|
|
|
-
|
|
|
// info
|
|
|
|
|
|
this.info = {
|
|
@@ -17913,6 +17993,13 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // Plugins
|
|
|
+
|
|
|
+ var shadowMapPlugin = new THREE.ShadowMapPlugin( this, lights, _webglObjects, _webglObjectsImmediate );
|
|
|
+
|
|
|
+ var spritePlugin = new THREE.SpritePlugin( this, sprites );
|
|
|
+ var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares );
|
|
|
+
|
|
|
// API
|
|
|
|
|
|
this.getContext = function () {
|
|
@@ -18097,24 +18184,6 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
};
|
|
|
|
|
|
- // Plugins
|
|
|
-
|
|
|
- this.addPostPlugin = function ( plugin ) {
|
|
|
-
|
|
|
- plugin.init( this, lights, _webglObjects, _webglObjectsImmediate );
|
|
|
-
|
|
|
- this.renderPluginsPost.push( plugin );
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
- this.addPrePlugin = function ( plugin ) {
|
|
|
-
|
|
|
- plugin.init( this, lights, _webglObjects, _webglObjectsImmediate );
|
|
|
-
|
|
|
- this.renderPluginsPre.push( plugin );
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
// Reset
|
|
|
|
|
|
this.resetGLState = function () {
|
|
@@ -18148,7 +18217,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
_oldDoubleSided = - 1;
|
|
|
_oldFlipSided = - 1;
|
|
|
|
|
|
- this.shadowMapPlugin.update( scene, camera );
|
|
|
+ shadowMapPlugin.update( scene, camera );
|
|
|
|
|
|
};
|
|
|
|
|
@@ -20955,6 +21024,9 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
opaqueObjects.length = 0;
|
|
|
transparentObjects.length = 0;
|
|
|
|
|
|
+ sprites.length = 0;
|
|
|
+ lensFlares.length = 0;
|
|
|
+
|
|
|
projectObject( scene, scene );
|
|
|
|
|
|
if ( _this.sortObjects === true ) {
|
|
@@ -20966,7 +21038,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
// custom render plugins (pre pass)
|
|
|
|
|
|
- renderPlugins( this.renderPluginsPre, scene, camera );
|
|
|
+ shadowMapPlugin.render( scene, camera );
|
|
|
|
|
|
//
|
|
|
|
|
@@ -21033,8 +21105,8 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
// custom render plugins (post pass)
|
|
|
|
|
|
- renderPlugins( this.renderPluginsPost, scene, camera );
|
|
|
-
|
|
|
+ spritePlugin.render( scene, camera );
|
|
|
+ lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight );
|
|
|
|
|
|
// Generate mipmap if we're using any kind of mipmap filtering
|
|
|
|
|
@@ -21071,32 +21143,44 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
initObject( object, scene );
|
|
|
|
|
|
- var webglObjects = _webglObjects[ object.id ];
|
|
|
+ if ( object instanceof THREE.Sprite ) {
|
|
|
|
|
|
- if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
|
|
|
+ sprites.push( object );
|
|
|
|
|
|
- updateObject( object, scene );
|
|
|
+ } else if ( object instanceof THREE.LensFlare ) {
|
|
|
|
|
|
- for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
|
|
|
+ lensFlares.push( object );
|
|
|
|
|
|
- var webglObject = webglObjects[i];
|
|
|
+ } else {
|
|
|
|
|
|
- unrollBufferMaterial( webglObject );
|
|
|
+ var webglObjects = _webglObjects[ object.id ];
|
|
|
|
|
|
- webglObject.render = true;
|
|
|
+ if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {
|
|
|
|
|
|
- if ( _this.sortObjects === true ) {
|
|
|
+ updateObject( object, scene );
|
|
|
|
|
|
- if ( object.renderDepth !== null ) {
|
|
|
+ for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
|
|
|
|
|
|
- webglObject.z = object.renderDepth;
|
|
|
+ var webglObject = webglObjects[i];
|
|
|
|
|
|
- } else {
|
|
|
+ unrollBufferMaterial( webglObject );
|
|
|
+
|
|
|
+ webglObject.render = true;
|
|
|
+
|
|
|
+ if ( _this.sortObjects === true ) {
|
|
|
+
|
|
|
+ if ( object.renderDepth !== null ) {
|
|
|
+
|
|
|
+ webglObject.z = object.renderDepth;
|
|
|
|
|
|
- _vector3.setFromMatrixPosition( object.matrixWorld );
|
|
|
- _vector3.applyProjection( _projScreenMatrix );
|
|
|
+ } else {
|
|
|
+
|
|
|
+ _vector3.setFromMatrixPosition( object.matrixWorld );
|
|
|
+ _vector3.applyProjection( _projScreenMatrix );
|
|
|
|
|
|
- webglObject.z = _vector3.z;
|
|
|
+ webglObject.z = _vector3.z;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
@@ -21116,18 +21200,6 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- function renderPlugins( plugins, scene, camera ) {
|
|
|
-
|
|
|
- if ( plugins.length === 0 ) return;
|
|
|
-
|
|
|
- for ( var i = 0, il = plugins.length; i < il; i ++ ) {
|
|
|
-
|
|
|
- plugins[ i ].render( scene, camera, _currentWidth, _currentHeight );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) {
|
|
|
|
|
|
var material;
|
|
@@ -21651,7 +21723,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
// Objects updates - custom attributes check
|
|
|
|
|
@@ -21665,7 +21737,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function clearCustomAttributes( material ) {
|
|
|
|
|
@@ -21675,7 +21747,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
// Objects removal
|
|
|
|
|
@@ -21699,7 +21771,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
delete object.__webglActive;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function removeInstances( objlist, object ) {
|
|
|
|
|
@@ -21713,16 +21785,10 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
// Materials
|
|
|
|
|
|
- this.initMaterial = function () {
|
|
|
-
|
|
|
- console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
function initMaterial( material, lights, fog, object ) {
|
|
|
|
|
|
material.addEventListener( 'dispose', onMaterialDispose );
|
|
@@ -21959,7 +22025,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function setProgram( camera, lights, fog, material, object ) {
|
|
|
|
|
@@ -22204,7 +22270,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
return program;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
// Uniforms (refresh uniforms objects)
|
|
|
|
|
@@ -22299,14 +22365,14 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
uniforms.combine.value = material.combine;
|
|
|
uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function refreshUniformsLine ( uniforms, material ) {
|
|
|
|
|
|
uniforms.diffuse.value = material.color;
|
|
|
uniforms.opacity.value = material.opacity;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function refreshUniformsDash ( uniforms, material ) {
|
|
|
|
|
@@ -22314,7 +22380,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
uniforms.totalSize.value = material.dashSize + material.gapSize;
|
|
|
uniforms.scale.value = material.scale;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function refreshUniformsParticle ( uniforms, material ) {
|
|
|
|
|
@@ -22325,7 +22391,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
uniforms.map.value = material.map;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function refreshUniformsFog ( uniforms, fog ) {
|
|
|
|
|
@@ -22342,7 +22408,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function refreshUniformsPhong ( uniforms, material ) {
|
|
|
|
|
@@ -22368,7 +22434,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function refreshUniformsLambert ( uniforms, material ) {
|
|
|
|
|
@@ -22390,7 +22456,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function refreshUniformsLights ( uniforms, lights ) {
|
|
|
|
|
@@ -22414,7 +22480,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
|
|
|
uniforms.hemisphereLightDirection.value = lights.hemi.positions;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
// If uniforms are marked as clean, they don't need to be loaded to the GPU.
|
|
|
|
|
@@ -22440,7 +22506,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
uniforms.hemisphereLightGroundColor.needsUpdate = boolean;
|
|
|
uniforms.hemisphereLightDirection.needsUpdate = boolean;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function refreshUniformsShadow ( uniforms, lights ) {
|
|
|
|
|
@@ -22472,7 +22538,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
// Uniforms (load to GPU)
|
|
|
|
|
@@ -22486,7 +22552,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function getTextureUnit() {
|
|
|
|
|
@@ -22502,7 +22568,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
return textureUnit;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function loadUniformsGeneric ( uniforms ) {
|
|
|
|
|
@@ -22838,14 +22904,14 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function setupMatrices ( object, camera ) {
|
|
|
|
|
|
object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
|
|
|
object._normalMatrix.getNormalMatrix( object._modelViewMatrix );
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
//
|
|
|
|
|
@@ -22855,7 +22921,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
array[ offset + 1 ] = color.g * color.g * intensitySq;
|
|
|
array[ offset + 2 ] = color.b * color.b * intensitySq;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function setColorLinear( array, offset, color, intensity ) {
|
|
|
|
|
@@ -22863,7 +22929,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
array[ offset + 1 ] = color.g * intensity;
|
|
|
array[ offset + 2 ] = color.b * intensity;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function setupLights ( lights ) {
|
|
|
|
|
@@ -23089,7 +23155,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
zlights.ambient[ 1 ] = g;
|
|
|
zlights.ambient[ 2 ] = b;
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
// GL state setting
|
|
|
|
|
@@ -23211,7 +23277,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
function setPolygonOffset ( polygonoffset, factor, units ) {
|
|
|
|
|
@@ -23240,7 +23306,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) {
|
|
|
|
|
@@ -23352,7 +23418,7 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
this.uploadTexture = function ( texture ) {
|
|
|
|
|
@@ -24020,13 +24086,25 @@ THREE.WebGLRenderer = function ( parameters ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- // default plugins (order is important)
|
|
|
+ // DEPRECATED
|
|
|
+
|
|
|
+ this.initMaterial = function () {
|
|
|
+
|
|
|
+ console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ this.addPrePlugin = function () {
|
|
|
+
|
|
|
+ console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );
|
|
|
|
|
|
- this.shadowMapPlugin = new THREE.ShadowMapPlugin( this );
|
|
|
- this.addPrePlugin( this.shadowMapPlugin );
|
|
|
+ };
|
|
|
+
|
|
|
+ this.addPostPlugin = function () {
|
|
|
|
|
|
- this.addPostPlugin( new THREE.SpritePlugin( this ) );
|
|
|
- this.addPostPlugin( new THREE.LensFlarePlugin( this ) );
|
|
|
+ console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );
|
|
|
+
|
|
|
+ };
|
|
|
|
|
|
};
|
|
|
|
|
@@ -24632,10384 +24710,10016 @@ THREE.WebGLShader = ( function () {
|
|
|
|
|
|
} )();
|
|
|
|
|
|
-// File:src/extras/GeometryUtils.js
|
|
|
+// File:src/renderers/webgl/plugins/LensFlarePlugin.js
|
|
|
|
|
|
/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author mikael emtinger / http://gomo.se/
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
*/
|
|
|
|
|
|
-THREE.GeometryUtils = {
|
|
|
+THREE.LensFlarePlugin = function ( renderer, flares ) {
|
|
|
|
|
|
- merge: function ( geometry1, geometry2, materialIndexOffset ) {
|
|
|
+ var gl = renderer.context;
|
|
|
|
|
|
- console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
|
|
|
+ var vertexBuffer, elementBuffer;
|
|
|
+ var program, attributes, uniforms;
|
|
|
+ var hasVertexTexture;
|
|
|
|
|
|
- var matrix;
|
|
|
+ var tempTexture, occlusionTexture;
|
|
|
|
|
|
- if ( geometry2 instanceof THREE.Mesh ) {
|
|
|
+ var init = function () {
|
|
|
|
|
|
- geometry2.matrixAutoUpdate && geometry2.updateMatrix();
|
|
|
+ var vertices = new Float32Array( [
|
|
|
+ -1, -1, 0, 0,
|
|
|
+ 1, -1, 1, 0,
|
|
|
+ 1, 1, 1, 1,
|
|
|
+ -1, 1, 0, 1
|
|
|
+ ] );
|
|
|
|
|
|
- matrix = geometry2.matrix;
|
|
|
- geometry2 = geometry2.geometry;
|
|
|
+ var faces = new Uint16Array( [
|
|
|
+ 0, 1, 2,
|
|
|
+ 0, 2, 3
|
|
|
+ ] );
|
|
|
|
|
|
- }
|
|
|
+ // buffers
|
|
|
|
|
|
- geometry1.merge( geometry2, matrix, materialIndexOffset );
|
|
|
+ vertexBuffer = gl.createBuffer();
|
|
|
+ elementBuffer = gl.createBuffer();
|
|
|
|
|
|
- },
|
|
|
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
|
|
|
+ gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
|
|
|
|
|
|
- center: function ( geometry ) {
|
|
|
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
|
|
|
+ gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
|
|
|
|
|
|
- console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
|
|
|
- return geometry.center();
|
|
|
+ // textures
|
|
|
|
|
|
- }
|
|
|
+ tempTexture = gl.createTexture();
|
|
|
+ occlusionTexture = gl.createTexture();
|
|
|
|
|
|
-};
|
|
|
+ gl.bindTexture( gl.TEXTURE_2D, tempTexture );
|
|
|
+ gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );
|
|
|
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
|
|
|
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
|
|
|
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
|
|
|
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
|
|
|
|
|
|
-// File:src/extras/ImageUtils.js
|
|
|
+ gl.bindTexture( gl.TEXTURE_2D, occlusionTexture );
|
|
|
+ gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
|
|
|
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
|
|
|
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
|
|
|
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
|
|
|
+ gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
|
|
|
|
|
|
-/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author Daosheng Mu / https://github.com/DaoshengMu/
|
|
|
- */
|
|
|
+ hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;
|
|
|
|
|
|
-THREE.ImageUtils = {
|
|
|
+ var shader;
|
|
|
|
|
|
- crossOrigin: undefined,
|
|
|
+ if ( hasVertexTexture ) {
|
|
|
|
|
|
- loadTexture: function ( url, mapping, onLoad, onError ) {
|
|
|
+ shader = {
|
|
|
|
|
|
- var loader = new THREE.ImageLoader();
|
|
|
- loader.crossOrigin = this.crossOrigin;
|
|
|
+ vertexShader: [
|
|
|
|
|
|
- var texture = new THREE.Texture( undefined, mapping );
|
|
|
+ "uniform lowp int renderType;",
|
|
|
|
|
|
- loader.load( url, function ( image ) {
|
|
|
+ "uniform vec3 screenPosition;",
|
|
|
+ "uniform vec2 scale;",
|
|
|
+ "uniform float rotation;",
|
|
|
|
|
|
- texture.image = image;
|
|
|
- texture.needsUpdate = true;
|
|
|
+ "uniform sampler2D occlusionMap;",
|
|
|
|
|
|
- if ( onLoad ) onLoad( texture );
|
|
|
+ "attribute vec2 position;",
|
|
|
+ "attribute vec2 uv;",
|
|
|
|
|
|
- }, undefined, function ( event ) {
|
|
|
+ "varying vec2 vUV;",
|
|
|
+ "varying float vVisibility;",
|
|
|
|
|
|
- if ( onError ) onError( event );
|
|
|
+ "void main() {",
|
|
|
|
|
|
- } );
|
|
|
+ "vUV = uv;",
|
|
|
|
|
|
- texture.sourceFile = url;
|
|
|
+ "vec2 pos = position;",
|
|
|
|
|
|
- return texture;
|
|
|
+ "if( renderType == 2 ) {",
|
|
|
|
|
|
- },
|
|
|
+ "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
|
|
|
|
|
|
- loadTextureCube: function ( array, mapping, onLoad, onError ) {
|
|
|
+ "vVisibility = visibility.r / 9.0;",
|
|
|
+ "vVisibility *= 1.0 - visibility.g / 9.0;",
|
|
|
+ "vVisibility *= visibility.b / 9.0;",
|
|
|
+ "vVisibility *= 1.0 - visibility.a / 9.0;",
|
|
|
|
|
|
- var images = [];
|
|
|
+ "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
|
|
|
+ "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
|
|
|
|
|
|
- var loader = new THREE.ImageLoader();
|
|
|
- loader.crossOrigin = this.crossOrigin;
|
|
|
+ "}",
|
|
|
|
|
|
- var texture = new THREE.CubeTexture( images, mapping );
|
|
|
+ "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
|
|
|
|
|
|
- // no flipping needed for cube textures
|
|
|
+ "}"
|
|
|
|
|
|
- texture.flipY = false;
|
|
|
+ ].join( "\n" ),
|
|
|
|
|
|
- var loaded = 0;
|
|
|
+ fragmentShader: [
|
|
|
|
|
|
- var loadTexture = function ( i ) {
|
|
|
+ "uniform lowp int renderType;",
|
|
|
|
|
|
- loader.load( array[ i ], function ( image ) {
|
|
|
+ "uniform sampler2D map;",
|
|
|
+ "uniform float opacity;",
|
|
|
+ "uniform vec3 color;",
|
|
|
|
|
|
- texture.images[ i ] = image;
|
|
|
+ "varying vec2 vUV;",
|
|
|
+ "varying float vVisibility;",
|
|
|
|
|
|
- loaded += 1;
|
|
|
+ "void main() {",
|
|
|
|
|
|
- if ( loaded === 6 ) {
|
|
|
+ // pink square
|
|
|
|
|
|
- texture.needsUpdate = true;
|
|
|
+ "if( renderType == 0 ) {",
|
|
|
|
|
|
- if ( onLoad ) onLoad( texture );
|
|
|
+ "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
|
|
|
|
|
|
- }
|
|
|
+ // restore
|
|
|
|
|
|
- } );
|
|
|
+ "} else if( renderType == 1 ) {",
|
|
|
|
|
|
- }
|
|
|
+ "gl_FragColor = texture2D( map, vUV );",
|
|
|
|
|
|
- for ( var i = 0, il = array.length; i < il; ++ i ) {
|
|
|
+ // flare
|
|
|
|
|
|
- loadTexture( i );
|
|
|
+ "} else {",
|
|
|
|
|
|
- }
|
|
|
+ "vec4 texture = texture2D( map, vUV );",
|
|
|
+ "texture.a *= opacity * vVisibility;",
|
|
|
+ "gl_FragColor = texture;",
|
|
|
+ "gl_FragColor.rgb *= color;",
|
|
|
|
|
|
- return texture;
|
|
|
+ "}",
|
|
|
|
|
|
- },
|
|
|
+ "}"
|
|
|
|
|
|
- loadCompressedTexture: function () {
|
|
|
+ ].join( "\n" )
|
|
|
|
|
|
- console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' )
|
|
|
+ };
|
|
|
|
|
|
- },
|
|
|
+ } else {
|
|
|
|
|
|
- loadCompressedTextureCube: function () {
|
|
|
+ shader = {
|
|
|
|
|
|
- console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' )
|
|
|
+ vertexShader: [
|
|
|
|
|
|
- },
|
|
|
+ "uniform lowp int renderType;",
|
|
|
|
|
|
- getNormalMap: function ( image, depth ) {
|
|
|
+ "uniform vec3 screenPosition;",
|
|
|
+ "uniform vec2 scale;",
|
|
|
+ "uniform float rotation;",
|
|
|
|
|
|
- // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
|
|
|
+ "attribute vec2 position;",
|
|
|
+ "attribute vec2 uv;",
|
|
|
|
|
|
- var cross = function ( a, b ) {
|
|
|
+ "varying vec2 vUV;",
|
|
|
|
|
|
- return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ];
|
|
|
+ "void main() {",
|
|
|
|
|
|
- }
|
|
|
+ "vUV = uv;",
|
|
|
|
|
|
- var subtract = function ( a, b ) {
|
|
|
+ "vec2 pos = position;",
|
|
|
|
|
|
- return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
|
|
|
+ "if( renderType == 2 ) {",
|
|
|
|
|
|
- }
|
|
|
+ "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
|
|
|
+ "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
|
|
|
|
|
|
- var normalize = function ( a ) {
|
|
|
+ "}",
|
|
|
|
|
|
- var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] );
|
|
|
- return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ];
|
|
|
+ "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
|
|
|
|
|
|
- }
|
|
|
+ "}"
|
|
|
|
|
|
- depth = depth | 1;
|
|
|
+ ].join( "\n" ),
|
|
|
|
|
|
- var width = image.width;
|
|
|
- var height = image.height;
|
|
|
+ fragmentShader: [
|
|
|
|
|
|
- var canvas = document.createElement( 'canvas' );
|
|
|
- canvas.width = width;
|
|
|
- canvas.height = height;
|
|
|
+ "precision mediump float;",
|
|
|
|
|
|
- var context = canvas.getContext( '2d' );
|
|
|
- context.drawImage( image, 0, 0 );
|
|
|
+ "uniform lowp int renderType;",
|
|
|
|
|
|
- var data = context.getImageData( 0, 0, width, height ).data;
|
|
|
- var imageData = context.createImageData( width, height );
|
|
|
- var output = imageData.data;
|
|
|
+ "uniform sampler2D map;",
|
|
|
+ "uniform sampler2D occlusionMap;",
|
|
|
+ "uniform float opacity;",
|
|
|
+ "uniform vec3 color;",
|
|
|
|
|
|
- for ( var x = 0; x < width; x ++ ) {
|
|
|
+ "varying vec2 vUV;",
|
|
|
|
|
|
- for ( var y = 0; y < height; y ++ ) {
|
|
|
+ "void main() {",
|
|
|
|
|
|
- var ly = y - 1 < 0 ? 0 : y - 1;
|
|
|
- var uy = y + 1 > height - 1 ? height - 1 : y + 1;
|
|
|
- var lx = x - 1 < 0 ? 0 : x - 1;
|
|
|
- var ux = x + 1 > width - 1 ? width - 1 : x + 1;
|
|
|
+ // pink square
|
|
|
|
|
|
- var points = [];
|
|
|
- var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ];
|
|
|
- points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] );
|
|
|
- points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] );
|
|
|
- points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] );
|
|
|
- points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] );
|
|
|
- points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] );
|
|
|
- points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] );
|
|
|
- points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] );
|
|
|
- points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] );
|
|
|
+ "if( renderType == 0 ) {",
|
|
|
|
|
|
- var normals = [];
|
|
|
- var num_points = points.length;
|
|
|
+ "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
|
|
|
|
|
|
- for ( var i = 0; i < num_points; i ++ ) {
|
|
|
+ // restore
|
|
|
|
|
|
- var v1 = points[ i ];
|
|
|
- var v2 = points[ ( i + 1 ) % num_points ];
|
|
|
- v1 = subtract( v1, origin );
|
|
|
- v2 = subtract( v2, origin );
|
|
|
- normals.push( normalize( cross( v1, v2 ) ) );
|
|
|
+ "} else if( renderType == 1 ) {",
|
|
|
|
|
|
- }
|
|
|
+ "gl_FragColor = texture2D( map, vUV );",
|
|
|
|
|
|
- var normal = [ 0, 0, 0 ];
|
|
|
+ // flare
|
|
|
|
|
|
- for ( var i = 0; i < normals.length; i ++ ) {
|
|
|
+ "} else {",
|
|
|
|
|
|
- normal[ 0 ] += normals[ i ][ 0 ];
|
|
|
- normal[ 1 ] += normals[ i ][ 1 ];
|
|
|
- normal[ 2 ] += normals[ i ][ 2 ];
|
|
|
+ "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;",
|
|
|
+ "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
|
|
|
+ "visibility = ( 1.0 - visibility / 4.0 );",
|
|
|
|
|
|
- }
|
|
|
+ "vec4 texture = texture2D( map, vUV );",
|
|
|
+ "texture.a *= opacity * visibility;",
|
|
|
+ "gl_FragColor = texture;",
|
|
|
+ "gl_FragColor.rgb *= color;",
|
|
|
|
|
|
- normal[ 0 ] /= normals.length;
|
|
|
- normal[ 1 ] /= normals.length;
|
|
|
- normal[ 2 ] /= normals.length;
|
|
|
+ "}",
|
|
|
|
|
|
- var idx = ( y * width + x ) * 4;
|
|
|
+ "}"
|
|
|
|
|
|
- output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0;
|
|
|
- output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0;
|
|
|
- output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0;
|
|
|
- output[ idx + 3 ] = 255;
|
|
|
+ ].join( "\n" )
|
|
|
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
}
|
|
|
|
|
|
- context.putImageData( imageData, 0, 0 );
|
|
|
-
|
|
|
- return canvas;
|
|
|
+ program = createProgram( shader );
|
|
|
|
|
|
- },
|
|
|
+ attributes = {
|
|
|
+ vertex: gl.getAttribLocation ( program, "position" ),
|
|
|
+ uv: gl.getAttribLocation ( program, "uv" )
|
|
|
+ }
|
|
|
|
|
|
- generateDataTexture: function ( width, height, color ) {
|
|
|
+ uniforms = {
|
|
|
+ renderType: gl.getUniformLocation( program, "renderType" ),
|
|
|
+ map: gl.getUniformLocation( program, "map" ),
|
|
|
+ occlusionMap: gl.getUniformLocation( program, "occlusionMap" ),
|
|
|
+ opacity: gl.getUniformLocation( program, "opacity" ),
|
|
|
+ color: gl.getUniformLocation( program, "color" ),
|
|
|
+ scale: gl.getUniformLocation( program, "scale" ),
|
|
|
+ rotation: gl.getUniformLocation( program, "rotation" ),
|
|
|
+ screenPosition: gl.getUniformLocation( program, "screenPosition" )
|
|
|
+ };
|
|
|
|
|
|
- var size = width * height;
|
|
|
- var data = new Uint8Array( 3 * size );
|
|
|
+ };
|
|
|
|
|
|
- var r = Math.floor( color.r * 255 );
|
|
|
- var g = Math.floor( color.g * 255 );
|
|
|
- var b = Math.floor( color.b * 255 );
|
|
|
+ /*
|
|
|
+ * Render lens flares
|
|
|
+ * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
|
|
|
+ * reads these back and calculates occlusion.
|
|
|
+ */
|
|
|
|
|
|
- for ( var i = 0; i < size; i ++ ) {
|
|
|
+ this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
|
|
|
|
|
|
- data[ i * 3 ] = r;
|
|
|
- data[ i * 3 + 1 ] = g;
|
|
|
- data[ i * 3 + 2 ] = b;
|
|
|
+ if ( flares.length === 0 ) return;
|
|
|
|
|
|
- }
|
|
|
+ var tempPosition = new THREE.Vector3();
|
|
|
|
|
|
- var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
|
|
|
- texture.needsUpdate = true;
|
|
|
+ var invAspect = viewportHeight / viewportWidth,
|
|
|
+ halfViewportWidth = viewportWidth * 0.5,
|
|
|
+ halfViewportHeight = viewportHeight * 0.5;
|
|
|
|
|
|
- return texture;
|
|
|
+ var size = 16 / viewportHeight,
|
|
|
+ scale = new THREE.Vector2( size * invAspect, size );
|
|
|
|
|
|
- }
|
|
|
+ var screenPosition = new THREE.Vector3( 1, 1, 0 ),
|
|
|
+ screenPositionPixels = new THREE.Vector2( 1, 1 );
|
|
|
|
|
|
-};
|
|
|
+ if ( program === undefined ) {
|
|
|
|
|
|
-// File:src/extras/SceneUtils.js
|
|
|
+ init();
|
|
|
|
|
|
-/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- */
|
|
|
+ }
|
|
|
|
|
|
-THREE.SceneUtils = {
|
|
|
+ gl.useProgram( program );
|
|
|
|
|
|
- createMultiMaterialObject: function ( geometry, materials ) {
|
|
|
+ gl.enableVertexAttribArray( attributes.vertex );
|
|
|
+ gl.enableVertexAttribArray( attributes.uv );
|
|
|
|
|
|
- var group = new THREE.Object3D();
|
|
|
+ // loop through all lens flares to update their occlusion and positions
|
|
|
+ // setup gl and common used attribs/unforms
|
|
|
|
|
|
- for ( var i = 0, l = materials.length; i < l; i ++ ) {
|
|
|
+ gl.uniform1i( uniforms.occlusionMap, 0 );
|
|
|
+ gl.uniform1i( uniforms.map, 1 );
|
|
|
|
|
|
- group.add( new THREE.Mesh( geometry, materials[ i ] ) );
|
|
|
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
|
|
|
+ gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );
|
|
|
+ gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
|
|
|
|
|
|
- }
|
|
|
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
|
|
|
|
|
|
- return group;
|
|
|
+ gl.disable( gl.CULL_FACE );
|
|
|
+ gl.depthMask( false );
|
|
|
|
|
|
- },
|
|
|
+ for ( var i = 0, l = flares.length; i < l; i ++ ) {
|
|
|
|
|
|
- detach: function ( child, parent, scene ) {
|
|
|
+ size = 16 / viewportHeight;
|
|
|
+ scale.set( size * invAspect, size );
|
|
|
|
|
|
- child.applyMatrix( parent.matrixWorld );
|
|
|
- parent.remove( child );
|
|
|
- scene.add( child );
|
|
|
+ // calc object screen position
|
|
|
|
|
|
- },
|
|
|
+ var flare = flares[ i ];
|
|
|
+
|
|
|
+ tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] );
|
|
|
|
|
|
- attach: function ( child, scene, parent ) {
|
|
|
+ tempPosition.applyMatrix4( camera.matrixWorldInverse );
|
|
|
+ tempPosition.applyProjection( camera.projectionMatrix );
|
|
|
|
|
|
- var matrixWorldInverse = new THREE.Matrix4();
|
|
|
- matrixWorldInverse.getInverse( parent.matrixWorld );
|
|
|
- child.applyMatrix( matrixWorldInverse );
|
|
|
+ // setup arrays for gl programs
|
|
|
|
|
|
- scene.remove( child );
|
|
|
- parent.add( child );
|
|
|
+ screenPosition.copy( tempPosition )
|
|
|
|
|
|
- }
|
|
|
+ screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
|
|
|
+ screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
|
|
|
|
|
|
-};
|
|
|
+ // screen cull
|
|
|
|
|
|
-// File:src/extras/FontUtils.js
|
|
|
+ if ( hasVertexTexture || (
|
|
|
+ screenPositionPixels.x > 0 &&
|
|
|
+ screenPositionPixels.x < viewportWidth &&
|
|
|
+ screenPositionPixels.y > 0 &&
|
|
|
+ screenPositionPixels.y < viewportHeight ) ) {
|
|
|
|
|
|
-/**
|
|
|
- * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- *
|
|
|
- * For Text operations in three.js (See TextGeometry)
|
|
|
- *
|
|
|
- * It uses techniques used in:
|
|
|
- *
|
|
|
- * typeface.js and canvastext
|
|
|
- * For converting fonts and rendering with javascript
|
|
|
- * http://typeface.neocracy.org
|
|
|
- *
|
|
|
- * Triangulation ported from AS3
|
|
|
- * Simple Polygon Triangulation
|
|
|
- * http://actionsnippet.com/?p=1462
|
|
|
- *
|
|
|
- * A Method to triangulate shapes with holes
|
|
|
- * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
|
|
|
- *
|
|
|
- */
|
|
|
+ // save current RGB to temp texture
|
|
|
|
|
|
-THREE.FontUtils = {
|
|
|
+ gl.activeTexture( gl.TEXTURE1 );
|
|
|
+ gl.bindTexture( gl.TEXTURE_2D, tempTexture );
|
|
|
+ gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
|
|
|
|
|
|
- faces: {},
|
|
|
|
|
|
- // Just for now. face[weight][style]
|
|
|
+ // render pink quad
|
|
|
|
|
|
- face: 'helvetiker',
|
|
|
- weight: 'normal',
|
|
|
- style: 'normal',
|
|
|
- size: 150,
|
|
|
- divisions: 10,
|
|
|
+ gl.uniform1i( uniforms.renderType, 0 );
|
|
|
+ gl.uniform2f( uniforms.scale, scale.x, scale.y );
|
|
|
+ gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
|
|
|
|
|
|
- getFace: function () {
|
|
|
+ gl.disable( gl.BLEND );
|
|
|
+ gl.enable( gl.DEPTH_TEST );
|
|
|
|
|
|
- try {
|
|
|
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
|
|
- return this.faces[ this.face ][ this.weight ][ this.style ];
|
|
|
|
|
|
- } catch (e) {
|
|
|
+ // copy result to occlusionMap
|
|
|
|
|
|
- throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing."
|
|
|
+ gl.activeTexture( gl.TEXTURE0 );
|
|
|
+ gl.bindTexture( gl.TEXTURE_2D, occlusionTexture );
|
|
|
+ gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
|
|
|
|
|
|
- };
|
|
|
|
|
|
- },
|
|
|
+ // restore graphics
|
|
|
|
|
|
- loadFace: function ( data ) {
|
|
|
+ gl.uniform1i( uniforms.renderType, 1 );
|
|
|
+ gl.disable( gl.DEPTH_TEST );
|
|
|
|
|
|
- var family = data.familyName.toLowerCase();
|
|
|
+ gl.activeTexture( gl.TEXTURE1 );
|
|
|
+ gl.bindTexture( gl.TEXTURE_2D, tempTexture );
|
|
|
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
|
|
- var ThreeFont = this;
|
|
|
|
|
|
- ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
|
|
|
+ // update object positions
|
|
|
|
|
|
- ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
|
|
|
- ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
|
|
|
+ flare.positionScreen.copy( screenPosition )
|
|
|
|
|
|
- var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
|
|
|
+ if ( flare.customUpdateCallback ) {
|
|
|
|
|
|
- return data;
|
|
|
+ flare.customUpdateCallback( flare );
|
|
|
|
|
|
- },
|
|
|
+ } else {
|
|
|
|
|
|
- drawText: function ( text ) {
|
|
|
+ flare.updateLensFlares();
|
|
|
|
|
|
- var characterPts = [], allPts = [];
|
|
|
+ }
|
|
|
|
|
|
- // RenderText
|
|
|
+ // render flares
|
|
|
|
|
|
- var i, p,
|
|
|
- face = this.getFace(),
|
|
|
- scale = this.size / face.resolution,
|
|
|
- offset = 0,
|
|
|
- chars = String( text ).split( '' ),
|
|
|
- length = chars.length;
|
|
|
+ gl.uniform1i( uniforms.renderType, 2 );
|
|
|
+ gl.enable( gl.BLEND );
|
|
|
|
|
|
- var fontPaths = [];
|
|
|
+ for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
|
|
|
|
|
|
- for ( i = 0; i < length; i ++ ) {
|
|
|
+ var sprite = flare.lensFlares[ j ];
|
|
|
|
|
|
- var path = new THREE.Path();
|
|
|
+ if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
|
|
|
|
|
|
- var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
|
|
|
- offset += ret.offset;
|
|
|
+ screenPosition.x = sprite.x;
|
|
|
+ screenPosition.y = sprite.y;
|
|
|
+ screenPosition.z = sprite.z;
|
|
|
|
|
|
- fontPaths.push( ret.path );
|
|
|
+ size = sprite.size * sprite.scale / viewportHeight;
|
|
|
|
|
|
- }
|
|
|
+ scale.x = size * invAspect;
|
|
|
+ scale.y = size;
|
|
|
|
|
|
- // get the width
|
|
|
+ gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
|
|
|
+ gl.uniform2f( uniforms.scale, scale.x, scale.y );
|
|
|
+ gl.uniform1f( uniforms.rotation, sprite.rotation );
|
|
|
|
|
|
- var width = offset / 2;
|
|
|
- //
|
|
|
- // for ( p = 0; p < allPts.length; p++ ) {
|
|
|
- //
|
|
|
- // allPts[ p ].x -= width;
|
|
|
- //
|
|
|
- // }
|
|
|
+ gl.uniform1f( uniforms.opacity, sprite.opacity );
|
|
|
+ gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
|
|
|
|
|
|
- //var extract = this.extractPoints( allPts, characterPts );
|
|
|
- //extract.contour = allPts;
|
|
|
+ renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
|
|
|
+ renderer.setTexture( sprite.texture, 1 );
|
|
|
|
|
|
- //extract.paths = fontPaths;
|
|
|
- //extract.offset = width;
|
|
|
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
|
|
- return { paths: fontPaths, offset: width };
|
|
|
+ }
|
|
|
|
|
|
- },
|
|
|
+ }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
+ // restore gl
|
|
|
|
|
|
- extractGlyphPoints: function ( c, face, scale, offset, path ) {
|
|
|
+ gl.enable( gl.CULL_FACE );
|
|
|
+ gl.enable( gl.DEPTH_TEST );
|
|
|
+ gl.depthMask( true );
|
|
|
|
|
|
- var pts = [];
|
|
|
+ renderer.resetGLState();
|
|
|
|
|
|
- var i, i2, divisions,
|
|
|
- outline, action, length,
|
|
|
- scaleX, scaleY,
|
|
|
- x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
|
|
|
- laste,
|
|
|
- glyph = face.glyphs[ c ] || face.glyphs[ '?' ];
|
|
|
+ };
|
|
|
|
|
|
- if ( ! glyph ) return;
|
|
|
+ function createProgram ( shader ) {
|
|
|
|
|
|
- if ( glyph.o ) {
|
|
|
+ var program = gl.createProgram();
|
|
|
|
|
|
- outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
|
|
|
- length = outline.length;
|
|
|
+ var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
|
|
|
+ var vertexShader = gl.createShader( gl.VERTEX_SHADER );
|
|
|
|
|
|
- scaleX = scale;
|
|
|
- scaleY = scale;
|
|
|
+ var prefix = "precision " + renderer.getPrecision() + " float;\n";
|
|
|
|
|
|
- for ( i = 0; i < length; ) {
|
|
|
+ gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
|
|
|
+ gl.shaderSource( vertexShader, prefix + shader.vertexShader );
|
|
|
|
|
|
- action = outline[ i ++ ];
|
|
|
+ gl.compileShader( fragmentShader );
|
|
|
+ gl.compileShader( vertexShader );
|
|
|
|
|
|
- //console.log( action );
|
|
|
+ gl.attachShader( program, fragmentShader );
|
|
|
+ gl.attachShader( program, vertexShader );
|
|
|
|
|
|
- switch ( action ) {
|
|
|
+ gl.linkProgram( program );
|
|
|
|
|
|
- case 'm':
|
|
|
+ return program;
|
|
|
|
|
|
- // Move To
|
|
|
+ }
|
|
|
|
|
|
- x = outline[ i ++ ] * scaleX + offset;
|
|
|
- y = outline[ i ++ ] * scaleY;
|
|
|
+};
|
|
|
|
|
|
- path.moveTo( x, y );
|
|
|
- break;
|
|
|
+// File:src/renderers/webgl/plugins/ShadowMapPlugin.js
|
|
|
|
|
|
- case 'l':
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ */
|
|
|
|
|
|
- // Line To
|
|
|
+THREE.ShadowMapPlugin = function ( _renderer, _lights, _webglObjects, _webglObjectsImmediate ) {
|
|
|
|
|
|
- x = outline[ i ++ ] * scaleX + offset;
|
|
|
- y = outline[ i ++ ] * scaleY;
|
|
|
- path.lineTo( x,y );
|
|
|
- break;
|
|
|
+ var _gl = _renderer.context;
|
|
|
|
|
|
- case 'q':
|
|
|
+ var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
|
|
|
|
|
|
- // QuadraticCurveTo
|
|
|
+ _frustum = new THREE.Frustum(),
|
|
|
+ _projScreenMatrix = new THREE.Matrix4(),
|
|
|
|
|
|
- cpx = outline[ i ++ ] * scaleX + offset;
|
|
|
- cpy = outline[ i ++ ] * scaleY;
|
|
|
- cpx1 = outline[ i ++ ] * scaleX + offset;
|
|
|
- cpy1 = outline[ i ++ ] * scaleY;
|
|
|
+ _min = new THREE.Vector3(),
|
|
|
+ _max = new THREE.Vector3(),
|
|
|
|
|
|
- path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
|
|
|
+ _matrixPosition = new THREE.Vector3(),
|
|
|
+
|
|
|
+ _renderList = [];
|
|
|
|
|
|
- laste = pts[ pts.length - 1 ];
|
|
|
+ // init
|
|
|
|
|
|
- if ( laste ) {
|
|
|
+ var depthShader = THREE.ShaderLib[ "depthRGBA" ];
|
|
|
+ var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
|
|
|
|
|
|
- cpx0 = laste.x;
|
|
|
- cpy0 = laste.y;
|
|
|
+ _depthMaterial = new THREE.ShaderMaterial( {
|
|
|
+ uniforms: depthUniforms,
|
|
|
+ vertexShader: depthShader.vertexShader,
|
|
|
+ fragmentShader: depthShader.fragmentShader
|
|
|
+ } );
|
|
|
|
|
|
- for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
|
|
|
+ _depthMaterialMorph = new THREE.ShaderMaterial( {
|
|
|
+ uniforms: depthUniforms,
|
|
|
+ vertexShader: depthShader.vertexShader,
|
|
|
+ fragmentShader: depthShader.fragmentShader,
|
|
|
+ morphTargets: true
|
|
|
+ } );
|
|
|
|
|
|
- var t = i2 / divisions;
|
|
|
- var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
|
|
|
- var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
|
|
|
- }
|
|
|
+ _depthMaterialSkin = new THREE.ShaderMaterial( {
|
|
|
+ uniforms: depthUniforms,
|
|
|
+ vertexShader: depthShader.vertexShader,
|
|
|
+ fragmentShader: depthShader.fragmentShader,
|
|
|
+ skinning: true
|
|
|
+ } );
|
|
|
|
|
|
- }
|
|
|
+ _depthMaterialMorphSkin = new THREE.ShaderMaterial( {
|
|
|
+ uniforms: depthUniforms,
|
|
|
+ vertexShader: depthShader.vertexShader,
|
|
|
+ fragmentShader: depthShader.fragmentShader,
|
|
|
+ morphTargets: true,
|
|
|
+ skinning: true
|
|
|
+ } );
|
|
|
|
|
|
- break;
|
|
|
+ _depthMaterial._shadowPass = true;
|
|
|
+ _depthMaterialMorph._shadowPass = true;
|
|
|
+ _depthMaterialSkin._shadowPass = true;
|
|
|
+ _depthMaterialMorphSkin._shadowPass = true;
|
|
|
|
|
|
- case 'b':
|
|
|
+ this.render = function ( scene, camera ) {
|
|
|
|
|
|
- // Cubic Bezier Curve
|
|
|
+ if ( _renderer.shadowMapEnabled === false || _renderer.shadowMapAutoUpdate === false ) return;
|
|
|
|
|
|
- cpx = outline[ i ++ ] * scaleX + offset;
|
|
|
- cpy = outline[ i ++ ] * scaleY;
|
|
|
- cpx1 = outline[ i ++ ] * scaleX + offset;
|
|
|
- cpy1 = outline[ i ++ ] * scaleY;
|
|
|
- cpx2 = outline[ i ++ ] * scaleX + offset;
|
|
|
- cpy2 = outline[ i ++ ] * scaleY;
|
|
|
+ this.update( scene, camera );
|
|
|
|
|
|
- path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
|
|
|
+ };
|
|
|
|
|
|
- laste = pts[ pts.length - 1 ];
|
|
|
+ this.update = function ( scene, camera ) {
|
|
|
|
|
|
- if ( laste ) {
|
|
|
+ var i, il, j, jl, n,
|
|
|
|
|
|
- cpx0 = laste.x;
|
|
|
- cpy0 = laste.y;
|
|
|
+ shadowMap, shadowMatrix, shadowCamera,
|
|
|
+ program, buffer, material,
|
|
|
+ webglObject, object, light,
|
|
|
|
|
|
- for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
|
|
|
+ lights = [],
|
|
|
+ k = 0,
|
|
|
|
|
|
- var t = i2 / divisions;
|
|
|
- var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
|
|
|
- var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
|
|
|
+ fog = null;
|
|
|
|
|
|
- }
|
|
|
+ // set GL state for depth map
|
|
|
|
|
|
- }
|
|
|
+ _gl.clearColor( 1, 1, 1, 1 );
|
|
|
+ _gl.disable( _gl.BLEND );
|
|
|
|
|
|
- break;
|
|
|
+ _gl.enable( _gl.CULL_FACE );
|
|
|
+ _gl.frontFace( _gl.CCW );
|
|
|
|
|
|
- }
|
|
|
+ if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
|
|
|
|
|
|
- }
|
|
|
- }
|
|
|
+ _gl.cullFace( _gl.FRONT );
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
+ _gl.cullFace( _gl.BACK );
|
|
|
|
|
|
- return { offset: glyph.ha * scale, path:path };
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ _renderer.setDepthTest( true );
|
|
|
|
|
|
+ // preprocess lights
|
|
|
+ // - skip lights that are not casting shadows
|
|
|
+ // - create virtual lights for cascaded shadow maps
|
|
|
|
|
|
-THREE.FontUtils.generateShapes = function ( text, parameters ) {
|
|
|
+ for ( i = 0, il = _lights.length; i < il; i ++ ) {
|
|
|
|
|
|
- // Parameters
|
|
|
+ light = _lights[ i ];
|
|
|
|
|
|
- parameters = parameters || {};
|
|
|
+ if ( ! light.castShadow ) continue;
|
|
|
|
|
|
- var size = parameters.size !== undefined ? parameters.size : 100;
|
|
|
- var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4;
|
|
|
+ if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
|
|
|
|
|
|
- var font = parameters.font !== undefined ? parameters.font : 'helvetiker';
|
|
|
- var weight = parameters.weight !== undefined ? parameters.weight : 'normal';
|
|
|
- var style = parameters.style !== undefined ? parameters.style : 'normal';
|
|
|
+ for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
|
|
|
|
|
|
- THREE.FontUtils.size = size;
|
|
|
- THREE.FontUtils.divisions = curveSegments;
|
|
|
+ var virtualLight;
|
|
|
|
|
|
- THREE.FontUtils.face = font;
|
|
|
- THREE.FontUtils.weight = weight;
|
|
|
- THREE.FontUtils.style = style;
|
|
|
+ if ( ! light.shadowCascadeArray[ n ] ) {
|
|
|
|
|
|
- // Get a Font data json object
|
|
|
+ virtualLight = createVirtualLight( light, n );
|
|
|
+ virtualLight.originalCamera = camera;
|
|
|
|
|
|
- var data = THREE.FontUtils.drawText( text );
|
|
|
+ var gyro = new THREE.Gyroscope();
|
|
|
+ gyro.position.copy( light.shadowCascadeOffset );
|
|
|
|
|
|
- var paths = data.paths;
|
|
|
- var shapes = [];
|
|
|
+ gyro.add( virtualLight );
|
|
|
+ gyro.add( virtualLight.target );
|
|
|
|
|
|
- for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
|
|
|
+ camera.add( gyro );
|
|
|
|
|
|
- Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
|
|
|
+ light.shadowCascadeArray[ n ] = virtualLight;
|
|
|
|
|
|
- }
|
|
|
+ console.log( "Created virtualLight", virtualLight );
|
|
|
|
|
|
- return shapes;
|
|
|
+ } else {
|
|
|
|
|
|
-};
|
|
|
+ virtualLight = light.shadowCascadeArray[ n ];
|
|
|
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- * This code is a quick port of code written in C++ which was submitted to
|
|
|
- * flipcode.com by John W. Ratcliff // July 22, 2000
|
|
|
- * See original code and more information here:
|
|
|
- * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
|
|
|
- *
|
|
|
- * ported to actionscript by Zevan Rosser
|
|
|
- * www.actionsnippet.com
|
|
|
- *
|
|
|
- * ported to javascript by Joshua Koo
|
|
|
- * http://www.lab4games.net/zz85/blog
|
|
|
- *
|
|
|
- */
|
|
|
+ updateVirtualLight( light, n );
|
|
|
|
|
|
+ lights[ k ] = virtualLight;
|
|
|
+ k ++;
|
|
|
|
|
|
-( function ( namespace ) {
|
|
|
+ }
|
|
|
|
|
|
- var EPSILON = 0.0000000001;
|
|
|
+ } else {
|
|
|
|
|
|
- // takes in an contour array and returns
|
|
|
+ lights[ k ] = light;
|
|
|
+ k ++;
|
|
|
|
|
|
- var process = function ( contour, indices ) {
|
|
|
+ }
|
|
|
|
|
|
- var n = contour.length;
|
|
|
+ }
|
|
|
|
|
|
- if ( n < 3 ) return null;
|
|
|
+ // render depth map
|
|
|
|
|
|
- var result = [],
|
|
|
- verts = [],
|
|
|
- vertIndices = [];
|
|
|
+ for ( i = 0, il = lights.length; i < il; i ++ ) {
|
|
|
|
|
|
- /* we want a counter-clockwise polygon in verts */
|
|
|
+ light = lights[ i ];
|
|
|
|
|
|
- var u, v, w;
|
|
|
+ if ( ! light.shadowMap ) {
|
|
|
|
|
|
- if ( area( contour ) > 0.0 ) {
|
|
|
+ var shadowFilter = THREE.LinearFilter;
|
|
|
|
|
|
- for ( v = 0; v < n; v ++ ) verts[ v ] = v;
|
|
|
+ if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) {
|
|
|
|
|
|
- } else {
|
|
|
+ shadowFilter = THREE.NearestFilter;
|
|
|
|
|
|
- for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
|
|
|
|
|
|
- var nv = n;
|
|
|
+ light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
|
|
|
+ light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
|
|
|
|
|
|
- /* remove nv - 2 vertices, creating 1 triangle every time */
|
|
|
+ light.shadowMatrix = new THREE.Matrix4();
|
|
|
|
|
|
- var count = 2 * nv; /* error detection */
|
|
|
+ }
|
|
|
|
|
|
- for ( v = nv - 1; nv > 2; ) {
|
|
|
+ if ( ! light.shadowCamera ) {
|
|
|
|
|
|
- /* if we loop, it is probably a non-simple polygon */
|
|
|
+ if ( light instanceof THREE.SpotLight ) {
|
|
|
|
|
|
- if ( ( count -- ) <= 0 ) {
|
|
|
+ light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
|
|
|
|
|
|
- //** Triangulate: ERROR - probable bad polygon!
|
|
|
+ } else if ( light instanceof THREE.DirectionalLight ) {
|
|
|
|
|
|
- //throw ( "Warning, unable to triangulate polygon!" );
|
|
|
- //return null;
|
|
|
- // Sometimes warning is fine, especially polygons are triangulated in reverse.
|
|
|
- console.log( 'Warning, unable to triangulate polygon!' );
|
|
|
+ light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
|
|
|
|
|
|
- if ( indices ) return vertIndices;
|
|
|
- return result;
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ console.error( "Unsupported light type for shadow" );
|
|
|
+ continue;
|
|
|
|
|
|
- /* three consecutive vertices in current polygon, <u,v,w> */
|
|
|
+ }
|
|
|
|
|
|
- u = v; if ( nv <= u ) u = 0; /* previous */
|
|
|
- v = u + 1; if ( nv <= v ) v = 0; /* new v */
|
|
|
- w = v + 1; if ( nv <= w ) w = 0; /* next */
|
|
|
+ scene.add( light.shadowCamera );
|
|
|
|
|
|
- if ( snip( contour, u, v, w, nv, verts ) ) {
|
|
|
+ if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
|
|
|
|
|
|
- var a, b, c, s, t;
|
|
|
+ }
|
|
|
|
|
|
- /* true names of the vertices */
|
|
|
+ if ( light.shadowCameraVisible && ! light.cameraHelper ) {
|
|
|
|
|
|
- a = verts[ u ];
|
|
|
- b = verts[ v ];
|
|
|
- c = verts[ w ];
|
|
|
+ light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
|
|
|
+ scene.add( light.cameraHelper );
|
|
|
|
|
|
- /* output Triangle */
|
|
|
+ }
|
|
|
|
|
|
- result.push( [ contour[ a ],
|
|
|
- contour[ b ],
|
|
|
- contour[ c ] ] );
|
|
|
+ if ( light.isVirtual && virtualLight.originalCamera == camera ) {
|
|
|
|
|
|
+ updateShadowCamera( camera, light );
|
|
|
|
|
|
- vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
|
|
|
+ }
|
|
|
|
|
|
- /* remove v from the remaining polygon */
|
|
|
+ shadowMap = light.shadowMap;
|
|
|
+ shadowMatrix = light.shadowMatrix;
|
|
|
+ shadowCamera = light.shadowCamera;
|
|
|
|
|
|
- for ( s = v, t = v + 1; t < nv; s++, t++ ) {
|
|
|
+ //
|
|
|
|
|
|
- verts[ s ] = verts[ t ];
|
|
|
+ shadowCamera.position.setFromMatrixPosition( light.matrixWorld );
|
|
|
+ _matrixPosition.setFromMatrixPosition( light.target.matrixWorld );
|
|
|
+ shadowCamera.lookAt( _matrixPosition );
|
|
|
+ shadowCamera.updateMatrixWorld();
|
|
|
|
|
|
- }
|
|
|
+ shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
|
|
|
|
|
|
- nv --;
|
|
|
+ //
|
|
|
|
|
|
- /* reset error detection counter */
|
|
|
+ if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
|
|
|
+ if ( light.shadowCameraVisible ) light.cameraHelper.update();
|
|
|
|
|
|
- count = 2 * nv;
|
|
|
+ // compute shadow matrix
|
|
|
|
|
|
- }
|
|
|
+ shadowMatrix.set(
|
|
|
+ 0.5, 0.0, 0.0, 0.5,
|
|
|
+ 0.0, 0.5, 0.0, 0.5,
|
|
|
+ 0.0, 0.0, 0.5, 0.5,
|
|
|
+ 0.0, 0.0, 0.0, 1.0
|
|
|
+ );
|
|
|
|
|
|
- }
|
|
|
+ shadowMatrix.multiply( shadowCamera.projectionMatrix );
|
|
|
+ shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
|
|
|
|
|
|
- if ( indices ) return vertIndices;
|
|
|
- return result;
|
|
|
+ // update camera matrices and frustum
|
|
|
|
|
|
- };
|
|
|
+ _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
|
|
|
+ _frustum.setFromMatrix( _projScreenMatrix );
|
|
|
|
|
|
- // calculate area of the contour polygon
|
|
|
+ // render shadow map
|
|
|
|
|
|
- var area = function ( contour ) {
|
|
|
+ _renderer.setRenderTarget( shadowMap );
|
|
|
+ _renderer.clear();
|
|
|
|
|
|
- var n = contour.length;
|
|
|
- var a = 0.0;
|
|
|
+ // set object matrices & frustum culling
|
|
|
|
|
|
- for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
|
|
|
+ _renderList.length = 0;
|
|
|
|
|
|
- a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
|
|
|
+ projectObject( scene, scene, shadowCamera );
|
|
|
|
|
|
- }
|
|
|
|
|
|
- return a * 0.5;
|
|
|
+ // render regular objects
|
|
|
|
|
|
- };
|
|
|
+ var objectMaterial, useMorphing, useSkinning;
|
|
|
|
|
|
- var snip = function ( contour, u, v, w, n, verts ) {
|
|
|
+ for ( j = 0, jl = _renderList.length; j < jl; j ++ ) {
|
|
|
|
|
|
- var p;
|
|
|
- var ax, ay, bx, by;
|
|
|
- var cx, cy, px, py;
|
|
|
+ webglObject = _renderList[ j ];
|
|
|
|
|
|
- ax = contour[ verts[ u ] ].x;
|
|
|
- ay = contour[ verts[ u ] ].y;
|
|
|
+ object = webglObject.object;
|
|
|
+ buffer = webglObject.buffer;
|
|
|
|
|
|
- bx = contour[ verts[ v ] ].x;
|
|
|
- by = contour[ verts[ v ] ].y;
|
|
|
+ // culling is overriden globally for all objects
|
|
|
+ // while rendering depth map
|
|
|
|
|
|
- cx = contour[ verts[ w ] ].x;
|
|
|
- cy = contour[ verts[ w ] ].y;
|
|
|
+ // need to deal with MeshFaceMaterial somehow
|
|
|
+ // in that case just use the first of material.materials for now
|
|
|
+ // (proper solution would require to break objects by materials
|
|
|
+ // similarly to regular rendering and then set corresponding
|
|
|
+ // depth materials per each chunk instead of just once per object)
|
|
|
|
|
|
- if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;
|
|
|
+ objectMaterial = getObjectMaterial( object );
|
|
|
|
|
|
- var aX, aY, bX, bY, cX, cY;
|
|
|
- var apx, apy, bpx, bpy, cpx, cpy;
|
|
|
- var cCROSSap, bCROSScp, aCROSSbp;
|
|
|
+ useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
|
|
|
+ useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
|
|
|
|
|
|
- aX = cx - bx; aY = cy - by;
|
|
|
- bX = ax - cx; bY = ay - cy;
|
|
|
- cX = bx - ax; cY = by - ay;
|
|
|
+ if ( object.customDepthMaterial ) {
|
|
|
|
|
|
- for ( p = 0; p < n; p ++ ) {
|
|
|
+ material = object.customDepthMaterial;
|
|
|
|
|
|
- px = contour[ verts[ p ] ].x
|
|
|
- py = contour[ verts[ p ] ].y
|
|
|
+ } else if ( useSkinning ) {
|
|
|
|
|
|
- if ( ( ( px === ax ) && ( py === ay ) ) ||
|
|
|
- ( ( px === bx ) && ( py === by ) ) ||
|
|
|
- ( ( px === cx ) && ( py === cy ) ) ) continue;
|
|
|
+ material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
|
|
|
|
|
|
- apx = px - ax; apy = py - ay;
|
|
|
- bpx = px - bx; bpy = py - by;
|
|
|
- cpx = px - cx; cpy = py - cy;
|
|
|
+ } else if ( useMorphing ) {
|
|
|
|
|
|
- // see if p is inside triangle abc
|
|
|
+ material = _depthMaterialMorph;
|
|
|
|
|
|
- aCROSSbp = aX * bpy - aY * bpx;
|
|
|
- cCROSSap = cX * apy - cY * apx;
|
|
|
- bCROSScp = bX * cpy - bY * cpx;
|
|
|
+ } else {
|
|
|
|
|
|
- if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false;
|
|
|
+ material = _depthMaterial;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- return true;
|
|
|
+ _renderer.setMaterialFaces( objectMaterial );
|
|
|
|
|
|
- };
|
|
|
+ if ( buffer instanceof THREE.BufferGeometry ) {
|
|
|
|
|
|
+ _renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object );
|
|
|
|
|
|
- namespace.Triangulate = process;
|
|
|
- namespace.Triangulate.area = area;
|
|
|
+ } else {
|
|
|
|
|
|
- return namespace;
|
|
|
+ _renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object );
|
|
|
|
|
|
-} )( THREE.FontUtils );
|
|
|
+ }
|
|
|
|
|
|
-// To use the typeface.js face files, hook up the API
|
|
|
-self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
|
|
|
-THREE.typeface_js = self._typeface_js;
|
|
|
+ }
|
|
|
|
|
|
-// File:src/extras/audio/Audio.js
|
|
|
+ // set matrices and render immediate objects
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
+ for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) {
|
|
|
|
|
|
-THREE.Audio = function ( listener ) {
|
|
|
+ webglObject = _webglObjectsImmediate[ j ];
|
|
|
+ object = webglObject.object;
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ if ( object.visible && object.castShadow ) {
|
|
|
|
|
|
- this.type = 'Audio';
|
|
|
+ object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
|
|
|
|
|
|
- this.context = listener.context;
|
|
|
- this.source = this.context.createBufferSource();
|
|
|
+ _renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object );
|
|
|
|
|
|
- this.gain = this.context.createGain();
|
|
|
- this.gain.connect( this.context.destination );
|
|
|
+ }
|
|
|
|
|
|
- this.panner = this.context.createPanner();
|
|
|
- this.panner.connect( this.gain );
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
+ // restore GL state
|
|
|
|
|
|
-THREE.Audio.prototype.load = function ( file ) {
|
|
|
+ var clearColor = _renderer.getClearColor(),
|
|
|
+ clearAlpha = _renderer.getClearAlpha();
|
|
|
|
|
|
- var scope = this;
|
|
|
+ _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
|
|
|
+ _gl.enable( _gl.BLEND );
|
|
|
|
|
|
- var request = new XMLHttpRequest();
|
|
|
- request.open( 'GET', file, true );
|
|
|
- request.responseType = 'arraybuffer';
|
|
|
- request.onload = function ( e ) {
|
|
|
+ if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
|
|
|
|
|
|
- scope.context.decodeAudioData( this.response, function ( buffer ) {
|
|
|
+ _gl.cullFace( _gl.BACK );
|
|
|
|
|
|
- scope.source.buffer = buffer;
|
|
|
- scope.source.connect( scope.panner );
|
|
|
- scope.source.start( 0 );
|
|
|
+ }
|
|
|
|
|
|
- } );
|
|
|
+ _renderer.resetGLState();
|
|
|
|
|
|
};
|
|
|
- request.send();
|
|
|
|
|
|
- return this;
|
|
|
+ function projectObject( scene, object, shadowCamera ){
|
|
|
|
|
|
-};
|
|
|
+ if ( object.visible ) {
|
|
|
|
|
|
-THREE.Audio.prototype.setLoop = function ( value ) {
|
|
|
+ var webglObjects = _webglObjects[ object.id ];
|
|
|
|
|
|
- this.source.loop = value;
|
|
|
+ if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) {
|
|
|
|
|
|
-};
|
|
|
+ for ( var i = 0, l = webglObjects.length; i < l; i ++ ) {
|
|
|
|
|
|
-THREE.Audio.prototype.setRefDistance = function ( value ) {
|
|
|
+ var webglObject = webglObjects[ i ];
|
|
|
|
|
|
- this.panner.refDistance = value;
|
|
|
+ object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
|
|
|
+ _renderList.push( webglObject );
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.Audio.prototype.setRolloffFactor = function ( value ) {
|
|
|
+ }
|
|
|
|
|
|
- this.panner.rolloffFactor = value;
|
|
|
+ for ( var i = 0, l = object.children.length; i < l; i ++ ) {
|
|
|
|
|
|
-};
|
|
|
+ projectObject( scene, object.children[ i ], shadowCamera );
|
|
|
|
|
|
-THREE.Audio.prototype.updateMatrixWorld = ( function () {
|
|
|
+ }
|
|
|
|
|
|
- var position = new THREE.Vector3();
|
|
|
+ }
|
|
|
|
|
|
- return function ( force ) {
|
|
|
+ }
|
|
|
|
|
|
- THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
|
|
|
+ function createVirtualLight( light, cascade ) {
|
|
|
|
|
|
- position.setFromMatrixPosition( this.matrixWorld );
|
|
|
+ var virtualLight = new THREE.DirectionalLight();
|
|
|
|
|
|
- this.panner.setPosition( position.x, position.y, position.z );
|
|
|
+ virtualLight.isVirtual = true;
|
|
|
|
|
|
- };
|
|
|
+ virtualLight.onlyShadow = true;
|
|
|
+ virtualLight.castShadow = true;
|
|
|
|
|
|
-} )();
|
|
|
+ virtualLight.shadowCameraNear = light.shadowCameraNear;
|
|
|
+ virtualLight.shadowCameraFar = light.shadowCameraFar;
|
|
|
|
|
|
-// File:src/extras/audio/AudioListener.js
|
|
|
+ virtualLight.shadowCameraLeft = light.shadowCameraLeft;
|
|
|
+ virtualLight.shadowCameraRight = light.shadowCameraRight;
|
|
|
+ virtualLight.shadowCameraBottom = light.shadowCameraBottom;
|
|
|
+ virtualLight.shadowCameraTop = light.shadowCameraTop;
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
+ virtualLight.shadowCameraVisible = light.shadowCameraVisible;
|
|
|
|
|
|
-THREE.AudioListener = function () {
|
|
|
+ virtualLight.shadowDarkness = light.shadowDarkness;
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
|
|
|
+ virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
|
|
|
+ virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
|
|
|
|
|
|
- this.type = 'AudioListener';
|
|
|
+ virtualLight.pointsWorld = [];
|
|
|
+ virtualLight.pointsFrustum = [];
|
|
|
|
|
|
- this.context = new ( window.AudioContext || window.webkitAudioContext )();
|
|
|
+ var pointsWorld = virtualLight.pointsWorld,
|
|
|
+ pointsFrustum = virtualLight.pointsFrustum;
|
|
|
|
|
|
-};
|
|
|
+ for ( var i = 0; i < 8; i ++ ) {
|
|
|
|
|
|
-THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
+ pointsWorld[ i ] = new THREE.Vector3();
|
|
|
+ pointsFrustum[ i ] = new THREE.Vector3();
|
|
|
|
|
|
-THREE.AudioListener.prototype.updateMatrixWorld = ( function () {
|
|
|
+ }
|
|
|
|
|
|
- var position = new THREE.Vector3();
|
|
|
- var quaternion = new THREE.Quaternion();
|
|
|
- var scale = new THREE.Vector3();
|
|
|
+ var nearZ = light.shadowCascadeNearZ[ cascade ];
|
|
|
+ var farZ = light.shadowCascadeFarZ[ cascade ];
|
|
|
|
|
|
- var orientation = new THREE.Vector3();
|
|
|
- var velocity = new THREE.Vector3();
|
|
|
+ pointsFrustum[ 0 ].set( - 1, - 1, nearZ );
|
|
|
+ pointsFrustum[ 1 ].set( 1, - 1, nearZ );
|
|
|
+ pointsFrustum[ 2 ].set( - 1, 1, nearZ );
|
|
|
+ pointsFrustum[ 3 ].set( 1, 1, nearZ );
|
|
|
|
|
|
- var positionPrev = new THREE.Vector3();
|
|
|
+ pointsFrustum[ 4 ].set( - 1, - 1, farZ );
|
|
|
+ pointsFrustum[ 5 ].set( 1, - 1, farZ );
|
|
|
+ pointsFrustum[ 6 ].set( - 1, 1, farZ );
|
|
|
+ pointsFrustum[ 7 ].set( 1, 1, farZ );
|
|
|
|
|
|
- return function ( force ) {
|
|
|
+ return virtualLight;
|
|
|
|
|
|
- THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
|
|
|
+ }
|
|
|
|
|
|
- var listener = this.context.listener;
|
|
|
+ // Synchronize virtual light with the original light
|
|
|
|
|
|
- this.matrixWorld.decompose( position, quaternion, scale );
|
|
|
+ function updateVirtualLight( light, cascade ) {
|
|
|
|
|
|
- orientation.set( 0, 0, -1 ).applyQuaternion( quaternion );
|
|
|
- velocity.subVectors( position, positionPrev );
|
|
|
+ var virtualLight = light.shadowCascadeArray[ cascade ];
|
|
|
|
|
|
- listener.setPosition( position.x, position.y, position.z );
|
|
|
- listener.setOrientation( orientation.x, orientation.y, orientation.z, this.up.x, this.up.y, this.up.z );
|
|
|
- listener.setVelocity( velocity.x, velocity.y, velocity.z );
|
|
|
+ virtualLight.position.copy( light.position );
|
|
|
+ virtualLight.target.position.copy( light.target.position );
|
|
|
+ virtualLight.lookAt( virtualLight.target );
|
|
|
|
|
|
- positionPrev.copy( position );
|
|
|
+ virtualLight.shadowCameraVisible = light.shadowCameraVisible;
|
|
|
+ virtualLight.shadowDarkness = light.shadowDarkness;
|
|
|
|
|
|
- };
|
|
|
+ virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
|
|
|
|
|
|
-} )();
|
|
|
+ var nearZ = light.shadowCascadeNearZ[ cascade ];
|
|
|
+ var farZ = light.shadowCascadeFarZ[ cascade ];
|
|
|
|
|
|
-// File:src/extras/core/Curve.js
|
|
|
+ var pointsFrustum = virtualLight.pointsFrustum;
|
|
|
|
|
|
-/**
|
|
|
- * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
- * Extensible curve object
|
|
|
- *
|
|
|
- * Some common of Curve methods
|
|
|
- * .getPoint(t), getTangent(t)
|
|
|
- * .getPointAt(u), getTagentAt(u)
|
|
|
- * .getPoints(), .getSpacedPoints()
|
|
|
- * .getLength()
|
|
|
- * .updateArcLengths()
|
|
|
- *
|
|
|
- * This following classes subclasses THREE.Curve:
|
|
|
- *
|
|
|
- * -- 2d classes --
|
|
|
- * THREE.LineCurve
|
|
|
- * THREE.QuadraticBezierCurve
|
|
|
- * THREE.CubicBezierCurve
|
|
|
- * THREE.SplineCurve
|
|
|
- * THREE.ArcCurve
|
|
|
- * THREE.EllipseCurve
|
|
|
- *
|
|
|
- * -- 3d classes --
|
|
|
- * THREE.LineCurve3
|
|
|
- * THREE.QuadraticBezierCurve3
|
|
|
- * THREE.CubicBezierCurve3
|
|
|
- * THREE.SplineCurve3
|
|
|
- * THREE.ClosedSplineCurve3
|
|
|
- *
|
|
|
- * A series of curves can be represented as a THREE.CurvePath
|
|
|
- *
|
|
|
- **/
|
|
|
+ pointsFrustum[ 0 ].z = nearZ;
|
|
|
+ pointsFrustum[ 1 ].z = nearZ;
|
|
|
+ pointsFrustum[ 2 ].z = nearZ;
|
|
|
+ pointsFrustum[ 3 ].z = nearZ;
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Abstract Curve base class
|
|
|
- **************************************************************/
|
|
|
+ pointsFrustum[ 4 ].z = farZ;
|
|
|
+ pointsFrustum[ 5 ].z = farZ;
|
|
|
+ pointsFrustum[ 6 ].z = farZ;
|
|
|
+ pointsFrustum[ 7 ].z = farZ;
|
|
|
|
|
|
-THREE.Curve = function () {
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ // Fit shadow camera's ortho frustum to camera frustum
|
|
|
|
|
|
-// Virtual base class method to overwrite and implement in subclasses
|
|
|
-// - t [0 .. 1]
|
|
|
+ function updateShadowCamera( camera, light ) {
|
|
|
|
|
|
-THREE.Curve.prototype.getPoint = function ( t ) {
|
|
|
+ var shadowCamera = light.shadowCamera,
|
|
|
+ pointsFrustum = light.pointsFrustum,
|
|
|
+ pointsWorld = light.pointsWorld;
|
|
|
|
|
|
- console.log( "Warning, getPoint() not implemented!" );
|
|
|
- return null;
|
|
|
+ _min.set( Infinity, Infinity, Infinity );
|
|
|
+ _max.set( - Infinity, - Infinity, - Infinity );
|
|
|
|
|
|
-};
|
|
|
+ for ( var i = 0; i < 8; i ++ ) {
|
|
|
|
|
|
-// Get point at relative position in curve according to arc length
|
|
|
-// - u [0 .. 1]
|
|
|
+ var p = pointsWorld[ i ];
|
|
|
|
|
|
-THREE.Curve.prototype.getPointAt = function ( u ) {
|
|
|
+ p.copy( pointsFrustum[ i ] );
|
|
|
+ p.unproject( camera );
|
|
|
|
|
|
- var t = this.getUtoTmapping( u );
|
|
|
- return this.getPoint( t );
|
|
|
+ p.applyMatrix4( shadowCamera.matrixWorldInverse );
|
|
|
|
|
|
-};
|
|
|
+ if ( p.x < _min.x ) _min.x = p.x;
|
|
|
+ if ( p.x > _max.x ) _max.x = p.x;
|
|
|
|
|
|
-// Get sequence of points using getPoint( t )
|
|
|
+ if ( p.y < _min.y ) _min.y = p.y;
|
|
|
+ if ( p.y > _max.y ) _max.y = p.y;
|
|
|
|
|
|
-THREE.Curve.prototype.getPoints = function ( divisions ) {
|
|
|
+ if ( p.z < _min.z ) _min.z = p.z;
|
|
|
+ if ( p.z > _max.z ) _max.z = p.z;
|
|
|
|
|
|
- if ( ! divisions ) divisions = 5;
|
|
|
+ }
|
|
|
|
|
|
- var d, pts = [];
|
|
|
+ shadowCamera.left = _min.x;
|
|
|
+ shadowCamera.right = _max.x;
|
|
|
+ shadowCamera.top = _max.y;
|
|
|
+ shadowCamera.bottom = _min.y;
|
|
|
|
|
|
- for ( d = 0; d <= divisions; d ++ ) {
|
|
|
+ // can't really fit near/far
|
|
|
+ //shadowCamera.near = _min.z;
|
|
|
+ //shadowCamera.far = _max.z;
|
|
|
|
|
|
- pts.push( this.getPoint( d / divisions ) );
|
|
|
+ shadowCamera.updateProjectionMatrix();
|
|
|
|
|
|
}
|
|
|
|
|
|
- return pts;
|
|
|
-
|
|
|
-};
|
|
|
+ // For the moment just ignore objects that have multiple materials with different animation methods
|
|
|
+ // Only the first material will be taken into account for deciding which depth material to use for shadow maps
|
|
|
|
|
|
-// Get sequence of points using getPointAt( u )
|
|
|
+ function getObjectMaterial( object ) {
|
|
|
|
|
|
-THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
|
|
|
+ return object.material instanceof THREE.MeshFaceMaterial
|
|
|
+ ? object.material.materials[ 0 ]
|
|
|
+ : object.material;
|
|
|
|
|
|
- if ( ! divisions ) divisions = 5;
|
|
|
+ };
|
|
|
|
|
|
- var d, pts = [];
|
|
|
+};
|
|
|
|
|
|
- for ( d = 0; d <= divisions; d ++ ) {
|
|
|
+// File:src/renderers/webgl/plugins/SpritePlugin.js
|
|
|
|
|
|
- pts.push( this.getPointAt( d / divisions ) );
|
|
|
+/**
|
|
|
+ * @author mikael emtinger / http://gomo.se/
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ */
|
|
|
|
|
|
- }
|
|
|
+THREE.SpritePlugin = function ( renderer, sprites ) {
|
|
|
|
|
|
- return pts;
|
|
|
+ var gl = renderer.context;
|
|
|
|
|
|
-};
|
|
|
+ var vertexBuffer, elementBuffer;
|
|
|
+ var program, attributes, uniforms;
|
|
|
|
|
|
-// Get total curve arc length
|
|
|
+ var texture;
|
|
|
+
|
|
|
+ var init = function () {
|
|
|
|
|
|
-THREE.Curve.prototype.getLength = function () {
|
|
|
+ var vertices = new Float32Array( [
|
|
|
+ - 0.5, - 0.5, 0, 0,
|
|
|
+ 0.5, - 0.5, 1, 0,
|
|
|
+ 0.5, 0.5, 1, 1,
|
|
|
+ - 0.5, 0.5, 0, 1
|
|
|
+ ] );
|
|
|
|
|
|
- var lengths = this.getLengths();
|
|
|
- return lengths[ lengths.length - 1 ];
|
|
|
+ var faces = new Uint16Array( [
|
|
|
+ 0, 1, 2,
|
|
|
+ 0, 2, 3
|
|
|
+ ] );
|
|
|
|
|
|
-};
|
|
|
+ vertexBuffer = gl.createBuffer();
|
|
|
+ elementBuffer = gl.createBuffer();
|
|
|
|
|
|
-// Get list of cumulative segment lengths
|
|
|
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
|
|
|
+ gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
|
|
|
|
|
|
-THREE.Curve.prototype.getLengths = function ( divisions ) {
|
|
|
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
|
|
|
+ gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
|
|
|
|
|
|
- if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
|
|
|
+ program = createProgram();
|
|
|
|
|
|
- if ( this.cacheArcLengths
|
|
|
- && ( this.cacheArcLengths.length == divisions + 1 )
|
|
|
- && ! this.needsUpdate) {
|
|
|
+ attributes = {
|
|
|
+ position: gl.getAttribLocation ( program, 'position' ),
|
|
|
+ uv: gl.getAttribLocation ( program, 'uv' )
|
|
|
+ };
|
|
|
|
|
|
- //console.log( "cached", this.cacheArcLengths );
|
|
|
- return this.cacheArcLengths;
|
|
|
+ uniforms = {
|
|
|
+ uvOffset: gl.getUniformLocation( program, 'uvOffset' ),
|
|
|
+ uvScale: gl.getUniformLocation( program, 'uvScale' ),
|
|
|
|
|
|
- }
|
|
|
+ rotation: gl.getUniformLocation( program, 'rotation' ),
|
|
|
+ scale: gl.getUniformLocation( program, 'scale' ),
|
|
|
|
|
|
- this.needsUpdate = false;
|
|
|
+ color: gl.getUniformLocation( program, 'color' ),
|
|
|
+ map: gl.getUniformLocation( program, 'map' ),
|
|
|
+ opacity: gl.getUniformLocation( program, 'opacity' ),
|
|
|
|
|
|
- var cache = [];
|
|
|
- var current, last = this.getPoint( 0 );
|
|
|
- var p, sum = 0;
|
|
|
+ modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ),
|
|
|
+ projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ),
|
|
|
|
|
|
- cache.push( 0 );
|
|
|
+ fogType: gl.getUniformLocation( program, 'fogType' ),
|
|
|
+ fogDensity: gl.getUniformLocation( program, 'fogDensity' ),
|
|
|
+ fogNear: gl.getUniformLocation( program, 'fogNear' ),
|
|
|
+ fogFar: gl.getUniformLocation( program, 'fogFar' ),
|
|
|
+ fogColor: gl.getUniformLocation( program, 'fogColor' ),
|
|
|
|
|
|
- for ( p = 1; p <= divisions; p ++ ) {
|
|
|
+ alphaTest: gl.getUniformLocation( program, 'alphaTest' )
|
|
|
+ };
|
|
|
|
|
|
- current = this.getPoint ( p / divisions );
|
|
|
- sum += current.distanceTo( last );
|
|
|
- cache.push( sum );
|
|
|
- last = current;
|
|
|
+ var canvas = document.createElement( 'canvas' );
|
|
|
+ canvas.width = 8;
|
|
|
+ canvas.height = 8;
|
|
|
|
|
|
- }
|
|
|
+ var context = canvas.getContext( '2d' );
|
|
|
+ context.fillStyle = 'white';
|
|
|
+ context.fillRect( 0, 0, 8, 8 );
|
|
|
|
|
|
- this.cacheArcLengths = cache;
|
|
|
+ texture = new THREE.Texture( canvas );
|
|
|
+ texture.needsUpdate = true;
|
|
|
|
|
|
- return cache; // { sums: cache, sum:sum }; Sum is in the last element.
|
|
|
+ };
|
|
|
|
|
|
-};
|
|
|
+ this.render = function ( scene, camera ) {
|
|
|
|
|
|
+ if ( sprites.length === 0 ) return;
|
|
|
|
|
|
-THREE.Curve.prototype.updateArcLengths = function() {
|
|
|
- this.needsUpdate = true;
|
|
|
- this.getLengths();
|
|
|
-};
|
|
|
+ // setup gl
|
|
|
|
|
|
-// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
|
|
|
+ if ( program === undefined ) {
|
|
|
|
|
|
-THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
|
|
|
+ init();
|
|
|
|
|
|
- var arcLengths = this.getLengths();
|
|
|
+ }
|
|
|
|
|
|
- var i = 0, il = arcLengths.length;
|
|
|
+ gl.useProgram( program );
|
|
|
|
|
|
- var targetArcLength; // The targeted u distance value to get
|
|
|
+ gl.enableVertexAttribArray( attributes.position );
|
|
|
+ gl.enableVertexAttribArray( attributes.uv );
|
|
|
|
|
|
- if ( distance ) {
|
|
|
+ gl.disable( gl.CULL_FACE );
|
|
|
+ gl.enable( gl.BLEND );
|
|
|
|
|
|
- targetArcLength = distance;
|
|
|
+ gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
|
|
|
+ gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );
|
|
|
+ gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
|
|
|
|
|
|
- } else {
|
|
|
+ gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
|
|
|
|
|
|
- targetArcLength = u * arcLengths[ il - 1 ];
|
|
|
+ gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
|
|
|
|
|
|
- }
|
|
|
+ gl.activeTexture( gl.TEXTURE0 );
|
|
|
+ gl.uniform1i( uniforms.map, 0 );
|
|
|
|
|
|
- //var time = Date.now();
|
|
|
+ var oldFogType = 0;
|
|
|
+ var sceneFogType = 0;
|
|
|
+ var fog = scene.fog;
|
|
|
|
|
|
- // binary search for the index with largest value smaller than target u distance
|
|
|
+ if ( fog ) {
|
|
|
|
|
|
- var low = 0, high = il - 1, comparison;
|
|
|
+ gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
|
|
|
|
|
|
- while ( low <= high ) {
|
|
|
+ if ( fog instanceof THREE.Fog ) {
|
|
|
|
|
|
- i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
|
|
|
+ gl.uniform1f( uniforms.fogNear, fog.near );
|
|
|
+ gl.uniform1f( uniforms.fogFar, fog.far );
|
|
|
|
|
|
- comparison = arcLengths[ i ] - targetArcLength;
|
|
|
+ gl.uniform1i( uniforms.fogType, 1 );
|
|
|
+ oldFogType = 1;
|
|
|
+ sceneFogType = 1;
|
|
|
|
|
|
- if ( comparison < 0 ) {
|
|
|
+ } else if ( fog instanceof THREE.FogExp2 ) {
|
|
|
|
|
|
- low = i + 1;
|
|
|
- continue;
|
|
|
+ gl.uniform1f( uniforms.fogDensity, fog.density );
|
|
|
|
|
|
- } else if ( comparison > 0 ) {
|
|
|
+ gl.uniform1i( uniforms.fogType, 2 );
|
|
|
+ oldFogType = 2;
|
|
|
+ sceneFogType = 2;
|
|
|
|
|
|
- high = i - 1;
|
|
|
- continue;
|
|
|
+ }
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- high = i;
|
|
|
- break;
|
|
|
-
|
|
|
- // DONE
|
|
|
+ gl.uniform1i( uniforms.fogType, 0 );
|
|
|
+ oldFogType = 0;
|
|
|
+ sceneFogType = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
|
|
|
- i = high;
|
|
|
+ // update positions and sort
|
|
|
|
|
|
- //console.log('b' , i, low, high, Date.now()- time);
|
|
|
+ for ( var i = 0, l = sprites.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( arcLengths[ i ] == targetArcLength ) {
|
|
|
+ var sprite = sprites[ i ];
|
|
|
|
|
|
- var t = i / ( il - 1 );
|
|
|
- return t;
|
|
|
+ sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
|
|
|
|
|
|
- }
|
|
|
+ if ( sprite.renderDepth === null ) {
|
|
|
|
|
|
- // we could get finer grain at lengths, or use simple interpolatation between two points
|
|
|
+ sprite.z = - sprite._modelViewMatrix.elements[ 14 ];
|
|
|
|
|
|
- var lengthBefore = arcLengths[ i ];
|
|
|
- var lengthAfter = arcLengths[ i + 1 ];
|
|
|
+ } else {
|
|
|
|
|
|
- var segmentLength = lengthAfter - lengthBefore;
|
|
|
+ sprite.z = sprite.renderDepth;
|
|
|
|
|
|
- // determine where we are between the 'before' and 'after' points
|
|
|
+ }
|
|
|
|
|
|
- var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
|
|
|
+ }
|
|
|
|
|
|
- // add that fractional amount to t
|
|
|
+ sprites.sort( painterSortStable );
|
|
|
|
|
|
- var t = ( i + segmentFraction ) / ( il -1 );
|
|
|
+ // render all sprites
|
|
|
|
|
|
- return t;
|
|
|
+ var scale = [];
|
|
|
|
|
|
-};
|
|
|
+ for ( var i = 0, l = sprites.length; i < l; i ++ ) {
|
|
|
|
|
|
-// Returns a unit vector tangent at t
|
|
|
-// In case any sub curve does not implement its tangent derivation,
|
|
|
-// 2 points a small delta apart will be used to find its gradient
|
|
|
-// which seems to give a reasonable approximation
|
|
|
+ var sprite = sprites[ i ];
|
|
|
+ var material = sprite.material;
|
|
|
|
|
|
-THREE.Curve.prototype.getTangent = function( t ) {
|
|
|
+ gl.uniform1f( uniforms.alphaTest, material.alphaTest );
|
|
|
+ gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
|
|
|
|
|
|
- var delta = 0.0001;
|
|
|
- var t1 = t - delta;
|
|
|
- var t2 = t + delta;
|
|
|
+ scale[ 0 ] = sprite.scale.x;
|
|
|
+ scale[ 1 ] = sprite.scale.y;
|
|
|
|
|
|
- // Capping in case of danger
|
|
|
+ var fogType = 0;
|
|
|
|
|
|
- if ( t1 < 0 ) t1 = 0;
|
|
|
- if ( t2 > 1 ) t2 = 1;
|
|
|
+ if ( scene.fog && material.fog ) {
|
|
|
|
|
|
- var pt1 = this.getPoint( t1 );
|
|
|
- var pt2 = this.getPoint( t2 );
|
|
|
+ fogType = sceneFogType;
|
|
|
|
|
|
- var vec = pt2.clone().sub(pt1);
|
|
|
- return vec.normalize();
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ if ( oldFogType !== fogType ) {
|
|
|
|
|
|
+ gl.uniform1i( uniforms.fogType, fogType );
|
|
|
+ oldFogType = fogType;
|
|
|
|
|
|
-THREE.Curve.prototype.getTangentAt = function ( u ) {
|
|
|
+ }
|
|
|
|
|
|
- var t = this.getUtoTmapping( u );
|
|
|
- return this.getTangent( t );
|
|
|
+ if ( material.map !== null ) {
|
|
|
|
|
|
-};
|
|
|
+ gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );
|
|
|
+ gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
+ gl.uniform2f( uniforms.uvOffset, 0, 0 );
|
|
|
+ gl.uniform2f( uniforms.uvScale, 1, 1 );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
+ gl.uniform1f( uniforms.opacity, material.opacity );
|
|
|
+ gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Utils
|
|
|
- **************************************************************/
|
|
|
+ gl.uniform1f( uniforms.rotation, material.rotation );
|
|
|
+ gl.uniform2fv( uniforms.scale, scale );
|
|
|
|
|
|
-THREE.Curve.Utils = {
|
|
|
+ renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
|
|
|
+ renderer.setDepthTest( material.depthTest );
|
|
|
+ renderer.setDepthWrite( material.depthWrite );
|
|
|
|
|
|
- tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
|
|
|
+ if ( material.map && material.map.image && material.map.image.width ) {
|
|
|
|
|
|
- return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
|
|
|
+ renderer.setTexture( material.map, 0 );
|
|
|
|
|
|
- },
|
|
|
+ } else {
|
|
|
|
|
|
- // Puay Bing, thanks for helping with this derivative!
|
|
|
+ renderer.setTexture( texture, 0 );
|
|
|
|
|
|
- tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
|
|
|
+ }
|
|
|
|
|
|
- return - 3 * p0 * (1 - t) * (1 - t) +
|
|
|
- 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
|
|
|
- 6 * t * p2 * (1-t) - 3 * t * t * p2 +
|
|
|
- 3 * t * t * p3;
|
|
|
+ gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
|
|
|
- },
|
|
|
+ }
|
|
|
|
|
|
- tangentSpline: function ( t, p0, p1, p2, p3 ) {
|
|
|
+ // restore gl
|
|
|
|
|
|
- // To check if my formulas are correct
|
|
|
+ gl.enable( gl.CULL_FACE );
|
|
|
+
|
|
|
+ renderer.resetGLState();
|
|
|
|
|
|
- var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
|
|
|
- var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
|
|
|
- var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2
|
|
|
- var h11 = 3 * t * t - 2 * t; // t3 − t2
|
|
|
+ };
|
|
|
|
|
|
- return h00 + h10 + h01 + h11;
|
|
|
+ function createProgram () {
|
|
|
|
|
|
- },
|
|
|
+ var program = gl.createProgram();
|
|
|
|
|
|
- // Catmull-Rom
|
|
|
+ var vertexShader = gl.createShader( gl.VERTEX_SHADER );
|
|
|
+ var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
|
|
|
|
|
|
- interpolate: function( p0, p1, p2, p3, t ) {
|
|
|
+ gl.shaderSource( vertexShader, [
|
|
|
|
|
|
- var v0 = ( p2 - p0 ) * 0.5;
|
|
|
- var v1 = ( p3 - p1 ) * 0.5;
|
|
|
- var t2 = t * t;
|
|
|
- var t3 = t * t2;
|
|
|
- return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
|
|
|
+ 'precision ' + renderer.getPrecision() + ' float;',
|
|
|
|
|
|
- }
|
|
|
+ 'uniform mat4 modelViewMatrix;',
|
|
|
+ 'uniform mat4 projectionMatrix;',
|
|
|
+ 'uniform float rotation;',
|
|
|
+ 'uniform vec2 scale;',
|
|
|
+ 'uniform vec2 uvOffset;',
|
|
|
+ 'uniform vec2 uvScale;',
|
|
|
|
|
|
-};
|
|
|
+ 'attribute vec2 position;',
|
|
|
+ 'attribute vec2 uv;',
|
|
|
|
|
|
+ 'varying vec2 vUV;',
|
|
|
|
|
|
-// TODO: Transformation for Curves?
|
|
|
+ 'void main() {',
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * 3D Curves
|
|
|
- **************************************************************/
|
|
|
+ 'vUV = uvOffset + uv * uvScale;',
|
|
|
|
|
|
-// A Factory method for creating new curve subclasses
|
|
|
+ 'vec2 alignedPosition = position * scale;',
|
|
|
|
|
|
-THREE.Curve.create = function ( constructor, getPointFunc ) {
|
|
|
+ 'vec2 rotatedPosition;',
|
|
|
+ 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
|
|
|
+ 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
|
|
|
|
|
|
- constructor.prototype = Object.create( THREE.Curve.prototype );
|
|
|
- constructor.prototype.getPoint = getPointFunc;
|
|
|
+ 'vec4 finalPosition;',
|
|
|
|
|
|
- return constructor;
|
|
|
+ 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
|
|
|
+ 'finalPosition.xy += rotatedPosition;',
|
|
|
+ 'finalPosition = projectionMatrix * finalPosition;',
|
|
|
|
|
|
-};
|
|
|
+ 'gl_Position = finalPosition;',
|
|
|
|
|
|
-// File:src/extras/core/CurvePath.js
|
|
|
+ '}'
|
|
|
|
|
|
-/**
|
|
|
- * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
- *
|
|
|
- **/
|
|
|
+ ].join( '\n' ) );
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Curved Path - a curve path is simply a array of connected
|
|
|
- * curves, but retains the api of a curve
|
|
|
- **************************************************************/
|
|
|
+ gl.shaderSource( fragmentShader, [
|
|
|
|
|
|
-THREE.CurvePath = function () {
|
|
|
+ 'precision ' + renderer.getPrecision() + ' float;',
|
|
|
|
|
|
- this.curves = [];
|
|
|
- this.bends = [];
|
|
|
-
|
|
|
- this.autoClose = false; // Automatically closes the path
|
|
|
-};
|
|
|
+ 'uniform vec3 color;',
|
|
|
+ 'uniform sampler2D map;',
|
|
|
+ 'uniform float opacity;',
|
|
|
|
|
|
-THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
|
|
|
+ 'uniform int fogType;',
|
|
|
+ 'uniform vec3 fogColor;',
|
|
|
+ 'uniform float fogDensity;',
|
|
|
+ 'uniform float fogNear;',
|
|
|
+ 'uniform float fogFar;',
|
|
|
+ 'uniform float alphaTest;',
|
|
|
|
|
|
-THREE.CurvePath.prototype.add = function ( curve ) {
|
|
|
+ 'varying vec2 vUV;',
|
|
|
|
|
|
- this.curves.push( curve );
|
|
|
+ 'void main() {',
|
|
|
|
|
|
-};
|
|
|
+ 'vec4 texture = texture2D( map, vUV );',
|
|
|
|
|
|
-THREE.CurvePath.prototype.checkConnection = function() {
|
|
|
- // TODO
|
|
|
- // If the ending of curve is not connected to the starting
|
|
|
- // or the next curve, then, this is not a real path
|
|
|
-};
|
|
|
+ 'if ( texture.a < alphaTest ) discard;',
|
|
|
|
|
|
-THREE.CurvePath.prototype.closePath = function() {
|
|
|
- // TODO Test
|
|
|
- // and verify for vector3 (needs to implement equals)
|
|
|
- // Add a line curve if start and end of lines are not connected
|
|
|
- var startPoint = this.curves[0].getPoint(0);
|
|
|
- var endPoint = this.curves[this.curves.length-1].getPoint(1);
|
|
|
-
|
|
|
- if (! startPoint.equals(endPoint)) {
|
|
|
- this.curves.push( new THREE.LineCurve(endPoint, startPoint) );
|
|
|
- }
|
|
|
-
|
|
|
-};
|
|
|
+ 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
|
|
|
|
|
|
-// To get accurate point with reference to
|
|
|
-// entire path distance at time t,
|
|
|
-// following has to be done:
|
|
|
+ 'if ( fogType > 0 ) {',
|
|
|
|
|
|
-// 1. Length of each sub path have to be known
|
|
|
-// 2. Locate and identify type of curve
|
|
|
-// 3. Get t for the curve
|
|
|
-// 4. Return curve.getPointAt(t')
|
|
|
+ 'float depth = gl_FragCoord.z / gl_FragCoord.w;',
|
|
|
+ 'float fogFactor = 0.0;',
|
|
|
|
|
|
-THREE.CurvePath.prototype.getPoint = function( t ) {
|
|
|
+ 'if ( fogType == 1 ) {',
|
|
|
|
|
|
- var d = t * this.getLength();
|
|
|
- var curveLengths = this.getCurveLengths();
|
|
|
- var i = 0, diff, curve;
|
|
|
+ 'fogFactor = smoothstep( fogNear, fogFar, depth );',
|
|
|
|
|
|
- // To think about boundaries points.
|
|
|
+ '} else {',
|
|
|
|
|
|
- while ( i < curveLengths.length ) {
|
|
|
+ 'const float LOG2 = 1.442695;',
|
|
|
+ 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
|
|
|
+ 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
|
|
|
|
|
|
- if ( curveLengths[ i ] >= d ) {
|
|
|
+ '}',
|
|
|
|
|
|
- diff = curveLengths[ i ] - d;
|
|
|
- curve = this.curves[ i ];
|
|
|
+ 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
|
|
|
|
|
|
- var u = 1 - diff / curve.getLength();
|
|
|
+ '}',
|
|
|
|
|
|
- return curve.getPointAt( u );
|
|
|
+ '}'
|
|
|
|
|
|
- break;
|
|
|
- }
|
|
|
+ ].join( '\n' ) );
|
|
|
|
|
|
- i ++;
|
|
|
+ gl.compileShader( vertexShader );
|
|
|
+ gl.compileShader( fragmentShader );
|
|
|
|
|
|
- }
|
|
|
+ gl.attachShader( program, vertexShader );
|
|
|
+ gl.attachShader( program, fragmentShader );
|
|
|
|
|
|
- return null;
|
|
|
+ gl.linkProgram( program );
|
|
|
|
|
|
- // loop where sum != 0, sum > d , sum+1 <d
|
|
|
+ return program;
|
|
|
|
|
|
-};
|
|
|
+ };
|
|
|
|
|
|
-/*
|
|
|
-THREE.CurvePath.prototype.getTangent = function( t ) {
|
|
|
-};*/
|
|
|
+ function painterSortStable ( a, b ) {
|
|
|
|
|
|
+ if ( a.z !== b.z ) {
|
|
|
|
|
|
-// We cannot use the default THREE.Curve getPoint() with getLength() because in
|
|
|
-// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
|
|
|
-// getPoint() depends on getLength
|
|
|
+ return b.z - a.z;
|
|
|
|
|
|
-THREE.CurvePath.prototype.getLength = function() {
|
|
|
+ } else {
|
|
|
|
|
|
- var lens = this.getCurveLengths();
|
|
|
- return lens[ lens.length - 1 ];
|
|
|
+ return b.id - a.id;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
|
|
|
};
|
|
|
|
|
|
-// Compute lengths and cache them
|
|
|
-// We cannot overwrite getLengths() because UtoT mapping uses it.
|
|
|
+// File:src/extras/GeometryUtils.js
|
|
|
|
|
|
-THREE.CurvePath.prototype.getCurveLengths = function() {
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- // We use cache values if curves and cache array are same length
|
|
|
+THREE.GeometryUtils = {
|
|
|
|
|
|
- if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) {
|
|
|
+ merge: function ( geometry1, geometry2, materialIndexOffset ) {
|
|
|
|
|
|
- return this.cacheLengths;
|
|
|
+ console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
|
|
|
|
|
|
- };
|
|
|
+ var matrix;
|
|
|
|
|
|
- // Get length of subsurve
|
|
|
- // Push sums into cached array
|
|
|
+ if ( geometry2 instanceof THREE.Mesh ) {
|
|
|
|
|
|
- var lengths = [], sums = 0;
|
|
|
- var i, il = this.curves.length;
|
|
|
+ geometry2.matrixAutoUpdate && geometry2.updateMatrix();
|
|
|
|
|
|
- for ( i = 0; i < il; i ++ ) {
|
|
|
+ matrix = geometry2.matrix;
|
|
|
+ geometry2 = geometry2.geometry;
|
|
|
|
|
|
- sums += this.curves[ i ].getLength();
|
|
|
- lengths.push( sums );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ geometry1.merge( geometry2, matrix, materialIndexOffset );
|
|
|
|
|
|
- this.cacheLengths = lengths;
|
|
|
+ },
|
|
|
|
|
|
- return lengths;
|
|
|
+ center: function ( geometry ) {
|
|
|
|
|
|
-};
|
|
|
+ console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
|
|
|
+ return geometry.center();
|
|
|
|
|
|
+ }
|
|
|
|
|
|
+};
|
|
|
|
|
|
-// Returns min and max coordinates
|
|
|
+// File:src/extras/ImageUtils.js
|
|
|
|
|
|
-THREE.CurvePath.prototype.getBoundingBox = function () {
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author Daosheng Mu / https://github.com/DaoshengMu/
|
|
|
+ */
|
|
|
|
|
|
- var points = this.getPoints();
|
|
|
+THREE.ImageUtils = {
|
|
|
|
|
|
- var maxX, maxY, maxZ;
|
|
|
- var minX, minY, minZ;
|
|
|
+ crossOrigin: undefined,
|
|
|
|
|
|
- maxX = maxY = Number.NEGATIVE_INFINITY;
|
|
|
- minX = minY = Number.POSITIVE_INFINITY;
|
|
|
+ loadTexture: function ( url, mapping, onLoad, onError ) {
|
|
|
|
|
|
- var p, i, il, sum;
|
|
|
+ var loader = new THREE.ImageLoader();
|
|
|
+ loader.crossOrigin = this.crossOrigin;
|
|
|
|
|
|
- var v3 = points[0] instanceof THREE.Vector3;
|
|
|
+ var texture = new THREE.Texture( undefined, mapping );
|
|
|
|
|
|
- sum = v3 ? new THREE.Vector3() : new THREE.Vector2();
|
|
|
+ loader.load( url, function ( image ) {
|
|
|
|
|
|
- for ( i = 0, il = points.length; i < il; i ++ ) {
|
|
|
+ texture.image = image;
|
|
|
+ texture.needsUpdate = true;
|
|
|
|
|
|
- p = points[ i ];
|
|
|
+ if ( onLoad ) onLoad( texture );
|
|
|
|
|
|
- if ( p.x > maxX ) maxX = p.x;
|
|
|
- else if ( p.x < minX ) minX = p.x;
|
|
|
+ }, undefined, function ( event ) {
|
|
|
|
|
|
- if ( p.y > maxY ) maxY = p.y;
|
|
|
- else if ( p.y < minY ) minY = p.y;
|
|
|
+ if ( onError ) onError( event );
|
|
|
|
|
|
- if ( v3 ) {
|
|
|
+ } );
|
|
|
|
|
|
- if ( p.z > maxZ ) maxZ = p.z;
|
|
|
- else if ( p.z < minZ ) minZ = p.z;
|
|
|
+ texture.sourceFile = url;
|
|
|
|
|
|
- }
|
|
|
+ return texture;
|
|
|
|
|
|
- sum.add( p );
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
+ loadTextureCube: function ( array, mapping, onLoad, onError ) {
|
|
|
|
|
|
- var ret = {
|
|
|
+ var images = [];
|
|
|
|
|
|
- minX: minX,
|
|
|
- minY: minY,
|
|
|
- maxX: maxX,
|
|
|
- maxY: maxY
|
|
|
+ var loader = new THREE.ImageLoader();
|
|
|
+ loader.crossOrigin = this.crossOrigin;
|
|
|
|
|
|
- };
|
|
|
+ var texture = new THREE.CubeTexture( images, mapping );
|
|
|
|
|
|
- if ( v3 ) {
|
|
|
+ // no flipping needed for cube textures
|
|
|
|
|
|
- ret.maxZ = maxZ;
|
|
|
- ret.minZ = minZ;
|
|
|
+ texture.flipY = false;
|
|
|
|
|
|
- }
|
|
|
+ var loaded = 0;
|
|
|
|
|
|
- return ret;
|
|
|
+ var loadTexture = function ( i ) {
|
|
|
|
|
|
-};
|
|
|
+ loader.load( array[ i ], function ( image ) {
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Create Geometries Helpers
|
|
|
- **************************************************************/
|
|
|
+ texture.images[ i ] = image;
|
|
|
|
|
|
-/// Generate geometry from path points (for Line or Points objects)
|
|
|
+ loaded += 1;
|
|
|
|
|
|
-THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {
|
|
|
+ if ( loaded === 6 ) {
|
|
|
|
|
|
- var pts = this.getPoints( divisions, true );
|
|
|
- return this.createGeometry( pts );
|
|
|
+ texture.needsUpdate = true;
|
|
|
|
|
|
-};
|
|
|
+ if ( onLoad ) onLoad( texture );
|
|
|
|
|
|
-// Generate geometry from equidistance sampling along the path
|
|
|
+ }
|
|
|
|
|
|
-THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {
|
|
|
+ } );
|
|
|
|
|
|
- var pts = this.getSpacedPoints( divisions, true );
|
|
|
- return this.createGeometry( pts );
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ for ( var i = 0, il = array.length; i < il; ++ i ) {
|
|
|
|
|
|
-THREE.CurvePath.prototype.createGeometry = function( points ) {
|
|
|
+ loadTexture( i );
|
|
|
|
|
|
- var geometry = new THREE.Geometry();
|
|
|
+ }
|
|
|
|
|
|
- for ( var i = 0; i < points.length; i ++ ) {
|
|
|
+ return texture;
|
|
|
|
|
|
- geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) );
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
+ loadCompressedTexture: function () {
|
|
|
|
|
|
- return geometry;
|
|
|
+ console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' )
|
|
|
|
|
|
-};
|
|
|
+ },
|
|
|
|
|
|
+ loadCompressedTextureCube: function () {
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Bend / Wrap Helper Methods
|
|
|
- **************************************************************/
|
|
|
+ console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' )
|
|
|
|
|
|
-// Wrap path / Bend modifiers?
|
|
|
+ },
|
|
|
|
|
|
-THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) {
|
|
|
+ getNormalMap: function ( image, depth ) {
|
|
|
|
|
|
- this.bends.push( bendpath );
|
|
|
+ // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
|
|
|
|
|
|
-};
|
|
|
+ var cross = function ( a, b ) {
|
|
|
|
|
|
-THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) {
|
|
|
+ return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ];
|
|
|
|
|
|
- var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
|
|
|
- var i, il;
|
|
|
+ }
|
|
|
|
|
|
- if ( ! bends ) {
|
|
|
+ var subtract = function ( a, b ) {
|
|
|
|
|
|
- bends = this.bends;
|
|
|
+ return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- for ( i = 0, il = bends.length; i < il; i ++ ) {
|
|
|
+ var normalize = function ( a ) {
|
|
|
|
|
|
- oldPts = this.getWrapPoints( oldPts, bends[ i ] );
|
|
|
+ var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] );
|
|
|
+ return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ];
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- return oldPts;
|
|
|
+ depth = depth | 1;
|
|
|
|
|
|
-};
|
|
|
+ var width = image.width;
|
|
|
+ var height = image.height;
|
|
|
|
|
|
-THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) {
|
|
|
+ var canvas = document.createElement( 'canvas' );
|
|
|
+ canvas.width = width;
|
|
|
+ canvas.height = height;
|
|
|
|
|
|
- var oldPts = this.getSpacedPoints( segments );
|
|
|
+ var context = canvas.getContext( '2d' );
|
|
|
+ context.drawImage( image, 0, 0 );
|
|
|
|
|
|
- var i, il;
|
|
|
+ var data = context.getImageData( 0, 0, width, height ).data;
|
|
|
+ var imageData = context.createImageData( width, height );
|
|
|
+ var output = imageData.data;
|
|
|
|
|
|
- if ( ! bends ) {
|
|
|
+ for ( var x = 0; x < width; x ++ ) {
|
|
|
|
|
|
- bends = this.bends;
|
|
|
+ for ( var y = 0; y < height; y ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ var ly = y - 1 < 0 ? 0 : y - 1;
|
|
|
+ var uy = y + 1 > height - 1 ? height - 1 : y + 1;
|
|
|
+ var lx = x - 1 < 0 ? 0 : x - 1;
|
|
|
+ var ux = x + 1 > width - 1 ? width - 1 : x + 1;
|
|
|
|
|
|
- for ( i = 0, il = bends.length; i < il; i ++ ) {
|
|
|
+ var points = [];
|
|
|
+ var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ];
|
|
|
+ points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] );
|
|
|
+ points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] );
|
|
|
+ points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] );
|
|
|
+ points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] );
|
|
|
+ points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] );
|
|
|
+ points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] );
|
|
|
+ points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] );
|
|
|
+ points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] );
|
|
|
|
|
|
- oldPts = this.getWrapPoints( oldPts, bends[ i ] );
|
|
|
+ var normals = [];
|
|
|
+ var num_points = points.length;
|
|
|
|
|
|
- }
|
|
|
+ for ( var i = 0; i < num_points; i ++ ) {
|
|
|
|
|
|
- return oldPts;
|
|
|
+ var v1 = points[ i ];
|
|
|
+ var v2 = points[ ( i + 1 ) % num_points ];
|
|
|
+ v1 = subtract( v1, origin );
|
|
|
+ v2 = subtract( v2, origin );
|
|
|
+ normals.push( normalize( cross( v1, v2 ) ) );
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-// This returns getPoints() bend/wrapped around the contour of a path.
|
|
|
-// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html
|
|
|
+ var normal = [ 0, 0, 0 ];
|
|
|
|
|
|
-THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) {
|
|
|
+ for ( var i = 0; i < normals.length; i ++ ) {
|
|
|
|
|
|
- var bounds = this.getBoundingBox();
|
|
|
+ normal[ 0 ] += normals[ i ][ 0 ];
|
|
|
+ normal[ 1 ] += normals[ i ][ 1 ];
|
|
|
+ normal[ 2 ] += normals[ i ][ 2 ];
|
|
|
|
|
|
- var i, il, p, oldX, oldY, xNorm;
|
|
|
+ }
|
|
|
|
|
|
- for ( i = 0, il = oldPts.length; i < il; i ++ ) {
|
|
|
+ normal[ 0 ] /= normals.length;
|
|
|
+ normal[ 1 ] /= normals.length;
|
|
|
+ normal[ 2 ] /= normals.length;
|
|
|
|
|
|
- p = oldPts[ i ];
|
|
|
+ var idx = ( y * width + x ) * 4;
|
|
|
|
|
|
- oldX = p.x;
|
|
|
- oldY = p.y;
|
|
|
+ output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0;
|
|
|
+ output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0;
|
|
|
+ output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0;
|
|
|
+ output[ idx + 3 ] = 255;
|
|
|
|
|
|
- xNorm = oldX / bounds.maxX;
|
|
|
+ }
|
|
|
|
|
|
- // If using actual distance, for length > path, requires line extrusions
|
|
|
- //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance
|
|
|
+ }
|
|
|
|
|
|
- xNorm = path.getUtoTmapping( xNorm, oldX );
|
|
|
+ context.putImageData( imageData, 0, 0 );
|
|
|
|
|
|
- // check for out of bounds?
|
|
|
+ return canvas;
|
|
|
|
|
|
- var pathPt = path.getPoint( xNorm );
|
|
|
- var normal = path.getTangent( xNorm );
|
|
|
- normal.set( - normal.y, normal.x ).multiplyScalar( oldY );
|
|
|
+ },
|
|
|
|
|
|
- p.x = pathPt.x + normal.x;
|
|
|
- p.y = pathPt.y + normal.y;
|
|
|
+ generateDataTexture: function ( width, height, color ) {
|
|
|
|
|
|
- }
|
|
|
+ var size = width * height;
|
|
|
+ var data = new Uint8Array( 3 * size );
|
|
|
|
|
|
- return oldPts;
|
|
|
+ var r = Math.floor( color.r * 255 );
|
|
|
+ var g = Math.floor( color.g * 255 );
|
|
|
+ var b = Math.floor( color.b * 255 );
|
|
|
|
|
|
-};
|
|
|
+ for ( var i = 0; i < size; i ++ ) {
|
|
|
|
|
|
+ data[ i * 3 ] = r;
|
|
|
+ data[ i * 3 + 1 ] = g;
|
|
|
+ data[ i * 3 + 2 ] = b;
|
|
|
|
|
|
-// File:src/extras/core/Gyroscope.js
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- */
|
|
|
+ var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
|
|
|
+ texture.needsUpdate = true;
|
|
|
|
|
|
-THREE.Gyroscope = function () {
|
|
|
+ return texture;
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ }
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
-
|
|
|
-THREE.Gyroscope.prototype.updateMatrixWorld = ( function () {
|
|
|
-
|
|
|
- var translationObject = new THREE.Vector3();
|
|
|
- var quaternionObject = new THREE.Quaternion();
|
|
|
- var scaleObject = new THREE.Vector3();
|
|
|
-
|
|
|
- var translationWorld = new THREE.Vector3();
|
|
|
- var quaternionWorld = new THREE.Quaternion();
|
|
|
- var scaleWorld = new THREE.Vector3();
|
|
|
-
|
|
|
- return function ( force ) {
|
|
|
-
|
|
|
- this.matrixAutoUpdate && this.updateMatrix();
|
|
|
+// File:src/extras/SceneUtils.js
|
|
|
|
|
|
- // update matrixWorld
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ */
|
|
|
|
|
|
- if ( this.matrixWorldNeedsUpdate || force ) {
|
|
|
+THREE.SceneUtils = {
|
|
|
|
|
|
- if ( this.parent ) {
|
|
|
+ createMultiMaterialObject: function ( geometry, materials ) {
|
|
|
|
|
|
- this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
|
|
|
+ var group = new THREE.Object3D();
|
|
|
|
|
|
- this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld );
|
|
|
- this.matrix.decompose( translationObject, quaternionObject, scaleObject );
|
|
|
+ for ( var i = 0, l = materials.length; i < l; i ++ ) {
|
|
|
|
|
|
- this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld );
|
|
|
+ group.add( new THREE.Mesh( geometry, materials[ i ] ) );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- } else {
|
|
|
+ return group;
|
|
|
|
|
|
- this.matrixWorld.copy( this.matrix );
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
+ detach: function ( child, parent, scene ) {
|
|
|
|
|
|
+ child.applyMatrix( parent.matrixWorld );
|
|
|
+ parent.remove( child );
|
|
|
+ scene.add( child );
|
|
|
|
|
|
- this.matrixWorldNeedsUpdate = false;
|
|
|
+ },
|
|
|
|
|
|
- force = true;
|
|
|
+ attach: function ( child, scene, parent ) {
|
|
|
|
|
|
- }
|
|
|
+ var matrixWorldInverse = new THREE.Matrix4();
|
|
|
+ matrixWorldInverse.getInverse( parent.matrixWorld );
|
|
|
+ child.applyMatrix( matrixWorldInverse );
|
|
|
|
|
|
- // update children
|
|
|
+ scene.remove( child );
|
|
|
+ parent.add( child );
|
|
|
|
|
|
- for ( var i = 0, l = this.children.length; i < l; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- this.children[ i ].updateMatrixWorld( force );
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
-}() );
|
|
|
-
|
|
|
-// File:src/extras/core/Path.js
|
|
|
+// File:src/extras/FontUtils.js
|
|
|
|
|
|
/**
|
|
|
* @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
- * Creates free form 2d path using series of points, lines or curves.
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
*
|
|
|
- **/
|
|
|
+ * For Text operations in three.js (See TextGeometry)
|
|
|
+ *
|
|
|
+ * It uses techniques used in:
|
|
|
+ *
|
|
|
+ * typeface.js and canvastext
|
|
|
+ * For converting fonts and rendering with javascript
|
|
|
+ * http://typeface.neocracy.org
|
|
|
+ *
|
|
|
+ * Triangulation ported from AS3
|
|
|
+ * Simple Polygon Triangulation
|
|
|
+ * http://actionsnippet.com/?p=1462
|
|
|
+ *
|
|
|
+ * A Method to triangulate shapes with holes
|
|
|
+ * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
|
|
|
+ *
|
|
|
+ */
|
|
|
|
|
|
-THREE.Path = function ( points ) {
|
|
|
+THREE.FontUtils = {
|
|
|
|
|
|
- THREE.CurvePath.call(this);
|
|
|
+ faces: {},
|
|
|
|
|
|
- this.actions = [];
|
|
|
+ // Just for now. face[weight][style]
|
|
|
|
|
|
- if ( points ) {
|
|
|
+ face: 'helvetiker',
|
|
|
+ weight: 'normal',
|
|
|
+ style: 'normal',
|
|
|
+ size: 150,
|
|
|
+ divisions: 10,
|
|
|
|
|
|
- this.fromPoints( points );
|
|
|
+ getFace: function () {
|
|
|
|
|
|
- }
|
|
|
+ try {
|
|
|
|
|
|
-};
|
|
|
+ return this.faces[ this.face ][ this.weight ][ this.style ];
|
|
|
|
|
|
-THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
|
|
|
+ } catch (e) {
|
|
|
|
|
|
-THREE.PathActions = {
|
|
|
+ throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing."
|
|
|
|
|
|
- MOVE_TO: 'moveTo',
|
|
|
- LINE_TO: 'lineTo',
|
|
|
- QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve
|
|
|
- BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve
|
|
|
- CSPLINE_THRU: 'splineThru', // Catmull-rom spline
|
|
|
- ARC: 'arc', // Circle
|
|
|
- ELLIPSE: 'ellipse'
|
|
|
-};
|
|
|
+ };
|
|
|
|
|
|
-// TODO Clean up PATH API
|
|
|
+ },
|
|
|
|
|
|
-// Create path using straight lines to connect all points
|
|
|
-// - vectors: array of Vector2
|
|
|
+ loadFace: function ( data ) {
|
|
|
|
|
|
-THREE.Path.prototype.fromPoints = function ( vectors ) {
|
|
|
+ var family = data.familyName.toLowerCase();
|
|
|
|
|
|
- this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
|
|
|
+ var ThreeFont = this;
|
|
|
|
|
|
- for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
|
|
|
+ ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
|
|
|
|
|
|
- this.lineTo( vectors[ v ].x, vectors[ v ].y );
|
|
|
+ ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
|
|
|
+ ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
|
|
|
|
|
|
- };
|
|
|
+ var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
|
|
|
|
|
|
-};
|
|
|
+ return data;
|
|
|
|
|
|
-// startPath() endPath()?
|
|
|
+ },
|
|
|
|
|
|
-THREE.Path.prototype.moveTo = function ( x, y ) {
|
|
|
+ drawText: function ( text ) {
|
|
|
|
|
|
- var args = Array.prototype.slice.call( arguments );
|
|
|
- this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
|
|
|
+ var characterPts = [], allPts = [];
|
|
|
|
|
|
-};
|
|
|
+ // RenderText
|
|
|
|
|
|
-THREE.Path.prototype.lineTo = function ( x, y ) {
|
|
|
+ var i, p,
|
|
|
+ face = this.getFace(),
|
|
|
+ scale = this.size / face.resolution,
|
|
|
+ offset = 0,
|
|
|
+ chars = String( text ).split( '' ),
|
|
|
+ length = chars.length;
|
|
|
|
|
|
- var args = Array.prototype.slice.call( arguments );
|
|
|
+ var fontPaths = [];
|
|
|
|
|
|
- var lastargs = this.actions[ this.actions.length - 1 ].args;
|
|
|
+ for ( i = 0; i < length; i ++ ) {
|
|
|
|
|
|
- var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
- var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
+ var path = new THREE.Path();
|
|
|
|
|
|
- var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
|
|
|
- this.curves.push( curve );
|
|
|
+ var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
|
|
|
+ offset += ret.offset;
|
|
|
|
|
|
- this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
|
|
|
+ fontPaths.push( ret.path );
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
|
|
|
+ // get the width
|
|
|
|
|
|
- var args = Array.prototype.slice.call( arguments );
|
|
|
+ var width = offset / 2;
|
|
|
+ //
|
|
|
+ // for ( p = 0; p < allPts.length; p++ ) {
|
|
|
+ //
|
|
|
+ // allPts[ p ].x -= width;
|
|
|
+ //
|
|
|
+ // }
|
|
|
|
|
|
- var lastargs = this.actions[ this.actions.length - 1 ].args;
|
|
|
+ //var extract = this.extractPoints( allPts, characterPts );
|
|
|
+ //extract.contour = allPts;
|
|
|
|
|
|
- var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
- var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
+ //extract.paths = fontPaths;
|
|
|
+ //extract.offset = width;
|
|
|
|
|
|
- var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
|
|
|
- new THREE.Vector2( aCPx, aCPy ),
|
|
|
- new THREE.Vector2( aX, aY ) );
|
|
|
- this.curves.push( curve );
|
|
|
+ return { paths: fontPaths, offset: width };
|
|
|
|
|
|
- this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
|
|
|
+ },
|
|
|
|
|
|
-};
|
|
|
|
|
|
-THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
|
|
|
- aCP2x, aCP2y,
|
|
|
- aX, aY ) {
|
|
|
|
|
|
- var args = Array.prototype.slice.call( arguments );
|
|
|
|
|
|
- var lastargs = this.actions[ this.actions.length - 1 ].args;
|
|
|
+ extractGlyphPoints: function ( c, face, scale, offset, path ) {
|
|
|
|
|
|
- var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
- var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
+ var pts = [];
|
|
|
|
|
|
- var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ),
|
|
|
- new THREE.Vector2( aCP1x, aCP1y ),
|
|
|
- new THREE.Vector2( aCP2x, aCP2y ),
|
|
|
- new THREE.Vector2( aX, aY ) );
|
|
|
- this.curves.push( curve );
|
|
|
+ var i, i2, divisions,
|
|
|
+ outline, action, length,
|
|
|
+ scaleX, scaleY,
|
|
|
+ x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
|
|
|
+ laste,
|
|
|
+ glyph = face.glyphs[ c ] || face.glyphs[ '?' ];
|
|
|
|
|
|
- this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } );
|
|
|
+ if ( ! glyph ) return;
|
|
|
|
|
|
-};
|
|
|
+ if ( glyph.o ) {
|
|
|
|
|
|
-THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
|
|
|
+ outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
|
|
|
+ length = outline.length;
|
|
|
|
|
|
- var args = Array.prototype.slice.call( arguments );
|
|
|
- var lastargs = this.actions[ this.actions.length - 1 ].args;
|
|
|
+ scaleX = scale;
|
|
|
+ scaleY = scale;
|
|
|
|
|
|
- var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
- var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
-//---
|
|
|
- var npts = [ new THREE.Vector2( x0, y0 ) ];
|
|
|
- Array.prototype.push.apply( npts, pts );
|
|
|
+ for ( i = 0; i < length; ) {
|
|
|
|
|
|
- var curve = new THREE.SplineCurve( npts );
|
|
|
- this.curves.push( curve );
|
|
|
+ action = outline[ i ++ ];
|
|
|
|
|
|
- this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
|
|
|
+ //console.log( action );
|
|
|
|
|
|
-};
|
|
|
+ switch ( action ) {
|
|
|
|
|
|
-// FUTURE: Change the API or follow canvas API?
|
|
|
+ case 'm':
|
|
|
|
|
|
-THREE.Path.prototype.arc = function ( aX, aY, aRadius,
|
|
|
- aStartAngle, aEndAngle, aClockwise ) {
|
|
|
+ // Move To
|
|
|
|
|
|
- var lastargs = this.actions[ this.actions.length - 1].args;
|
|
|
- var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
- var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
+ x = outline[ i ++ ] * scaleX + offset;
|
|
|
+ y = outline[ i ++ ] * scaleY;
|
|
|
|
|
|
- this.absarc(aX + x0, aY + y0, aRadius,
|
|
|
- aStartAngle, aEndAngle, aClockwise );
|
|
|
+ path.moveTo( x, y );
|
|
|
+ break;
|
|
|
|
|
|
- };
|
|
|
+ case 'l':
|
|
|
|
|
|
- THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
|
|
|
- aStartAngle, aEndAngle, aClockwise ) {
|
|
|
- this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
|
|
|
- };
|
|
|
+ // Line To
|
|
|
|
|
|
-THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius,
|
|
|
- aStartAngle, aEndAngle, aClockwise ) {
|
|
|
+ x = outline[ i ++ ] * scaleX + offset;
|
|
|
+ y = outline[ i ++ ] * scaleY;
|
|
|
+ path.lineTo( x,y );
|
|
|
+ break;
|
|
|
|
|
|
- var lastargs = this.actions[ this.actions.length - 1].args;
|
|
|
- var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
- var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
+ case 'q':
|
|
|
|
|
|
- this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
|
|
|
- aStartAngle, aEndAngle, aClockwise );
|
|
|
+ // QuadraticCurveTo
|
|
|
|
|
|
- };
|
|
|
+ cpx = outline[ i ++ ] * scaleX + offset;
|
|
|
+ cpy = outline[ i ++ ] * scaleY;
|
|
|
+ cpx1 = outline[ i ++ ] * scaleX + offset;
|
|
|
+ cpy1 = outline[ i ++ ] * scaleY;
|
|
|
|
|
|
+ path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
|
|
|
|
|
|
-THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
|
|
|
- aStartAngle, aEndAngle, aClockwise ) {
|
|
|
+ laste = pts[ pts.length - 1 ];
|
|
|
|
|
|
- var args = Array.prototype.slice.call( arguments );
|
|
|
- var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
|
|
|
- aStartAngle, aEndAngle, aClockwise );
|
|
|
- this.curves.push( curve );
|
|
|
+ if ( laste ) {
|
|
|
|
|
|
- var lastPoint = curve.getPoint(1);
|
|
|
- args.push(lastPoint.x);
|
|
|
- args.push(lastPoint.y);
|
|
|
+ cpx0 = laste.x;
|
|
|
+ cpy0 = laste.y;
|
|
|
|
|
|
- this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
|
|
|
+ for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
|
|
|
|
|
|
- };
|
|
|
+ var t = i2 / divisions;
|
|
|
+ var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
|
|
|
+ var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
|
|
|
+ }
|
|
|
|
|
|
-THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
|
|
|
+ }
|
|
|
|
|
|
- if ( ! divisions ) divisions = 40;
|
|
|
+ break;
|
|
|
|
|
|
- var points = [];
|
|
|
+ case 'b':
|
|
|
|
|
|
- for ( var i = 0; i < divisions; i ++ ) {
|
|
|
+ // Cubic Bezier Curve
|
|
|
|
|
|
- points.push( this.getPoint( i / divisions ) );
|
|
|
+ cpx = outline[ i ++ ] * scaleX + offset;
|
|
|
+ cpy = outline[ i ++ ] * scaleY;
|
|
|
+ cpx1 = outline[ i ++ ] * scaleX + offset;
|
|
|
+ cpy1 = outline[ i ++ ] * scaleY;
|
|
|
+ cpx2 = outline[ i ++ ] * scaleX + offset;
|
|
|
+ cpy2 = outline[ i ++ ] * scaleY;
|
|
|
|
|
|
- //if( !this.getPoint( i / divisions ) ) throw "DIE";
|
|
|
+ path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
|
|
|
|
|
|
- }
|
|
|
+ laste = pts[ pts.length - 1 ];
|
|
|
|
|
|
- // if ( closedPath ) {
|
|
|
- //
|
|
|
- // points.push( points[ 0 ] );
|
|
|
- //
|
|
|
- // }
|
|
|
+ if ( laste ) {
|
|
|
|
|
|
- return points;
|
|
|
-
|
|
|
-};
|
|
|
+ cpx0 = laste.x;
|
|
|
+ cpy0 = laste.y;
|
|
|
|
|
|
-/* Return an array of vectors based on contour of the path */
|
|
|
+ for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
|
|
|
|
|
|
-THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
|
|
|
+ var t = i2 / divisions;
|
|
|
+ var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
|
|
|
+ var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
|
|
|
|
|
|
- if (this.useSpacedPoints) {
|
|
|
- console.log('tata');
|
|
|
- return this.getSpacedPoints( divisions, closedPath );
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- divisions = divisions || 12;
|
|
|
+ }
|
|
|
|
|
|
- var points = [];
|
|
|
+ break;
|
|
|
|
|
|
- var i, il, item, action, args;
|
|
|
- var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
|
|
|
- laste, j,
|
|
|
- t, tx, ty;
|
|
|
+ }
|
|
|
|
|
|
- for ( i = 0, il = this.actions.length; i < il; i ++ ) {
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- item = this.actions[ i ];
|
|
|
|
|
|
- action = item.action;
|
|
|
- args = item.args;
|
|
|
|
|
|
- switch( action ) {
|
|
|
+ return { offset: glyph.ha * scale, path:path };
|
|
|
+ }
|
|
|
|
|
|
- case THREE.PathActions.MOVE_TO:
|
|
|
+};
|
|
|
|
|
|
- points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
|
|
|
|
|
|
- break;
|
|
|
+THREE.FontUtils.generateShapes = function ( text, parameters ) {
|
|
|
|
|
|
- case THREE.PathActions.LINE_TO:
|
|
|
+ // Parameters
|
|
|
|
|
|
- points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
|
|
|
+ parameters = parameters || {};
|
|
|
|
|
|
- break;
|
|
|
+ var size = parameters.size !== undefined ? parameters.size : 100;
|
|
|
+ var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4;
|
|
|
|
|
|
- case THREE.PathActions.QUADRATIC_CURVE_TO:
|
|
|
+ var font = parameters.font !== undefined ? parameters.font : 'helvetiker';
|
|
|
+ var weight = parameters.weight !== undefined ? parameters.weight : 'normal';
|
|
|
+ var style = parameters.style !== undefined ? parameters.style : 'normal';
|
|
|
|
|
|
- cpx = args[ 2 ];
|
|
|
- cpy = args[ 3 ];
|
|
|
+ THREE.FontUtils.size = size;
|
|
|
+ THREE.FontUtils.divisions = curveSegments;
|
|
|
|
|
|
- cpx1 = args[ 0 ];
|
|
|
- cpy1 = args[ 1 ];
|
|
|
+ THREE.FontUtils.face = font;
|
|
|
+ THREE.FontUtils.weight = weight;
|
|
|
+ THREE.FontUtils.style = style;
|
|
|
|
|
|
- if ( points.length > 0 ) {
|
|
|
+ // Get a Font data json object
|
|
|
|
|
|
- laste = points[ points.length - 1 ];
|
|
|
+ var data = THREE.FontUtils.drawText( text );
|
|
|
|
|
|
- cpx0 = laste.x;
|
|
|
- cpy0 = laste.y;
|
|
|
+ var paths = data.paths;
|
|
|
+ var shapes = [];
|
|
|
|
|
|
- } else {
|
|
|
+ for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
|
|
|
|
|
|
- laste = this.actions[ i - 1 ].args;
|
|
|
+ Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
|
|
|
|
|
|
- cpx0 = laste[ laste.length - 2 ];
|
|
|
- cpy0 = laste[ laste.length - 1 ];
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ return shapes;
|
|
|
|
|
|
- for ( j = 1; j <= divisions; j ++ ) {
|
|
|
+};
|
|
|
|
|
|
- t = j / divisions;
|
|
|
|
|
|
- tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
|
|
|
- ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
|
|
|
+/**
|
|
|
+ * This code is a quick port of code written in C++ which was submitted to
|
|
|
+ * flipcode.com by John W. Ratcliff // July 22, 2000
|
|
|
+ * See original code and more information here:
|
|
|
+ * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
|
|
|
+ *
|
|
|
+ * ported to actionscript by Zevan Rosser
|
|
|
+ * www.actionsnippet.com
|
|
|
+ *
|
|
|
+ * ported to javascript by Joshua Koo
|
|
|
+ * http://www.lab4games.net/zz85/blog
|
|
|
+ *
|
|
|
+ */
|
|
|
|
|
|
- points.push( new THREE.Vector2( tx, ty ) );
|
|
|
|
|
|
- }
|
|
|
+( function ( namespace ) {
|
|
|
|
|
|
- break;
|
|
|
+ var EPSILON = 0.0000000001;
|
|
|
|
|
|
- case THREE.PathActions.BEZIER_CURVE_TO:
|
|
|
+ // takes in an contour array and returns
|
|
|
|
|
|
- cpx = args[ 4 ];
|
|
|
- cpy = args[ 5 ];
|
|
|
+ var process = function ( contour, indices ) {
|
|
|
|
|
|
- cpx1 = args[ 0 ];
|
|
|
- cpy1 = args[ 1 ];
|
|
|
+ var n = contour.length;
|
|
|
|
|
|
- cpx2 = args[ 2 ];
|
|
|
- cpy2 = args[ 3 ];
|
|
|
+ if ( n < 3 ) return null;
|
|
|
|
|
|
- if ( points.length > 0 ) {
|
|
|
+ var result = [],
|
|
|
+ verts = [],
|
|
|
+ vertIndices = [];
|
|
|
|
|
|
- laste = points[ points.length - 1 ];
|
|
|
+ /* we want a counter-clockwise polygon in verts */
|
|
|
|
|
|
- cpx0 = laste.x;
|
|
|
- cpy0 = laste.y;
|
|
|
+ var u, v, w;
|
|
|
|
|
|
- } else {
|
|
|
+ if ( area( contour ) > 0.0 ) {
|
|
|
|
|
|
- laste = this.actions[ i - 1 ].args;
|
|
|
+ for ( v = 0; v < n; v ++ ) verts[ v ] = v;
|
|
|
|
|
|
- cpx0 = laste[ laste.length - 2 ];
|
|
|
- cpy0 = laste[ laste.length - 1 ];
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- for ( j = 1; j <= divisions; j ++ ) {
|
|
|
+ var nv = n;
|
|
|
|
|
|
- t = j / divisions;
|
|
|
+ /* remove nv - 2 vertices, creating 1 triangle every time */
|
|
|
|
|
|
- tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
|
|
|
- ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
|
|
|
+ var count = 2 * nv; /* error detection */
|
|
|
|
|
|
- points.push( new THREE.Vector2( tx, ty ) );
|
|
|
+ for ( v = nv - 1; nv > 2; ) {
|
|
|
|
|
|
- }
|
|
|
+ /* if we loop, it is probably a non-simple polygon */
|
|
|
|
|
|
- break;
|
|
|
+ if ( ( count -- ) <= 0 ) {
|
|
|
|
|
|
- case THREE.PathActions.CSPLINE_THRU:
|
|
|
+ //** Triangulate: ERROR - probable bad polygon!
|
|
|
|
|
|
- laste = this.actions[ i - 1 ].args;
|
|
|
+ //throw ( "Warning, unable to triangulate polygon!" );
|
|
|
+ //return null;
|
|
|
+ // Sometimes warning is fine, especially polygons are triangulated in reverse.
|
|
|
+ console.log( 'Warning, unable to triangulate polygon!' );
|
|
|
|
|
|
- var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
|
|
|
- var spts = [ last ];
|
|
|
+ if ( indices ) return vertIndices;
|
|
|
+ return result;
|
|
|
|
|
|
- var n = divisions * args[ 0 ].length;
|
|
|
+ }
|
|
|
|
|
|
- spts = spts.concat( args[ 0 ] );
|
|
|
+ /* three consecutive vertices in current polygon, <u,v,w> */
|
|
|
|
|
|
- var spline = new THREE.SplineCurve( spts );
|
|
|
+ u = v; if ( nv <= u ) u = 0; /* previous */
|
|
|
+ v = u + 1; if ( nv <= v ) v = 0; /* new v */
|
|
|
+ w = v + 1; if ( nv <= w ) w = 0; /* next */
|
|
|
|
|
|
- for ( j = 1; j <= n; j ++ ) {
|
|
|
+ if ( snip( contour, u, v, w, nv, verts ) ) {
|
|
|
|
|
|
- points.push( spline.getPointAt( j / n ) ) ;
|
|
|
+ var a, b, c, s, t;
|
|
|
|
|
|
- }
|
|
|
+ /* true names of the vertices */
|
|
|
|
|
|
- break;
|
|
|
+ a = verts[ u ];
|
|
|
+ b = verts[ v ];
|
|
|
+ c = verts[ w ];
|
|
|
|
|
|
- case THREE.PathActions.ARC:
|
|
|
+ /* output Triangle */
|
|
|
|
|
|
- var aX = args[ 0 ], aY = args[ 1 ],
|
|
|
- aRadius = args[ 2 ],
|
|
|
- aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
|
|
|
- aClockwise = !! args[ 5 ];
|
|
|
+ result.push( [ contour[ a ],
|
|
|
+ contour[ b ],
|
|
|
+ contour[ c ] ] );
|
|
|
|
|
|
- var deltaAngle = aEndAngle - aStartAngle;
|
|
|
- var angle;
|
|
|
- var tdivisions = divisions * 2;
|
|
|
|
|
|
- for ( j = 1; j <= tdivisions; j ++ ) {
|
|
|
+ vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
|
|
|
|
|
|
- t = j / tdivisions;
|
|
|
+ /* remove v from the remaining polygon */
|
|
|
|
|
|
- if ( ! aClockwise ) {
|
|
|
+ for ( s = v, t = v + 1; t < nv; s++, t++ ) {
|
|
|
|
|
|
- t = 1 - t;
|
|
|
+ verts[ s ] = verts[ t ];
|
|
|
|
|
|
}
|
|
|
|
|
|
- angle = aStartAngle + t * deltaAngle;
|
|
|
-
|
|
|
- tx = aX + aRadius * Math.cos( angle );
|
|
|
- ty = aY + aRadius * Math.sin( angle );
|
|
|
+ nv --;
|
|
|
|
|
|
- //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
|
|
|
+ /* reset error detection counter */
|
|
|
|
|
|
- points.push( new THREE.Vector2( tx, ty ) );
|
|
|
+ count = 2 * nv;
|
|
|
|
|
|
}
|
|
|
|
|
|
- //console.log(points);
|
|
|
-
|
|
|
- break;
|
|
|
-
|
|
|
- case THREE.PathActions.ELLIPSE:
|
|
|
-
|
|
|
- var aX = args[ 0 ], aY = args[ 1 ],
|
|
|
- xRadius = args[ 2 ],
|
|
|
- yRadius = args[ 3 ],
|
|
|
- aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
|
|
|
- aClockwise = !! args[ 6 ];
|
|
|
+ }
|
|
|
|
|
|
+ if ( indices ) return vertIndices;
|
|
|
+ return result;
|
|
|
|
|
|
- var deltaAngle = aEndAngle - aStartAngle;
|
|
|
- var angle;
|
|
|
- var tdivisions = divisions * 2;
|
|
|
+ };
|
|
|
|
|
|
- for ( j = 1; j <= tdivisions; j ++ ) {
|
|
|
+ // calculate area of the contour polygon
|
|
|
|
|
|
- t = j / tdivisions;
|
|
|
+ var area = function ( contour ) {
|
|
|
|
|
|
- if ( ! aClockwise ) {
|
|
|
+ var n = contour.length;
|
|
|
+ var a = 0.0;
|
|
|
|
|
|
- t = 1 - t;
|
|
|
+ for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
|
|
|
|
|
|
- angle = aStartAngle + t * deltaAngle;
|
|
|
+ }
|
|
|
|
|
|
- tx = aX + xRadius * Math.cos( angle );
|
|
|
- ty = aY + yRadius * Math.sin( angle );
|
|
|
+ return a * 0.5;
|
|
|
|
|
|
- //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
|
|
|
+ };
|
|
|
|
|
|
- points.push( new THREE.Vector2( tx, ty ) );
|
|
|
+ var snip = function ( contour, u, v, w, n, verts ) {
|
|
|
|
|
|
- }
|
|
|
+ var p;
|
|
|
+ var ax, ay, bx, by;
|
|
|
+ var cx, cy, px, py;
|
|
|
|
|
|
- //console.log(points);
|
|
|
+ ax = contour[ verts[ u ] ].x;
|
|
|
+ ay = contour[ verts[ u ] ].y;
|
|
|
|
|
|
- break;
|
|
|
+ bx = contour[ verts[ v ] ].x;
|
|
|
+ by = contour[ verts[ v ] ].y;
|
|
|
|
|
|
- } // end switch
|
|
|
+ cx = contour[ verts[ w ] ].x;
|
|
|
+ cy = contour[ verts[ w ] ].y;
|
|
|
|
|
|
- }
|
|
|
+ if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;
|
|
|
|
|
|
+ var aX, aY, bX, bY, cX, cY;
|
|
|
+ var apx, apy, bpx, bpy, cpx, cpy;
|
|
|
+ var cCROSSap, bCROSScp, aCROSSbp;
|
|
|
|
|
|
+ aX = cx - bx; aY = cy - by;
|
|
|
+ bX = ax - cx; bY = ay - cy;
|
|
|
+ cX = bx - ax; cY = by - ay;
|
|
|
|
|
|
- // Normalize to remove the closing point by default.
|
|
|
- var lastPoint = points[ points.length - 1];
|
|
|
- var EPSILON = 0.0000000001;
|
|
|
- if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON &&
|
|
|
- Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
|
|
|
- points.splice( points.length - 1, 1);
|
|
|
- if ( closedPath ) {
|
|
|
-
|
|
|
- points.push( points[ 0 ] );
|
|
|
+ for ( p = 0; p < n; p ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ px = contour[ verts[ p ] ].x
|
|
|
+ py = contour[ verts[ p ] ].y
|
|
|
|
|
|
- return points;
|
|
|
+ if ( ( ( px === ax ) && ( py === ay ) ) ||
|
|
|
+ ( ( px === bx ) && ( py === by ) ) ||
|
|
|
+ ( ( px === cx ) && ( py === cy ) ) ) continue;
|
|
|
|
|
|
-};
|
|
|
+ apx = px - ax; apy = py - ay;
|
|
|
+ bpx = px - bx; bpy = py - by;
|
|
|
+ cpx = px - cx; cpy = py - cy;
|
|
|
|
|
|
-//
|
|
|
-// Breaks path into shapes
|
|
|
-//
|
|
|
-// Assumptions (if parameter isCCW==true the opposite holds):
|
|
|
-// - solid shapes are defined clockwise (CW)
|
|
|
-// - holes are defined counterclockwise (CCW)
|
|
|
-//
|
|
|
-// If parameter noHoles==true:
|
|
|
-// - all subPaths are regarded as solid shapes
|
|
|
-// - definition order CW/CCW has no relevance
|
|
|
-//
|
|
|
+ // see if p is inside triangle abc
|
|
|
|
|
|
-THREE.Path.prototype.toShapes = function( isCCW, noHoles ) {
|
|
|
+ aCROSSbp = aX * bpy - aY * bpx;
|
|
|
+ cCROSSap = cX * apy - cY * apx;
|
|
|
+ bCROSScp = bX * cpy - bY * cpx;
|
|
|
|
|
|
- function extractSubpaths( inActions ) {
|
|
|
+ if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false;
|
|
|
|
|
|
- var i, il, item, action, args;
|
|
|
+ }
|
|
|
|
|
|
- var subPaths = [], lastPath = new THREE.Path();
|
|
|
+ return true;
|
|
|
|
|
|
- for ( i = 0, il = inActions.length; i < il; i ++ ) {
|
|
|
+ };
|
|
|
|
|
|
- item = inActions[ i ];
|
|
|
|
|
|
- args = item.args;
|
|
|
- action = item.action;
|
|
|
+ namespace.Triangulate = process;
|
|
|
+ namespace.Triangulate.area = area;
|
|
|
|
|
|
- if ( action == THREE.PathActions.MOVE_TO ) {
|
|
|
+ return namespace;
|
|
|
|
|
|
- if ( lastPath.actions.length != 0 ) {
|
|
|
+} )( THREE.FontUtils );
|
|
|
|
|
|
- subPaths.push( lastPath );
|
|
|
- lastPath = new THREE.Path();
|
|
|
+// To use the typeface.js face files, hook up the API
|
|
|
+self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
|
|
|
+THREE.typeface_js = self._typeface_js;
|
|
|
|
|
|
- }
|
|
|
+// File:src/extras/audio/Audio.js
|
|
|
|
|
|
- }
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- lastPath[ action ].apply( lastPath, args );
|
|
|
+THREE.Audio = function ( listener ) {
|
|
|
|
|
|
- }
|
|
|
+ THREE.Object3D.call( this );
|
|
|
|
|
|
- if ( lastPath.actions.length != 0 ) {
|
|
|
+ this.type = 'Audio';
|
|
|
|
|
|
- subPaths.push( lastPath );
|
|
|
+ this.context = listener.context;
|
|
|
+ this.source = this.context.createBufferSource();
|
|
|
|
|
|
- }
|
|
|
+ this.gain = this.context.createGain();
|
|
|
+ this.gain.connect( this.context.destination );
|
|
|
|
|
|
- // console.log(subPaths);
|
|
|
+ this.panner = this.context.createPanner();
|
|
|
+ this.panner.connect( this.gain );
|
|
|
|
|
|
- return subPaths;
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- function toShapesNoHoles( inSubpaths ) {
|
|
|
+THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
- var shapes = [];
|
|
|
+THREE.Audio.prototype.load = function ( file ) {
|
|
|
|
|
|
- for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) {
|
|
|
+ var scope = this;
|
|
|
|
|
|
- var tmpPath = inSubpaths[ i ];
|
|
|
+ var request = new XMLHttpRequest();
|
|
|
+ request.open( 'GET', file, true );
|
|
|
+ request.responseType = 'arraybuffer';
|
|
|
+ request.onload = function ( e ) {
|
|
|
|
|
|
- var tmpShape = new THREE.Shape();
|
|
|
- tmpShape.actions = tmpPath.actions;
|
|
|
- tmpShape.curves = tmpPath.curves;
|
|
|
+ scope.context.decodeAudioData( this.response, function ( buffer ) {
|
|
|
|
|
|
- shapes.push( tmpShape );
|
|
|
- }
|
|
|
+ scope.source.buffer = buffer;
|
|
|
+ scope.source.connect( scope.panner );
|
|
|
+ scope.source.start( 0 );
|
|
|
|
|
|
- //console.log("shape", shapes);
|
|
|
+ } );
|
|
|
|
|
|
- return shapes;
|
|
|
};
|
|
|
+ request.send();
|
|
|
|
|
|
- function isPointInsidePolygon( inPt, inPolygon ) {
|
|
|
- var EPSILON = 0.0000000001;
|
|
|
+ return this;
|
|
|
|
|
|
- var polyLen = inPolygon.length;
|
|
|
+};
|
|
|
|
|
|
- // inPt on polygon contour => immediate success or
|
|
|
- // toggling of inside/outside at every single! intersection point of an edge
|
|
|
- // with the horizontal line through inPt, left of inPt
|
|
|
- // not counting lowerY endpoints of edges and whole edges on that line
|
|
|
- var inside = false;
|
|
|
- for( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
|
|
|
- var edgeLowPt = inPolygon[ p ];
|
|
|
- var edgeHighPt = inPolygon[ q ];
|
|
|
+THREE.Audio.prototype.setLoop = function ( value ) {
|
|
|
|
|
|
- var edgeDx = edgeHighPt.x - edgeLowPt.x;
|
|
|
- var edgeDy = edgeHighPt.y - edgeLowPt.y;
|
|
|
+ this.source.loop = value;
|
|
|
|
|
|
- if ( Math.abs(edgeDy) > EPSILON ) { // not parallel
|
|
|
- if ( edgeDy < 0 ) {
|
|
|
- edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
|
|
|
- edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
|
|
|
- }
|
|
|
- if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
|
|
|
+};
|
|
|
|
|
|
- if ( inPt.y == edgeLowPt.y ) {
|
|
|
- if ( inPt.x == edgeLowPt.x ) return true; // inPt is on contour ?
|
|
|
- // continue; // no intersection or edgeLowPt => doesn't count !!!
|
|
|
- } else {
|
|
|
- var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y);
|
|
|
- if ( perpEdge == 0 ) return true; // inPt is on contour ?
|
|
|
- if ( perpEdge < 0 ) continue;
|
|
|
- inside = ! inside; // true intersection left of inPt
|
|
|
- }
|
|
|
- } else { // parallel or colinear
|
|
|
- if ( inPt.y != edgeLowPt.y ) continue; // parallel
|
|
|
- // egde lies on the same horizontal line as inPt
|
|
|
- if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
|
|
|
- ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !
|
|
|
- // continue;
|
|
|
- }
|
|
|
- }
|
|
|
+THREE.Audio.prototype.setRefDistance = function ( value ) {
|
|
|
|
|
|
- return inside;
|
|
|
- }
|
|
|
+ this.panner.refDistance = value;
|
|
|
|
|
|
+};
|
|
|
|
|
|
- var subPaths = extractSubpaths( this.actions );
|
|
|
- if ( subPaths.length == 0 ) return [];
|
|
|
+THREE.Audio.prototype.setRolloffFactor = function ( value ) {
|
|
|
|
|
|
- if ( noHoles === true ) return toShapesNoHoles( subPaths );
|
|
|
+ this.panner.rolloffFactor = value;
|
|
|
|
|
|
+};
|
|
|
|
|
|
- var solid, tmpPath, tmpShape, shapes = [];
|
|
|
+THREE.Audio.prototype.updateMatrixWorld = ( function () {
|
|
|
|
|
|
- if ( subPaths.length == 1) {
|
|
|
+ var position = new THREE.Vector3();
|
|
|
|
|
|
- tmpPath = subPaths[0];
|
|
|
- tmpShape = new THREE.Shape();
|
|
|
- tmpShape.actions = tmpPath.actions;
|
|
|
- tmpShape.curves = tmpPath.curves;
|
|
|
- shapes.push( tmpShape );
|
|
|
- return shapes;
|
|
|
+ return function ( force ) {
|
|
|
|
|
|
- }
|
|
|
+ THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
|
|
|
|
|
|
- var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
|
|
|
- holesFirst = isCCW ? ! holesFirst : holesFirst;
|
|
|
+ position.setFromMatrixPosition( this.matrixWorld );
|
|
|
|
|
|
- // console.log("Holes first", holesFirst);
|
|
|
-
|
|
|
- var betterShapeHoles = [];
|
|
|
- var newShapes = [];
|
|
|
- var newShapeHoles = [];
|
|
|
- var mainIdx = 0;
|
|
|
- var tmpPoints;
|
|
|
+ this.panner.setPosition( position.x, position.y, position.z );
|
|
|
|
|
|
- newShapes[mainIdx] = undefined;
|
|
|
- newShapeHoles[mainIdx] = [];
|
|
|
+ };
|
|
|
|
|
|
- var i, il;
|
|
|
+} )();
|
|
|
|
|
|
- for ( i = 0, il = subPaths.length; i < il; i ++ ) {
|
|
|
+// File:src/extras/audio/AudioListener.js
|
|
|
|
|
|
- tmpPath = subPaths[ i ];
|
|
|
- tmpPoints = tmpPath.getPoints();
|
|
|
- solid = THREE.Shape.Utils.isClockWise( tmpPoints );
|
|
|
- solid = isCCW ? ! solid : solid;
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- if ( solid ) {
|
|
|
+THREE.AudioListener = function () {
|
|
|
|
|
|
- if ( (! holesFirst ) && ( newShapes[mainIdx] ) ) mainIdx ++;
|
|
|
+ THREE.Object3D.call( this );
|
|
|
|
|
|
- newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints };
|
|
|
- newShapes[mainIdx].s.actions = tmpPath.actions;
|
|
|
- newShapes[mainIdx].s.curves = tmpPath.curves;
|
|
|
-
|
|
|
- if ( holesFirst ) mainIdx ++;
|
|
|
- newShapeHoles[mainIdx] = [];
|
|
|
+ this.type = 'AudioListener';
|
|
|
|
|
|
- //console.log('cw', i);
|
|
|
+ this.context = new ( window.AudioContext || window.webkitAudioContext )();
|
|
|
|
|
|
- } else {
|
|
|
+};
|
|
|
|
|
|
- newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } );
|
|
|
+THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
- //console.log('ccw', i);
|
|
|
+THREE.AudioListener.prototype.updateMatrixWorld = ( function () {
|
|
|
|
|
|
- }
|
|
|
+ var position = new THREE.Vector3();
|
|
|
+ var quaternion = new THREE.Quaternion();
|
|
|
+ var scale = new THREE.Vector3();
|
|
|
|
|
|
- }
|
|
|
+ var orientation = new THREE.Vector3();
|
|
|
+ var velocity = new THREE.Vector3();
|
|
|
|
|
|
- // only Holes? -> probably all Shapes with wrong orientation
|
|
|
- if ( ! newShapes[0] ) return toShapesNoHoles( subPaths );
|
|
|
+ var positionPrev = new THREE.Vector3();
|
|
|
|
|
|
+ return function ( force ) {
|
|
|
|
|
|
- if ( newShapes.length > 1 ) {
|
|
|
- var ambigious = false;
|
|
|
- var toChange = [];
|
|
|
+ THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
|
|
|
|
|
|
- for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
|
|
|
- betterShapeHoles[sIdx] = [];
|
|
|
- }
|
|
|
- for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
|
|
|
- var sh = newShapes[sIdx];
|
|
|
- var sho = newShapeHoles[sIdx];
|
|
|
- for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
|
|
|
- var ho = sho[hIdx];
|
|
|
- var hole_unassigned = true;
|
|
|
- for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
|
|
|
- if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) {
|
|
|
- if ( sIdx != s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
|
|
|
- if ( hole_unassigned ) {
|
|
|
- hole_unassigned = false;
|
|
|
- betterShapeHoles[s2Idx].push( ho );
|
|
|
- } else {
|
|
|
- ambigious = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); }
|
|
|
- }
|
|
|
- }
|
|
|
- // console.log("ambigious: ", ambigious);
|
|
|
- if ( toChange.length > 0 ) {
|
|
|
- // console.log("to change: ", toChange);
|
|
|
- if (! ambigious) newShapeHoles = betterShapeHoles;
|
|
|
- }
|
|
|
- }
|
|
|
+ var listener = this.context.listener;
|
|
|
|
|
|
- var tmpHoles, j, jl;
|
|
|
- for ( i = 0, il = newShapes.length; i < il; i ++ ) {
|
|
|
- tmpShape = newShapes[i].s;
|
|
|
- shapes.push( tmpShape );
|
|
|
- tmpHoles = newShapeHoles[i];
|
|
|
- for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
|
|
|
- tmpShape.holes.push( tmpHoles[j].h );
|
|
|
- }
|
|
|
- }
|
|
|
+ this.matrixWorld.decompose( position, quaternion, scale );
|
|
|
|
|
|
- //console.log("shape", shapes);
|
|
|
+ orientation.set( 0, 0, -1 ).applyQuaternion( quaternion );
|
|
|
+ velocity.subVectors( position, positionPrev );
|
|
|
|
|
|
- return shapes;
|
|
|
+ listener.setPosition( position.x, position.y, position.z );
|
|
|
+ listener.setOrientation( orientation.x, orientation.y, orientation.z, this.up.x, this.up.y, this.up.z );
|
|
|
+ listener.setVelocity( velocity.x, velocity.y, velocity.z );
|
|
|
|
|
|
-};
|
|
|
+ positionPrev.copy( position );
|
|
|
|
|
|
-// File:src/extras/core/Shape.js
|
|
|
+ };
|
|
|
+
|
|
|
+} )();
|
|
|
+
|
|
|
+// File:src/extras/core/Curve.js
|
|
|
|
|
|
/**
|
|
|
* @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
- * Defines a 2d shape plane using paths.
|
|
|
+ * Extensible curve object
|
|
|
+ *
|
|
|
+ * Some common of Curve methods
|
|
|
+ * .getPoint(t), getTangent(t)
|
|
|
+ * .getPointAt(u), getTagentAt(u)
|
|
|
+ * .getPoints(), .getSpacedPoints()
|
|
|
+ * .getLength()
|
|
|
+ * .updateArcLengths()
|
|
|
+ *
|
|
|
+ * This following classes subclasses THREE.Curve:
|
|
|
+ *
|
|
|
+ * -- 2d classes --
|
|
|
+ * THREE.LineCurve
|
|
|
+ * THREE.QuadraticBezierCurve
|
|
|
+ * THREE.CubicBezierCurve
|
|
|
+ * THREE.SplineCurve
|
|
|
+ * THREE.ArcCurve
|
|
|
+ * THREE.EllipseCurve
|
|
|
+ *
|
|
|
+ * -- 3d classes --
|
|
|
+ * THREE.LineCurve3
|
|
|
+ * THREE.QuadraticBezierCurve3
|
|
|
+ * THREE.CubicBezierCurve3
|
|
|
+ * THREE.SplineCurve3
|
|
|
+ * THREE.ClosedSplineCurve3
|
|
|
+ *
|
|
|
+ * A series of curves can be represented as a THREE.CurvePath
|
|
|
+ *
|
|
|
**/
|
|
|
|
|
|
-// STEP 1 Create a path.
|
|
|
-// STEP 2 Turn path into shape.
|
|
|
-// STEP 3 ExtrudeGeometry takes in Shape/Shapes
|
|
|
-// STEP 3a - Extract points from each shape, turn to vertices
|
|
|
-// STEP 3b - Triangulate each shape, add faces.
|
|
|
-
|
|
|
-THREE.Shape = function () {
|
|
|
+/**************************************************************
|
|
|
+ * Abstract Curve base class
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- THREE.Path.apply( this, arguments );
|
|
|
- this.holes = [];
|
|
|
+THREE.Curve = function () {
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.Shape.prototype = Object.create( THREE.Path.prototype );
|
|
|
-
|
|
|
-// Convenience method to return ExtrudeGeometry
|
|
|
+// Virtual base class method to overwrite and implement in subclasses
|
|
|
+// - t [0 .. 1]
|
|
|
|
|
|
-THREE.Shape.prototype.extrude = function ( options ) {
|
|
|
+THREE.Curve.prototype.getPoint = function ( t ) {
|
|
|
|
|
|
- var extruded = new THREE.ExtrudeGeometry( this, options );
|
|
|
- return extruded;
|
|
|
+ console.log( "Warning, getPoint() not implemented!" );
|
|
|
+ return null;
|
|
|
|
|
|
};
|
|
|
|
|
|
-// Convenience method to return ShapeGeometry
|
|
|
+// Get point at relative position in curve according to arc length
|
|
|
+// - u [0 .. 1]
|
|
|
|
|
|
-THREE.Shape.prototype.makeGeometry = function ( options ) {
|
|
|
+THREE.Curve.prototype.getPointAt = function ( u ) {
|
|
|
|
|
|
- var geometry = new THREE.ShapeGeometry( this, options );
|
|
|
- return geometry;
|
|
|
+ var t = this.getUtoTmapping( u );
|
|
|
+ return this.getPoint( t );
|
|
|
|
|
|
};
|
|
|
|
|
|
-// Get points of holes
|
|
|
+// Get sequence of points using getPoint( t )
|
|
|
|
|
|
-THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
|
|
|
+THREE.Curve.prototype.getPoints = function ( divisions ) {
|
|
|
|
|
|
- var i, il = this.holes.length, holesPts = [];
|
|
|
+ if ( ! divisions ) divisions = 5;
|
|
|
|
|
|
- for ( i = 0; i < il; i ++ ) {
|
|
|
+ var d, pts = [];
|
|
|
|
|
|
- holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
|
|
|
+ for ( d = 0; d <= divisions; d ++ ) {
|
|
|
+
|
|
|
+ pts.push( this.getPoint( d / divisions ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- return holesPts;
|
|
|
+ return pts;
|
|
|
|
|
|
};
|
|
|
|
|
|
-// Get points of holes (spaced by regular distance)
|
|
|
+// Get sequence of points using getPointAt( u )
|
|
|
|
|
|
-THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
|
|
|
+THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
|
|
|
|
|
|
- var i, il = this.holes.length, holesPts = [];
|
|
|
+ if ( ! divisions ) divisions = 5;
|
|
|
|
|
|
- for ( i = 0; i < il; i ++ ) {
|
|
|
+ var d, pts = [];
|
|
|
|
|
|
- holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
|
|
|
+ for ( d = 0; d <= divisions; d ++ ) {
|
|
|
+
|
|
|
+ pts.push( this.getPointAt( d / divisions ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- return holesPts;
|
|
|
+ return pts;
|
|
|
|
|
|
};
|
|
|
|
|
|
+// Get total curve arc length
|
|
|
|
|
|
-// Get points of shape and holes (keypoints based on segments parameter)
|
|
|
+THREE.Curve.prototype.getLength = function () {
|
|
|
|
|
|
-THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
|
|
|
+ var lengths = this.getLengths();
|
|
|
+ return lengths[ lengths.length - 1 ];
|
|
|
|
|
|
- return {
|
|
|
+};
|
|
|
|
|
|
- shape: this.getTransformedPoints( divisions ),
|
|
|
- holes: this.getPointsHoles( divisions )
|
|
|
+// Get list of cumulative segment lengths
|
|
|
|
|
|
- };
|
|
|
+THREE.Curve.prototype.getLengths = function ( divisions ) {
|
|
|
|
|
|
-};
|
|
|
+ if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
|
|
|
|
|
|
-THREE.Shape.prototype.extractPoints = function ( divisions ) {
|
|
|
+ if ( this.cacheArcLengths
|
|
|
+ && ( this.cacheArcLengths.length == divisions + 1 )
|
|
|
+ && ! this.needsUpdate) {
|
|
|
+
|
|
|
+ //console.log( "cached", this.cacheArcLengths );
|
|
|
+ return this.cacheArcLengths;
|
|
|
|
|
|
- if (this.useSpacedPoints) {
|
|
|
- return this.extractAllSpacedPoints(divisions);
|
|
|
}
|
|
|
|
|
|
- return this.extractAllPoints(divisions);
|
|
|
+ this.needsUpdate = false;
|
|
|
|
|
|
-};
|
|
|
+ var cache = [];
|
|
|
+ var current, last = this.getPoint( 0 );
|
|
|
+ var p, sum = 0;
|
|
|
|
|
|
-//
|
|
|
-// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
|
|
|
-//
|
|
|
-// return {
|
|
|
-//
|
|
|
-// shape: this.transform( bend, divisions ),
|
|
|
-// holes: this.getPointsHoles( divisions, bend )
|
|
|
-//
|
|
|
-// };
|
|
|
-//
|
|
|
-// };
|
|
|
+ cache.push( 0 );
|
|
|
|
|
|
-// Get points of shape and holes (spaced by regular distance)
|
|
|
+ for ( p = 1; p <= divisions; p ++ ) {
|
|
|
|
|
|
-THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
|
|
|
+ current = this.getPoint ( p / divisions );
|
|
|
+ sum += current.distanceTo( last );
|
|
|
+ cache.push( sum );
|
|
|
+ last = current;
|
|
|
|
|
|
- return {
|
|
|
+ }
|
|
|
|
|
|
- shape: this.getTransformedSpacedPoints( divisions ),
|
|
|
- holes: this.getSpacedPointsHoles( divisions )
|
|
|
+ this.cacheArcLengths = cache;
|
|
|
|
|
|
- };
|
|
|
+ return cache; // { sums: cache, sum:sum }; Sum is in the last element.
|
|
|
|
|
|
};
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Utils
|
|
|
- **************************************************************/
|
|
|
|
|
|
-THREE.Shape.Utils = {
|
|
|
+THREE.Curve.prototype.updateArcLengths = function() {
|
|
|
+ this.needsUpdate = true;
|
|
|
+ this.getLengths();
|
|
|
+};
|
|
|
|
|
|
- triangulateShape: function ( contour, holes ) {
|
|
|
+// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
|
|
|
|
|
|
- function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {
|
|
|
- // inOtherPt needs to be colinear to the inSegment
|
|
|
- if ( inSegPt1.x != inSegPt2.x ) {
|
|
|
- if ( inSegPt1.x < inSegPt2.x ) {
|
|
|
- return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );
|
|
|
- } else {
|
|
|
- return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );
|
|
|
- }
|
|
|
- } else {
|
|
|
- if ( inSegPt1.y < inSegPt2.y ) {
|
|
|
- return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );
|
|
|
- } else {
|
|
|
- return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
|
|
|
|
|
|
- function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
|
|
|
- var EPSILON = 0.0000000001;
|
|
|
+ var arcLengths = this.getLengths();
|
|
|
|
|
|
- var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
|
|
|
- var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
|
|
|
+ var i = 0, il = arcLengths.length;
|
|
|
|
|
|
- var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
|
|
|
- var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
|
|
|
+ var targetArcLength; // The targeted u distance value to get
|
|
|
|
|
|
- var limit = seg1dy * seg2dx - seg1dx * seg2dy;
|
|
|
- var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
|
|
|
+ if ( distance ) {
|
|
|
|
|
|
- if ( Math.abs(limit) > EPSILON ) { // not parallel
|
|
|
+ targetArcLength = distance;
|
|
|
|
|
|
- var perpSeg2;
|
|
|
- if ( limit > 0 ) {
|
|
|
- if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return [];
|
|
|
- perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
|
|
|
- if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return [];
|
|
|
- } else {
|
|
|
- if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return [];
|
|
|
- perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
|
|
|
- if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return [];
|
|
|
- }
|
|
|
+ } else {
|
|
|
|
|
|
- // i.e. to reduce rounding errors
|
|
|
- // intersection at endpoint of segment#1?
|
|
|
- if ( perpSeg2 == 0 ) {
|
|
|
- if ( ( inExcludeAdjacentSegs ) &&
|
|
|
- ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return [];
|
|
|
- return [ inSeg1Pt1 ];
|
|
|
- }
|
|
|
- if ( perpSeg2 == limit ) {
|
|
|
- if ( ( inExcludeAdjacentSegs ) &&
|
|
|
- ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return [];
|
|
|
- return [ inSeg1Pt2 ];
|
|
|
- }
|
|
|
- // intersection at endpoint of segment#2?
|
|
|
- if ( perpSeg1 == 0 ) return [ inSeg2Pt1 ];
|
|
|
- if ( perpSeg1 == limit ) return [ inSeg2Pt2 ];
|
|
|
+ targetArcLength = u * arcLengths[ il - 1 ];
|
|
|
|
|
|
- // return real intersection point
|
|
|
- var factorSeg1 = perpSeg2 / limit;
|
|
|
- return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
|
|
|
- y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];
|
|
|
+ }
|
|
|
|
|
|
- } else { // parallel or colinear
|
|
|
- if ( ( perpSeg1 != 0 ) ||
|
|
|
- ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) return [];
|
|
|
+ //var time = Date.now();
|
|
|
|
|
|
- // they are collinear or degenerate
|
|
|
- var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) ); // segment1 ist just a point?
|
|
|
- var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) ); // segment2 ist just a point?
|
|
|
- // both segments are points
|
|
|
- if ( seg1Pt && seg2Pt ) {
|
|
|
- if ( (inSeg1Pt1.x != inSeg2Pt1.x) ||
|
|
|
- (inSeg1Pt1.y != inSeg2Pt1.y) ) return []; // they are distinct points
|
|
|
- return [ inSeg1Pt1 ]; // they are the same point
|
|
|
- }
|
|
|
- // segment#1 is a single point
|
|
|
- if ( seg1Pt ) {
|
|
|
- if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2
|
|
|
- return [ inSeg1Pt1 ];
|
|
|
- }
|
|
|
- // segment#2 is a single point
|
|
|
- if ( seg2Pt ) {
|
|
|
- if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1
|
|
|
- return [ inSeg2Pt1 ];
|
|
|
- }
|
|
|
+ // binary search for the index with largest value smaller than target u distance
|
|
|
|
|
|
- // they are collinear segments, which might overlap
|
|
|
- var seg1min, seg1max, seg1minVal, seg1maxVal;
|
|
|
- var seg2min, seg2max, seg2minVal, seg2maxVal;
|
|
|
- if (seg1dx != 0) { // the segments are NOT on a vertical line
|
|
|
- if ( inSeg1Pt1.x < inSeg1Pt2.x ) {
|
|
|
- seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
|
|
|
- seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;
|
|
|
- } else {
|
|
|
- seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
|
|
|
- seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;
|
|
|
- }
|
|
|
- if ( inSeg2Pt1.x < inSeg2Pt2.x ) {
|
|
|
- seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
|
|
|
- seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;
|
|
|
- } else {
|
|
|
- seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
|
|
|
- seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;
|
|
|
- }
|
|
|
- } else { // the segments are on a vertical line
|
|
|
- if ( inSeg1Pt1.y < inSeg1Pt2.y ) {
|
|
|
- seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
|
|
|
- seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;
|
|
|
- } else {
|
|
|
- seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
|
|
|
- seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;
|
|
|
- }
|
|
|
- if ( inSeg2Pt1.y < inSeg2Pt2.y ) {
|
|
|
- seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
|
|
|
- seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;
|
|
|
- } else {
|
|
|
- seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
|
|
|
- seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;
|
|
|
- }
|
|
|
- }
|
|
|
- if ( seg1minVal <= seg2minVal ) {
|
|
|
- if ( seg1maxVal < seg2minVal ) return [];
|
|
|
- if ( seg1maxVal == seg2minVal ) {
|
|
|
- if ( inExcludeAdjacentSegs ) return [];
|
|
|
- return [ seg2min ];
|
|
|
- }
|
|
|
- if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ];
|
|
|
- return [ seg2min, seg2max ];
|
|
|
- } else {
|
|
|
- if ( seg1minVal > seg2maxVal ) return [];
|
|
|
- if ( seg1minVal == seg2maxVal ) {
|
|
|
- if ( inExcludeAdjacentSegs ) return [];
|
|
|
- return [ seg1min ];
|
|
|
- }
|
|
|
- if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ];
|
|
|
- return [ seg1min, seg2max ];
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ var low = 0, high = il - 1, comparison;
|
|
|
|
|
|
- function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
|
|
|
- // The order of legs is important
|
|
|
+ while ( low <= high ) {
|
|
|
|
|
|
- var EPSILON = 0.0000000001;
|
|
|
+ i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
|
|
|
|
|
|
- // translation of all points, so that Vertex is at (0,0)
|
|
|
- var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y;
|
|
|
- var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y;
|
|
|
- var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y;
|
|
|
+ comparison = arcLengths[ i ] - targetArcLength;
|
|
|
|
|
|
- // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
|
|
|
- var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX;
|
|
|
- var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX;
|
|
|
+ if ( comparison < 0 ) {
|
|
|
|
|
|
- if ( Math.abs(from2toAngle) > EPSILON ) { // angle != 180 deg.
|
|
|
+ low = i + 1;
|
|
|
+ continue;
|
|
|
|
|
|
- var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX;
|
|
|
- // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
|
|
|
+ } else if ( comparison > 0 ) {
|
|
|
|
|
|
- if ( from2toAngle > 0 ) { // main angle < 180 deg.
|
|
|
- return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );
|
|
|
- } else { // main angle > 180 deg.
|
|
|
- return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );
|
|
|
- }
|
|
|
- } else { // angle == 180 deg.
|
|
|
- // console.log( "from2to: 180 deg., from2other: " + from2otherAngle );
|
|
|
- return ( from2otherAngle > 0 );
|
|
|
- }
|
|
|
- }
|
|
|
+ high = i - 1;
|
|
|
+ continue;
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
- function removeHoles( contour, holes ) {
|
|
|
+ high = i;
|
|
|
+ break;
|
|
|
|
|
|
- var shape = contour.concat(); // work on this shape
|
|
|
- var hole;
|
|
|
+ // DONE
|
|
|
|
|
|
- function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
|
|
|
- // Check if hole point lies within angle around shape point
|
|
|
- var lastShapeIdx = shape.length - 1;
|
|
|
+ }
|
|
|
|
|
|
- var prevShapeIdx = inShapeIdx - 1;
|
|
|
- if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx;
|
|
|
+ }
|
|
|
|
|
|
- var nextShapeIdx = inShapeIdx + 1;
|
|
|
- if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0;
|
|
|
+ i = high;
|
|
|
|
|
|
- var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] );
|
|
|
- if (! insideAngle ) {
|
|
|
- // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
|
|
|
- return false;
|
|
|
- }
|
|
|
+ //console.log('b' , i, low, high, Date.now()- time);
|
|
|
|
|
|
- // Check if shape point lies within angle around hole point
|
|
|
- var lastHoleIdx = hole.length - 1;
|
|
|
+ if ( arcLengths[ i ] == targetArcLength ) {
|
|
|
|
|
|
- var prevHoleIdx = inHoleIdx - 1;
|
|
|
- if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx;
|
|
|
+ var t = i / ( il - 1 );
|
|
|
+ return t;
|
|
|
|
|
|
- var nextHoleIdx = inHoleIdx + 1;
|
|
|
- if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0;
|
|
|
+ }
|
|
|
|
|
|
- insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] );
|
|
|
- if (! insideAngle ) {
|
|
|
- // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
|
|
|
- return false;
|
|
|
- }
|
|
|
+ // we could get finer grain at lengths, or use simple interpolatation between two points
|
|
|
|
|
|
- return true;
|
|
|
- }
|
|
|
+ var lengthBefore = arcLengths[ i ];
|
|
|
+ var lengthAfter = arcLengths[ i + 1 ];
|
|
|
|
|
|
- function intersectsShapeEdge( inShapePt, inHolePt ) {
|
|
|
- // checks for intersections with shape edges
|
|
|
- var sIdx, nextIdx, intersection;
|
|
|
- for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {
|
|
|
- nextIdx = sIdx+1; nextIdx %= shape.length;
|
|
|
- intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true );
|
|
|
- if ( intersection.length > 0 ) return true;
|
|
|
- }
|
|
|
+ var segmentLength = lengthAfter - lengthBefore;
|
|
|
|
|
|
- return false;
|
|
|
- }
|
|
|
+ // determine where we are between the 'before' and 'after' points
|
|
|
|
|
|
- var indepHoles = [];
|
|
|
+ var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
|
|
|
|
|
|
- function intersectsHoleEdge( inShapePt, inHolePt ) {
|
|
|
- // checks for intersections with hole edges
|
|
|
- var ihIdx, chkHole,
|
|
|
- hIdx, nextIdx, intersection;
|
|
|
- for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {
|
|
|
- chkHole = holes[indepHoles[ihIdx]];
|
|
|
- for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {
|
|
|
- nextIdx = hIdx+1; nextIdx %= chkHole.length;
|
|
|
- intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true );
|
|
|
- if ( intersection.length > 0 ) return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
+ // add that fractional amount to t
|
|
|
|
|
|
- var holeIndex, shapeIndex,
|
|
|
- shapePt, holePt,
|
|
|
- holeIdx, cutKey, failedCuts = [],
|
|
|
- tmpShape1, tmpShape2,
|
|
|
- tmpHole1, tmpHole2;
|
|
|
+ var t = ( i + segmentFraction ) / ( il -1 );
|
|
|
|
|
|
- for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
+ return t;
|
|
|
|
|
|
- indepHoles.push( h );
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+// Returns a unit vector tangent at t
|
|
|
+// In case any sub curve does not implement its tangent derivation,
|
|
|
+// 2 points a small delta apart will be used to find its gradient
|
|
|
+// which seems to give a reasonable approximation
|
|
|
|
|
|
- var minShapeIndex = 0;
|
|
|
- var counter = indepHoles.length * 2;
|
|
|
- while ( indepHoles.length > 0 ) {
|
|
|
- counter --;
|
|
|
- if ( counter < 0 ) {
|
|
|
- console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
|
|
|
- break;
|
|
|
- }
|
|
|
+THREE.Curve.prototype.getTangent = function( t ) {
|
|
|
|
|
|
- // search for shape-vertex and hole-vertex,
|
|
|
- // which can be connected without intersections
|
|
|
- for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {
|
|
|
+ var delta = 0.0001;
|
|
|
+ var t1 = t - delta;
|
|
|
+ var t2 = t + delta;
|
|
|
|
|
|
- shapePt = shape[ shapeIndex ];
|
|
|
- holeIndex = - 1;
|
|
|
+ // Capping in case of danger
|
|
|
|
|
|
- // search for hole which can be reached without intersections
|
|
|
- for ( var h = 0; h < indepHoles.length; h ++ ) {
|
|
|
- holeIdx = indepHoles[h];
|
|
|
+ if ( t1 < 0 ) t1 = 0;
|
|
|
+ if ( t2 > 1 ) t2 = 1;
|
|
|
|
|
|
- // prevent multiple checks
|
|
|
- cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
|
|
|
- if ( failedCuts[cutKey] !== undefined ) continue;
|
|
|
+ var pt1 = this.getPoint( t1 );
|
|
|
+ var pt2 = this.getPoint( t2 );
|
|
|
|
|
|
- hole = holes[holeIdx];
|
|
|
- for ( var h2 = 0; h2 < hole.length; h2 ++ ) {
|
|
|
- holePt = hole[ h2 ];
|
|
|
- if (! isCutLineInsideAngles( shapeIndex, h2 ) ) continue;
|
|
|
- if ( intersectsShapeEdge( shapePt, holePt ) ) continue;
|
|
|
- if ( intersectsHoleEdge( shapePt, holePt ) ) continue;
|
|
|
+ var vec = pt2.clone().sub(pt1);
|
|
|
+ return vec.normalize();
|
|
|
|
|
|
- holeIndex = h2;
|
|
|
- indepHoles.splice(h,1);
|
|
|
+};
|
|
|
|
|
|
- tmpShape1 = shape.slice( 0, shapeIndex+1 );
|
|
|
- tmpShape2 = shape.slice( shapeIndex );
|
|
|
- tmpHole1 = hole.slice( holeIndex );
|
|
|
- tmpHole2 = hole.slice( 0, holeIndex+1 );
|
|
|
|
|
|
- shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
|
|
|
+THREE.Curve.prototype.getTangentAt = function ( u ) {
|
|
|
|
|
|
- minShapeIndex = shapeIndex;
|
|
|
+ var t = this.getUtoTmapping( u );
|
|
|
+ return this.getTangent( t );
|
|
|
|
|
|
- // Debug only, to show the selected cuts
|
|
|
- // glob_CutLines.push( [ shapePt, holePt ] );
|
|
|
+};
|
|
|
|
|
|
- break;
|
|
|
- }
|
|
|
- if ( holeIndex >= 0 ) break; // hole-vertex found
|
|
|
|
|
|
- failedCuts[cutKey] = true; // remember failure
|
|
|
- }
|
|
|
- if ( holeIndex >= 0 ) break; // hole-vertex found
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- return shape; /* shape with no holes */
|
|
|
- }
|
|
|
|
|
|
|
|
|
- var i, il, f, face,
|
|
|
- key, index,
|
|
|
- allPointsMap = {};
|
|
|
-
|
|
|
- // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
|
|
|
+/**************************************************************
|
|
|
+ * Utils
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- var allpoints = contour.concat();
|
|
|
+THREE.Curve.Utils = {
|
|
|
|
|
|
- for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
+ tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
|
|
|
|
|
|
- Array.prototype.push.apply( allpoints, holes[h] );
|
|
|
+ return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- //console.log( "allpoints",allpoints, allpoints.length );
|
|
|
+ // Puay Bing, thanks for helping with this derivative!
|
|
|
|
|
|
- // prepare all points map
|
|
|
+ tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
|
|
|
|
|
|
- for ( i = 0, il = allpoints.length; i < il; i ++ ) {
|
|
|
+ return - 3 * p0 * (1 - t) * (1 - t) +
|
|
|
+ 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
|
|
|
+ 6 * t * p2 * (1-t) - 3 * t * t * p2 +
|
|
|
+ 3 * t * t * p3;
|
|
|
|
|
|
- key = allpoints[ i ].x + ":" + allpoints[ i ].y;
|
|
|
+ },
|
|
|
|
|
|
- if ( allPointsMap[ key ] !== undefined ) {
|
|
|
+ tangentSpline: function ( t, p0, p1, p2, p3 ) {
|
|
|
|
|
|
- console.log( "Duplicate point", key );
|
|
|
+ // To check if my formulas are correct
|
|
|
|
|
|
- }
|
|
|
+ var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
|
|
|
+ var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
|
|
|
+ var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2
|
|
|
+ var h11 = 3 * t * t - 2 * t; // t3 − t2
|
|
|
|
|
|
- allPointsMap[ key ] = i;
|
|
|
+ return h00 + h10 + h01 + h11;
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- // remove holes by cutting paths to holes and adding them to the shape
|
|
|
- var shapeWithoutHoles = removeHoles( contour, holes );
|
|
|
+ // Catmull-Rom
|
|
|
|
|
|
- var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
|
|
|
- //console.log( "triangles",triangles, triangles.length );
|
|
|
+ interpolate: function( p0, p1, p2, p3, t ) {
|
|
|
|
|
|
- // check all face vertices against all points map
|
|
|
+ var v0 = ( p2 - p0 ) * 0.5;
|
|
|
+ var v1 = ( p3 - p1 ) * 0.5;
|
|
|
+ var t2 = t * t;
|
|
|
+ var t3 = t * t2;
|
|
|
+ return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
|
|
|
|
|
|
- for ( i = 0, il = triangles.length; i < il; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- face = triangles[ i ];
|
|
|
+};
|
|
|
|
|
|
- for ( f = 0; f < 3; f ++ ) {
|
|
|
|
|
|
- key = face[ f ].x + ":" + face[ f ].y;
|
|
|
+// TODO: Transformation for Curves?
|
|
|
|
|
|
- index = allPointsMap[ key ];
|
|
|
+/**************************************************************
|
|
|
+ * 3D Curves
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- if ( index !== undefined ) {
|
|
|
+// A Factory method for creating new curve subclasses
|
|
|
|
|
|
- face[ f ] = index;
|
|
|
+THREE.Curve.create = function ( constructor, getPointFunc ) {
|
|
|
|
|
|
- }
|
|
|
+ constructor.prototype = Object.create( THREE.Curve.prototype );
|
|
|
+ constructor.prototype.getPoint = getPointFunc;
|
|
|
|
|
|
- }
|
|
|
+ return constructor;
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- return triangles.concat();
|
|
|
+// File:src/extras/core/CurvePath.js
|
|
|
|
|
|
- },
|
|
|
+/**
|
|
|
+ * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
+ *
|
|
|
+ **/
|
|
|
|
|
|
- isClockWise: function ( pts ) {
|
|
|
+/**************************************************************
|
|
|
+ * Curved Path - a curve path is simply a array of connected
|
|
|
+ * curves, but retains the api of a curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- return THREE.FontUtils.Triangulate.area( pts ) < 0;
|
|
|
+THREE.CurvePath = function () {
|
|
|
|
|
|
- },
|
|
|
+ this.curves = [];
|
|
|
+ this.bends = [];
|
|
|
+
|
|
|
+ this.autoClose = false; // Automatically closes the path
|
|
|
+};
|
|
|
|
|
|
- // Bezier Curves formulas obtained from
|
|
|
- // http://en.wikipedia.org/wiki/B%C3%A9zier_curve
|
|
|
+THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
|
|
|
|
|
|
- // Quad Bezier Functions
|
|
|
+THREE.CurvePath.prototype.add = function ( curve ) {
|
|
|
|
|
|
- b2p0: function ( t, p ) {
|
|
|
+ this.curves.push( curve );
|
|
|
|
|
|
- var k = 1 - t;
|
|
|
- return k * k * p;
|
|
|
+};
|
|
|
|
|
|
- },
|
|
|
+THREE.CurvePath.prototype.checkConnection = function() {
|
|
|
+ // TODO
|
|
|
+ // If the ending of curve is not connected to the starting
|
|
|
+ // or the next curve, then, this is not a real path
|
|
|
+};
|
|
|
|
|
|
- b2p1: function ( t, p ) {
|
|
|
+THREE.CurvePath.prototype.closePath = function() {
|
|
|
+ // TODO Test
|
|
|
+ // and verify for vector3 (needs to implement equals)
|
|
|
+ // Add a line curve if start and end of lines are not connected
|
|
|
+ var startPoint = this.curves[0].getPoint(0);
|
|
|
+ var endPoint = this.curves[this.curves.length-1].getPoint(1);
|
|
|
+
|
|
|
+ if (! startPoint.equals(endPoint)) {
|
|
|
+ this.curves.push( new THREE.LineCurve(endPoint, startPoint) );
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
|
|
|
- return 2 * ( 1 - t ) * t * p;
|
|
|
+// To get accurate point with reference to
|
|
|
+// entire path distance at time t,
|
|
|
+// following has to be done:
|
|
|
|
|
|
- },
|
|
|
+// 1. Length of each sub path have to be known
|
|
|
+// 2. Locate and identify type of curve
|
|
|
+// 3. Get t for the curve
|
|
|
+// 4. Return curve.getPointAt(t')
|
|
|
|
|
|
- b2p2: function ( t, p ) {
|
|
|
+THREE.CurvePath.prototype.getPoint = function( t ) {
|
|
|
|
|
|
- return t * t * p;
|
|
|
+ var d = t * this.getLength();
|
|
|
+ var curveLengths = this.getCurveLengths();
|
|
|
+ var i = 0, diff, curve;
|
|
|
|
|
|
- },
|
|
|
+ // To think about boundaries points.
|
|
|
|
|
|
- b2: function ( t, p0, p1, p2 ) {
|
|
|
+ while ( i < curveLengths.length ) {
|
|
|
|
|
|
- return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
|
|
|
+ if ( curveLengths[ i ] >= d ) {
|
|
|
|
|
|
- },
|
|
|
+ diff = curveLengths[ i ] - d;
|
|
|
+ curve = this.curves[ i ];
|
|
|
|
|
|
- // Cubic Bezier Functions
|
|
|
+ var u = 1 - diff / curve.getLength();
|
|
|
|
|
|
- b3p0: function ( t, p ) {
|
|
|
+ return curve.getPointAt( u );
|
|
|
|
|
|
- var k = 1 - t;
|
|
|
- return k * k * k * p;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- },
|
|
|
+ i ++;
|
|
|
|
|
|
- b3p1: function ( t, p ) {
|
|
|
+ }
|
|
|
|
|
|
- var k = 1 - t;
|
|
|
- return 3 * k * k * t * p;
|
|
|
+ return null;
|
|
|
|
|
|
- },
|
|
|
+ // loop where sum != 0, sum > d , sum+1 <d
|
|
|
|
|
|
- b3p2: function ( t, p ) {
|
|
|
+};
|
|
|
|
|
|
- var k = 1 - t;
|
|
|
- return 3 * k * t * t * p;
|
|
|
+/*
|
|
|
+THREE.CurvePath.prototype.getTangent = function( t ) {
|
|
|
+};*/
|
|
|
|
|
|
- },
|
|
|
|
|
|
- b3p3: function ( t, p ) {
|
|
|
+// We cannot use the default THREE.Curve getPoint() with getLength() because in
|
|
|
+// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
|
|
|
+// getPoint() depends on getLength
|
|
|
|
|
|
- return t * t * t * p;
|
|
|
+THREE.CurvePath.prototype.getLength = function() {
|
|
|
|
|
|
- },
|
|
|
+ var lens = this.getCurveLengths();
|
|
|
+ return lens[ lens.length - 1 ];
|
|
|
|
|
|
- b3: function ( t, p0, p1, p2, p3 ) {
|
|
|
+};
|
|
|
|
|
|
- return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 );
|
|
|
+// Compute lengths and cache them
|
|
|
+// We cannot overwrite getLengths() because UtoT mapping uses it.
|
|
|
|
|
|
- }
|
|
|
+THREE.CurvePath.prototype.getCurveLengths = function() {
|
|
|
|
|
|
-};
|
|
|
+ // We use cache values if curves and cache array are same length
|
|
|
|
|
|
+ if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) {
|
|
|
|
|
|
-// File:src/extras/curves/LineCurve.js
|
|
|
+ return this.cacheLengths;
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Line
|
|
|
- **************************************************************/
|
|
|
+ };
|
|
|
|
|
|
-THREE.LineCurve = function ( v1, v2 ) {
|
|
|
+ // Get length of subsurve
|
|
|
+ // Push sums into cached array
|
|
|
|
|
|
- this.v1 = v1;
|
|
|
- this.v2 = v2;
|
|
|
+ var lengths = [], sums = 0;
|
|
|
+ var i, il = this.curves.length;
|
|
|
|
|
|
-};
|
|
|
+ for ( i = 0; i < il; i ++ ) {
|
|
|
|
|
|
-THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
+ sums += this.curves[ i ].getLength();
|
|
|
+ lengths.push( sums );
|
|
|
|
|
|
-THREE.LineCurve.prototype.getPoint = function ( t ) {
|
|
|
+ }
|
|
|
|
|
|
- var point = this.v2.clone().sub(this.v1);
|
|
|
- point.multiplyScalar( t ).add( this.v1 );
|
|
|
+ this.cacheLengths = lengths;
|
|
|
|
|
|
- return point;
|
|
|
+ return lengths;
|
|
|
|
|
|
};
|
|
|
|
|
|
-// Line curve is linear, so we can overwrite default getPointAt
|
|
|
|
|
|
-THREE.LineCurve.prototype.getPointAt = function ( u ) {
|
|
|
|
|
|
- return this.getPoint( u );
|
|
|
+// Returns min and max coordinates
|
|
|
|
|
|
-};
|
|
|
+THREE.CurvePath.prototype.getBoundingBox = function () {
|
|
|
|
|
|
-THREE.LineCurve.prototype.getTangent = function( t ) {
|
|
|
+ var points = this.getPoints();
|
|
|
|
|
|
- var tangent = this.v2.clone().sub(this.v1);
|
|
|
+ var maxX, maxY, maxZ;
|
|
|
+ var minX, minY, minZ;
|
|
|
|
|
|
- return tangent.normalize();
|
|
|
+ maxX = maxY = Number.NEGATIVE_INFINITY;
|
|
|
+ minX = minY = Number.POSITIVE_INFINITY;
|
|
|
|
|
|
-};
|
|
|
+ var p, i, il, sum;
|
|
|
|
|
|
-// File:src/extras/curves/QuadraticBezierCurve.js
|
|
|
+ var v3 = points[0] instanceof THREE.Vector3;
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Quadratic Bezier curve
|
|
|
- **************************************************************/
|
|
|
+ sum = v3 ? new THREE.Vector3() : new THREE.Vector2();
|
|
|
|
|
|
+ for ( i = 0, il = points.length; i < il; i ++ ) {
|
|
|
|
|
|
-THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
|
|
|
+ p = points[ i ];
|
|
|
|
|
|
- this.v0 = v0;
|
|
|
- this.v1 = v1;
|
|
|
- this.v2 = v2;
|
|
|
+ if ( p.x > maxX ) maxX = p.x;
|
|
|
+ else if ( p.x < minX ) minX = p.x;
|
|
|
|
|
|
-};
|
|
|
+ if ( p.y > maxY ) maxY = p.y;
|
|
|
+ else if ( p.y < minY ) minY = p.y;
|
|
|
|
|
|
-THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
+ if ( v3 ) {
|
|
|
|
|
|
+ if ( p.z > maxZ ) maxZ = p.z;
|
|
|
+ else if ( p.z < minZ ) minZ = p.z;
|
|
|
|
|
|
-THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
|
|
|
+ }
|
|
|
|
|
|
- var vector = new THREE.Vector2();
|
|
|
+ sum.add( p );
|
|
|
|
|
|
- vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
|
|
|
- vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
|
|
|
-
|
|
|
- return vector;
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ var ret = {
|
|
|
|
|
|
+ minX: minX,
|
|
|
+ minY: minY,
|
|
|
+ maxX: maxX,
|
|
|
+ maxY: maxY
|
|
|
|
|
|
-THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
|
|
|
+ };
|
|
|
|
|
|
- var vector = new THREE.Vector2();
|
|
|
+ if ( v3 ) {
|
|
|
|
|
|
- vector.x = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
|
|
|
- vector.y = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
|
|
|
+ ret.maxZ = maxZ;
|
|
|
+ ret.minZ = minZ;
|
|
|
|
|
|
- // returns unit vector
|
|
|
+ }
|
|
|
|
|
|
- return vector.normalize();
|
|
|
+ return ret;
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/curves/CubicBezierCurve.js
|
|
|
-
|
|
|
/**************************************************************
|
|
|
- * Cubic Bezier curve
|
|
|
+ * Create Geometries Helpers
|
|
|
**************************************************************/
|
|
|
|
|
|
-THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
|
|
|
-
|
|
|
- this.v0 = v0;
|
|
|
- this.v1 = v1;
|
|
|
- this.v2 = v2;
|
|
|
- this.v3 = v3;
|
|
|
+/// Generate geometry from path points (for Line or Points objects)
|
|
|
|
|
|
-};
|
|
|
+THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {
|
|
|
|
|
|
-THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
+ var pts = this.getPoints( divisions, true );
|
|
|
+ return this.createGeometry( pts );
|
|
|
|
|
|
-THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
|
|
|
+};
|
|
|
|
|
|
- var tx, ty;
|
|
|
+// Generate geometry from equidistance sampling along the path
|
|
|
|
|
|
- tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
|
|
|
- ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
|
|
|
+THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {
|
|
|
|
|
|
- return new THREE.Vector2( tx, ty );
|
|
|
+ var pts = this.getSpacedPoints( divisions, true );
|
|
|
+ return this.createGeometry( pts );
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
|
|
|
+THREE.CurvePath.prototype.createGeometry = function( points ) {
|
|
|
|
|
|
- var tx, ty;
|
|
|
+ var geometry = new THREE.Geometry();
|
|
|
|
|
|
- tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
|
|
|
- ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
|
|
|
+ for ( var i = 0; i < points.length; i ++ ) {
|
|
|
|
|
|
- var tangent = new THREE.Vector2( tx, ty );
|
|
|
- tangent.normalize();
|
|
|
+ geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) );
|
|
|
|
|
|
- return tangent;
|
|
|
+ }
|
|
|
+
|
|
|
+ return geometry;
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/curves/SplineCurve.js
|
|
|
|
|
|
/**************************************************************
|
|
|
- * Spline curve
|
|
|
+ * Bend / Wrap Helper Methods
|
|
|
**************************************************************/
|
|
|
|
|
|
-THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
|
|
|
+// Wrap path / Bend modifiers?
|
|
|
|
|
|
- this.points = ( points == undefined ) ? [] : points;
|
|
|
+THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) {
|
|
|
+
|
|
|
+ this.bends.push( bendpath );
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
+THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) {
|
|
|
|
|
|
-THREE.SplineCurve.prototype.getPoint = function ( t ) {
|
|
|
+ var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
|
|
|
+ var i, il;
|
|
|
|
|
|
- var points = this.points;
|
|
|
- var point = ( points.length - 1 ) * t;
|
|
|
+ if ( ! bends ) {
|
|
|
|
|
|
- var intPoint = Math.floor( point );
|
|
|
- var weight = point - intPoint;
|
|
|
+ bends = this.bends;
|
|
|
|
|
|
- var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]
|
|
|
- var point1 = points[ intPoint ]
|
|
|
- var point2 = points[ intPoint > points.length - 2 ? points.length -1 : intPoint + 1 ]
|
|
|
- var point3 = points[ intPoint > points.length - 3 ? points.length -1 : intPoint + 2 ]
|
|
|
+ }
|
|
|
|
|
|
- var vector = new THREE.Vector2();
|
|
|
+ for ( i = 0, il = bends.length; i < il; i ++ ) {
|
|
|
|
|
|
- vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
|
|
|
- vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
|
|
|
+ oldPts = this.getWrapPoints( oldPts, bends[ i ] );
|
|
|
|
|
|
- return vector;
|
|
|
+ }
|
|
|
+
|
|
|
+ return oldPts;
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/curves/EllipseCurve.js
|
|
|
+THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) {
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Ellipse curve
|
|
|
- **************************************************************/
|
|
|
+ var oldPts = this.getSpacedPoints( segments );
|
|
|
|
|
|
-THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) {
|
|
|
+ var i, il;
|
|
|
|
|
|
- this.aX = aX;
|
|
|
- this.aY = aY;
|
|
|
+ if ( ! bends ) {
|
|
|
|
|
|
- this.xRadius = xRadius;
|
|
|
- this.yRadius = yRadius;
|
|
|
+ bends = this.bends;
|
|
|
|
|
|
- this.aStartAngle = aStartAngle;
|
|
|
- this.aEndAngle = aEndAngle;
|
|
|
+ }
|
|
|
|
|
|
- this.aClockwise = aClockwise;
|
|
|
+ for ( i = 0, il = bends.length; i < il; i ++ ) {
|
|
|
|
|
|
-};
|
|
|
+ oldPts = this.getWrapPoints( oldPts, bends[ i ] );
|
|
|
|
|
|
-THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
+ }
|
|
|
|
|
|
-THREE.EllipseCurve.prototype.getPoint = function ( t ) {
|
|
|
+ return oldPts;
|
|
|
|
|
|
- var deltaAngle = this.aEndAngle - this.aStartAngle;
|
|
|
+};
|
|
|
|
|
|
- if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
|
|
|
- if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
|
|
|
+// This returns getPoints() bend/wrapped around the contour of a path.
|
|
|
+// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html
|
|
|
|
|
|
- var angle;
|
|
|
+THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) {
|
|
|
|
|
|
- if ( this.aClockwise === true ) {
|
|
|
+ var bounds = this.getBoundingBox();
|
|
|
|
|
|
- angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
|
|
|
+ var i, il, p, oldX, oldY, xNorm;
|
|
|
|
|
|
- } else {
|
|
|
+ for ( i = 0, il = oldPts.length; i < il; i ++ ) {
|
|
|
|
|
|
- angle = this.aStartAngle + t * deltaAngle;
|
|
|
+ p = oldPts[ i ];
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- var vector = new THREE.Vector2();
|
|
|
+ oldX = p.x;
|
|
|
+ oldY = p.y;
|
|
|
|
|
|
- vector.x = this.aX + this.xRadius * Math.cos( angle );
|
|
|
- vector.y = this.aY + this.yRadius * Math.sin( angle );
|
|
|
+ xNorm = oldX / bounds.maxX;
|
|
|
|
|
|
- return vector;
|
|
|
+ // If using actual distance, for length > path, requires line extrusions
|
|
|
+ //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance
|
|
|
|
|
|
-};
|
|
|
+ xNorm = path.getUtoTmapping( xNorm, oldX );
|
|
|
|
|
|
-// File:src/extras/curves/ArcCurve.js
|
|
|
+ // check for out of bounds?
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Arc curve
|
|
|
- **************************************************************/
|
|
|
+ var pathPt = path.getPoint( xNorm );
|
|
|
+ var normal = path.getTangent( xNorm );
|
|
|
+ normal.set( - normal.y, normal.x ).multiplyScalar( oldY );
|
|
|
|
|
|
-THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
|
|
|
+ p.x = pathPt.x + normal.x;
|
|
|
+ p.y = pathPt.y + normal.y;
|
|
|
|
|
|
- THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
|
|
|
+ return oldPts;
|
|
|
|
|
|
-// File:src/extras/curves/LineCurve3.js
|
|
|
+};
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Line3D
|
|
|
- **************************************************************/
|
|
|
|
|
|
-THREE.LineCurve3 = THREE.Curve.create(
|
|
|
+// File:src/extras/core/Gyroscope.js
|
|
|
|
|
|
- function ( v1, v2 ) {
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ */
|
|
|
|
|
|
- this.v1 = v1;
|
|
|
- this.v2 = v2;
|
|
|
+THREE.Gyroscope = function () {
|
|
|
|
|
|
- },
|
|
|
+ THREE.Object3D.call( this );
|
|
|
|
|
|
- function ( t ) {
|
|
|
+};
|
|
|
|
|
|
- var vector = new THREE.Vector3();
|
|
|
+THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
- vector.subVectors( this.v2, this.v1 ); // diff
|
|
|
- vector.multiplyScalar( t );
|
|
|
- vector.add( this.v1 );
|
|
|
+THREE.Gyroscope.prototype.updateMatrixWorld = ( function () {
|
|
|
|
|
|
- return vector;
|
|
|
+ var translationObject = new THREE.Vector3();
|
|
|
+ var quaternionObject = new THREE.Quaternion();
|
|
|
+ var scaleObject = new THREE.Vector3();
|
|
|
|
|
|
- }
|
|
|
+ var translationWorld = new THREE.Vector3();
|
|
|
+ var quaternionWorld = new THREE.Quaternion();
|
|
|
+ var scaleWorld = new THREE.Vector3();
|
|
|
|
|
|
-);
|
|
|
+ return function ( force ) {
|
|
|
|
|
|
-// File:src/extras/curves/QuadraticBezierCurve3.js
|
|
|
+ this.matrixAutoUpdate && this.updateMatrix();
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Quadratic Bezier 3D curve
|
|
|
- **************************************************************/
|
|
|
+ // update matrixWorld
|
|
|
|
|
|
-THREE.QuadraticBezierCurve3 = THREE.Curve.create(
|
|
|
+ if ( this.matrixWorldNeedsUpdate || force ) {
|
|
|
|
|
|
- function ( v0, v1, v2 ) {
|
|
|
+ if ( this.parent ) {
|
|
|
|
|
|
- this.v0 = v0;
|
|
|
- this.v1 = v1;
|
|
|
- this.v2 = v2;
|
|
|
+ this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
|
|
|
|
|
|
- },
|
|
|
+ this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld );
|
|
|
+ this.matrix.decompose( translationObject, quaternionObject, scaleObject );
|
|
|
|
|
|
- function ( t ) {
|
|
|
+ this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld );
|
|
|
|
|
|
- var vector = new THREE.Vector3();
|
|
|
|
|
|
- vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
|
|
|
- vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
|
|
|
- vector.z = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
|
|
|
+ } else {
|
|
|
|
|
|
- return vector;
|
|
|
+ this.matrixWorld.copy( this.matrix );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
-);
|
|
|
|
|
|
-// File:src/extras/curves/CubicBezierCurve3.js
|
|
|
+ this.matrixWorldNeedsUpdate = false;
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Cubic Bezier 3D curve
|
|
|
- **************************************************************/
|
|
|
+ force = true;
|
|
|
|
|
|
-THREE.CubicBezierCurve3 = THREE.Curve.create(
|
|
|
+ }
|
|
|
|
|
|
- function ( v0, v1, v2, v3 ) {
|
|
|
+ // update children
|
|
|
|
|
|
- this.v0 = v0;
|
|
|
- this.v1 = v1;
|
|
|
- this.v2 = v2;
|
|
|
- this.v3 = v3;
|
|
|
+ for ( var i = 0, l = this.children.length; i < l; i ++ ) {
|
|
|
|
|
|
- },
|
|
|
+ this.children[ i ].updateMatrixWorld( force );
|
|
|
|
|
|
- function ( t ) {
|
|
|
+ }
|
|
|
|
|
|
- var vector = new THREE.Vector3();
|
|
|
+ };
|
|
|
+
|
|
|
+}() );
|
|
|
|
|
|
- vector.x = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
|
|
|
- vector.y = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
|
|
|
- vector.z = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
|
|
|
+// File:src/extras/core/Path.js
|
|
|
|
|
|
- return vector;
|
|
|
+/**
|
|
|
+ * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
+ * Creates free form 2d path using series of points, lines or curves.
|
|
|
+ *
|
|
|
+ **/
|
|
|
|
|
|
- }
|
|
|
+THREE.Path = function ( points ) {
|
|
|
|
|
|
-);
|
|
|
+ THREE.CurvePath.call(this);
|
|
|
|
|
|
-// File:src/extras/curves/SplineCurve3.js
|
|
|
+ this.actions = [];
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Spline 3D curve
|
|
|
- **************************************************************/
|
|
|
+ if ( points ) {
|
|
|
|
|
|
+ this.fromPoints( points );
|
|
|
|
|
|
-THREE.SplineCurve3 = THREE.Curve.create(
|
|
|
+ }
|
|
|
|
|
|
- function ( points /* array of Vector3 */) {
|
|
|
+};
|
|
|
|
|
|
- this.points = ( points == undefined ) ? [] : points;
|
|
|
+THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
|
|
|
|
|
|
- },
|
|
|
+THREE.PathActions = {
|
|
|
|
|
|
- function ( t ) {
|
|
|
+ MOVE_TO: 'moveTo',
|
|
|
+ LINE_TO: 'lineTo',
|
|
|
+ QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve
|
|
|
+ BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve
|
|
|
+ CSPLINE_THRU: 'splineThru', // Catmull-rom spline
|
|
|
+ ARC: 'arc', // Circle
|
|
|
+ ELLIPSE: 'ellipse'
|
|
|
+};
|
|
|
|
|
|
- var points = this.points;
|
|
|
- var point = ( points.length - 1 ) * t;
|
|
|
+// TODO Clean up PATH API
|
|
|
|
|
|
- var intPoint = Math.floor( point );
|
|
|
- var weight = point - intPoint;
|
|
|
+// Create path using straight lines to connect all points
|
|
|
+// - vectors: array of Vector2
|
|
|
|
|
|
- var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ];
|
|
|
- var point1 = points[ intPoint ];
|
|
|
- var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
|
|
|
- var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
|
|
|
+THREE.Path.prototype.fromPoints = function ( vectors ) {
|
|
|
|
|
|
- var vector = new THREE.Vector3();
|
|
|
+ this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
|
|
|
|
|
|
- vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
|
|
|
- vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
|
|
|
- vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight );
|
|
|
+ for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
|
|
|
|
|
|
- return vector;
|
|
|
+ this.lineTo( vectors[ v ].x, vectors[ v ].y );
|
|
|
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
-);
|
|
|
+};
|
|
|
|
|
|
-// File:src/extras/curves/ClosedSplineCurve3.js
|
|
|
+// startPath() endPath()?
|
|
|
|
|
|
-/**************************************************************
|
|
|
- * Closed Spline 3D curve
|
|
|
- **************************************************************/
|
|
|
+THREE.Path.prototype.moveTo = function ( x, y ) {
|
|
|
|
|
|
+ var args = Array.prototype.slice.call( arguments );
|
|
|
+ this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
|
|
|
|
|
|
-THREE.ClosedSplineCurve3 = THREE.Curve.create(
|
|
|
+};
|
|
|
|
|
|
- function ( points /* array of Vector3 */) {
|
|
|
+THREE.Path.prototype.lineTo = function ( x, y ) {
|
|
|
|
|
|
- this.points = ( points == undefined ) ? [] : points;
|
|
|
+ var args = Array.prototype.slice.call( arguments );
|
|
|
|
|
|
- },
|
|
|
+ var lastargs = this.actions[ this.actions.length - 1 ].args;
|
|
|
|
|
|
- function ( t ) {
|
|
|
+ var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
+ var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
|
|
|
- var points = this.points;
|
|
|
- var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1
|
|
|
+ var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
|
|
|
+ this.curves.push( curve );
|
|
|
|
|
|
- var intPoint = Math.floor( point );
|
|
|
- var weight = point - intPoint;
|
|
|
+ this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
|
|
|
|
|
|
- intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
|
|
|
+};
|
|
|
|
|
|
- var point0 = points[ ( intPoint - 1 ) % points.length ];
|
|
|
- var point1 = points[ ( intPoint ) % points.length ];
|
|
|
- var point2 = points[ ( intPoint + 1 ) % points.length ];
|
|
|
- var point3 = points[ ( intPoint + 2 ) % points.length ];
|
|
|
+THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
|
|
|
|
|
|
- var vector = new THREE.Vector3();
|
|
|
+ var args = Array.prototype.slice.call( arguments );
|
|
|
|
|
|
- vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
|
|
|
- vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
|
|
|
- vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight );
|
|
|
+ var lastargs = this.actions[ this.actions.length - 1 ].args;
|
|
|
|
|
|
- return vector;
|
|
|
+ var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
+ var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
|
|
|
- }
|
|
|
+ var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
|
|
|
+ new THREE.Vector2( aCPx, aCPy ),
|
|
|
+ new THREE.Vector2( aX, aY ) );
|
|
|
+ this.curves.push( curve );
|
|
|
|
|
|
-);
|
|
|
+ this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
|
|
|
|
|
|
-// File:src/extras/animation/AnimationHandler.js
|
|
|
+};
|
|
|
|
|
|
-/**
|
|
|
- * @author mikael emtinger / http://gomo.se/
|
|
|
- */
|
|
|
+THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
|
|
|
+ aCP2x, aCP2y,
|
|
|
+ aX, aY ) {
|
|
|
|
|
|
-THREE.AnimationHandler = {
|
|
|
+ var args = Array.prototype.slice.call( arguments );
|
|
|
|
|
|
- LINEAR: 0,
|
|
|
- CATMULLROM: 1,
|
|
|
- CATMULLROM_FORWARD: 2,
|
|
|
+ var lastargs = this.actions[ this.actions.length - 1 ].args;
|
|
|
|
|
|
- //
|
|
|
+ var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
+ var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
|
|
|
- add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); },
|
|
|
- get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); },
|
|
|
- remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); },
|
|
|
+ var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ),
|
|
|
+ new THREE.Vector2( aCP1x, aCP1y ),
|
|
|
+ new THREE.Vector2( aCP2x, aCP2y ),
|
|
|
+ new THREE.Vector2( aX, aY ) );
|
|
|
+ this.curves.push( curve );
|
|
|
|
|
|
- //
|
|
|
+ this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } );
|
|
|
|
|
|
- animations: [],
|
|
|
+};
|
|
|
|
|
|
- init: function ( data ) {
|
|
|
+THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
|
|
|
|
|
|
- if ( data.initialized === true ) return;
|
|
|
+ var args = Array.prototype.slice.call( arguments );
|
|
|
+ var lastargs = this.actions[ this.actions.length - 1 ].args;
|
|
|
|
|
|
- // loop through all keys
|
|
|
+ var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
+ var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
+//---
|
|
|
+ var npts = [ new THREE.Vector2( x0, y0 ) ];
|
|
|
+ Array.prototype.push.apply( npts, pts );
|
|
|
|
|
|
- for ( var h = 0; h < data.hierarchy.length; h ++ ) {
|
|
|
+ var curve = new THREE.SplineCurve( npts );
|
|
|
+ this.curves.push( curve );
|
|
|
|
|
|
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
+ this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
|
|
|
|
|
|
- // remove minus times
|
|
|
+};
|
|
|
|
|
|
- if ( data.hierarchy[ h ].keys[ k ].time < 0 ) {
|
|
|
+// FUTURE: Change the API or follow canvas API?
|
|
|
|
|
|
- data.hierarchy[ h ].keys[ k ].time = 0;
|
|
|
+THREE.Path.prototype.arc = function ( aX, aY, aRadius,
|
|
|
+ aStartAngle, aEndAngle, aClockwise ) {
|
|
|
|
|
|
- }
|
|
|
+ var lastargs = this.actions[ this.actions.length - 1].args;
|
|
|
+ var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
+ var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
|
|
|
- // create quaternions
|
|
|
+ this.absarc(aX + x0, aY + y0, aRadius,
|
|
|
+ aStartAngle, aEndAngle, aClockwise );
|
|
|
|
|
|
- if ( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
|
|
|
- ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
|
|
|
+ };
|
|
|
|
|
|
- var quat = data.hierarchy[ h ].keys[ k ].rot;
|
|
|
- data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat );
|
|
|
+ THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
|
|
|
+ aStartAngle, aEndAngle, aClockwise ) {
|
|
|
+ this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
|
|
|
+ };
|
|
|
|
|
|
- }
|
|
|
+THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius,
|
|
|
+ aStartAngle, aEndAngle, aClockwise ) {
|
|
|
|
|
|
- }
|
|
|
+ var lastargs = this.actions[ this.actions.length - 1].args;
|
|
|
+ var x0 = lastargs[ lastargs.length - 2 ];
|
|
|
+ var y0 = lastargs[ lastargs.length - 1 ];
|
|
|
|
|
|
- // prepare morph target keys
|
|
|
+ this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
|
|
|
+ aStartAngle, aEndAngle, aClockwise );
|
|
|
|
|
|
- if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
|
|
|
+ };
|
|
|
|
|
|
- // get all used
|
|
|
|
|
|
- var usedMorphTargets = {};
|
|
|
+THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
|
|
|
+ aStartAngle, aEndAngle, aClockwise ) {
|
|
|
|
|
|
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
+ var args = Array.prototype.slice.call( arguments );
|
|
|
+ var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
|
|
|
+ aStartAngle, aEndAngle, aClockwise );
|
|
|
+ this.curves.push( curve );
|
|
|
|
|
|
- for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
|
|
|
+ var lastPoint = curve.getPoint(1);
|
|
|
+ args.push(lastPoint.x);
|
|
|
+ args.push(lastPoint.y);
|
|
|
|
|
|
- var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
|
|
|
- usedMorphTargets[ morphTargetName ] = - 1;
|
|
|
+ this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
|
|
|
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
- }
|
|
|
+THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
|
|
|
|
|
|
- data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
|
|
|
+ if ( ! divisions ) divisions = 40;
|
|
|
|
|
|
+ var points = [];
|
|
|
|
|
|
- // set all used on all frames
|
|
|
+ for ( var i = 0; i < divisions; i ++ ) {
|
|
|
|
|
|
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
+ points.push( this.getPoint( i / divisions ) );
|
|
|
|
|
|
- var influences = {};
|
|
|
+ //if( !this.getPoint( i / divisions ) ) throw "DIE";
|
|
|
|
|
|
- for ( var morphTargetName in usedMorphTargets ) {
|
|
|
+ }
|
|
|
|
|
|
- for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
|
|
|
+ // if ( closedPath ) {
|
|
|
+ //
|
|
|
+ // points.push( points[ 0 ] );
|
|
|
+ //
|
|
|
+ // }
|
|
|
|
|
|
- if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
|
|
|
+ return points;
|
|
|
|
|
|
- influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
|
|
|
- break;
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+/* Return an array of vectors based on contour of the path */
|
|
|
|
|
|
- }
|
|
|
+THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
|
|
|
|
|
|
- if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
|
|
|
+ if (this.useSpacedPoints) {
|
|
|
+ console.log('tata');
|
|
|
+ return this.getSpacedPoints( divisions, closedPath );
|
|
|
+ }
|
|
|
|
|
|
- influences[ morphTargetName ] = 0;
|
|
|
+ divisions = divisions || 12;
|
|
|
|
|
|
- }
|
|
|
+ var points = [];
|
|
|
|
|
|
- }
|
|
|
+ var i, il, item, action, args;
|
|
|
+ var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
|
|
|
+ laste, j,
|
|
|
+ t, tx, ty;
|
|
|
|
|
|
- data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
|
|
|
+ for ( i = 0, il = this.actions.length; i < il; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ item = this.actions[ i ];
|
|
|
|
|
|
- }
|
|
|
+ action = item.action;
|
|
|
+ args = item.args;
|
|
|
|
|
|
+ switch( action ) {
|
|
|
|
|
|
- // remove all keys that are on the same time
|
|
|
-
|
|
|
- for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
-
|
|
|
- if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
|
|
|
-
|
|
|
- data.hierarchy[ h ].keys.splice( k, 1 );
|
|
|
- k --;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
+ case THREE.PathActions.MOVE_TO:
|
|
|
|
|
|
+ points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
|
|
|
|
|
|
- // set index
|
|
|
+ break;
|
|
|
|
|
|
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
+ case THREE.PathActions.LINE_TO:
|
|
|
|
|
|
- data.hierarchy[ h ].keys[ k ].index = k;
|
|
|
+ points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
|
|
|
|
|
|
- }
|
|
|
+ break;
|
|
|
|
|
|
- }
|
|
|
+ case THREE.PathActions.QUADRATIC_CURVE_TO:
|
|
|
|
|
|
- data.initialized = true;
|
|
|
+ cpx = args[ 2 ];
|
|
|
+ cpy = args[ 3 ];
|
|
|
|
|
|
- return data;
|
|
|
+ cpx1 = args[ 0 ];
|
|
|
+ cpy1 = args[ 1 ];
|
|
|
|
|
|
- },
|
|
|
+ if ( points.length > 0 ) {
|
|
|
|
|
|
- parse: function ( root ) {
|
|
|
+ laste = points[ points.length - 1 ];
|
|
|
|
|
|
- var parseRecurseHierarchy = function ( root, hierarchy ) {
|
|
|
+ cpx0 = laste.x;
|
|
|
+ cpy0 = laste.y;
|
|
|
|
|
|
- hierarchy.push( root );
|
|
|
+ } else {
|
|
|
|
|
|
- for ( var c = 0; c < root.children.length; c ++ )
|
|
|
- parseRecurseHierarchy( root.children[ c ], hierarchy );
|
|
|
+ laste = this.actions[ i - 1 ].args;
|
|
|
|
|
|
- };
|
|
|
+ cpx0 = laste[ laste.length - 2 ];
|
|
|
+ cpy0 = laste[ laste.length - 1 ];
|
|
|
|
|
|
- // setup hierarchy
|
|
|
+ }
|
|
|
|
|
|
- var hierarchy = [];
|
|
|
+ for ( j = 1; j <= divisions; j ++ ) {
|
|
|
|
|
|
- if ( root instanceof THREE.SkinnedMesh ) {
|
|
|
+ t = j / divisions;
|
|
|
|
|
|
- for ( var b = 0; b < root.skeleton.bones.length; b ++ ) {
|
|
|
+ tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
|
|
|
+ ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
|
|
|
|
|
|
- hierarchy.push( root.skeleton.bones[ b ] );
|
|
|
+ points.push( new THREE.Vector2( tx, ty ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- } else {
|
|
|
-
|
|
|
- parseRecurseHierarchy( root, hierarchy );
|
|
|
+ break;
|
|
|
|
|
|
- }
|
|
|
+ case THREE.PathActions.BEZIER_CURVE_TO:
|
|
|
|
|
|
- return hierarchy;
|
|
|
+ cpx = args[ 4 ];
|
|
|
+ cpy = args[ 5 ];
|
|
|
|
|
|
- },
|
|
|
+ cpx1 = args[ 0 ];
|
|
|
+ cpy1 = args[ 1 ];
|
|
|
|
|
|
- play: function ( animation ) {
|
|
|
+ cpx2 = args[ 2 ];
|
|
|
+ cpy2 = args[ 3 ];
|
|
|
|
|
|
- if ( this.animations.indexOf( animation ) === - 1 ) {
|
|
|
+ if ( points.length > 0 ) {
|
|
|
|
|
|
- this.animations.push( animation );
|
|
|
+ laste = points[ points.length - 1 ];
|
|
|
|
|
|
- }
|
|
|
+ cpx0 = laste.x;
|
|
|
+ cpy0 = laste.y;
|
|
|
|
|
|
- },
|
|
|
+ } else {
|
|
|
|
|
|
- stop: function ( animation ) {
|
|
|
+ laste = this.actions[ i - 1 ].args;
|
|
|
|
|
|
- var index = this.animations.indexOf( animation );
|
|
|
+ cpx0 = laste[ laste.length - 2 ];
|
|
|
+ cpy0 = laste[ laste.length - 1 ];
|
|
|
|
|
|
- if ( index !== - 1 ) {
|
|
|
+ }
|
|
|
|
|
|
- this.animations.splice( index, 1 );
|
|
|
|
|
|
- }
|
|
|
+ for ( j = 1; j <= divisions; j ++ ) {
|
|
|
|
|
|
- },
|
|
|
+ t = j / divisions;
|
|
|
|
|
|
- update: function ( deltaTimeMS ) {
|
|
|
+ tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
|
|
|
+ ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
|
|
|
|
|
|
- for ( var i = 0; i < this.animations.length; i ++ ) {
|
|
|
+ points.push( new THREE.Vector2( tx, ty ) );
|
|
|
|
|
|
- this.animations[ i ].resetBlendWeights( );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ break;
|
|
|
|
|
|
- for ( var i = 0; i < this.animations.length; i ++ ) {
|
|
|
+ case THREE.PathActions.CSPLINE_THRU:
|
|
|
|
|
|
- this.animations[ i ].update( deltaTimeMS );
|
|
|
+ laste = this.actions[ i - 1 ].args;
|
|
|
|
|
|
- }
|
|
|
+ var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
|
|
|
+ var spts = [ last ];
|
|
|
|
|
|
- }
|
|
|
+ var n = divisions * args[ 0 ].length;
|
|
|
|
|
|
-};
|
|
|
+ spts = spts.concat( args[ 0 ] );
|
|
|
|
|
|
-// File:src/extras/animation/Animation.js
|
|
|
+ var spline = new THREE.SplineCurve( spts );
|
|
|
|
|
|
-/**
|
|
|
- * @author mikael emtinger / http://gomo.se/
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- */
|
|
|
+ for ( j = 1; j <= n; j ++ ) {
|
|
|
|
|
|
-THREE.Animation = function ( root, data ) {
|
|
|
+ points.push( spline.getPointAt( j / n ) ) ;
|
|
|
|
|
|
- this.root = root;
|
|
|
- this.data = THREE.AnimationHandler.init( data );
|
|
|
- this.hierarchy = THREE.AnimationHandler.parse( root );
|
|
|
+ }
|
|
|
|
|
|
- this.currentTime = 0;
|
|
|
- this.timeScale = 1;
|
|
|
+ break;
|
|
|
|
|
|
- this.isPlaying = false;
|
|
|
- this.loop = true;
|
|
|
- this.weight = 0;
|
|
|
+ case THREE.PathActions.ARC:
|
|
|
|
|
|
- this.interpolationType = THREE.AnimationHandler.LINEAR;
|
|
|
+ var aX = args[ 0 ], aY = args[ 1 ],
|
|
|
+ aRadius = args[ 2 ],
|
|
|
+ aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
|
|
|
+ aClockwise = !! args[ 5 ];
|
|
|
|
|
|
-};
|
|
|
+ var deltaAngle = aEndAngle - aStartAngle;
|
|
|
+ var angle;
|
|
|
+ var tdivisions = divisions * 2;
|
|
|
|
|
|
+ for ( j = 1; j <= tdivisions; j ++ ) {
|
|
|
|
|
|
-THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ];
|
|
|
+ t = j / tdivisions;
|
|
|
|
|
|
+ if ( ! aClockwise ) {
|
|
|
|
|
|
-THREE.Animation.prototype.play = function ( startTime, weight ) {
|
|
|
+ t = 1 - t;
|
|
|
|
|
|
- this.currentTime = startTime !== undefined ? startTime : 0;
|
|
|
- this.weight = weight !== undefined ? weight: 1;
|
|
|
+ }
|
|
|
|
|
|
- this.isPlaying = true;
|
|
|
+ angle = aStartAngle + t * deltaAngle;
|
|
|
|
|
|
- this.reset();
|
|
|
+ tx = aX + aRadius * Math.cos( angle );
|
|
|
+ ty = aY + aRadius * Math.sin( angle );
|
|
|
|
|
|
- THREE.AnimationHandler.play( this );
|
|
|
+ //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
|
|
|
|
|
|
-};
|
|
|
+ points.push( new THREE.Vector2( tx, ty ) );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
-THREE.Animation.prototype.stop = function() {
|
|
|
+ //console.log(points);
|
|
|
|
|
|
- this.isPlaying = false;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case THREE.PathActions.ELLIPSE:
|
|
|
|
|
|
- THREE.AnimationHandler.stop( this );
|
|
|
+ var aX = args[ 0 ], aY = args[ 1 ],
|
|
|
+ xRadius = args[ 2 ],
|
|
|
+ yRadius = args[ 3 ],
|
|
|
+ aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
|
|
|
+ aClockwise = !! args[ 6 ];
|
|
|
|
|
|
-};
|
|
|
|
|
|
-THREE.Animation.prototype.reset = function () {
|
|
|
+ var deltaAngle = aEndAngle - aStartAngle;
|
|
|
+ var angle;
|
|
|
+ var tdivisions = divisions * 2;
|
|
|
|
|
|
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
+ for ( j = 1; j <= tdivisions; j ++ ) {
|
|
|
|
|
|
- var object = this.hierarchy[ h ];
|
|
|
+ t = j / tdivisions;
|
|
|
|
|
|
- object.matrixAutoUpdate = true;
|
|
|
+ if ( ! aClockwise ) {
|
|
|
|
|
|
- if ( object.animationCache === undefined ) {
|
|
|
+ t = 1 - t;
|
|
|
|
|
|
- object.animationCache = {
|
|
|
- animations: {},
|
|
|
- blending: {
|
|
|
- positionWeight: 0.0,
|
|
|
- quaternionWeight: 0.0,
|
|
|
- scaleWeight: 0.0
|
|
|
}
|
|
|
- };
|
|
|
- }
|
|
|
|
|
|
- if ( object.animationCache.animations[this.data.name] === undefined ) {
|
|
|
+ angle = aStartAngle + t * deltaAngle;
|
|
|
|
|
|
- object.animationCache.animations[this.data.name] = {};
|
|
|
- object.animationCache.animations[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 };
|
|
|
- object.animationCache.animations[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 };
|
|
|
- object.animationCache.animations[this.data.name].originalMatrix = object.matrix;
|
|
|
+ tx = aX + xRadius * Math.cos( angle );
|
|
|
+ ty = aY + yRadius * Math.sin( angle );
|
|
|
|
|
|
- }
|
|
|
+ //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
|
|
|
|
|
|
- var animationCache = object.animationCache.animations[this.data.name];
|
|
|
+ points.push( new THREE.Vector2( tx, ty ) );
|
|
|
|
|
|
- // Get keys to match our current time
|
|
|
+ }
|
|
|
|
|
|
- for ( var t = 0; t < 3; t ++ ) {
|
|
|
+ //console.log(points);
|
|
|
|
|
|
- var type = this.keyTypes[ t ];
|
|
|
+ break;
|
|
|
|
|
|
- var prevKey = this.data.hierarchy[ h ].keys[ 0 ];
|
|
|
- var nextKey = this.getNextKeyWith( type, h, 1 );
|
|
|
+ } // end switch
|
|
|
|
|
|
- while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
|
|
|
+ }
|
|
|
|
|
|
- prevKey = nextKey;
|
|
|
- nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
|
|
|
|
|
|
- }
|
|
|
|
|
|
- animationCache.prevKey[ type ] = prevKey;
|
|
|
- animationCache.nextKey[ type ] = nextKey;
|
|
|
+ // Normalize to remove the closing point by default.
|
|
|
+ var lastPoint = points[ points.length - 1];
|
|
|
+ var EPSILON = 0.0000000001;
|
|
|
+ if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON &&
|
|
|
+ Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
|
|
|
+ points.splice( points.length - 1, 1);
|
|
|
+ if ( closedPath ) {
|
|
|
|
|
|
- }
|
|
|
+ points.push( points[ 0 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ return points;
|
|
|
+
|
|
|
};
|
|
|
|
|
|
-THREE.Animation.prototype.resetBlendWeights = function () {
|
|
|
+//
|
|
|
+// Breaks path into shapes
|
|
|
+//
|
|
|
+// Assumptions (if parameter isCCW==true the opposite holds):
|
|
|
+// - solid shapes are defined clockwise (CW)
|
|
|
+// - holes are defined counterclockwise (CCW)
|
|
|
+//
|
|
|
+// If parameter noHoles==true:
|
|
|
+// - all subPaths are regarded as solid shapes
|
|
|
+// - definition order CW/CCW has no relevance
|
|
|
+//
|
|
|
|
|
|
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
+THREE.Path.prototype.toShapes = function( isCCW, noHoles ) {
|
|
|
|
|
|
- var object = this.hierarchy[ h ];
|
|
|
+ function extractSubpaths( inActions ) {
|
|
|
|
|
|
- if ( object.animationCache !== undefined ) {
|
|
|
+ var i, il, item, action, args;
|
|
|
|
|
|
- object.animationCache.blending.positionWeight = 0.0;
|
|
|
- object.animationCache.blending.quaternionWeight = 0.0;
|
|
|
- object.animationCache.blending.scaleWeight = 0.0;
|
|
|
+ var subPaths = [], lastPath = new THREE.Path();
|
|
|
|
|
|
- }
|
|
|
+ for ( i = 0, il = inActions.length; i < il; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-THREE.Animation.prototype.update = (function(){
|
|
|
-
|
|
|
- var points = [];
|
|
|
- var target = new THREE.Vector3();
|
|
|
- var newVector = new THREE.Vector3();
|
|
|
- var newQuat = new THREE.Quaternion();
|
|
|
-
|
|
|
- // Catmull-Rom spline
|
|
|
+ item = inActions[ i ];
|
|
|
|
|
|
- var interpolateCatmullRom = function ( points, scale ) {
|
|
|
+ args = item.args;
|
|
|
+ action = item.action;
|
|
|
|
|
|
- var c = [], v3 = [],
|
|
|
- point, intPoint, weight, w2, w3,
|
|
|
- pa, pb, pc, pd;
|
|
|
+ if ( action == THREE.PathActions.MOVE_TO ) {
|
|
|
|
|
|
- point = ( points.length - 1 ) * scale;
|
|
|
- intPoint = Math.floor( point );
|
|
|
- weight = point - intPoint;
|
|
|
+ if ( lastPath.actions.length != 0 ) {
|
|
|
|
|
|
- c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
|
|
|
- c[ 1 ] = intPoint;
|
|
|
- c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
|
|
|
- c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
|
|
|
+ subPaths.push( lastPath );
|
|
|
+ lastPath = new THREE.Path();
|
|
|
|
|
|
- pa = points[ c[ 0 ] ];
|
|
|
- pb = points[ c[ 1 ] ];
|
|
|
- pc = points[ c[ 2 ] ];
|
|
|
- pd = points[ c[ 3 ] ];
|
|
|
+ }
|
|
|
|
|
|
- w2 = weight * weight;
|
|
|
- w3 = weight * w2;
|
|
|
+ }
|
|
|
|
|
|
- v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
|
|
|
- v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
|
|
|
- v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
|
|
|
+ lastPath[ action ].apply( lastPath, args );
|
|
|
|
|
|
- return v3;
|
|
|
+ }
|
|
|
|
|
|
- };
|
|
|
+ if ( lastPath.actions.length != 0 ) {
|
|
|
|
|
|
- var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
|
|
|
+ subPaths.push( lastPath );
|
|
|
|
|
|
- var v0 = ( p2 - p0 ) * 0.5,
|
|
|
- v1 = ( p3 - p1 ) * 0.5;
|
|
|
+ }
|
|
|
|
|
|
- return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
|
|
|
+ // console.log(subPaths);
|
|
|
|
|
|
- };
|
|
|
+ return subPaths;
|
|
|
+ }
|
|
|
|
|
|
- return function ( delta ) {
|
|
|
+ function toShapesNoHoles( inSubpaths ) {
|
|
|
|
|
|
- if ( this.isPlaying === false ) return;
|
|
|
+ var shapes = [];
|
|
|
|
|
|
- this.currentTime += delta * this.timeScale;
|
|
|
+ for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) {
|
|
|
|
|
|
- if ( this.weight === 0 )
|
|
|
- return;
|
|
|
+ var tmpPath = inSubpaths[ i ];
|
|
|
|
|
|
- //
|
|
|
+ var tmpShape = new THREE.Shape();
|
|
|
+ tmpShape.actions = tmpPath.actions;
|
|
|
+ tmpShape.curves = tmpPath.curves;
|
|
|
|
|
|
- var duration = this.data.length;
|
|
|
+ shapes.push( tmpShape );
|
|
|
+ }
|
|
|
|
|
|
- if ( this.currentTime > duration || this.currentTime < 0 ) {
|
|
|
+ //console.log("shape", shapes);
|
|
|
|
|
|
- if ( this.loop ) {
|
|
|
+ return shapes;
|
|
|
+ };
|
|
|
|
|
|
- this.currentTime %= duration;
|
|
|
+ function isPointInsidePolygon( inPt, inPolygon ) {
|
|
|
+ var EPSILON = 0.0000000001;
|
|
|
|
|
|
- if ( this.currentTime < 0 )
|
|
|
- this.currentTime += duration;
|
|
|
+ var polyLen = inPolygon.length;
|
|
|
|
|
|
- this.reset();
|
|
|
+ // inPt on polygon contour => immediate success or
|
|
|
+ // toggling of inside/outside at every single! intersection point of an edge
|
|
|
+ // with the horizontal line through inPt, left of inPt
|
|
|
+ // not counting lowerY endpoints of edges and whole edges on that line
|
|
|
+ var inside = false;
|
|
|
+ for( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
|
|
|
+ var edgeLowPt = inPolygon[ p ];
|
|
|
+ var edgeHighPt = inPolygon[ q ];
|
|
|
|
|
|
- } else {
|
|
|
+ var edgeDx = edgeHighPt.x - edgeLowPt.x;
|
|
|
+ var edgeDy = edgeHighPt.y - edgeLowPt.y;
|
|
|
|
|
|
- this.stop();
|
|
|
- return;
|
|
|
+ if ( Math.abs(edgeDy) > EPSILON ) { // not parallel
|
|
|
+ if ( edgeDy < 0 ) {
|
|
|
+ edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
|
|
|
+ edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
|
|
|
+ }
|
|
|
+ if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
|
|
|
|
|
|
+ if ( inPt.y == edgeLowPt.y ) {
|
|
|
+ if ( inPt.x == edgeLowPt.x ) return true; // inPt is on contour ?
|
|
|
+ // continue; // no intersection or edgeLowPt => doesn't count !!!
|
|
|
+ } else {
|
|
|
+ var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y);
|
|
|
+ if ( perpEdge == 0 ) return true; // inPt is on contour ?
|
|
|
+ if ( perpEdge < 0 ) continue;
|
|
|
+ inside = ! inside; // true intersection left of inPt
|
|
|
+ }
|
|
|
+ } else { // parallel or colinear
|
|
|
+ if ( inPt.y != edgeLowPt.y ) continue; // parallel
|
|
|
+ // egde lies on the same horizontal line as inPt
|
|
|
+ if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
|
|
|
+ ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !
|
|
|
+ // continue;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
-
|
|
|
- var object = this.hierarchy[ h ];
|
|
|
- var animationCache = object.animationCache.animations[this.data.name];
|
|
|
- var blending = object.animationCache.blending;
|
|
|
-
|
|
|
- // loop through pos/rot/scl
|
|
|
-
|
|
|
- for ( var t = 0; t < 3; t ++ ) {
|
|
|
-
|
|
|
- // get keys
|
|
|
-
|
|
|
- var type = this.keyTypes[ t ];
|
|
|
- var prevKey = animationCache.prevKey[ type ];
|
|
|
- var nextKey = animationCache.nextKey[ type ];
|
|
|
+ return inside;
|
|
|
+ }
|
|
|
|
|
|
- if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) ||
|
|
|
- ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) {
|
|
|
|
|
|
- prevKey = this.data.hierarchy[ h ].keys[ 0 ];
|
|
|
- nextKey = this.getNextKeyWith( type, h, 1 );
|
|
|
+ var subPaths = extractSubpaths( this.actions );
|
|
|
+ if ( subPaths.length == 0 ) return [];
|
|
|
|
|
|
- while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
|
|
|
+ if ( noHoles === true ) return toShapesNoHoles( subPaths );
|
|
|
|
|
|
- prevKey = nextKey;
|
|
|
- nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
|
|
|
|
|
|
- }
|
|
|
+ var solid, tmpPath, tmpShape, shapes = [];
|
|
|
|
|
|
- animationCache.prevKey[ type ] = prevKey;
|
|
|
- animationCache.nextKey[ type ] = nextKey;
|
|
|
+ if ( subPaths.length == 1) {
|
|
|
|
|
|
- }
|
|
|
+ tmpPath = subPaths[0];
|
|
|
+ tmpShape = new THREE.Shape();
|
|
|
+ tmpShape.actions = tmpPath.actions;
|
|
|
+ tmpShape.curves = tmpPath.curves;
|
|
|
+ shapes.push( tmpShape );
|
|
|
+ return shapes;
|
|
|
|
|
|
- object.matrixAutoUpdate = true;
|
|
|
- object.matrixWorldNeedsUpdate = true;
|
|
|
+ }
|
|
|
|
|
|
- var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
|
|
|
+ var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
|
|
|
+ holesFirst = isCCW ? ! holesFirst : holesFirst;
|
|
|
|
|
|
- var prevXYZ = prevKey[ type ];
|
|
|
- var nextXYZ = nextKey[ type ];
|
|
|
+ // console.log("Holes first", holesFirst);
|
|
|
+
|
|
|
+ var betterShapeHoles = [];
|
|
|
+ var newShapes = [];
|
|
|
+ var newShapeHoles = [];
|
|
|
+ var mainIdx = 0;
|
|
|
+ var tmpPoints;
|
|
|
|
|
|
- if ( scale < 0 ) scale = 0;
|
|
|
- if ( scale > 1 ) scale = 1;
|
|
|
+ newShapes[mainIdx] = undefined;
|
|
|
+ newShapeHoles[mainIdx] = [];
|
|
|
|
|
|
- // interpolate
|
|
|
+ var i, il;
|
|
|
|
|
|
- if ( type === "pos" ) {
|
|
|
+ for ( i = 0, il = subPaths.length; i < il; i ++ ) {
|
|
|
|
|
|
- if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
|
|
|
+ tmpPath = subPaths[ i ];
|
|
|
+ tmpPoints = tmpPath.getPoints();
|
|
|
+ solid = THREE.Shape.Utils.isClockWise( tmpPoints );
|
|
|
+ solid = isCCW ? ! solid : solid;
|
|
|
|
|
|
- newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
|
|
|
- newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
|
|
|
- newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
|
|
|
+ if ( solid ) {
|
|
|
|
|
|
- // blend
|
|
|
- var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
|
|
|
- object.position.lerp( newVector, proportionalWeight );
|
|
|
- blending.positionWeight += this.weight;
|
|
|
+ if ( (! holesFirst ) && ( newShapes[mainIdx] ) ) mainIdx ++;
|
|
|
|
|
|
- } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
|
|
|
- this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
|
|
|
+ newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints };
|
|
|
+ newShapes[mainIdx].s.actions = tmpPath.actions;
|
|
|
+ newShapes[mainIdx].s.curves = tmpPath.curves;
|
|
|
+
|
|
|
+ if ( holesFirst ) mainIdx ++;
|
|
|
+ newShapeHoles[mainIdx] = [];
|
|
|
|
|
|
- points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
|
|
|
- points[ 1 ] = prevXYZ;
|
|
|
- points[ 2 ] = nextXYZ;
|
|
|
- points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
|
|
|
+ //console.log('cw', i);
|
|
|
|
|
|
- scale = scale * 0.33 + 0.33;
|
|
|
+ } else {
|
|
|
|
|
|
- var currentPoint = interpolateCatmullRom( points, scale );
|
|
|
- var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
|
|
|
- blending.positionWeight += this.weight;
|
|
|
+ newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } );
|
|
|
|
|
|
- // blend
|
|
|
+ //console.log('ccw', i);
|
|
|
|
|
|
- var vector = object.position;
|
|
|
-
|
|
|
- vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight;
|
|
|
- vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight;
|
|
|
- vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight;
|
|
|
+ }
|
|
|
|
|
|
- if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
|
|
|
+ }
|
|
|
|
|
|
- var forwardPoint = interpolateCatmullRom( points, scale * 1.01 );
|
|
|
+ // only Holes? -> probably all Shapes with wrong orientation
|
|
|
+ if ( ! newShapes[0] ) return toShapesNoHoles( subPaths );
|
|
|
|
|
|
- target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
|
|
|
- target.sub( vector );
|
|
|
- target.y = 0;
|
|
|
- target.normalize();
|
|
|
|
|
|
- var angle = Math.atan2( target.x, target.z );
|
|
|
- object.rotation.set( 0, angle, 0 );
|
|
|
+ if ( newShapes.length > 1 ) {
|
|
|
+ var ambigious = false;
|
|
|
+ var toChange = [];
|
|
|
|
|
|
+ for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
|
|
|
+ betterShapeHoles[sIdx] = [];
|
|
|
+ }
|
|
|
+ for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
|
|
|
+ var sh = newShapes[sIdx];
|
|
|
+ var sho = newShapeHoles[sIdx];
|
|
|
+ for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
|
|
|
+ var ho = sho[hIdx];
|
|
|
+ var hole_unassigned = true;
|
|
|
+ for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
|
|
|
+ if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) {
|
|
|
+ if ( sIdx != s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
|
|
|
+ if ( hole_unassigned ) {
|
|
|
+ hole_unassigned = false;
|
|
|
+ betterShapeHoles[s2Idx].push( ho );
|
|
|
+ } else {
|
|
|
+ ambigious = true;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
+ }
|
|
|
+ if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // console.log("ambigious: ", ambigious);
|
|
|
+ if ( toChange.length > 0 ) {
|
|
|
+ // console.log("to change: ", toChange);
|
|
|
+ if (! ambigious) newShapeHoles = betterShapeHoles;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- } else if ( type === "rot" ) {
|
|
|
-
|
|
|
- THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale );
|
|
|
-
|
|
|
- // Avoid paying the cost of an additional slerp if we don't have to
|
|
|
- if ( blending.quaternionWeight === 0 ) {
|
|
|
-
|
|
|
- object.quaternion.copy(newQuat);
|
|
|
- blending.quaternionWeight = this.weight;
|
|
|
+ var tmpHoles, j, jl;
|
|
|
+ for ( i = 0, il = newShapes.length; i < il; i ++ ) {
|
|
|
+ tmpShape = newShapes[i].s;
|
|
|
+ shapes.push( tmpShape );
|
|
|
+ tmpHoles = newShapeHoles[i];
|
|
|
+ for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
|
|
|
+ tmpShape.holes.push( tmpHoles[j].h );
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- } else {
|
|
|
+ //console.log("shape", shapes);
|
|
|
|
|
|
- var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight );
|
|
|
- THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight );
|
|
|
- blending.quaternionWeight += this.weight;
|
|
|
+ return shapes;
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- } else if ( type === "scl" ) {
|
|
|
+// File:src/extras/core/Shape.js
|
|
|
|
|
|
- newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
|
|
|
- newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
|
|
|
- newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
|
|
|
+/**
|
|
|
+ * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
+ * Defines a 2d shape plane using paths.
|
|
|
+ **/
|
|
|
|
|
|
- var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight );
|
|
|
- object.scale.lerp( newVector, proportionalWeight );
|
|
|
- blending.scaleWeight += this.weight;
|
|
|
+// STEP 1 Create a path.
|
|
|
+// STEP 2 Turn path into shape.
|
|
|
+// STEP 3 ExtrudeGeometry takes in Shape/Shapes
|
|
|
+// STEP 3a - Extract points from each shape, turn to vertices
|
|
|
+// STEP 3b - Triangulate each shape, add faces.
|
|
|
|
|
|
- }
|
|
|
+THREE.Shape = function () {
|
|
|
|
|
|
- }
|
|
|
+ THREE.Path.apply( this, arguments );
|
|
|
+ this.holes = [];
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- return true;
|
|
|
+THREE.Shape.prototype = Object.create( THREE.Path.prototype );
|
|
|
|
|
|
- };
|
|
|
+// Convenience method to return ExtrudeGeometry
|
|
|
|
|
|
-})();
|
|
|
+THREE.Shape.prototype.extrude = function ( options ) {
|
|
|
|
|
|
+ var extruded = new THREE.ExtrudeGeometry( this, options );
|
|
|
+ return extruded;
|
|
|
|
|
|
+};
|
|
|
|
|
|
+// Convenience method to return ShapeGeometry
|
|
|
|
|
|
+THREE.Shape.prototype.makeGeometry = function ( options ) {
|
|
|
|
|
|
-// Get next key with
|
|
|
+ var geometry = new THREE.ShapeGeometry( this, options );
|
|
|
+ return geometry;
|
|
|
|
|
|
-THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) {
|
|
|
+};
|
|
|
|
|
|
- var keys = this.data.hierarchy[ h ].keys;
|
|
|
+// Get points of holes
|
|
|
|
|
|
- if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
|
|
|
- this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
|
|
|
+THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
|
|
|
|
|
|
- key = key < keys.length - 1 ? key : keys.length - 1;
|
|
|
+ var i, il = this.holes.length, holesPts = [];
|
|
|
|
|
|
- } else {
|
|
|
+ for ( i = 0; i < il; i ++ ) {
|
|
|
|
|
|
- key = key % keys.length;
|
|
|
+ holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( ; key < keys.length; key ++ ) {
|
|
|
-
|
|
|
- if ( keys[ key ][ type ] !== undefined ) {
|
|
|
-
|
|
|
- return keys[ key ];
|
|
|
+ return holesPts;
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+// Get points of holes (spaced by regular distance)
|
|
|
|
|
|
- return this.data.hierarchy[ h ].keys[ 0 ];
|
|
|
+THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
|
|
|
|
|
|
-};
|
|
|
+ var i, il = this.holes.length, holesPts = [];
|
|
|
|
|
|
-// Get previous key with
|
|
|
+ for ( i = 0; i < il; i ++ ) {
|
|
|
|
|
|
-THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) {
|
|
|
+ holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
|
|
|
|
|
|
- var keys = this.data.hierarchy[ h ].keys;
|
|
|
+ }
|
|
|
|
|
|
- if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
|
|
|
- this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
|
|
|
+ return holesPts;
|
|
|
|
|
|
- key = key > 0 ? key : 0;
|
|
|
+};
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
- key = key >= 0 ? key : key + keys.length;
|
|
|
+// Get points of shape and holes (keypoints based on segments parameter)
|
|
|
|
|
|
- }
|
|
|
+THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
|
|
|
|
|
|
+ return {
|
|
|
|
|
|
- for ( ; key >= 0; key -- ) {
|
|
|
+ shape: this.getTransformedPoints( divisions ),
|
|
|
+ holes: this.getPointsHoles( divisions )
|
|
|
|
|
|
- if ( keys[ key ][ type ] !== undefined ) {
|
|
|
+ };
|
|
|
|
|
|
- return keys[ key ];
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+THREE.Shape.prototype.extractPoints = function ( divisions ) {
|
|
|
|
|
|
+ if (this.useSpacedPoints) {
|
|
|
+ return this.extractAllSpacedPoints(divisions);
|
|
|
}
|
|
|
|
|
|
- return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
|
|
|
+ return this.extractAllPoints(divisions);
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/animation/KeyFrameAnimation.js
|
|
|
-
|
|
|
-/**
|
|
|
- * @author mikael emtinger / http://gomo.se/
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- * @author khang duong
|
|
|
- * @author erik kitson
|
|
|
- */
|
|
|
-
|
|
|
-THREE.KeyFrameAnimation = function ( data ) {
|
|
|
+//
|
|
|
+// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
|
|
|
+//
|
|
|
+// return {
|
|
|
+//
|
|
|
+// shape: this.transform( bend, divisions ),
|
|
|
+// holes: this.getPointsHoles( divisions, bend )
|
|
|
+//
|
|
|
+// };
|
|
|
+//
|
|
|
+// };
|
|
|
|
|
|
- this.root = data.node;
|
|
|
- this.data = THREE.AnimationHandler.init( data );
|
|
|
- this.hierarchy = THREE.AnimationHandler.parse( this.root );
|
|
|
- this.currentTime = 0;
|
|
|
- this.timeScale = 0.001;
|
|
|
- this.isPlaying = false;
|
|
|
- this.isPaused = true;
|
|
|
- this.loop = true;
|
|
|
+// Get points of shape and holes (spaced by regular distance)
|
|
|
|
|
|
- // initialize to first keyframes
|
|
|
+THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
|
|
|
|
|
|
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
+ return {
|
|
|
|
|
|
- var keys = this.data.hierarchy[h].keys,
|
|
|
- sids = this.data.hierarchy[h].sids,
|
|
|
- obj = this.hierarchy[h];
|
|
|
+ shape: this.getTransformedSpacedPoints( divisions ),
|
|
|
+ holes: this.getSpacedPointsHoles( divisions )
|
|
|
|
|
|
- if ( keys.length && sids ) {
|
|
|
+ };
|
|
|
|
|
|
- for ( var s = 0; s < sids.length; s ++ ) {
|
|
|
+};
|
|
|
|
|
|
- var sid = sids[ s ],
|
|
|
- next = this.getNextKeyWith( sid, h, 0 );
|
|
|
+/**************************************************************
|
|
|
+ * Utils
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- if ( next ) {
|
|
|
+THREE.Shape.Utils = {
|
|
|
|
|
|
- next.apply( sid );
|
|
|
+ triangulateShape: function ( contour, holes ) {
|
|
|
|
|
|
+ function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {
|
|
|
+ // inOtherPt needs to be colinear to the inSegment
|
|
|
+ if ( inSegPt1.x != inSegPt2.x ) {
|
|
|
+ if ( inSegPt1.x < inSegPt2.x ) {
|
|
|
+ return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );
|
|
|
+ } else {
|
|
|
+ return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if ( inSegPt1.y < inSegPt2.y ) {
|
|
|
+ return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );
|
|
|
+ } else {
|
|
|
+ return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
-
|
|
|
- obj.matrixAutoUpdate = false;
|
|
|
- this.data.hierarchy[h].node.updateMatrix();
|
|
|
- obj.matrixWorldNeedsUpdate = true;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
-};
|
|
|
+ function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
|
|
|
+ var EPSILON = 0.0000000001;
|
|
|
|
|
|
+ var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
|
|
|
+ var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
|
|
|
|
|
|
-THREE.KeyFrameAnimation.prototype.play = function ( startTime ) {
|
|
|
+ var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
|
|
|
+ var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
|
|
|
|
|
|
- this.currentTime = startTime !== undefined ? startTime : 0;
|
|
|
+ var limit = seg1dy * seg2dx - seg1dx * seg2dy;
|
|
|
+ var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
|
|
|
|
|
|
- if ( this.isPlaying === false ) {
|
|
|
+ if ( Math.abs(limit) > EPSILON ) { // not parallel
|
|
|
|
|
|
- this.isPlaying = true;
|
|
|
+ var perpSeg2;
|
|
|
+ if ( limit > 0 ) {
|
|
|
+ if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return [];
|
|
|
+ perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
|
|
|
+ if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return [];
|
|
|
+ } else {
|
|
|
+ if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return [];
|
|
|
+ perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
|
|
|
+ if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return [];
|
|
|
+ }
|
|
|
|
|
|
- // reset key cache
|
|
|
+ // i.e. to reduce rounding errors
|
|
|
+ // intersection at endpoint of segment#1?
|
|
|
+ if ( perpSeg2 == 0 ) {
|
|
|
+ if ( ( inExcludeAdjacentSegs ) &&
|
|
|
+ ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return [];
|
|
|
+ return [ inSeg1Pt1 ];
|
|
|
+ }
|
|
|
+ if ( perpSeg2 == limit ) {
|
|
|
+ if ( ( inExcludeAdjacentSegs ) &&
|
|
|
+ ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return [];
|
|
|
+ return [ inSeg1Pt2 ];
|
|
|
+ }
|
|
|
+ // intersection at endpoint of segment#2?
|
|
|
+ if ( perpSeg1 == 0 ) return [ inSeg2Pt1 ];
|
|
|
+ if ( perpSeg1 == limit ) return [ inSeg2Pt2 ];
|
|
|
|
|
|
- var h, hl = this.hierarchy.length,
|
|
|
- object,
|
|
|
- node;
|
|
|
+ // return real intersection point
|
|
|
+ var factorSeg1 = perpSeg2 / limit;
|
|
|
+ return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
|
|
|
+ y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];
|
|
|
|
|
|
- for ( h = 0; h < hl; h ++ ) {
|
|
|
+ } else { // parallel or colinear
|
|
|
+ if ( ( perpSeg1 != 0 ) ||
|
|
|
+ ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) return [];
|
|
|
|
|
|
- object = this.hierarchy[ h ];
|
|
|
- node = this.data.hierarchy[ h ];
|
|
|
-
|
|
|
- if ( node.animationCache === undefined ) {
|
|
|
-
|
|
|
- node.animationCache = {};
|
|
|
- node.animationCache.prevKey = null;
|
|
|
- node.animationCache.nextKey = null;
|
|
|
- node.animationCache.originalMatrix = object.matrix;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- var keys = this.data.hierarchy[h].keys;
|
|
|
-
|
|
|
- if (keys.length) {
|
|
|
-
|
|
|
- node.animationCache.prevKey = keys[ 0 ];
|
|
|
- node.animationCache.nextKey = keys[ 1 ];
|
|
|
-
|
|
|
- this.startTime = Math.min( keys[0].time, this.startTime );
|
|
|
- this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
|
|
|
+ // they are collinear or degenerate
|
|
|
+ var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) ); // segment1 ist just a point?
|
|
|
+ var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) ); // segment2 ist just a point?
|
|
|
+ // both segments are points
|
|
|
+ if ( seg1Pt && seg2Pt ) {
|
|
|
+ if ( (inSeg1Pt1.x != inSeg2Pt1.x) ||
|
|
|
+ (inSeg1Pt1.y != inSeg2Pt1.y) ) return []; // they are distinct points
|
|
|
+ return [ inSeg1Pt1 ]; // they are the same point
|
|
|
+ }
|
|
|
+ // segment#1 is a single point
|
|
|
+ if ( seg1Pt ) {
|
|
|
+ if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2
|
|
|
+ return [ inSeg1Pt1 ];
|
|
|
+ }
|
|
|
+ // segment#2 is a single point
|
|
|
+ if ( seg2Pt ) {
|
|
|
+ if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1
|
|
|
+ return [ inSeg2Pt1 ];
|
|
|
+ }
|
|
|
|
|
|
+ // they are collinear segments, which might overlap
|
|
|
+ var seg1min, seg1max, seg1minVal, seg1maxVal;
|
|
|
+ var seg2min, seg2max, seg2minVal, seg2maxVal;
|
|
|
+ if (seg1dx != 0) { // the segments are NOT on a vertical line
|
|
|
+ if ( inSeg1Pt1.x < inSeg1Pt2.x ) {
|
|
|
+ seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
|
|
|
+ seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;
|
|
|
+ } else {
|
|
|
+ seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
|
|
|
+ seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;
|
|
|
+ }
|
|
|
+ if ( inSeg2Pt1.x < inSeg2Pt2.x ) {
|
|
|
+ seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
|
|
|
+ seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;
|
|
|
+ } else {
|
|
|
+ seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
|
|
|
+ seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;
|
|
|
+ }
|
|
|
+ } else { // the segments are on a vertical line
|
|
|
+ if ( inSeg1Pt1.y < inSeg1Pt2.y ) {
|
|
|
+ seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
|
|
|
+ seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;
|
|
|
+ } else {
|
|
|
+ seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
|
|
|
+ seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;
|
|
|
+ }
|
|
|
+ if ( inSeg2Pt1.y < inSeg2Pt2.y ) {
|
|
|
+ seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
|
|
|
+ seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;
|
|
|
+ } else {
|
|
|
+ seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
|
|
|
+ seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ( seg1minVal <= seg2minVal ) {
|
|
|
+ if ( seg1maxVal < seg2minVal ) return [];
|
|
|
+ if ( seg1maxVal == seg2minVal ) {
|
|
|
+ if ( inExcludeAdjacentSegs ) return [];
|
|
|
+ return [ seg2min ];
|
|
|
+ }
|
|
|
+ if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ];
|
|
|
+ return [ seg2min, seg2max ];
|
|
|
+ } else {
|
|
|
+ if ( seg1minVal > seg2maxVal ) return [];
|
|
|
+ if ( seg1minVal == seg2maxVal ) {
|
|
|
+ if ( inExcludeAdjacentSegs ) return [];
|
|
|
+ return [ seg1min ];
|
|
|
+ }
|
|
|
+ if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ];
|
|
|
+ return [ seg1min, seg2max ];
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- this.update( 0 );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- this.isPaused = false;
|
|
|
-
|
|
|
- THREE.AnimationHandler.play( this );
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-THREE.KeyFrameAnimation.prototype.stop = function() {
|
|
|
-
|
|
|
- this.isPlaying = false;
|
|
|
- this.isPaused = false;
|
|
|
-
|
|
|
- THREE.AnimationHandler.stop( this );
|
|
|
-
|
|
|
- // reset JIT matrix and remove cache
|
|
|
+ function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
|
|
|
+ // The order of legs is important
|
|
|
|
|
|
- for ( var h = 0; h < this.data.hierarchy.length; h ++ ) {
|
|
|
-
|
|
|
- var obj = this.hierarchy[ h ];
|
|
|
- var node = this.data.hierarchy[ h ];
|
|
|
+ var EPSILON = 0.0000000001;
|
|
|
|
|
|
- if ( node.animationCache !== undefined ) {
|
|
|
+ // translation of all points, so that Vertex is at (0,0)
|
|
|
+ var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y;
|
|
|
+ var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y;
|
|
|
+ var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y;
|
|
|
|
|
|
- var original = node.animationCache.originalMatrix;
|
|
|
+ // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
|
|
|
+ var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX;
|
|
|
+ var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX;
|
|
|
|
|
|
- original.copy( obj.matrix );
|
|
|
- obj.matrix = original;
|
|
|
+ if ( Math.abs(from2toAngle) > EPSILON ) { // angle != 180 deg.
|
|
|
|
|
|
- delete node.animationCache;
|
|
|
+ var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX;
|
|
|
+ // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
|
|
|
|
|
|
+ if ( from2toAngle > 0 ) { // main angle < 180 deg.
|
|
|
+ return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );
|
|
|
+ } else { // main angle > 180 deg.
|
|
|
+ return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );
|
|
|
+ }
|
|
|
+ } else { // angle == 180 deg.
|
|
|
+ // console.log( "from2to: 180 deg., from2other: " + from2otherAngle );
|
|
|
+ return ( from2otherAngle > 0 );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-// Update
|
|
|
-
|
|
|
-THREE.KeyFrameAnimation.prototype.update = function ( delta ) {
|
|
|
-
|
|
|
- if ( this.isPlaying === false ) return;
|
|
|
-
|
|
|
- this.currentTime += delta * this.timeScale;
|
|
|
-
|
|
|
- //
|
|
|
-
|
|
|
- var duration = this.data.length;
|
|
|
-
|
|
|
- if ( this.loop === true && this.currentTime > duration ) {
|
|
|
-
|
|
|
- this.currentTime %= duration;
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- this.currentTime = Math.min( this.currentTime, duration );
|
|
|
-
|
|
|
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
+ function removeHoles( contour, holes ) {
|
|
|
|
|
|
- var object = this.hierarchy[ h ];
|
|
|
- var node = this.data.hierarchy[ h ];
|
|
|
+ var shape = contour.concat(); // work on this shape
|
|
|
+ var hole;
|
|
|
|
|
|
- var keys = node.keys,
|
|
|
- animationCache = node.animationCache;
|
|
|
+ function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
|
|
|
+ // Check if hole point lies within angle around shape point
|
|
|
+ var lastShapeIdx = shape.length - 1;
|
|
|
|
|
|
+ var prevShapeIdx = inShapeIdx - 1;
|
|
|
+ if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx;
|
|
|
|
|
|
- if ( keys.length ) {
|
|
|
+ var nextShapeIdx = inShapeIdx + 1;
|
|
|
+ if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0;
|
|
|
|
|
|
- var prevKey = animationCache.prevKey;
|
|
|
- var nextKey = animationCache.nextKey;
|
|
|
+ var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] );
|
|
|
+ if (! insideAngle ) {
|
|
|
+ // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- if ( nextKey.time <= this.currentTime ) {
|
|
|
+ // Check if shape point lies within angle around hole point
|
|
|
+ var lastHoleIdx = hole.length - 1;
|
|
|
|
|
|
- while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
|
|
|
+ var prevHoleIdx = inHoleIdx - 1;
|
|
|
+ if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx;
|
|
|
|
|
|
- prevKey = nextKey;
|
|
|
- nextKey = keys[ prevKey.index + 1 ];
|
|
|
+ var nextHoleIdx = inHoleIdx + 1;
|
|
|
+ if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0;
|
|
|
|
|
|
+ insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] );
|
|
|
+ if (! insideAngle ) {
|
|
|
+ // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- animationCache.prevKey = prevKey;
|
|
|
- animationCache.nextKey = nextKey;
|
|
|
-
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
- if ( nextKey.time >= this.currentTime ) {
|
|
|
-
|
|
|
- prevKey.interpolate( nextKey, this.currentTime );
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- prevKey.interpolate( nextKey, nextKey.time );
|
|
|
+ function intersectsShapeEdge( inShapePt, inHolePt ) {
|
|
|
+ // checks for intersections with shape edges
|
|
|
+ var sIdx, nextIdx, intersection;
|
|
|
+ for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {
|
|
|
+ nextIdx = sIdx+1; nextIdx %= shape.length;
|
|
|
+ intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true );
|
|
|
+ if ( intersection.length > 0 ) return true;
|
|
|
+ }
|
|
|
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- this.data.hierarchy[ h ].node.updateMatrix();
|
|
|
- object.matrixWorldNeedsUpdate = true;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-// Get next key with
|
|
|
-
|
|
|
-THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) {
|
|
|
-
|
|
|
- var keys = this.data.hierarchy[ h ].keys;
|
|
|
- key = key % keys.length;
|
|
|
-
|
|
|
- for ( ; key < keys.length; key ++ ) {
|
|
|
-
|
|
|
- if ( keys[ key ].hasTarget( sid ) ) {
|
|
|
-
|
|
|
- return keys[ key ];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return keys[ 0 ];
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-// Get previous key with
|
|
|
-
|
|
|
-THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) {
|
|
|
-
|
|
|
- var keys = this.data.hierarchy[ h ].keys;
|
|
|
- key = key >= 0 ? key : key + keys.length;
|
|
|
-
|
|
|
- for ( ; key >= 0; key -- ) {
|
|
|
-
|
|
|
- if ( keys[ key ].hasTarget( sid ) ) {
|
|
|
-
|
|
|
- return keys[ key ];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return keys[ keys.length - 1 ];
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-// File:src/extras/animation/MorphAnimation.js
|
|
|
-
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com
|
|
|
- */
|
|
|
-
|
|
|
-THREE.MorphAnimation = function ( mesh ) {
|
|
|
-
|
|
|
- this.mesh = mesh;
|
|
|
- this.frames = mesh.morphTargetInfluences.length;
|
|
|
- this.currentTime = 0;
|
|
|
- this.duration = 1000;
|
|
|
- this.loop = true;
|
|
|
-
|
|
|
- this.isPlaying = false;
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-THREE.MorphAnimation.prototype = {
|
|
|
-
|
|
|
- play: function () {
|
|
|
-
|
|
|
- this.isPlaying = true;
|
|
|
+ var indepHoles = [];
|
|
|
|
|
|
- },
|
|
|
+ function intersectsHoleEdge( inShapePt, inHolePt ) {
|
|
|
+ // checks for intersections with hole edges
|
|
|
+ var ihIdx, chkHole,
|
|
|
+ hIdx, nextIdx, intersection;
|
|
|
+ for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {
|
|
|
+ chkHole = holes[indepHoles[ihIdx]];
|
|
|
+ for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {
|
|
|
+ nextIdx = hIdx+1; nextIdx %= chkHole.length;
|
|
|
+ intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true );
|
|
|
+ if ( intersection.length > 0 ) return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- pause: function () {
|
|
|
+ var holeIndex, shapeIndex,
|
|
|
+ shapePt, holePt,
|
|
|
+ holeIdx, cutKey, failedCuts = [],
|
|
|
+ tmpShape1, tmpShape2,
|
|
|
+ tmpHole1, tmpHole2;
|
|
|
|
|
|
- this.isPlaying = false;
|
|
|
+ for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
|
|
|
- },
|
|
|
+ indepHoles.push( h );
|
|
|
|
|
|
- update: ( function () {
|
|
|
+ }
|
|
|
|
|
|
- var lastFrame = 0;
|
|
|
- var currentFrame = 0;
|
|
|
+ var minShapeIndex = 0;
|
|
|
+ var counter = indepHoles.length * 2;
|
|
|
+ while ( indepHoles.length > 0 ) {
|
|
|
+ counter --;
|
|
|
+ if ( counter < 0 ) {
|
|
|
+ console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- return function ( delta ) {
|
|
|
+ // search for shape-vertex and hole-vertex,
|
|
|
+ // which can be connected without intersections
|
|
|
+ for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {
|
|
|
|
|
|
- if ( this.isPlaying === false ) return;
|
|
|
+ shapePt = shape[ shapeIndex ];
|
|
|
+ holeIndex = - 1;
|
|
|
|
|
|
- this.currentTime += delta;
|
|
|
+ // search for hole which can be reached without intersections
|
|
|
+ for ( var h = 0; h < indepHoles.length; h ++ ) {
|
|
|
+ holeIdx = indepHoles[h];
|
|
|
|
|
|
- if ( this.loop === true && this.currentTime > this.duration ) {
|
|
|
+ // prevent multiple checks
|
|
|
+ cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
|
|
|
+ if ( failedCuts[cutKey] !== undefined ) continue;
|
|
|
|
|
|
- this.currentTime %= this.duration;
|
|
|
+ hole = holes[holeIdx];
|
|
|
+ for ( var h2 = 0; h2 < hole.length; h2 ++ ) {
|
|
|
+ holePt = hole[ h2 ];
|
|
|
+ if (! isCutLineInsideAngles( shapeIndex, h2 ) ) continue;
|
|
|
+ if ( intersectsShapeEdge( shapePt, holePt ) ) continue;
|
|
|
+ if ( intersectsHoleEdge( shapePt, holePt ) ) continue;
|
|
|
|
|
|
- }
|
|
|
+ holeIndex = h2;
|
|
|
+ indepHoles.splice(h,1);
|
|
|
|
|
|
- this.currentTime = Math.min( this.currentTime, this.duration );
|
|
|
+ tmpShape1 = shape.slice( 0, shapeIndex+1 );
|
|
|
+ tmpShape2 = shape.slice( shapeIndex );
|
|
|
+ tmpHole1 = hole.slice( holeIndex );
|
|
|
+ tmpHole2 = hole.slice( 0, holeIndex+1 );
|
|
|
|
|
|
- var interpolation = this.duration / this.frames;
|
|
|
- var frame = Math.floor( this.currentTime / interpolation );
|
|
|
+ shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
|
|
|
|
|
|
- if ( frame != currentFrame ) {
|
|
|
+ minShapeIndex = shapeIndex;
|
|
|
|
|
|
- this.mesh.morphTargetInfluences[ lastFrame ] = 0;
|
|
|
- this.mesh.morphTargetInfluences[ currentFrame ] = 1;
|
|
|
- this.mesh.morphTargetInfluences[ frame ] = 0;
|
|
|
+ // Debug only, to show the selected cuts
|
|
|
+ // glob_CutLines.push( [ shapePt, holePt ] );
|
|
|
|
|
|
- lastFrame = currentFrame;
|
|
|
- currentFrame = frame;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if ( holeIndex >= 0 ) break; // hole-vertex found
|
|
|
|
|
|
+ failedCuts[cutKey] = true; // remember failure
|
|
|
+ }
|
|
|
+ if ( holeIndex >= 0 ) break; // hole-vertex found
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- this.mesh.morphTargetInfluences[ frame ] = ( this.currentTime % interpolation ) / interpolation;
|
|
|
- this.mesh.morphTargetInfluences[ lastFrame ] = 1 - this.mesh.morphTargetInfluences[ frame ];
|
|
|
-
|
|
|
+ return shape; /* shape with no holes */
|
|
|
}
|
|
|
|
|
|
- } )()
|
|
|
|
|
|
-};
|
|
|
+ var i, il, f, face,
|
|
|
+ key, index,
|
|
|
+ allPointsMap = {};
|
|
|
|
|
|
-// File:src/extras/geometries/BoxGeometry.js
|
|
|
+ // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
|
|
|
- */
|
|
|
+ var allpoints = contour.concat();
|
|
|
|
|
|
-THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
|
|
|
+ for ( var h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ Array.prototype.push.apply( allpoints, holes[h] );
|
|
|
|
|
|
- this.type = 'BoxGeometry';
|
|
|
+ }
|
|
|
|
|
|
- this.parameters = {
|
|
|
- width: width,
|
|
|
- height: height,
|
|
|
- depth: depth,
|
|
|
- widthSegments: widthSegments,
|
|
|
- heightSegments: heightSegments,
|
|
|
- depthSegments: depthSegments
|
|
|
- };
|
|
|
+ //console.log( "allpoints",allpoints, allpoints.length );
|
|
|
|
|
|
- this.widthSegments = widthSegments || 1;
|
|
|
- this.heightSegments = heightSegments || 1;
|
|
|
- this.depthSegments = depthSegments || 1;
|
|
|
+ // prepare all points map
|
|
|
|
|
|
- var scope = this;
|
|
|
+ for ( i = 0, il = allpoints.length; i < il; i ++ ) {
|
|
|
|
|
|
- var width_half = width / 2;
|
|
|
- var height_half = height / 2;
|
|
|
- var depth_half = depth / 2;
|
|
|
+ key = allpoints[ i ].x + ":" + allpoints[ i ].y;
|
|
|
|
|
|
- buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px
|
|
|
- buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx
|
|
|
- buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py
|
|
|
- buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny
|
|
|
- buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz
|
|
|
- buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz
|
|
|
+ if ( allPointsMap[ key ] !== undefined ) {
|
|
|
|
|
|
- function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
|
|
|
+ console.log( "Duplicate point", key );
|
|
|
|
|
|
- var w, ix, iy,
|
|
|
- gridX = scope.widthSegments,
|
|
|
- gridY = scope.heightSegments,
|
|
|
- width_half = width / 2,
|
|
|
- height_half = height / 2,
|
|
|
- offset = scope.vertices.length;
|
|
|
+ }
|
|
|
|
|
|
- if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
|
|
|
+ allPointsMap[ key ] = i;
|
|
|
|
|
|
- w = 'z';
|
|
|
+ }
|
|
|
|
|
|
- } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
|
|
|
+ // remove holes by cutting paths to holes and adding them to the shape
|
|
|
+ var shapeWithoutHoles = removeHoles( contour, holes );
|
|
|
|
|
|
- w = 'y';
|
|
|
- gridY = scope.depthSegments;
|
|
|
+ var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
|
|
|
+ //console.log( "triangles",triangles, triangles.length );
|
|
|
|
|
|
- } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
|
|
|
+ // check all face vertices against all points map
|
|
|
|
|
|
- w = 'x';
|
|
|
- gridX = scope.depthSegments;
|
|
|
+ for ( i = 0, il = triangles.length; i < il; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ face = triangles[ i ];
|
|
|
|
|
|
- var gridX1 = gridX + 1,
|
|
|
- gridY1 = gridY + 1,
|
|
|
- segment_width = width / gridX,
|
|
|
- segment_height = height / gridY,
|
|
|
- normal = new THREE.Vector3();
|
|
|
+ for ( f = 0; f < 3; f ++ ) {
|
|
|
|
|
|
- normal[ w ] = depth > 0 ? 1 : - 1;
|
|
|
+ key = face[ f ].x + ":" + face[ f ].y;
|
|
|
|
|
|
- for ( iy = 0; iy < gridY1; iy ++ ) {
|
|
|
+ index = allPointsMap[ key ];
|
|
|
|
|
|
- for ( ix = 0; ix < gridX1; ix ++ ) {
|
|
|
+ if ( index !== undefined ) {
|
|
|
|
|
|
- var vector = new THREE.Vector3();
|
|
|
- vector[ u ] = ( ix * segment_width - width_half ) * udir;
|
|
|
- vector[ v ] = ( iy * segment_height - height_half ) * vdir;
|
|
|
- vector[ w ] = depth;
|
|
|
+ face[ f ] = index;
|
|
|
|
|
|
- scope.vertices.push( vector );
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( iy = 0; iy < gridY; iy ++ ) {
|
|
|
+ return triangles.concat();
|
|
|
|
|
|
- for ( ix = 0; ix < gridX; ix ++ ) {
|
|
|
+ },
|
|
|
|
|
|
- var a = ix + gridX1 * iy;
|
|
|
- var b = ix + gridX1 * ( iy + 1 );
|
|
|
- var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
|
|
|
- var d = ( ix + 1 ) + gridX1 * iy;
|
|
|
+ isClockWise: function ( pts ) {
|
|
|
|
|
|
- var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY );
|
|
|
- var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY );
|
|
|
- var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY );
|
|
|
- var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY );
|
|
|
+ return THREE.FontUtils.Triangulate.area( pts ) < 0;
|
|
|
|
|
|
- var face = new THREE.Face3( a + offset, b + offset, d + offset );
|
|
|
- face.normal.copy( normal );
|
|
|
- face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
|
|
|
- face.materialIndex = materialIndex;
|
|
|
+ },
|
|
|
|
|
|
- scope.faces.push( face );
|
|
|
- scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
|
|
|
+ // Bezier Curves formulas obtained from
|
|
|
+ // http://en.wikipedia.org/wiki/B%C3%A9zier_curve
|
|
|
|
|
|
- face = new THREE.Face3( b + offset, c + offset, d + offset );
|
|
|
- face.normal.copy( normal );
|
|
|
- face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
|
|
|
- face.materialIndex = materialIndex;
|
|
|
+ // Quad Bezier Functions
|
|
|
|
|
|
- scope.faces.push( face );
|
|
|
- scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
+ b2p0: function ( t, p ) {
|
|
|
|
|
|
- }
|
|
|
+ var k = 1 - t;
|
|
|
+ return k * k * p;
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
+ b2p1: function ( t, p ) {
|
|
|
|
|
|
- this.mergeVertices();
|
|
|
+ return 2 * ( 1 - t ) * t * p;
|
|
|
|
|
|
-};
|
|
|
+ },
|
|
|
|
|
|
-THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ b2p2: function ( t, p ) {
|
|
|
|
|
|
-// File:src/extras/geometries/CircleGeometry.js
|
|
|
+ return t * t * p;
|
|
|
|
|
|
-/**
|
|
|
- * @author hughes
|
|
|
- */
|
|
|
+ },
|
|
|
|
|
|
-THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
|
|
|
+ b2: function ( t, p0, p1, p2 ) {
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
|
|
|
|
|
|
- this.type = 'CircleGeometry';
|
|
|
+ },
|
|
|
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- segments: segments,
|
|
|
- thetaStart: thetaStart,
|
|
|
- thetaLength: thetaLength
|
|
|
- };
|
|
|
+ // Cubic Bezier Functions
|
|
|
|
|
|
- radius = radius || 50;
|
|
|
- segments = segments !== undefined ? Math.max( 3, segments ) : 8;
|
|
|
+ b3p0: function ( t, p ) {
|
|
|
|
|
|
- thetaStart = thetaStart !== undefined ? thetaStart : 0;
|
|
|
- thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
|
|
|
+ var k = 1 - t;
|
|
|
+ return k * k * k * p;
|
|
|
|
|
|
- var i, uvs = [],
|
|
|
- center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 );
|
|
|
+ },
|
|
|
|
|
|
- this.vertices.push(center);
|
|
|
- uvs.push( centerUV );
|
|
|
+ b3p1: function ( t, p ) {
|
|
|
|
|
|
- for ( i = 0; i <= segments; i ++ ) {
|
|
|
+ var k = 1 - t;
|
|
|
+ return 3 * k * k * t * p;
|
|
|
|
|
|
- var vertex = new THREE.Vector3();
|
|
|
- var segment = thetaStart + i / segments * thetaLength;
|
|
|
+ },
|
|
|
|
|
|
- vertex.x = radius * Math.cos( segment );
|
|
|
- vertex.y = radius * Math.sin( segment );
|
|
|
+ b3p2: function ( t, p ) {
|
|
|
|
|
|
- this.vertices.push( vertex );
|
|
|
- uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) );
|
|
|
+ var k = 1 - t;
|
|
|
+ return 3 * k * t * t * p;
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- var n = new THREE.Vector3( 0, 0, 1 );
|
|
|
+ b3p3: function ( t, p ) {
|
|
|
|
|
|
- for ( i = 1; i <= segments; i ++ ) {
|
|
|
+ return t * t * t * p;
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] );
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
+ b3: function ( t, p0, p1, p2, p3 ) {
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
+ return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 );
|
|
|
|
|
|
- this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
|
|
|
+ }
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
-
|
|
|
-// File:src/extras/geometries/CubeGeometry.js
|
|
|
-
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
-
|
|
|
|
|
|
-THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
|
|
|
-
|
|
|
- console.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' );
|
|
|
- return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments );
|
|
|
-
|
|
|
- };
|
|
|
-
|
|
|
-// File:src/extras/geometries/CylinderGeometry.js
|
|
|
+// File:src/extras/curves/LineCurve.js
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
+/**************************************************************
|
|
|
+ * Line
|
|
|
+ **************************************************************/
|
|
|
|
|
|
-THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) {
|
|
|
+THREE.LineCurve = function ( v1, v2 ) {
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ this.v1 = v1;
|
|
|
+ this.v2 = v2;
|
|
|
|
|
|
- this.type = 'CylinderGeometry';
|
|
|
+};
|
|
|
|
|
|
- this.parameters = {
|
|
|
- radiusTop: radiusTop,
|
|
|
- radiusBottom: radiusBottom,
|
|
|
- height: height,
|
|
|
- radialSegments: radialSegments,
|
|
|
- heightSegments: heightSegments,
|
|
|
- openEnded: openEnded
|
|
|
- };
|
|
|
+THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
|
|
|
- radiusTop = radiusTop !== undefined ? radiusTop : 20;
|
|
|
- radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
|
|
|
- height = height !== undefined ? height : 100;
|
|
|
+THREE.LineCurve.prototype.getPoint = function ( t ) {
|
|
|
|
|
|
- radialSegments = radialSegments || 8;
|
|
|
- heightSegments = heightSegments || 1;
|
|
|
+ var point = this.v2.clone().sub(this.v1);
|
|
|
+ point.multiplyScalar( t ).add( this.v1 );
|
|
|
|
|
|
- openEnded = openEnded !== undefined ? openEnded : false;
|
|
|
+ return point;
|
|
|
|
|
|
- var heightHalf = height / 2;
|
|
|
+};
|
|
|
|
|
|
- var x, y, vertices = [], uvs = [];
|
|
|
+// Line curve is linear, so we can overwrite default getPointAt
|
|
|
|
|
|
- for ( y = 0; y <= heightSegments; y ++ ) {
|
|
|
+THREE.LineCurve.prototype.getPointAt = function ( u ) {
|
|
|
|
|
|
- var verticesRow = [];
|
|
|
- var uvsRow = [];
|
|
|
+ return this.getPoint( u );
|
|
|
|
|
|
- var v = y / heightSegments;
|
|
|
- var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
|
|
|
+};
|
|
|
|
|
|
- for ( x = 0; x <= radialSegments; x ++ ) {
|
|
|
+THREE.LineCurve.prototype.getTangent = function( t ) {
|
|
|
|
|
|
- var u = x / radialSegments;
|
|
|
+ var tangent = this.v2.clone().sub(this.v1);
|
|
|
|
|
|
- var vertex = new THREE.Vector3();
|
|
|
- vertex.x = radius * Math.sin( u * Math.PI * 2 );
|
|
|
- vertex.y = - v * height + heightHalf;
|
|
|
- vertex.z = radius * Math.cos( u * Math.PI * 2 );
|
|
|
+ return tangent.normalize();
|
|
|
|
|
|
- this.vertices.push( vertex );
|
|
|
+};
|
|
|
|
|
|
- verticesRow.push( this.vertices.length - 1 );
|
|
|
- uvsRow.push( new THREE.Vector2( u, 1 - v ) );
|
|
|
+// File:src/extras/curves/QuadraticBezierCurve.js
|
|
|
|
|
|
- }
|
|
|
+/**************************************************************
|
|
|
+ * Quadratic Bezier curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- vertices.push( verticesRow );
|
|
|
- uvs.push( uvsRow );
|
|
|
|
|
|
- }
|
|
|
+THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
|
|
|
|
|
|
- var tanTheta = ( radiusBottom - radiusTop ) / height;
|
|
|
- var na, nb;
|
|
|
+ this.v0 = v0;
|
|
|
+ this.v1 = v1;
|
|
|
+ this.v2 = v2;
|
|
|
|
|
|
- for ( x = 0; x < radialSegments; x ++ ) {
|
|
|
+};
|
|
|
|
|
|
- if ( radiusTop !== 0 ) {
|
|
|
+THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
|
|
|
- na = this.vertices[ vertices[ 0 ][ x ] ].clone();
|
|
|
- nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
|
|
|
|
|
|
- } else {
|
|
|
+THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
|
|
|
|
|
|
- na = this.vertices[ vertices[ 1 ][ x ] ].clone();
|
|
|
- nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
|
|
|
+ var vector = new THREE.Vector2();
|
|
|
|
|
|
- }
|
|
|
+ vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
|
|
|
+ vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
|
|
|
|
|
|
- na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize();
|
|
|
- nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize();
|
|
|
+ return vector;
|
|
|
|
|
|
- for ( y = 0; y < heightSegments; y ++ ) {
|
|
|
+};
|
|
|
|
|
|
- var v1 = vertices[ y ][ x ];
|
|
|
- var v2 = vertices[ y + 1 ][ x ];
|
|
|
- var v3 = vertices[ y + 1 ][ x + 1 ];
|
|
|
- var v4 = vertices[ y ][ x + 1 ];
|
|
|
|
|
|
- var n1 = na.clone();
|
|
|
- var n2 = na.clone();
|
|
|
- var n3 = nb.clone();
|
|
|
- var n4 = nb.clone();
|
|
|
+THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
|
|
|
|
|
|
- var uv1 = uvs[ y ][ x ].clone();
|
|
|
- var uv2 = uvs[ y + 1 ][ x ].clone();
|
|
|
- var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
|
|
|
- var uv4 = uvs[ y ][ x + 1 ].clone();
|
|
|
+ var vector = new THREE.Vector2();
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
|
|
|
+ vector.x = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
|
|
|
+ vector.y = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
|
|
|
+ // returns unit vector
|
|
|
|
|
|
- }
|
|
|
+ return vector.normalize();
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- // top cap
|
|
|
+// File:src/extras/curves/CubicBezierCurve.js
|
|
|
|
|
|
- if ( openEnded === false && radiusTop > 0 ) {
|
|
|
+/**************************************************************
|
|
|
+ * Cubic Bezier curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
|
|
|
+THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
|
|
|
|
|
|
- for ( x = 0; x < radialSegments; x ++ ) {
|
|
|
+ this.v0 = v0;
|
|
|
+ this.v1 = v1;
|
|
|
+ this.v2 = v2;
|
|
|
+ this.v3 = v3;
|
|
|
|
|
|
- var v1 = vertices[ 0 ][ x ];
|
|
|
- var v2 = vertices[ 0 ][ x + 1 ];
|
|
|
- var v3 = this.vertices.length - 1;
|
|
|
+};
|
|
|
|
|
|
- var n1 = new THREE.Vector3( 0, 1, 0 );
|
|
|
- var n2 = new THREE.Vector3( 0, 1, 0 );
|
|
|
- var n3 = new THREE.Vector3( 0, 1, 0 );
|
|
|
+THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
|
|
|
- var uv1 = uvs[ 0 ][ x ].clone();
|
|
|
- var uv2 = uvs[ 0 ][ x + 1 ].clone();
|
|
|
- var uv3 = new THREE.Vector2( uv2.x, 0 );
|
|
|
+THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
|
|
|
+ var tx, ty;
|
|
|
|
|
|
- }
|
|
|
+ tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
|
|
|
+ ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
|
|
|
|
|
|
- }
|
|
|
+ return new THREE.Vector2( tx, ty );
|
|
|
|
|
|
- // bottom cap
|
|
|
+};
|
|
|
|
|
|
- if ( openEnded === false && radiusBottom > 0 ) {
|
|
|
+THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
|
|
|
|
|
|
- this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
|
|
|
+ var tx, ty;
|
|
|
|
|
|
- for ( x = 0; x < radialSegments; x ++ ) {
|
|
|
+ tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
|
|
|
+ ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
|
|
|
|
|
|
- var v1 = vertices[ y ][ x + 1 ];
|
|
|
- var v2 = vertices[ y ][ x ];
|
|
|
- var v3 = this.vertices.length - 1;
|
|
|
+ var tangent = new THREE.Vector2( tx, ty );
|
|
|
+ tangent.normalize();
|
|
|
|
|
|
- var n1 = new THREE.Vector3( 0, - 1, 0 );
|
|
|
- var n2 = new THREE.Vector3( 0, - 1, 0 );
|
|
|
- var n3 = new THREE.Vector3( 0, - 1, 0 );
|
|
|
+ return tangent;
|
|
|
|
|
|
- var uv1 = uvs[ y ][ x + 1 ].clone();
|
|
|
- var uv2 = uvs[ y ][ x ].clone();
|
|
|
- var uv3 = new THREE.Vector2( uv2.x, 1 );
|
|
|
+};
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
|
|
|
+// File:src/extras/curves/SplineCurve.js
|
|
|
|
|
|
- }
|
|
|
+/**************************************************************
|
|
|
+ * Spline curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- }
|
|
|
+THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
+ this.points = ( points == undefined ) ? [] : points;
|
|
|
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
-THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
|
|
|
-// File:src/extras/geometries/ExtrudeGeometry.js
|
|
|
+THREE.SplineCurve.prototype.getPoint = function ( t ) {
|
|
|
|
|
|
-/**
|
|
|
- * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
- *
|
|
|
- * Creates extruded geometry from a path shape.
|
|
|
- *
|
|
|
- * parameters = {
|
|
|
- *
|
|
|
- * curveSegments: <int>, // number of points on the curves
|
|
|
- * steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too
|
|
|
- * amount: <int>, // Depth to extrude the shape
|
|
|
- *
|
|
|
- * bevelEnabled: <bool>, // turn on bevel
|
|
|
- * bevelThickness: <float>, // how deep into the original shape bevel goes
|
|
|
- * bevelSize: <float>, // how far from shape outline is bevel
|
|
|
- * bevelSegments: <int>, // number of bevel layers
|
|
|
- *
|
|
|
- * extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
|
|
|
- * frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals
|
|
|
- *
|
|
|
- * material: <int> // material index for front and back faces
|
|
|
- * extrudeMaterial: <int> // material index for extrusion and beveled faces
|
|
|
- * uvGenerator: <Object> // object that provides UV generator functions
|
|
|
- *
|
|
|
- * }
|
|
|
- **/
|
|
|
+ var points = this.points;
|
|
|
+ var point = ( points.length - 1 ) * t;
|
|
|
|
|
|
-THREE.ExtrudeGeometry = function ( shapes, options ) {
|
|
|
+ var intPoint = Math.floor( point );
|
|
|
+ var weight = point - intPoint;
|
|
|
|
|
|
- if ( typeof( shapes ) === "undefined" ) {
|
|
|
- shapes = [];
|
|
|
- return;
|
|
|
- }
|
|
|
+ var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]
|
|
|
+ var point1 = points[ intPoint ]
|
|
|
+ var point2 = points[ intPoint > points.length - 2 ? points.length -1 : intPoint + 1 ]
|
|
|
+ var point3 = points[ intPoint > points.length - 3 ? points.length -1 : intPoint + 2 ]
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ var vector = new THREE.Vector2();
|
|
|
|
|
|
- this.type = 'ExtrudeGeometry';
|
|
|
+ vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
|
|
|
+ vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
|
|
|
|
|
|
- shapes = shapes instanceof Array ? shapes : [ shapes ];
|
|
|
+ return vector;
|
|
|
|
|
|
- this.addShapeList( shapes, options );
|
|
|
+};
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
+// File:src/extras/curves/EllipseCurve.js
|
|
|
|
|
|
- // can't really use automatic vertex normals
|
|
|
- // as then front and back sides get smoothed too
|
|
|
- // should do separate smoothing just for sides
|
|
|
+/**************************************************************
|
|
|
+ * Ellipse curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- //this.computeVertexNormals();
|
|
|
+THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) {
|
|
|
|
|
|
- //console.log( "took", ( Date.now() - startTime ) );
|
|
|
+ this.aX = aX;
|
|
|
+ this.aY = aY;
|
|
|
|
|
|
-};
|
|
|
+ this.xRadius = xRadius;
|
|
|
+ this.yRadius = yRadius;
|
|
|
|
|
|
-THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ this.aStartAngle = aStartAngle;
|
|
|
+ this.aEndAngle = aEndAngle;
|
|
|
|
|
|
-THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {
|
|
|
- var sl = shapes.length;
|
|
|
+ this.aClockwise = aClockwise;
|
|
|
|
|
|
- for ( var s = 0; s < sl; s ++ ) {
|
|
|
- var shape = shapes[ s ];
|
|
|
- this.addShape( shape, options );
|
|
|
- }
|
|
|
};
|
|
|
|
|
|
-THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
|
|
|
+THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
|
|
|
|
|
|
- var amount = options.amount !== undefined ? options.amount : 100;
|
|
|
+THREE.EllipseCurve.prototype.getPoint = function ( t ) {
|
|
|
|
|
|
- var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
|
|
|
- var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
|
|
|
- var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
|
|
|
+ var deltaAngle = this.aEndAngle - this.aStartAngle;
|
|
|
|
|
|
- var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
|
|
|
+ if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
|
|
|
+ if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
|
|
|
|
|
|
- var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
|
|
|
+ var angle;
|
|
|
|
|
|
- var steps = options.steps !== undefined ? options.steps : 1;
|
|
|
+ if ( this.aClockwise === true ) {
|
|
|
|
|
|
- var extrudePath = options.extrudePath;
|
|
|
- var extrudePts, extrudeByPath = false;
|
|
|
+ angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
|
|
|
|
|
|
- var material = options.material;
|
|
|
- var extrudeMaterial = options.extrudeMaterial;
|
|
|
+ } else {
|
|
|
|
|
|
- // Use default WorldUVGenerator if no UV generators are specified.
|
|
|
- var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
|
|
|
+ angle = this.aStartAngle + t * deltaAngle;
|
|
|
|
|
|
- var splineTube, binormal, normal, position2;
|
|
|
- if ( extrudePath ) {
|
|
|
+ }
|
|
|
+
|
|
|
+ var vector = new THREE.Vector2();
|
|
|
|
|
|
- extrudePts = extrudePath.getSpacedPoints( steps );
|
|
|
+ vector.x = this.aX + this.xRadius * Math.cos( angle );
|
|
|
+ vector.y = this.aY + this.yRadius * Math.sin( angle );
|
|
|
|
|
|
- extrudeByPath = true;
|
|
|
- bevelEnabled = false; // bevels not supported for path extrusion
|
|
|
+ return vector;
|
|
|
|
|
|
- // SETUP TNB variables
|
|
|
+};
|
|
|
|
|
|
- // Reuse TNB from TubeGeomtry for now.
|
|
|
- // TODO1 - have a .isClosed in spline?
|
|
|
+// File:src/extras/curves/ArcCurve.js
|
|
|
|
|
|
- splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
|
|
|
+/**************************************************************
|
|
|
+ * Arc curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
|
|
|
+THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
|
|
|
|
|
|
- binormal = new THREE.Vector3();
|
|
|
- normal = new THREE.Vector3();
|
|
|
- position2 = new THREE.Vector3();
|
|
|
+ THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
|
|
|
|
|
|
- // Safeguards if bevels are not enabled
|
|
|
+// File:src/extras/curves/LineCurve3.js
|
|
|
|
|
|
- if ( ! bevelEnabled ) {
|
|
|
+/**************************************************************
|
|
|
+ * Line3D
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- bevelSegments = 0;
|
|
|
- bevelThickness = 0;
|
|
|
- bevelSize = 0;
|
|
|
+THREE.LineCurve3 = THREE.Curve.create(
|
|
|
|
|
|
- }
|
|
|
+ function ( v1, v2 ) {
|
|
|
|
|
|
- // Variables initalization
|
|
|
+ this.v1 = v1;
|
|
|
+ this.v2 = v2;
|
|
|
|
|
|
- var ahole, h, hl; // looping of holes
|
|
|
- var scope = this;
|
|
|
- var bevelPoints = [];
|
|
|
+ },
|
|
|
|
|
|
- var shapesOffset = this.vertices.length;
|
|
|
+ function ( t ) {
|
|
|
|
|
|
- var shapePoints = shape.extractPoints( curveSegments );
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
|
|
|
- var vertices = shapePoints.shape;
|
|
|
- var holes = shapePoints.holes;
|
|
|
+ vector.subVectors( this.v2, this.v1 ); // diff
|
|
|
+ vector.multiplyScalar( t );
|
|
|
+ vector.add( this.v1 );
|
|
|
|
|
|
- var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ;
|
|
|
+ return vector;
|
|
|
|
|
|
- if ( reverse ) {
|
|
|
+ }
|
|
|
|
|
|
- vertices = vertices.reverse();
|
|
|
+);
|
|
|
|
|
|
- // Maybe we should also check if holes are in the opposite direction, just to be safe ...
|
|
|
+// File:src/extras/curves/QuadraticBezierCurve3.js
|
|
|
|
|
|
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
+/**************************************************************
|
|
|
+ * Quadratic Bezier 3D curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- ahole = holes[ h ];
|
|
|
+THREE.QuadraticBezierCurve3 = THREE.Curve.create(
|
|
|
|
|
|
- if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
|
|
|
+ function ( v0, v1, v2 ) {
|
|
|
|
|
|
- holes[ h ] = ahole.reverse();
|
|
|
+ this.v0 = v0;
|
|
|
+ this.v1 = v1;
|
|
|
+ this.v2 = v2;
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
+ function ( t ) {
|
|
|
|
|
|
- reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
|
|
|
- }
|
|
|
+ vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
|
|
|
+ vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
|
|
|
+ vector.z = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
|
|
|
|
|
|
+ return vector;
|
|
|
|
|
|
- var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
|
|
|
+ }
|
|
|
|
|
|
- /* Vertices */
|
|
|
+);
|
|
|
|
|
|
- var contour = vertices; // vertices has all points but contour has only points of circumference
|
|
|
+// File:src/extras/curves/CubicBezierCurve3.js
|
|
|
|
|
|
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
+/**************************************************************
|
|
|
+ * Cubic Bezier 3D curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- ahole = holes[ h ];
|
|
|
+THREE.CubicBezierCurve3 = THREE.Curve.create(
|
|
|
|
|
|
- vertices = vertices.concat( ahole );
|
|
|
+ function ( v0, v1, v2, v3 ) {
|
|
|
|
|
|
- }
|
|
|
+ this.v0 = v0;
|
|
|
+ this.v1 = v1;
|
|
|
+ this.v2 = v2;
|
|
|
+ this.v3 = v3;
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- function scalePt2 ( pt, vec, size ) {
|
|
|
+ function ( t ) {
|
|
|
|
|
|
- if ( ! vec ) console.log( "die" );
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
|
|
|
- return vec.clone().multiplyScalar( size ).add( pt );
|
|
|
+ vector.x = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
|
|
|
+ vector.y = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
|
|
|
+ vector.z = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
|
|
|
+
|
|
|
+ return vector;
|
|
|
|
|
|
}
|
|
|
|
|
|
- var b, bs, t, z,
|
|
|
- vert, vlen = vertices.length,
|
|
|
- face, flen = faces.length,
|
|
|
- cont, clen = contour.length;
|
|
|
+);
|
|
|
|
|
|
+// File:src/extras/curves/SplineCurve3.js
|
|
|
|
|
|
- // Find directions for point movement
|
|
|
+/**************************************************************
|
|
|
+ * Spline 3D curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- var RAD_TO_DEGREES = 180 / Math.PI;
|
|
|
|
|
|
+THREE.SplineCurve3 = THREE.Curve.create(
|
|
|
|
|
|
- function getBevelVec( inPt, inPrev, inNext ) {
|
|
|
+ function ( points /* array of Vector3 */) {
|
|
|
|
|
|
- var EPSILON = 0.0000000001;
|
|
|
-
|
|
|
- // computes for inPt the corresponding point inPt' on a new contour
|
|
|
- // shiftet by 1 unit (length of normalized vector) to the left
|
|
|
- // if we walk along contour clockwise, this new contour is outside the old one
|
|
|
- //
|
|
|
- // inPt' is the intersection of the two lines parallel to the two
|
|
|
- // adjacent edges of inPt at a distance of 1 unit on the left side.
|
|
|
-
|
|
|
- var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt
|
|
|
+ this.points = ( points == undefined ) ? [] : points;
|
|
|
|
|
|
- // good reading for geometry algorithms (here: line-line intersection)
|
|
|
- // http://geomalgorithms.com/a05-_intersect-1.html
|
|
|
+ },
|
|
|
|
|
|
- var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y;
|
|
|
- var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y;
|
|
|
-
|
|
|
- var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
|
|
|
-
|
|
|
- // check for colinear edges
|
|
|
- var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
|
|
|
-
|
|
|
- if ( Math.abs( colinear0 ) > EPSILON ) { // not colinear
|
|
|
-
|
|
|
- // length of vectors for normalizing
|
|
|
-
|
|
|
- var v_prev_len = Math.sqrt( v_prev_lensq );
|
|
|
- var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
|
|
|
-
|
|
|
- // shift adjacent points by unit vectors to the left
|
|
|
-
|
|
|
- var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
|
|
|
- var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
|
|
|
-
|
|
|
- var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
|
|
|
- var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
|
|
|
-
|
|
|
- // scaling factor for v_prev to intersection point
|
|
|
-
|
|
|
- var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
|
|
|
- ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /
|
|
|
- ( v_prev_x * v_next_y - v_prev_y * v_next_x );
|
|
|
-
|
|
|
- // vector from inPt to intersection point
|
|
|
-
|
|
|
- v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
|
|
|
- v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
|
|
|
-
|
|
|
- // Don't normalize!, otherwise sharp corners become ugly
|
|
|
- // but prevent crazy spikes
|
|
|
- var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y )
|
|
|
- if ( v_trans_lensq <= 2 ) {
|
|
|
- return new THREE.Vector2( v_trans_x, v_trans_y );
|
|
|
- } else {
|
|
|
- shrink_by = Math.sqrt( v_trans_lensq / 2 );
|
|
|
- }
|
|
|
-
|
|
|
- } else { // handle special case of colinear edges
|
|
|
+ function ( t ) {
|
|
|
|
|
|
- var direction_eq = false; // assumes: opposite
|
|
|
- if ( v_prev_x > EPSILON ) {
|
|
|
- if ( v_next_x > EPSILON ) { direction_eq = true; }
|
|
|
- } else {
|
|
|
- if ( v_prev_x < - EPSILON ) {
|
|
|
- if ( v_next_x < - EPSILON ) { direction_eq = true; }
|
|
|
- } else {
|
|
|
- if ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; }
|
|
|
- }
|
|
|
- }
|
|
|
+ var points = this.points;
|
|
|
+ var point = ( points.length - 1 ) * t;
|
|
|
|
|
|
- if ( direction_eq ) {
|
|
|
- // console.log("Warning: lines are a straight sequence");
|
|
|
- v_trans_x = - v_prev_y;
|
|
|
- v_trans_y = v_prev_x;
|
|
|
- shrink_by = Math.sqrt( v_prev_lensq );
|
|
|
- } else {
|
|
|
- // console.log("Warning: lines are a straight spike");
|
|
|
- v_trans_x = v_prev_x;
|
|
|
- v_trans_y = v_prev_y;
|
|
|
- shrink_by = Math.sqrt( v_prev_lensq / 2 );
|
|
|
- }
|
|
|
+ var intPoint = Math.floor( point );
|
|
|
+ var weight = point - intPoint;
|
|
|
|
|
|
- }
|
|
|
+ var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ];
|
|
|
+ var point1 = points[ intPoint ];
|
|
|
+ var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
|
|
|
+ var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
|
|
|
+
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
+
|
|
|
+ vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
|
|
|
+ vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
|
|
|
+ vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight );
|
|
|
|
|
|
- return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
|
|
|
+ return vector;
|
|
|
|
|
|
}
|
|
|
|
|
|
+);
|
|
|
|
|
|
- var contourMovements = [];
|
|
|
-
|
|
|
- for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
|
|
|
+// File:src/extras/curves/ClosedSplineCurve3.js
|
|
|
|
|
|
- if ( j === il ) j = 0;
|
|
|
- if ( k === il ) k = 0;
|
|
|
+/**************************************************************
|
|
|
+ * Closed Spline 3D curve
|
|
|
+ **************************************************************/
|
|
|
|
|
|
- // (j)---(i)---(k)
|
|
|
- // console.log('i,j,k', i, j , k)
|
|
|
|
|
|
- var pt_i = contour[ i ];
|
|
|
- var pt_j = contour[ j ];
|
|
|
- var pt_k = contour[ k ];
|
|
|
+THREE.ClosedSplineCurve3 = THREE.Curve.create(
|
|
|
|
|
|
- contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
|
|
|
+ function ( points /* array of Vector3 */) {
|
|
|
|
|
|
- }
|
|
|
+ this.points = ( points == undefined ) ? [] : points;
|
|
|
|
|
|
- var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
|
|
|
+ },
|
|
|
|
|
|
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
+ function ( t ) {
|
|
|
|
|
|
- ahole = holes[ h ];
|
|
|
+ var points = this.points;
|
|
|
+ var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1
|
|
|
|
|
|
- oneHoleMovements = [];
|
|
|
+ var intPoint = Math.floor( point );
|
|
|
+ var weight = point - intPoint;
|
|
|
|
|
|
- for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
|
|
|
+ intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
|
|
|
|
|
|
- if ( j === il ) j = 0;
|
|
|
- if ( k === il ) k = 0;
|
|
|
+ var point0 = points[ ( intPoint - 1 ) % points.length ];
|
|
|
+ var point1 = points[ ( intPoint ) % points.length ];
|
|
|
+ var point2 = points[ ( intPoint + 1 ) % points.length ];
|
|
|
+ var point3 = points[ ( intPoint + 2 ) % points.length ];
|
|
|
|
|
|
- // (j)---(i)---(k)
|
|
|
- oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
|
|
|
- }
|
|
|
+ vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight );
|
|
|
+ vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight );
|
|
|
+ vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight );
|
|
|
|
|
|
- holesMovements.push( oneHoleMovements );
|
|
|
- verticesMovements = verticesMovements.concat( oneHoleMovements );
|
|
|
+ return vector;
|
|
|
|
|
|
}
|
|
|
|
|
|
+);
|
|
|
|
|
|
- // Loop bevelSegments, 1 for the front, 1 for the back
|
|
|
-
|
|
|
- for ( b = 0; b < bevelSegments; b ++ ) {
|
|
|
- //for ( b = bevelSegments; b > 0; b -- ) {
|
|
|
-
|
|
|
- t = b / bevelSegments;
|
|
|
- z = bevelThickness * ( 1 - t );
|
|
|
-
|
|
|
- //z = bevelThickness * t;
|
|
|
- bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
|
|
|
- //bs = bevelSize * t ; // linear
|
|
|
-
|
|
|
- // contract shape
|
|
|
+// File:src/extras/animation/AnimationHandler.js
|
|
|
|
|
|
- for ( i = 0, il = contour.length; i < il; i ++ ) {
|
|
|
+/**
|
|
|
+ * @author mikael emtinger / http://gomo.se/
|
|
|
+ */
|
|
|
|
|
|
- vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
|
|
|
+THREE.AnimationHandler = {
|
|
|
|
|
|
- v( vert.x, vert.y, - z );
|
|
|
+ LINEAR: 0,
|
|
|
+ CATMULLROM: 1,
|
|
|
+ CATMULLROM_FORWARD: 2,
|
|
|
|
|
|
- }
|
|
|
+ //
|
|
|
|
|
|
- // expand holes
|
|
|
+ add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); },
|
|
|
+ get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); },
|
|
|
+ remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); },
|
|
|
|
|
|
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
+ //
|
|
|
|
|
|
- ahole = holes[ h ];
|
|
|
- oneHoleMovements = holesMovements[ h ];
|
|
|
+ animations: [],
|
|
|
|
|
|
- for ( i = 0, il = ahole.length; i < il; i ++ ) {
|
|
|
+ init: function ( data ) {
|
|
|
|
|
|
- vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
|
|
|
+ if ( data.initialized === true ) return;
|
|
|
|
|
|
- v( vert.x, vert.y, - z );
|
|
|
+ // loop through all keys
|
|
|
|
|
|
- }
|
|
|
+ for ( var h = 0; h < data.hierarchy.length; h ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ // remove minus times
|
|
|
|
|
|
- bs = bevelSize;
|
|
|
+ if ( data.hierarchy[ h ].keys[ k ].time < 0 ) {
|
|
|
|
|
|
- // Back facing vertices
|
|
|
+ data.hierarchy[ h ].keys[ k ].time = 0;
|
|
|
|
|
|
- for ( i = 0; i < vlen; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
|
|
|
+ // create quaternions
|
|
|
|
|
|
- if ( ! extrudeByPath ) {
|
|
|
+ if ( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
|
|
|
+ ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
|
|
|
|
|
|
- v( vert.x, vert.y, 0 );
|
|
|
+ var quat = data.hierarchy[ h ].keys[ k ].rot;
|
|
|
+ data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat );
|
|
|
|
|
|
- } else {
|
|
|
+ }
|
|
|
|
|
|
- // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
|
|
|
+ }
|
|
|
|
|
|
- normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
|
|
|
- binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
|
|
|
+ // prepare morph target keys
|
|
|
|
|
|
- position2.copy( extrudePts[0] ).add(normal).add(binormal);
|
|
|
+ if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
|
|
|
|
|
|
- v( position2.x, position2.y, position2.z );
|
|
|
+ // get all used
|
|
|
|
|
|
- }
|
|
|
+ var usedMorphTargets = {};
|
|
|
|
|
|
- }
|
|
|
+ for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
|
|
|
- // Add stepped vertices...
|
|
|
- // Including front facing vertices
|
|
|
+ for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
|
|
|
|
|
|
- var s;
|
|
|
+ var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
|
|
|
+ usedMorphTargets[ morphTargetName ] = - 1;
|
|
|
|
|
|
- for ( s = 1; s <= steps; s ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- for ( i = 0; i < vlen; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
|
|
|
+ data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
|
|
|
|
|
|
- if ( ! extrudeByPath ) {
|
|
|
|
|
|
- v( vert.x, vert.y, amount / steps * s );
|
|
|
+ // set all used on all frames
|
|
|
|
|
|
- } else {
|
|
|
+ for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
|
|
|
- // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
|
|
|
+ var influences = {};
|
|
|
|
|
|
- normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x );
|
|
|
- binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y );
|
|
|
+ for ( var morphTargetName in usedMorphTargets ) {
|
|
|
|
|
|
- position2.copy( extrudePts[s] ).add( normal ).add( binormal );
|
|
|
+ for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
|
|
|
|
|
|
- v( position2.x, position2.y, position2.z );
|
|
|
+ if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
|
|
|
|
|
|
- }
|
|
|
+ influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
|
|
|
+ break;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
+ if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
|
|
|
|
|
|
- // Add bevel segments planes
|
|
|
+ influences[ morphTargetName ] = 0;
|
|
|
|
|
|
- //for ( b = 1; b <= bevelSegments; b ++ ) {
|
|
|
- for ( b = bevelSegments - 1; b >= 0; b -- ) {
|
|
|
+ }
|
|
|
|
|
|
- t = b / bevelSegments;
|
|
|
- z = bevelThickness * ( 1 - t );
|
|
|
- //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
|
|
|
- bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
|
|
|
+ }
|
|
|
|
|
|
- // contract shape
|
|
|
+ data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
|
|
|
|
|
|
- for ( i = 0, il = contour.length; i < il; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
|
|
|
- v( vert.x, vert.y, amount + z );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
- // expand holes
|
|
|
+ // remove all keys that are on the same time
|
|
|
|
|
|
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
+ for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
|
|
|
- ahole = holes[ h ];
|
|
|
- oneHoleMovements = holesMovements[ h ];
|
|
|
+ if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
|
|
|
|
|
|
- for ( i = 0, il = ahole.length; i < il; i ++ ) {
|
|
|
+ data.hierarchy[ h ].keys.splice( k, 1 );
|
|
|
+ k --;
|
|
|
|
|
|
- vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
|
|
|
+ }
|
|
|
|
|
|
- if ( ! extrudeByPath ) {
|
|
|
+ }
|
|
|
|
|
|
- v( vert.x, vert.y, amount + z );
|
|
|
|
|
|
- } else {
|
|
|
+ // set index
|
|
|
|
|
|
- v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
|
|
|
+ for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ data.hierarchy[ h ].keys[ k ].index = k;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- /* Faces */
|
|
|
+ data.initialized = true;
|
|
|
|
|
|
- // Top and bottom faces
|
|
|
+ return data;
|
|
|
|
|
|
- buildLidFaces();
|
|
|
+ },
|
|
|
|
|
|
- // Sides faces
|
|
|
+ parse: function ( root ) {
|
|
|
|
|
|
- buildSideFaces();
|
|
|
+ var parseRecurseHierarchy = function ( root, hierarchy ) {
|
|
|
|
|
|
+ hierarchy.push( root );
|
|
|
|
|
|
- ///// Internal functions
|
|
|
+ for ( var c = 0; c < root.children.length; c ++ )
|
|
|
+ parseRecurseHierarchy( root.children[ c ], hierarchy );
|
|
|
|
|
|
- function buildLidFaces() {
|
|
|
+ };
|
|
|
|
|
|
- if ( bevelEnabled ) {
|
|
|
+ // setup hierarchy
|
|
|
|
|
|
- var layer = 0 ; // steps + 1
|
|
|
- var offset = vlen * layer;
|
|
|
+ var hierarchy = [];
|
|
|
|
|
|
- // Bottom faces
|
|
|
+ if ( root instanceof THREE.SkinnedMesh ) {
|
|
|
|
|
|
- for ( i = 0; i < flen; i ++ ) {
|
|
|
+ for ( var b = 0; b < root.skeleton.bones.length; b ++ ) {
|
|
|
|
|
|
- face = faces[ i ];
|
|
|
- f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset );
|
|
|
+ hierarchy.push( root.skeleton.bones[ b ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- layer = steps + bevelSegments * 2;
|
|
|
- offset = vlen * layer;
|
|
|
+ } else {
|
|
|
|
|
|
- // Top faces
|
|
|
+ parseRecurseHierarchy( root, hierarchy );
|
|
|
|
|
|
- for ( i = 0; i < flen; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- face = faces[ i ];
|
|
|
- f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
|
|
|
+ return hierarchy;
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- } else {
|
|
|
+ play: function ( animation ) {
|
|
|
|
|
|
- // Bottom faces
|
|
|
+ if ( this.animations.indexOf( animation ) === - 1 ) {
|
|
|
|
|
|
- for ( i = 0; i < flen; i ++ ) {
|
|
|
+ this.animations.push( animation );
|
|
|
|
|
|
- face = faces[ i ];
|
|
|
- f3( face[ 2 ], face[ 1 ], face[ 0 ] );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- // Top faces
|
|
|
+ stop: function ( animation ) {
|
|
|
+
|
|
|
+ var index = this.animations.indexOf( animation );
|
|
|
|
|
|
- for ( i = 0; i < flen; i ++ ) {
|
|
|
+ if ( index !== - 1 ) {
|
|
|
|
|
|
- face = faces[ i ];
|
|
|
- f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
|
|
|
+ this.animations.splice( index, 1 );
|
|
|
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
- // Create faces for the z-sides of the shape
|
|
|
+ update: function ( deltaTimeMS ) {
|
|
|
|
|
|
- function buildSideFaces() {
|
|
|
+ for ( var i = 0; i < this.animations.length; i ++ ) {
|
|
|
|
|
|
- var layeroffset = 0;
|
|
|
- sidewalls( contour, layeroffset );
|
|
|
- layeroffset += contour.length;
|
|
|
+ this.animations[ i ].resetBlendWeights( );
|
|
|
|
|
|
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- ahole = holes[ h ];
|
|
|
- sidewalls( ahole, layeroffset );
|
|
|
+ for ( var i = 0; i < this.animations.length; i ++ ) {
|
|
|
|
|
|
- //, true
|
|
|
- layeroffset += ahole.length;
|
|
|
+ this.animations[ i ].update( deltaTimeMS );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- function sidewalls( contour, layeroffset ) {
|
|
|
-
|
|
|
- var j, k;
|
|
|
- i = contour.length;
|
|
|
-
|
|
|
- while ( --i >= 0 ) {
|
|
|
+};
|
|
|
|
|
|
- j = i;
|
|
|
- k = i - 1;
|
|
|
- if ( k < 0 ) k = contour.length - 1;
|
|
|
+// File:src/extras/animation/Animation.js
|
|
|
|
|
|
- //console.log('b', i,j, i-1, k,vertices.length);
|
|
|
+/**
|
|
|
+ * @author mikael emtinger / http://gomo.se/
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ */
|
|
|
|
|
|
- var s = 0, sl = steps + bevelSegments * 2;
|
|
|
+THREE.Animation = function ( root, data ) {
|
|
|
|
|
|
- for ( s = 0; s < sl; s ++ ) {
|
|
|
+ this.root = root;
|
|
|
+ this.data = THREE.AnimationHandler.init( data );
|
|
|
+ this.hierarchy = THREE.AnimationHandler.parse( root );
|
|
|
|
|
|
- var slen1 = vlen * s;
|
|
|
- var slen2 = vlen * ( s + 1 );
|
|
|
+ this.currentTime = 0;
|
|
|
+ this.timeScale = 1;
|
|
|
|
|
|
- var a = layeroffset + j + slen1,
|
|
|
- b = layeroffset + k + slen1,
|
|
|
- c = layeroffset + k + slen2,
|
|
|
- d = layeroffset + j + slen2;
|
|
|
+ this.isPlaying = false;
|
|
|
+ this.loop = true;
|
|
|
+ this.weight = 0;
|
|
|
|
|
|
- f4( a, b, c, d, contour, s, sl, j, k );
|
|
|
+ this.interpolationType = THREE.AnimationHandler.LINEAR;
|
|
|
|
|
|
- }
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
|
|
|
+THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ];
|
|
|
|
|
|
- function v( x, y, z ) {
|
|
|
|
|
|
- scope.vertices.push( new THREE.Vector3( x, y, z ) );
|
|
|
+THREE.Animation.prototype.play = function ( startTime, weight ) {
|
|
|
|
|
|
- }
|
|
|
+ this.currentTime = startTime !== undefined ? startTime : 0;
|
|
|
+ this.weight = weight !== undefined ? weight: 1;
|
|
|
|
|
|
- function f3( a, b, c ) {
|
|
|
+ this.isPlaying = true;
|
|
|
|
|
|
- a += shapesOffset;
|
|
|
- b += shapesOffset;
|
|
|
- c += shapesOffset;
|
|
|
+ this.reset();
|
|
|
|
|
|
- // normal, color, material
|
|
|
- scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
|
|
|
+ THREE.AnimationHandler.play( this );
|
|
|
|
|
|
- var uvs = uvgen.generateTopUV( scope, a, b, c );
|
|
|
+};
|
|
|
|
|
|
- scope.faceVertexUvs[ 0 ].push( uvs );
|
|
|
|
|
|
- }
|
|
|
+THREE.Animation.prototype.stop = function() {
|
|
|
|
|
|
- function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
|
|
|
+ this.isPlaying = false;
|
|
|
|
|
|
- a += shapesOffset;
|
|
|
- b += shapesOffset;
|
|
|
- c += shapesOffset;
|
|
|
- d += shapesOffset;
|
|
|
+ THREE.AnimationHandler.stop( this );
|
|
|
|
|
|
- scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) );
|
|
|
- scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) );
|
|
|
+};
|
|
|
|
|
|
- var uvs = uvgen.generateSideWallUV( scope, a, b, c, d );
|
|
|
+THREE.Animation.prototype.reset = function () {
|
|
|
|
|
|
- scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
|
|
|
- scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
|
|
|
+ for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ var object = this.hierarchy[ h ];
|
|
|
|
|
|
-};
|
|
|
+ object.matrixAutoUpdate = true;
|
|
|
|
|
|
-THREE.ExtrudeGeometry.WorldUVGenerator = {
|
|
|
+ if ( object.animationCache === undefined ) {
|
|
|
|
|
|
- generateTopUV: function ( geometry, indexA, indexB, indexC ) {
|
|
|
+ object.animationCache = {
|
|
|
+ animations: {},
|
|
|
+ blending: {
|
|
|
+ positionWeight: 0.0,
|
|
|
+ quaternionWeight: 0.0,
|
|
|
+ scaleWeight: 0.0
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
- var vertices = geometry.vertices;
|
|
|
+ if ( object.animationCache.animations[this.data.name] === undefined ) {
|
|
|
|
|
|
- var a = vertices[ indexA ];
|
|
|
- var b = vertices[ indexB ];
|
|
|
- var c = vertices[ indexC ];
|
|
|
+ object.animationCache.animations[this.data.name] = {};
|
|
|
+ object.animationCache.animations[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 };
|
|
|
+ object.animationCache.animations[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 };
|
|
|
+ object.animationCache.animations[this.data.name].originalMatrix = object.matrix;
|
|
|
|
|
|
- return [
|
|
|
- new THREE.Vector2( a.x, a.y ),
|
|
|
- new THREE.Vector2( b.x, b.y ),
|
|
|
- new THREE.Vector2( c.x, c.y )
|
|
|
- ];
|
|
|
+ }
|
|
|
|
|
|
- },
|
|
|
+ var animationCache = object.animationCache.animations[this.data.name];
|
|
|
|
|
|
- generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {
|
|
|
+ // Get keys to match our current time
|
|
|
|
|
|
- var vertices = geometry.vertices;
|
|
|
+ for ( var t = 0; t < 3; t ++ ) {
|
|
|
|
|
|
- var a = vertices[ indexA ];
|
|
|
- var b = vertices[ indexB ];
|
|
|
- var c = vertices[ indexC ];
|
|
|
- var d = vertices[ indexD ];
|
|
|
+ var type = this.keyTypes[ t ];
|
|
|
|
|
|
- if ( Math.abs( a.y - b.y ) < 0.01 ) {
|
|
|
- return [
|
|
|
- new THREE.Vector2( a.x, 1 - a.z ),
|
|
|
- new THREE.Vector2( b.x, 1 - b.z ),
|
|
|
- new THREE.Vector2( c.x, 1 - c.z ),
|
|
|
- new THREE.Vector2( d.x, 1 - d.z )
|
|
|
- ];
|
|
|
- } else {
|
|
|
- return [
|
|
|
- new THREE.Vector2( a.y, 1 - a.z ),
|
|
|
- new THREE.Vector2( b.y, 1 - b.z ),
|
|
|
- new THREE.Vector2( c.y, 1 - c.z ),
|
|
|
- new THREE.Vector2( d.y, 1 - d.z )
|
|
|
- ];
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
+ var prevKey = this.data.hierarchy[ h ].keys[ 0 ];
|
|
|
+ var nextKey = this.getNextKeyWith( type, h, 1 );
|
|
|
|
|
|
-// File:src/extras/geometries/ShapeGeometry.js
|
|
|
+ while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
|
|
|
|
|
|
-/**
|
|
|
- * @author jonobr1 / http://jonobr1.com
|
|
|
- *
|
|
|
- * Creates a one-sided polygonal geometry from a path shape. Similar to
|
|
|
- * ExtrudeGeometry.
|
|
|
- *
|
|
|
- * parameters = {
|
|
|
- *
|
|
|
- * curveSegments: <int>, // number of points on the curves. NOT USED AT THE MOMENT.
|
|
|
- *
|
|
|
- * material: <int> // material index for front and back faces
|
|
|
- * uvGenerator: <Object> // object that provides UV generator functions
|
|
|
- *
|
|
|
- * }
|
|
|
- **/
|
|
|
+ prevKey = nextKey;
|
|
|
+ nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
|
|
|
|
|
|
-THREE.ShapeGeometry = function ( shapes, options ) {
|
|
|
+ }
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ animationCache.prevKey[ type ] = prevKey;
|
|
|
+ animationCache.nextKey[ type ] = nextKey;
|
|
|
|
|
|
- this.type = 'ShapeGeometry';
|
|
|
+ }
|
|
|
|
|
|
- if ( shapes instanceof Array === false ) shapes = [ shapes ];
|
|
|
+ }
|
|
|
|
|
|
- this.addShapeList( shapes, options );
|
|
|
+};
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
+THREE.Animation.prototype.resetBlendWeights = function () {
|
|
|
|
|
|
-};
|
|
|
+ for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
|
|
|
-THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ var object = this.hierarchy[ h ];
|
|
|
|
|
|
-/**
|
|
|
- * Add an array of shapes to THREE.ShapeGeometry.
|
|
|
- */
|
|
|
-THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {
|
|
|
+ if ( object.animationCache !== undefined ) {
|
|
|
|
|
|
- for ( var i = 0, l = shapes.length; i < l; i ++ ) {
|
|
|
+ object.animationCache.blending.positionWeight = 0.0;
|
|
|
+ object.animationCache.blending.quaternionWeight = 0.0;
|
|
|
+ object.animationCache.blending.scaleWeight = 0.0;
|
|
|
|
|
|
- this.addShape( shapes[ i ], options );
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- return this;
|
|
|
-
|
|
|
};
|
|
|
|
|
|
-/**
|
|
|
- * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
|
|
|
- */
|
|
|
-THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {
|
|
|
-
|
|
|
- if ( options === undefined ) options = {};
|
|
|
- var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
|
|
|
-
|
|
|
- var material = options.material;
|
|
|
- var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;
|
|
|
+THREE.Animation.prototype.update = (function(){
|
|
|
|
|
|
- //
|
|
|
+ var points = [];
|
|
|
+ var target = new THREE.Vector3();
|
|
|
+ var newVector = new THREE.Vector3();
|
|
|
+ var newQuat = new THREE.Quaternion();
|
|
|
|
|
|
- var i, l, hole, s;
|
|
|
+ // Catmull-Rom spline
|
|
|
|
|
|
- var shapesOffset = this.vertices.length;
|
|
|
- var shapePoints = shape.extractPoints( curveSegments );
|
|
|
+ var interpolateCatmullRom = function ( points, scale ) {
|
|
|
|
|
|
- var vertices = shapePoints.shape;
|
|
|
- var holes = shapePoints.holes;
|
|
|
+ var c = [], v3 = [],
|
|
|
+ point, intPoint, weight, w2, w3,
|
|
|
+ pa, pb, pc, pd;
|
|
|
|
|
|
- var reverse = ! THREE.Shape.Utils.isClockWise( vertices );
|
|
|
+ point = ( points.length - 1 ) * scale;
|
|
|
+ intPoint = Math.floor( point );
|
|
|
+ weight = point - intPoint;
|
|
|
|
|
|
- if ( reverse ) {
|
|
|
+ c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
|
|
|
+ c[ 1 ] = intPoint;
|
|
|
+ c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
|
|
|
+ c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
|
|
|
|
|
|
- vertices = vertices.reverse();
|
|
|
+ pa = points[ c[ 0 ] ];
|
|
|
+ pb = points[ c[ 1 ] ];
|
|
|
+ pc = points[ c[ 2 ] ];
|
|
|
+ pd = points[ c[ 3 ] ];
|
|
|
|
|
|
- // Maybe we should also check if holes are in the opposite direction, just to be safe...
|
|
|
+ w2 = weight * weight;
|
|
|
+ w3 = weight * w2;
|
|
|
|
|
|
- for ( i = 0, l = holes.length; i < l; i ++ ) {
|
|
|
+ v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
|
|
|
+ v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
|
|
|
+ v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
|
|
|
|
|
|
- hole = holes[ i ];
|
|
|
+ return v3;
|
|
|
|
|
|
- if ( THREE.Shape.Utils.isClockWise( hole ) ) {
|
|
|
+ };
|
|
|
|
|
|
- holes[ i ] = hole.reverse();
|
|
|
+ var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
|
|
|
|
|
|
- }
|
|
|
+ var v0 = ( p2 - p0 ) * 0.5,
|
|
|
+ v1 = ( p3 - p1 ) * 0.5;
|
|
|
|
|
|
- }
|
|
|
+ return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
|
|
|
|
|
|
- reverse = false;
|
|
|
+ };
|
|
|
|
|
|
- }
|
|
|
+ return function ( delta ) {
|
|
|
|
|
|
- var faces = THREE.Shape.Utils.triangulateShape( vertices, holes );
|
|
|
+ if ( this.isPlaying === false ) return;
|
|
|
|
|
|
- // Vertices
|
|
|
+ this.currentTime += delta * this.timeScale;
|
|
|
|
|
|
- var contour = vertices;
|
|
|
+ if ( this.weight === 0 )
|
|
|
+ return;
|
|
|
|
|
|
- for ( i = 0, l = holes.length; i < l; i ++ ) {
|
|
|
+ //
|
|
|
|
|
|
- hole = holes[ i ];
|
|
|
- vertices = vertices.concat( hole );
|
|
|
+ var duration = this.data.length;
|
|
|
|
|
|
- }
|
|
|
+ if ( this.currentTime > duration || this.currentTime < 0 ) {
|
|
|
|
|
|
- //
|
|
|
+ if ( this.loop ) {
|
|
|
|
|
|
- var vert, vlen = vertices.length;
|
|
|
- var face, flen = faces.length;
|
|
|
- var cont, clen = contour.length;
|
|
|
+ this.currentTime %= duration;
|
|
|
|
|
|
- for ( i = 0; i < vlen; i ++ ) {
|
|
|
+ if ( this.currentTime < 0 )
|
|
|
+ this.currentTime += duration;
|
|
|
|
|
|
- vert = vertices[ i ];
|
|
|
+ this.reset();
|
|
|
|
|
|
- this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ this.stop();
|
|
|
+ return;
|
|
|
|
|
|
- for ( i = 0; i < flen; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- face = faces[ i ];
|
|
|
+ }
|
|
|
|
|
|
- var a = face[ 0 ] + shapesOffset;
|
|
|
- var b = face[ 1 ] + shapesOffset;
|
|
|
- var c = face[ 2 ] + shapesOffset;
|
|
|
+ for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) );
|
|
|
+ var object = this.hierarchy[ h ];
|
|
|
+ var animationCache = object.animationCache.animations[this.data.name];
|
|
|
+ var blending = object.animationCache.blending;
|
|
|
|
|
|
- }
|
|
|
+ // loop through pos/rot/scl
|
|
|
|
|
|
-};
|
|
|
+ for ( var t = 0; t < 3; t ++ ) {
|
|
|
|
|
|
-// File:src/extras/geometries/LatheGeometry.js
|
|
|
+ // get keys
|
|
|
|
|
|
-/**
|
|
|
- * @author astrodud / http://astrodud.isgreat.org/
|
|
|
- * @author zz85 / https://github.com/zz85
|
|
|
- * @author bhouston / http://exocortex.com
|
|
|
- */
|
|
|
+ var type = this.keyTypes[ t ];
|
|
|
+ var prevKey = animationCache.prevKey[ type ];
|
|
|
+ var nextKey = animationCache.nextKey[ type ];
|
|
|
|
|
|
-// points - to create a closed torus, one must use a set of points
|
|
|
-// like so: [ a, b, c, d, a ], see first is the same as last.
|
|
|
-// segments - the number of circumference segments to create
|
|
|
-// phiStart - the starting radian
|
|
|
-// phiLength - the radian (0 to 2*PI) range of the lathed section
|
|
|
-// 2*pi is a closed lathe, less than 2PI is a portion.
|
|
|
+ if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) ||
|
|
|
+ ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) {
|
|
|
|
|
|
-THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {
|
|
|
+ prevKey = this.data.hierarchy[ h ].keys[ 0 ];
|
|
|
+ nextKey = this.getNextKeyWith( type, h, 1 );
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
|
|
|
|
|
|
- this.type = 'LatheGeometry';
|
|
|
+ prevKey = nextKey;
|
|
|
+ nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
|
|
|
|
|
|
- this.parameters = {
|
|
|
- points: points,
|
|
|
- segments: segments,
|
|
|
- phiStart: phiStart,
|
|
|
- phiLength: phiLength
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
- segments = segments || 12;
|
|
|
- phiStart = phiStart || 0;
|
|
|
- phiLength = phiLength || 2 * Math.PI;
|
|
|
+ animationCache.prevKey[ type ] = prevKey;
|
|
|
+ animationCache.nextKey[ type ] = nextKey;
|
|
|
|
|
|
- var inversePointLength = 1.0 / ( points.length - 1 );
|
|
|
- var inverseSegments = 1.0 / segments;
|
|
|
+ }
|
|
|
|
|
|
- for ( var i = 0, il = segments; i <= il; i ++ ) {
|
|
|
+ object.matrixAutoUpdate = true;
|
|
|
+ object.matrixWorldNeedsUpdate = true;
|
|
|
|
|
|
- var phi = phiStart + i * inverseSegments * phiLength;
|
|
|
+ var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
|
|
|
|
|
|
- var c = Math.cos( phi ),
|
|
|
- s = Math.sin( phi );
|
|
|
+ var prevXYZ = prevKey[ type ];
|
|
|
+ var nextXYZ = nextKey[ type ];
|
|
|
|
|
|
- for ( var j = 0, jl = points.length; j < jl; j ++ ) {
|
|
|
+ if ( scale < 0 ) scale = 0;
|
|
|
+ if ( scale > 1 ) scale = 1;
|
|
|
|
|
|
- var pt = points[ j ];
|
|
|
+ // interpolate
|
|
|
|
|
|
- var vertex = new THREE.Vector3();
|
|
|
+ if ( type === "pos" ) {
|
|
|
|
|
|
- vertex.x = c * pt.x - s * pt.y;
|
|
|
- vertex.y = s * pt.x + c * pt.y;
|
|
|
- vertex.z = pt.z;
|
|
|
+ if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
|
|
|
|
|
|
- this.vertices.push( vertex );
|
|
|
+ newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
|
|
|
+ newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
|
|
|
+ newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
|
|
|
|
|
|
- }
|
|
|
+ // blend
|
|
|
+ var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
|
|
|
+ object.position.lerp( newVector, proportionalWeight );
|
|
|
+ blending.positionWeight += this.weight;
|
|
|
|
|
|
- }
|
|
|
+ } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
|
|
|
+ this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
|
|
|
|
|
|
- var np = points.length;
|
|
|
+ points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
|
|
|
+ points[ 1 ] = prevXYZ;
|
|
|
+ points[ 2 ] = nextXYZ;
|
|
|
+ points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
|
|
|
|
|
|
- for ( var i = 0, il = segments; i < il; i ++ ) {
|
|
|
+ scale = scale * 0.33 + 0.33;
|
|
|
|
|
|
- for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {
|
|
|
+ var currentPoint = interpolateCatmullRom( points, scale );
|
|
|
+ var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
|
|
|
+ blending.positionWeight += this.weight;
|
|
|
|
|
|
- var base = j + np * i;
|
|
|
- var a = base;
|
|
|
- var b = base + np;
|
|
|
- var c = base + 1 + np;
|
|
|
- var d = base + 1;
|
|
|
+ // blend
|
|
|
|
|
|
- var u0 = i * inverseSegments;
|
|
|
- var v0 = j * inversePointLength;
|
|
|
- var u1 = u0 + inverseSegments;
|
|
|
- var v1 = v0 + inversePointLength;
|
|
|
+ var vector = object.position;
|
|
|
+
|
|
|
+ vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight;
|
|
|
+ vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight;
|
|
|
+ vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight;
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( a, b, d ) );
|
|
|
+ if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
|
|
|
|
|
|
- this.faceVertexUvs[ 0 ].push( [
|
|
|
+ var forwardPoint = interpolateCatmullRom( points, scale * 1.01 );
|
|
|
|
|
|
- new THREE.Vector2( u0, v0 ),
|
|
|
- new THREE.Vector2( u1, v0 ),
|
|
|
- new THREE.Vector2( u0, v1 )
|
|
|
+ target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
|
|
|
+ target.sub( vector );
|
|
|
+ target.y = 0;
|
|
|
+ target.normalize();
|
|
|
|
|
|
- ] );
|
|
|
+ var angle = Math.atan2( target.x, target.z );
|
|
|
+ object.rotation.set( 0, angle, 0 );
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( b, c, d ) );
|
|
|
+ }
|
|
|
|
|
|
- this.faceVertexUvs[ 0 ].push( [
|
|
|
+ }
|
|
|
|
|
|
- new THREE.Vector2( u1, v0 ),
|
|
|
- new THREE.Vector2( u1, v1 ),
|
|
|
- new THREE.Vector2( u0, v1 )
|
|
|
+ } else if ( type === "rot" ) {
|
|
|
|
|
|
- ] );
|
|
|
+ THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale );
|
|
|
|
|
|
+ // Avoid paying the cost of an additional slerp if we don't have to
|
|
|
+ if ( blending.quaternionWeight === 0 ) {
|
|
|
|
|
|
- }
|
|
|
+ object.quaternion.copy(newQuat);
|
|
|
+ blending.quaternionWeight = this.weight;
|
|
|
|
|
|
- }
|
|
|
+ } else {
|
|
|
|
|
|
- this.mergeVertices();
|
|
|
- this.computeFaceNormals();
|
|
|
- this.computeVertexNormals();
|
|
|
+ var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight );
|
|
|
+ THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight );
|
|
|
+ blending.quaternionWeight += this.weight;
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ } else if ( type === "scl" ) {
|
|
|
|
|
|
-// File:src/extras/geometries/PlaneGeometry.js
|
|
|
+ newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
|
|
|
+ newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
|
|
|
+ newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
|
|
|
- */
|
|
|
+ var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight );
|
|
|
+ object.scale.lerp( newVector, proportionalWeight );
|
|
|
+ blending.scaleWeight += this.weight;
|
|
|
|
|
|
-THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {
|
|
|
+ }
|
|
|
|
|
|
- console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' );
|
|
|
+ }
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ }
|
|
|
|
|
|
- this.type = 'PlaneGeometry';
|
|
|
+ return true;
|
|
|
|
|
|
- this.parameters = {
|
|
|
- width: width,
|
|
|
- height: height,
|
|
|
- widthSegments: widthSegments,
|
|
|
- heightSegments: heightSegments
|
|
|
};
|
|
|
|
|
|
- this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );
|
|
|
-
|
|
|
-};
|
|
|
+})();
|
|
|
|
|
|
-THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
-// File:src/extras/geometries/PlaneBufferGeometry.js
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
|
|
|
- */
|
|
|
|
|
|
-THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) {
|
|
|
|
|
|
- THREE.BufferGeometry.call( this );
|
|
|
+// Get next key with
|
|
|
|
|
|
- this.type = 'PlaneBufferGeometry';
|
|
|
+THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) {
|
|
|
|
|
|
- this.parameters = {
|
|
|
- width: width,
|
|
|
- height: height,
|
|
|
- widthSegments: widthSegments,
|
|
|
- heightSegments: heightSegments
|
|
|
- };
|
|
|
+ var keys = this.data.hierarchy[ h ].keys;
|
|
|
|
|
|
- var width_half = width / 2;
|
|
|
- var height_half = height / 2;
|
|
|
+ if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
|
|
|
+ this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
|
|
|
|
|
|
- var gridX = widthSegments || 1;
|
|
|
- var gridY = heightSegments || 1;
|
|
|
+ key = key < keys.length - 1 ? key : keys.length - 1;
|
|
|
|
|
|
- var gridX1 = gridX + 1;
|
|
|
- var gridY1 = gridY + 1;
|
|
|
+ } else {
|
|
|
|
|
|
- var segment_width = width / gridX;
|
|
|
- var segment_height = height / gridY;
|
|
|
+ key = key % keys.length;
|
|
|
|
|
|
- var vertices = new Float32Array( gridX1 * gridY1 * 3 );
|
|
|
- var normals = new Float32Array( gridX1 * gridY1 * 3 );
|
|
|
- var uvs = new Float32Array( gridX1 * gridY1 * 2 );
|
|
|
+ }
|
|
|
|
|
|
- var offset = 0;
|
|
|
- var offset2 = 0;
|
|
|
+ for ( ; key < keys.length; key ++ ) {
|
|
|
|
|
|
- for ( var iy = 0; iy < gridY1; iy ++ ) {
|
|
|
+ if ( keys[ key ][ type ] !== undefined ) {
|
|
|
|
|
|
- var y = iy * segment_height - height_half;
|
|
|
+ return keys[ key ];
|
|
|
|
|
|
- for ( var ix = 0; ix < gridX1; ix ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- var x = ix * segment_width - width_half;
|
|
|
+ }
|
|
|
|
|
|
- vertices[ offset ] = x;
|
|
|
- vertices[ offset + 1 ] = - y;
|
|
|
+ return this.data.hierarchy[ h ].keys[ 0 ];
|
|
|
|
|
|
- normals[ offset + 2 ] = 1;
|
|
|
+};
|
|
|
|
|
|
- uvs[ offset2 ] = ix / gridX;
|
|
|
- uvs[ offset2 + 1 ] = 1 - ( iy / gridY );
|
|
|
+// Get previous key with
|
|
|
|
|
|
- offset += 3;
|
|
|
- offset2 += 2;
|
|
|
+THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) {
|
|
|
|
|
|
- }
|
|
|
+ var keys = this.data.hierarchy[ h ].keys;
|
|
|
|
|
|
- }
|
|
|
+ if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
|
|
|
+ this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
|
|
|
|
|
|
- offset = 0;
|
|
|
+ key = key > 0 ? key : 0;
|
|
|
|
|
|
- var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );
|
|
|
+ } else {
|
|
|
|
|
|
- for ( var iy = 0; iy < gridY; iy ++ ) {
|
|
|
+ key = key >= 0 ? key : key + keys.length;
|
|
|
|
|
|
- for ( var ix = 0; ix < gridX; ix ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- var a = ix + gridX1 * iy;
|
|
|
- var b = ix + gridX1 * ( iy + 1 );
|
|
|
- var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
|
|
|
- var d = ( ix + 1 ) + gridX1 * iy;
|
|
|
|
|
|
- indices[ offset ] = a;
|
|
|
- indices[ offset + 1 ] = b;
|
|
|
- indices[ offset + 2 ] = d;
|
|
|
+ for ( ; key >= 0; key -- ) {
|
|
|
|
|
|
- indices[ offset + 3 ] = b;
|
|
|
- indices[ offset + 4 ] = c;
|
|
|
- indices[ offset + 5 ] = d;
|
|
|
+ if ( keys[ key ][ type ] !== undefined ) {
|
|
|
|
|
|
- offset += 6;
|
|
|
+ return keys[ key ];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
|
|
|
- this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
|
|
|
- this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
|
|
|
- this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
|
|
|
+ return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
|
|
|
-
|
|
|
-// File:src/extras/geometries/RingGeometry.js
|
|
|
+// File:src/extras/animation/KeyFrameAnimation.js
|
|
|
|
|
|
/**
|
|
|
- * @author Kaleb Murphy
|
|
|
+ * @author mikael emtinger / http://gomo.se/
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author khang duong
|
|
|
+ * @author erik kitson
|
|
|
*/
|
|
|
|
|
|
-THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
|
|
|
+THREE.KeyFrameAnimation = function ( data ) {
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ this.root = data.node;
|
|
|
+ this.data = THREE.AnimationHandler.init( data );
|
|
|
+ this.hierarchy = THREE.AnimationHandler.parse( this.root );
|
|
|
+ this.currentTime = 0;
|
|
|
+ this.timeScale = 0.001;
|
|
|
+ this.isPlaying = false;
|
|
|
+ this.isPaused = true;
|
|
|
+ this.loop = true;
|
|
|
|
|
|
- this.type = 'RingGeometry';
|
|
|
+ // initialize to first keyframes
|
|
|
|
|
|
- this.parameters = {
|
|
|
- innerRadius: innerRadius,
|
|
|
- outerRadius: outerRadius,
|
|
|
- thetaSegments: thetaSegments,
|
|
|
- phiSegments: phiSegments,
|
|
|
- thetaStart: thetaStart,
|
|
|
- thetaLength: thetaLength
|
|
|
- };
|
|
|
+ for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
|
|
|
- innerRadius = innerRadius || 0;
|
|
|
- outerRadius = outerRadius || 50;
|
|
|
+ var keys = this.data.hierarchy[h].keys,
|
|
|
+ sids = this.data.hierarchy[h].sids,
|
|
|
+ obj = this.hierarchy[h];
|
|
|
|
|
|
- thetaStart = thetaStart !== undefined ? thetaStart : 0;
|
|
|
- thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
|
|
|
+ if ( keys.length && sids ) {
|
|
|
|
|
|
- thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
|
|
|
- phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;
|
|
|
+ for ( var s = 0; s < sids.length; s ++ ) {
|
|
|
|
|
|
- var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
|
|
|
+ var sid = sids[ s ],
|
|
|
+ next = this.getNextKeyWith( sid, h, 0 );
|
|
|
|
|
|
- for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring
|
|
|
+ if ( next ) {
|
|
|
|
|
|
- for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle
|
|
|
+ next.apply( sid );
|
|
|
|
|
|
- var vertex = new THREE.Vector3();
|
|
|
- var segment = thetaStart + o / thetaSegments * thetaLength;
|
|
|
- vertex.x = radius * Math.cos( segment );
|
|
|
- vertex.y = radius * Math.sin( segment );
|
|
|
+ }
|
|
|
|
|
|
- this.vertices.push( vertex );
|
|
|
- uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) );
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- radius += radiusStep;
|
|
|
+ obj.matrixAutoUpdate = false;
|
|
|
+ this.data.hierarchy[h].node.updateMatrix();
|
|
|
+ obj.matrixWorldNeedsUpdate = true;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- var n = new THREE.Vector3( 0, 0, 1 );
|
|
|
+};
|
|
|
|
|
|
- for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring
|
|
|
|
|
|
- var thetaSegment = i * (thetaSegments + 1);
|
|
|
+THREE.KeyFrameAnimation.prototype.play = function ( startTime ) {
|
|
|
|
|
|
- for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle
|
|
|
+ this.currentTime = startTime !== undefined ? startTime : 0;
|
|
|
|
|
|
- var segment = o + thetaSegment;
|
|
|
+ if ( this.isPlaying === false ) {
|
|
|
|
|
|
- var v1 = segment;
|
|
|
- var v2 = segment + thetaSegments + 1;
|
|
|
- var v3 = segment + thetaSegments + 2;
|
|
|
+ this.isPlaying = true;
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]);
|
|
|
+ // reset key cache
|
|
|
|
|
|
- v1 = segment;
|
|
|
- v2 = segment + thetaSegments + 2;
|
|
|
- v3 = segment + 1;
|
|
|
+ var h, hl = this.hierarchy.length,
|
|
|
+ object,
|
|
|
+ node;
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]);
|
|
|
+ for ( h = 0; h < hl; h ++ ) {
|
|
|
+
|
|
|
+ object = this.hierarchy[ h ];
|
|
|
+ node = this.data.hierarchy[ h ];
|
|
|
+
|
|
|
+ if ( node.animationCache === undefined ) {
|
|
|
+
|
|
|
+ node.animationCache = {};
|
|
|
+ node.animationCache.prevKey = null;
|
|
|
+ node.animationCache.nextKey = null;
|
|
|
+ node.animationCache.originalMatrix = object.matrix;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var keys = this.data.hierarchy[h].keys;
|
|
|
+
|
|
|
+ if (keys.length) {
|
|
|
+
|
|
|
+ node.animationCache.prevKey = keys[ 0 ];
|
|
|
+ node.animationCache.nextKey = keys[ 1 ];
|
|
|
+
|
|
|
+ this.startTime = Math.min( keys[0].time, this.startTime );
|
|
|
+ this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ this.update( 0 );
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
+ this.isPaused = false;
|
|
|
|
|
|
- this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
|
|
|
+ THREE.AnimationHandler.play( this );
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
+THREE.KeyFrameAnimation.prototype.stop = function() {
|
|
|
|
|
|
-// File:src/extras/geometries/SphereGeometry.js
|
|
|
+ this.isPlaying = false;
|
|
|
+ this.isPaused = false;
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
+ THREE.AnimationHandler.stop( this );
|
|
|
|
|
|
-THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
|
|
|
+ // reset JIT matrix and remove cache
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ for ( var h = 0; h < this.data.hierarchy.length; h ++ ) {
|
|
|
+
|
|
|
+ var obj = this.hierarchy[ h ];
|
|
|
+ var node = this.data.hierarchy[ h ];
|
|
|
|
|
|
- this.type = 'SphereGeometry';
|
|
|
+ if ( node.animationCache !== undefined ) {
|
|
|
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- widthSegments: widthSegments,
|
|
|
- heightSegments: heightSegments,
|
|
|
- phiStart: phiStart,
|
|
|
- phiLength: phiLength,
|
|
|
- thetaStart: thetaStart,
|
|
|
- thetaLength: thetaLength
|
|
|
- };
|
|
|
+ var original = node.animationCache.originalMatrix;
|
|
|
|
|
|
- radius = radius || 50;
|
|
|
+ original.copy( obj.matrix );
|
|
|
+ obj.matrix = original;
|
|
|
|
|
|
- widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
|
|
|
- heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
|
|
|
+ delete node.animationCache;
|
|
|
|
|
|
- phiStart = phiStart !== undefined ? phiStart : 0;
|
|
|
- phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
|
|
|
+ }
|
|
|
|
|
|
- thetaStart = thetaStart !== undefined ? thetaStart : 0;
|
|
|
- thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
|
|
|
+ }
|
|
|
|
|
|
- var x, y, vertices = [], uvs = [];
|
|
|
+};
|
|
|
|
|
|
- for ( y = 0; y <= heightSegments; y ++ ) {
|
|
|
|
|
|
- var verticesRow = [];
|
|
|
- var uvsRow = [];
|
|
|
+// Update
|
|
|
|
|
|
- for ( x = 0; x <= widthSegments; x ++ ) {
|
|
|
+THREE.KeyFrameAnimation.prototype.update = function ( delta ) {
|
|
|
|
|
|
- var u = x / widthSegments;
|
|
|
- var v = y / heightSegments;
|
|
|
+ if ( this.isPlaying === false ) return;
|
|
|
|
|
|
- var vertex = new THREE.Vector3();
|
|
|
- vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
|
|
|
- vertex.y = radius * Math.cos( thetaStart + v * thetaLength );
|
|
|
- vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
|
|
|
+ this.currentTime += delta * this.timeScale;
|
|
|
|
|
|
- this.vertices.push( vertex );
|
|
|
+ //
|
|
|
+
|
|
|
+ var duration = this.data.length;
|
|
|
+
|
|
|
+ if ( this.loop === true && this.currentTime > duration ) {
|
|
|
+
|
|
|
+ this.currentTime %= duration;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.currentTime = Math.min( this.currentTime, duration );
|
|
|
+
|
|
|
+ for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
|
|
|
|
|
|
- verticesRow.push( this.vertices.length - 1 );
|
|
|
- uvsRow.push( new THREE.Vector2( u, 1 - v ) );
|
|
|
+ var object = this.hierarchy[ h ];
|
|
|
+ var node = this.data.hierarchy[ h ];
|
|
|
|
|
|
- }
|
|
|
+ var keys = node.keys,
|
|
|
+ animationCache = node.animationCache;
|
|
|
|
|
|
- vertices.push( verticesRow );
|
|
|
- uvs.push( uvsRow );
|
|
|
|
|
|
- }
|
|
|
+ if ( keys.length ) {
|
|
|
|
|
|
- for ( y = 0; y < heightSegments; y ++ ) {
|
|
|
+ var prevKey = animationCache.prevKey;
|
|
|
+ var nextKey = animationCache.nextKey;
|
|
|
|
|
|
- for ( x = 0; x < widthSegments; x ++ ) {
|
|
|
+ if ( nextKey.time <= this.currentTime ) {
|
|
|
|
|
|
- var v1 = vertices[ y ][ x + 1 ];
|
|
|
- var v2 = vertices[ y ][ x ];
|
|
|
- var v3 = vertices[ y + 1 ][ x ];
|
|
|
- var v4 = vertices[ y + 1 ][ x + 1 ];
|
|
|
+ while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
|
|
|
|
|
|
- var n1 = this.vertices[ v1 ].clone().normalize();
|
|
|
- var n2 = this.vertices[ v2 ].clone().normalize();
|
|
|
- var n3 = this.vertices[ v3 ].clone().normalize();
|
|
|
- var n4 = this.vertices[ v4 ].clone().normalize();
|
|
|
+ prevKey = nextKey;
|
|
|
+ nextKey = keys[ prevKey.index + 1 ];
|
|
|
|
|
|
- var uv1 = uvs[ y ][ x + 1 ].clone();
|
|
|
- var uv2 = uvs[ y ][ x ].clone();
|
|
|
- var uv3 = uvs[ y + 1 ][ x ].clone();
|
|
|
- var uv4 = uvs[ y + 1 ][ x + 1 ].clone();
|
|
|
+ }
|
|
|
|
|
|
- if ( Math.abs( this.vertices[ v1 ].y ) === radius ) {
|
|
|
+ animationCache.prevKey = prevKey;
|
|
|
+ animationCache.nextKey = nextKey;
|
|
|
|
|
|
- uv1.x = ( uv1.x + uv2.x ) / 2;
|
|
|
- this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] );
|
|
|
+ }
|
|
|
|
|
|
- } else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) {
|
|
|
+ if ( nextKey.time >= this.currentTime ) {
|
|
|
|
|
|
- uv3.x = ( uv3.x + uv4.x ) / 2;
|
|
|
- this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
|
|
|
+ prevKey.interpolate( nextKey, this.currentTime );
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
|
|
|
-
|
|
|
- this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
|
|
|
+ prevKey.interpolate( nextKey, nextKey.time );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ this.data.hierarchy[ h ].node.updateMatrix();
|
|
|
+ object.matrixWorldNeedsUpdate = true;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
-
|
|
|
- this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
|
|
|
-
|
|
|
};
|
|
|
|
|
|
-THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+// Get next key with
|
|
|
|
|
|
-// File:src/extras/geometries/TextGeometry.js
|
|
|
+THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) {
|
|
|
|
|
|
-/**
|
|
|
- * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- *
|
|
|
- * For creating 3D text geometry in three.js
|
|
|
- *
|
|
|
- * Text = 3D Text
|
|
|
- *
|
|
|
- * parameters = {
|
|
|
- * size: <float>, // size of the text
|
|
|
- * height: <float>, // thickness to extrude text
|
|
|
- * curveSegments: <int>, // number of points on the curves
|
|
|
- *
|
|
|
- * font: <string>, // font name
|
|
|
- * weight: <string>, // font weight (normal, bold)
|
|
|
- * style: <string>, // font style (normal, italics)
|
|
|
- *
|
|
|
- * bevelEnabled: <bool>, // turn on bevel
|
|
|
- * bevelThickness: <float>, // how deep into text bevel goes
|
|
|
- * bevelSize: <float>, // how far from text outline is bevel
|
|
|
- * }
|
|
|
- *
|
|
|
- */
|
|
|
+ var keys = this.data.hierarchy[ h ].keys;
|
|
|
+ key = key % keys.length;
|
|
|
|
|
|
-/* Usage Examples
|
|
|
+ for ( ; key < keys.length; key ++ ) {
|
|
|
|
|
|
- // TextGeometry wrapper
|
|
|
+ if ( keys[ key ].hasTarget( sid ) ) {
|
|
|
|
|
|
- var text3d = new TextGeometry( text, options );
|
|
|
+ return keys[ key ];
|
|
|
|
|
|
- // Complete manner
|
|
|
+ }
|
|
|
|
|
|
- var textShapes = THREE.FontUtils.generateShapes( text, options );
|
|
|
- var text3d = new ExtrudeGeometry( textShapes, options );
|
|
|
+ }
|
|
|
|
|
|
-*/
|
|
|
+ return keys[ 0 ];
|
|
|
|
|
|
+};
|
|
|
|
|
|
-THREE.TextGeometry = function ( text, parameters ) {
|
|
|
+// Get previous key with
|
|
|
|
|
|
- parameters = parameters || {};
|
|
|
+THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) {
|
|
|
|
|
|
- var textShapes = THREE.FontUtils.generateShapes( text, parameters );
|
|
|
+ var keys = this.data.hierarchy[ h ].keys;
|
|
|
+ key = key >= 0 ? key : key + keys.length;
|
|
|
|
|
|
- // translate parameters to ExtrudeGeometry API
|
|
|
+ for ( ; key >= 0; key -- ) {
|
|
|
|
|
|
- parameters.amount = parameters.height !== undefined ? parameters.height : 50;
|
|
|
+ if ( keys[ key ].hasTarget( sid ) ) {
|
|
|
|
|
|
- // defaults
|
|
|
+ return keys[ key ];
|
|
|
|
|
|
- if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
|
|
|
- if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
|
|
|
- if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
|
|
|
+ }
|
|
|
|
|
|
- THREE.ExtrudeGeometry.call( this, textShapes, parameters );
|
|
|
+ }
|
|
|
|
|
|
- this.type = 'TextGeometry';
|
|
|
+ return keys[ keys.length - 1 ];
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
|
|
|
-
|
|
|
-// File:src/extras/geometries/TorusGeometry.js
|
|
|
+// File:src/extras/animation/MorphAnimation.js
|
|
|
|
|
|
/**
|
|
|
- * @author oosmoxiecode
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888
|
|
|
+ * @author mrdoob / http://mrdoob.com
|
|
|
*/
|
|
|
|
|
|
-THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {
|
|
|
-
|
|
|
- THREE.Geometry.call( this );
|
|
|
-
|
|
|
- this.type = 'TorusGeometry';
|
|
|
-
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- tube: tube,
|
|
|
- radialSegments: radialSegments,
|
|
|
- tubularSegments: tubularSegments,
|
|
|
- arc: arc
|
|
|
- };
|
|
|
-
|
|
|
- radius = radius || 100;
|
|
|
- tube = tube || 40;
|
|
|
- radialSegments = radialSegments || 8;
|
|
|
- tubularSegments = tubularSegments || 6;
|
|
|
- arc = arc || Math.PI * 2;
|
|
|
-
|
|
|
- var center = new THREE.Vector3(), uvs = [], normals = [];
|
|
|
-
|
|
|
- for ( var j = 0; j <= radialSegments; j ++ ) {
|
|
|
-
|
|
|
- for ( var i = 0; i <= tubularSegments; i ++ ) {
|
|
|
-
|
|
|
- var u = i / tubularSegments * arc;
|
|
|
- var v = j / radialSegments * Math.PI * 2;
|
|
|
+THREE.MorphAnimation = function ( mesh ) {
|
|
|
|
|
|
- center.x = radius * Math.cos( u );
|
|
|
- center.y = radius * Math.sin( u );
|
|
|
+ this.mesh = mesh;
|
|
|
+ this.frames = mesh.morphTargetInfluences.length;
|
|
|
+ this.currentTime = 0;
|
|
|
+ this.duration = 1000;
|
|
|
+ this.loop = true;
|
|
|
|
|
|
- var vertex = new THREE.Vector3();
|
|
|
- vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
|
|
|
- vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
|
|
|
- vertex.z = tube * Math.sin( v );
|
|
|
+ this.isPlaying = false;
|
|
|
|
|
|
- this.vertices.push( vertex );
|
|
|
+};
|
|
|
|
|
|
- uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) );
|
|
|
- normals.push( vertex.clone().sub( center ).normalize() );
|
|
|
+THREE.MorphAnimation.prototype = {
|
|
|
|
|
|
- }
|
|
|
+ play: function () {
|
|
|
|
|
|
- }
|
|
|
+ this.isPlaying = true;
|
|
|
|
|
|
- for ( var j = 1; j <= radialSegments; j ++ ) {
|
|
|
+ },
|
|
|
|
|
|
- for ( var i = 1; i <= tubularSegments; i ++ ) {
|
|
|
+ pause: function () {
|
|
|
|
|
|
- var a = ( tubularSegments + 1 ) * j + i - 1;
|
|
|
- var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
|
|
|
- var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
|
|
|
- var d = ( tubularSegments + 1 ) * j + i;
|
|
|
+ this.isPlaying = false;
|
|
|
|
|
|
- var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] );
|
|
|
- this.faces.push( face );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] );
|
|
|
+ },
|
|
|
|
|
|
- face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] );
|
|
|
- this.faces.push( face );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
|
|
|
+ update: ( function () {
|
|
|
|
|
|
- }
|
|
|
+ var lastFrame = 0;
|
|
|
+ var currentFrame = 0;
|
|
|
|
|
|
- }
|
|
|
+ return function ( delta ) {
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
+ if ( this.isPlaying === false ) return;
|
|
|
|
|
|
-};
|
|
|
+ this.currentTime += delta;
|
|
|
|
|
|
-THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ if ( this.loop === true && this.currentTime > this.duration ) {
|
|
|
|
|
|
-// File:src/extras/geometries/TorusKnotGeometry.js
|
|
|
+ this.currentTime %= this.duration;
|
|
|
|
|
|
-/**
|
|
|
- * @author oosmoxiecode
|
|
|
- * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
|
|
|
- */
|
|
|
+ }
|
|
|
|
|
|
-THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) {
|
|
|
+ this.currentTime = Math.min( this.currentTime, this.duration );
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ var interpolation = this.duration / this.frames;
|
|
|
+ var frame = Math.floor( this.currentTime / interpolation );
|
|
|
|
|
|
- this.type = 'TorusKnotGeometry';
|
|
|
+ if ( frame != currentFrame ) {
|
|
|
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- tube: tube,
|
|
|
- radialSegments: radialSegments,
|
|
|
- tubularSegments: tubularSegments,
|
|
|
- p: p,
|
|
|
- q: q,
|
|
|
- heightScale: heightScale
|
|
|
- };
|
|
|
+ this.mesh.morphTargetInfluences[ lastFrame ] = 0;
|
|
|
+ this.mesh.morphTargetInfluences[ currentFrame ] = 1;
|
|
|
+ this.mesh.morphTargetInfluences[ frame ] = 0;
|
|
|
|
|
|
- radius = radius || 100;
|
|
|
- tube = tube || 40;
|
|
|
- radialSegments = radialSegments || 64;
|
|
|
- tubularSegments = tubularSegments || 8;
|
|
|
- p = p || 2;
|
|
|
- q = q || 3;
|
|
|
- heightScale = heightScale || 1;
|
|
|
-
|
|
|
- var grid = new Array( radialSegments );
|
|
|
- var tang = new THREE.Vector3();
|
|
|
- var n = new THREE.Vector3();
|
|
|
- var bitan = new THREE.Vector3();
|
|
|
+ lastFrame = currentFrame;
|
|
|
+ currentFrame = frame;
|
|
|
|
|
|
- for ( var i = 0; i < radialSegments; ++ i ) {
|
|
|
+ }
|
|
|
|
|
|
- grid[ i ] = new Array( tubularSegments );
|
|
|
- var u = i / radialSegments * 2 * p * Math.PI;
|
|
|
- var p1 = getPos( u, q, p, radius, heightScale );
|
|
|
- var p2 = getPos( u + 0.01, q, p, radius, heightScale );
|
|
|
- tang.subVectors( p2, p1 );
|
|
|
- n.addVectors( p2, p1 );
|
|
|
+ this.mesh.morphTargetInfluences[ frame ] = ( this.currentTime % interpolation ) / interpolation;
|
|
|
+ this.mesh.morphTargetInfluences[ lastFrame ] = 1 - this.mesh.morphTargetInfluences[ frame ];
|
|
|
|
|
|
- bitan.crossVectors( tang, n );
|
|
|
- n.crossVectors( bitan, tang );
|
|
|
- bitan.normalize();
|
|
|
- n.normalize();
|
|
|
+ }
|
|
|
|
|
|
- for ( var j = 0; j < tubularSegments; ++ j ) {
|
|
|
+ } )()
|
|
|
|
|
|
- var v = j / tubularSegments * 2 * Math.PI;
|
|
|
- var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
|
|
|
- var cy = tube * Math.sin( v );
|
|
|
+};
|
|
|
|
|
|
- var pos = new THREE.Vector3();
|
|
|
- pos.x = p1.x + cx * n.x + cy * bitan.x;
|
|
|
- pos.y = p1.y + cx * n.y + cy * bitan.y;
|
|
|
- pos.z = p1.z + cx * n.z + cy * bitan.z;
|
|
|
+// File:src/extras/geometries/BoxGeometry.js
|
|
|
|
|
|
- grid[ i ][ j ] = this.vertices.push( pos ) - 1;
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
|
|
|
+ */
|
|
|
|
|
|
- }
|
|
|
+THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
|
|
|
|
|
|
- }
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- for ( var i = 0; i < radialSegments; ++ i ) {
|
|
|
+ this.type = 'BoxGeometry';
|
|
|
|
|
|
- for ( var j = 0; j < tubularSegments; ++ j ) {
|
|
|
+ this.parameters = {
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
+ depth: depth,
|
|
|
+ widthSegments: widthSegments,
|
|
|
+ heightSegments: heightSegments,
|
|
|
+ depthSegments: depthSegments
|
|
|
+ };
|
|
|
|
|
|
- var ip = ( i + 1 ) % radialSegments;
|
|
|
- var jp = ( j + 1 ) % tubularSegments;
|
|
|
+ this.widthSegments = widthSegments || 1;
|
|
|
+ this.heightSegments = heightSegments || 1;
|
|
|
+ this.depthSegments = depthSegments || 1;
|
|
|
|
|
|
- var a = grid[ i ][ j ];
|
|
|
- var b = grid[ ip ][ j ];
|
|
|
- var c = grid[ ip ][ jp ];
|
|
|
- var d = grid[ i ][ jp ];
|
|
|
+ var scope = this;
|
|
|
|
|
|
- var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments );
|
|
|
- var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments );
|
|
|
- var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments );
|
|
|
- var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments );
|
|
|
+ var width_half = width / 2;
|
|
|
+ var height_half = height / 2;
|
|
|
+ var depth_half = depth / 2;
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( a, b, d ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
|
|
|
+ buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px
|
|
|
+ buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx
|
|
|
+ buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py
|
|
|
+ buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny
|
|
|
+ buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz
|
|
|
+ buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( b, c, d ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
+ function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {
|
|
|
|
|
|
- }
|
|
|
- }
|
|
|
+ var w, ix, iy,
|
|
|
+ gridX = scope.widthSegments,
|
|
|
+ gridY = scope.heightSegments,
|
|
|
+ width_half = width / 2,
|
|
|
+ height_half = height / 2,
|
|
|
+ offset = scope.vertices.length;
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
- this.computeVertexNormals();
|
|
|
+ if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
|
|
|
|
|
|
- function getPos( u, in_q, in_p, radius, heightScale ) {
|
|
|
+ w = 'z';
|
|
|
|
|
|
- var cu = Math.cos( u );
|
|
|
- var su = Math.sin( u );
|
|
|
- var quOverP = in_q / in_p * u;
|
|
|
- var cs = Math.cos( quOverP );
|
|
|
+ } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
|
|
|
|
|
|
- var tx = radius * ( 2 + cs ) * 0.5 * cu;
|
|
|
- var ty = radius * ( 2 + cs ) * su * 0.5;
|
|
|
- var tz = heightScale * radius * Math.sin( quOverP ) * 0.5;
|
|
|
+ w = 'y';
|
|
|
+ gridY = scope.depthSegments;
|
|
|
|
|
|
- return new THREE.Vector3( tx, ty, tz );
|
|
|
+ } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
|
|
|
|
|
|
- }
|
|
|
+ w = 'x';
|
|
|
+ gridX = scope.depthSegments;
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ var gridX1 = gridX + 1,
|
|
|
+ gridY1 = gridY + 1,
|
|
|
+ segment_width = width / gridX,
|
|
|
+ segment_height = height / gridY,
|
|
|
+ normal = new THREE.Vector3();
|
|
|
|
|
|
-// File:src/extras/geometries/TubeGeometry.js
|
|
|
+ normal[ w ] = depth > 0 ? 1 : - 1;
|
|
|
|
|
|
-/**
|
|
|
- * @author WestLangley / https://github.com/WestLangley
|
|
|
- * @author zz85 / https://github.com/zz85
|
|
|
- * @author miningold / https://github.com/miningold
|
|
|
- *
|
|
|
- * Modified from the TorusKnotGeometry by @oosmoxiecode
|
|
|
- *
|
|
|
- * Creates a tube which extrudes along a 3d spline
|
|
|
- *
|
|
|
- * Uses parallel transport frames as described in
|
|
|
- * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
|
|
|
- */
|
|
|
+ for ( iy = 0; iy < gridY1; iy ++ ) {
|
|
|
|
|
|
-THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) {
|
|
|
+ for ( ix = 0; ix < gridX1; ix ++ ) {
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
+ vector[ u ] = ( ix * segment_width - width_half ) * udir;
|
|
|
+ vector[ v ] = ( iy * segment_height - height_half ) * vdir;
|
|
|
+ vector[ w ] = depth;
|
|
|
|
|
|
- this.type = 'TubeGeometry';
|
|
|
+ scope.vertices.push( vector );
|
|
|
|
|
|
- this.parameters = {
|
|
|
- path: path,
|
|
|
- segments: segments,
|
|
|
- radius: radius,
|
|
|
- radialSegments: radialSegments,
|
|
|
- closed: closed
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
- segments = segments || 64;
|
|
|
- radius = radius || 1;
|
|
|
- radialSegments = radialSegments || 8;
|
|
|
- closed = closed || false;
|
|
|
+ }
|
|
|
|
|
|
- var grid = [];
|
|
|
+ for ( iy = 0; iy < gridY; iy ++ ) {
|
|
|
|
|
|
- var scope = this,
|
|
|
+ for ( ix = 0; ix < gridX; ix ++ ) {
|
|
|
|
|
|
- tangent,
|
|
|
- normal,
|
|
|
- binormal,
|
|
|
+ var a = ix + gridX1 * iy;
|
|
|
+ var b = ix + gridX1 * ( iy + 1 );
|
|
|
+ var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
|
|
|
+ var d = ( ix + 1 ) + gridX1 * iy;
|
|
|
|
|
|
- numpoints = segments + 1,
|
|
|
+ var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY );
|
|
|
+ var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY );
|
|
|
+ var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY );
|
|
|
+ var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY );
|
|
|
|
|
|
- x, y, z,
|
|
|
- tx, ty, tz,
|
|
|
- u, v,
|
|
|
+ var face = new THREE.Face3( a + offset, b + offset, d + offset );
|
|
|
+ face.normal.copy( normal );
|
|
|
+ face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
|
|
|
+ face.materialIndex = materialIndex;
|
|
|
|
|
|
- cx, cy,
|
|
|
- pos, pos2 = new THREE.Vector3(),
|
|
|
- i, j,
|
|
|
- ip, jp,
|
|
|
- a, b, c, d,
|
|
|
- uva, uvb, uvc, uvd;
|
|
|
+ scope.faces.push( face );
|
|
|
+ scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
|
|
|
|
|
|
- var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ),
|
|
|
- tangents = frames.tangents,
|
|
|
- normals = frames.normals,
|
|
|
- binormals = frames.binormals;
|
|
|
+ face = new THREE.Face3( b + offset, c + offset, d + offset );
|
|
|
+ face.normal.copy( normal );
|
|
|
+ face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
|
|
|
+ face.materialIndex = materialIndex;
|
|
|
|
|
|
- // proxy internals
|
|
|
- this.tangents = tangents;
|
|
|
- this.normals = normals;
|
|
|
- this.binormals = binormals;
|
|
|
+ scope.faces.push( face );
|
|
|
+ scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
|
|
|
- function vert( x, y, z ) {
|
|
|
+ }
|
|
|
|
|
|
- return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- // consruct the grid
|
|
|
+ this.mergeVertices();
|
|
|
|
|
|
- for ( i = 0; i < numpoints; i ++ ) {
|
|
|
+};
|
|
|
|
|
|
- grid[ i ] = [];
|
|
|
+THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
- u = i / ( numpoints - 1 );
|
|
|
+// File:src/extras/geometries/CircleGeometry.js
|
|
|
|
|
|
- pos = path.getPointAt( u );
|
|
|
+/**
|
|
|
+ * @author hughes
|
|
|
+ */
|
|
|
|
|
|
- tangent = tangents[ i ];
|
|
|
- normal = normals[ i ];
|
|
|
- binormal = binormals[ i ];
|
|
|
+THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
|
|
|
|
|
|
- for ( j = 0; j < radialSegments; j ++ ) {
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- v = j / radialSegments * 2 * Math.PI;
|
|
|
+ this.type = 'CircleGeometry';
|
|
|
|
|
|
- cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
|
|
|
- cy = radius * Math.sin( v );
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ segments: segments,
|
|
|
+ thetaStart: thetaStart,
|
|
|
+ thetaLength: thetaLength
|
|
|
+ };
|
|
|
|
|
|
- pos2.copy( pos );
|
|
|
- pos2.x += cx * normal.x + cy * binormal.x;
|
|
|
- pos2.y += cx * normal.y + cy * binormal.y;
|
|
|
- pos2.z += cx * normal.z + cy * binormal.z;
|
|
|
+ radius = radius || 50;
|
|
|
+ segments = segments !== undefined ? Math.max( 3, segments ) : 8;
|
|
|
|
|
|
- grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
|
|
|
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
|
|
|
+ thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
|
|
|
|
|
|
- }
|
|
|
- }
|
|
|
+ var i, uvs = [],
|
|
|
+ center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 );
|
|
|
|
|
|
+ this.vertices.push(center);
|
|
|
+ uvs.push( centerUV );
|
|
|
|
|
|
- // construct the mesh
|
|
|
+ for ( i = 0; i <= segments; i ++ ) {
|
|
|
|
|
|
- for ( i = 0; i < segments; i ++ ) {
|
|
|
+ var vertex = new THREE.Vector3();
|
|
|
+ var segment = thetaStart + i / segments * thetaLength;
|
|
|
|
|
|
- for ( j = 0; j < radialSegments; j ++ ) {
|
|
|
+ vertex.x = radius * Math.cos( segment );
|
|
|
+ vertex.y = radius * Math.sin( segment );
|
|
|
|
|
|
- ip = ( closed ) ? (i + 1) % segments : i + 1;
|
|
|
- jp = (j + 1) % radialSegments;
|
|
|
+ this.vertices.push( vertex );
|
|
|
+ uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) );
|
|
|
|
|
|
- a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! ***
|
|
|
- b = grid[ ip ][ j ];
|
|
|
- c = grid[ ip ][ jp ];
|
|
|
- d = grid[ i ][ jp ];
|
|
|
+ }
|
|
|
|
|
|
- uva = new THREE.Vector2( i / segments, j / radialSegments );
|
|
|
- uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments );
|
|
|
- uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments );
|
|
|
- uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments );
|
|
|
+ var n = new THREE.Vector3( 0, 0, 1 );
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( a, b, d ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
|
|
|
+ for ( i = 1; i <= segments; i ++ ) {
|
|
|
|
|
|
- this.faces.push( new THREE.Face3( b, c, d ) );
|
|
|
- this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
+ this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] );
|
|
|
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
this.computeFaceNormals();
|
|
|
- this.computeVertexNormals();
|
|
|
+
|
|
|
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
+// File:src/extras/geometries/CubeGeometry.js
|
|
|
|
|
|
-// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
|
|
|
-THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) {
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- var tangent = new THREE.Vector3(),
|
|
|
- normal = new THREE.Vector3(),
|
|
|
- binormal = new THREE.Vector3(),
|
|
|
|
|
|
- tangents = [],
|
|
|
- normals = [],
|
|
|
- binormals = [],
|
|
|
+THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
|
|
|
|
|
|
- vec = new THREE.Vector3(),
|
|
|
- mat = new THREE.Matrix4(),
|
|
|
+ console.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' );
|
|
|
+ return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments );
|
|
|
|
|
|
- numpoints = segments + 1,
|
|
|
- theta,
|
|
|
- epsilon = 0.0001,
|
|
|
- smallest,
|
|
|
+ };
|
|
|
|
|
|
- tx, ty, tz,
|
|
|
- i, u, v;
|
|
|
+// File:src/extras/geometries/CylinderGeometry.js
|
|
|
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- // expose internals
|
|
|
- this.tangents = tangents;
|
|
|
- this.normals = normals;
|
|
|
- this.binormals = binormals;
|
|
|
+THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) {
|
|
|
|
|
|
- // compute the tangent vectors for each segment on the path
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- for ( i = 0; i < numpoints; i ++ ) {
|
|
|
+ this.type = 'CylinderGeometry';
|
|
|
|
|
|
- u = i / ( numpoints - 1 );
|
|
|
+ this.parameters = {
|
|
|
+ radiusTop: radiusTop,
|
|
|
+ radiusBottom: radiusBottom,
|
|
|
+ height: height,
|
|
|
+ radialSegments: radialSegments,
|
|
|
+ heightSegments: heightSegments,
|
|
|
+ openEnded: openEnded
|
|
|
+ };
|
|
|
|
|
|
- tangents[ i ] = path.getTangentAt( u );
|
|
|
- tangents[ i ].normalize();
|
|
|
+ radiusTop = radiusTop !== undefined ? radiusTop : 20;
|
|
|
+ radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
|
|
|
+ height = height !== undefined ? height : 100;
|
|
|
|
|
|
- }
|
|
|
+ radialSegments = radialSegments || 8;
|
|
|
+ heightSegments = heightSegments || 1;
|
|
|
|
|
|
- initialNormal3();
|
|
|
+ openEnded = openEnded !== undefined ? openEnded : false;
|
|
|
|
|
|
- /*
|
|
|
- function initialNormal1(lastBinormal) {
|
|
|
- // fixed start binormal. Has dangers of 0 vectors
|
|
|
- normals[ 0 ] = new THREE.Vector3();
|
|
|
- binormals[ 0 ] = new THREE.Vector3();
|
|
|
- if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
|
|
|
- normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize();
|
|
|
- binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
|
|
|
- }
|
|
|
+ var heightHalf = height / 2;
|
|
|
|
|
|
- function initialNormal2() {
|
|
|
+ var x, y, vertices = [], uvs = [];
|
|
|
|
|
|
- // This uses the Frenet-Serret formula for deriving binormal
|
|
|
- var t2 = path.getTangentAt( epsilon );
|
|
|
+ for ( y = 0; y <= heightSegments; y ++ ) {
|
|
|
|
|
|
- normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
|
|
|
- binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );
|
|
|
+ var verticesRow = [];
|
|
|
+ var uvsRow = [];
|
|
|
|
|
|
- normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
|
|
|
- binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
|
|
|
+ var v = y / heightSegments;
|
|
|
+ var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
|
|
|
|
|
|
- }
|
|
|
- */
|
|
|
+ for ( x = 0; x <= radialSegments; x ++ ) {
|
|
|
|
|
|
- function initialNormal3() {
|
|
|
- // select an initial normal vector perpenicular to the first tangent vector,
|
|
|
- // and in the direction of the smallest tangent xyz component
|
|
|
+ var u = x / radialSegments;
|
|
|
|
|
|
- normals[ 0 ] = new THREE.Vector3();
|
|
|
- binormals[ 0 ] = new THREE.Vector3();
|
|
|
- smallest = Number.MAX_VALUE;
|
|
|
- tx = Math.abs( tangents[ 0 ].x );
|
|
|
- ty = Math.abs( tangents[ 0 ].y );
|
|
|
- tz = Math.abs( tangents[ 0 ].z );
|
|
|
+ var vertex = new THREE.Vector3();
|
|
|
+ vertex.x = radius * Math.sin( u * Math.PI * 2 );
|
|
|
+ vertex.y = - v * height + heightHalf;
|
|
|
+ vertex.z = radius * Math.cos( u * Math.PI * 2 );
|
|
|
|
|
|
- if ( tx <= smallest ) {
|
|
|
- smallest = tx;
|
|
|
- normal.set( 1, 0, 0 );
|
|
|
- }
|
|
|
+ this.vertices.push( vertex );
|
|
|
|
|
|
- if ( ty <= smallest ) {
|
|
|
- smallest = ty;
|
|
|
- normal.set( 0, 1, 0 );
|
|
|
- }
|
|
|
+ verticesRow.push( this.vertices.length - 1 );
|
|
|
+ uvsRow.push( new THREE.Vector2( u, 1 - v ) );
|
|
|
|
|
|
- if ( tz <= smallest ) {
|
|
|
- normal.set( 0, 0, 1 );
|
|
|
}
|
|
|
|
|
|
- vec.crossVectors( tangents[ 0 ], normal ).normalize();
|
|
|
+ vertices.push( verticesRow );
|
|
|
+ uvs.push( uvsRow );
|
|
|
|
|
|
- normals[ 0 ].crossVectors( tangents[ 0 ], vec );
|
|
|
- binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
|
|
|
}
|
|
|
|
|
|
+ var tanTheta = ( radiusBottom - radiusTop ) / height;
|
|
|
+ var na, nb;
|
|
|
|
|
|
- // compute the slowly-varying normal and binormal vectors for each segment on the path
|
|
|
+ for ( x = 0; x < radialSegments; x ++ ) {
|
|
|
|
|
|
- for ( i = 1; i < numpoints; i ++ ) {
|
|
|
+ if ( radiusTop !== 0 ) {
|
|
|
|
|
|
- normals[ i ] = normals[ i-1 ].clone();
|
|
|
+ na = this.vertices[ vertices[ 0 ][ x ] ].clone();
|
|
|
+ nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
|
|
|
|
|
|
- binormals[ i ] = binormals[ i-1 ].clone();
|
|
|
+ } else {
|
|
|
|
|
|
- vec.crossVectors( tangents[ i-1 ], tangents[ i ] );
|
|
|
+ na = this.vertices[ vertices[ 1 ][ x ] ].clone();
|
|
|
+ nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
|
|
|
|
|
|
- if ( vec.length() > epsilon ) {
|
|
|
+ }
|
|
|
|
|
|
- vec.normalize();
|
|
|
+ na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize();
|
|
|
+ nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize();
|
|
|
|
|
|
- theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
|
|
|
+ for ( y = 0; y < heightSegments; y ++ ) {
|
|
|
|
|
|
- normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
|
|
|
+ var v1 = vertices[ y ][ x ];
|
|
|
+ var v2 = vertices[ y + 1 ][ x ];
|
|
|
+ var v3 = vertices[ y + 1 ][ x + 1 ];
|
|
|
+ var v4 = vertices[ y ][ x + 1 ];
|
|
|
|
|
|
- }
|
|
|
+ var n1 = na.clone();
|
|
|
+ var n2 = na.clone();
|
|
|
+ var n3 = nb.clone();
|
|
|
+ var n4 = nb.clone();
|
|
|
|
|
|
- binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
|
|
|
+ var uv1 = uvs[ y ][ x ].clone();
|
|
|
+ var uv2 = uvs[ y + 1 ][ x ].clone();
|
|
|
+ var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
|
|
|
+ var uv4 = uvs[ y ][ x + 1 ].clone();
|
|
|
+
|
|
|
+ this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
|
|
|
+
|
|
|
+ this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // top cap
|
|
|
|
|
|
- // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
|
|
|
+ if ( openEnded === false && radiusTop > 0 ) {
|
|
|
|
|
|
- if ( closed ) {
|
|
|
+ this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
|
|
|
|
|
|
- theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) );
|
|
|
- theta /= ( numpoints - 1 );
|
|
|
+ for ( x = 0; x < radialSegments; x ++ ) {
|
|
|
|
|
|
- if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
|
|
|
+ var v1 = vertices[ 0 ][ x ];
|
|
|
+ var v2 = vertices[ 0 ][ x + 1 ];
|
|
|
+ var v3 = this.vertices.length - 1;
|
|
|
|
|
|
- theta = - theta;
|
|
|
+ var n1 = new THREE.Vector3( 0, 1, 0 );
|
|
|
+ var n2 = new THREE.Vector3( 0, 1, 0 );
|
|
|
+ var n3 = new THREE.Vector3( 0, 1, 0 );
|
|
|
+
|
|
|
+ var uv1 = uvs[ 0 ][ x ].clone();
|
|
|
+ var uv2 = uvs[ 0 ][ x + 1 ].clone();
|
|
|
+ var uv3 = new THREE.Vector2( uv2.x, 0 );
|
|
|
+
|
|
|
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( i = 1; i < numpoints; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- // twist a little...
|
|
|
- normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
|
|
|
- binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
|
|
|
+ // bottom cap
|
|
|
+
|
|
|
+ if ( openEnded === false && radiusBottom > 0 ) {
|
|
|
+
|
|
|
+ this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
|
|
|
+
|
|
|
+ for ( x = 0; x < radialSegments; x ++ ) {
|
|
|
+
|
|
|
+ var v1 = vertices[ y ][ x + 1 ];
|
|
|
+ var v2 = vertices[ y ][ x ];
|
|
|
+ var v3 = this.vertices.length - 1;
|
|
|
+
|
|
|
+ var n1 = new THREE.Vector3( 0, - 1, 0 );
|
|
|
+ var n2 = new THREE.Vector3( 0, - 1, 0 );
|
|
|
+ var n3 = new THREE.Vector3( 0, - 1, 0 );
|
|
|
+
|
|
|
+ var uv1 = uvs[ y ][ x + 1 ].clone();
|
|
|
+ var uv2 = uvs[ y ][ x ].clone();
|
|
|
+ var uv3 = new THREE.Vector2( uv2.x, 1 );
|
|
|
+
|
|
|
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
-};
|
|
|
|
|
|
-// File:src/extras/geometries/PolyhedronGeometry.js
|
|
|
+ this.computeFaceNormals();
|
|
|
|
|
|
-/**
|
|
|
- * @author clockworkgeek / https://github.com/clockworkgeek
|
|
|
- * @author timothypratley / https://github.com/timothypratley
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
-*/
|
|
|
+}
|
|
|
|
|
|
-THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) {
|
|
|
+THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+
|
|
|
+// File:src/extras/geometries/ExtrudeGeometry.js
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
+ *
|
|
|
+ * Creates extruded geometry from a path shape.
|
|
|
+ *
|
|
|
+ * parameters = {
|
|
|
+ *
|
|
|
+ * curveSegments: <int>, // number of points on the curves
|
|
|
+ * steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too
|
|
|
+ * amount: <int>, // Depth to extrude the shape
|
|
|
+ *
|
|
|
+ * bevelEnabled: <bool>, // turn on bevel
|
|
|
+ * bevelThickness: <float>, // how deep into the original shape bevel goes
|
|
|
+ * bevelSize: <float>, // how far from shape outline is bevel
|
|
|
+ * bevelSegments: <int>, // number of bevel layers
|
|
|
+ *
|
|
|
+ * extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
|
|
|
+ * frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals
|
|
|
+ *
|
|
|
+ * material: <int> // material index for front and back faces
|
|
|
+ * extrudeMaterial: <int> // material index for extrusion and beveled faces
|
|
|
+ * uvGenerator: <Object> // object that provides UV generator functions
|
|
|
+ *
|
|
|
+ * }
|
|
|
+ **/
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+THREE.ExtrudeGeometry = function ( shapes, options ) {
|
|
|
|
|
|
- this.type = 'PolyhedronGeometry';
|
|
|
+ if ( typeof( shapes ) === "undefined" ) {
|
|
|
+ shapes = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- this.parameters = {
|
|
|
- vertices: vertices,
|
|
|
- indices: indices,
|
|
|
- radius: radius,
|
|
|
- detail: detail
|
|
|
- };
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- radius = radius || 1;
|
|
|
- detail = detail || 0;
|
|
|
+ this.type = 'ExtrudeGeometry';
|
|
|
|
|
|
- var that = this;
|
|
|
+ shapes = shapes instanceof Array ? shapes : [ shapes ];
|
|
|
|
|
|
- for ( var i = 0, l = vertices.length; i < l; i += 3 ) {
|
|
|
+ this.addShapeList( shapes, options );
|
|
|
|
|
|
- prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
|
|
|
+ this.computeFaceNormals();
|
|
|
|
|
|
- }
|
|
|
+ // can't really use automatic vertex normals
|
|
|
+ // as then front and back sides get smoothed too
|
|
|
+ // should do separate smoothing just for sides
|
|
|
|
|
|
- var midpoints = [], p = this.vertices;
|
|
|
+ //this.computeVertexNormals();
|
|
|
|
|
|
- var faces = [];
|
|
|
+ //console.log( "took", ( Date.now() - startTime ) );
|
|
|
|
|
|
- for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) {
|
|
|
+};
|
|
|
|
|
|
- var v1 = p[ indices[ i ] ];
|
|
|
- var v2 = p[ indices[ i + 1 ] ];
|
|
|
- var v3 = p[ indices[ i + 2 ] ];
|
|
|
+THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
- faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
|
|
|
+THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {
|
|
|
+ var sl = shapes.length;
|
|
|
|
|
|
+ for ( var s = 0; s < sl; s ++ ) {
|
|
|
+ var shape = shapes[ s ];
|
|
|
+ this.addShape( shape, options );
|
|
|
}
|
|
|
+};
|
|
|
|
|
|
- var centroid = new THREE.Vector3();
|
|
|
-
|
|
|
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
|
|
|
|
|
|
- subdivide( faces[ i ], detail );
|
|
|
+ var amount = options.amount !== undefined ? options.amount : 100;
|
|
|
|
|
|
- }
|
|
|
+ var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
|
|
|
+ var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
|
|
|
+ var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
|
|
|
|
|
|
+ var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
|
|
|
|
|
|
- // Handle case when face straddles the seam
|
|
|
+ var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
|
|
|
|
|
|
- for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
|
|
|
+ var steps = options.steps !== undefined ? options.steps : 1;
|
|
|
|
|
|
- var uvs = this.faceVertexUvs[ 0 ][ i ];
|
|
|
+ var extrudePath = options.extrudePath;
|
|
|
+ var extrudePts, extrudeByPath = false;
|
|
|
|
|
|
- var x0 = uvs[ 0 ].x;
|
|
|
- var x1 = uvs[ 1 ].x;
|
|
|
- var x2 = uvs[ 2 ].x;
|
|
|
+ var material = options.material;
|
|
|
+ var extrudeMaterial = options.extrudeMaterial;
|
|
|
|
|
|
- var max = Math.max( x0, Math.max( x1, x2 ) );
|
|
|
- var min = Math.min( x0, Math.min( x1, x2 ) );
|
|
|
+ // Use default WorldUVGenerator if no UV generators are specified.
|
|
|
+ var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
|
|
|
|
|
|
- if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary
|
|
|
+ var splineTube, binormal, normal, position2;
|
|
|
+ if ( extrudePath ) {
|
|
|
|
|
|
- if ( x0 < 0.2 ) uvs[ 0 ].x += 1;
|
|
|
- if ( x1 < 0.2 ) uvs[ 1 ].x += 1;
|
|
|
- if ( x2 < 0.2 ) uvs[ 2 ].x += 1;
|
|
|
+ extrudePts = extrudePath.getSpacedPoints( steps );
|
|
|
|
|
|
- }
|
|
|
+ extrudeByPath = true;
|
|
|
+ bevelEnabled = false; // bevels not supported for path extrusion
|
|
|
|
|
|
- }
|
|
|
+ // SETUP TNB variables
|
|
|
|
|
|
+ // Reuse TNB from TubeGeomtry for now.
|
|
|
+ // TODO1 - have a .isClosed in spline?
|
|
|
|
|
|
- // Apply radius
|
|
|
+ splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
|
|
|
|
|
|
- for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
|
|
|
+ // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
|
|
|
|
|
|
- this.vertices[ i ].multiplyScalar( radius );
|
|
|
+ binormal = new THREE.Vector3();
|
|
|
+ normal = new THREE.Vector3();
|
|
|
+ position2 = new THREE.Vector3();
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // Safeguards if bevels are not enabled
|
|
|
|
|
|
- // Merge vertices
|
|
|
+ if ( ! bevelEnabled ) {
|
|
|
|
|
|
- this.mergeVertices();
|
|
|
+ bevelSegments = 0;
|
|
|
+ bevelThickness = 0;
|
|
|
+ bevelSize = 0;
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
+ }
|
|
|
|
|
|
- this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
|
|
|
+ // Variables initalization
|
|
|
|
|
|
+ var ahole, h, hl; // looping of holes
|
|
|
+ var scope = this;
|
|
|
+ var bevelPoints = [];
|
|
|
|
|
|
- // Project vector onto sphere's surface
|
|
|
+ var shapesOffset = this.vertices.length;
|
|
|
|
|
|
- function prepare( vector ) {
|
|
|
+ var shapePoints = shape.extractPoints( curveSegments );
|
|
|
|
|
|
- var vertex = vector.normalize().clone();
|
|
|
- vertex.index = that.vertices.push( vertex ) - 1;
|
|
|
+ var vertices = shapePoints.shape;
|
|
|
+ var holes = shapePoints.holes;
|
|
|
|
|
|
- // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
|
|
|
+ var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ;
|
|
|
|
|
|
- var u = azimuth( vector ) / 2 / Math.PI + 0.5;
|
|
|
- var v = inclination( vector ) / Math.PI + 0.5;
|
|
|
- vertex.uv = new THREE.Vector2( u, 1 - v );
|
|
|
+ if ( reverse ) {
|
|
|
|
|
|
- return vertex;
|
|
|
+ vertices = vertices.reverse();
|
|
|
|
|
|
- }
|
|
|
+ // Maybe we should also check if holes are in the opposite direction, just to be safe ...
|
|
|
|
|
|
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
|
|
|
- // Approximate a curved face with recursively sub-divided triangles.
|
|
|
+ ahole = holes[ h ];
|
|
|
|
|
|
- function make( v1, v2, v3 ) {
|
|
|
+ if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
|
|
|
|
|
|
- var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
|
|
|
- that.faces.push( face );
|
|
|
+ holes[ h ] = ahole.reverse();
|
|
|
|
|
|
- centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );
|
|
|
+ }
|
|
|
|
|
|
- var azi = azimuth( centroid );
|
|
|
+ }
|
|
|
|
|
|
- that.faceVertexUvs[ 0 ].push( [
|
|
|
- correctUV( v1.uv, v1, azi ),
|
|
|
- correctUV( v2.uv, v2, azi ),
|
|
|
- correctUV( v3.uv, v3, azi )
|
|
|
- ] );
|
|
|
+ reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
- // Analytically subdivide a face to the required detail level.
|
|
|
-
|
|
|
- function subdivide( face, detail ) {
|
|
|
-
|
|
|
- var cols = Math.pow(2, detail);
|
|
|
- var cells = Math.pow(4, detail);
|
|
|
- var a = prepare( that.vertices[ face.a ] );
|
|
|
- var b = prepare( that.vertices[ face.b ] );
|
|
|
- var c = prepare( that.vertices[ face.c ] );
|
|
|
- var v = [];
|
|
|
+ var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
|
|
|
|
|
|
- // Construct all of the vertices for this subdivision.
|
|
|
+ /* Vertices */
|
|
|
|
|
|
- for ( var i = 0 ; i <= cols; i ++ ) {
|
|
|
+ var contour = vertices; // vertices has all points but contour has only points of circumference
|
|
|
|
|
|
- v[ i ] = [];
|
|
|
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
|
|
|
- var aj = prepare( a.clone().lerp( c, i / cols ) );
|
|
|
- var bj = prepare( b.clone().lerp( c, i / cols ) );
|
|
|
- var rows = cols - i;
|
|
|
+ ahole = holes[ h ];
|
|
|
|
|
|
- for ( var j = 0; j <= rows; j ++) {
|
|
|
+ vertices = vertices.concat( ahole );
|
|
|
|
|
|
- if ( j == 0 && i == cols ) {
|
|
|
+ }
|
|
|
|
|
|
- v[ i ][ j ] = aj;
|
|
|
|
|
|
- } else {
|
|
|
+ function scalePt2 ( pt, vec, size ) {
|
|
|
|
|
|
- v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) );
|
|
|
+ if ( ! vec ) console.log( "die" );
|
|
|
|
|
|
- }
|
|
|
+ return vec.clone().multiplyScalar( size ).add( pt );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ var b, bs, t, z,
|
|
|
+ vert, vlen = vertices.length,
|
|
|
+ face, flen = faces.length,
|
|
|
+ cont, clen = contour.length;
|
|
|
|
|
|
- // Construct all of the faces.
|
|
|
|
|
|
- for ( var i = 0; i < cols ; i ++ ) {
|
|
|
+ // Find directions for point movement
|
|
|
|
|
|
- for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) {
|
|
|
+ var RAD_TO_DEGREES = 180 / Math.PI;
|
|
|
|
|
|
- var k = Math.floor( j / 2 );
|
|
|
|
|
|
- if ( j % 2 == 0 ) {
|
|
|
+ function getBevelVec( inPt, inPrev, inNext ) {
|
|
|
|
|
|
- make(
|
|
|
- v[ i ][ k + 1],
|
|
|
- v[ i + 1 ][ k ],
|
|
|
- v[ i ][ k ]
|
|
|
- );
|
|
|
+ var EPSILON = 0.0000000001;
|
|
|
+
|
|
|
+ // computes for inPt the corresponding point inPt' on a new contour
|
|
|
+ // shiftet by 1 unit (length of normalized vector) to the left
|
|
|
+ // if we walk along contour clockwise, this new contour is outside the old one
|
|
|
+ //
|
|
|
+ // inPt' is the intersection of the two lines parallel to the two
|
|
|
+ // adjacent edges of inPt at a distance of 1 unit on the left side.
|
|
|
+
|
|
|
+ var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt
|
|
|
|
|
|
- } else {
|
|
|
+ // good reading for geometry algorithms (here: line-line intersection)
|
|
|
+ // http://geomalgorithms.com/a05-_intersect-1.html
|
|
|
|
|
|
- make(
|
|
|
- v[ i ][ k + 1 ],
|
|
|
- v[ i + 1][ k + 1],
|
|
|
- v[ i + 1 ][ k ]
|
|
|
- );
|
|
|
+ var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y;
|
|
|
+ var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y;
|
|
|
+
|
|
|
+ var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
|
|
|
+
|
|
|
+ // check for colinear edges
|
|
|
+ var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
|
|
|
+
|
|
|
+ if ( Math.abs( colinear0 ) > EPSILON ) { // not colinear
|
|
|
+
|
|
|
+ // length of vectors for normalizing
|
|
|
+
|
|
|
+ var v_prev_len = Math.sqrt( v_prev_lensq );
|
|
|
+ var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
|
|
|
+
|
|
|
+ // shift adjacent points by unit vectors to the left
|
|
|
+
|
|
|
+ var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
|
|
|
+ var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
|
|
|
+
|
|
|
+ var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
|
|
|
+ var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
|
|
|
+
|
|
|
+ // scaling factor for v_prev to intersection point
|
|
|
+
|
|
|
+ var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
|
|
|
+ ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /
|
|
|
+ ( v_prev_x * v_next_y - v_prev_y * v_next_x );
|
|
|
+
|
|
|
+ // vector from inPt to intersection point
|
|
|
+
|
|
|
+ v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
|
|
|
+ v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
|
|
|
+
|
|
|
+ // Don't normalize!, otherwise sharp corners become ugly
|
|
|
+ // but prevent crazy spikes
|
|
|
+ var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y )
|
|
|
+ if ( v_trans_lensq <= 2 ) {
|
|
|
+ return new THREE.Vector2( v_trans_x, v_trans_y );
|
|
|
+ } else {
|
|
|
+ shrink_by = Math.sqrt( v_trans_lensq / 2 );
|
|
|
+ }
|
|
|
+
|
|
|
+ } else { // handle special case of colinear edges
|
|
|
|
|
|
+ var direction_eq = false; // assumes: opposite
|
|
|
+ if ( v_prev_x > EPSILON ) {
|
|
|
+ if ( v_next_x > EPSILON ) { direction_eq = true; }
|
|
|
+ } else {
|
|
|
+ if ( v_prev_x < - EPSILON ) {
|
|
|
+ if ( v_next_x < - EPSILON ) { direction_eq = true; }
|
|
|
+ } else {
|
|
|
+ if ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
+ if ( direction_eq ) {
|
|
|
+ // console.log("Warning: lines are a straight sequence");
|
|
|
+ v_trans_x = - v_prev_y;
|
|
|
+ v_trans_y = v_prev_x;
|
|
|
+ shrink_by = Math.sqrt( v_prev_lensq );
|
|
|
+ } else {
|
|
|
+ // console.log("Warning: lines are a straight spike");
|
|
|
+ v_trans_x = v_prev_x;
|
|
|
+ v_trans_y = v_prev_y;
|
|
|
+ shrink_by = Math.sqrt( v_prev_lensq / 2 );
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // Angle around the Y axis, counter-clockwise when looking from above.
|
|
|
-
|
|
|
- function azimuth( vector ) {
|
|
|
-
|
|
|
- return Math.atan2( vector.z, - vector.x );
|
|
|
+ return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
- // Angle above the XZ plane.
|
|
|
-
|
|
|
- function inclination( vector ) {
|
|
|
-
|
|
|
- return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
|
|
|
+ var contourMovements = [];
|
|
|
|
|
|
- }
|
|
|
+ for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
|
|
|
|
|
|
+ if ( j === il ) j = 0;
|
|
|
+ if ( k === il ) k = 0;
|
|
|
|
|
|
- // Texture fixing helper. Spheres have some odd behaviours.
|
|
|
+ // (j)---(i)---(k)
|
|
|
+ // console.log('i,j,k', i, j , k)
|
|
|
|
|
|
- function correctUV( uv, vector, azimuth ) {
|
|
|
+ var pt_i = contour[ i ];
|
|
|
+ var pt_j = contour[ j ];
|
|
|
+ var pt_k = contour[ k ];
|
|
|
|
|
|
- if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y );
|
|
|
- if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y );
|
|
|
- return uv.clone();
|
|
|
+ contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
|
|
|
|
|
|
-};
|
|
|
-
|
|
|
-THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
-
|
|
|
-// File:src/extras/geometries/DodecahedronGeometry.js
|
|
|
-
|
|
|
-/**
|
|
|
- * @author Abe Pazos / https://hamoid.com
|
|
|
- */
|
|
|
-
|
|
|
-THREE.DodecahedronGeometry = function ( radius, detail ) {
|
|
|
-
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- detail: detail
|
|
|
- };
|
|
|
-
|
|
|
- var t = ( 1 + Math.sqrt( 5 ) ) / 2;
|
|
|
- var r = 1 / t;
|
|
|
-
|
|
|
- var vertices = [
|
|
|
-
|
|
|
- // (±1, ±1, ±1)
|
|
|
- -1, -1, -1, -1, -1, 1,
|
|
|
- -1, 1, -1, -1, 1, 1,
|
|
|
- 1, -1, -1, 1, -1, 1,
|
|
|
- 1, 1, -1, 1, 1, 1,
|
|
|
-
|
|
|
- // (0, ±1/φ, ±φ)
|
|
|
- 0, -r, -t, 0, -r, t,
|
|
|
- 0, r, -t, 0, r, t,
|
|
|
-
|
|
|
- // (±1/φ, ±φ, 0)
|
|
|
- -r, -t, 0, -r, t, 0,
|
|
|
- r, -t, 0, r, t, 0,
|
|
|
-
|
|
|
- // (±φ, 0, ±1/φ)
|
|
|
- -t, 0, -r, t, 0, -r,
|
|
|
- -t, 0, r, t, 0, r
|
|
|
- ];
|
|
|
-
|
|
|
- var indices = [
|
|
|
- 3, 11, 7, 3, 7, 15, 3, 15, 13,
|
|
|
- 7, 19, 17, 7, 17, 6, 7, 6, 15,
|
|
|
- 17, 4, 8, 17, 8, 10, 17, 10, 6,
|
|
|
- 8, 0, 16, 8, 16, 2, 8, 2, 10,
|
|
|
- 0, 12, 1, 0, 1, 18, 0, 18, 16,
|
|
|
- 6, 10, 2, 6, 2, 13, 6, 13, 15,
|
|
|
- 2, 16, 18, 2, 18, 3, 2, 3, 13,
|
|
|
- 18, 1, 9, 18, 9, 11, 18, 11, 3,
|
|
|
- 4, 14, 12, 4, 12, 0, 4, 0, 8,
|
|
|
- 11, 9, 5, 11, 5, 19, 11, 19, 7,
|
|
|
- 19, 5, 14, 19, 14, 4, 19, 4, 17,
|
|
|
- 1, 12, 14, 1, 14, 5, 1, 5, 9
|
|
|
- ];
|
|
|
-
|
|
|
- THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
-
|
|
|
-// File:src/extras/geometries/IcosahedronGeometry.js
|
|
|
-
|
|
|
-/**
|
|
|
- * @author timothypratley / https://github.com/timothypratley
|
|
|
- */
|
|
|
-
|
|
|
-THREE.IcosahedronGeometry = function ( radius, detail ) {
|
|
|
-
|
|
|
- var t = ( 1 + Math.sqrt( 5 ) ) / 2;
|
|
|
-
|
|
|
- var vertices = [
|
|
|
- - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,
|
|
|
- 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,
|
|
|
- t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1
|
|
|
- ];
|
|
|
-
|
|
|
- var indices = [
|
|
|
- 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,
|
|
|
- 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
|
|
|
- 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,
|
|
|
- 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1
|
|
|
- ];
|
|
|
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
|
|
|
- THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
|
|
|
+ ahole = holes[ h ];
|
|
|
|
|
|
- this.type = 'IcosahedronGeometry';
|
|
|
+ oneHoleMovements = [];
|
|
|
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- detail: detail
|
|
|
- };
|
|
|
-};
|
|
|
+ for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
|
|
|
|
|
|
-THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ if ( j === il ) j = 0;
|
|
|
+ if ( k === il ) k = 0;
|
|
|
|
|
|
-// File:src/extras/geometries/OctahedronGeometry.js
|
|
|
+ // (j)---(i)---(k)
|
|
|
+ oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
|
|
|
|
|
|
-/**
|
|
|
- * @author timothypratley / https://github.com/timothypratley
|
|
|
- */
|
|
|
+ }
|
|
|
|
|
|
-THREE.OctahedronGeometry = function ( radius, detail ) {
|
|
|
+ holesMovements.push( oneHoleMovements );
|
|
|
+ verticesMovements = verticesMovements.concat( oneHoleMovements );
|
|
|
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- detail: detail
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
- var vertices = [
|
|
|
- 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1
|
|
|
- ];
|
|
|
|
|
|
- var indices = [
|
|
|
- 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2
|
|
|
- ];
|
|
|
+ // Loop bevelSegments, 1 for the front, 1 for the back
|
|
|
|
|
|
- THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
|
|
|
+ for ( b = 0; b < bevelSegments; b ++ ) {
|
|
|
+ //for ( b = bevelSegments; b > 0; b -- ) {
|
|
|
|
|
|
- this.type = 'OctahedronGeometry';
|
|
|
+ t = b / bevelSegments;
|
|
|
+ z = bevelThickness * ( 1 - t );
|
|
|
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- detail: detail
|
|
|
- };
|
|
|
-};
|
|
|
+ //z = bevelThickness * t;
|
|
|
+ bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
|
|
|
+ //bs = bevelSize * t ; // linear
|
|
|
|
|
|
-THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ // contract shape
|
|
|
|
|
|
-// File:src/extras/geometries/TetrahedronGeometry.js
|
|
|
+ for ( i = 0, il = contour.length; i < il; i ++ ) {
|
|
|
|
|
|
-/**
|
|
|
- * @author timothypratley / https://github.com/timothypratley
|
|
|
- */
|
|
|
+ vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
|
|
|
|
|
|
-THREE.TetrahedronGeometry = function ( radius, detail ) {
|
|
|
+ v( vert.x, vert.y, - z );
|
|
|
|
|
|
- var vertices = [
|
|
|
- 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1
|
|
|
- ];
|
|
|
+ }
|
|
|
|
|
|
- var indices = [
|
|
|
- 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
|
|
|
- ];
|
|
|
+ // expand holes
|
|
|
|
|
|
- THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
|
|
|
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
|
|
|
- this.type = 'TetrahedronGeometry';
|
|
|
+ ahole = holes[ h ];
|
|
|
+ oneHoleMovements = holesMovements[ h ];
|
|
|
|
|
|
- this.parameters = {
|
|
|
- radius: radius,
|
|
|
- detail: detail
|
|
|
- };
|
|
|
+ for ( i = 0, il = ahole.length; i < il; i ++ ) {
|
|
|
|
|
|
-};
|
|
|
+ vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
|
|
|
|
|
|
-THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ v( vert.x, vert.y, - z );
|
|
|
|
|
|
-// File:src/extras/geometries/ParametricGeometry.js
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- * @author zz85 / https://github.com/zz85
|
|
|
- * Parametric Surfaces Geometry
|
|
|
- * based on the brilliant article by @prideout http://prideout.net/blog/?p=44
|
|
|
- *
|
|
|
- * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements );
|
|
|
- *
|
|
|
- */
|
|
|
+ }
|
|
|
|
|
|
-THREE.ParametricGeometry = function ( func, slices, stacks ) {
|
|
|
+ }
|
|
|
|
|
|
- THREE.Geometry.call( this );
|
|
|
+ bs = bevelSize;
|
|
|
|
|
|
- this.type = 'ParametricGeometry';
|
|
|
+ // Back facing vertices
|
|
|
|
|
|
- this.parameters = {
|
|
|
- func: func,
|
|
|
- slices: slices,
|
|
|
- stacks: stacks
|
|
|
- };
|
|
|
+ for ( i = 0; i < vlen; i ++ ) {
|
|
|
|
|
|
- var verts = this.vertices;
|
|
|
- var faces = this.faces;
|
|
|
- var uvs = this.faceVertexUvs[ 0 ];
|
|
|
+ vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
|
|
|
|
|
|
- var i, il, j, p;
|
|
|
- var u, v;
|
|
|
+ if ( ! extrudeByPath ) {
|
|
|
|
|
|
- var stackCount = stacks + 1;
|
|
|
- var sliceCount = slices + 1;
|
|
|
+ v( vert.x, vert.y, 0 );
|
|
|
|
|
|
- for ( i = 0; i <= stacks; i ++ ) {
|
|
|
+ } else {
|
|
|
|
|
|
- v = i / stacks;
|
|
|
+ // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
|
|
|
|
|
|
- for ( j = 0; j <= slices; j ++ ) {
|
|
|
+ normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
|
|
|
+ binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
|
|
|
|
|
|
- u = j / slices;
|
|
|
+ position2.copy( extrudePts[0] ).add(normal).add(binormal);
|
|
|
|
|
|
- p = func( u, v );
|
|
|
- verts.push( p );
|
|
|
+ v( position2.x, position2.y, position2.z );
|
|
|
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- var a, b, c, d;
|
|
|
- var uva, uvb, uvc, uvd;
|
|
|
+ // Add stepped vertices...
|
|
|
+ // Including front facing vertices
|
|
|
|
|
|
- for ( i = 0; i < stacks; i ++ ) {
|
|
|
+ var s;
|
|
|
|
|
|
- for ( j = 0; j < slices; j ++ ) {
|
|
|
+ for ( s = 1; s <= steps; s ++ ) {
|
|
|
|
|
|
- a = i * sliceCount + j;
|
|
|
- b = i * sliceCount + j + 1;
|
|
|
- c = (i + 1) * sliceCount + j + 1;
|
|
|
- d = (i + 1) * sliceCount + j;
|
|
|
+ for ( i = 0; i < vlen; i ++ ) {
|
|
|
|
|
|
- uva = new THREE.Vector2( j / slices, i / stacks );
|
|
|
- uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks );
|
|
|
- uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks );
|
|
|
- uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks );
|
|
|
+ vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
|
|
|
|
|
|
- faces.push( new THREE.Face3( a, b, d ) );
|
|
|
- uvs.push( [ uva, uvb, uvd ] );
|
|
|
+ if ( ! extrudeByPath ) {
|
|
|
|
|
|
- faces.push( new THREE.Face3( b, c, d ) );
|
|
|
- uvs.push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
+ v( vert.x, vert.y, amount / steps * s );
|
|
|
|
|
|
- }
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
|
|
|
|
|
|
- // console.log(this);
|
|
|
+ normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x );
|
|
|
+ binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y );
|
|
|
|
|
|
- // magic bullet
|
|
|
- // var diff = this.mergeVertices();
|
|
|
- // console.log('removed ', diff, ' vertices by merging');
|
|
|
+ position2.copy( extrudePts[s] ).add( normal ).add( binormal );
|
|
|
|
|
|
- this.computeFaceNormals();
|
|
|
- this.computeVertexNormals();
|
|
|
+ v( position2.x, position2.y, position2.z );
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+ }
|
|
|
|
|
|
-// File:src/extras/helpers/AxisHelper.js
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- * @author sroucheray / http://sroucheray.org/
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
|
|
|
-THREE.AxisHelper = function ( size ) {
|
|
|
+ // Add bevel segments planes
|
|
|
|
|
|
- size = size || 1;
|
|
|
+ //for ( b = 1; b <= bevelSegments; b ++ ) {
|
|
|
+ for ( b = bevelSegments - 1; b >= 0; b -- ) {
|
|
|
|
|
|
- var vertices = new Float32Array( [
|
|
|
- 0, 0, 0, size, 0, 0,
|
|
|
- 0, 0, 0, 0, size, 0,
|
|
|
- 0, 0, 0, 0, 0, size
|
|
|
- ] );
|
|
|
+ t = b / bevelSegments;
|
|
|
+ z = bevelThickness * ( 1 - t );
|
|
|
+ //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
|
|
|
+ bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
|
|
|
|
|
|
- var colors = new Float32Array( [
|
|
|
- 1, 0, 0, 1, 0.6, 0,
|
|
|
- 0, 1, 0, 0.6, 1, 0,
|
|
|
- 0, 0, 1, 0, 0.6, 1
|
|
|
- ] );
|
|
|
+ // contract shape
|
|
|
|
|
|
- var geometry = new THREE.BufferGeometry();
|
|
|
- geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
|
|
|
- geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
|
|
|
+ for ( i = 0, il = contour.length; i < il; i ++ ) {
|
|
|
|
|
|
- var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
|
|
|
+ vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
|
|
|
+ v( vert.x, vert.y, amount + z );
|
|
|
|
|
|
- THREE.Line.call( this, geometry, material, THREE.LinePieces );
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ // expand holes
|
|
|
|
|
|
-THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
|
|
|
-// File:src/extras/helpers/ArrowHelper.js
|
|
|
+ ahole = holes[ h ];
|
|
|
+ oneHoleMovements = holesMovements[ h ];
|
|
|
|
|
|
-/**
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
- * @author zz85 / http://github.com/zz85
|
|
|
- * @author bhouston / http://exocortex.com
|
|
|
- *
|
|
|
- * Creates an arrow for visualizing directions
|
|
|
- *
|
|
|
- * Parameters:
|
|
|
- * dir - Vector3
|
|
|
- * origin - Vector3
|
|
|
- * length - Number
|
|
|
- * color - color in hex value
|
|
|
- * headLength - Number
|
|
|
- * headWidth - Number
|
|
|
- */
|
|
|
+ for ( i = 0, il = ahole.length; i < il; i ++ ) {
|
|
|
|
|
|
-THREE.ArrowHelper = ( function () {
|
|
|
+ vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
|
|
|
|
|
|
- var lineGeometry = new THREE.Geometry();
|
|
|
- lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
|
|
|
+ if ( ! extrudeByPath ) {
|
|
|
|
|
|
- var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 );
|
|
|
- coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
|
|
|
+ v( vert.x, vert.y, amount + z );
|
|
|
|
|
|
- return function ( dir, origin, length, color, headLength, headWidth ) {
|
|
|
+ } else {
|
|
|
|
|
|
- // dir is assumed to be normalized
|
|
|
+ v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ }
|
|
|
|
|
|
- if ( color === undefined ) color = 0xffff00;
|
|
|
- if ( length === undefined ) length = 1;
|
|
|
- if ( headLength === undefined ) headLength = 0.2 * length;
|
|
|
- if ( headWidth === undefined ) headWidth = 0.2 * headLength;
|
|
|
+ }
|
|
|
|
|
|
- this.position.copy( origin );
|
|
|
+ }
|
|
|
|
|
|
- this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
|
|
|
- this.line.matrixAutoUpdate = false;
|
|
|
- this.add( this.line );
|
|
|
+ }
|
|
|
|
|
|
- this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) );
|
|
|
- this.cone.matrixAutoUpdate = false;
|
|
|
- this.add( this.cone );
|
|
|
+ /* Faces */
|
|
|
|
|
|
- this.setDirection( dir );
|
|
|
- this.setLength( length, headLength, headWidth );
|
|
|
+ // Top and bottom faces
|
|
|
|
|
|
- }
|
|
|
+ buildLidFaces();
|
|
|
|
|
|
-}() );
|
|
|
+ // Sides faces
|
|
|
|
|
|
-THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
+ buildSideFaces();
|
|
|
|
|
|
-THREE.ArrowHelper.prototype.setDirection = ( function () {
|
|
|
|
|
|
- var axis = new THREE.Vector3();
|
|
|
- var radians;
|
|
|
+ ///// Internal functions
|
|
|
|
|
|
- return function ( dir ) {
|
|
|
+ function buildLidFaces() {
|
|
|
|
|
|
- // dir is assumed to be normalized
|
|
|
+ if ( bevelEnabled ) {
|
|
|
|
|
|
- if ( dir.y > 0.99999 ) {
|
|
|
+ var layer = 0 ; // steps + 1
|
|
|
+ var offset = vlen * layer;
|
|
|
|
|
|
- this.quaternion.set( 0, 0, 0, 1 );
|
|
|
+ // Bottom faces
|
|
|
|
|
|
- } else if ( dir.y < - 0.99999 ) {
|
|
|
+ for ( i = 0; i < flen; i ++ ) {
|
|
|
|
|
|
- this.quaternion.set( 1, 0, 0, 0 );
|
|
|
+ face = faces[ i ];
|
|
|
+ f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset );
|
|
|
|
|
|
- } else {
|
|
|
+ }
|
|
|
|
|
|
- axis.set( dir.z, 0, - dir.x ).normalize();
|
|
|
+ layer = steps + bevelSegments * 2;
|
|
|
+ offset = vlen * layer;
|
|
|
|
|
|
- radians = Math.acos( dir.y );
|
|
|
+ // Top faces
|
|
|
|
|
|
- this.quaternion.setFromAxisAngle( axis, radians );
|
|
|
+ for ( i = 0; i < flen; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ face = faces[ i ];
|
|
|
+ f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
-}() );
|
|
|
+ } else {
|
|
|
|
|
|
-THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {
|
|
|
+ // Bottom faces
|
|
|
|
|
|
- if ( headLength === undefined ) headLength = 0.2 * length;
|
|
|
- if ( headWidth === undefined ) headWidth = 0.2 * headLength;
|
|
|
+ for ( i = 0; i < flen; i ++ ) {
|
|
|
|
|
|
- this.line.scale.set( 1, length, 1 );
|
|
|
- this.line.updateMatrix();
|
|
|
+ face = faces[ i ];
|
|
|
+ f3( face[ 2 ], face[ 1 ], face[ 0 ] );
|
|
|
|
|
|
- this.cone.scale.set( headWidth, headLength, headWidth );
|
|
|
- this.cone.position.y = length;
|
|
|
- this.cone.updateMatrix();
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ // Top faces
|
|
|
|
|
|
-THREE.ArrowHelper.prototype.setColor = function ( color ) {
|
|
|
+ for ( i = 0; i < flen; i ++ ) {
|
|
|
|
|
|
- this.line.material.color.set( color );
|
|
|
- this.cone.material.color.set( color );
|
|
|
+ face = faces[ i ];
|
|
|
+ f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-// File:src/extras/helpers/BoxHelper.js
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
+ // Create faces for the z-sides of the shape
|
|
|
|
|
|
-THREE.BoxHelper = function ( object ) {
|
|
|
+ function buildSideFaces() {
|
|
|
|
|
|
- var geometry = new THREE.BufferGeometry();
|
|
|
- geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) );
|
|
|
+ var layeroffset = 0;
|
|
|
+ sidewalls( contour, layeroffset );
|
|
|
+ layeroffset += contour.length;
|
|
|
|
|
|
- THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces );
|
|
|
+ for ( h = 0, hl = holes.length; h < hl; h ++ ) {
|
|
|
|
|
|
- if ( object !== undefined ) {
|
|
|
+ ahole = holes[ h ];
|
|
|
+ sidewalls( ahole, layeroffset );
|
|
|
|
|
|
- this.update( object );
|
|
|
+ //, true
|
|
|
+ layeroffset += ahole.length;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
-};
|
|
|
+ function sidewalls( contour, layeroffset ) {
|
|
|
|
|
|
-THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ var j, k;
|
|
|
+ i = contour.length;
|
|
|
|
|
|
-THREE.BoxHelper.prototype.update = function ( object ) {
|
|
|
+ while ( --i >= 0 ) {
|
|
|
|
|
|
- var geometry = object.geometry;
|
|
|
+ j = i;
|
|
|
+ k = i - 1;
|
|
|
+ if ( k < 0 ) k = contour.length - 1;
|
|
|
|
|
|
- if ( geometry.boundingBox === null ) {
|
|
|
+ //console.log('b', i,j, i-1, k,vertices.length);
|
|
|
|
|
|
- geometry.computeBoundingBox();
|
|
|
+ var s = 0, sl = steps + bevelSegments * 2;
|
|
|
|
|
|
- }
|
|
|
+ for ( s = 0; s < sl; s ++ ) {
|
|
|
|
|
|
- var min = geometry.boundingBox.min;
|
|
|
- var max = geometry.boundingBox.max;
|
|
|
+ var slen1 = vlen * s;
|
|
|
+ var slen2 = vlen * ( s + 1 );
|
|
|
|
|
|
- /*
|
|
|
- 5____4
|
|
|
- 1/___0/|
|
|
|
- | 6__|_7
|
|
|
- 2/___3/
|
|
|
+ var a = layeroffset + j + slen1,
|
|
|
+ b = layeroffset + k + slen1,
|
|
|
+ c = layeroffset + k + slen2,
|
|
|
+ d = layeroffset + j + slen2;
|
|
|
|
|
|
- 0: max.x, max.y, max.z
|
|
|
- 1: min.x, max.y, max.z
|
|
|
- 2: min.x, min.y, max.z
|
|
|
- 3: max.x, min.y, max.z
|
|
|
- 4: max.x, max.y, min.z
|
|
|
- 5: min.x, max.y, min.z
|
|
|
- 6: min.x, min.y, min.z
|
|
|
- 7: max.x, min.y, min.z
|
|
|
- */
|
|
|
+ f4( a, b, c, d, contour, s, sl, j, k );
|
|
|
|
|
|
- var vertices = this.geometry.attributes.position.array;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- vertices[ 0 ] = max.x; vertices[ 1 ] = max.y; vertices[ 2 ] = max.z;
|
|
|
- vertices[ 3 ] = min.x; vertices[ 4 ] = max.y; vertices[ 5 ] = max.z;
|
|
|
+ }
|
|
|
|
|
|
- vertices[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z;
|
|
|
- vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z;
|
|
|
|
|
|
- vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z;
|
|
|
- vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z;
|
|
|
+ function v( x, y, z ) {
|
|
|
|
|
|
- vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z;
|
|
|
- vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z;
|
|
|
+ scope.vertices.push( new THREE.Vector3( x, y, z ) );
|
|
|
|
|
|
- //
|
|
|
+ }
|
|
|
|
|
|
- vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z;
|
|
|
- vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z;
|
|
|
+ function f3( a, b, c ) {
|
|
|
|
|
|
- vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z;
|
|
|
- vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z;
|
|
|
+ a += shapesOffset;
|
|
|
+ b += shapesOffset;
|
|
|
+ c += shapesOffset;
|
|
|
|
|
|
- vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z;
|
|
|
- vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z;
|
|
|
+ // normal, color, material
|
|
|
+ scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
|
|
|
|
|
|
- vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z;
|
|
|
- vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z;
|
|
|
+ var uvs = uvgen.generateTopUV( scope, a, b, c );
|
|
|
|
|
|
- //
|
|
|
+ scope.faceVertexUvs[ 0 ].push( uvs );
|
|
|
|
|
|
- vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z;
|
|
|
- vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z;
|
|
|
+ }
|
|
|
|
|
|
- vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z;
|
|
|
- vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z;
|
|
|
+ function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
|
|
|
|
|
|
- vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z;
|
|
|
- vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z;
|
|
|
+ a += shapesOffset;
|
|
|
+ b += shapesOffset;
|
|
|
+ c += shapesOffset;
|
|
|
+ d += shapesOffset;
|
|
|
|
|
|
- vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z;
|
|
|
- vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z;
|
|
|
+ scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) );
|
|
|
+ scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) );
|
|
|
|
|
|
- this.geometry.attributes.position.needsUpdate = true;
|
|
|
+ var uvs = uvgen.generateSideWallUV( scope, a, b, c, d );
|
|
|
|
|
|
- this.geometry.computeBoundingSphere();
|
|
|
+ scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
|
|
|
+ scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
|
|
|
|
|
|
- this.matrix = object.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ }
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/helpers/BoundingBoxHelper.js
|
|
|
+THREE.ExtrudeGeometry.WorldUVGenerator = {
|
|
|
|
|
|
-/**
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
- */
|
|
|
+ generateTopUV: function ( geometry, indexA, indexB, indexC ) {
|
|
|
|
|
|
-// a helper to show the world-axis-aligned bounding box for an object
|
|
|
+ var vertices = geometry.vertices;
|
|
|
|
|
|
-THREE.BoundingBoxHelper = function ( object, hex ) {
|
|
|
+ var a = vertices[ indexA ];
|
|
|
+ var b = vertices[ indexB ];
|
|
|
+ var c = vertices[ indexC ];
|
|
|
|
|
|
- var color = ( hex !== undefined ) ? hex : 0x888888;
|
|
|
+ return [
|
|
|
+ new THREE.Vector2( a.x, a.y ),
|
|
|
+ new THREE.Vector2( b.x, b.y ),
|
|
|
+ new THREE.Vector2( c.x, c.y )
|
|
|
+ ];
|
|
|
|
|
|
- this.object = object;
|
|
|
+ },
|
|
|
|
|
|
- this.box = new THREE.Box3();
|
|
|
+ generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {
|
|
|
|
|
|
- THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );
|
|
|
+ var vertices = geometry.vertices;
|
|
|
+
|
|
|
+ var a = vertices[ indexA ];
|
|
|
+ var b = vertices[ indexB ];
|
|
|
+ var c = vertices[ indexC ];
|
|
|
+ var d = vertices[ indexD ];
|
|
|
|
|
|
+ if ( Math.abs( a.y - b.y ) < 0.01 ) {
|
|
|
+ return [
|
|
|
+ new THREE.Vector2( a.x, 1 - a.z ),
|
|
|
+ new THREE.Vector2( b.x, 1 - b.z ),
|
|
|
+ new THREE.Vector2( c.x, 1 - c.z ),
|
|
|
+ new THREE.Vector2( d.x, 1 - d.z )
|
|
|
+ ];
|
|
|
+ } else {
|
|
|
+ return [
|
|
|
+ new THREE.Vector2( a.y, 1 - a.z ),
|
|
|
+ new THREE.Vector2( b.y, 1 - b.z ),
|
|
|
+ new THREE.Vector2( c.y, 1 - c.z ),
|
|
|
+ new THREE.Vector2( d.y, 1 - d.z )
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
|
|
|
+// File:src/extras/geometries/ShapeGeometry.js
|
|
|
|
|
|
-THREE.BoundingBoxHelper.prototype.update = function () {
|
|
|
+/**
|
|
|
+ * @author jonobr1 / http://jonobr1.com
|
|
|
+ *
|
|
|
+ * Creates a one-sided polygonal geometry from a path shape. Similar to
|
|
|
+ * ExtrudeGeometry.
|
|
|
+ *
|
|
|
+ * parameters = {
|
|
|
+ *
|
|
|
+ * curveSegments: <int>, // number of points on the curves. NOT USED AT THE MOMENT.
|
|
|
+ *
|
|
|
+ * material: <int> // material index for front and back faces
|
|
|
+ * uvGenerator: <Object> // object that provides UV generator functions
|
|
|
+ *
|
|
|
+ * }
|
|
|
+ **/
|
|
|
|
|
|
- this.box.setFromObject( this.object );
|
|
|
+THREE.ShapeGeometry = function ( shapes, options ) {
|
|
|
|
|
|
- this.box.size( this.scale );
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- this.box.center( this.position );
|
|
|
+ this.type = 'ShapeGeometry';
|
|
|
+
|
|
|
+ if ( shapes instanceof Array === false ) shapes = [ shapes ];
|
|
|
+
|
|
|
+ this.addShapeList( shapes, options );
|
|
|
+
|
|
|
+ this.computeFaceNormals();
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/helpers/CameraHelper.js
|
|
|
+THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- *
|
|
|
- * - shows frustum, line of sight and up of the camera
|
|
|
- * - suitable for fast updates
|
|
|
- * - based on frustum visualization in lightgl.js shadowmap example
|
|
|
- * http://evanw.github.com/lightgl.js/tests/shadowmap.html
|
|
|
+ * Add an array of shapes to THREE.ShapeGeometry.
|
|
|
*/
|
|
|
+THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {
|
|
|
|
|
|
-THREE.CameraHelper = function ( camera ) {
|
|
|
+ for ( var i = 0, l = shapes.length; i < l; i ++ ) {
|
|
|
|
|
|
- var geometry = new THREE.Geometry();
|
|
|
- var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
|
|
|
+ this.addShape( shapes[ i ], options );
|
|
|
|
|
|
- var pointMap = {};
|
|
|
+ }
|
|
|
|
|
|
- // colors
|
|
|
+ return this;
|
|
|
|
|
|
- var hexFrustum = 0xffaa00;
|
|
|
- var hexCone = 0xff0000;
|
|
|
- var hexUp = 0x00aaff;
|
|
|
- var hexTarget = 0xffffff;
|
|
|
- var hexCross = 0x333333;
|
|
|
+};
|
|
|
|
|
|
- // near
|
|
|
+/**
|
|
|
+ * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
|
|
|
+ */
|
|
|
+THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {
|
|
|
|
|
|
- addLine( "n1", "n2", hexFrustum );
|
|
|
- addLine( "n2", "n4", hexFrustum );
|
|
|
- addLine( "n4", "n3", hexFrustum );
|
|
|
- addLine( "n3", "n1", hexFrustum );
|
|
|
+ if ( options === undefined ) options = {};
|
|
|
+ var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
|
|
|
|
|
|
- // far
|
|
|
+ var material = options.material;
|
|
|
+ var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;
|
|
|
|
|
|
- addLine( "f1", "f2", hexFrustum );
|
|
|
- addLine( "f2", "f4", hexFrustum );
|
|
|
- addLine( "f4", "f3", hexFrustum );
|
|
|
- addLine( "f3", "f1", hexFrustum );
|
|
|
+ //
|
|
|
|
|
|
- // sides
|
|
|
+ var i, l, hole, s;
|
|
|
|
|
|
- addLine( "n1", "f1", hexFrustum );
|
|
|
- addLine( "n2", "f2", hexFrustum );
|
|
|
- addLine( "n3", "f3", hexFrustum );
|
|
|
- addLine( "n4", "f4", hexFrustum );
|
|
|
+ var shapesOffset = this.vertices.length;
|
|
|
+ var shapePoints = shape.extractPoints( curveSegments );
|
|
|
|
|
|
- // cone
|
|
|
+ var vertices = shapePoints.shape;
|
|
|
+ var holes = shapePoints.holes;
|
|
|
|
|
|
- addLine( "p", "n1", hexCone );
|
|
|
- addLine( "p", "n2", hexCone );
|
|
|
- addLine( "p", "n3", hexCone );
|
|
|
- addLine( "p", "n4", hexCone );
|
|
|
+ var reverse = ! THREE.Shape.Utils.isClockWise( vertices );
|
|
|
|
|
|
- // up
|
|
|
+ if ( reverse ) {
|
|
|
|
|
|
- addLine( "u1", "u2", hexUp );
|
|
|
- addLine( "u2", "u3", hexUp );
|
|
|
- addLine( "u3", "u1", hexUp );
|
|
|
+ vertices = vertices.reverse();
|
|
|
|
|
|
- // target
|
|
|
+ // Maybe we should also check if holes are in the opposite direction, just to be safe...
|
|
|
|
|
|
- addLine( "c", "t", hexTarget );
|
|
|
- addLine( "p", "c", hexCross );
|
|
|
+ for ( i = 0, l = holes.length; i < l; i ++ ) {
|
|
|
|
|
|
- // cross
|
|
|
+ hole = holes[ i ];
|
|
|
|
|
|
- addLine( "cn1", "cn2", hexCross );
|
|
|
- addLine( "cn3", "cn4", hexCross );
|
|
|
+ if ( THREE.Shape.Utils.isClockWise( hole ) ) {
|
|
|
|
|
|
- addLine( "cf1", "cf2", hexCross );
|
|
|
- addLine( "cf3", "cf4", hexCross );
|
|
|
+ holes[ i ] = hole.reverse();
|
|
|
|
|
|
- function addLine( a, b, hex ) {
|
|
|
+ }
|
|
|
|
|
|
- addPoint( a, hex );
|
|
|
- addPoint( b, hex );
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ reverse = false;
|
|
|
|
|
|
- function addPoint( id, hex ) {
|
|
|
+ }
|
|
|
|
|
|
- geometry.vertices.push( new THREE.Vector3() );
|
|
|
- geometry.colors.push( new THREE.Color( hex ) );
|
|
|
+ var faces = THREE.Shape.Utils.triangulateShape( vertices, holes );
|
|
|
|
|
|
- if ( pointMap[ id ] === undefined ) {
|
|
|
+ // Vertices
|
|
|
|
|
|
- pointMap[ id ] = [];
|
|
|
+ var contour = vertices;
|
|
|
|
|
|
- }
|
|
|
+ for ( i = 0, l = holes.length; i < l; i ++ ) {
|
|
|
|
|
|
- pointMap[ id ].push( geometry.vertices.length - 1 );
|
|
|
+ hole = holes[ i ];
|
|
|
+ vertices = vertices.concat( hole );
|
|
|
|
|
|
}
|
|
|
|
|
|
- THREE.Line.call( this, geometry, material, THREE.LinePieces );
|
|
|
-
|
|
|
- this.camera = camera;
|
|
|
- this.matrix = camera.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
-
|
|
|
- this.pointMap = pointMap;
|
|
|
+ //
|
|
|
|
|
|
- this.update();
|
|
|
+ var vert, vlen = vertices.length;
|
|
|
+ var face, flen = faces.length;
|
|
|
+ var cont, clen = contour.length;
|
|
|
|
|
|
-};
|
|
|
+ for ( i = 0; i < vlen; i ++ ) {
|
|
|
|
|
|
-THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ vert = vertices[ i ];
|
|
|
|
|
|
-THREE.CameraHelper.prototype.update = function () {
|
|
|
+ this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );
|
|
|
|
|
|
- var geometry, pointMap;
|
|
|
-
|
|
|
- var vector = new THREE.Vector3();
|
|
|
- var camera = new THREE.Camera();
|
|
|
+ }
|
|
|
|
|
|
- var setPoint = function ( point, x, y, z ) {
|
|
|
+ for ( i = 0; i < flen; i ++ ) {
|
|
|
|
|
|
- vector.set( x, y, z ).unproject( camera );
|
|
|
+ face = faces[ i ];
|
|
|
|
|
|
- var points = pointMap[ point ];
|
|
|
+ var a = face[ 0 ] + shapesOffset;
|
|
|
+ var b = face[ 1 ] + shapesOffset;
|
|
|
+ var c = face[ 2 ] + shapesOffset;
|
|
|
|
|
|
- if ( points !== undefined ) {
|
|
|
+ this.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) );
|
|
|
|
|
|
- for ( var i = 0, il = points.length; i < il; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- geometry.vertices[ points[ i ] ].copy( vector );
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+// File:src/extras/geometries/LatheGeometry.js
|
|
|
|
|
|
- }
|
|
|
+/**
|
|
|
+ * @author astrodud / http://astrodud.isgreat.org/
|
|
|
+ * @author zz85 / https://github.com/zz85
|
|
|
+ * @author bhouston / http://exocortex.com
|
|
|
+ */
|
|
|
|
|
|
- };
|
|
|
+// points - to create a closed torus, one must use a set of points
|
|
|
+// like so: [ a, b, c, d, a ], see first is the same as last.
|
|
|
+// segments - the number of circumference segments to create
|
|
|
+// phiStart - the starting radian
|
|
|
+// phiLength - the radian (0 to 2*PI) range of the lathed section
|
|
|
+// 2*pi is a closed lathe, less than 2PI is a portion.
|
|
|
|
|
|
- return function () {
|
|
|
+THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {
|
|
|
|
|
|
- geometry = this.geometry;
|
|
|
- pointMap = this.pointMap;
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- var w = 1, h = 1;
|
|
|
+ this.type = 'LatheGeometry';
|
|
|
|
|
|
- // we need just camera projection matrix
|
|
|
- // world matrix must be identity
|
|
|
+ this.parameters = {
|
|
|
+ points: points,
|
|
|
+ segments: segments,
|
|
|
+ phiStart: phiStart,
|
|
|
+ phiLength: phiLength
|
|
|
+ };
|
|
|
|
|
|
- camera.projectionMatrix.copy( this.camera.projectionMatrix );
|
|
|
+ segments = segments || 12;
|
|
|
+ phiStart = phiStart || 0;
|
|
|
+ phiLength = phiLength || 2 * Math.PI;
|
|
|
|
|
|
- // center / target
|
|
|
+ var inversePointLength = 1.0 / ( points.length - 1 );
|
|
|
+ var inverseSegments = 1.0 / segments;
|
|
|
|
|
|
- setPoint( "c", 0, 0, - 1 );
|
|
|
- setPoint( "t", 0, 0, 1 );
|
|
|
+ for ( var i = 0, il = segments; i <= il; i ++ ) {
|
|
|
|
|
|
- // near
|
|
|
+ var phi = phiStart + i * inverseSegments * phiLength;
|
|
|
|
|
|
- setPoint( "n1", - w, - h, - 1 );
|
|
|
- setPoint( "n2", w, - h, - 1 );
|
|
|
- setPoint( "n3", - w, h, - 1 );
|
|
|
- setPoint( "n4", w, h, - 1 );
|
|
|
+ var c = Math.cos( phi ),
|
|
|
+ s = Math.sin( phi );
|
|
|
|
|
|
- // far
|
|
|
+ for ( var j = 0, jl = points.length; j < jl; j ++ ) {
|
|
|
|
|
|
- setPoint( "f1", - w, - h, 1 );
|
|
|
- setPoint( "f2", w, - h, 1 );
|
|
|
- setPoint( "f3", - w, h, 1 );
|
|
|
- setPoint( "f4", w, h, 1 );
|
|
|
+ var pt = points[ j ];
|
|
|
|
|
|
- // up
|
|
|
+ var vertex = new THREE.Vector3();
|
|
|
|
|
|
- setPoint( "u1", w * 0.7, h * 1.1, - 1 );
|
|
|
- setPoint( "u2", - w * 0.7, h * 1.1, - 1 );
|
|
|
- setPoint( "u3", 0, h * 2, - 1 );
|
|
|
+ vertex.x = c * pt.x - s * pt.y;
|
|
|
+ vertex.y = s * pt.x + c * pt.y;
|
|
|
+ vertex.z = pt.z;
|
|
|
|
|
|
- // cross
|
|
|
+ this.vertices.push( vertex );
|
|
|
|
|
|
- setPoint( "cf1", - w, 0, 1 );
|
|
|
- setPoint( "cf2", w, 0, 1 );
|
|
|
- setPoint( "cf3", 0, - h, 1 );
|
|
|
- setPoint( "cf4", 0, h, 1 );
|
|
|
+ }
|
|
|
|
|
|
- setPoint( "cn1", - w, 0, - 1 );
|
|
|
- setPoint( "cn2", w, 0, - 1 );
|
|
|
- setPoint( "cn3", 0, - h, - 1 );
|
|
|
- setPoint( "cn4", 0, h, - 1 );
|
|
|
+ }
|
|
|
|
|
|
- geometry.verticesNeedUpdate = true;
|
|
|
+ var np = points.length;
|
|
|
|
|
|
- };
|
|
|
+ for ( var i = 0, il = segments; i < il; i ++ ) {
|
|
|
|
|
|
-}();
|
|
|
+ for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {
|
|
|
|
|
|
-// File:src/extras/helpers/DirectionalLightHelper.js
|
|
|
+ var base = j + np * i;
|
|
|
+ var a = base;
|
|
|
+ var b = base + np;
|
|
|
+ var c = base + 1 + np;
|
|
|
+ var d = base + 1;
|
|
|
|
|
|
-/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
- */
|
|
|
+ var u0 = i * inverseSegments;
|
|
|
+ var v0 = j * inversePointLength;
|
|
|
+ var u1 = u0 + inverseSegments;
|
|
|
+ var v1 = v0 + inversePointLength;
|
|
|
|
|
|
-THREE.DirectionalLightHelper = function ( light, size ) {
|
|
|
+ this.faces.push( new THREE.Face3( a, b, d ) );
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [
|
|
|
|
|
|
- this.light = light;
|
|
|
- this.light.updateMatrixWorld();
|
|
|
+ new THREE.Vector2( u0, v0 ),
|
|
|
+ new THREE.Vector2( u1, v0 ),
|
|
|
+ new THREE.Vector2( u0, v1 )
|
|
|
|
|
|
- this.matrix = light.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ ] );
|
|
|
|
|
|
- size = size || 1;
|
|
|
+ this.faces.push( new THREE.Face3( b, c, d ) );
|
|
|
|
|
|
- var geometry = new THREE.Geometry();
|
|
|
- geometry.vertices.push(
|
|
|
- new THREE.Vector3( - size, size, 0 ),
|
|
|
- new THREE.Vector3( size, size, 0 ),
|
|
|
- new THREE.Vector3( size, - size, 0 ),
|
|
|
- new THREE.Vector3( - size, - size, 0 ),
|
|
|
- new THREE.Vector3( - size, size, 0 )
|
|
|
- );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [
|
|
|
|
|
|
- var material = new THREE.LineBasicMaterial( { fog: false } );
|
|
|
- material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
+ new THREE.Vector2( u1, v0 ),
|
|
|
+ new THREE.Vector2( u1, v1 ),
|
|
|
+ new THREE.Vector2( u0, v1 )
|
|
|
|
|
|
- this.lightPlane = new THREE.Line( geometry, material );
|
|
|
- this.add( this.lightPlane );
|
|
|
+ ] );
|
|
|
|
|
|
- geometry = new THREE.Geometry();
|
|
|
- geometry.vertices.push(
|
|
|
- new THREE.Vector3(),
|
|
|
- new THREE.Vector3()
|
|
|
- );
|
|
|
|
|
|
- material = new THREE.LineBasicMaterial( { fog: false } );
|
|
|
- material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
+ }
|
|
|
|
|
|
- this.targetLine = new THREE.Line( geometry, material );
|
|
|
- this.add( this.targetLine );
|
|
|
+ }
|
|
|
|
|
|
- this.update();
|
|
|
+ this.mergeVertices();
|
|
|
+ this.computeFaceNormals();
|
|
|
+ this.computeVertexNormals();
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
+THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
-THREE.DirectionalLightHelper.prototype.dispose = function () {
|
|
|
+// File:src/extras/geometries/PlaneGeometry.js
|
|
|
|
|
|
- this.lightPlane.geometry.dispose();
|
|
|
- this.lightPlane.material.dispose();
|
|
|
- this.targetLine.geometry.dispose();
|
|
|
- this.targetLine.material.dispose();
|
|
|
-};
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
|
|
|
+ */
|
|
|
|
|
|
-THREE.DirectionalLightHelper.prototype.update = function () {
|
|
|
+THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {
|
|
|
|
|
|
- var v1 = new THREE.Vector3();
|
|
|
- var v2 = new THREE.Vector3();
|
|
|
- var v3 = new THREE.Vector3();
|
|
|
+ console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' );
|
|
|
|
|
|
- return function () {
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- v1.setFromMatrixPosition( this.light.matrixWorld );
|
|
|
- v2.setFromMatrixPosition( this.light.target.matrixWorld );
|
|
|
- v3.subVectors( v2, v1 );
|
|
|
+ this.type = 'PlaneGeometry';
|
|
|
|
|
|
- this.lightPlane.lookAt( v3 );
|
|
|
- this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
+ this.parameters = {
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
+ widthSegments: widthSegments,
|
|
|
+ heightSegments: heightSegments
|
|
|
+ };
|
|
|
|
|
|
- this.targetLine.geometry.vertices[ 1 ].copy( v3 );
|
|
|
- this.targetLine.geometry.verticesNeedUpdate = true;
|
|
|
- this.targetLine.material.color.copy( this.lightPlane.material.color );
|
|
|
+ this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );
|
|
|
|
|
|
- };
|
|
|
+};
|
|
|
|
|
|
-}();
|
|
|
+THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
-// File:src/extras/helpers/EdgesHelper.js
|
|
|
+// File:src/extras/geometries/PlaneBufferGeometry.js
|
|
|
|
|
|
/**
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
|
|
|
*/
|
|
|
|
|
|
-THREE.EdgesHelper = function ( object, hex ) {
|
|
|
+THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) {
|
|
|
|
|
|
- var color = ( hex !== undefined ) ? hex : 0xffffff;
|
|
|
+ THREE.BufferGeometry.call( this );
|
|
|
|
|
|
- var edge = [ 0, 0 ], hash = {};
|
|
|
- var sortFunction = function ( a, b ) { return a - b };
|
|
|
+ this.type = 'PlaneBufferGeometry';
|
|
|
|
|
|
- var keys = [ 'a', 'b', 'c' ];
|
|
|
- var geometry = new THREE.BufferGeometry();
|
|
|
+ this.parameters = {
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
+ widthSegments: widthSegments,
|
|
|
+ heightSegments: heightSegments
|
|
|
+ };
|
|
|
|
|
|
- var geometry2 = object.geometry.clone();
|
|
|
+ var width_half = width / 2;
|
|
|
+ var height_half = height / 2;
|
|
|
|
|
|
- geometry2.mergeVertices();
|
|
|
- geometry2.computeFaceNormals();
|
|
|
+ var gridX = widthSegments || 1;
|
|
|
+ var gridY = heightSegments || 1;
|
|
|
|
|
|
- var vertices = geometry2.vertices;
|
|
|
- var faces = geometry2.faces;
|
|
|
- var numEdges = 0;
|
|
|
+ var gridX1 = gridX + 1;
|
|
|
+ var gridY1 = gridY + 1;
|
|
|
|
|
|
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+ var segment_width = width / gridX;
|
|
|
+ var segment_height = height / gridY;
|
|
|
|
|
|
- var face = faces[ i ];
|
|
|
+ var vertices = new Float32Array( gridX1 * gridY1 * 3 );
|
|
|
+ var normals = new Float32Array( gridX1 * gridY1 * 3 );
|
|
|
+ var uvs = new Float32Array( gridX1 * gridY1 * 2 );
|
|
|
|
|
|
- for ( var j = 0; j < 3; j ++ ) {
|
|
|
+ var offset = 0;
|
|
|
+ var offset2 = 0;
|
|
|
|
|
|
- edge[ 0 ] = face[ keys[ j ] ];
|
|
|
- edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
|
|
|
- edge.sort( sortFunction );
|
|
|
+ for ( var iy = 0; iy < gridY1; iy ++ ) {
|
|
|
+
|
|
|
+ var y = iy * segment_height - height_half;
|
|
|
|
|
|
- var key = edge.toString();
|
|
|
+ for ( var ix = 0; ix < gridX1; ix ++ ) {
|
|
|
|
|
|
- if ( hash[ key ] === undefined ) {
|
|
|
+ var x = ix * segment_width - width_half;
|
|
|
|
|
|
- hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };
|
|
|
- numEdges ++;
|
|
|
+ vertices[ offset ] = x;
|
|
|
+ vertices[ offset + 1 ] = - y;
|
|
|
|
|
|
- } else {
|
|
|
+ normals[ offset + 2 ] = 1;
|
|
|
|
|
|
- hash[ key ].face2 = i;
|
|
|
+ uvs[ offset2 ] = ix / gridX;
|
|
|
+ uvs[ offset2 + 1 ] = 1 - ( iy / gridY );
|
|
|
|
|
|
- }
|
|
|
+ offset += 3;
|
|
|
+ offset2 += 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( numEdges * 2 * 3 ), 3 ) );
|
|
|
+ offset = 0;
|
|
|
|
|
|
- var coords = geometry.attributes.position.array;
|
|
|
+ var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );
|
|
|
|
|
|
- var index = 0;
|
|
|
+ for ( var iy = 0; iy < gridY; iy ++ ) {
|
|
|
|
|
|
- for ( var key in hash ) {
|
|
|
+ for ( var ix = 0; ix < gridX; ix ++ ) {
|
|
|
|
|
|
- var h = hash[ key ];
|
|
|
+ var a = ix + gridX1 * iy;
|
|
|
+ var b = ix + gridX1 * ( iy + 1 );
|
|
|
+ var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
|
|
|
+ var d = ( ix + 1 ) + gridX1 * iy;
|
|
|
|
|
|
- if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK
|
|
|
+ indices[ offset ] = a;
|
|
|
+ indices[ offset + 1 ] = b;
|
|
|
+ indices[ offset + 2 ] = d;
|
|
|
|
|
|
- var vertex = vertices[ h.vert1 ];
|
|
|
- coords[ index ++ ] = vertex.x;
|
|
|
- coords[ index ++ ] = vertex.y;
|
|
|
- coords[ index ++ ] = vertex.z;
|
|
|
+ indices[ offset + 3 ] = b;
|
|
|
+ indices[ offset + 4 ] = c;
|
|
|
+ indices[ offset + 5 ] = d;
|
|
|
|
|
|
- vertex = vertices[ h.vert2 ];
|
|
|
- coords[ index ++ ] = vertex.x;
|
|
|
- coords[ index ++ ] = vertex.y;
|
|
|
- coords[ index ++ ] = vertex.z;
|
|
|
+ offset += 6;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
|
|
|
-
|
|
|
- this.matrix = object.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
|
|
|
+ this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
|
|
|
+ this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
|
|
|
+ this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
|
|
|
|
|
|
-// File:src/extras/helpers/FaceNormalsHelper.js
|
|
|
+// File:src/extras/geometries/RingGeometry.js
|
|
|
|
|
|
/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
-*/
|
|
|
-
|
|
|
-THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {
|
|
|
-
|
|
|
- this.object = object;
|
|
|
+ * @author Kaleb Murphy
|
|
|
+ */
|
|
|
|
|
|
- this.size = ( size !== undefined ) ? size : 1;
|
|
|
+THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
|
|
|
|
|
|
- var color = ( hex !== undefined ) ? hex : 0xffff00;
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- var width = ( linewidth !== undefined ) ? linewidth : 1;
|
|
|
+ this.type = 'RingGeometry';
|
|
|
|
|
|
- var geometry = new THREE.Geometry();
|
|
|
+ this.parameters = {
|
|
|
+ innerRadius: innerRadius,
|
|
|
+ outerRadius: outerRadius,
|
|
|
+ thetaSegments: thetaSegments,
|
|
|
+ phiSegments: phiSegments,
|
|
|
+ thetaStart: thetaStart,
|
|
|
+ thetaLength: thetaLength
|
|
|
+ };
|
|
|
|
|
|
- var faces = this.object.geometry.faces;
|
|
|
+ innerRadius = innerRadius || 0;
|
|
|
+ outerRadius = outerRadius || 50;
|
|
|
|
|
|
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
|
|
|
+ thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
|
|
|
|
|
|
- geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
|
|
|
+ thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
|
|
|
+ phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;
|
|
|
|
|
|
- }
|
|
|
+ var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
|
|
|
|
|
|
- THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
|
|
|
+ for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring
|
|
|
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle
|
|
|
|
|
|
- this.normalMatrix = new THREE.Matrix3();
|
|
|
+ var vertex = new THREE.Vector3();
|
|
|
+ var segment = thetaStart + o / thetaSegments * thetaLength;
|
|
|
+ vertex.x = radius * Math.cos( segment );
|
|
|
+ vertex.y = radius * Math.sin( segment );
|
|
|
|
|
|
- this.update();
|
|
|
+ this.vertices.push( vertex );
|
|
|
+ uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) );
|
|
|
+ }
|
|
|
|
|
|
-};
|
|
|
+ radius += radiusStep;
|
|
|
|
|
|
-THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ }
|
|
|
|
|
|
-THREE.FaceNormalsHelper.prototype.update = function () {
|
|
|
+ var n = new THREE.Vector3( 0, 0, 1 );
|
|
|
|
|
|
- var vertices = this.geometry.vertices;
|
|
|
+ for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring
|
|
|
|
|
|
- var object = this.object;
|
|
|
- var objectVertices = object.geometry.vertices;
|
|
|
- var objectFaces = object.geometry.faces;
|
|
|
- var objectWorldMatrix = object.matrixWorld;
|
|
|
+ var thetaSegment = i * (thetaSegments + 1);
|
|
|
|
|
|
- object.updateMatrixWorld( true );
|
|
|
+ for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle
|
|
|
|
|
|
- this.normalMatrix.getNormalMatrix( objectWorldMatrix );
|
|
|
+ var segment = o + thetaSegment;
|
|
|
|
|
|
- for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) {
|
|
|
+ var v1 = segment;
|
|
|
+ var v2 = segment + thetaSegments + 1;
|
|
|
+ var v3 = segment + thetaSegments + 2;
|
|
|
|
|
|
- var face = objectFaces[ i ];
|
|
|
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]);
|
|
|
|
|
|
- vertices[ i2 ].copy( objectVertices[ face.a ] )
|
|
|
- .add( objectVertices[ face.b ] )
|
|
|
- .add( objectVertices[ face.c ] )
|
|
|
- .divideScalar( 3 )
|
|
|
- .applyMatrix4( objectWorldMatrix );
|
|
|
+ v1 = segment;
|
|
|
+ v2 = segment + thetaSegments + 2;
|
|
|
+ v3 = segment + 1;
|
|
|
|
|
|
- vertices[ i2 + 1 ].copy( face.normal )
|
|
|
- .applyMatrix3( this.normalMatrix )
|
|
|
- .normalize()
|
|
|
- .multiplyScalar( this.size )
|
|
|
- .add( vertices[ i2 ] );
|
|
|
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]);
|
|
|
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- this.geometry.verticesNeedUpdate = true;
|
|
|
+ this.computeFaceNormals();
|
|
|
|
|
|
- return this;
|
|
|
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
|
|
|
|
|
|
};
|
|
|
|
|
|
+THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+
|
|
|
|
|
|
-// File:src/extras/helpers/GridHelper.js
|
|
|
+// File:src/extras/geometries/SphereGeometry.js
|
|
|
|
|
|
/**
|
|
|
* @author mrdoob / http://mrdoob.com/
|
|
|
*/
|
|
|
|
|
|
-THREE.GridHelper = function ( size, step ) {
|
|
|
-
|
|
|
- var geometry = new THREE.Geometry();
|
|
|
- var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
|
|
|
+THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
|
|
|
|
|
|
- this.color1 = new THREE.Color( 0x444444 );
|
|
|
- this.color2 = new THREE.Color( 0x888888 );
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- for ( var i = - size; i <= size; i += step ) {
|
|
|
+ this.type = 'SphereGeometry';
|
|
|
|
|
|
- geometry.vertices.push(
|
|
|
- new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ),
|
|
|
- new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size )
|
|
|
- );
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ widthSegments: widthSegments,
|
|
|
+ heightSegments: heightSegments,
|
|
|
+ phiStart: phiStart,
|
|
|
+ phiLength: phiLength,
|
|
|
+ thetaStart: thetaStart,
|
|
|
+ thetaLength: thetaLength
|
|
|
+ };
|
|
|
|
|
|
- var color = i === 0 ? this.color1 : this.color2;
|
|
|
+ radius = radius || 50;
|
|
|
|
|
|
- geometry.colors.push( color, color, color, color );
|
|
|
+ widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
|
|
|
+ heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
|
|
|
|
|
|
- }
|
|
|
+ phiStart = phiStart !== undefined ? phiStart : 0;
|
|
|
+ phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
|
|
|
|
|
|
- THREE.Line.call( this, geometry, material, THREE.LinePieces );
|
|
|
+ thetaStart = thetaStart !== undefined ? thetaStart : 0;
|
|
|
+ thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
|
|
|
|
|
|
-};
|
|
|
+ var x, y, vertices = [], uvs = [];
|
|
|
|
|
|
-THREE.GridHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ for ( y = 0; y <= heightSegments; y ++ ) {
|
|
|
|
|
|
-THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) {
|
|
|
+ var verticesRow = [];
|
|
|
+ var uvsRow = [];
|
|
|
|
|
|
- this.color1.set( colorCenterLine );
|
|
|
- this.color2.set( colorGrid );
|
|
|
+ for ( x = 0; x <= widthSegments; x ++ ) {
|
|
|
|
|
|
- this.geometry.colorsNeedUpdate = true;
|
|
|
+ var u = x / widthSegments;
|
|
|
+ var v = y / heightSegments;
|
|
|
|
|
|
-}
|
|
|
+ var vertex = new THREE.Vector3();
|
|
|
+ vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
|
|
|
+ vertex.y = radius * Math.cos( thetaStart + v * thetaLength );
|
|
|
+ vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
|
|
|
|
|
|
-// File:src/extras/helpers/HemisphereLightHelper.js
|
|
|
+ this.vertices.push( vertex );
|
|
|
|
|
|
-/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
+ verticesRow.push( this.vertices.length - 1 );
|
|
|
+ uvsRow.push( new THREE.Vector2( u, 1 - v ) );
|
|
|
|
|
|
-THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) {
|
|
|
+ }
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ vertices.push( verticesRow );
|
|
|
+ uvs.push( uvsRow );
|
|
|
|
|
|
- this.light = light;
|
|
|
- this.light.updateMatrixWorld();
|
|
|
+ }
|
|
|
|
|
|
- this.matrix = light.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ for ( y = 0; y < heightSegments; y ++ ) {
|
|
|
|
|
|
- this.colors = [ new THREE.Color(), new THREE.Color() ];
|
|
|
+ for ( x = 0; x < widthSegments; x ++ ) {
|
|
|
|
|
|
- var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
|
|
|
- geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
|
|
|
+ var v1 = vertices[ y ][ x + 1 ];
|
|
|
+ var v2 = vertices[ y ][ x ];
|
|
|
+ var v3 = vertices[ y + 1 ][ x ];
|
|
|
+ var v4 = vertices[ y + 1 ][ x + 1 ];
|
|
|
|
|
|
- for ( var i = 0, il = 8; i < il; i ++ ) {
|
|
|
+ var n1 = this.vertices[ v1 ].clone().normalize();
|
|
|
+ var n2 = this.vertices[ v2 ].clone().normalize();
|
|
|
+ var n3 = this.vertices[ v3 ].clone().normalize();
|
|
|
+ var n4 = this.vertices[ v4 ].clone().normalize();
|
|
|
|
|
|
- geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ];
|
|
|
+ var uv1 = uvs[ y ][ x + 1 ].clone();
|
|
|
+ var uv2 = uvs[ y ][ x ].clone();
|
|
|
+ var uv3 = uvs[ y + 1 ][ x ].clone();
|
|
|
+ var uv4 = uvs[ y + 1 ][ x + 1 ].clone();
|
|
|
|
|
|
- }
|
|
|
+ if ( Math.abs( this.vertices[ v1 ].y ) === radius ) {
|
|
|
|
|
|
- var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } );
|
|
|
+ uv1.x = ( uv1.x + uv2.x ) / 2;
|
|
|
+ this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] );
|
|
|
|
|
|
- this.lightSphere = new THREE.Mesh( geometry, material );
|
|
|
- this.add( this.lightSphere );
|
|
|
+ } else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) {
|
|
|
|
|
|
- this.update();
|
|
|
+ uv3.x = ( uv3.x + uv4.x ) / 2;
|
|
|
+ this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
|
|
|
|
|
|
-};
|
|
|
+ } else {
|
|
|
|
|
|
-THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
+ this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );
|
|
|
|
|
|
-THREE.HemisphereLightHelper.prototype.dispose = function () {
|
|
|
- this.lightSphere.geometry.dispose();
|
|
|
- this.lightSphere.material.dispose();
|
|
|
-};
|
|
|
+ this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
|
|
|
|
|
|
-THREE.HemisphereLightHelper.prototype.update = function () {
|
|
|
+ }
|
|
|
|
|
|
- var vector = new THREE.Vector3();
|
|
|
+ }
|
|
|
|
|
|
- return function () {
|
|
|
+ }
|
|
|
|
|
|
- this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
- this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity );
|
|
|
+ this.computeFaceNormals();
|
|
|
|
|
|
- this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );
|
|
|
- this.lightSphere.geometry.colorsNeedUpdate = true;
|
|
|
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
-}();
|
|
|
+THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
-// File:src/extras/helpers/PointLightHelper.js
|
|
|
+// File:src/extras/geometries/TextGeometry.js
|
|
|
|
|
|
/**
|
|
|
+ * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
* @author alteredq / http://alteredqualia.com/
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
+ *
|
|
|
+ * For creating 3D text geometry in three.js
|
|
|
+ *
|
|
|
+ * Text = 3D Text
|
|
|
+ *
|
|
|
+ * parameters = {
|
|
|
+ * size: <float>, // size of the text
|
|
|
+ * height: <float>, // thickness to extrude text
|
|
|
+ * curveSegments: <int>, // number of points on the curves
|
|
|
+ *
|
|
|
+ * font: <string>, // font name
|
|
|
+ * weight: <string>, // font weight (normal, bold)
|
|
|
+ * style: <string>, // font style (normal, italics)
|
|
|
+ *
|
|
|
+ * bevelEnabled: <bool>, // turn on bevel
|
|
|
+ * bevelThickness: <float>, // how deep into text bevel goes
|
|
|
+ * bevelSize: <float>, // how far from text outline is bevel
|
|
|
+ * }
|
|
|
+ *
|
|
|
*/
|
|
|
|
|
|
-THREE.PointLightHelper = function ( light, sphereSize ) {
|
|
|
+/* Usage Examples
|
|
|
|
|
|
- this.light = light;
|
|
|
- this.light.updateMatrixWorld();
|
|
|
+ // TextGeometry wrapper
|
|
|
|
|
|
- var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
|
|
|
- var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
|
|
|
- material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
+ var text3d = new TextGeometry( text, options );
|
|
|
|
|
|
- THREE.Mesh.call( this, geometry, material );
|
|
|
+ // Complete manner
|
|
|
|
|
|
- this.matrix = this.light.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ var textShapes = THREE.FontUtils.generateShapes( text, options );
|
|
|
+ var text3d = new ExtrudeGeometry( textShapes, options );
|
|
|
|
|
|
- /*
|
|
|
- var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
|
|
|
- var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
|
|
|
+*/
|
|
|
|
|
|
- this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
|
|
|
- this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
|
|
|
|
|
|
- var d = light.distance;
|
|
|
+THREE.TextGeometry = function ( text, parameters ) {
|
|
|
|
|
|
- if ( d === 0.0 ) {
|
|
|
+ parameters = parameters || {};
|
|
|
|
|
|
- this.lightDistance.visible = false;
|
|
|
+ var textShapes = THREE.FontUtils.generateShapes( text, parameters );
|
|
|
|
|
|
- } else {
|
|
|
+ // translate parameters to ExtrudeGeometry API
|
|
|
|
|
|
- this.lightDistance.scale.set( d, d, d );
|
|
|
+ parameters.amount = parameters.height !== undefined ? parameters.height : 50;
|
|
|
|
|
|
- }
|
|
|
+ // defaults
|
|
|
|
|
|
- this.add( this.lightDistance );
|
|
|
- */
|
|
|
+ if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
|
|
|
+ if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
|
|
|
+ if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
|
|
|
+
|
|
|
+ THREE.ExtrudeGeometry.call( this, textShapes, parameters );
|
|
|
+
|
|
|
+ this.type = 'TextGeometry';
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype );
|
|
|
+THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
|
|
|
|
|
|
-THREE.PointLightHelper.prototype.dispose = function () {
|
|
|
+// File:src/extras/geometries/TorusGeometry.js
|
|
|
|
|
|
- this.geometry.dispose();
|
|
|
- this.material.dispose();
|
|
|
-};
|
|
|
+/**
|
|
|
+ * @author oosmoxiecode
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888
|
|
|
+ */
|
|
|
|
|
|
-THREE.PointLightHelper.prototype.update = function () {
|
|
|
+THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {
|
|
|
|
|
|
- this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- /*
|
|
|
- var d = this.light.distance;
|
|
|
+ this.type = 'TorusGeometry';
|
|
|
|
|
|
- if ( d === 0.0 ) {
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ tube: tube,
|
|
|
+ radialSegments: radialSegments,
|
|
|
+ tubularSegments: tubularSegments,
|
|
|
+ arc: arc
|
|
|
+ };
|
|
|
|
|
|
- this.lightDistance.visible = false;
|
|
|
+ radius = radius || 100;
|
|
|
+ tube = tube || 40;
|
|
|
+ radialSegments = radialSegments || 8;
|
|
|
+ tubularSegments = tubularSegments || 6;
|
|
|
+ arc = arc || Math.PI * 2;
|
|
|
|
|
|
- } else {
|
|
|
+ var center = new THREE.Vector3(), uvs = [], normals = [];
|
|
|
|
|
|
- this.lightDistance.visible = true;
|
|
|
- this.lightDistance.scale.set( d, d, d );
|
|
|
+ for ( var j = 0; j <= radialSegments; j ++ ) {
|
|
|
|
|
|
- }
|
|
|
- */
|
|
|
+ for ( var i = 0; i <= tubularSegments; i ++ ) {
|
|
|
|
|
|
-};
|
|
|
+ var u = i / tubularSegments * arc;
|
|
|
+ var v = j / radialSegments * Math.PI * 2;
|
|
|
|
|
|
-// File:src/extras/helpers/SkeletonHelper.js
|
|
|
+ center.x = radius * Math.cos( u );
|
|
|
+ center.y = radius * Math.sin( u );
|
|
|
|
|
|
-/**
|
|
|
- * @author Sean Griffin / http://twitter.com/sgrif
|
|
|
- * @author Michael Guerrero / http://realitymeltdown.com
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author ikerr / http://verold.com
|
|
|
- */
|
|
|
+ var vertex = new THREE.Vector3();
|
|
|
+ vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
|
|
|
+ vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
|
|
|
+ vertex.z = tube * Math.sin( v );
|
|
|
|
|
|
-THREE.SkeletonHelper = function ( object ) {
|
|
|
+ this.vertices.push( vertex );
|
|
|
|
|
|
- this.bones = this.getBoneList( object );
|
|
|
+ uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) );
|
|
|
+ normals.push( vertex.clone().sub( center ).normalize() );
|
|
|
|
|
|
- var geometry = new THREE.Geometry();
|
|
|
+ }
|
|
|
|
|
|
- for ( var i = 0; i < this.bones.length; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- var bone = this.bones[ i ];
|
|
|
+ for ( var j = 1; j <= radialSegments; j ++ ) {
|
|
|
|
|
|
- if ( bone.parent instanceof THREE.Bone ) {
|
|
|
+ for ( var i = 1; i <= tubularSegments; i ++ ) {
|
|
|
|
|
|
- geometry.vertices.push( new THREE.Vector3() );
|
|
|
- geometry.vertices.push( new THREE.Vector3() );
|
|
|
- geometry.colors.push( new THREE.Color( 0, 0, 1 ) );
|
|
|
- geometry.colors.push( new THREE.Color( 0, 1, 0 ) );
|
|
|
+ var a = ( tubularSegments + 1 ) * j + i - 1;
|
|
|
+ var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
|
|
|
+ var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
|
|
|
+ var d = ( tubularSegments + 1 ) * j + i;
|
|
|
+
|
|
|
+ var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] );
|
|
|
+ this.faces.push( face );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] );
|
|
|
+
|
|
|
+ face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] );
|
|
|
+ this.faces.push( face );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } );
|
|
|
+ this.computeFaceNormals();
|
|
|
|
|
|
- THREE.Line.call( this, geometry, material, THREE.LinePieces );
|
|
|
+};
|
|
|
|
|
|
- this.root = object;
|
|
|
+THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
- this.matrix = object.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+// File:src/extras/geometries/TorusKnotGeometry.js
|
|
|
|
|
|
- this.update();
|
|
|
+/**
|
|
|
+ * @author oosmoxiecode
|
|
|
+ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
|
|
|
+ */
|
|
|
|
|
|
-};
|
|
|
+THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) {
|
|
|
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
-THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ this.type = 'TorusKnotGeometry';
|
|
|
|
|
|
-THREE.SkeletonHelper.prototype.getBoneList = function( object ) {
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ tube: tube,
|
|
|
+ radialSegments: radialSegments,
|
|
|
+ tubularSegments: tubularSegments,
|
|
|
+ p: p,
|
|
|
+ q: q,
|
|
|
+ heightScale: heightScale
|
|
|
+ };
|
|
|
|
|
|
- var boneList = [];
|
|
|
+ radius = radius || 100;
|
|
|
+ tube = tube || 40;
|
|
|
+ radialSegments = radialSegments || 64;
|
|
|
+ tubularSegments = tubularSegments || 8;
|
|
|
+ p = p || 2;
|
|
|
+ q = q || 3;
|
|
|
+ heightScale = heightScale || 1;
|
|
|
+
|
|
|
+ var grid = new Array( radialSegments );
|
|
|
+ var tang = new THREE.Vector3();
|
|
|
+ var n = new THREE.Vector3();
|
|
|
+ var bitan = new THREE.Vector3();
|
|
|
|
|
|
- if ( object instanceof THREE.Bone ) {
|
|
|
+ for ( var i = 0; i < radialSegments; ++ i ) {
|
|
|
|
|
|
- boneList.push( object );
|
|
|
+ grid[ i ] = new Array( tubularSegments );
|
|
|
+ var u = i / radialSegments * 2 * p * Math.PI;
|
|
|
+ var p1 = getPos( u, q, p, radius, heightScale );
|
|
|
+ var p2 = getPos( u + 0.01, q, p, radius, heightScale );
|
|
|
+ tang.subVectors( p2, p1 );
|
|
|
+ n.addVectors( p2, p1 );
|
|
|
|
|
|
- }
|
|
|
+ bitan.crossVectors( tang, n );
|
|
|
+ n.crossVectors( bitan, tang );
|
|
|
+ bitan.normalize();
|
|
|
+ n.normalize();
|
|
|
|
|
|
- for ( var i = 0; i < object.children.length; i ++ ) {
|
|
|
+ for ( var j = 0; j < tubularSegments; ++ j ) {
|
|
|
|
|
|
- boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) );
|
|
|
+ var v = j / tubularSegments * 2 * Math.PI;
|
|
|
+ var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
|
|
|
+ var cy = tube * Math.sin( v );
|
|
|
|
|
|
- }
|
|
|
+ var pos = new THREE.Vector3();
|
|
|
+ pos.x = p1.x + cx * n.x + cy * bitan.x;
|
|
|
+ pos.y = p1.y + cx * n.y + cy * bitan.y;
|
|
|
+ pos.z = p1.z + cx * n.z + cy * bitan.z;
|
|
|
|
|
|
- return boneList;
|
|
|
+ grid[ i ][ j ] = this.vertices.push( pos ) - 1;
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.SkeletonHelper.prototype.update = function () {
|
|
|
+ }
|
|
|
|
|
|
- var geometry = this.geometry;
|
|
|
+ for ( var i = 0; i < radialSegments; ++ i ) {
|
|
|
|
|
|
- var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld );
|
|
|
+ for ( var j = 0; j < tubularSegments; ++ j ) {
|
|
|
|
|
|
- var boneMatrix = new THREE.Matrix4();
|
|
|
+ var ip = ( i + 1 ) % radialSegments;
|
|
|
+ var jp = ( j + 1 ) % tubularSegments;
|
|
|
|
|
|
- var j = 0;
|
|
|
+ var a = grid[ i ][ j ];
|
|
|
+ var b = grid[ ip ][ j ];
|
|
|
+ var c = grid[ ip ][ jp ];
|
|
|
+ var d = grid[ i ][ jp ];
|
|
|
|
|
|
- for ( var i = 0; i < this.bones.length; i ++ ) {
|
|
|
+ var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments );
|
|
|
+ var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments );
|
|
|
+ var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments );
|
|
|
+ var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments );
|
|
|
|
|
|
- var bone = this.bones[ i ];
|
|
|
+ this.faces.push( new THREE.Face3( a, b, d ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
|
|
|
|
|
|
- if ( bone.parent instanceof THREE.Bone ) {
|
|
|
+ this.faces.push( new THREE.Face3( b, c, d ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
|
|
|
- boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
|
|
|
- geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
|
|
|
- geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix );
|
|
|
+ this.computeFaceNormals();
|
|
|
+ this.computeVertexNormals();
|
|
|
|
|
|
- j += 2;
|
|
|
+ function getPos( u, in_q, in_p, radius, heightScale ) {
|
|
|
|
|
|
- }
|
|
|
+ var cu = Math.cos( u );
|
|
|
+ var su = Math.sin( u );
|
|
|
+ var quOverP = in_q / in_p * u;
|
|
|
+ var cs = Math.cos( quOverP );
|
|
|
|
|
|
- }
|
|
|
+ var tx = radius * ( 2 + cs ) * 0.5 * cu;
|
|
|
+ var ty = radius * ( 2 + cs ) * su * 0.5;
|
|
|
+ var tz = heightScale * radius * Math.sin( quOverP ) * 0.5;
|
|
|
|
|
|
- geometry.verticesNeedUpdate = true;
|
|
|
+ return new THREE.Vector3( tx, ty, tz );
|
|
|
|
|
|
- geometry.computeBoundingSphere();
|
|
|
+ }
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/helpers/SpotLightHelper.js
|
|
|
+THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
+
|
|
|
+// File:src/extras/geometries/TubeGeometry.js
|
|
|
|
|
|
/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
-*/
|
|
|
+ * @author WestLangley / https://github.com/WestLangley
|
|
|
+ * @author zz85 / https://github.com/zz85
|
|
|
+ * @author miningold / https://github.com/miningold
|
|
|
+ *
|
|
|
+ * Modified from the TorusKnotGeometry by @oosmoxiecode
|
|
|
+ *
|
|
|
+ * Creates a tube which extrudes along a 3d spline
|
|
|
+ *
|
|
|
+ * Uses parallel transport frames as described in
|
|
|
+ * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
|
|
|
+ */
|
|
|
|
|
|
-THREE.SpotLightHelper = function ( light ) {
|
|
|
+THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) {
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- this.light = light;
|
|
|
- this.light.updateMatrixWorld();
|
|
|
+ this.type = 'TubeGeometry';
|
|
|
|
|
|
- this.matrix = light.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ this.parameters = {
|
|
|
+ path: path,
|
|
|
+ segments: segments,
|
|
|
+ radius: radius,
|
|
|
+ radialSegments: radialSegments,
|
|
|
+ closed: closed
|
|
|
+ };
|
|
|
|
|
|
- var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true );
|
|
|
+ segments = segments || 64;
|
|
|
+ radius = radius || 1;
|
|
|
+ radialSegments = radialSegments || 8;
|
|
|
+ closed = closed || false;
|
|
|
|
|
|
- geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
|
|
|
- geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
|
|
|
+ var grid = [];
|
|
|
|
|
|
- var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
|
|
|
+ var scope = this,
|
|
|
|
|
|
- this.cone = new THREE.Mesh( geometry, material );
|
|
|
- this.add( this.cone );
|
|
|
+ tangent,
|
|
|
+ normal,
|
|
|
+ binormal,
|
|
|
|
|
|
- this.update();
|
|
|
+ numpoints = segments + 1,
|
|
|
|
|
|
-};
|
|
|
+ x, y, z,
|
|
|
+ tx, ty, tz,
|
|
|
+ u, v,
|
|
|
|
|
|
-THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
+ cx, cy,
|
|
|
+ pos, pos2 = new THREE.Vector3(),
|
|
|
+ i, j,
|
|
|
+ ip, jp,
|
|
|
+ a, b, c, d,
|
|
|
+ uva, uvb, uvc, uvd;
|
|
|
|
|
|
-THREE.SpotLightHelper.prototype.dispose = function () {
|
|
|
- this.cone.geometry.dispose();
|
|
|
- this.cone.material.dispose();
|
|
|
-};
|
|
|
+ var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ),
|
|
|
+ tangents = frames.tangents,
|
|
|
+ normals = frames.normals,
|
|
|
+ binormals = frames.binormals;
|
|
|
|
|
|
-THREE.SpotLightHelper.prototype.update = function () {
|
|
|
+ // proxy internals
|
|
|
+ this.tangents = tangents;
|
|
|
+ this.normals = normals;
|
|
|
+ this.binormals = binormals;
|
|
|
|
|
|
- var vector = new THREE.Vector3();
|
|
|
- var vector2 = new THREE.Vector3();
|
|
|
+ function vert( x, y, z ) {
|
|
|
|
|
|
- return function () {
|
|
|
+ return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
|
|
|
|
|
|
- var coneLength = this.light.distance ? this.light.distance : 10000;
|
|
|
- var coneWidth = coneLength * Math.tan( this.light.angle );
|
|
|
+ }
|
|
|
|
|
|
- this.cone.scale.set( coneWidth, coneWidth, coneLength );
|
|
|
+ // consruct the grid
|
|
|
|
|
|
- vector.setFromMatrixPosition( this.light.matrixWorld );
|
|
|
- vector2.setFromMatrixPosition( this.light.target.matrixWorld );
|
|
|
+ for ( i = 0; i < numpoints; i ++ ) {
|
|
|
|
|
|
- this.cone.lookAt( vector2.sub( vector ) );
|
|
|
+ grid[ i ] = [];
|
|
|
|
|
|
- this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
+ u = i / ( numpoints - 1 );
|
|
|
|
|
|
- };
|
|
|
+ pos = path.getPointAt( u );
|
|
|
|
|
|
-}();
|
|
|
+ tangent = tangents[ i ];
|
|
|
+ normal = normals[ i ];
|
|
|
+ binormal = binormals[ i ];
|
|
|
|
|
|
-// File:src/extras/helpers/VertexNormalsHelper.js
|
|
|
+ for ( j = 0; j < radialSegments; j ++ ) {
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
-*/
|
|
|
+ v = j / radialSegments * 2 * Math.PI;
|
|
|
|
|
|
-THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {
|
|
|
+ cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
|
|
|
+ cy = radius * Math.sin( v );
|
|
|
|
|
|
- this.object = object;
|
|
|
+ pos2.copy( pos );
|
|
|
+ pos2.x += cx * normal.x + cy * binormal.x;
|
|
|
+ pos2.y += cx * normal.y + cy * binormal.y;
|
|
|
+ pos2.z += cx * normal.z + cy * binormal.z;
|
|
|
|
|
|
- this.size = ( size !== undefined ) ? size : 1;
|
|
|
+ grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
|
|
|
|
|
|
- var color = ( hex !== undefined ) ? hex : 0xff0000;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- var width = ( linewidth !== undefined ) ? linewidth : 1;
|
|
|
|
|
|
- var geometry = new THREE.Geometry();
|
|
|
+ // construct the mesh
|
|
|
|
|
|
- var vertices = object.geometry.vertices;
|
|
|
+ for ( i = 0; i < segments; i ++ ) {
|
|
|
|
|
|
- var faces = object.geometry.faces;
|
|
|
+ for ( j = 0; j < radialSegments; j ++ ) {
|
|
|
|
|
|
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+ ip = ( closed ) ? (i + 1) % segments : i + 1;
|
|
|
+ jp = (j + 1) % radialSegments;
|
|
|
|
|
|
- var face = faces[ i ];
|
|
|
+ a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! ***
|
|
|
+ b = grid[ ip ][ j ];
|
|
|
+ c = grid[ ip ][ jp ];
|
|
|
+ d = grid[ i ][ jp ];
|
|
|
|
|
|
- for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
|
|
|
+ uva = new THREE.Vector2( i / segments, j / radialSegments );
|
|
|
+ uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments );
|
|
|
+ uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments );
|
|
|
+ uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments );
|
|
|
|
|
|
- geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
|
|
|
+ this.faces.push( new THREE.Face3( a, b, d ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
|
|
|
|
|
|
- }
|
|
|
+ this.faces.push( new THREE.Face3( b, c, d ) );
|
|
|
+ this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
|
|
|
+ this.computeFaceNormals();
|
|
|
+ this.computeVertexNormals();
|
|
|
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+};
|
|
|
|
|
|
- this.normalMatrix = new THREE.Matrix3();
|
|
|
+THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
- this.update();
|
|
|
|
|
|
-};
|
|
|
+// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
|
|
|
+THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) {
|
|
|
|
|
|
-THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ var tangent = new THREE.Vector3(),
|
|
|
+ normal = new THREE.Vector3(),
|
|
|
+ binormal = new THREE.Vector3(),
|
|
|
|
|
|
-THREE.VertexNormalsHelper.prototype.update = ( function ( object ) {
|
|
|
+ tangents = [],
|
|
|
+ normals = [],
|
|
|
+ binormals = [],
|
|
|
|
|
|
- var v1 = new THREE.Vector3();
|
|
|
+ vec = new THREE.Vector3(),
|
|
|
+ mat = new THREE.Matrix4(),
|
|
|
|
|
|
- return function( object ) {
|
|
|
+ numpoints = segments + 1,
|
|
|
+ theta,
|
|
|
+ epsilon = 0.0001,
|
|
|
+ smallest,
|
|
|
|
|
|
- var keys = [ 'a', 'b', 'c', 'd' ];
|
|
|
+ tx, ty, tz,
|
|
|
+ i, u, v;
|
|
|
|
|
|
- this.object.updateMatrixWorld( true );
|
|
|
|
|
|
- this.normalMatrix.getNormalMatrix( this.object.matrixWorld );
|
|
|
+ // expose internals
|
|
|
+ this.tangents = tangents;
|
|
|
+ this.normals = normals;
|
|
|
+ this.binormals = binormals;
|
|
|
|
|
|
- var vertices = this.geometry.vertices;
|
|
|
+ // compute the tangent vectors for each segment on the path
|
|
|
|
|
|
- var verts = this.object.geometry.vertices;
|
|
|
+ for ( i = 0; i < numpoints; i ++ ) {
|
|
|
|
|
|
- var faces = this.object.geometry.faces;
|
|
|
+ u = i / ( numpoints - 1 );
|
|
|
|
|
|
- var worldMatrix = this.object.matrixWorld;
|
|
|
+ tangents[ i ] = path.getTangentAt( u );
|
|
|
+ tangents[ i ].normalize();
|
|
|
|
|
|
- var idx = 0;
|
|
|
+ }
|
|
|
|
|
|
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+ initialNormal3();
|
|
|
|
|
|
- var face = faces[ i ];
|
|
|
+ /*
|
|
|
+ function initialNormal1(lastBinormal) {
|
|
|
+ // fixed start binormal. Has dangers of 0 vectors
|
|
|
+ normals[ 0 ] = new THREE.Vector3();
|
|
|
+ binormals[ 0 ] = new THREE.Vector3();
|
|
|
+ if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
|
|
|
+ normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize();
|
|
|
+ binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
|
|
|
+ }
|
|
|
|
|
|
- for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
|
|
|
+ function initialNormal2() {
|
|
|
|
|
|
- var vertexId = face[ keys[ j ] ];
|
|
|
- var vertex = verts[ vertexId ];
|
|
|
+ // This uses the Frenet-Serret formula for deriving binormal
|
|
|
+ var t2 = path.getTangentAt( epsilon );
|
|
|
|
|
|
- var normal = face.vertexNormals[ j ];
|
|
|
+ normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
|
|
|
+ binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );
|
|
|
|
|
|
- vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
|
|
|
+ normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
|
|
|
+ binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
|
|
|
|
|
|
- v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size );
|
|
|
+ }
|
|
|
+ */
|
|
|
|
|
|
- v1.add( vertices[ idx ] );
|
|
|
- idx = idx + 1;
|
|
|
+ function initialNormal3() {
|
|
|
+ // select an initial normal vector perpenicular to the first tangent vector,
|
|
|
+ // and in the direction of the smallest tangent xyz component
|
|
|
|
|
|
- vertices[ idx ].copy( v1 );
|
|
|
- idx = idx + 1;
|
|
|
+ normals[ 0 ] = new THREE.Vector3();
|
|
|
+ binormals[ 0 ] = new THREE.Vector3();
|
|
|
+ smallest = Number.MAX_VALUE;
|
|
|
+ tx = Math.abs( tangents[ 0 ].x );
|
|
|
+ ty = Math.abs( tangents[ 0 ].y );
|
|
|
+ tz = Math.abs( tangents[ 0 ].z );
|
|
|
|
|
|
- }
|
|
|
+ if ( tx <= smallest ) {
|
|
|
+ smallest = tx;
|
|
|
+ normal.set( 1, 0, 0 );
|
|
|
+ }
|
|
|
|
|
|
+ if ( ty <= smallest ) {
|
|
|
+ smallest = ty;
|
|
|
+ normal.set( 0, 1, 0 );
|
|
|
}
|
|
|
|
|
|
- this.geometry.verticesNeedUpdate = true;
|
|
|
+ if ( tz <= smallest ) {
|
|
|
+ normal.set( 0, 0, 1 );
|
|
|
+ }
|
|
|
|
|
|
- return this;
|
|
|
+ vec.crossVectors( tangents[ 0 ], normal ).normalize();
|
|
|
|
|
|
+ normals[ 0 ].crossVectors( tangents[ 0 ], vec );
|
|
|
+ binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
|
|
|
}
|
|
|
|
|
|
-}());
|
|
|
-
|
|
|
-// File:src/extras/helpers/VertexTangentsHelper.js
|
|
|
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- * @author WestLangley / http://github.com/WestLangley
|
|
|
-*/
|
|
|
+ // compute the slowly-varying normal and binormal vectors for each segment on the path
|
|
|
|
|
|
-THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) {
|
|
|
+ for ( i = 1; i < numpoints; i ++ ) {
|
|
|
|
|
|
- this.object = object;
|
|
|
+ normals[ i ] = normals[ i-1 ].clone();
|
|
|
|
|
|
- this.size = ( size !== undefined ) ? size : 1;
|
|
|
+ binormals[ i ] = binormals[ i-1 ].clone();
|
|
|
|
|
|
- var color = ( hex !== undefined ) ? hex : 0x0000ff;
|
|
|
+ vec.crossVectors( tangents[ i-1 ], tangents[ i ] );
|
|
|
|
|
|
- var width = ( linewidth !== undefined ) ? linewidth : 1;
|
|
|
+ if ( vec.length() > epsilon ) {
|
|
|
|
|
|
- var geometry = new THREE.Geometry();
|
|
|
+ vec.normalize();
|
|
|
|
|
|
- var vertices = object.geometry.vertices;
|
|
|
+ theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
|
|
|
|
|
|
- var faces = object.geometry.faces;
|
|
|
+ normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
|
|
|
|
|
|
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- var face = faces[ i ];
|
|
|
+ binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
|
|
|
|
|
|
- for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- geometry.vertices.push( new THREE.Vector3() );
|
|
|
- geometry.vertices.push( new THREE.Vector3() );
|
|
|
|
|
|
- }
|
|
|
+ // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
|
|
|
|
|
|
- }
|
|
|
+ if ( closed ) {
|
|
|
|
|
|
- THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
|
|
|
+ theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) );
|
|
|
+ theta /= ( numpoints - 1 );
|
|
|
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
|
|
|
|
|
|
- this.update();
|
|
|
+ theta = - theta;
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ for ( i = 1; i < numpoints; i ++ ) {
|
|
|
|
|
|
-THREE.VertexTangentsHelper.prototype.update = ( function ( object ) {
|
|
|
+ // twist a little...
|
|
|
+ normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
|
|
|
+ binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
|
|
|
|
|
|
- var v1 = new THREE.Vector3();
|
|
|
+ }
|
|
|
|
|
|
- return function( object ) {
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
- var keys = [ 'a', 'b', 'c', 'd' ];
|
|
|
+// File:src/extras/geometries/PolyhedronGeometry.js
|
|
|
|
|
|
- this.object.updateMatrixWorld( true );
|
|
|
+/**
|
|
|
+ * @author clockworkgeek / https://github.com/clockworkgeek
|
|
|
+ * @author timothypratley / https://github.com/timothypratley
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
+*/
|
|
|
|
|
|
- var vertices = this.geometry.vertices;
|
|
|
+THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) {
|
|
|
|
|
|
- var verts = this.object.geometry.vertices;
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- var faces = this.object.geometry.faces;
|
|
|
+ this.type = 'PolyhedronGeometry';
|
|
|
|
|
|
- var worldMatrix = this.object.matrixWorld;
|
|
|
+ this.parameters = {
|
|
|
+ vertices: vertices,
|
|
|
+ indices: indices,
|
|
|
+ radius: radius,
|
|
|
+ detail: detail
|
|
|
+ };
|
|
|
|
|
|
- var idx = 0;
|
|
|
+ radius = radius || 1;
|
|
|
+ detail = detail || 0;
|
|
|
|
|
|
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+ var that = this;
|
|
|
|
|
|
- var face = faces[ i ];
|
|
|
+ for ( var i = 0, l = vertices.length; i < l; i += 3 ) {
|
|
|
|
|
|
- for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
|
|
|
+ prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );
|
|
|
|
|
|
- var vertexId = face[ keys[ j ] ];
|
|
|
- var vertex = verts[ vertexId ];
|
|
|
+ }
|
|
|
|
|
|
- var tangent = face.vertexTangents[ j ];
|
|
|
+ var midpoints = [], p = this.vertices;
|
|
|
|
|
|
- vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
|
|
|
+ var faces = [];
|
|
|
|
|
|
- v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size );
|
|
|
+ for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) {
|
|
|
|
|
|
- v1.add( vertices[ idx ] );
|
|
|
- idx = idx + 1;
|
|
|
+ var v1 = p[ indices[ i ] ];
|
|
|
+ var v2 = p[ indices[ i + 1 ] ];
|
|
|
+ var v3 = p[ indices[ i + 2 ] ];
|
|
|
|
|
|
- vertices[ idx ].copy( v1 );
|
|
|
- idx = idx + 1;
|
|
|
+ faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ var centroid = new THREE.Vector3();
|
|
|
|
|
|
- this.geometry.verticesNeedUpdate = true;
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- return this;
|
|
|
+ subdivide( faces[ i ], detail );
|
|
|
|
|
|
}
|
|
|
|
|
|
-}());
|
|
|
-
|
|
|
-// File:src/extras/helpers/WireframeHelper.js
|
|
|
-
|
|
|
-/**
|
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
- */
|
|
|
|
|
|
-THREE.WireframeHelper = function ( object, hex ) {
|
|
|
+ // Handle case when face straddles the seam
|
|
|
|
|
|
- var color = ( hex !== undefined ) ? hex : 0xffffff;
|
|
|
+ for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
|
|
|
|
|
|
- var edge = [ 0, 0 ], hash = {};
|
|
|
- var sortFunction = function ( a, b ) { return a - b };
|
|
|
+ var uvs = this.faceVertexUvs[ 0 ][ i ];
|
|
|
|
|
|
- var keys = [ 'a', 'b', 'c' ];
|
|
|
- var geometry = new THREE.BufferGeometry();
|
|
|
+ var x0 = uvs[ 0 ].x;
|
|
|
+ var x1 = uvs[ 1 ].x;
|
|
|
+ var x2 = uvs[ 2 ].x;
|
|
|
|
|
|
- if ( object.geometry instanceof THREE.Geometry ) {
|
|
|
+ var max = Math.max( x0, Math.max( x1, x2 ) );
|
|
|
+ var min = Math.min( x0, Math.min( x1, x2 ) );
|
|
|
|
|
|
- var vertices = object.geometry.vertices;
|
|
|
- var faces = object.geometry.faces;
|
|
|
- var numEdges = 0;
|
|
|
+ if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary
|
|
|
|
|
|
- // allocate maximal size
|
|
|
- var edges = new Uint32Array( 6 * faces.length );
|
|
|
+ if ( x0 < 0.2 ) uvs[ 0 ].x += 1;
|
|
|
+ if ( x1 < 0.2 ) uvs[ 1 ].x += 1;
|
|
|
+ if ( x2 < 0.2 ) uvs[ 2 ].x += 1;
|
|
|
|
|
|
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- var face = faces[ i ];
|
|
|
+ }
|
|
|
|
|
|
- for ( var j = 0; j < 3; j ++ ) {
|
|
|
|
|
|
- edge[ 0 ] = face[ keys[ j ] ];
|
|
|
- edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
|
|
|
- edge.sort( sortFunction );
|
|
|
+ // Apply radius
|
|
|
|
|
|
- var key = edge.toString();
|
|
|
+ for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( hash[ key ] === undefined ) {
|
|
|
+ this.vertices[ i ].multiplyScalar( radius );
|
|
|
|
|
|
- edges[ 2 * numEdges ] = edge[ 0 ];
|
|
|
- edges[ 2 * numEdges + 1 ] = edge[ 1 ];
|
|
|
- hash[ key ] = true;
|
|
|
- numEdges ++;
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
- }
|
|
|
+ // Merge vertices
|
|
|
|
|
|
- }
|
|
|
+ this.mergeVertices();
|
|
|
|
|
|
- var coords = new Float32Array( numEdges * 2 * 3 );
|
|
|
+ this.computeFaceNormals();
|
|
|
|
|
|
- for ( var i = 0, l = numEdges; i < l; i ++ ) {
|
|
|
+ this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
|
|
|
|
|
|
- for ( var j = 0; j < 2; j ++ ) {
|
|
|
|
|
|
- var vertex = vertices[ edges [ 2 * i + j] ];
|
|
|
+ // Project vector onto sphere's surface
|
|
|
|
|
|
- var index = 6 * i + 3 * j;
|
|
|
- coords[ index + 0 ] = vertex.x;
|
|
|
- coords[ index + 1 ] = vertex.y;
|
|
|
- coords[ index + 2 ] = vertex.z;
|
|
|
+ function prepare( vector ) {
|
|
|
|
|
|
- }
|
|
|
+ var vertex = vector.normalize().clone();
|
|
|
+ vertex.index = that.vertices.push( vertex ) - 1;
|
|
|
|
|
|
- }
|
|
|
+ // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
|
|
|
|
|
|
- geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
|
|
|
+ var u = azimuth( vector ) / 2 / Math.PI + 0.5;
|
|
|
+ var v = inclination( vector ) / Math.PI + 0.5;
|
|
|
+ vertex.uv = new THREE.Vector2( u, 1 - v );
|
|
|
|
|
|
- } else if ( object.geometry instanceof THREE.BufferGeometry ) {
|
|
|
+ return vertex;
|
|
|
|
|
|
- if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry
|
|
|
+ }
|
|
|
|
|
|
- var vertices = object.geometry.attributes.position.array;
|
|
|
- var indices = object.geometry.attributes.index.array;
|
|
|
- var drawcalls = object.geometry.drawcalls;
|
|
|
- var numEdges = 0;
|
|
|
|
|
|
- if ( drawcalls.length === 0 ) {
|
|
|
+ // Approximate a curved face with recursively sub-divided triangles.
|
|
|
|
|
|
- drawcalls = [ { count : indices.length, index : 0, start : 0 } ];
|
|
|
+ function make( v1, v2, v3 ) {
|
|
|
|
|
|
- }
|
|
|
+ var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
|
|
|
+ that.faces.push( face );
|
|
|
|
|
|
- // allocate maximal size
|
|
|
- var edges = new Uint32Array( 2 * indices.length );
|
|
|
+ centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );
|
|
|
|
|
|
- for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) {
|
|
|
+ var azi = azimuth( centroid );
|
|
|
|
|
|
- var start = drawcalls[ o ].start;
|
|
|
- var count = drawcalls[ o ].count;
|
|
|
- var index = drawcalls[ o ].index;
|
|
|
+ that.faceVertexUvs[ 0 ].push( [
|
|
|
+ correctUV( v1.uv, v1, azi ),
|
|
|
+ correctUV( v2.uv, v2, azi ),
|
|
|
+ correctUV( v3.uv, v3, azi )
|
|
|
+ ] );
|
|
|
|
|
|
- for ( var i = start, il = start + count; i < il; i += 3 ) {
|
|
|
+ }
|
|
|
|
|
|
- for ( var j = 0; j < 3; j ++ ) {
|
|
|
|
|
|
- edge[ 0 ] = index + indices[ i + j ];
|
|
|
- edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ];
|
|
|
- edge.sort( sortFunction );
|
|
|
+ // Analytically subdivide a face to the required detail level.
|
|
|
|
|
|
- var key = edge.toString();
|
|
|
+ function subdivide( face, detail ) {
|
|
|
|
|
|
- if ( hash[ key ] === undefined ) {
|
|
|
+ var cols = Math.pow(2, detail);
|
|
|
+ var cells = Math.pow(4, detail);
|
|
|
+ var a = prepare( that.vertices[ face.a ] );
|
|
|
+ var b = prepare( that.vertices[ face.b ] );
|
|
|
+ var c = prepare( that.vertices[ face.c ] );
|
|
|
+ var v = [];
|
|
|
|
|
|
- edges[ 2 * numEdges ] = edge[ 0 ];
|
|
|
- edges[ 2 * numEdges + 1 ] = edge[ 1 ];
|
|
|
- hash[ key ] = true;
|
|
|
- numEdges ++;
|
|
|
+ // Construct all of the vertices for this subdivision.
|
|
|
|
|
|
- }
|
|
|
+ for ( var i = 0 ; i <= cols; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ v[ i ] = [];
|
|
|
|
|
|
- }
|
|
|
+ var aj = prepare( a.clone().lerp( c, i / cols ) );
|
|
|
+ var bj = prepare( b.clone().lerp( c, i / cols ) );
|
|
|
+ var rows = cols - i;
|
|
|
|
|
|
- }
|
|
|
+ for ( var j = 0; j <= rows; j ++) {
|
|
|
|
|
|
- var coords = new Float32Array( numEdges * 2 * 3 );
|
|
|
+ if ( j == 0 && i == cols ) {
|
|
|
|
|
|
- for ( var i = 0, l = numEdges; i < l; i ++ ) {
|
|
|
+ v[ i ][ j ] = aj;
|
|
|
|
|
|
- for ( var j = 0; j < 2; j ++ ) {
|
|
|
+ } else {
|
|
|
|
|
|
- var index = 6 * i + 3 * j;
|
|
|
- var index2 = 3 * edges[ 2 * i + j];
|
|
|
- coords[ index + 0 ] = vertices[ index2 ];
|
|
|
- coords[ index + 1 ] = vertices[ index2 + 1 ];
|
|
|
- coords[ index + 2 ] = vertices[ index2 + 2 ];
|
|
|
+ v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
|
|
|
+ }
|
|
|
|
|
|
- } else { // non-indexed BufferGeometry
|
|
|
+ // Construct all of the faces.
|
|
|
|
|
|
- var vertices = object.geometry.attributes.position.array;
|
|
|
- var numEdges = vertices.length / 3;
|
|
|
- var numTris = numEdges / 3;
|
|
|
+ for ( var i = 0; i < cols ; i ++ ) {
|
|
|
|
|
|
- var coords = new Float32Array( numEdges * 2 * 3 );
|
|
|
+ for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) {
|
|
|
|
|
|
- for ( var i = 0, l = numTris; i < l; i ++ ) {
|
|
|
+ var k = Math.floor( j / 2 );
|
|
|
|
|
|
- for ( var j = 0; j < 3; j ++ ) {
|
|
|
+ if ( j % 2 == 0 ) {
|
|
|
|
|
|
- var index = 18 * i + 6 * j;
|
|
|
+ make(
|
|
|
+ v[ i ][ k + 1],
|
|
|
+ v[ i + 1 ][ k ],
|
|
|
+ v[ i ][ k ]
|
|
|
+ );
|
|
|
|
|
|
- var index1 = 9 * i + 3 * j;
|
|
|
- coords[ index + 0 ] = vertices[ index1 ];
|
|
|
- coords[ index + 1 ] = vertices[ index1 + 1 ];
|
|
|
- coords[ index + 2 ] = vertices[ index1 + 2 ];
|
|
|
+ } else {
|
|
|
|
|
|
- var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 );
|
|
|
- coords[ index + 3 ] = vertices[ index2 ];
|
|
|
- coords[ index + 4 ] = vertices[ index2 + 1 ];
|
|
|
- coords[ index + 5 ] = vertices[ index2 + 2 ];
|
|
|
+ make(
|
|
|
+ v[ i ][ k + 1 ],
|
|
|
+ v[ i + 1][ k + 1],
|
|
|
+ v[ i + 1 ][ k ]
|
|
|
+ );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
|
|
|
-
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
|
|
|
|
|
|
- this.matrix = object.matrixWorld;
|
|
|
- this.matrixAutoUpdate = false;
|
|
|
+ // Angle around the Y axis, counter-clockwise when looking from above.
|
|
|
|
|
|
-};
|
|
|
+ function azimuth( vector ) {
|
|
|
|
|
|
-THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+ return Math.atan2( vector.z, - vector.x );
|
|
|
|
|
|
-// File:src/extras/objects/ImmediateRenderObject.js
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- */
|
|
|
|
|
|
-THREE.ImmediateRenderObject = function () {
|
|
|
+ // Angle above the XZ plane.
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ function inclination( vector ) {
|
|
|
|
|
|
- this.render = function ( renderCallback ) {};
|
|
|
+ return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
|
|
|
|
|
|
-};
|
|
|
+ }
|
|
|
|
|
|
-THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
-// File:src/extras/objects/LensFlare.js
|
|
|
+ // Texture fixing helper. Spheres have some odd behaviours.
|
|
|
|
|
|
-/**
|
|
|
- * @author mikael emtinger / http://gomo.se/
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- */
|
|
|
+ function correctUV( uv, vector, azimuth ) {
|
|
|
|
|
|
-THREE.LensFlare = function ( texture, size, distance, blending, color ) {
|
|
|
+ if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y );
|
|
|
+ if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y );
|
|
|
+ return uv.clone();
|
|
|
|
|
|
- THREE.Object3D.call( this );
|
|
|
+ }
|
|
|
|
|
|
- this.lensFlares = [];
|
|
|
|
|
|
- this.positionScreen = new THREE.Vector3();
|
|
|
- this.customUpdateCallback = undefined;
|
|
|
+};
|
|
|
|
|
|
- if( texture !== undefined ) {
|
|
|
+THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
- this.add( texture, size, distance, blending, color );
|
|
|
+// File:src/extras/geometries/DodecahedronGeometry.js
|
|
|
|
|
|
- }
|
|
|
+/**
|
|
|
+ * @author Abe Pazos / https://hamoid.com
|
|
|
+ */
|
|
|
|
|
|
-};
|
|
|
+THREE.DodecahedronGeometry = function ( radius, detail ) {
|
|
|
|
|
|
-THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ detail: detail
|
|
|
+ };
|
|
|
|
|
|
+ var t = ( 1 + Math.sqrt( 5 ) ) / 2;
|
|
|
+ var r = 1 / t;
|
|
|
|
|
|
-/*
|
|
|
- * Add: adds another flare
|
|
|
- */
|
|
|
+ var vertices = [
|
|
|
|
|
|
-THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
|
|
|
+ // (±1, ±1, ±1)
|
|
|
+ -1, -1, -1, -1, -1, 1,
|
|
|
+ -1, 1, -1, -1, 1, 1,
|
|
|
+ 1, -1, -1, 1, -1, 1,
|
|
|
+ 1, 1, -1, 1, 1, 1,
|
|
|
|
|
|
- if ( size === undefined ) size = - 1;
|
|
|
- if ( distance === undefined ) distance = 0;
|
|
|
- if ( opacity === undefined ) opacity = 1;
|
|
|
- if ( color === undefined ) color = new THREE.Color( 0xffffff );
|
|
|
- if ( blending === undefined ) blending = THREE.NormalBlending;
|
|
|
+ // (0, ±1/φ, ±φ)
|
|
|
+ 0, -r, -t, 0, -r, t,
|
|
|
+ 0, r, -t, 0, r, t,
|
|
|
|
|
|
- distance = Math.min( distance, Math.max( 0, distance ) );
|
|
|
+ // (±1/φ, ±φ, 0)
|
|
|
+ -r, -t, 0, -r, t, 0,
|
|
|
+ r, -t, 0, r, t, 0,
|
|
|
|
|
|
- this.lensFlares.push( {
|
|
|
- texture: texture, // THREE.Texture
|
|
|
- size: size, // size in pixels (-1 = use texture.width)
|
|
|
- distance: distance, // distance (0-1) from light source (0=at light source)
|
|
|
- x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back
|
|
|
- scale: 1, // scale
|
|
|
- rotation: 1, // rotation
|
|
|
- opacity: opacity, // opacity
|
|
|
- color: color, // color
|
|
|
- blending: blending // blending
|
|
|
- } );
|
|
|
+ // (±φ, 0, ±1/φ)
|
|
|
+ -t, 0, -r, t, 0, -r,
|
|
|
+ -t, 0, r, t, 0, r
|
|
|
+ ];
|
|
|
+
|
|
|
+ var indices = [
|
|
|
+ 3, 11, 7, 3, 7, 15, 3, 15, 13,
|
|
|
+ 7, 19, 17, 7, 17, 6, 7, 6, 15,
|
|
|
+ 17, 4, 8, 17, 8, 10, 17, 10, 6,
|
|
|
+ 8, 0, 16, 8, 16, 2, 8, 2, 10,
|
|
|
+ 0, 12, 1, 0, 1, 18, 0, 18, 16,
|
|
|
+ 6, 10, 2, 6, 2, 13, 6, 13, 15,
|
|
|
+ 2, 16, 18, 2, 18, 3, 2, 3, 13,
|
|
|
+ 18, 1, 9, 18, 9, 11, 18, 11, 3,
|
|
|
+ 4, 14, 12, 4, 12, 0, 4, 0, 8,
|
|
|
+ 11, 9, 5, 11, 5, 19, 11, 19, 7,
|
|
|
+ 19, 5, 14, 19, 14, 4, 19, 4, 17,
|
|
|
+ 1, 12, 14, 1, 14, 5, 1, 5, 9
|
|
|
+ ];
|
|
|
+
|
|
|
+ THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
|
|
|
|
|
|
};
|
|
|
|
|
|
-/*
|
|
|
- * Update lens flares update positions on all flares based on the screen position
|
|
|
- * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
|
|
|
- */
|
|
|
+THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
-THREE.LensFlare.prototype.updateLensFlares = function () {
|
|
|
+// File:src/extras/geometries/IcosahedronGeometry.js
|
|
|
|
|
|
- var f, fl = this.lensFlares.length;
|
|
|
- var flare;
|
|
|
- var vecX = - this.positionScreen.x * 2;
|
|
|
- var vecY = - this.positionScreen.y * 2;
|
|
|
+/**
|
|
|
+ * @author timothypratley / https://github.com/timothypratley
|
|
|
+ */
|
|
|
|
|
|
- for( f = 0; f < fl; f ++ ) {
|
|
|
+THREE.IcosahedronGeometry = function ( radius, detail ) {
|
|
|
|
|
|
- flare = this.lensFlares[ f ];
|
|
|
+ var t = ( 1 + Math.sqrt( 5 ) ) / 2;
|
|
|
|
|
|
- flare.x = this.positionScreen.x + vecX * flare.distance;
|
|
|
- flare.y = this.positionScreen.y + vecY * flare.distance;
|
|
|
+ var vertices = [
|
|
|
+ - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,
|
|
|
+ 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,
|
|
|
+ t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1
|
|
|
+ ];
|
|
|
|
|
|
- flare.wantedRotation = flare.x * Math.PI * 0.25;
|
|
|
- flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
|
|
|
+ var indices = [
|
|
|
+ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,
|
|
|
+ 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
|
|
|
+ 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,
|
|
|
+ 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1
|
|
|
+ ];
|
|
|
|
|
|
- }
|
|
|
+ THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
|
|
|
+
|
|
|
+ this.type = 'IcosahedronGeometry';
|
|
|
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ detail: detail
|
|
|
+ };
|
|
|
};
|
|
|
|
|
|
+THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
-// File:src/extras/objects/MorphBlendMesh.js
|
|
|
+// File:src/extras/geometries/OctahedronGeometry.js
|
|
|
|
|
|
/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author timothypratley / https://github.com/timothypratley
|
|
|
*/
|
|
|
|
|
|
-THREE.MorphBlendMesh = function( geometry, material ) {
|
|
|
-
|
|
|
- THREE.Mesh.call( this, geometry, material );
|
|
|
-
|
|
|
- this.animationsMap = {};
|
|
|
- this.animationsList = [];
|
|
|
-
|
|
|
- // prepare default animation
|
|
|
- // (all frames played together in 1 second)
|
|
|
+THREE.OctahedronGeometry = function ( radius, detail ) {
|
|
|
|
|
|
- var numFrames = this.geometry.morphTargets.length;
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ detail: detail
|
|
|
+ };
|
|
|
|
|
|
- var name = "__default";
|
|
|
+ var vertices = [
|
|
|
+ 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1
|
|
|
+ ];
|
|
|
|
|
|
- var startFrame = 0;
|
|
|
- var endFrame = numFrames - 1;
|
|
|
+ var indices = [
|
|
|
+ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2
|
|
|
+ ];
|
|
|
|
|
|
- var fps = numFrames / 1;
|
|
|
+ THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
|
|
|
|
|
|
- this.createAnimation( name, startFrame, endFrame, fps );
|
|
|
- this.setAnimationWeight( name, 1 );
|
|
|
+ this.type = 'OctahedronGeometry';
|
|
|
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ detail: detail
|
|
|
+ };
|
|
|
};
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
|
|
|
+THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
|
|
|
+// File:src/extras/geometries/TetrahedronGeometry.js
|
|
|
|
|
|
- var animation = {
|
|
|
+/**
|
|
|
+ * @author timothypratley / https://github.com/timothypratley
|
|
|
+ */
|
|
|
|
|
|
- startFrame: start,
|
|
|
- endFrame: end,
|
|
|
+THREE.TetrahedronGeometry = function ( radius, detail ) {
|
|
|
|
|
|
- length: end - start + 1,
|
|
|
+ var vertices = [
|
|
|
+ 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1
|
|
|
+ ];
|
|
|
|
|
|
- fps: fps,
|
|
|
- duration: ( end - start ) / fps,
|
|
|
+ var indices = [
|
|
|
+ 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
|
|
|
+ ];
|
|
|
|
|
|
- lastFrame: 0,
|
|
|
- currentFrame: 0,
|
|
|
+ THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );
|
|
|
|
|
|
- active: false,
|
|
|
+ this.type = 'TetrahedronGeometry';
|
|
|
|
|
|
- time: 0,
|
|
|
- direction: 1,
|
|
|
- weight: 1,
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ detail: detail
|
|
|
+ };
|
|
|
|
|
|
- directionBackwards: false,
|
|
|
- mirroredLoop: false
|
|
|
+};
|
|
|
|
|
|
- };
|
|
|
+THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
- this.animationsMap[ name ] = animation;
|
|
|
- this.animationsList.push( animation );
|
|
|
+// File:src/extras/geometries/ParametricGeometry.js
|
|
|
|
|
|
-};
|
|
|
+/**
|
|
|
+ * @author zz85 / https://github.com/zz85
|
|
|
+ * Parametric Surfaces Geometry
|
|
|
+ * based on the brilliant article by @prideout http://prideout.net/blog/?p=44
|
|
|
+ *
|
|
|
+ * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements );
|
|
|
+ *
|
|
|
+ */
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
|
|
|
+THREE.ParametricGeometry = function ( func, slices, stacks ) {
|
|
|
|
|
|
- var pattern = /([a-z]+)_?(\d+)/;
|
|
|
+ THREE.Geometry.call( this );
|
|
|
|
|
|
- var firstAnimation, frameRanges = {};
|
|
|
+ this.type = 'ParametricGeometry';
|
|
|
|
|
|
- var geometry = this.geometry;
|
|
|
+ this.parameters = {
|
|
|
+ func: func,
|
|
|
+ slices: slices,
|
|
|
+ stacks: stacks
|
|
|
+ };
|
|
|
|
|
|
- for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
|
|
|
+ var verts = this.vertices;
|
|
|
+ var faces = this.faces;
|
|
|
+ var uvs = this.faceVertexUvs[ 0 ];
|
|
|
|
|
|
- var morph = geometry.morphTargets[ i ];
|
|
|
- var chunks = morph.name.match( pattern );
|
|
|
+ var i, il, j, p;
|
|
|
+ var u, v;
|
|
|
|
|
|
- if ( chunks && chunks.length > 1 ) {
|
|
|
+ var stackCount = stacks + 1;
|
|
|
+ var sliceCount = slices + 1;
|
|
|
|
|
|
- var name = chunks[ 1 ];
|
|
|
- var num = chunks[ 2 ];
|
|
|
+ for ( i = 0; i <= stacks; i ++ ) {
|
|
|
|
|
|
- if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };
|
|
|
+ v = i / stacks;
|
|
|
|
|
|
- var range = frameRanges[ name ];
|
|
|
+ for ( j = 0; j <= slices; j ++ ) {
|
|
|
|
|
|
- if ( i < range.start ) range.start = i;
|
|
|
- if ( i > range.end ) range.end = i;
|
|
|
+ u = j / slices;
|
|
|
|
|
|
- if ( ! firstAnimation ) firstAnimation = name;
|
|
|
+ p = func( u, v );
|
|
|
+ verts.push( p );
|
|
|
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- for ( var name in frameRanges ) {
|
|
|
-
|
|
|
- var range = frameRanges[ name ];
|
|
|
- this.createAnimation( name, range.start, range.end, fps );
|
|
|
+ var a, b, c, d;
|
|
|
+ var uva, uvb, uvc, uvd;
|
|
|
|
|
|
- }
|
|
|
+ for ( i = 0; i < stacks; i ++ ) {
|
|
|
|
|
|
- this.firstAnimation = firstAnimation;
|
|
|
+ for ( j = 0; j < slices; j ++ ) {
|
|
|
|
|
|
-};
|
|
|
+ a = i * sliceCount + j;
|
|
|
+ b = i * sliceCount + j + 1;
|
|
|
+ c = (i + 1) * sliceCount + j + 1;
|
|
|
+ d = (i + 1) * sliceCount + j;
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
|
|
|
+ uva = new THREE.Vector2( j / slices, i / stacks );
|
|
|
+ uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks );
|
|
|
+ uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks );
|
|
|
+ uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks );
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+ faces.push( new THREE.Face3( a, b, d ) );
|
|
|
+ uvs.push( [ uva, uvb, uvd ] );
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+ faces.push( new THREE.Face3( b, c, d ) );
|
|
|
+ uvs.push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
|
|
|
- animation.direction = 1;
|
|
|
- animation.directionBackwards = false;
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
-};
|
|
|
+ // console.log(this);
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
|
|
|
+ // magic bullet
|
|
|
+ // var diff = this.mergeVertices();
|
|
|
+ // console.log('removed ', diff, ' vertices by merging');
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+ this.computeFaceNormals();
|
|
|
+ this.computeVertexNormals();
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+};
|
|
|
|
|
|
- animation.direction = - 1;
|
|
|
- animation.directionBackwards = true;
|
|
|
+THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
|
|
|
|
- }
|
|
|
+// File:src/extras/helpers/AxisHelper.js
|
|
|
|
|
|
-};
|
|
|
+/**
|
|
|
+ * @author sroucheray / http://sroucheray.org/
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
|
|
|
+THREE.AxisHelper = function ( size ) {
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+ size = size || 1;
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+ var vertices = new Float32Array( [
|
|
|
+ 0, 0, 0, size, 0, 0,
|
|
|
+ 0, 0, 0, 0, size, 0,
|
|
|
+ 0, 0, 0, 0, 0, size
|
|
|
+ ] );
|
|
|
|
|
|
- animation.fps = fps;
|
|
|
- animation.duration = ( animation.end - animation.start ) / animation.fps;
|
|
|
+ var colors = new Float32Array( [
|
|
|
+ 1, 0, 0, 1, 0.6, 0,
|
|
|
+ 0, 1, 0, 0.6, 1, 0,
|
|
|
+ 0, 0, 1, 0, 0.6, 1
|
|
|
+ ] );
|
|
|
|
|
|
- }
|
|
|
+ var geometry = new THREE.BufferGeometry();
|
|
|
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
|
|
|
+ geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
|
|
|
|
|
|
-};
|
|
|
+ var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
|
|
|
+ THREE.Line.call( this, geometry, material, THREE.LinePieces );
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+};
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- animation.duration = duration;
|
|
|
- animation.fps = ( animation.end - animation.start ) / animation.duration;
|
|
|
+// File:src/extras/helpers/ArrowHelper.js
|
|
|
|
|
|
- }
|
|
|
+/**
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
+ * @author zz85 / http://github.com/zz85
|
|
|
+ * @author bhouston / http://exocortex.com
|
|
|
+ *
|
|
|
+ * Creates an arrow for visualizing directions
|
|
|
+ *
|
|
|
+ * Parameters:
|
|
|
+ * dir - Vector3
|
|
|
+ * origin - Vector3
|
|
|
+ * length - Number
|
|
|
+ * color - color in hex value
|
|
|
+ * headLength - Number
|
|
|
+ * headWidth - Number
|
|
|
+ */
|
|
|
|
|
|
-};
|
|
|
+THREE.ArrowHelper = ( function () {
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
|
|
|
+ var lineGeometry = new THREE.Geometry();
|
|
|
+ lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+ var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 );
|
|
|
+ coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+ return function ( dir, origin, length, color, headLength, headWidth ) {
|
|
|
|
|
|
- animation.weight = weight;
|
|
|
+ // dir is assumed to be normalized
|
|
|
|
|
|
- }
|
|
|
+ THREE.Object3D.call( this );
|
|
|
|
|
|
-};
|
|
|
+ if ( color === undefined ) color = 0xffff00;
|
|
|
+ if ( length === undefined ) length = 1;
|
|
|
+ if ( headLength === undefined ) headLength = 0.2 * length;
|
|
|
+ if ( headWidth === undefined ) headWidth = 0.2 * headLength;
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
|
|
|
+ this.position.copy( origin );
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+ this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
|
|
|
+ this.line.matrixAutoUpdate = false;
|
|
|
+ this.add( this.line );
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+ this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) );
|
|
|
+ this.cone.matrixAutoUpdate = false;
|
|
|
+ this.add( this.cone );
|
|
|
|
|
|
- animation.time = time;
|
|
|
+ this.setDirection( dir );
|
|
|
+ this.setLength( length, headLength, headWidth );
|
|
|
|
|
|
}
|
|
|
|
|
|
-};
|
|
|
+}() );
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
|
|
|
+THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
- var time = 0;
|
|
|
+THREE.ArrowHelper.prototype.setDirection = ( function () {
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+ var axis = new THREE.Vector3();
|
|
|
+ var radians;
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+ return function ( dir ) {
|
|
|
|
|
|
- time = animation.time;
|
|
|
+ // dir is assumed to be normalized
|
|
|
|
|
|
- }
|
|
|
+ if ( dir.y > 0.99999 ) {
|
|
|
|
|
|
- return time;
|
|
|
+ this.quaternion.set( 0, 0, 0, 1 );
|
|
|
|
|
|
-};
|
|
|
+ } else if ( dir.y < - 0.99999 ) {
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
|
|
|
+ this.quaternion.set( 1, 0, 0, 0 );
|
|
|
|
|
|
- var duration = - 1;
|
|
|
+ } else {
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+ axis.set( dir.z, 0, - dir.x ).normalize();
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+ radians = Math.acos( dir.y );
|
|
|
|
|
|
- duration = animation.duration;
|
|
|
+ this.quaternion.setFromAxisAngle( axis, radians );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- return duration;
|
|
|
+ };
|
|
|
|
|
|
-};
|
|
|
+}() );
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
|
|
|
+THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+ if ( headLength === undefined ) headLength = 0.2 * length;
|
|
|
+ if ( headWidth === undefined ) headWidth = 0.2 * headLength;
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+ this.line.scale.set( 1, length, 1 );
|
|
|
+ this.line.updateMatrix();
|
|
|
|
|
|
- animation.time = 0;
|
|
|
- animation.active = true;
|
|
|
+ this.cone.scale.set( headWidth, headLength, headWidth );
|
|
|
+ this.cone.position.y = length;
|
|
|
+ this.cone.updateMatrix();
|
|
|
|
|
|
- } else {
|
|
|
+};
|
|
|
|
|
|
- console.warn( "animation[" + name + "] undefined" );
|
|
|
+THREE.ArrowHelper.prototype.setColor = function ( color ) {
|
|
|
|
|
|
- }
|
|
|
+ this.line.material.color.set( color );
|
|
|
+ this.cone.material.color.set( color );
|
|
|
|
|
|
};
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
|
|
|
+// File:src/extras/helpers/BoxHelper.js
|
|
|
|
|
|
- var animation = this.animationsMap[ name ];
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- if ( animation ) {
|
|
|
+THREE.BoxHelper = function ( object ) {
|
|
|
|
|
|
- animation.active = false;
|
|
|
+ var geometry = new THREE.BufferGeometry();
|
|
|
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) );
|
|
|
|
|
|
- }
|
|
|
+ THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces );
|
|
|
|
|
|
-};
|
|
|
+ if ( object !== undefined ) {
|
|
|
|
|
|
-THREE.MorphBlendMesh.prototype.update = function ( delta ) {
|
|
|
+ this.update( object );
|
|
|
|
|
|
- for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- var animation = this.animationsList[ i ];
|
|
|
+};
|
|
|
|
|
|
- if ( ! animation.active ) continue;
|
|
|
+THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- var frameTime = animation.duration / animation.length;
|
|
|
+THREE.BoxHelper.prototype.update = function ( object ) {
|
|
|
|
|
|
- animation.time += animation.direction * delta;
|
|
|
+ var geometry = object.geometry;
|
|
|
|
|
|
- if ( animation.mirroredLoop ) {
|
|
|
+ if ( geometry.boundingBox === null ) {
|
|
|
|
|
|
- if ( animation.time > animation.duration || animation.time < 0 ) {
|
|
|
+ geometry.computeBoundingBox();
|
|
|
|
|
|
- animation.direction *= - 1;
|
|
|
+ }
|
|
|
|
|
|
- if ( animation.time > animation.duration ) {
|
|
|
+ var min = geometry.boundingBox.min;
|
|
|
+ var max = geometry.boundingBox.max;
|
|
|
|
|
|
- animation.time = animation.duration;
|
|
|
- animation.directionBackwards = true;
|
|
|
+ /*
|
|
|
+ 5____4
|
|
|
+ 1/___0/|
|
|
|
+ | 6__|_7
|
|
|
+ 2/___3/
|
|
|
|
|
|
- }
|
|
|
+ 0: max.x, max.y, max.z
|
|
|
+ 1: min.x, max.y, max.z
|
|
|
+ 2: min.x, min.y, max.z
|
|
|
+ 3: max.x, min.y, max.z
|
|
|
+ 4: max.x, max.y, min.z
|
|
|
+ 5: min.x, max.y, min.z
|
|
|
+ 6: min.x, min.y, min.z
|
|
|
+ 7: max.x, min.y, min.z
|
|
|
+ */
|
|
|
|
|
|
- if ( animation.time < 0 ) {
|
|
|
+ var vertices = this.geometry.attributes.position.array;
|
|
|
|
|
|
- animation.time = 0;
|
|
|
- animation.directionBackwards = false;
|
|
|
+ vertices[ 0 ] = max.x; vertices[ 1 ] = max.y; vertices[ 2 ] = max.z;
|
|
|
+ vertices[ 3 ] = min.x; vertices[ 4 ] = max.y; vertices[ 5 ] = max.z;
|
|
|
|
|
|
- }
|
|
|
+ vertices[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z;
|
|
|
+ vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z;
|
|
|
|
|
|
- }
|
|
|
+ vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z;
|
|
|
+ vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z;
|
|
|
|
|
|
- } else {
|
|
|
+ vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z;
|
|
|
+ vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z;
|
|
|
|
|
|
- animation.time = animation.time % animation.duration;
|
|
|
+ //
|
|
|
|
|
|
- if ( animation.time < 0 ) animation.time += animation.duration;
|
|
|
+ vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z;
|
|
|
+ vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z;
|
|
|
|
|
|
- }
|
|
|
+ vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z;
|
|
|
+ vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z;
|
|
|
|
|
|
- var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
|
|
|
- var weight = animation.weight;
|
|
|
+ vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z;
|
|
|
+ vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z;
|
|
|
|
|
|
- if ( keyframe !== animation.currentFrame ) {
|
|
|
+ vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z;
|
|
|
+ vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z;
|
|
|
|
|
|
- this.morphTargetInfluences[ animation.lastFrame ] = 0;
|
|
|
- this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
|
|
|
+ //
|
|
|
|
|
|
- this.morphTargetInfluences[ keyframe ] = 0;
|
|
|
+ vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z;
|
|
|
+ vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z;
|
|
|
|
|
|
- animation.lastFrame = animation.currentFrame;
|
|
|
- animation.currentFrame = keyframe;
|
|
|
+ vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z;
|
|
|
+ vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z;
|
|
|
|
|
|
- }
|
|
|
+ vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z;
|
|
|
+ vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z;
|
|
|
|
|
|
- var mix = ( animation.time % frameTime ) / frameTime;
|
|
|
+ vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z;
|
|
|
+ vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z;
|
|
|
|
|
|
- if ( animation.directionBackwards ) mix = 1 - mix;
|
|
|
+ this.geometry.attributes.position.needsUpdate = true;
|
|
|
|
|
|
- this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
|
|
|
- this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
|
|
|
+ this.geometry.computeBoundingSphere();
|
|
|
|
|
|
- }
|
|
|
+ this.matrix = object.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/renderers/plugins/LensFlarePlugin.js
|
|
|
+// File:src/extras/helpers/BoundingBoxHelper.js
|
|
|
|
|
|
/**
|
|
|
- * @author mikael emtinger / http://gomo.se/
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
*/
|
|
|
|
|
|
-THREE.LensFlarePlugin = function ( renderer ) {
|
|
|
-
|
|
|
- var gl = renderer.context;
|
|
|
-
|
|
|
- var flares = [];
|
|
|
-
|
|
|
- var vertexBuffer, elementBuffer;
|
|
|
- var program, attributes, uniforms;
|
|
|
- var hasVertexTexture;
|
|
|
-
|
|
|
- var tempTexture, occlusionTexture;
|
|
|
-
|
|
|
- var init = function () {
|
|
|
+// a helper to show the world-axis-aligned bounding box for an object
|
|
|
|
|
|
- var vertices = new Float32Array( [
|
|
|
- -1, -1, 0, 0,
|
|
|
- 1, -1, 1, 0,
|
|
|
- 1, 1, 1, 1,
|
|
|
- -1, 1, 0, 1
|
|
|
- ] );
|
|
|
+THREE.BoundingBoxHelper = function ( object, hex ) {
|
|
|
|
|
|
- var faces = new Uint16Array( [
|
|
|
- 0, 1, 2,
|
|
|
- 0, 2, 3
|
|
|
- ] );
|
|
|
+ var color = ( hex !== undefined ) ? hex : 0x888888;
|
|
|
|
|
|
- // buffers
|
|
|
+ this.object = object;
|
|
|
|
|
|
- vertexBuffer = gl.createBuffer();
|
|
|
- elementBuffer = gl.createBuffer();
|
|
|
+ this.box = new THREE.Box3();
|
|
|
|
|
|
- gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
|
|
|
- gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
|
|
|
+ THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );
|
|
|
|
|
|
- gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
|
|
|
- gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
|
|
|
+};
|
|
|
|
|
|
- // textures
|
|
|
+THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
|
|
|
|
|
|
- tempTexture = gl.createTexture();
|
|
|
- occlusionTexture = gl.createTexture();
|
|
|
+THREE.BoundingBoxHelper.prototype.update = function () {
|
|
|
|
|
|
- gl.bindTexture( gl.TEXTURE_2D, tempTexture );
|
|
|
- gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );
|
|
|
- gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
|
|
|
- gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
|
|
|
- gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
|
|
|
- gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
|
|
|
+ this.box.setFromObject( this.object );
|
|
|
|
|
|
- gl.bindTexture( gl.TEXTURE_2D, occlusionTexture );
|
|
|
- gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
|
|
|
- gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
|
|
|
- gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
|
|
|
- gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
|
|
|
- gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
|
|
|
+ this.box.size( this.scale );
|
|
|
|
|
|
- hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;
|
|
|
+ this.box.center( this.position );
|
|
|
|
|
|
- if ( hasVertexTexture ) {
|
|
|
+};
|
|
|
|
|
|
- program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ] );
|
|
|
+// File:src/extras/helpers/CameraHelper.js
|
|
|
|
|
|
- } else {
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ *
|
|
|
+ * - shows frustum, line of sight and up of the camera
|
|
|
+ * - suitable for fast updates
|
|
|
+ * - based on frustum visualization in lightgl.js shadowmap example
|
|
|
+ * http://evanw.github.com/lightgl.js/tests/shadowmap.html
|
|
|
+ */
|
|
|
|
|
|
- program = createProgram( THREE.ShaderFlares[ "lensFlare" ] );
|
|
|
+THREE.CameraHelper = function ( camera ) {
|
|
|
|
|
|
- }
|
|
|
+ var geometry = new THREE.Geometry();
|
|
|
+ var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
|
|
|
|
|
|
- attributes = {
|
|
|
- vertex: gl.getAttribLocation ( program, "position" ),
|
|
|
- uv: gl.getAttribLocation ( program, "uv" )
|
|
|
- }
|
|
|
+ var pointMap = {};
|
|
|
|
|
|
- uniforms = {
|
|
|
- renderType: gl.getUniformLocation( program, "renderType" ),
|
|
|
- map: gl.getUniformLocation( program, "map" ),
|
|
|
- occlusionMap: gl.getUniformLocation( program, "occlusionMap" ),
|
|
|
- opacity: gl.getUniformLocation( program, "opacity" ),
|
|
|
- color: gl.getUniformLocation( program, "color" ),
|
|
|
- scale: gl.getUniformLocation( program, "scale" ),
|
|
|
- rotation: gl.getUniformLocation( program, "rotation" ),
|
|
|
- screenPosition: gl.getUniformLocation( program, "screenPosition" )
|
|
|
- };
|
|
|
+ // colors
|
|
|
|
|
|
- };
|
|
|
+ var hexFrustum = 0xffaa00;
|
|
|
+ var hexCone = 0xff0000;
|
|
|
+ var hexUp = 0x00aaff;
|
|
|
+ var hexTarget = 0xffffff;
|
|
|
+ var hexCross = 0x333333;
|
|
|
|
|
|
- this.init = function () {}; // TODO: Remove
|
|
|
+ // near
|
|
|
|
|
|
- /*
|
|
|
- * Render lens flares
|
|
|
- * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
|
|
|
- * reads these back and calculates occlusion.
|
|
|
- */
|
|
|
+ addLine( "n1", "n2", hexFrustum );
|
|
|
+ addLine( "n2", "n4", hexFrustum );
|
|
|
+ addLine( "n4", "n3", hexFrustum );
|
|
|
+ addLine( "n3", "n1", hexFrustum );
|
|
|
|
|
|
- this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
|
|
|
+ // far
|
|
|
|
|
|
- flares.length = 0;
|
|
|
+ addLine( "f1", "f2", hexFrustum );
|
|
|
+ addLine( "f2", "f4", hexFrustum );
|
|
|
+ addLine( "f4", "f3", hexFrustum );
|
|
|
+ addLine( "f3", "f1", hexFrustum );
|
|
|
|
|
|
- scene.traverseVisible( function ( child ) {
|
|
|
+ // sides
|
|
|
|
|
|
- if ( child instanceof THREE.LensFlare ) {
|
|
|
+ addLine( "n1", "f1", hexFrustum );
|
|
|
+ addLine( "n2", "f2", hexFrustum );
|
|
|
+ addLine( "n3", "f3", hexFrustum );
|
|
|
+ addLine( "n4", "f4", hexFrustum );
|
|
|
|
|
|
- flares.push( child );
|
|
|
+ // cone
|
|
|
|
|
|
- }
|
|
|
+ addLine( "p", "n1", hexCone );
|
|
|
+ addLine( "p", "n2", hexCone );
|
|
|
+ addLine( "p", "n3", hexCone );
|
|
|
+ addLine( "p", "n4", hexCone );
|
|
|
|
|
|
- } );
|
|
|
+ // up
|
|
|
|
|
|
- if ( flares.length === 0 ) return;
|
|
|
+ addLine( "u1", "u2", hexUp );
|
|
|
+ addLine( "u2", "u3", hexUp );
|
|
|
+ addLine( "u3", "u1", hexUp );
|
|
|
|
|
|
- var tempPosition = new THREE.Vector3();
|
|
|
+ // target
|
|
|
|
|
|
- var invAspect = viewportHeight / viewportWidth,
|
|
|
- halfViewportWidth = viewportWidth * 0.5,
|
|
|
- halfViewportHeight = viewportHeight * 0.5;
|
|
|
+ addLine( "c", "t", hexTarget );
|
|
|
+ addLine( "p", "c", hexCross );
|
|
|
|
|
|
- var size = 16 / viewportHeight,
|
|
|
- scale = new THREE.Vector2( size * invAspect, size );
|
|
|
+ // cross
|
|
|
|
|
|
- var screenPosition = new THREE.Vector3( 1, 1, 0 ),
|
|
|
- screenPositionPixels = new THREE.Vector2( 1, 1 );
|
|
|
+ addLine( "cn1", "cn2", hexCross );
|
|
|
+ addLine( "cn3", "cn4", hexCross );
|
|
|
|
|
|
- if ( program === undefined ) {
|
|
|
+ addLine( "cf1", "cf2", hexCross );
|
|
|
+ addLine( "cf3", "cf4", hexCross );
|
|
|
|
|
|
- init();
|
|
|
+ function addLine( a, b, hex ) {
|
|
|
|
|
|
- }
|
|
|
+ addPoint( a, hex );
|
|
|
+ addPoint( b, hex );
|
|
|
|
|
|
- gl.useProgram( program );
|
|
|
+ }
|
|
|
|
|
|
- gl.enableVertexAttribArray( attributes.vertex );
|
|
|
- gl.enableVertexAttribArray( attributes.uv );
|
|
|
+ function addPoint( id, hex ) {
|
|
|
|
|
|
- // loop through all lens flares to update their occlusion and positions
|
|
|
- // setup gl and common used attribs/unforms
|
|
|
+ geometry.vertices.push( new THREE.Vector3() );
|
|
|
+ geometry.colors.push( new THREE.Color( hex ) );
|
|
|
|
|
|
- gl.uniform1i( uniforms.occlusionMap, 0 );
|
|
|
- gl.uniform1i( uniforms.map, 1 );
|
|
|
+ if ( pointMap[ id ] === undefined ) {
|
|
|
|
|
|
- gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
|
|
|
- gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );
|
|
|
- gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
|
|
|
+ pointMap[ id ] = [];
|
|
|
|
|
|
- gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
|
|
|
+ }
|
|
|
|
|
|
- gl.disable( gl.CULL_FACE );
|
|
|
- gl.depthMask( false );
|
|
|
+ pointMap[ id ].push( geometry.vertices.length - 1 );
|
|
|
|
|
|
- for ( var i = 0, l = flares.length; i < l; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- size = 16 / viewportHeight;
|
|
|
- scale.set( size * invAspect, size );
|
|
|
+ THREE.Line.call( this, geometry, material, THREE.LinePieces );
|
|
|
|
|
|
- // calc object screen position
|
|
|
+ this.camera = camera;
|
|
|
+ this.matrix = camera.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- var flare = flares[ i ];
|
|
|
-
|
|
|
- tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] );
|
|
|
+ this.pointMap = pointMap;
|
|
|
|
|
|
- tempPosition.applyMatrix4( camera.matrixWorldInverse );
|
|
|
- tempPosition.applyProjection( camera.projectionMatrix );
|
|
|
+ this.update();
|
|
|
|
|
|
- // setup arrays for gl programs
|
|
|
+};
|
|
|
|
|
|
- screenPosition.copy( tempPosition )
|
|
|
+THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
|
|
|
- screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
|
|
|
+THREE.CameraHelper.prototype.update = function () {
|
|
|
|
|
|
- // screen cull
|
|
|
+ var geometry, pointMap;
|
|
|
+
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
+ var camera = new THREE.Camera();
|
|
|
|
|
|
- if ( hasVertexTexture || (
|
|
|
- screenPositionPixels.x > 0 &&
|
|
|
- screenPositionPixels.x < viewportWidth &&
|
|
|
- screenPositionPixels.y > 0 &&
|
|
|
- screenPositionPixels.y < viewportHeight ) ) {
|
|
|
+ var setPoint = function ( point, x, y, z ) {
|
|
|
|
|
|
- // save current RGB to temp texture
|
|
|
+ vector.set( x, y, z ).unproject( camera );
|
|
|
|
|
|
- gl.activeTexture( gl.TEXTURE1 );
|
|
|
- gl.bindTexture( gl.TEXTURE_2D, tempTexture );
|
|
|
- gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
|
|
|
+ var points = pointMap[ point ];
|
|
|
|
|
|
+ if ( points !== undefined ) {
|
|
|
|
|
|
- // render pink quad
|
|
|
+ for ( var i = 0, il = points.length; i < il; i ++ ) {
|
|
|
|
|
|
- gl.uniform1i( uniforms.renderType, 0 );
|
|
|
- gl.uniform2f( uniforms.scale, scale.x, scale.y );
|
|
|
- gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
|
|
|
+ geometry.vertices[ points[ i ] ].copy( vector );
|
|
|
|
|
|
- gl.disable( gl.BLEND );
|
|
|
- gl.enable( gl.DEPTH_TEST );
|
|
|
+ }
|
|
|
|
|
|
- gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
+ }
|
|
|
|
|
|
+ };
|
|
|
|
|
|
- // copy result to occlusionMap
|
|
|
+ return function () {
|
|
|
|
|
|
- gl.activeTexture( gl.TEXTURE0 );
|
|
|
- gl.bindTexture( gl.TEXTURE_2D, occlusionTexture );
|
|
|
- gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
|
|
|
+ geometry = this.geometry;
|
|
|
+ pointMap = this.pointMap;
|
|
|
|
|
|
+ var w = 1, h = 1;
|
|
|
|
|
|
- // restore graphics
|
|
|
+ // we need just camera projection matrix
|
|
|
+ // world matrix must be identity
|
|
|
|
|
|
- gl.uniform1i( uniforms.renderType, 1 );
|
|
|
- gl.disable( gl.DEPTH_TEST );
|
|
|
+ camera.projectionMatrix.copy( this.camera.projectionMatrix );
|
|
|
|
|
|
- gl.activeTexture( gl.TEXTURE1 );
|
|
|
- gl.bindTexture( gl.TEXTURE_2D, tempTexture );
|
|
|
- gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
+ // center / target
|
|
|
|
|
|
+ setPoint( "c", 0, 0, - 1 );
|
|
|
+ setPoint( "t", 0, 0, 1 );
|
|
|
|
|
|
- // update object positions
|
|
|
+ // near
|
|
|
|
|
|
- flare.positionScreen.copy( screenPosition )
|
|
|
+ setPoint( "n1", - w, - h, - 1 );
|
|
|
+ setPoint( "n2", w, - h, - 1 );
|
|
|
+ setPoint( "n3", - w, h, - 1 );
|
|
|
+ setPoint( "n4", w, h, - 1 );
|
|
|
|
|
|
- if ( flare.customUpdateCallback ) {
|
|
|
+ // far
|
|
|
|
|
|
- flare.customUpdateCallback( flare );
|
|
|
+ setPoint( "f1", - w, - h, 1 );
|
|
|
+ setPoint( "f2", w, - h, 1 );
|
|
|
+ setPoint( "f3", - w, h, 1 );
|
|
|
+ setPoint( "f4", w, h, 1 );
|
|
|
|
|
|
- } else {
|
|
|
+ // up
|
|
|
|
|
|
- flare.updateLensFlares();
|
|
|
+ setPoint( "u1", w * 0.7, h * 1.1, - 1 );
|
|
|
+ setPoint( "u2", - w * 0.7, h * 1.1, - 1 );
|
|
|
+ setPoint( "u3", 0, h * 2, - 1 );
|
|
|
|
|
|
- }
|
|
|
+ // cross
|
|
|
|
|
|
- // render flares
|
|
|
+ setPoint( "cf1", - w, 0, 1 );
|
|
|
+ setPoint( "cf2", w, 0, 1 );
|
|
|
+ setPoint( "cf3", 0, - h, 1 );
|
|
|
+ setPoint( "cf4", 0, h, 1 );
|
|
|
|
|
|
- gl.uniform1i( uniforms.renderType, 2 );
|
|
|
- gl.enable( gl.BLEND );
|
|
|
+ setPoint( "cn1", - w, 0, - 1 );
|
|
|
+ setPoint( "cn2", w, 0, - 1 );
|
|
|
+ setPoint( "cn3", 0, - h, - 1 );
|
|
|
+ setPoint( "cn4", 0, h, - 1 );
|
|
|
|
|
|
- for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
|
|
|
+ geometry.verticesNeedUpdate = true;
|
|
|
|
|
|
- var sprite = flare.lensFlares[ j ];
|
|
|
+ };
|
|
|
|
|
|
- if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
|
|
|
+}();
|
|
|
|
|
|
- screenPosition.x = sprite.x;
|
|
|
- screenPosition.y = sprite.y;
|
|
|
- screenPosition.z = sprite.z;
|
|
|
+// File:src/extras/helpers/DirectionalLightHelper.js
|
|
|
|
|
|
- size = sprite.size * sprite.scale / viewportHeight;
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
+ */
|
|
|
|
|
|
- scale.x = size * invAspect;
|
|
|
- scale.y = size;
|
|
|
+THREE.DirectionalLightHelper = function ( light, size ) {
|
|
|
|
|
|
- gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
|
|
|
- gl.uniform2f( uniforms.scale, scale.x, scale.y );
|
|
|
- gl.uniform1f( uniforms.rotation, sprite.rotation );
|
|
|
+ THREE.Object3D.call( this );
|
|
|
|
|
|
- gl.uniform1f( uniforms.opacity, sprite.opacity );
|
|
|
- gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
|
|
|
+ this.light = light;
|
|
|
+ this.light.updateMatrixWorld();
|
|
|
|
|
|
- renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
|
|
|
- renderer.setTexture( sprite.texture, 1 );
|
|
|
+ this.matrix = light.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
+ size = size || 1;
|
|
|
|
|
|
- }
|
|
|
+ var geometry = new THREE.Geometry();
|
|
|
+ geometry.vertices.push(
|
|
|
+ new THREE.Vector3( - size, size, 0 ),
|
|
|
+ new THREE.Vector3( size, size, 0 ),
|
|
|
+ new THREE.Vector3( size, - size, 0 ),
|
|
|
+ new THREE.Vector3( - size, - size, 0 ),
|
|
|
+ new THREE.Vector3( - size, size, 0 )
|
|
|
+ );
|
|
|
|
|
|
- }
|
|
|
+ var material = new THREE.LineBasicMaterial( { fog: false } );
|
|
|
+ material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
|
|
|
- }
|
|
|
+ this.lightPlane = new THREE.Line( geometry, material );
|
|
|
+ this.add( this.lightPlane );
|
|
|
|
|
|
- }
|
|
|
+ geometry = new THREE.Geometry();
|
|
|
+ geometry.vertices.push(
|
|
|
+ new THREE.Vector3(),
|
|
|
+ new THREE.Vector3()
|
|
|
+ );
|
|
|
|
|
|
- // restore gl
|
|
|
+ material = new THREE.LineBasicMaterial( { fog: false } );
|
|
|
+ material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
|
|
|
- gl.enable( gl.CULL_FACE );
|
|
|
- gl.enable( gl.DEPTH_TEST );
|
|
|
- gl.depthMask( true );
|
|
|
+ this.targetLine = new THREE.Line( geometry, material );
|
|
|
+ this.add( this.targetLine );
|
|
|
|
|
|
- renderer.resetGLState();
|
|
|
+ this.update();
|
|
|
|
|
|
- };
|
|
|
+};
|
|
|
|
|
|
- function createProgram ( shader ) {
|
|
|
+THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
- var program = gl.createProgram();
|
|
|
+THREE.DirectionalLightHelper.prototype.dispose = function () {
|
|
|
|
|
|
- var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
|
|
|
- var vertexShader = gl.createShader( gl.VERTEX_SHADER );
|
|
|
+ this.lightPlane.geometry.dispose();
|
|
|
+ this.lightPlane.material.dispose();
|
|
|
+ this.targetLine.geometry.dispose();
|
|
|
+ this.targetLine.material.dispose();
|
|
|
+};
|
|
|
|
|
|
- var prefix = "precision " + renderer.getPrecision() + " float;\n";
|
|
|
+THREE.DirectionalLightHelper.prototype.update = function () {
|
|
|
|
|
|
- gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
|
|
|
- gl.shaderSource( vertexShader, prefix + shader.vertexShader );
|
|
|
+ var v1 = new THREE.Vector3();
|
|
|
+ var v2 = new THREE.Vector3();
|
|
|
+ var v3 = new THREE.Vector3();
|
|
|
|
|
|
- gl.compileShader( fragmentShader );
|
|
|
- gl.compileShader( vertexShader );
|
|
|
+ return function () {
|
|
|
|
|
|
- gl.attachShader( program, fragmentShader );
|
|
|
- gl.attachShader( program, vertexShader );
|
|
|
+ v1.setFromMatrixPosition( this.light.matrixWorld );
|
|
|
+ v2.setFromMatrixPosition( this.light.target.matrixWorld );
|
|
|
+ v3.subVectors( v2, v1 );
|
|
|
|
|
|
- gl.linkProgram( program );
|
|
|
+ this.lightPlane.lookAt( v3 );
|
|
|
+ this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
|
|
|
- return program;
|
|
|
+ this.targetLine.geometry.vertices[ 1 ].copy( v3 );
|
|
|
+ this.targetLine.geometry.verticesNeedUpdate = true;
|
|
|
+ this.targetLine.material.color.copy( this.lightPlane.material.color );
|
|
|
|
|
|
};
|
|
|
|
|
|
-};
|
|
|
+}();
|
|
|
|
|
|
-// File:src/extras/renderers/plugins/ShadowMapPlugin.js
|
|
|
+// File:src/extras/helpers/EdgesHelper.js
|
|
|
|
|
|
/**
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
*/
|
|
|
|
|
|
-THREE.ShadowMapPlugin = function () {
|
|
|
+THREE.EdgesHelper = function ( object, hex ) {
|
|
|
|
|
|
- var _gl,
|
|
|
- _renderer,
|
|
|
- _lights, _webglObjects, _webglObjectsImmediate,
|
|
|
- _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
|
|
|
+ var color = ( hex !== undefined ) ? hex : 0xffffff;
|
|
|
|
|
|
- _frustum = new THREE.Frustum(),
|
|
|
- _projScreenMatrix = new THREE.Matrix4(),
|
|
|
+ var edge = [ 0, 0 ], hash = {};
|
|
|
+ var sortFunction = function ( a, b ) { return a - b };
|
|
|
|
|
|
- _min = new THREE.Vector3(),
|
|
|
- _max = new THREE.Vector3(),
|
|
|
+ var keys = [ 'a', 'b', 'c' ];
|
|
|
+ var geometry = new THREE.BufferGeometry();
|
|
|
|
|
|
- _matrixPosition = new THREE.Vector3(),
|
|
|
-
|
|
|
- _renderList = [];
|
|
|
+ var geometry2 = object.geometry.clone();
|
|
|
|
|
|
- this.init = function ( renderer, lights, webglObjects, webglObjectsImmediate ) {
|
|
|
+ geometry2.mergeVertices();
|
|
|
+ geometry2.computeFaceNormals();
|
|
|
|
|
|
- _gl = renderer.context;
|
|
|
- _renderer = renderer;
|
|
|
- _lights = lights;
|
|
|
+ var vertices = geometry2.vertices;
|
|
|
+ var faces = geometry2.faces;
|
|
|
+ var numEdges = 0;
|
|
|
|
|
|
- _webglObjects = webglObjects;
|
|
|
- _webglObjectsImmediate = webglObjectsImmediate;
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- var depthShader = THREE.ShaderLib[ "depthRGBA" ];
|
|
|
- var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
|
|
|
+ var face = faces[ i ];
|
|
|
|
|
|
- _depthMaterial = new THREE.ShaderMaterial( {
|
|
|
- uniforms: depthUniforms,
|
|
|
- vertexShader: depthShader.vertexShader,
|
|
|
- fragmentShader: depthShader.fragmentShader
|
|
|
- } );
|
|
|
+ for ( var j = 0; j < 3; j ++ ) {
|
|
|
|
|
|
- _depthMaterialMorph = new THREE.ShaderMaterial( {
|
|
|
- uniforms: depthUniforms,
|
|
|
- vertexShader: depthShader.vertexShader,
|
|
|
- fragmentShader: depthShader.fragmentShader,
|
|
|
- morphTargets: true
|
|
|
- } );
|
|
|
+ edge[ 0 ] = face[ keys[ j ] ];
|
|
|
+ edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
|
|
|
+ edge.sort( sortFunction );
|
|
|
|
|
|
- _depthMaterialSkin = new THREE.ShaderMaterial( {
|
|
|
- uniforms: depthUniforms,
|
|
|
- vertexShader: depthShader.vertexShader,
|
|
|
- fragmentShader: depthShader.fragmentShader,
|
|
|
- skinning: true
|
|
|
- } );
|
|
|
+ var key = edge.toString();
|
|
|
|
|
|
- _depthMaterialMorphSkin = new THREE.ShaderMaterial( {
|
|
|
- uniforms: depthUniforms,
|
|
|
- vertexShader: depthShader.vertexShader,
|
|
|
- fragmentShader: depthShader.fragmentShader,
|
|
|
- morphTargets: true,
|
|
|
- skinning: true
|
|
|
- } );
|
|
|
+ if ( hash[ key ] === undefined ) {
|
|
|
|
|
|
- _depthMaterial._shadowPass = true;
|
|
|
- _depthMaterialMorph._shadowPass = true;
|
|
|
- _depthMaterialSkin._shadowPass = true;
|
|
|
- _depthMaterialMorphSkin._shadowPass = true;
|
|
|
+ hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };
|
|
|
+ numEdges ++;
|
|
|
|
|
|
- };
|
|
|
+ } else {
|
|
|
|
|
|
- this.render = function ( scene, camera ) {
|
|
|
+ hash[ key ].face2 = i;
|
|
|
|
|
|
- if ( _renderer.shadowMapEnabled === false || _renderer.shadowMapAutoUpdate === false ) return;
|
|
|
+ }
|
|
|
|
|
|
- this.update( scene, camera );
|
|
|
+ }
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
- this.update = function ( scene, camera ) {
|
|
|
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( numEdges * 2 * 3 ), 3 ) );
|
|
|
|
|
|
- var i, il, j, jl, n,
|
|
|
+ var coords = geometry.attributes.position.array;
|
|
|
|
|
|
- shadowMap, shadowMatrix, shadowCamera,
|
|
|
- program, buffer, material,
|
|
|
- webglObject, object, light,
|
|
|
+ var index = 0;
|
|
|
|
|
|
- lights = [],
|
|
|
- k = 0,
|
|
|
+ for ( var key in hash ) {
|
|
|
|
|
|
- fog = null;
|
|
|
+ var h = hash[ key ];
|
|
|
|
|
|
- // set GL state for depth map
|
|
|
+ if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK
|
|
|
|
|
|
- _gl.clearColor( 1, 1, 1, 1 );
|
|
|
- _gl.disable( _gl.BLEND );
|
|
|
+ var vertex = vertices[ h.vert1 ];
|
|
|
+ coords[ index ++ ] = vertex.x;
|
|
|
+ coords[ index ++ ] = vertex.y;
|
|
|
+ coords[ index ++ ] = vertex.z;
|
|
|
|
|
|
- _gl.enable( _gl.CULL_FACE );
|
|
|
- _gl.frontFace( _gl.CCW );
|
|
|
+ vertex = vertices[ h.vert2 ];
|
|
|
+ coords[ index ++ ] = vertex.x;
|
|
|
+ coords[ index ++ ] = vertex.y;
|
|
|
+ coords[ index ++ ] = vertex.z;
|
|
|
|
|
|
- if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
|
|
|
+ }
|
|
|
|
|
|
- _gl.cullFace( _gl.FRONT );
|
|
|
+ }
|
|
|
|
|
|
- } else {
|
|
|
+ THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
|
|
|
|
|
|
- _gl.cullFace( _gl.BACK );
|
|
|
+ this.matrix = object.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- _renderer.setDepthTest( true );
|
|
|
+THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- // preprocess lights
|
|
|
- // - skip lights that are not casting shadows
|
|
|
- // - create virtual lights for cascaded shadow maps
|
|
|
+// File:src/extras/helpers/FaceNormalsHelper.js
|
|
|
|
|
|
- for ( i = 0, il = _lights.length; i < il; i ++ ) {
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
+*/
|
|
|
|
|
|
- light = _lights[ i ];
|
|
|
+THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {
|
|
|
|
|
|
- if ( ! light.castShadow ) continue;
|
|
|
+ this.object = object;
|
|
|
|
|
|
- if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
|
|
|
+ this.size = ( size !== undefined ) ? size : 1;
|
|
|
|
|
|
- for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
|
|
|
+ var color = ( hex !== undefined ) ? hex : 0xffff00;
|
|
|
|
|
|
- var virtualLight;
|
|
|
+ var width = ( linewidth !== undefined ) ? linewidth : 1;
|
|
|
|
|
|
- if ( ! light.shadowCascadeArray[ n ] ) {
|
|
|
+ var geometry = new THREE.Geometry();
|
|
|
|
|
|
- virtualLight = createVirtualLight( light, n );
|
|
|
- virtualLight.originalCamera = camera;
|
|
|
+ var faces = this.object.geometry.faces;
|
|
|
|
|
|
- var gyro = new THREE.Gyroscope();
|
|
|
- gyro.position.copy( light.shadowCascadeOffset );
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- gyro.add( virtualLight );
|
|
|
- gyro.add( virtualLight.target );
|
|
|
+ geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
|
|
|
|
|
|
- camera.add( gyro );
|
|
|
+ }
|
|
|
|
|
|
- light.shadowCascadeArray[ n ] = virtualLight;
|
|
|
+ THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
|
|
|
|
|
|
- console.log( "Created virtualLight", virtualLight );
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- } else {
|
|
|
+ this.normalMatrix = new THREE.Matrix3();
|
|
|
|
|
|
- virtualLight = light.shadowCascadeArray[ n ];
|
|
|
+ this.update();
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- updateVirtualLight( light, n );
|
|
|
+THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- lights[ k ] = virtualLight;
|
|
|
- k ++;
|
|
|
+THREE.FaceNormalsHelper.prototype.update = function () {
|
|
|
|
|
|
- }
|
|
|
+ var vertices = this.geometry.vertices;
|
|
|
|
|
|
- } else {
|
|
|
+ var object = this.object;
|
|
|
+ var objectVertices = object.geometry.vertices;
|
|
|
+ var objectFaces = object.geometry.faces;
|
|
|
+ var objectWorldMatrix = object.matrixWorld;
|
|
|
|
|
|
- lights[ k ] = light;
|
|
|
- k ++;
|
|
|
+ object.updateMatrixWorld( true );
|
|
|
|
|
|
- }
|
|
|
+ this.normalMatrix.getNormalMatrix( objectWorldMatrix );
|
|
|
|
|
|
- }
|
|
|
+ for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) {
|
|
|
|
|
|
- // render depth map
|
|
|
+ var face = objectFaces[ i ];
|
|
|
|
|
|
- for ( i = 0, il = lights.length; i < il; i ++ ) {
|
|
|
+ vertices[ i2 ].copy( objectVertices[ face.a ] )
|
|
|
+ .add( objectVertices[ face.b ] )
|
|
|
+ .add( objectVertices[ face.c ] )
|
|
|
+ .divideScalar( 3 )
|
|
|
+ .applyMatrix4( objectWorldMatrix );
|
|
|
|
|
|
- light = lights[ i ];
|
|
|
+ vertices[ i2 + 1 ].copy( face.normal )
|
|
|
+ .applyMatrix3( this.normalMatrix )
|
|
|
+ .normalize()
|
|
|
+ .multiplyScalar( this.size )
|
|
|
+ .add( vertices[ i2 ] );
|
|
|
|
|
|
- if ( ! light.shadowMap ) {
|
|
|
+ }
|
|
|
|
|
|
- var shadowFilter = THREE.LinearFilter;
|
|
|
+ this.geometry.verticesNeedUpdate = true;
|
|
|
|
|
|
- if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) {
|
|
|
+ return this;
|
|
|
|
|
|
- shadowFilter = THREE.NearestFilter;
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
|
|
|
- var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
|
|
|
+// File:src/extras/helpers/GridHelper.js
|
|
|
|
|
|
- light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
|
|
|
- light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- light.shadowMatrix = new THREE.Matrix4();
|
|
|
+THREE.GridHelper = function ( size, step ) {
|
|
|
|
|
|
- }
|
|
|
+ var geometry = new THREE.Geometry();
|
|
|
+ var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
|
|
|
|
|
|
- if ( ! light.shadowCamera ) {
|
|
|
+ this.color1 = new THREE.Color( 0x444444 );
|
|
|
+ this.color2 = new THREE.Color( 0x888888 );
|
|
|
|
|
|
- if ( light instanceof THREE.SpotLight ) {
|
|
|
+ for ( var i = - size; i <= size; i += step ) {
|
|
|
|
|
|
- light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
|
|
|
+ geometry.vertices.push(
|
|
|
+ new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ),
|
|
|
+ new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size )
|
|
|
+ );
|
|
|
|
|
|
- } else if ( light instanceof THREE.DirectionalLight ) {
|
|
|
+ var color = i === 0 ? this.color1 : this.color2;
|
|
|
|
|
|
- light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
|
|
|
+ geometry.colors.push( color, color, color, color );
|
|
|
|
|
|
- } else {
|
|
|
+ }
|
|
|
|
|
|
- console.error( "Unsupported light type for shadow" );
|
|
|
- continue;
|
|
|
+ THREE.Line.call( this, geometry, material, THREE.LinePieces );
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- scene.add( light.shadowCamera );
|
|
|
+THREE.GridHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
|
|
|
+THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) {
|
|
|
|
|
|
- }
|
|
|
+ this.color1.set( colorCenterLine );
|
|
|
+ this.color2.set( colorGrid );
|
|
|
|
|
|
- if ( light.shadowCameraVisible && ! light.cameraHelper ) {
|
|
|
+ this.geometry.colorsNeedUpdate = true;
|
|
|
|
|
|
- light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
|
|
|
- scene.add( light.cameraHelper );
|
|
|
+}
|
|
|
|
|
|
- }
|
|
|
+// File:src/extras/helpers/HemisphereLightHelper.js
|
|
|
|
|
|
- if ( light.isVirtual && virtualLight.originalCamera == camera ) {
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- updateShadowCamera( camera, light );
|
|
|
+THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) {
|
|
|
|
|
|
- }
|
|
|
+ THREE.Object3D.call( this );
|
|
|
|
|
|
- shadowMap = light.shadowMap;
|
|
|
- shadowMatrix = light.shadowMatrix;
|
|
|
- shadowCamera = light.shadowCamera;
|
|
|
+ this.light = light;
|
|
|
+ this.light.updateMatrixWorld();
|
|
|
|
|
|
- //
|
|
|
+ this.matrix = light.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- shadowCamera.position.setFromMatrixPosition( light.matrixWorld );
|
|
|
- _matrixPosition.setFromMatrixPosition( light.target.matrixWorld );
|
|
|
- shadowCamera.lookAt( _matrixPosition );
|
|
|
- shadowCamera.updateMatrixWorld();
|
|
|
+ this.colors = [ new THREE.Color(), new THREE.Color() ];
|
|
|
|
|
|
- shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
|
|
|
+ var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
|
|
|
+ geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
|
|
|
|
|
|
- //
|
|
|
+ for ( var i = 0, il = 8; i < il; i ++ ) {
|
|
|
|
|
|
- if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
|
|
|
- if ( light.shadowCameraVisible ) light.cameraHelper.update();
|
|
|
+ geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ];
|
|
|
|
|
|
- // compute shadow matrix
|
|
|
+ }
|
|
|
|
|
|
- shadowMatrix.set(
|
|
|
- 0.5, 0.0, 0.0, 0.5,
|
|
|
- 0.0, 0.5, 0.0, 0.5,
|
|
|
- 0.0, 0.0, 0.5, 0.5,
|
|
|
- 0.0, 0.0, 0.0, 1.0
|
|
|
- );
|
|
|
+ var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } );
|
|
|
|
|
|
- shadowMatrix.multiply( shadowCamera.projectionMatrix );
|
|
|
- shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
|
|
|
+ this.lightSphere = new THREE.Mesh( geometry, material );
|
|
|
+ this.add( this.lightSphere );
|
|
|
|
|
|
- // update camera matrices and frustum
|
|
|
+ this.update();
|
|
|
|
|
|
- _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
|
|
|
- _frustum.setFromMatrix( _projScreenMatrix );
|
|
|
+};
|
|
|
|
|
|
- // render shadow map
|
|
|
+THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
- _renderer.setRenderTarget( shadowMap );
|
|
|
- _renderer.clear();
|
|
|
+THREE.HemisphereLightHelper.prototype.dispose = function () {
|
|
|
+ this.lightSphere.geometry.dispose();
|
|
|
+ this.lightSphere.material.dispose();
|
|
|
+};
|
|
|
|
|
|
- // set object matrices & frustum culling
|
|
|
+THREE.HemisphereLightHelper.prototype.update = function () {
|
|
|
|
|
|
- _renderList.length = 0;
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
|
|
|
- projectObject( scene, scene, shadowCamera );
|
|
|
+ return function () {
|
|
|
|
|
|
+ this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
+ this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity );
|
|
|
|
|
|
- // render regular objects
|
|
|
+ this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );
|
|
|
+ this.lightSphere.geometry.colorsNeedUpdate = true;
|
|
|
|
|
|
- var objectMaterial, useMorphing, useSkinning;
|
|
|
+ }
|
|
|
|
|
|
- for ( j = 0, jl = _renderList.length; j < jl; j ++ ) {
|
|
|
+}();
|
|
|
|
|
|
- webglObject = _renderList[ j ];
|
|
|
+// File:src/extras/helpers/PointLightHelper.js
|
|
|
|
|
|
- object = webglObject.object;
|
|
|
- buffer = webglObject.buffer;
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- // culling is overriden globally for all objects
|
|
|
- // while rendering depth map
|
|
|
+THREE.PointLightHelper = function ( light, sphereSize ) {
|
|
|
|
|
|
- // need to deal with MeshFaceMaterial somehow
|
|
|
- // in that case just use the first of material.materials for now
|
|
|
- // (proper solution would require to break objects by materials
|
|
|
- // similarly to regular rendering and then set corresponding
|
|
|
- // depth materials per each chunk instead of just once per object)
|
|
|
+ this.light = light;
|
|
|
+ this.light.updateMatrixWorld();
|
|
|
|
|
|
- objectMaterial = getObjectMaterial( object );
|
|
|
+ var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
|
|
|
+ var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
|
|
|
+ material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
|
|
|
- useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
|
|
|
- useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
|
|
|
+ THREE.Mesh.call( this, geometry, material );
|
|
|
|
|
|
- if ( object.customDepthMaterial ) {
|
|
|
+ this.matrix = this.light.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- material = object.customDepthMaterial;
|
|
|
+ /*
|
|
|
+ var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
|
|
|
+ var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
|
|
|
|
|
|
- } else if ( useSkinning ) {
|
|
|
+ this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
|
|
|
+ this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
|
|
|
|
|
|
- material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
|
|
|
+ var d = light.distance;
|
|
|
|
|
|
- } else if ( useMorphing ) {
|
|
|
+ if ( d === 0.0 ) {
|
|
|
|
|
|
- material = _depthMaterialMorph;
|
|
|
+ this.lightDistance.visible = false;
|
|
|
|
|
|
- } else {
|
|
|
+ } else {
|
|
|
|
|
|
- material = _depthMaterial;
|
|
|
+ this.lightDistance.scale.set( d, d, d );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- _renderer.setMaterialFaces( objectMaterial );
|
|
|
+ this.add( this.lightDistance );
|
|
|
+ */
|
|
|
|
|
|
- if ( buffer instanceof THREE.BufferGeometry ) {
|
|
|
+};
|
|
|
|
|
|
- _renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object );
|
|
|
+THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype );
|
|
|
|
|
|
- } else {
|
|
|
+THREE.PointLightHelper.prototype.dispose = function () {
|
|
|
|
|
|
- _renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object );
|
|
|
+ this.geometry.dispose();
|
|
|
+ this.material.dispose();
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+THREE.PointLightHelper.prototype.update = function () {
|
|
|
|
|
|
- }
|
|
|
+ this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
|
|
|
- // set matrices and render immediate objects
|
|
|
+ /*
|
|
|
+ var d = this.light.distance;
|
|
|
|
|
|
- for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) {
|
|
|
+ if ( d === 0.0 ) {
|
|
|
|
|
|
- webglObject = _webglObjectsImmediate[ j ];
|
|
|
- object = webglObject.object;
|
|
|
+ this.lightDistance.visible = false;
|
|
|
|
|
|
- if ( object.visible && object.castShadow ) {
|
|
|
+ } else {
|
|
|
|
|
|
- object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
|
|
|
+ this.lightDistance.visible = true;
|
|
|
+ this.lightDistance.scale.set( d, d, d );
|
|
|
|
|
|
- _renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object );
|
|
|
+ }
|
|
|
+ */
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
+// File:src/extras/helpers/SkeletonHelper.js
|
|
|
|
|
|
- }
|
|
|
+/**
|
|
|
+ * @author Sean Griffin / http://twitter.com/sgrif
|
|
|
+ * @author Michael Guerrero / http://realitymeltdown.com
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author ikerr / http://verold.com
|
|
|
+ */
|
|
|
|
|
|
- // restore GL state
|
|
|
+THREE.SkeletonHelper = function ( object ) {
|
|
|
|
|
|
- var clearColor = _renderer.getClearColor(),
|
|
|
- clearAlpha = _renderer.getClearAlpha();
|
|
|
+ this.bones = this.getBoneList( object );
|
|
|
|
|
|
- _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
|
|
|
- _gl.enable( _gl.BLEND );
|
|
|
+ var geometry = new THREE.Geometry();
|
|
|
|
|
|
- if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
|
|
|
+ for ( var i = 0; i < this.bones.length; i ++ ) {
|
|
|
|
|
|
- _gl.cullFace( _gl.BACK );
|
|
|
+ var bone = this.bones[ i ];
|
|
|
|
|
|
- }
|
|
|
+ if ( bone.parent instanceof THREE.Bone ) {
|
|
|
|
|
|
- _renderer.resetGLState();
|
|
|
+ geometry.vertices.push( new THREE.Vector3() );
|
|
|
+ geometry.vertices.push( new THREE.Vector3() );
|
|
|
+ geometry.colors.push( new THREE.Color( 0, 0, 1 ) );
|
|
|
+ geometry.colors.push( new THREE.Color( 0, 1, 0 ) );
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
|
|
|
- function projectObject( scene, object, shadowCamera ){
|
|
|
+ }
|
|
|
|
|
|
- if ( object.visible ) {
|
|
|
+ var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } );
|
|
|
|
|
|
- var webglObjects = _webglObjects[ object.id ];
|
|
|
+ THREE.Line.call( this, geometry, material, THREE.LinePieces );
|
|
|
|
|
|
- if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) {
|
|
|
+ this.root = object;
|
|
|
|
|
|
- for (var i = 0, l = webglObjects.length; i < l; i ++ ) {
|
|
|
+ this.matrix = object.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- var webglObject = webglObjects[ i ];
|
|
|
+ this.update();
|
|
|
|
|
|
- object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
|
|
|
- _renderList.push( webglObject );
|
|
|
+};
|
|
|
|
|
|
- }
|
|
|
|
|
|
- }
|
|
|
+THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- for ( var i = 0, l = object.children.length; i < l; i ++ ) {
|
|
|
+THREE.SkeletonHelper.prototype.getBoneList = function( object ) {
|
|
|
|
|
|
- projectObject( scene, object.children[ i ], shadowCamera );
|
|
|
+ var boneList = [];
|
|
|
|
|
|
- }
|
|
|
+ if ( object instanceof THREE.Bone ) {
|
|
|
|
|
|
- }
|
|
|
+ boneList.push( object );
|
|
|
|
|
|
}
|
|
|
|
|
|
- function createVirtualLight( light, cascade ) {
|
|
|
-
|
|
|
- var virtualLight = new THREE.DirectionalLight();
|
|
|
+ for ( var i = 0; i < object.children.length; i ++ ) {
|
|
|
|
|
|
- virtualLight.isVirtual = true;
|
|
|
+ boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) );
|
|
|
|
|
|
- virtualLight.onlyShadow = true;
|
|
|
- virtualLight.castShadow = true;
|
|
|
+ }
|
|
|
|
|
|
- virtualLight.shadowCameraNear = light.shadowCameraNear;
|
|
|
- virtualLight.shadowCameraFar = light.shadowCameraFar;
|
|
|
+ return boneList;
|
|
|
|
|
|
- virtualLight.shadowCameraLeft = light.shadowCameraLeft;
|
|
|
- virtualLight.shadowCameraRight = light.shadowCameraRight;
|
|
|
- virtualLight.shadowCameraBottom = light.shadowCameraBottom;
|
|
|
- virtualLight.shadowCameraTop = light.shadowCameraTop;
|
|
|
+};
|
|
|
|
|
|
- virtualLight.shadowCameraVisible = light.shadowCameraVisible;
|
|
|
+THREE.SkeletonHelper.prototype.update = function () {
|
|
|
|
|
|
- virtualLight.shadowDarkness = light.shadowDarkness;
|
|
|
+ var geometry = this.geometry;
|
|
|
|
|
|
- virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
|
|
|
- virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
|
|
|
- virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
|
|
|
+ var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld );
|
|
|
|
|
|
- virtualLight.pointsWorld = [];
|
|
|
- virtualLight.pointsFrustum = [];
|
|
|
+ var boneMatrix = new THREE.Matrix4();
|
|
|
|
|
|
- var pointsWorld = virtualLight.pointsWorld,
|
|
|
- pointsFrustum = virtualLight.pointsFrustum;
|
|
|
+ var j = 0;
|
|
|
|
|
|
- for ( var i = 0; i < 8; i ++ ) {
|
|
|
+ for ( var i = 0; i < this.bones.length; i ++ ) {
|
|
|
|
|
|
- pointsWorld[ i ] = new THREE.Vector3();
|
|
|
- pointsFrustum[ i ] = new THREE.Vector3();
|
|
|
+ var bone = this.bones[ i ];
|
|
|
|
|
|
- }
|
|
|
+ if ( bone.parent instanceof THREE.Bone ) {
|
|
|
|
|
|
- var nearZ = light.shadowCascadeNearZ[ cascade ];
|
|
|
- var farZ = light.shadowCascadeFarZ[ cascade ];
|
|
|
+ boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
|
|
|
+ geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );
|
|
|
|
|
|
- pointsFrustum[ 0 ].set( - 1, - 1, nearZ );
|
|
|
- pointsFrustum[ 1 ].set( 1, - 1, nearZ );
|
|
|
- pointsFrustum[ 2 ].set( - 1, 1, nearZ );
|
|
|
- pointsFrustum[ 3 ].set( 1, 1, nearZ );
|
|
|
+ boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
|
|
|
+ geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix );
|
|
|
|
|
|
- pointsFrustum[ 4 ].set( - 1, - 1, farZ );
|
|
|
- pointsFrustum[ 5 ].set( 1, - 1, farZ );
|
|
|
- pointsFrustum[ 6 ].set( - 1, 1, farZ );
|
|
|
- pointsFrustum[ 7 ].set( 1, 1, farZ );
|
|
|
+ j += 2;
|
|
|
|
|
|
- return virtualLight;
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- // Synchronize virtual light with the original light
|
|
|
+ geometry.verticesNeedUpdate = true;
|
|
|
|
|
|
- function updateVirtualLight( light, cascade ) {
|
|
|
+ geometry.computeBoundingSphere();
|
|
|
|
|
|
- var virtualLight = light.shadowCascadeArray[ cascade ];
|
|
|
+};
|
|
|
|
|
|
- virtualLight.position.copy( light.position );
|
|
|
- virtualLight.target.position.copy( light.target.position );
|
|
|
- virtualLight.lookAt( virtualLight.target );
|
|
|
+// File:src/extras/helpers/SpotLightHelper.js
|
|
|
|
|
|
- virtualLight.shadowCameraVisible = light.shadowCameraVisible;
|
|
|
- virtualLight.shadowDarkness = light.shadowDarkness;
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
+*/
|
|
|
|
|
|
- virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
|
|
|
+THREE.SpotLightHelper = function ( light ) {
|
|
|
|
|
|
- var nearZ = light.shadowCascadeNearZ[ cascade ];
|
|
|
- var farZ = light.shadowCascadeFarZ[ cascade ];
|
|
|
+ THREE.Object3D.call( this );
|
|
|
|
|
|
- var pointsFrustum = virtualLight.pointsFrustum;
|
|
|
+ this.light = light;
|
|
|
+ this.light.updateMatrixWorld();
|
|
|
|
|
|
- pointsFrustum[ 0 ].z = nearZ;
|
|
|
- pointsFrustum[ 1 ].z = nearZ;
|
|
|
- pointsFrustum[ 2 ].z = nearZ;
|
|
|
- pointsFrustum[ 3 ].z = nearZ;
|
|
|
+ this.matrix = light.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- pointsFrustum[ 4 ].z = farZ;
|
|
|
- pointsFrustum[ 5 ].z = farZ;
|
|
|
- pointsFrustum[ 6 ].z = farZ;
|
|
|
- pointsFrustum[ 7 ].z = farZ;
|
|
|
+ var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true );
|
|
|
+
|
|
|
+ geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) );
|
|
|
+ geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
|
|
|
|
|
|
- }
|
|
|
+ var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
|
|
|
|
|
|
- // Fit shadow camera's ortho frustum to camera frustum
|
|
|
+ this.cone = new THREE.Mesh( geometry, material );
|
|
|
+ this.add( this.cone );
|
|
|
|
|
|
- function updateShadowCamera( camera, light ) {
|
|
|
+ this.update();
|
|
|
|
|
|
- var shadowCamera = light.shadowCamera,
|
|
|
- pointsFrustum = light.pointsFrustum,
|
|
|
- pointsWorld = light.pointsWorld;
|
|
|
+};
|
|
|
|
|
|
- _min.set( Infinity, Infinity, Infinity );
|
|
|
- _max.set( - Infinity, - Infinity, - Infinity );
|
|
|
+THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
- for ( var i = 0; i < 8; i ++ ) {
|
|
|
+THREE.SpotLightHelper.prototype.dispose = function () {
|
|
|
+ this.cone.geometry.dispose();
|
|
|
+ this.cone.material.dispose();
|
|
|
+};
|
|
|
|
|
|
- var p = pointsWorld[ i ];
|
|
|
+THREE.SpotLightHelper.prototype.update = function () {
|
|
|
|
|
|
- p.copy( pointsFrustum[ i ] );
|
|
|
- p.unproject( camera );
|
|
|
+ var vector = new THREE.Vector3();
|
|
|
+ var vector2 = new THREE.Vector3();
|
|
|
|
|
|
- p.applyMatrix4( shadowCamera.matrixWorldInverse );
|
|
|
+ return function () {
|
|
|
|
|
|
- if ( p.x < _min.x ) _min.x = p.x;
|
|
|
- if ( p.x > _max.x ) _max.x = p.x;
|
|
|
+ var coneLength = this.light.distance ? this.light.distance : 10000;
|
|
|
+ var coneWidth = coneLength * Math.tan( this.light.angle );
|
|
|
|
|
|
- if ( p.y < _min.y ) _min.y = p.y;
|
|
|
- if ( p.y > _max.y ) _max.y = p.y;
|
|
|
+ this.cone.scale.set( coneWidth, coneWidth, coneLength );
|
|
|
|
|
|
- if ( p.z < _min.z ) _min.z = p.z;
|
|
|
- if ( p.z > _max.z ) _max.z = p.z;
|
|
|
+ vector.setFromMatrixPosition( this.light.matrixWorld );
|
|
|
+ vector2.setFromMatrixPosition( this.light.target.matrixWorld );
|
|
|
|
|
|
- }
|
|
|
+ this.cone.lookAt( vector2.sub( vector ) );
|
|
|
|
|
|
- shadowCamera.left = _min.x;
|
|
|
- shadowCamera.right = _max.x;
|
|
|
- shadowCamera.top = _max.y;
|
|
|
- shadowCamera.bottom = _min.y;
|
|
|
+ this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
|
|
|
|
|
- // can't really fit near/far
|
|
|
- //shadowCamera.near = _min.z;
|
|
|
- //shadowCamera.far = _max.z;
|
|
|
+ };
|
|
|
|
|
|
- shadowCamera.updateProjectionMatrix();
|
|
|
+}();
|
|
|
|
|
|
- }
|
|
|
+// File:src/extras/helpers/VertexNormalsHelper.js
|
|
|
|
|
|
- // For the moment just ignore objects that have multiple materials with different animation methods
|
|
|
- // Only the first material will be taken into account for deciding which depth material to use for shadow maps
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
+*/
|
|
|
|
|
|
- function getObjectMaterial( object ) {
|
|
|
+THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {
|
|
|
|
|
|
- return object.material instanceof THREE.MeshFaceMaterial
|
|
|
- ? object.material.materials[ 0 ]
|
|
|
- : object.material;
|
|
|
+ this.object = object;
|
|
|
|
|
|
- };
|
|
|
+ this.size = ( size !== undefined ) ? size : 1;
|
|
|
|
|
|
-};
|
|
|
+ var color = ( hex !== undefined ) ? hex : 0xff0000;
|
|
|
|
|
|
-// File:src/extras/renderers/plugins/SpritePlugin.js
|
|
|
+ var width = ( linewidth !== undefined ) ? linewidth : 1;
|
|
|
|
|
|
-/**
|
|
|
- * @author mikael emtinger / http://gomo.se/
|
|
|
- * @author alteredq / http://alteredqualia.com/
|
|
|
- */
|
|
|
+ var geometry = new THREE.Geometry();
|
|
|
|
|
|
-THREE.SpritePlugin = function ( renderer ) {
|
|
|
+ var vertices = object.geometry.vertices;
|
|
|
|
|
|
- var gl = renderer.context;
|
|
|
+ var faces = object.geometry.faces;
|
|
|
|
|
|
- var sprites = [];
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- var vertexBuffer, elementBuffer;
|
|
|
- var program, attributes, uniforms;
|
|
|
+ var face = faces[ i ];
|
|
|
|
|
|
- var texture;
|
|
|
-
|
|
|
- var init = function () {
|
|
|
+ for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
|
|
|
|
|
|
- var vertices = new Float32Array( [
|
|
|
- - 0.5, - 0.5, 0, 0,
|
|
|
- 0.5, - 0.5, 1, 0,
|
|
|
- 0.5, 0.5, 1, 1,
|
|
|
- - 0.5, 0.5, 0, 1
|
|
|
- ] );
|
|
|
+ geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() );
|
|
|
|
|
|
- var faces = new Uint16Array( [
|
|
|
- 0, 1, 2,
|
|
|
- 0, 2, 3
|
|
|
- ] );
|
|
|
+ }
|
|
|
|
|
|
- vertexBuffer = gl.createBuffer();
|
|
|
- elementBuffer = gl.createBuffer();
|
|
|
+ }
|
|
|
|
|
|
- gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
|
|
|
- gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );
|
|
|
+ THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
|
|
|
|
|
|
- gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
|
|
|
- gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- program = createProgram();
|
|
|
+ this.normalMatrix = new THREE.Matrix3();
|
|
|
|
|
|
- attributes = {
|
|
|
- position: gl.getAttribLocation ( program, 'position' ),
|
|
|
- uv: gl.getAttribLocation ( program, 'uv' )
|
|
|
- };
|
|
|
+ this.update();
|
|
|
|
|
|
- uniforms = {
|
|
|
- uvOffset: gl.getUniformLocation( program, 'uvOffset' ),
|
|
|
- uvScale: gl.getUniformLocation( program, 'uvScale' ),
|
|
|
+};
|
|
|
|
|
|
- rotation: gl.getUniformLocation( program, 'rotation' ),
|
|
|
- scale: gl.getUniformLocation( program, 'scale' ),
|
|
|
+THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- color: gl.getUniformLocation( program, 'color' ),
|
|
|
- map: gl.getUniformLocation( program, 'map' ),
|
|
|
- opacity: gl.getUniformLocation( program, 'opacity' ),
|
|
|
+THREE.VertexNormalsHelper.prototype.update = ( function ( object ) {
|
|
|
|
|
|
- modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ),
|
|
|
- projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ),
|
|
|
+ var v1 = new THREE.Vector3();
|
|
|
|
|
|
- fogType: gl.getUniformLocation( program, 'fogType' ),
|
|
|
- fogDensity: gl.getUniformLocation( program, 'fogDensity' ),
|
|
|
- fogNear: gl.getUniformLocation( program, 'fogNear' ),
|
|
|
- fogFar: gl.getUniformLocation( program, 'fogFar' ),
|
|
|
- fogColor: gl.getUniformLocation( program, 'fogColor' ),
|
|
|
+ return function( object ) {
|
|
|
|
|
|
- alphaTest: gl.getUniformLocation( program, 'alphaTest' )
|
|
|
- };
|
|
|
+ var keys = [ 'a', 'b', 'c', 'd' ];
|
|
|
|
|
|
- var canvas = document.createElement( 'canvas' );
|
|
|
- canvas.width = 8;
|
|
|
- canvas.height = 8;
|
|
|
+ this.object.updateMatrixWorld( true );
|
|
|
|
|
|
- var context = canvas.getContext( '2d' );
|
|
|
- context.fillStyle = 'white';
|
|
|
- context.fillRect( 0, 0, 8, 8 );
|
|
|
+ this.normalMatrix.getNormalMatrix( this.object.matrixWorld );
|
|
|
|
|
|
- texture = new THREE.Texture( canvas );
|
|
|
- texture.needsUpdate = true;
|
|
|
+ var vertices = this.geometry.vertices;
|
|
|
|
|
|
- };
|
|
|
+ var verts = this.object.geometry.vertices;
|
|
|
|
|
|
- this.init = function () {}; // TODO: Remove
|
|
|
+ var faces = this.object.geometry.faces;
|
|
|
|
|
|
- this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
|
|
|
+ var worldMatrix = this.object.matrixWorld;
|
|
|
|
|
|
- sprites.length = 0;
|
|
|
+ var idx = 0;
|
|
|
|
|
|
- scene.traverseVisible( function ( child ) {
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( child instanceof THREE.Sprite ) {
|
|
|
+ var face = faces[ i ];
|
|
|
|
|
|
- sprites.push( child );
|
|
|
+ for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ var vertexId = face[ keys[ j ] ];
|
|
|
+ var vertex = verts[ vertexId ];
|
|
|
|
|
|
- } );
|
|
|
+ var normal = face.vertexNormals[ j ];
|
|
|
|
|
|
- if ( sprites.length === 0 ) return;
|
|
|
+ vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
|
|
|
|
|
|
- // setup gl
|
|
|
+ v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size );
|
|
|
|
|
|
- if ( program === undefined ) {
|
|
|
+ v1.add( vertices[ idx ] );
|
|
|
+ idx = idx + 1;
|
|
|
|
|
|
- init();
|
|
|
+ vertices[ idx ].copy( v1 );
|
|
|
+ idx = idx + 1;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- gl.useProgram( program );
|
|
|
+ }
|
|
|
|
|
|
- gl.enableVertexAttribArray( attributes.position );
|
|
|
- gl.enableVertexAttribArray( attributes.uv );
|
|
|
+ this.geometry.verticesNeedUpdate = true;
|
|
|
|
|
|
- gl.disable( gl.CULL_FACE );
|
|
|
- gl.enable( gl.BLEND );
|
|
|
+ return this;
|
|
|
|
|
|
- gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
|
|
|
- gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );
|
|
|
- gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );
|
|
|
+ }
|
|
|
|
|
|
- gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
|
|
|
+}());
|
|
|
|
|
|
- gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
|
|
|
+// File:src/extras/helpers/VertexTangentsHelper.js
|
|
|
|
|
|
- gl.activeTexture( gl.TEXTURE0 );
|
|
|
- gl.uniform1i( uniforms.map, 0 );
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ * @author WestLangley / http://github.com/WestLangley
|
|
|
+*/
|
|
|
|
|
|
- var oldFogType = 0;
|
|
|
- var sceneFogType = 0;
|
|
|
- var fog = scene.fog;
|
|
|
+THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) {
|
|
|
|
|
|
- if ( fog ) {
|
|
|
+ this.object = object;
|
|
|
|
|
|
- gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );
|
|
|
+ this.size = ( size !== undefined ) ? size : 1;
|
|
|
|
|
|
- if ( fog instanceof THREE.Fog ) {
|
|
|
+ var color = ( hex !== undefined ) ? hex : 0x0000ff;
|
|
|
|
|
|
- gl.uniform1f( uniforms.fogNear, fog.near );
|
|
|
- gl.uniform1f( uniforms.fogFar, fog.far );
|
|
|
+ var width = ( linewidth !== undefined ) ? linewidth : 1;
|
|
|
|
|
|
- gl.uniform1i( uniforms.fogType, 1 );
|
|
|
- oldFogType = 1;
|
|
|
- sceneFogType = 1;
|
|
|
+ var geometry = new THREE.Geometry();
|
|
|
|
|
|
- } else if ( fog instanceof THREE.FogExp2 ) {
|
|
|
+ var vertices = object.geometry.vertices;
|
|
|
|
|
|
- gl.uniform1f( uniforms.fogDensity, fog.density );
|
|
|
+ var faces = object.geometry.faces;
|
|
|
|
|
|
- gl.uniform1i( uniforms.fogType, 2 );
|
|
|
- oldFogType = 2;
|
|
|
- sceneFogType = 2;
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ var face = faces[ i ];
|
|
|
|
|
|
- } else {
|
|
|
+ for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
|
|
|
|
|
|
- gl.uniform1i( uniforms.fogType, 0 );
|
|
|
- oldFogType = 0;
|
|
|
- sceneFogType = 0;
|
|
|
+ geometry.vertices.push( new THREE.Vector3() );
|
|
|
+ geometry.vertices.push( new THREE.Vector3() );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // update positions and sort
|
|
|
+ THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces );
|
|
|
|
|
|
- for ( var i = 0, l = sprites.length; i < l; i ++ ) {
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
- var sprite = sprites[ i ];
|
|
|
+ this.update();
|
|
|
|
|
|
- sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
|
|
|
+};
|
|
|
|
|
|
- if ( sprite.renderDepth === null ) {
|
|
|
+THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
|
|
|
- sprite.z = - sprite._modelViewMatrix.elements[ 14 ];
|
|
|
+THREE.VertexTangentsHelper.prototype.update = ( function ( object ) {
|
|
|
|
|
|
- } else {
|
|
|
+ var v1 = new THREE.Vector3();
|
|
|
|
|
|
- sprite.z = sprite.renderDepth;
|
|
|
+ return function( object ) {
|
|
|
+
|
|
|
+ var keys = [ 'a', 'b', 'c', 'd' ];
|
|
|
+
|
|
|
+ this.object.updateMatrixWorld( true );
|
|
|
+
|
|
|
+ var vertices = this.geometry.vertices;
|
|
|
+
|
|
|
+ var verts = this.object.geometry.vertices;
|
|
|
|
|
|
- }
|
|
|
+ var faces = this.object.geometry.faces;
|
|
|
|
|
|
- }
|
|
|
+ var worldMatrix = this.object.matrixWorld;
|
|
|
|
|
|
- sprites.sort( painterSortStable );
|
|
|
+ var idx = 0;
|
|
|
|
|
|
- // render all sprites
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- var scale = [];
|
|
|
+ var face = faces[ i ];
|
|
|
|
|
|
- for ( var i = 0, l = sprites.length; i < l; i ++ ) {
|
|
|
+ for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) {
|
|
|
|
|
|
- var sprite = sprites[ i ];
|
|
|
- var material = sprite.material;
|
|
|
+ var vertexId = face[ keys[ j ] ];
|
|
|
+ var vertex = verts[ vertexId ];
|
|
|
|
|
|
- gl.uniform1f( uniforms.alphaTest, material.alphaTest );
|
|
|
- gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
|
|
|
+ var tangent = face.vertexTangents[ j ];
|
|
|
|
|
|
- scale[ 0 ] = sprite.scale.x;
|
|
|
- scale[ 1 ] = sprite.scale.y;
|
|
|
+ vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix );
|
|
|
|
|
|
- var fogType = 0;
|
|
|
+ v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size );
|
|
|
|
|
|
- if ( scene.fog && material.fog ) {
|
|
|
+ v1.add( vertices[ idx ] );
|
|
|
+ idx = idx + 1;
|
|
|
|
|
|
- fogType = sceneFogType;
|
|
|
+ vertices[ idx ].copy( v1 );
|
|
|
+ idx = idx + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( oldFogType !== fogType ) {
|
|
|
+ }
|
|
|
|
|
|
- gl.uniform1i( uniforms.fogType, fogType );
|
|
|
- oldFogType = fogType;
|
|
|
+ this.geometry.verticesNeedUpdate = true;
|
|
|
|
|
|
- }
|
|
|
+ return this;
|
|
|
|
|
|
- if ( material.map !== null ) {
|
|
|
+ }
|
|
|
|
|
|
- gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );
|
|
|
- gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );
|
|
|
+}());
|
|
|
|
|
|
- } else {
|
|
|
+// File:src/extras/helpers/WireframeHelper.js
|
|
|
|
|
|
- gl.uniform2f( uniforms.uvOffset, 0, 0 );
|
|
|
- gl.uniform2f( uniforms.uvScale, 1, 1 );
|
|
|
+/**
|
|
|
+ * @author mrdoob / http://mrdoob.com/
|
|
|
+ */
|
|
|
|
|
|
- }
|
|
|
+THREE.WireframeHelper = function ( object, hex ) {
|
|
|
|
|
|
- gl.uniform1f( uniforms.opacity, material.opacity );
|
|
|
- gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
|
|
|
+ var color = ( hex !== undefined ) ? hex : 0xffffff;
|
|
|
|
|
|
- gl.uniform1f( uniforms.rotation, material.rotation );
|
|
|
- gl.uniform2fv( uniforms.scale, scale );
|
|
|
+ var edge = [ 0, 0 ], hash = {};
|
|
|
+ var sortFunction = function ( a, b ) { return a - b };
|
|
|
|
|
|
- renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
|
|
|
- renderer.setDepthTest( material.depthTest );
|
|
|
- renderer.setDepthWrite( material.depthWrite );
|
|
|
+ var keys = [ 'a', 'b', 'c' ];
|
|
|
+ var geometry = new THREE.BufferGeometry();
|
|
|
|
|
|
- if ( material.map && material.map.image && material.map.image.width ) {
|
|
|
+ if ( object.geometry instanceof THREE.Geometry ) {
|
|
|
|
|
|
- renderer.setTexture( material.map, 0 );
|
|
|
+ var vertices = object.geometry.vertices;
|
|
|
+ var faces = object.geometry.faces;
|
|
|
+ var numEdges = 0;
|
|
|
|
|
|
- } else {
|
|
|
+ // allocate maximal size
|
|
|
+ var edges = new Uint32Array( 6 * faces.length );
|
|
|
|
|
|
- renderer.setTexture( texture, 0 );
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ var face = faces[ i ];
|
|
|
|
|
|
- gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
|
|
|
+ for ( var j = 0; j < 3; j ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ edge[ 0 ] = face[ keys[ j ] ];
|
|
|
+ edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
|
|
|
+ edge.sort( sortFunction );
|
|
|
|
|
|
- // restore gl
|
|
|
+ var key = edge.toString();
|
|
|
|
|
|
- gl.enable( gl.CULL_FACE );
|
|
|
-
|
|
|
- renderer.resetGLState();
|
|
|
+ if ( hash[ key ] === undefined ) {
|
|
|
|
|
|
- };
|
|
|
+ edges[ 2 * numEdges ] = edge[ 0 ];
|
|
|
+ edges[ 2 * numEdges + 1 ] = edge[ 1 ];
|
|
|
+ hash[ key ] = true;
|
|
|
+ numEdges ++;
|
|
|
|
|
|
- function createProgram () {
|
|
|
+ }
|
|
|
|
|
|
- var program = gl.createProgram();
|
|
|
+ }
|
|
|
|
|
|
- var vertexShader = gl.createShader( gl.VERTEX_SHADER );
|
|
|
- var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
|
|
|
+ }
|
|
|
|
|
|
- gl.shaderSource( vertexShader, [
|
|
|
+ var coords = new Float32Array( numEdges * 2 * 3 );
|
|
|
|
|
|
- 'precision ' + renderer.getPrecision() + ' float;',
|
|
|
+ for ( var i = 0, l = numEdges; i < l; i ++ ) {
|
|
|
|
|
|
- 'uniform mat4 modelViewMatrix;',
|
|
|
- 'uniform mat4 projectionMatrix;',
|
|
|
- 'uniform float rotation;',
|
|
|
- 'uniform vec2 scale;',
|
|
|
- 'uniform vec2 uvOffset;',
|
|
|
- 'uniform vec2 uvScale;',
|
|
|
+ for ( var j = 0; j < 2; j ++ ) {
|
|
|
|
|
|
- 'attribute vec2 position;',
|
|
|
- 'attribute vec2 uv;',
|
|
|
+ var vertex = vertices[ edges [ 2 * i + j] ];
|
|
|
|
|
|
- 'varying vec2 vUV;',
|
|
|
+ var index = 6 * i + 3 * j;
|
|
|
+ coords[ index + 0 ] = vertex.x;
|
|
|
+ coords[ index + 1 ] = vertex.y;
|
|
|
+ coords[ index + 2 ] = vertex.z;
|
|
|
|
|
|
- 'void main() {',
|
|
|
+ }
|
|
|
|
|
|
- 'vUV = uvOffset + uv * uvScale;',
|
|
|
+ }
|
|
|
|
|
|
- 'vec2 alignedPosition = position * scale;',
|
|
|
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
|
|
|
|
|
|
- 'vec2 rotatedPosition;',
|
|
|
- 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
|
|
|
- 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
|
|
|
+ } else if ( object.geometry instanceof THREE.BufferGeometry ) {
|
|
|
|
|
|
- 'vec4 finalPosition;',
|
|
|
+ if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry
|
|
|
|
|
|
- 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
|
|
|
- 'finalPosition.xy += rotatedPosition;',
|
|
|
- 'finalPosition = projectionMatrix * finalPosition;',
|
|
|
+ var vertices = object.geometry.attributes.position.array;
|
|
|
+ var indices = object.geometry.attributes.index.array;
|
|
|
+ var drawcalls = object.geometry.drawcalls;
|
|
|
+ var numEdges = 0;
|
|
|
|
|
|
- 'gl_Position = finalPosition;',
|
|
|
+ if ( drawcalls.length === 0 ) {
|
|
|
|
|
|
- '}'
|
|
|
+ drawcalls = [ { count : indices.length, index : 0, start : 0 } ];
|
|
|
|
|
|
- ].join( '\n' ) );
|
|
|
+ }
|
|
|
|
|
|
- gl.shaderSource( fragmentShader, [
|
|
|
+ // allocate maximal size
|
|
|
+ var edges = new Uint32Array( 2 * indices.length );
|
|
|
|
|
|
- 'precision ' + renderer.getPrecision() + ' float;',
|
|
|
+ for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) {
|
|
|
|
|
|
- 'uniform vec3 color;',
|
|
|
- 'uniform sampler2D map;',
|
|
|
- 'uniform float opacity;',
|
|
|
+ var start = drawcalls[ o ].start;
|
|
|
+ var count = drawcalls[ o ].count;
|
|
|
+ var index = drawcalls[ o ].index;
|
|
|
|
|
|
- 'uniform int fogType;',
|
|
|
- 'uniform vec3 fogColor;',
|
|
|
- 'uniform float fogDensity;',
|
|
|
- 'uniform float fogNear;',
|
|
|
- 'uniform float fogFar;',
|
|
|
- 'uniform float alphaTest;',
|
|
|
+ for ( var i = start, il = start + count; i < il; i += 3 ) {
|
|
|
|
|
|
- 'varying vec2 vUV;',
|
|
|
+ for ( var j = 0; j < 3; j ++ ) {
|
|
|
|
|
|
- 'void main() {',
|
|
|
+ edge[ 0 ] = index + indices[ i + j ];
|
|
|
+ edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ];
|
|
|
+ edge.sort( sortFunction );
|
|
|
|
|
|
- 'vec4 texture = texture2D( map, vUV );',
|
|
|
+ var key = edge.toString();
|
|
|
|
|
|
- 'if ( texture.a < alphaTest ) discard;',
|
|
|
+ if ( hash[ key ] === undefined ) {
|
|
|
|
|
|
- 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
|
|
|
+ edges[ 2 * numEdges ] = edge[ 0 ];
|
|
|
+ edges[ 2 * numEdges + 1 ] = edge[ 1 ];
|
|
|
+ hash[ key ] = true;
|
|
|
+ numEdges ++;
|
|
|
|
|
|
- 'if ( fogType > 0 ) {',
|
|
|
+ }
|
|
|
|
|
|
- 'float depth = gl_FragCoord.z / gl_FragCoord.w;',
|
|
|
- 'float fogFactor = 0.0;',
|
|
|
+ }
|
|
|
|
|
|
- 'if ( fogType == 1 ) {',
|
|
|
+ }
|
|
|
|
|
|
- 'fogFactor = smoothstep( fogNear, fogFar, depth );',
|
|
|
+ }
|
|
|
|
|
|
- '} else {',
|
|
|
+ var coords = new Float32Array( numEdges * 2 * 3 );
|
|
|
|
|
|
- 'const float LOG2 = 1.442695;',
|
|
|
- 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
|
|
|
- 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
|
|
|
+ for ( var i = 0, l = numEdges; i < l; i ++ ) {
|
|
|
|
|
|
- '}',
|
|
|
+ for ( var j = 0; j < 2; j ++ ) {
|
|
|
|
|
|
- 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
|
|
|
+ var index = 6 * i + 3 * j;
|
|
|
+ var index2 = 3 * edges[ 2 * i + j];
|
|
|
+ coords[ index + 0 ] = vertices[ index2 ];
|
|
|
+ coords[ index + 1 ] = vertices[ index2 + 1 ];
|
|
|
+ coords[ index + 2 ] = vertices[ index2 + 2 ];
|
|
|
|
|
|
- '}',
|
|
|
+ }
|
|
|
|
|
|
- '}'
|
|
|
+ }
|
|
|
|
|
|
- ].join( '\n' ) );
|
|
|
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
|
|
|
|
|
|
- gl.compileShader( vertexShader );
|
|
|
- gl.compileShader( fragmentShader );
|
|
|
+ } else { // non-indexed BufferGeometry
|
|
|
|
|
|
- gl.attachShader( program, vertexShader );
|
|
|
- gl.attachShader( program, fragmentShader );
|
|
|
+ var vertices = object.geometry.attributes.position.array;
|
|
|
+ var numEdges = vertices.length / 3;
|
|
|
+ var numTris = numEdges / 3;
|
|
|
|
|
|
- gl.linkProgram( program );
|
|
|
+ var coords = new Float32Array( numEdges * 2 * 3 );
|
|
|
|
|
|
- return program;
|
|
|
+ for ( var i = 0, l = numTris; i < l; i ++ ) {
|
|
|
|
|
|
- };
|
|
|
+ for ( var j = 0; j < 3; j ++ ) {
|
|
|
|
|
|
- function painterSortStable ( a, b ) {
|
|
|
+ var index = 18 * i + 6 * j;
|
|
|
|
|
|
- if ( a.z !== b.z ) {
|
|
|
+ var index1 = 9 * i + 3 * j;
|
|
|
+ coords[ index + 0 ] = vertices[ index1 ];
|
|
|
+ coords[ index + 1 ] = vertices[ index1 + 1 ];
|
|
|
+ coords[ index + 2 ] = vertices[ index1 + 2 ];
|
|
|
|
|
|
- return b.z - a.z;
|
|
|
+ var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 );
|
|
|
+ coords[ index + 3 ] = vertices[ index2 ];
|
|
|
+ coords[ index + 4 ] = vertices[ index2 + 1 ];
|
|
|
+ coords[ index + 5 ] = vertices[ index2 + 2 ];
|
|
|
|
|
|
- } else {
|
|
|
+ }
|
|
|
|
|
|
- return b.id - a.id;
|
|
|
+ }
|
|
|
+
|
|
|
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- };
|
|
|
+ }
|
|
|
+
|
|
|
+ THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces );
|
|
|
+
|
|
|
+ this.matrix = object.matrixWorld;
|
|
|
+ this.matrixAutoUpdate = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
-// File:src/extras/renderers/plugins/DepthPassPlugin.js
|
|
|
+THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype );
|
|
|
+
|
|
|
+// File:src/extras/objects/ImmediateRenderObject.js
|
|
|
|
|
|
/**
|
|
|
* @author alteredq / http://alteredqualia.com/
|
|
|
*/
|
|
|
|
|
|
-THREE.DepthPassPlugin = function () {
|
|
|
-
|
|
|
- this.enabled = false;
|
|
|
- this.renderTarget = null;
|
|
|
-
|
|
|
- var _gl,
|
|
|
- _renderer,
|
|
|
- _lights, _webglObjects, _webglObjectsImmediate,
|
|
|
- _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
|
|
|
-
|
|
|
- _frustum = new THREE.Frustum(),
|
|
|
- _projScreenMatrix = new THREE.Matrix4(),
|
|
|
- _renderList = [];
|
|
|
+THREE.ImmediateRenderObject = function () {
|
|
|
|
|
|
- this.init = function ( renderer, lights, webglObjects, webglObjectsImmediate ) {
|
|
|
+ THREE.Object3D.call( this );
|
|
|
|
|
|
- _gl = renderer.context;
|
|
|
- _renderer = renderer;
|
|
|
- _lights = lights;
|
|
|
+ this.render = function ( renderCallback ) {};
|
|
|
|
|
|
- _webglObjects = webglObjects;
|
|
|
- _webglObjectsImmediate = webglObjectsImmediate;
|
|
|
+};
|
|
|
|
|
|
- var depthShader = THREE.ShaderLib[ "depthRGBA" ];
|
|
|
- var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
|
|
|
+THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
|
|
|
|
|
|
- _depthMaterial = new THREE.ShaderMaterial( {
|
|
|
- fragmentShader: depthShader.fragmentShader,
|
|
|
- vertexShader: depthShader.vertexShader,
|
|
|
- uniforms: depthUniforms
|
|
|
- } );
|
|
|
-
|
|
|
- _depthMaterialMorph = new THREE.ShaderMaterial( {
|
|
|
- fragmentShader: depthShader.fragmentShader,
|
|
|
- vertexShader: depthShader.vertexShader,
|
|
|
- uniforms: depthUniforms,
|
|
|
- morphTargets: true
|
|
|
- } );
|
|
|
-
|
|
|
- _depthMaterialSkin = new THREE.ShaderMaterial( {
|
|
|
- fragmentShader: depthShader.fragmentShader,
|
|
|
- vertexShader: depthShader.vertexShader,
|
|
|
- uniforms: depthUniforms,
|
|
|
- skinning: true
|
|
|
- } );
|
|
|
-
|
|
|
- _depthMaterialMorphSkin = new THREE.ShaderMaterial( {
|
|
|
- fragmentShader: depthShader.fragmentShader,
|
|
|
- vertexShader: depthShader.vertexShader,
|
|
|
- uniforms: depthUniforms,
|
|
|
- morphTargets: true,
|
|
|
- skinning: true
|
|
|
- } );
|
|
|
+// File:src/extras/objects/MorphBlendMesh.js
|
|
|
|
|
|
- _depthMaterial._shadowPass = true;
|
|
|
- _depthMaterialMorph._shadowPass = true;
|
|
|
- _depthMaterialSkin._shadowPass = true;
|
|
|
- _depthMaterialMorphSkin._shadowPass = true;
|
|
|
+/**
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ */
|
|
|
|
|
|
- };
|
|
|
+THREE.MorphBlendMesh = function( geometry, material ) {
|
|
|
|
|
|
- this.render = function ( scene, camera ) {
|
|
|
+ THREE.Mesh.call( this, geometry, material );
|
|
|
|
|
|
- if ( ! this.enabled ) return;
|
|
|
+ this.animationsMap = {};
|
|
|
+ this.animationsList = [];
|
|
|
|
|
|
- this.update( scene, camera );
|
|
|
+ // prepare default animation
|
|
|
+ // (all frames played together in 1 second)
|
|
|
|
|
|
- };
|
|
|
+ var numFrames = this.geometry.morphTargets.length;
|
|
|
|
|
|
- this.update = function ( scene, camera ) {
|
|
|
+ var name = "__default";
|
|
|
|
|
|
- var i, il, j, jl, n,
|
|
|
+ var startFrame = 0;
|
|
|
+ var endFrame = numFrames - 1;
|
|
|
|
|
|
- program, buffer, material,
|
|
|
- webglObject, object, light,
|
|
|
- renderList,
|
|
|
+ var fps = numFrames / 1;
|
|
|
|
|
|
- fog = null;
|
|
|
+ this.createAnimation( name, startFrame, endFrame, fps );
|
|
|
+ this.setAnimationWeight( name, 1 );
|
|
|
|
|
|
- // set GL state for depth map
|
|
|
+};
|
|
|
|
|
|
- _gl.clearColor( 1, 1, 1, 1 );
|
|
|
- _gl.disable( _gl.BLEND );
|
|
|
+THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
|
|
|
|
|
|
- _renderer.setDepthTest( true );
|
|
|
+THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
|
|
|
|
|
|
- // update scene
|
|
|
+ var animation = {
|
|
|
|
|
|
- if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
|
|
|
+ startFrame: start,
|
|
|
+ endFrame: end,
|
|
|
|
|
|
- // update camera matrices and frustum
|
|
|
+ length: end - start + 1,
|
|
|
|
|
|
- camera.matrixWorldInverse.getInverse( camera.matrixWorld );
|
|
|
+ fps: fps,
|
|
|
+ duration: ( end - start ) / fps,
|
|
|
|
|
|
- _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
|
|
|
- _frustum.setFromMatrix( _projScreenMatrix );
|
|
|
+ lastFrame: 0,
|
|
|
+ currentFrame: 0,
|
|
|
|
|
|
- // render depth map
|
|
|
+ active: false,
|
|
|
|
|
|
- _renderer.setRenderTarget( this.renderTarget );
|
|
|
- _renderer.clear();
|
|
|
+ time: 0,
|
|
|
+ direction: 1,
|
|
|
+ weight: 1,
|
|
|
|
|
|
- // set object matrices & frustum culling
|
|
|
-
|
|
|
- _renderList.length = 0;
|
|
|
- projectObject(scene,scene,camera);
|
|
|
+ directionBackwards: false,
|
|
|
+ mirroredLoop: false
|
|
|
|
|
|
- // render regular objects
|
|
|
+ };
|
|
|
|
|
|
- var objectMaterial, useMorphing, useSkinning;
|
|
|
+ this.animationsMap[ name ] = animation;
|
|
|
+ this.animationsList.push( animation );
|
|
|
|
|
|
- for ( j = 0, jl = _renderList.length; j < jl; j ++ ) {
|
|
|
+};
|
|
|
|
|
|
- webglObject = _renderList[ j ];
|
|
|
+THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
|
|
|
|
|
|
- object = webglObject.object;
|
|
|
- buffer = webglObject.buffer;
|
|
|
+ var pattern = /([a-z]+)_?(\d+)/;
|
|
|
|
|
|
- // todo: create proper depth material for particles
|
|
|
+ var firstAnimation, frameRanges = {};
|
|
|
|
|
|
- if ( object instanceof THREE.PointCloud && ! object.customDepthMaterial ) continue;
|
|
|
+ var geometry = this.geometry;
|
|
|
|
|
|
- objectMaterial = getObjectMaterial( object );
|
|
|
+ for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
|
|
|
|
|
|
- if ( objectMaterial ) _renderer.setMaterialFaces( object.material );
|
|
|
+ var morph = geometry.morphTargets[ i ];
|
|
|
+ var chunks = morph.name.match( pattern );
|
|
|
|
|
|
- useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
|
|
|
- useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
|
|
|
+ if ( chunks && chunks.length > 1 ) {
|
|
|
|
|
|
- if ( object.customDepthMaterial ) {
|
|
|
+ var name = chunks[ 1 ];
|
|
|
+ var num = chunks[ 2 ];
|
|
|
|
|
|
- material = object.customDepthMaterial;
|
|
|
+ if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };
|
|
|
|
|
|
- } else if ( useSkinning ) {
|
|
|
+ var range = frameRanges[ name ];
|
|
|
|
|
|
- material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
|
|
|
+ if ( i < range.start ) range.start = i;
|
|
|
+ if ( i > range.end ) range.end = i;
|
|
|
|
|
|
- } else if ( useMorphing ) {
|
|
|
+ if ( ! firstAnimation ) firstAnimation = name;
|
|
|
|
|
|
- material = _depthMaterialMorph;
|
|
|
+ }
|
|
|
|
|
|
- } else {
|
|
|
+ }
|
|
|
|
|
|
- material = _depthMaterial;
|
|
|
+ for ( var name in frameRanges ) {
|
|
|
|
|
|
- }
|
|
|
+ var range = frameRanges[ name ];
|
|
|
+ this.createAnimation( name, range.start, range.end, fps );
|
|
|
|
|
|
- if ( buffer instanceof THREE.BufferGeometry ) {
|
|
|
+ }
|
|
|
|
|
|
- _renderer.renderBufferDirect( camera, _lights, fog, material, buffer, object );
|
|
|
+ this.firstAnimation = firstAnimation;
|
|
|
|
|
|
- } else {
|
|
|
+};
|
|
|
|
|
|
- _renderer.renderBuffer( camera, _lights, fog, material, buffer, object );
|
|
|
+THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
|
|
|
|
|
|
- }
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- }
|
|
|
+ animation.direction = 1;
|
|
|
+ animation.directionBackwards = false;
|
|
|
|
|
|
- // set matrices and render immediate objects
|
|
|
+ }
|
|
|
|
|
|
- for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) {
|
|
|
+};
|
|
|
|
|
|
- webglObject = _webglObjectsImmediate[ j ];
|
|
|
- object = webglObject.object;
|
|
|
+THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
|
|
|
|
|
|
- if ( object.visible ) {
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
- object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- _renderer.renderImmediateObject( camera, _lights, fog, _depthMaterial, object );
|
|
|
+ animation.direction = - 1;
|
|
|
+ animation.directionBackwards = true;
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+};
|
|
|
|
|
|
- // restore GL state
|
|
|
+THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
|
|
|
|
|
|
- var clearColor = _renderer.getClearColor(),
|
|
|
- clearAlpha = _renderer.getClearAlpha();
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
- _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
|
|
|
- _gl.enable( _gl.BLEND );
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- };
|
|
|
-
|
|
|
- function projectObject(scene, object,camera){
|
|
|
-
|
|
|
- if ( object.visible ) {
|
|
|
-
|
|
|
- var webglObjects = _webglObjects[object.id];
|
|
|
-
|
|
|
- if (webglObjects && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) {
|
|
|
-
|
|
|
-
|
|
|
- for (var i = 0, l = webglObjects.length; i < l; i++){
|
|
|
-
|
|
|
- var webglObject = webglObjects[i];
|
|
|
-
|
|
|
- object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
|
|
|
- _renderList.push(webglObject);
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for(var i = 0, l = object.children.length; i < l; i++) {
|
|
|
-
|
|
|
- projectObject(scene, object.children[i], camera);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
+ animation.fps = fps;
|
|
|
+ animation.duration = ( animation.end - animation.start ) / animation.fps;
|
|
|
|
|
|
- // For the moment just ignore objects that have multiple materials with different animation methods
|
|
|
- // Only the first material will be taken into account for deciding which depth material to use
|
|
|
+ }
|
|
|
|
|
|
- function getObjectMaterial( object ) {
|
|
|
+};
|
|
|
|
|
|
- return object.material instanceof THREE.MeshFaceMaterial
|
|
|
- ? object.material.materials[ 0 ]
|
|
|
- : object.material;
|
|
|
+THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
|
|
|
|
|
|
- };
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
-};
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
+ animation.duration = duration;
|
|
|
+ animation.fps = ( animation.end - animation.start ) / animation.duration;
|
|
|
|
|
|
-// File:src/extras/shaders/ShaderFlares.js
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- * @author mikael emtinger / http://gomo.se/
|
|
|
- */
|
|
|
+};
|
|
|
|
|
|
-THREE.ShaderFlares = {
|
|
|
+THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
|
|
|
|
|
|
- 'lensFlareVertexTexture': {
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
- vertexShader: [
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- "uniform lowp int renderType;",
|
|
|
+ animation.weight = weight;
|
|
|
|
|
|
- "uniform vec3 screenPosition;",
|
|
|
- "uniform vec2 scale;",
|
|
|
- "uniform float rotation;",
|
|
|
+ }
|
|
|
|
|
|
- "uniform sampler2D occlusionMap;",
|
|
|
+};
|
|
|
|
|
|
- "attribute vec2 position;",
|
|
|
- "attribute vec2 uv;",
|
|
|
+THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
|
|
|
|
|
|
- "varying vec2 vUV;",
|
|
|
- "varying float vVisibility;",
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
- "void main() {",
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- "vUV = uv;",
|
|
|
+ animation.time = time;
|
|
|
|
|
|
- "vec2 pos = position;",
|
|
|
+ }
|
|
|
|
|
|
- "if( renderType == 2 ) {",
|
|
|
+};
|
|
|
|
|
|
- "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
|
|
|
+THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
|
|
|
|
|
|
- "vVisibility = visibility.r / 9.0;",
|
|
|
- "vVisibility *= 1.0 - visibility.g / 9.0;",
|
|
|
- "vVisibility *= visibility.b / 9.0;",
|
|
|
- "vVisibility *= 1.0 - visibility.a / 9.0;",
|
|
|
+ var time = 0;
|
|
|
|
|
|
- "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
|
|
|
- "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
- "}",
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
|
|
|
+ time = animation.time;
|
|
|
|
|
|
- "}"
|
|
|
+ }
|
|
|
|
|
|
- ].join( "\n" ),
|
|
|
+ return time;
|
|
|
|
|
|
- fragmentShader: [
|
|
|
+};
|
|
|
|
|
|
- "uniform lowp int renderType;",
|
|
|
+THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
|
|
|
|
|
|
- "uniform sampler2D map;",
|
|
|
- "uniform float opacity;",
|
|
|
- "uniform vec3 color;",
|
|
|
+ var duration = - 1;
|
|
|
|
|
|
- "varying vec2 vUV;",
|
|
|
- "varying float vVisibility;",
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
- "void main() {",
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- // pink square
|
|
|
+ duration = animation.duration;
|
|
|
|
|
|
- "if( renderType == 0 ) {",
|
|
|
+ }
|
|
|
|
|
|
- "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
|
|
|
+ return duration;
|
|
|
|
|
|
- // restore
|
|
|
+};
|
|
|
|
|
|
- "} else if( renderType == 1 ) {",
|
|
|
+THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
|
|
|
|
|
|
- "gl_FragColor = texture2D( map, vUV );",
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
- // flare
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- "} else {",
|
|
|
+ animation.time = 0;
|
|
|
+ animation.active = true;
|
|
|
|
|
|
- "vec4 texture = texture2D( map, vUV );",
|
|
|
- "texture.a *= opacity * vVisibility;",
|
|
|
- "gl_FragColor = texture;",
|
|
|
- "gl_FragColor.rgb *= color;",
|
|
|
+ } else {
|
|
|
|
|
|
- "}",
|
|
|
+ console.warn( "animation[" + name + "] undefined" );
|
|
|
|
|
|
- "}"
|
|
|
- ].join( "\n" )
|
|
|
+ }
|
|
|
|
|
|
- },
|
|
|
+};
|
|
|
|
|
|
+THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
|
|
|
|
|
|
- 'lensFlare': {
|
|
|
+ var animation = this.animationsMap[ name ];
|
|
|
|
|
|
- vertexShader: [
|
|
|
+ if ( animation ) {
|
|
|
|
|
|
- "uniform lowp int renderType;",
|
|
|
+ animation.active = false;
|
|
|
|
|
|
- "uniform vec3 screenPosition;",
|
|
|
- "uniform vec2 scale;",
|
|
|
- "uniform float rotation;",
|
|
|
+ }
|
|
|
|
|
|
- "attribute vec2 position;",
|
|
|
- "attribute vec2 uv;",
|
|
|
+};
|
|
|
|
|
|
- "varying vec2 vUV;",
|
|
|
+THREE.MorphBlendMesh.prototype.update = function ( delta ) {
|
|
|
|
|
|
- "void main() {",
|
|
|
+ for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
|
|
|
|
|
|
- "vUV = uv;",
|
|
|
+ var animation = this.animationsList[ i ];
|
|
|
|
|
|
- "vec2 pos = position;",
|
|
|
+ if ( ! animation.active ) continue;
|
|
|
|
|
|
- "if( renderType == 2 ) {",
|
|
|
+ var frameTime = animation.duration / animation.length;
|
|
|
|
|
|
- "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
|
|
|
- "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
|
|
|
+ animation.time += animation.direction * delta;
|
|
|
|
|
|
- "}",
|
|
|
+ if ( animation.mirroredLoop ) {
|
|
|
|
|
|
- "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
|
|
|
+ if ( animation.time > animation.duration || animation.time < 0 ) {
|
|
|
|
|
|
- "}"
|
|
|
+ animation.direction *= - 1;
|
|
|
|
|
|
- ].join( "\n" ),
|
|
|
+ if ( animation.time > animation.duration ) {
|
|
|
|
|
|
- fragmentShader: [
|
|
|
+ animation.time = animation.duration;
|
|
|
+ animation.directionBackwards = true;
|
|
|
|
|
|
- "precision mediump float;",
|
|
|
+ }
|
|
|
|
|
|
- "uniform lowp int renderType;",
|
|
|
+ if ( animation.time < 0 ) {
|
|
|
|
|
|
- "uniform sampler2D map;",
|
|
|
- "uniform sampler2D occlusionMap;",
|
|
|
- "uniform float opacity;",
|
|
|
- "uniform vec3 color;",
|
|
|
+ animation.time = 0;
|
|
|
+ animation.directionBackwards = false;
|
|
|
|
|
|
- "varying vec2 vUV;",
|
|
|
+ }
|
|
|
|
|
|
- "void main() {",
|
|
|
+ }
|
|
|
|
|
|
- // pink square
|
|
|
+ } else {
|
|
|
|
|
|
- "if( renderType == 0 ) {",
|
|
|
+ animation.time = animation.time % animation.duration;
|
|
|
|
|
|
- "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
|
|
|
+ if ( animation.time < 0 ) animation.time += animation.duration;
|
|
|
|
|
|
- // restore
|
|
|
+ }
|
|
|
|
|
|
- "} else if( renderType == 1 ) {",
|
|
|
+ var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
|
|
|
+ var weight = animation.weight;
|
|
|
|
|
|
- "gl_FragColor = texture2D( map, vUV );",
|
|
|
+ if ( keyframe !== animation.currentFrame ) {
|
|
|
|
|
|
- // flare
|
|
|
+ this.morphTargetInfluences[ animation.lastFrame ] = 0;
|
|
|
+ this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
|
|
|
|
|
|
- "} else {",
|
|
|
+ this.morphTargetInfluences[ keyframe ] = 0;
|
|
|
|
|
|
- "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;",
|
|
|
- "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
|
|
|
- "visibility = ( 1.0 - visibility / 4.0 );",
|
|
|
+ animation.lastFrame = animation.currentFrame;
|
|
|
+ animation.currentFrame = keyframe;
|
|
|
|
|
|
- "vec4 texture = texture2D( map, vUV );",
|
|
|
- "texture.a *= opacity * visibility;",
|
|
|
- "gl_FragColor = texture;",
|
|
|
- "gl_FragColor.rgb *= color;",
|
|
|
+ }
|
|
|
|
|
|
- "}",
|
|
|
+ var mix = ( animation.time % frameTime ) / frameTime;
|
|
|
|
|
|
- "}"
|
|
|
+ if ( animation.directionBackwards ) mix = 1 - mix;
|
|
|
|
|
|
- ].join( "\n" )
|
|
|
+ this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
|
|
|
+ this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
|
|
|
|
|
|
}
|
|
|
|