Initial commit
This commit is contained in:
485
HTML/ThreeJS/node_modules/three/examples/js/misc/ConvexObjectBreaker.js
generated
vendored
Normal file
485
HTML/ThreeJS/node_modules/three/examples/js/misc/ConvexObjectBreaker.js
generated
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
( function () {
|
||||
|
||||
/**
|
||||
* @fileoverview This class can be used to subdivide a convex Geometry object into pieces.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* Use the function prepareBreakableObject to prepare a THREE.Mesh object to be broken.
|
||||
*
|
||||
* Then, call the various functions to subdivide the object (subdivideByImpact, cutByPlane)
|
||||
*
|
||||
* Sub-objects that are product of subdivision don't need prepareBreakableObject to be called on them.
|
||||
*
|
||||
* Requisites for the object:
|
||||
*
|
||||
* - THREE.Mesh object must have a BufferGeometry (not Geometry) and a Material
|
||||
*
|
||||
* - Vertex normals must be planar (not smoothed)
|
||||
*
|
||||
* - The geometry must be convex (this is not checked in the library). You can create convex
|
||||
* geometries with THREE.ConvexGeometry. The BoxGeometry, SphereGeometry and other convex primitives
|
||||
* can also be used.
|
||||
*
|
||||
* Note: This lib adds member variables to object's userData member (see prepareBreakableObject function)
|
||||
* Use with caution and read the code when using with other libs.
|
||||
*
|
||||
* @param {double} minSizeForBreak Min size a debris can have to break.
|
||||
* @param {double} smallDelta Max distance to consider that a point belongs to a plane.
|
||||
*
|
||||
*/
|
||||
|
||||
const _v1 = new THREE.Vector3();
|
||||
|
||||
class ConvexObjectBreaker {
|
||||
|
||||
constructor( minSizeForBreak = 1.4, smallDelta = 0.0001 ) {
|
||||
|
||||
this.minSizeForBreak = minSizeForBreak;
|
||||
this.smallDelta = smallDelta;
|
||||
this.tempLine1 = new THREE.Line3();
|
||||
this.tempPlane1 = new THREE.Plane();
|
||||
this.tempPlane2 = new THREE.Plane();
|
||||
this.tempPlane_Cut = new THREE.Plane();
|
||||
this.tempCM1 = new THREE.Vector3();
|
||||
this.tempCM2 = new THREE.Vector3();
|
||||
this.tempVector3 = new THREE.Vector3();
|
||||
this.tempVector3_2 = new THREE.Vector3();
|
||||
this.tempVector3_3 = new THREE.Vector3();
|
||||
this.tempVector3_P0 = new THREE.Vector3();
|
||||
this.tempVector3_P1 = new THREE.Vector3();
|
||||
this.tempVector3_P2 = new THREE.Vector3();
|
||||
this.tempVector3_N0 = new THREE.Vector3();
|
||||
this.tempVector3_N1 = new THREE.Vector3();
|
||||
this.tempVector3_AB = new THREE.Vector3();
|
||||
this.tempVector3_CB = new THREE.Vector3();
|
||||
this.tempResultObjects = {
|
||||
object1: null,
|
||||
object2: null
|
||||
};
|
||||
this.segments = [];
|
||||
const n = 30 * 30;
|
||||
|
||||
for ( let i = 0; i < n; i ++ ) this.segments[ i ] = false;
|
||||
|
||||
}
|
||||
|
||||
prepareBreakableObject( object, mass, velocity, angularVelocity, breakable ) {
|
||||
|
||||
// object is a Object3d (normally a THREE.Mesh), must have a BufferGeometry, and it must be convex.
|
||||
// Its material property is propagated to its children (sub-pieces)
|
||||
// mass must be > 0
|
||||
if ( ! object.geometry.isBufferGeometry ) {
|
||||
|
||||
console.error( 'THREE.ConvexObjectBreaker.prepareBreakableObject(): Parameter object must have a BufferGeometry.' );
|
||||
|
||||
}
|
||||
|
||||
const userData = object.userData;
|
||||
userData.mass = mass;
|
||||
userData.velocity = velocity.clone();
|
||||
userData.angularVelocity = angularVelocity.clone();
|
||||
userData.breakable = breakable;
|
||||
|
||||
}
|
||||
/*
|
||||
* @param {int} maxRadialIterations Iterations for radial cuts.
|
||||
* @param {int} maxRandomIterations Max random iterations for not-radial cuts
|
||||
*
|
||||
* Returns the array of pieces
|
||||
*/
|
||||
|
||||
|
||||
subdivideByImpact( object, pointOfImpact, normal, maxRadialIterations, maxRandomIterations ) {
|
||||
|
||||
const debris = [];
|
||||
const tempPlane1 = this.tempPlane1;
|
||||
const tempPlane2 = this.tempPlane2;
|
||||
this.tempVector3.addVectors( pointOfImpact, normal );
|
||||
tempPlane1.setFromCoplanarPoints( pointOfImpact, object.position, this.tempVector3 );
|
||||
const maxTotalIterations = maxRandomIterations + maxRadialIterations;
|
||||
const scope = this;
|
||||
|
||||
function subdivideRadial( subObject, startAngle, endAngle, numIterations ) {
|
||||
|
||||
if ( Math.random() < numIterations * 0.05 || numIterations > maxTotalIterations ) {
|
||||
|
||||
debris.push( subObject );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
let angle = Math.PI;
|
||||
|
||||
if ( numIterations === 0 ) {
|
||||
|
||||
tempPlane2.normal.copy( tempPlane1.normal );
|
||||
tempPlane2.constant = tempPlane1.constant;
|
||||
|
||||
} else {
|
||||
|
||||
if ( numIterations <= maxRadialIterations ) {
|
||||
|
||||
angle = ( endAngle - startAngle ) * ( 0.2 + 0.6 * Math.random() ) + startAngle; // Rotate tempPlane2 at impact point around normal axis and the angle
|
||||
|
||||
scope.tempVector3_2.copy( object.position ).sub( pointOfImpact ).applyAxisAngle( normal, angle ).add( pointOfImpact );
|
||||
tempPlane2.setFromCoplanarPoints( pointOfImpact, scope.tempVector3, scope.tempVector3_2 );
|
||||
|
||||
} else {
|
||||
|
||||
angle = ( 0.5 * ( numIterations & 1 ) + 0.2 * ( 2 - Math.random() ) ) * Math.PI; // Rotate tempPlane2 at object position around normal axis and the angle
|
||||
|
||||
scope.tempVector3_2.copy( pointOfImpact ).sub( subObject.position ).applyAxisAngle( normal, angle ).add( subObject.position );
|
||||
scope.tempVector3_3.copy( normal ).add( subObject.position );
|
||||
tempPlane2.setFromCoplanarPoints( subObject.position, scope.tempVector3_3, scope.tempVector3_2 );
|
||||
|
||||
}
|
||||
|
||||
} // Perform the cut
|
||||
|
||||
|
||||
scope.cutByPlane( subObject, tempPlane2, scope.tempResultObjects );
|
||||
const obj1 = scope.tempResultObjects.object1;
|
||||
const obj2 = scope.tempResultObjects.object2;
|
||||
|
||||
if ( obj1 ) {
|
||||
|
||||
subdivideRadial( obj1, startAngle, angle, numIterations + 1 );
|
||||
|
||||
}
|
||||
|
||||
if ( obj2 ) {
|
||||
|
||||
subdivideRadial( obj2, angle, endAngle, numIterations + 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
subdivideRadial( object, 0, 2 * Math.PI, 0 );
|
||||
return debris;
|
||||
|
||||
}
|
||||
|
||||
cutByPlane( object, plane, output ) {
|
||||
|
||||
// Returns breakable objects in output.object1 and output.object2 members, the resulting 2 pieces of the cut.
|
||||
// object2 can be null if the plane doesn't cut the object.
|
||||
// object1 can be null only in case of internal error
|
||||
// Returned value is number of pieces, 0 for error.
|
||||
const geometry = object.geometry;
|
||||
const coords = geometry.attributes.position.array;
|
||||
const normals = geometry.attributes.normal.array;
|
||||
const numPoints = coords.length / 3;
|
||||
let numFaces = numPoints / 3;
|
||||
let indices = geometry.getIndex();
|
||||
|
||||
if ( indices ) {
|
||||
|
||||
indices = indices.array;
|
||||
numFaces = indices.length / 3;
|
||||
|
||||
}
|
||||
|
||||
function getVertexIndex( faceIdx, vert ) {
|
||||
|
||||
// vert = 0, 1 or 2.
|
||||
const idx = faceIdx * 3 + vert;
|
||||
return indices ? indices[ idx ] : idx;
|
||||
|
||||
}
|
||||
|
||||
const points1 = [];
|
||||
const points2 = [];
|
||||
const delta = this.smallDelta; // Reset segments mark
|
||||
|
||||
const numPointPairs = numPoints * numPoints;
|
||||
|
||||
for ( let i = 0; i < numPointPairs; i ++ ) this.segments[ i ] = false;
|
||||
|
||||
const p0 = this.tempVector3_P0;
|
||||
const p1 = this.tempVector3_P1;
|
||||
const n0 = this.tempVector3_N0;
|
||||
const n1 = this.tempVector3_N1; // Iterate through the faces to mark edges shared by coplanar faces
|
||||
|
||||
for ( let i = 0; i < numFaces - 1; i ++ ) {
|
||||
|
||||
const a1 = getVertexIndex( i, 0 );
|
||||
const b1 = getVertexIndex( i, 1 );
|
||||
const c1 = getVertexIndex( i, 2 ); // Assuming all 3 vertices have the same normal
|
||||
|
||||
n0.set( normals[ a1 ], normals[ a1 ] + 1, normals[ a1 ] + 2 );
|
||||
|
||||
for ( let j = i + 1; j < numFaces; j ++ ) {
|
||||
|
||||
const a2 = getVertexIndex( j, 0 );
|
||||
const b2 = getVertexIndex( j, 1 );
|
||||
const c2 = getVertexIndex( j, 2 ); // Assuming all 3 vertices have the same normal
|
||||
|
||||
n1.set( normals[ a2 ], normals[ a2 ] + 1, normals[ a2 ] + 2 );
|
||||
const coplanar = 1 - n0.dot( n1 ) < delta;
|
||||
|
||||
if ( coplanar ) {
|
||||
|
||||
if ( a1 === a2 || a1 === b2 || a1 === c2 ) {
|
||||
|
||||
if ( b1 === a2 || b1 === b2 || b1 === c2 ) {
|
||||
|
||||
this.segments[ a1 * numPoints + b1 ] = true;
|
||||
this.segments[ b1 * numPoints + a1 ] = true;
|
||||
|
||||
} else {
|
||||
|
||||
this.segments[ c1 * numPoints + a1 ] = true;
|
||||
this.segments[ a1 * numPoints + c1 ] = true;
|
||||
|
||||
}
|
||||
|
||||
} else if ( b1 === a2 || b1 === b2 || b1 === c2 ) {
|
||||
|
||||
this.segments[ c1 * numPoints + b1 ] = true;
|
||||
this.segments[ b1 * numPoints + c1 ] = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // Transform the plane to object local space
|
||||
|
||||
|
||||
const localPlane = this.tempPlane_Cut;
|
||||
object.updateMatrix();
|
||||
ConvexObjectBreaker.transformPlaneToLocalSpace( plane, object.matrix, localPlane ); // Iterate through the faces adding points to both pieces
|
||||
|
||||
for ( let i = 0; i < numFaces; i ++ ) {
|
||||
|
||||
const va = getVertexIndex( i, 0 );
|
||||
const vb = getVertexIndex( i, 1 );
|
||||
const vc = getVertexIndex( i, 2 );
|
||||
|
||||
for ( let segment = 0; segment < 3; segment ++ ) {
|
||||
|
||||
const i0 = segment === 0 ? va : segment === 1 ? vb : vc;
|
||||
const i1 = segment === 0 ? vb : segment === 1 ? vc : va;
|
||||
const segmentState = this.segments[ i0 * numPoints + i1 ];
|
||||
if ( segmentState ) continue; // The segment already has been processed in another face
|
||||
// Mark segment as processed (also inverted segment)
|
||||
|
||||
this.segments[ i0 * numPoints + i1 ] = true;
|
||||
this.segments[ i1 * numPoints + i0 ] = true;
|
||||
p0.set( coords[ 3 * i0 ], coords[ 3 * i0 + 1 ], coords[ 3 * i0 + 2 ] );
|
||||
p1.set( coords[ 3 * i1 ], coords[ 3 * i1 + 1 ], coords[ 3 * i1 + 2 ] ); // mark: 1 for negative side, 2 for positive side, 3 for coplanar point
|
||||
|
||||
let mark0 = 0;
|
||||
let d = localPlane.distanceToPoint( p0 );
|
||||
|
||||
if ( d > delta ) {
|
||||
|
||||
mark0 = 2;
|
||||
points2.push( p0.clone() );
|
||||
|
||||
} else if ( d < - delta ) {
|
||||
|
||||
mark0 = 1;
|
||||
points1.push( p0.clone() );
|
||||
|
||||
} else {
|
||||
|
||||
mark0 = 3;
|
||||
points1.push( p0.clone() );
|
||||
points2.push( p0.clone() );
|
||||
|
||||
} // mark: 1 for negative side, 2 for positive side, 3 for coplanar point
|
||||
|
||||
|
||||
let mark1 = 0;
|
||||
d = localPlane.distanceToPoint( p1 );
|
||||
|
||||
if ( d > delta ) {
|
||||
|
||||
mark1 = 2;
|
||||
points2.push( p1.clone() );
|
||||
|
||||
} else if ( d < - delta ) {
|
||||
|
||||
mark1 = 1;
|
||||
points1.push( p1.clone() );
|
||||
|
||||
} else {
|
||||
|
||||
mark1 = 3;
|
||||
points1.push( p1.clone() );
|
||||
points2.push( p1.clone() );
|
||||
|
||||
}
|
||||
|
||||
if ( mark0 === 1 && mark1 === 2 || mark0 === 2 && mark1 === 1 ) {
|
||||
|
||||
// Intersection of segment with the plane
|
||||
this.tempLine1.start.copy( p0 );
|
||||
this.tempLine1.end.copy( p1 );
|
||||
let intersection = new THREE.Vector3();
|
||||
intersection = localPlane.intersectLine( this.tempLine1, intersection );
|
||||
|
||||
if ( intersection === null ) {
|
||||
|
||||
// Shouldn't happen
|
||||
console.error( 'Internal error: segment does not intersect plane.' );
|
||||
output.segmentedObject1 = null;
|
||||
output.segmentedObject2 = null;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
points1.push( intersection );
|
||||
points2.push( intersection.clone() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // Calculate debris mass (very fast and imprecise):
|
||||
|
||||
|
||||
const newMass = object.userData.mass * 0.5; // Calculate debris Center of Mass (again fast and imprecise)
|
||||
|
||||
this.tempCM1.set( 0, 0, 0 );
|
||||
let radius1 = 0;
|
||||
const numPoints1 = points1.length;
|
||||
|
||||
if ( numPoints1 > 0 ) {
|
||||
|
||||
for ( let i = 0; i < numPoints1; i ++ ) this.tempCM1.add( points1[ i ] );
|
||||
|
||||
this.tempCM1.divideScalar( numPoints1 );
|
||||
|
||||
for ( let i = 0; i < numPoints1; i ++ ) {
|
||||
|
||||
const p = points1[ i ];
|
||||
p.sub( this.tempCM1 );
|
||||
radius1 = Math.max( radius1, p.x, p.y, p.z );
|
||||
|
||||
}
|
||||
|
||||
this.tempCM1.add( object.position );
|
||||
|
||||
}
|
||||
|
||||
this.tempCM2.set( 0, 0, 0 );
|
||||
let radius2 = 0;
|
||||
const numPoints2 = points2.length;
|
||||
|
||||
if ( numPoints2 > 0 ) {
|
||||
|
||||
for ( let i = 0; i < numPoints2; i ++ ) this.tempCM2.add( points2[ i ] );
|
||||
|
||||
this.tempCM2.divideScalar( numPoints2 );
|
||||
|
||||
for ( let i = 0; i < numPoints2; i ++ ) {
|
||||
|
||||
const p = points2[ i ];
|
||||
p.sub( this.tempCM2 );
|
||||
radius2 = Math.max( radius2, p.x, p.y, p.z );
|
||||
|
||||
}
|
||||
|
||||
this.tempCM2.add( object.position );
|
||||
|
||||
}
|
||||
|
||||
let object1 = null;
|
||||
let object2 = null;
|
||||
let numObjects = 0;
|
||||
|
||||
if ( numPoints1 > 4 ) {
|
||||
|
||||
object1 = new THREE.Mesh( new THREE.ConvexGeometry( points1 ), object.material );
|
||||
object1.position.copy( this.tempCM1 );
|
||||
object1.quaternion.copy( object.quaternion );
|
||||
this.prepareBreakableObject( object1, newMass, object.userData.velocity, object.userData.angularVelocity, 2 * radius1 > this.minSizeForBreak );
|
||||
numObjects ++;
|
||||
|
||||
}
|
||||
|
||||
if ( numPoints2 > 4 ) {
|
||||
|
||||
object2 = new THREE.Mesh( new THREE.ConvexGeometry( points2 ), object.material );
|
||||
object2.position.copy( this.tempCM2 );
|
||||
object2.quaternion.copy( object.quaternion );
|
||||
this.prepareBreakableObject( object2, newMass, object.userData.velocity, object.userData.angularVelocity, 2 * radius2 > this.minSizeForBreak );
|
||||
numObjects ++;
|
||||
|
||||
}
|
||||
|
||||
output.object1 = object1;
|
||||
output.object2 = object2;
|
||||
return numObjects;
|
||||
|
||||
}
|
||||
|
||||
static transformFreeVector( v, m ) {
|
||||
|
||||
// input:
|
||||
// vector interpreted as a free vector
|
||||
// THREE.Matrix4 orthogonal matrix (matrix without scale)
|
||||
const x = v.x,
|
||||
y = v.y,
|
||||
z = v.z;
|
||||
const e = m.elements;
|
||||
v.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
|
||||
v.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
|
||||
v.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
static transformFreeVectorInverse( v, m ) {
|
||||
|
||||
// input:
|
||||
// vector interpreted as a free vector
|
||||
// THREE.Matrix4 orthogonal matrix (matrix without scale)
|
||||
const x = v.x,
|
||||
y = v.y,
|
||||
z = v.z;
|
||||
const e = m.elements;
|
||||
v.x = e[ 0 ] * x + e[ 1 ] * y + e[ 2 ] * z;
|
||||
v.y = e[ 4 ] * x + e[ 5 ] * y + e[ 6 ] * z;
|
||||
v.z = e[ 8 ] * x + e[ 9 ] * y + e[ 10 ] * z;
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
static transformTiedVectorInverse( v, m ) {
|
||||
|
||||
// input:
|
||||
// vector interpreted as a tied (ordinary) vector
|
||||
// THREE.Matrix4 orthogonal matrix (matrix without scale)
|
||||
const x = v.x,
|
||||
y = v.y,
|
||||
z = v.z;
|
||||
const e = m.elements;
|
||||
v.x = e[ 0 ] * x + e[ 1 ] * y + e[ 2 ] * z - e[ 12 ];
|
||||
v.y = e[ 4 ] * x + e[ 5 ] * y + e[ 6 ] * z - e[ 13 ];
|
||||
v.z = e[ 8 ] * x + e[ 9 ] * y + e[ 10 ] * z - e[ 14 ];
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
static transformPlaneToLocalSpace( plane, m, resultPlane ) {
|
||||
|
||||
resultPlane.normal.copy( plane.normal );
|
||||
resultPlane.constant = plane.constant;
|
||||
const referencePoint = ConvexObjectBreaker.transformTiedVectorInverse( plane.coplanarPoint( _v1 ), m );
|
||||
ConvexObjectBreaker.transformFreeVectorInverse( resultPlane.normal, m ); // recalculate constant (like in setFromNormalAndCoplanarPoint)
|
||||
|
||||
resultPlane.constant = - referencePoint.dot( resultPlane.normal );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.ConvexObjectBreaker = ConvexObjectBreaker;
|
||||
|
||||
} )();
|
||||
360
HTML/ThreeJS/node_modules/three/examples/js/misc/GPUComputationRenderer.js
generated
vendored
Normal file
360
HTML/ThreeJS/node_modules/three/examples/js/misc/GPUComputationRenderer.js
generated
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
( function () {
|
||||
|
||||
/**
|
||||
* GPUComputationRenderer, based on SimulationRenderer by zz85
|
||||
*
|
||||
* The GPUComputationRenderer uses the concept of variables. These variables are RGBA float textures that hold 4 floats
|
||||
* for each compute element (texel)
|
||||
*
|
||||
* Each variable has a fragment shader that defines the computation made to obtain the variable in question.
|
||||
* You can use as many variables you need, and make dependencies so you can use textures of other variables in the shader
|
||||
* (the sampler uniforms are added automatically) Most of the variables will need themselves as dependency.
|
||||
*
|
||||
* The renderer has actually two render targets per variable, to make ping-pong. Textures from the current frame are used
|
||||
* as inputs to render the textures of the next frame.
|
||||
*
|
||||
* The render targets of the variables can be used as input textures for your visualization shaders.
|
||||
*
|
||||
* Variable names should be valid identifiers and should not collide with THREE GLSL used identifiers.
|
||||
* a common approach could be to use 'texture' prefixing the variable name; i.e texturePosition, textureVelocity...
|
||||
*
|
||||
* The size of the computation (sizeX * sizeY) is defined as 'resolution' automatically in the shader. For example:
|
||||
* #DEFINE resolution vec2( 1024.0, 1024.0 )
|
||||
*
|
||||
* -------------
|
||||
*
|
||||
* Basic use:
|
||||
*
|
||||
* // Initialization...
|
||||
*
|
||||
* // Create computation renderer
|
||||
* const gpuCompute = new GPUComputationRenderer( 1024, 1024, renderer );
|
||||
*
|
||||
* // Create initial state float textures
|
||||
* const pos0 = gpuCompute.createTexture();
|
||||
* const vel0 = gpuCompute.createTexture();
|
||||
* // and fill in here the texture data...
|
||||
*
|
||||
* // Add texture variables
|
||||
* const velVar = gpuCompute.addVariable( "textureVelocity", fragmentShaderVel, pos0 );
|
||||
* const posVar = gpuCompute.addVariable( "texturePosition", fragmentShaderPos, vel0 );
|
||||
*
|
||||
* // Add variable dependencies
|
||||
* gpuCompute.setVariableDependencies( velVar, [ velVar, posVar ] );
|
||||
* gpuCompute.setVariableDependencies( posVar, [ velVar, posVar ] );
|
||||
*
|
||||
* // Add custom uniforms
|
||||
* velVar.material.uniforms.time = { value: 0.0 };
|
||||
*
|
||||
* // Check for completeness
|
||||
* const error = gpuCompute.init();
|
||||
* if ( error !== null ) {
|
||||
* console.error( error );
|
||||
* }
|
||||
*
|
||||
*
|
||||
* // In each frame...
|
||||
*
|
||||
* // Compute!
|
||||
* gpuCompute.compute();
|
||||
*
|
||||
* // Update texture uniforms in your visualization materials with the gpu renderer output
|
||||
* myMaterial.uniforms.myTexture.value = gpuCompute.getCurrentRenderTarget( posVar ).texture;
|
||||
*
|
||||
* // Do your rendering
|
||||
* renderer.render( myScene, myCamera );
|
||||
*
|
||||
* -------------
|
||||
*
|
||||
* Also, you can use utility functions to create THREE.ShaderMaterial and perform computations (rendering between textures)
|
||||
* Note that the shaders can have multiple input textures.
|
||||
*
|
||||
* const myFilter1 = gpuCompute.createShaderMaterial( myFilterFragmentShader1, { theTexture: { value: null } } );
|
||||
* const myFilter2 = gpuCompute.createShaderMaterial( myFilterFragmentShader2, { theTexture: { value: null } } );
|
||||
*
|
||||
* const inputTexture = gpuCompute.createTexture();
|
||||
*
|
||||
* // Fill in here inputTexture...
|
||||
*
|
||||
* myFilter1.uniforms.theTexture.value = inputTexture;
|
||||
*
|
||||
* const myRenderTarget = gpuCompute.createRenderTarget();
|
||||
* myFilter2.uniforms.theTexture.value = myRenderTarget.texture;
|
||||
*
|
||||
* const outputRenderTarget = gpuCompute.createRenderTarget();
|
||||
*
|
||||
* // Now use the output texture where you want:
|
||||
* myMaterial.uniforms.map.value = outputRenderTarget.texture;
|
||||
*
|
||||
* // And compute each frame, before rendering to screen:
|
||||
* gpuCompute.doRenderTarget( myFilter1, myRenderTarget );
|
||||
* gpuCompute.doRenderTarget( myFilter2, outputRenderTarget );
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param {int} sizeX Computation problem size is always 2d: sizeX * sizeY elements.
|
||||
* @param {int} sizeY Computation problem size is always 2d: sizeX * sizeY elements.
|
||||
* @param {WebGLRenderer} renderer The renderer
|
||||
*/
|
||||
|
||||
class GPUComputationRenderer {
|
||||
|
||||
constructor( sizeX, sizeY, renderer ) {
|
||||
|
||||
this.variables = [];
|
||||
this.currentTextureIndex = 0;
|
||||
let dataType = THREE.FloatType;
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.Camera();
|
||||
camera.position.z = 1;
|
||||
const passThruUniforms = {
|
||||
passThruTexture: {
|
||||
value: null
|
||||
}
|
||||
};
|
||||
const passThruShader = createShaderMaterial( getPassThroughFragmentShader(), passThruUniforms );
|
||||
const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), passThruShader );
|
||||
scene.add( mesh );
|
||||
|
||||
this.setDataType = function ( type ) {
|
||||
|
||||
dataType = type;
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
this.addVariable = function ( variableName, computeFragmentShader, initialValueTexture ) {
|
||||
|
||||
const material = this.createShaderMaterial( computeFragmentShader );
|
||||
const variable = {
|
||||
name: variableName,
|
||||
initialValueTexture: initialValueTexture,
|
||||
material: material,
|
||||
dependencies: null,
|
||||
renderTargets: [],
|
||||
wrapS: null,
|
||||
wrapT: null,
|
||||
minFilter: THREE.NearestFilter,
|
||||
magFilter: THREE.NearestFilter
|
||||
};
|
||||
this.variables.push( variable );
|
||||
return variable;
|
||||
|
||||
};
|
||||
|
||||
this.setVariableDependencies = function ( variable, dependencies ) {
|
||||
|
||||
variable.dependencies = dependencies;
|
||||
|
||||
};
|
||||
|
||||
this.init = function () {
|
||||
|
||||
if ( renderer.capabilities.isWebGL2 === false && renderer.extensions.has( 'OES_texture_float' ) === false ) {
|
||||
|
||||
return 'No OES_texture_float support for float textures.';
|
||||
|
||||
}
|
||||
|
||||
if ( renderer.capabilities.maxVertexTextures === 0 ) {
|
||||
|
||||
return 'No support for vertex shader textures.';
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < this.variables.length; i ++ ) {
|
||||
|
||||
const variable = this.variables[ i ]; // Creates rendertargets and initialize them with input texture
|
||||
|
||||
variable.renderTargets[ 0 ] = this.createRenderTarget( sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter );
|
||||
variable.renderTargets[ 1 ] = this.createRenderTarget( sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter );
|
||||
this.renderTexture( variable.initialValueTexture, variable.renderTargets[ 0 ] );
|
||||
this.renderTexture( variable.initialValueTexture, variable.renderTargets[ 1 ] ); // Adds dependencies uniforms to the THREE.ShaderMaterial
|
||||
|
||||
const material = variable.material;
|
||||
const uniforms = material.uniforms;
|
||||
|
||||
if ( variable.dependencies !== null ) {
|
||||
|
||||
for ( let d = 0; d < variable.dependencies.length; d ++ ) {
|
||||
|
||||
const depVar = variable.dependencies[ d ];
|
||||
|
||||
if ( depVar.name !== variable.name ) {
|
||||
|
||||
// Checks if variable exists
|
||||
let found = false;
|
||||
|
||||
for ( let j = 0; j < this.variables.length; j ++ ) {
|
||||
|
||||
if ( depVar.name === this.variables[ j ].name ) {
|
||||
|
||||
found = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( ! found ) {
|
||||
|
||||
return 'Variable dependency not found. Variable=' + variable.name + ', dependency=' + depVar.name;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uniforms[ depVar.name ] = {
|
||||
value: null
|
||||
};
|
||||
material.fragmentShader = '\nuniform sampler2D ' + depVar.name + ';\n' + material.fragmentShader;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.currentTextureIndex = 0;
|
||||
return null;
|
||||
|
||||
};
|
||||
|
||||
this.compute = function () {
|
||||
|
||||
const currentTextureIndex = this.currentTextureIndex;
|
||||
const nextTextureIndex = this.currentTextureIndex === 0 ? 1 : 0;
|
||||
|
||||
for ( let i = 0, il = this.variables.length; i < il; i ++ ) {
|
||||
|
||||
const variable = this.variables[ i ]; // Sets texture dependencies uniforms
|
||||
|
||||
if ( variable.dependencies !== null ) {
|
||||
|
||||
const uniforms = variable.material.uniforms;
|
||||
|
||||
for ( let d = 0, dl = variable.dependencies.length; d < dl; d ++ ) {
|
||||
|
||||
const depVar = variable.dependencies[ d ];
|
||||
uniforms[ depVar.name ].value = depVar.renderTargets[ currentTextureIndex ].texture;
|
||||
|
||||
}
|
||||
|
||||
} // Performs the computation for this variable
|
||||
|
||||
|
||||
this.doRenderTarget( variable.material, variable.renderTargets[ nextTextureIndex ] );
|
||||
|
||||
}
|
||||
|
||||
this.currentTextureIndex = nextTextureIndex;
|
||||
|
||||
};
|
||||
|
||||
this.getCurrentRenderTarget = function ( variable ) {
|
||||
|
||||
return variable.renderTargets[ this.currentTextureIndex ];
|
||||
|
||||
};
|
||||
|
||||
this.getAlternateRenderTarget = function ( variable ) {
|
||||
|
||||
return variable.renderTargets[ this.currentTextureIndex === 0 ? 1 : 0 ];
|
||||
|
||||
};
|
||||
|
||||
function addResolutionDefine( materialShader ) {
|
||||
|
||||
materialShader.defines.resolution = 'vec2( ' + sizeX.toFixed( 1 ) + ', ' + sizeY.toFixed( 1 ) + ' )';
|
||||
|
||||
}
|
||||
|
||||
this.addResolutionDefine = addResolutionDefine; // The following functions can be used to compute things manually
|
||||
|
||||
function createShaderMaterial( computeFragmentShader, uniforms ) {
|
||||
|
||||
uniforms = uniforms || {};
|
||||
const material = new THREE.ShaderMaterial( {
|
||||
uniforms: uniforms,
|
||||
vertexShader: getPassThroughVertexShader(),
|
||||
fragmentShader: computeFragmentShader
|
||||
} );
|
||||
addResolutionDefine( material );
|
||||
return material;
|
||||
|
||||
}
|
||||
|
||||
this.createShaderMaterial = createShaderMaterial;
|
||||
|
||||
this.createRenderTarget = function ( sizeXTexture, sizeYTexture, wrapS, wrapT, minFilter, magFilter ) {
|
||||
|
||||
sizeXTexture = sizeXTexture || sizeX;
|
||||
sizeYTexture = sizeYTexture || sizeY;
|
||||
wrapS = wrapS || THREE.ClampToEdgeWrapping;
|
||||
wrapT = wrapT || THREE.ClampToEdgeWrapping;
|
||||
minFilter = minFilter || THREE.NearestFilter;
|
||||
magFilter = magFilter || THREE.NearestFilter;
|
||||
const renderTarget = new THREE.WebGLRenderTarget( sizeXTexture, sizeYTexture, {
|
||||
wrapS: wrapS,
|
||||
wrapT: wrapT,
|
||||
minFilter: minFilter,
|
||||
magFilter: magFilter,
|
||||
format: THREE.RGBAFormat,
|
||||
type: dataType,
|
||||
depthBuffer: false
|
||||
} );
|
||||
return renderTarget;
|
||||
|
||||
};
|
||||
|
||||
this.createTexture = function () {
|
||||
|
||||
const data = new Float32Array( sizeX * sizeY * 4 );
|
||||
const texture = new THREE.DataTexture( data, sizeX, sizeY, THREE.RGBAFormat, THREE.FloatType );
|
||||
texture.needsUpdate = true;
|
||||
return texture;
|
||||
|
||||
};
|
||||
|
||||
this.renderTexture = function ( input, output ) {
|
||||
|
||||
// Takes a texture, and render out in rendertarget
|
||||
// input = Texture
|
||||
// output = RenderTarget
|
||||
passThruUniforms.passThruTexture.value = input;
|
||||
this.doRenderTarget( passThruShader, output );
|
||||
passThruUniforms.passThruTexture.value = null;
|
||||
|
||||
};
|
||||
|
||||
this.doRenderTarget = function ( material, output ) {
|
||||
|
||||
const currentRenderTarget = renderer.getRenderTarget();
|
||||
mesh.material = material;
|
||||
renderer.setRenderTarget( output );
|
||||
renderer.render( scene, camera );
|
||||
mesh.material = passThruShader;
|
||||
renderer.setRenderTarget( currentRenderTarget );
|
||||
|
||||
}; // Shaders
|
||||
|
||||
|
||||
function getPassThroughVertexShader() {
|
||||
|
||||
return 'void main() {\n' + '\n' + ' gl_Position = vec4( position, 1.0 );\n' + '\n' + '}\n';
|
||||
|
||||
}
|
||||
|
||||
function getPassThroughFragmentShader() {
|
||||
|
||||
return 'uniform sampler2D passThruTexture;\n' + '\n' + 'void main() {\n' + '\n' + ' vec2 uv = gl_FragCoord.xy / resolution.xy;\n' + '\n' + ' gl_FragColor = texture2D( passThruTexture, uv );\n' + '\n' + '}\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.GPUComputationRenderer = GPUComputationRenderer;
|
||||
|
||||
} )();
|
||||
60
HTML/ThreeJS/node_modules/three/examples/js/misc/Gyroscope.js
generated
vendored
Normal file
60
HTML/ThreeJS/node_modules/three/examples/js/misc/Gyroscope.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
( function () {
|
||||
|
||||
const _translationObject = new THREE.Vector3();
|
||||
|
||||
const _quaternionObject = new THREE.Quaternion();
|
||||
|
||||
const _scaleObject = new THREE.Vector3();
|
||||
|
||||
const _translationWorld = new THREE.Vector3();
|
||||
|
||||
const _quaternionWorld = new THREE.Quaternion();
|
||||
|
||||
const _scaleWorld = new THREE.Vector3();
|
||||
|
||||
class Gyroscope extends THREE.Object3D {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
}
|
||||
|
||||
updateMatrixWorld( force ) {
|
||||
|
||||
this.matrixAutoUpdate && this.updateMatrix(); // update matrixWorld
|
||||
|
||||
if ( this.matrixWorldNeedsUpdate || force ) {
|
||||
|
||||
if ( this.parent !== null ) {
|
||||
|
||||
this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
|
||||
this.matrixWorld.decompose( _translationWorld, _quaternionWorld, _scaleWorld );
|
||||
this.matrix.decompose( _translationObject, _quaternionObject, _scaleObject );
|
||||
this.matrixWorld.compose( _translationWorld, _quaternionObject, _scaleWorld );
|
||||
|
||||
} else {
|
||||
|
||||
this.matrixWorld.copy( this.matrix );
|
||||
|
||||
}
|
||||
|
||||
this.matrixWorldNeedsUpdate = false;
|
||||
force = true;
|
||||
|
||||
} // update children
|
||||
|
||||
|
||||
for ( let i = 0, l = this.children.length; i < l; i ++ ) {
|
||||
|
||||
this.children[ i ].updateMatrixWorld( force );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.Gyroscope = Gyroscope;
|
||||
|
||||
} )();
|
||||
244
HTML/ThreeJS/node_modules/three/examples/js/misc/MD2Character.js
generated
vendored
Normal file
244
HTML/ThreeJS/node_modules/three/examples/js/misc/MD2Character.js
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
( function () {
|
||||
|
||||
class MD2Character {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.scale = 1;
|
||||
this.animationFPS = 6;
|
||||
this.root = new THREE.Object3D();
|
||||
this.meshBody = null;
|
||||
this.meshWeapon = null;
|
||||
this.skinsBody = [];
|
||||
this.skinsWeapon = [];
|
||||
this.weapons = [];
|
||||
this.activeAnimation = null;
|
||||
this.mixer = null;
|
||||
|
||||
this.onLoadComplete = function () {};
|
||||
|
||||
this.loadCounter = 0;
|
||||
|
||||
}
|
||||
|
||||
loadParts( config ) {
|
||||
|
||||
const scope = this;
|
||||
|
||||
function createPart( geometry, skinMap ) {
|
||||
|
||||
const materialWireframe = new THREE.MeshLambertMaterial( {
|
||||
color: 0xffaa00,
|
||||
wireframe: true
|
||||
} );
|
||||
const materialTexture = new THREE.MeshLambertMaterial( {
|
||||
color: 0xffffff,
|
||||
wireframe: false,
|
||||
map: skinMap
|
||||
} ); //
|
||||
|
||||
const mesh = new THREE.Mesh( geometry, materialTexture );
|
||||
mesh.rotation.y = - Math.PI / 2;
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true; //
|
||||
|
||||
mesh.materialTexture = materialTexture;
|
||||
mesh.materialWireframe = materialWireframe;
|
||||
return mesh;
|
||||
|
||||
}
|
||||
|
||||
function loadTextures( baseUrl, textureUrls ) {
|
||||
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
const textures = [];
|
||||
|
||||
for ( let i = 0; i < textureUrls.length; i ++ ) {
|
||||
|
||||
textures[ i ] = textureLoader.load( baseUrl + textureUrls[ i ], checkLoadingComplete );
|
||||
textures[ i ].mapping = THREE.UVMapping;
|
||||
textures[ i ].name = textureUrls[ i ];
|
||||
textures[ i ].encoding = THREE.sRGBEncoding;
|
||||
|
||||
}
|
||||
|
||||
return textures;
|
||||
|
||||
}
|
||||
|
||||
function checkLoadingComplete() {
|
||||
|
||||
scope.loadCounter -= 1;
|
||||
if ( scope.loadCounter === 0 ) scope.onLoadComplete();
|
||||
|
||||
}
|
||||
|
||||
this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;
|
||||
const weaponsTextures = [];
|
||||
|
||||
for ( let i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ]; // SKINS
|
||||
|
||||
|
||||
this.skinsBody = loadTextures( config.baseUrl + 'skins/', config.skins );
|
||||
this.skinsWeapon = loadTextures( config.baseUrl + 'skins/', weaponsTextures ); // BODY
|
||||
|
||||
const loader = new THREE.MD2Loader();
|
||||
loader.load( config.baseUrl + config.body, function ( geo ) {
|
||||
|
||||
const boundingBox = new THREE.Box3();
|
||||
boundingBox.setFromBufferAttribute( geo.attributes.position );
|
||||
scope.root.position.y = - scope.scale * boundingBox.min.y;
|
||||
const mesh = createPart( geo, scope.skinsBody[ 0 ] );
|
||||
mesh.scale.set( scope.scale, scope.scale, scope.scale );
|
||||
scope.root.add( mesh );
|
||||
scope.meshBody = mesh;
|
||||
scope.meshBody.clipOffset = 0;
|
||||
scope.activeAnimationClipName = mesh.geometry.animations[ 0 ].name;
|
||||
scope.mixer = new THREE.AnimationMixer( mesh );
|
||||
checkLoadingComplete();
|
||||
|
||||
} ); // WEAPONS
|
||||
|
||||
const generateCallback = function ( index, name ) {
|
||||
|
||||
return function ( geo ) {
|
||||
|
||||
const mesh = createPart( geo, scope.skinsWeapon[ index ] );
|
||||
mesh.scale.set( scope.scale, scope.scale, scope.scale );
|
||||
mesh.visible = false;
|
||||
mesh.name = name;
|
||||
scope.root.add( mesh );
|
||||
scope.weapons[ index ] = mesh;
|
||||
scope.meshWeapon = mesh;
|
||||
checkLoadingComplete();
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
for ( let i = 0; i < config.weapons.length; i ++ ) {
|
||||
|
||||
loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setPlaybackRate( rate ) {
|
||||
|
||||
if ( rate !== 0 ) {
|
||||
|
||||
this.mixer.timeScale = 1 / rate;
|
||||
|
||||
} else {
|
||||
|
||||
this.mixer.timeScale = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setWireframe( wireframeEnabled ) {
|
||||
|
||||
if ( wireframeEnabled ) {
|
||||
|
||||
if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe;
|
||||
if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe;
|
||||
|
||||
} else {
|
||||
|
||||
if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture;
|
||||
if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setSkin( index ) {
|
||||
|
||||
if ( this.meshBody && this.meshBody.material.wireframe === false ) {
|
||||
|
||||
this.meshBody.material.map = this.skinsBody[ index ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setWeapon( index ) {
|
||||
|
||||
for ( let i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false;
|
||||
|
||||
const activeWeapon = this.weapons[ index ];
|
||||
|
||||
if ( activeWeapon ) {
|
||||
|
||||
activeWeapon.visible = true;
|
||||
this.meshWeapon = activeWeapon;
|
||||
this.syncWeaponAnimation();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setAnimation( clipName ) {
|
||||
|
||||
if ( this.meshBody ) {
|
||||
|
||||
if ( this.meshBody.activeAction ) {
|
||||
|
||||
this.meshBody.activeAction.stop();
|
||||
this.meshBody.activeAction = null;
|
||||
|
||||
}
|
||||
|
||||
const action = this.mixer.clipAction( clipName, this.meshBody );
|
||||
|
||||
if ( action ) {
|
||||
|
||||
this.meshBody.activeAction = action.play();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.activeClipName = clipName;
|
||||
this.syncWeaponAnimation();
|
||||
|
||||
}
|
||||
|
||||
syncWeaponAnimation() {
|
||||
|
||||
const clipName = this.activeClipName;
|
||||
|
||||
if ( this.meshWeapon ) {
|
||||
|
||||
if ( this.meshWeapon.activeAction ) {
|
||||
|
||||
this.meshWeapon.activeAction.stop();
|
||||
this.meshWeapon.activeAction = null;
|
||||
|
||||
}
|
||||
|
||||
const action = this.mixer.clipAction( clipName, this.meshWeapon );
|
||||
|
||||
if ( action ) {
|
||||
|
||||
this.meshWeapon.activeAction = action.syncWith( this.meshBody.activeAction ).play();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
update( delta ) {
|
||||
|
||||
if ( this.mixer ) this.mixer.update( delta );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.MD2Character = MD2Character;
|
||||
|
||||
} )();
|
||||
494
HTML/ThreeJS/node_modules/three/examples/js/misc/MD2CharacterComplex.js
generated
vendored
Normal file
494
HTML/ThreeJS/node_modules/three/examples/js/misc/MD2CharacterComplex.js
generated
vendored
Normal file
@@ -0,0 +1,494 @@
|
||||
( function () {
|
||||
|
||||
class MD2CharacterComplex {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.scale = 1; // animation parameters
|
||||
|
||||
this.animationFPS = 6;
|
||||
this.transitionFrames = 15; // movement model parameters
|
||||
|
||||
this.maxSpeed = 275;
|
||||
this.maxReverseSpeed = - 275;
|
||||
this.frontAcceleration = 600;
|
||||
this.backAcceleration = 600;
|
||||
this.frontDecceleration = 600;
|
||||
this.angularSpeed = 2.5; // rig
|
||||
|
||||
this.root = new THREE.Object3D();
|
||||
this.meshBody = null;
|
||||
this.meshWeapon = null;
|
||||
this.controls = null; // skins
|
||||
|
||||
this.skinsBody = [];
|
||||
this.skinsWeapon = [];
|
||||
this.weapons = [];
|
||||
this.currentSkin = undefined; //
|
||||
|
||||
this.onLoadComplete = function () {}; // internals
|
||||
|
||||
|
||||
this.meshes = [];
|
||||
this.animations = {};
|
||||
this.loadCounter = 0; // internal movement control variables
|
||||
|
||||
this.speed = 0;
|
||||
this.bodyOrientation = 0;
|
||||
this.walkSpeed = this.maxSpeed;
|
||||
this.crouchSpeed = this.maxSpeed * 0.5; // internal animation parameters
|
||||
|
||||
this.activeAnimation = null;
|
||||
this.oldAnimation = null; // API
|
||||
|
||||
}
|
||||
|
||||
enableShadows( enable ) {
|
||||
|
||||
for ( let i = 0; i < this.meshes.length; i ++ ) {
|
||||
|
||||
this.meshes[ i ].castShadow = enable;
|
||||
this.meshes[ i ].receiveShadow = enable;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setVisible( enable ) {
|
||||
|
||||
for ( let i = 0; i < this.meshes.length; i ++ ) {
|
||||
|
||||
this.meshes[ i ].visible = enable;
|
||||
this.meshes[ i ].visible = enable;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
shareParts( original ) {
|
||||
|
||||
this.animations = original.animations;
|
||||
this.walkSpeed = original.walkSpeed;
|
||||
this.crouchSpeed = original.crouchSpeed;
|
||||
this.skinsBody = original.skinsBody;
|
||||
this.skinsWeapon = original.skinsWeapon; // BODY
|
||||
|
||||
const mesh = this._createPart( original.meshBody.geometry, this.skinsBody[ 0 ] );
|
||||
|
||||
mesh.scale.set( this.scale, this.scale, this.scale );
|
||||
this.root.position.y = original.root.position.y;
|
||||
this.root.add( mesh );
|
||||
this.meshBody = mesh;
|
||||
this.meshes.push( mesh ); // WEAPONS
|
||||
|
||||
for ( let i = 0; i < original.weapons.length; i ++ ) {
|
||||
|
||||
const meshWeapon = this._createPart( original.weapons[ i ].geometry, this.skinsWeapon[ i ] );
|
||||
|
||||
meshWeapon.scale.set( this.scale, this.scale, this.scale );
|
||||
meshWeapon.visible = false;
|
||||
meshWeapon.name = original.weapons[ i ].name;
|
||||
this.root.add( meshWeapon );
|
||||
this.weapons[ i ] = meshWeapon;
|
||||
this.meshWeapon = meshWeapon;
|
||||
this.meshes.push( meshWeapon );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
loadParts( config ) {
|
||||
|
||||
const scope = this;
|
||||
|
||||
function loadTextures( baseUrl, textureUrls ) {
|
||||
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
const textures = [];
|
||||
|
||||
for ( let i = 0; i < textureUrls.length; i ++ ) {
|
||||
|
||||
textures[ i ] = textureLoader.load( baseUrl + textureUrls[ i ], checkLoadingComplete );
|
||||
textures[ i ].mapping = THREE.UVMapping;
|
||||
textures[ i ].name = textureUrls[ i ];
|
||||
textures[ i ].encoding = THREE.sRGBEncoding;
|
||||
|
||||
}
|
||||
|
||||
return textures;
|
||||
|
||||
}
|
||||
|
||||
function checkLoadingComplete() {
|
||||
|
||||
scope.loadCounter -= 1;
|
||||
if ( scope.loadCounter === 0 ) scope.onLoadComplete();
|
||||
|
||||
}
|
||||
|
||||
this.animations = config.animations;
|
||||
this.walkSpeed = config.walkSpeed;
|
||||
this.crouchSpeed = config.crouchSpeed;
|
||||
this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;
|
||||
const weaponsTextures = [];
|
||||
|
||||
for ( let i = 0; i < config.weapons.length; i ++ ) weaponsTextures[ i ] = config.weapons[ i ][ 1 ]; // SKINS
|
||||
|
||||
|
||||
this.skinsBody = loadTextures( config.baseUrl + 'skins/', config.skins );
|
||||
this.skinsWeapon = loadTextures( config.baseUrl + 'skins/', weaponsTextures ); // BODY
|
||||
|
||||
const loader = new THREE.MD2Loader();
|
||||
loader.load( config.baseUrl + config.body, function ( geo ) {
|
||||
|
||||
const boundingBox = new THREE.Box3();
|
||||
boundingBox.setFromBufferAttribute( geo.attributes.position );
|
||||
scope.root.position.y = - scope.scale * boundingBox.min.y;
|
||||
|
||||
const mesh = scope._createPart( geo, scope.skinsBody[ 0 ] );
|
||||
|
||||
mesh.scale.set( scope.scale, scope.scale, scope.scale );
|
||||
scope.root.add( mesh );
|
||||
scope.meshBody = mesh;
|
||||
scope.meshes.push( mesh );
|
||||
checkLoadingComplete();
|
||||
|
||||
} ); // WEAPONS
|
||||
|
||||
const generateCallback = function ( index, name ) {
|
||||
|
||||
return function ( geo ) {
|
||||
|
||||
const mesh = scope._createPart( geo, scope.skinsWeapon[ index ] );
|
||||
|
||||
mesh.scale.set( scope.scale, scope.scale, scope.scale );
|
||||
mesh.visible = false;
|
||||
mesh.name = name;
|
||||
scope.root.add( mesh );
|
||||
scope.weapons[ index ] = mesh;
|
||||
scope.meshWeapon = mesh;
|
||||
scope.meshes.push( mesh );
|
||||
checkLoadingComplete();
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
for ( let i = 0; i < config.weapons.length; i ++ ) {
|
||||
|
||||
loader.load( config.baseUrl + config.weapons[ i ][ 0 ], generateCallback( i, config.weapons[ i ][ 0 ] ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setPlaybackRate( rate ) {
|
||||
|
||||
if ( this.meshBody ) this.meshBody.duration = this.meshBody.baseDuration / rate;
|
||||
if ( this.meshWeapon ) this.meshWeapon.duration = this.meshWeapon.baseDuration / rate;
|
||||
|
||||
}
|
||||
|
||||
setWireframe( wireframeEnabled ) {
|
||||
|
||||
if ( wireframeEnabled ) {
|
||||
|
||||
if ( this.meshBody ) this.meshBody.material = this.meshBody.materialWireframe;
|
||||
if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialWireframe;
|
||||
|
||||
} else {
|
||||
|
||||
if ( this.meshBody ) this.meshBody.material = this.meshBody.materialTexture;
|
||||
if ( this.meshWeapon ) this.meshWeapon.material = this.meshWeapon.materialTexture;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setSkin( index ) {
|
||||
|
||||
if ( this.meshBody && this.meshBody.material.wireframe === false ) {
|
||||
|
||||
this.meshBody.material.map = this.skinsBody[ index ];
|
||||
this.currentSkin = index;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setWeapon( index ) {
|
||||
|
||||
for ( let i = 0; i < this.weapons.length; i ++ ) this.weapons[ i ].visible = false;
|
||||
|
||||
const activeWeapon = this.weapons[ index ];
|
||||
|
||||
if ( activeWeapon ) {
|
||||
|
||||
activeWeapon.visible = true;
|
||||
this.meshWeapon = activeWeapon;
|
||||
|
||||
if ( this.activeAnimation ) {
|
||||
|
||||
activeWeapon.playAnimation( this.activeAnimation );
|
||||
this.meshWeapon.setAnimationTime( this.activeAnimation, this.meshBody.getAnimationTime( this.activeAnimation ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setAnimation( animationName ) {
|
||||
|
||||
if ( animationName === this.activeAnimation || ! animationName ) return;
|
||||
|
||||
if ( this.meshBody ) {
|
||||
|
||||
this.meshBody.setAnimationWeight( animationName, 0 );
|
||||
this.meshBody.playAnimation( animationName );
|
||||
this.oldAnimation = this.activeAnimation;
|
||||
this.activeAnimation = animationName;
|
||||
this.blendCounter = this.transitionFrames;
|
||||
|
||||
}
|
||||
|
||||
if ( this.meshWeapon ) {
|
||||
|
||||
this.meshWeapon.setAnimationWeight( animationName, 0 );
|
||||
this.meshWeapon.playAnimation( animationName );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
update( delta ) {
|
||||
|
||||
if ( this.controls ) this.updateMovementModel( delta );
|
||||
|
||||
if ( this.animations ) {
|
||||
|
||||
this.updateBehaviors();
|
||||
this.updateAnimations( delta );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateAnimations( delta ) {
|
||||
|
||||
let mix = 1;
|
||||
|
||||
if ( this.blendCounter > 0 ) {
|
||||
|
||||
mix = ( this.transitionFrames - this.blendCounter ) / this.transitionFrames;
|
||||
this.blendCounter -= 1;
|
||||
|
||||
}
|
||||
|
||||
if ( this.meshBody ) {
|
||||
|
||||
this.meshBody.update( delta );
|
||||
this.meshBody.setAnimationWeight( this.activeAnimation, mix );
|
||||
this.meshBody.setAnimationWeight( this.oldAnimation, 1 - mix );
|
||||
|
||||
}
|
||||
|
||||
if ( this.meshWeapon ) {
|
||||
|
||||
this.meshWeapon.update( delta );
|
||||
this.meshWeapon.setAnimationWeight( this.activeAnimation, mix );
|
||||
this.meshWeapon.setAnimationWeight( this.oldAnimation, 1 - mix );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateBehaviors() {
|
||||
|
||||
const controls = this.controls;
|
||||
const animations = this.animations;
|
||||
let moveAnimation, idleAnimation; // crouch vs stand
|
||||
|
||||
if ( controls.crouch ) {
|
||||
|
||||
moveAnimation = animations[ 'crouchMove' ];
|
||||
idleAnimation = animations[ 'crouchIdle' ];
|
||||
|
||||
} else {
|
||||
|
||||
moveAnimation = animations[ 'move' ];
|
||||
idleAnimation = animations[ 'idle' ];
|
||||
|
||||
} // actions
|
||||
|
||||
|
||||
if ( controls.jump ) {
|
||||
|
||||
moveAnimation = animations[ 'jump' ];
|
||||
idleAnimation = animations[ 'jump' ];
|
||||
|
||||
}
|
||||
|
||||
if ( controls.attack ) {
|
||||
|
||||
if ( controls.crouch ) {
|
||||
|
||||
moveAnimation = animations[ 'crouchAttack' ];
|
||||
idleAnimation = animations[ 'crouchAttack' ];
|
||||
|
||||
} else {
|
||||
|
||||
moveAnimation = animations[ 'attack' ];
|
||||
idleAnimation = animations[ 'attack' ];
|
||||
|
||||
}
|
||||
|
||||
} // set animations
|
||||
|
||||
|
||||
if ( controls.moveForward || controls.moveBackward || controls.moveLeft || controls.moveRight ) {
|
||||
|
||||
if ( this.activeAnimation !== moveAnimation ) {
|
||||
|
||||
this.setAnimation( moveAnimation );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( Math.abs( this.speed ) < 0.2 * this.maxSpeed && ! ( controls.moveLeft || controls.moveRight || controls.moveForward || controls.moveBackward ) ) {
|
||||
|
||||
if ( this.activeAnimation !== idleAnimation ) {
|
||||
|
||||
this.setAnimation( idleAnimation );
|
||||
|
||||
}
|
||||
|
||||
} // set animation direction
|
||||
|
||||
|
||||
if ( controls.moveForward ) {
|
||||
|
||||
if ( this.meshBody ) {
|
||||
|
||||
this.meshBody.setAnimationDirectionForward( this.activeAnimation );
|
||||
this.meshBody.setAnimationDirectionForward( this.oldAnimation );
|
||||
|
||||
}
|
||||
|
||||
if ( this.meshWeapon ) {
|
||||
|
||||
this.meshWeapon.setAnimationDirectionForward( this.activeAnimation );
|
||||
this.meshWeapon.setAnimationDirectionForward( this.oldAnimation );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( controls.moveBackward ) {
|
||||
|
||||
if ( this.meshBody ) {
|
||||
|
||||
this.meshBody.setAnimationDirectionBackward( this.activeAnimation );
|
||||
this.meshBody.setAnimationDirectionBackward( this.oldAnimation );
|
||||
|
||||
}
|
||||
|
||||
if ( this.meshWeapon ) {
|
||||
|
||||
this.meshWeapon.setAnimationDirectionBackward( this.activeAnimation );
|
||||
this.meshWeapon.setAnimationDirectionBackward( this.oldAnimation );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateMovementModel( delta ) {
|
||||
|
||||
function exponentialEaseOut( k ) {
|
||||
|
||||
return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;
|
||||
|
||||
}
|
||||
|
||||
const controls = this.controls; // speed based on controls
|
||||
|
||||
if ( controls.crouch ) this.maxSpeed = this.crouchSpeed; else this.maxSpeed = this.walkSpeed;
|
||||
this.maxReverseSpeed = - this.maxSpeed;
|
||||
if ( controls.moveForward ) this.speed = THREE.MathUtils.clamp( this.speed + delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
|
||||
if ( controls.moveBackward ) this.speed = THREE.MathUtils.clamp( this.speed - delta * this.backAcceleration, this.maxReverseSpeed, this.maxSpeed ); // orientation based on controls
|
||||
// (don't just stand while turning)
|
||||
|
||||
const dir = 1;
|
||||
|
||||
if ( controls.moveLeft ) {
|
||||
|
||||
this.bodyOrientation += delta * this.angularSpeed;
|
||||
this.speed = THREE.MathUtils.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
|
||||
|
||||
}
|
||||
|
||||
if ( controls.moveRight ) {
|
||||
|
||||
this.bodyOrientation -= delta * this.angularSpeed;
|
||||
this.speed = THREE.MathUtils.clamp( this.speed + dir * delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
|
||||
|
||||
} // speed decay
|
||||
|
||||
|
||||
if ( ! ( controls.moveForward || controls.moveBackward ) ) {
|
||||
|
||||
if ( this.speed > 0 ) {
|
||||
|
||||
const k = exponentialEaseOut( this.speed / this.maxSpeed );
|
||||
this.speed = THREE.MathUtils.clamp( this.speed - k * delta * this.frontDecceleration, 0, this.maxSpeed );
|
||||
|
||||
} else {
|
||||
|
||||
const k = exponentialEaseOut( this.speed / this.maxReverseSpeed );
|
||||
this.speed = THREE.MathUtils.clamp( this.speed + k * delta * this.backAcceleration, this.maxReverseSpeed, 0 );
|
||||
|
||||
}
|
||||
|
||||
} // displacement
|
||||
|
||||
|
||||
const forwardDelta = this.speed * delta;
|
||||
this.root.position.x += Math.sin( this.bodyOrientation ) * forwardDelta;
|
||||
this.root.position.z += Math.cos( this.bodyOrientation ) * forwardDelta; // steering
|
||||
|
||||
this.root.rotation.y = this.bodyOrientation;
|
||||
|
||||
} // internal
|
||||
|
||||
|
||||
_createPart( geometry, skinMap ) {
|
||||
|
||||
const materialWireframe = new THREE.MeshLambertMaterial( {
|
||||
color: 0xffaa00,
|
||||
wireframe: true
|
||||
} );
|
||||
const materialTexture = new THREE.MeshLambertMaterial( {
|
||||
color: 0xffffff,
|
||||
wireframe: false,
|
||||
map: skinMap
|
||||
} ); //
|
||||
|
||||
const mesh = new THREE.MorphBlendMesh( geometry, materialTexture );
|
||||
mesh.rotation.y = - Math.PI / 2; //
|
||||
|
||||
mesh.materialTexture = materialTexture;
|
||||
mesh.materialWireframe = materialWireframe; //
|
||||
|
||||
mesh.autoCreateAnimations( this.animationFPS );
|
||||
return mesh;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.MD2CharacterComplex = MD2CharacterComplex;
|
||||
|
||||
} )();
|
||||
69
HTML/ThreeJS/node_modules/three/examples/js/misc/MorphAnimMesh.js
generated
vendored
Normal file
69
HTML/ThreeJS/node_modules/three/examples/js/misc/MorphAnimMesh.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
( function () {
|
||||
|
||||
class MorphAnimMesh extends THREE.Mesh {
|
||||
|
||||
constructor( geometry, material ) {
|
||||
|
||||
super( geometry, material );
|
||||
this.type = 'MorphAnimMesh';
|
||||
this.mixer = new THREE.AnimationMixer( this );
|
||||
this.activeAction = null;
|
||||
|
||||
}
|
||||
|
||||
setDirectionForward() {
|
||||
|
||||
this.mixer.timeScale = 1.0;
|
||||
|
||||
}
|
||||
|
||||
setDirectionBackward() {
|
||||
|
||||
this.mixer.timeScale = - 1.0;
|
||||
|
||||
}
|
||||
|
||||
playAnimation( label, fps ) {
|
||||
|
||||
if ( this.activeAction ) {
|
||||
|
||||
this.activeAction.stop();
|
||||
this.activeAction = null;
|
||||
|
||||
}
|
||||
|
||||
const clip = THREE.AnimationClip.findByName( this, label );
|
||||
|
||||
if ( clip ) {
|
||||
|
||||
const action = this.mixer.clipAction( clip );
|
||||
action.timeScale = clip.tracks.length * fps / clip.duration;
|
||||
this.activeAction = action.play();
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'THREE.MorphAnimMesh: animations[' + label + '] undefined in .playAnimation()' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateAnimation( delta ) {
|
||||
|
||||
this.mixer.update( delta );
|
||||
|
||||
}
|
||||
|
||||
copy( source ) {
|
||||
|
||||
super.copy( source );
|
||||
this.mixer = new THREE.AnimationMixer( this );
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.MorphAnimMesh = MorphAnimMesh;
|
||||
|
||||
} )();
|
||||
292
HTML/ThreeJS/node_modules/three/examples/js/misc/MorphBlendMesh.js
generated
vendored
Normal file
292
HTML/ThreeJS/node_modules/three/examples/js/misc/MorphBlendMesh.js
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
( function () {
|
||||
|
||||
class MorphBlendMesh extends THREE.Mesh {
|
||||
|
||||
constructor( geometry, material ) {
|
||||
|
||||
super( geometry, material );
|
||||
this.animationsMap = {};
|
||||
this.animationsList = []; // prepare default animation
|
||||
// (all frames played together in 1 second)
|
||||
|
||||
const numFrames = Object.keys( this.morphTargetDictionary ).length;
|
||||
const name = '__default';
|
||||
const startFrame = 0;
|
||||
const endFrame = numFrames - 1;
|
||||
const fps = numFrames / 1;
|
||||
this.createAnimation( name, startFrame, endFrame, fps );
|
||||
this.setAnimationWeight( name, 1 );
|
||||
|
||||
}
|
||||
|
||||
createAnimation( name, start, end, fps ) {
|
||||
|
||||
const animation = {
|
||||
start: start,
|
||||
end: end,
|
||||
length: end - start + 1,
|
||||
fps: fps,
|
||||
duration: ( end - start ) / fps,
|
||||
lastFrame: 0,
|
||||
currentFrame: 0,
|
||||
active: false,
|
||||
time: 0,
|
||||
direction: 1,
|
||||
weight: 1,
|
||||
directionBackwards: false,
|
||||
mirroredLoop: false
|
||||
};
|
||||
this.animationsMap[ name ] = animation;
|
||||
this.animationsList.push( animation );
|
||||
|
||||
}
|
||||
|
||||
autoCreateAnimations( fps ) {
|
||||
|
||||
const pattern = /([a-z]+)_?(\d+)/i;
|
||||
let firstAnimation;
|
||||
const frameRanges = {};
|
||||
let i = 0;
|
||||
|
||||
for ( const key in this.morphTargetDictionary ) {
|
||||
|
||||
const chunks = key.match( pattern );
|
||||
|
||||
if ( chunks && chunks.length > 1 ) {
|
||||
|
||||
const name = chunks[ 1 ];
|
||||
if ( ! frameRanges[ name ] ) frameRanges[ name ] = {
|
||||
start: Infinity,
|
||||
end: - Infinity
|
||||
};
|
||||
const range = frameRanges[ name ];
|
||||
if ( i < range.start ) range.start = i;
|
||||
if ( i > range.end ) range.end = i;
|
||||
if ( ! firstAnimation ) firstAnimation = name;
|
||||
|
||||
}
|
||||
|
||||
i ++;
|
||||
|
||||
}
|
||||
|
||||
for ( const name in frameRanges ) {
|
||||
|
||||
const range = frameRanges[ name ];
|
||||
this.createAnimation( name, range.start, range.end, fps );
|
||||
|
||||
}
|
||||
|
||||
this.firstAnimation = firstAnimation;
|
||||
|
||||
}
|
||||
|
||||
setAnimationDirectionForward( name ) {
|
||||
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
animation.direction = 1;
|
||||
animation.directionBackwards = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setAnimationDirectionBackward( name ) {
|
||||
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
animation.direction = - 1;
|
||||
animation.directionBackwards = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setAnimationFPS( name, fps ) {
|
||||
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
animation.fps = fps;
|
||||
animation.duration = ( animation.end - animation.start ) / animation.fps;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setAnimationDuration( name, duration ) {
|
||||
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
animation.duration = duration;
|
||||
animation.fps = ( animation.end - animation.start ) / animation.duration;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setAnimationWeight( name, weight ) {
|
||||
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
animation.weight = weight;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setAnimationTime( name, time ) {
|
||||
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
animation.time = time;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getAnimationTime( name ) {
|
||||
|
||||
let time = 0;
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
time = animation.time;
|
||||
|
||||
}
|
||||
|
||||
return time;
|
||||
|
||||
}
|
||||
|
||||
getAnimationDuration( name ) {
|
||||
|
||||
let duration = - 1;
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
duration = animation.duration;
|
||||
|
||||
}
|
||||
|
||||
return duration;
|
||||
|
||||
}
|
||||
|
||||
playAnimation( name ) {
|
||||
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
animation.time = 0;
|
||||
animation.active = true;
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.MorphBlendMesh: animation[' + name + '] undefined in .playAnimation()' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stopAnimation( name ) {
|
||||
|
||||
const animation = this.animationsMap[ name ];
|
||||
|
||||
if ( animation ) {
|
||||
|
||||
animation.active = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
update( delta ) {
|
||||
|
||||
for ( let i = 0, il = this.animationsList.length; i < il; i ++ ) {
|
||||
|
||||
const animation = this.animationsList[ i ];
|
||||
if ( ! animation.active ) continue;
|
||||
const frameTime = animation.duration / animation.length;
|
||||
animation.time += animation.direction * delta;
|
||||
|
||||
if ( animation.mirroredLoop ) {
|
||||
|
||||
if ( animation.time > animation.duration || animation.time < 0 ) {
|
||||
|
||||
animation.direction *= - 1;
|
||||
|
||||
if ( animation.time > animation.duration ) {
|
||||
|
||||
animation.time = animation.duration;
|
||||
animation.directionBackwards = true;
|
||||
|
||||
}
|
||||
|
||||
if ( animation.time < 0 ) {
|
||||
|
||||
animation.time = 0;
|
||||
animation.directionBackwards = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
animation.time = animation.time % animation.duration;
|
||||
if ( animation.time < 0 ) animation.time += animation.duration;
|
||||
|
||||
}
|
||||
|
||||
const keyframe = animation.start + THREE.MathUtils.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
|
||||
const weight = animation.weight;
|
||||
|
||||
if ( keyframe !== animation.currentFrame ) {
|
||||
|
||||
this.morphTargetInfluences[ animation.lastFrame ] = 0;
|
||||
this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
|
||||
this.morphTargetInfluences[ keyframe ] = 0;
|
||||
animation.lastFrame = animation.currentFrame;
|
||||
animation.currentFrame = keyframe;
|
||||
|
||||
}
|
||||
|
||||
let mix = animation.time % frameTime / frameTime;
|
||||
if ( animation.directionBackwards ) mix = 1 - mix;
|
||||
|
||||
if ( animation.currentFrame !== animation.lastFrame ) {
|
||||
|
||||
this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
|
||||
this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
|
||||
|
||||
} else {
|
||||
|
||||
this.morphTargetInfluences[ animation.currentFrame ] = weight;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.MorphBlendMesh = MorphBlendMesh;
|
||||
|
||||
} )();
|
||||
331
HTML/ThreeJS/node_modules/three/examples/js/misc/ProgressiveLightMap.js
generated
vendored
Normal file
331
HTML/ThreeJS/node_modules/three/examples/js/misc/ProgressiveLightMap.js
generated
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
( function () {
|
||||
|
||||
/**
|
||||
* Progressive Light Map Accumulator, by [zalo](https://github.com/zalo/)
|
||||
*
|
||||
* To use, simply construct a `ProgressiveLightMap` object,
|
||||
* `plmap.addObjectsToLightMap(object)` an array of semi-static
|
||||
* objects and lights to the class once, and then call
|
||||
* `plmap.update(camera)` every frame to begin accumulating
|
||||
* lighting samples.
|
||||
*
|
||||
* This should begin accumulating lightmaps which apply to
|
||||
* your objects, so you can start jittering lighting to achieve
|
||||
* the texture-space effect you're looking for.
|
||||
*
|
||||
* @param {WebGLRenderer} renderer A WebGL Rendering Context
|
||||
* @param {number} res The side-long dimension of you total lightmap
|
||||
*/
|
||||
|
||||
class ProgressiveLightMap {
|
||||
|
||||
constructor( renderer, res = 1024 ) {
|
||||
|
||||
this.renderer = renderer;
|
||||
this.res = res;
|
||||
this.lightMapContainers = [];
|
||||
this.compiled = false;
|
||||
this.scene = new THREE.Scene();
|
||||
this.scene.background = null;
|
||||
this.tinyTarget = new THREE.WebGLRenderTarget( 1, 1 );
|
||||
this.buffer1Active = false;
|
||||
this.firstUpdate = true;
|
||||
this.warned = false; // Create the Progressive LightMap Texture
|
||||
|
||||
const format = /(Android|iPad|iPhone|iPod)/g.test( navigator.userAgent ) ? THREE.HalfFloatType : THREE.FloatType;
|
||||
this.progressiveLightMap1 = new THREE.WebGLRenderTarget( this.res, this.res, {
|
||||
type: format
|
||||
} );
|
||||
this.progressiveLightMap2 = new THREE.WebGLRenderTarget( this.res, this.res, {
|
||||
type: format
|
||||
} ); // Inject some spicy new logic into a standard phong material
|
||||
|
||||
this.uvMat = new THREE.MeshPhongMaterial();
|
||||
this.uvMat.uniforms = {};
|
||||
|
||||
this.uvMat.onBeforeCompile = shader => {
|
||||
|
||||
// Vertex Shader: Set Vertex Positions to the Unwrapped UV Positions
|
||||
shader.vertexShader = '#define USE_LIGHTMAP\n' + shader.vertexShader.slice( 0, - 1 ) + ' gl_Position = vec4((uv2 - 0.5) * 2.0, 1.0, 1.0); }'; // Fragment Shader: Set Pixels to average in the Previous frame's Shadows
|
||||
|
||||
const bodyStart = shader.fragmentShader.indexOf( 'void main() {' );
|
||||
shader.fragmentShader = 'varying vec2 vUv2;\n' + shader.fragmentShader.slice( 0, bodyStart ) + ' uniform sampler2D previousShadowMap;\n uniform float averagingWindow;\n' + shader.fragmentShader.slice( bodyStart - 1, - 1 ) + `\nvec3 texelOld = texture2D(previousShadowMap, vUv2).rgb;
|
||||
gl_FragColor.rgb = mix(texelOld, gl_FragColor.rgb, 1.0/averagingWindow);
|
||||
}`; // Set the Previous Frame's Texture Buffer and Averaging Window
|
||||
|
||||
shader.uniforms.previousShadowMap = {
|
||||
value: this.progressiveLightMap1.texture
|
||||
};
|
||||
shader.uniforms.averagingWindow = {
|
||||
value: 100
|
||||
};
|
||||
this.uvMat.uniforms = shader.uniforms; // Set the new Shader to this
|
||||
|
||||
this.uvMat.userData.shader = shader;
|
||||
this.compiled = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
* Sets these objects' materials' lightmaps and modifies their uv2's.
|
||||
* @param {Object3D} objects An array of objects and lights to set up your lightmap.
|
||||
*/
|
||||
|
||||
|
||||
addObjectsToLightMap( objects ) {
|
||||
|
||||
// Prepare list of UV bounding boxes for packing later...
|
||||
this.uv_boxes = [];
|
||||
const padding = 3 / this.res;
|
||||
|
||||
for ( let ob = 0; ob < objects.length; ob ++ ) {
|
||||
|
||||
const object = objects[ ob ]; // If this object is a light, simply add it to the internal scene
|
||||
|
||||
if ( object.isLight ) {
|
||||
|
||||
this.scene.attach( object );
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if ( ! object.geometry.hasAttribute( 'uv' ) ) {
|
||||
|
||||
console.warn( 'All lightmap objects need UVs!' );
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if ( this.blurringPlane == null ) {
|
||||
|
||||
this._initializeBlurPlane( this.res, this.progressiveLightMap1 );
|
||||
|
||||
} // Apply the lightmap to the object
|
||||
|
||||
|
||||
object.material.lightMap = this.progressiveLightMap2.texture;
|
||||
object.material.dithering = true;
|
||||
object.castShadow = true;
|
||||
object.receiveShadow = true;
|
||||
object.renderOrder = 1000 + ob; // Prepare UV boxes for potpack
|
||||
// TODO: Size these by object surface area
|
||||
|
||||
this.uv_boxes.push( {
|
||||
w: 1 + padding * 2,
|
||||
h: 1 + padding * 2,
|
||||
index: ob
|
||||
} );
|
||||
this.lightMapContainers.push( {
|
||||
basicMat: object.material,
|
||||
object: object
|
||||
} );
|
||||
this.compiled = false;
|
||||
|
||||
} // Pack the objects' lightmap UVs into the same global space
|
||||
|
||||
|
||||
const dimensions = potpack( this.uv_boxes );
|
||||
this.uv_boxes.forEach( box => {
|
||||
|
||||
const uv2 = objects[ box.index ].geometry.getAttribute( 'uv' ).clone();
|
||||
|
||||
for ( let i = 0; i < uv2.array.length; i += uv2.itemSize ) {
|
||||
|
||||
uv2.array[ i ] = ( uv2.array[ i ] + box.x + padding ) / dimensions.w;
|
||||
uv2.array[ i + 1 ] = ( uv2.array[ i + 1 ] + box.y + padding ) / dimensions.h;
|
||||
|
||||
}
|
||||
|
||||
objects[ box.index ].geometry.setAttribute( 'uv2', uv2 );
|
||||
objects[ box.index ].geometry.getAttribute( 'uv2' ).needsUpdate = true;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
/**
|
||||
* This function renders each mesh one at a time into their respective surface maps
|
||||
* @param {Camera} camera Standard Rendering Camera
|
||||
* @param {number} blendWindow When >1, samples will accumulate over time.
|
||||
* @param {boolean} blurEdges Whether to fix UV Edges via blurring
|
||||
*/
|
||||
|
||||
|
||||
update( camera, blendWindow = 100, blurEdges = true ) {
|
||||
|
||||
if ( this.blurringPlane == null ) {
|
||||
|
||||
return;
|
||||
|
||||
} // Store the original Render Target
|
||||
|
||||
|
||||
const oldTarget = this.renderer.getRenderTarget(); // The blurring plane applies blur to the seams of the lightmap
|
||||
|
||||
this.blurringPlane.visible = blurEdges; // Steal the Object3D from the real world to our special dimension
|
||||
|
||||
for ( let l = 0; l < this.lightMapContainers.length; l ++ ) {
|
||||
|
||||
this.lightMapContainers[ l ].object.oldScene = this.lightMapContainers[ l ].object.parent;
|
||||
this.scene.attach( this.lightMapContainers[ l ].object );
|
||||
|
||||
} // Render once normally to initialize everything
|
||||
|
||||
|
||||
if ( this.firstUpdate ) {
|
||||
|
||||
this.renderer.setRenderTarget( this.tinyTarget ); // Tiny for Speed
|
||||
|
||||
this.renderer.render( this.scene, camera );
|
||||
this.firstUpdate = false;
|
||||
|
||||
} // Set each object's material to the UV Unwrapped Surface Mapping Version
|
||||
|
||||
|
||||
for ( let l = 0; l < this.lightMapContainers.length; l ++ ) {
|
||||
|
||||
this.uvMat.uniforms.averagingWindow = {
|
||||
value: blendWindow
|
||||
};
|
||||
this.lightMapContainers[ l ].object.material = this.uvMat;
|
||||
this.lightMapContainers[ l ].object.oldFrustumCulled = this.lightMapContainers[ l ].object.frustumCulled;
|
||||
this.lightMapContainers[ l ].object.frustumCulled = false;
|
||||
|
||||
} // Ping-pong two surface buffers for reading/writing
|
||||
|
||||
|
||||
const activeMap = this.buffer1Active ? this.progressiveLightMap1 : this.progressiveLightMap2;
|
||||
const inactiveMap = this.buffer1Active ? this.progressiveLightMap2 : this.progressiveLightMap1; // Render the object's surface maps
|
||||
|
||||
this.renderer.setRenderTarget( activeMap );
|
||||
this.uvMat.uniforms.previousShadowMap = {
|
||||
value: inactiveMap.texture
|
||||
};
|
||||
this.blurringPlane.material.uniforms.previousShadowMap = {
|
||||
value: inactiveMap.texture
|
||||
};
|
||||
this.buffer1Active = ! this.buffer1Active;
|
||||
this.renderer.render( this.scene, camera ); // Restore the object's Real-time Material and add it back to the original world
|
||||
|
||||
for ( let l = 0; l < this.lightMapContainers.length; l ++ ) {
|
||||
|
||||
this.lightMapContainers[ l ].object.frustumCulled = this.lightMapContainers[ l ].object.oldFrustumCulled;
|
||||
this.lightMapContainers[ l ].object.material = this.lightMapContainers[ l ].basicMat;
|
||||
this.lightMapContainers[ l ].object.oldScene.attach( this.lightMapContainers[ l ].object );
|
||||
|
||||
} // Restore the original Render Target
|
||||
|
||||
|
||||
this.renderer.setRenderTarget( oldTarget );
|
||||
|
||||
}
|
||||
/** DEBUG
|
||||
* Draw the lightmap in the main scene. Call this after adding the objects to it.
|
||||
* @param {boolean} visible Whether the debug plane should be visible
|
||||
* @param {Vector3} position Where the debug plane should be drawn
|
||||
*/
|
||||
|
||||
|
||||
showDebugLightmap( visible, position = undefined ) {
|
||||
|
||||
if ( this.lightMapContainers.length == 0 ) {
|
||||
|
||||
if ( ! this.warned ) {
|
||||
|
||||
console.warn( 'Call this after adding the objects!' );
|
||||
this.warned = true;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if ( this.labelMesh == null ) {
|
||||
|
||||
this.labelMaterial = new THREE.MeshBasicMaterial( {
|
||||
map: this.progressiveLightMap1.texture,
|
||||
side: THREE.DoubleSide
|
||||
} );
|
||||
this.labelPlane = new THREE.PlaneGeometry( 100, 100 );
|
||||
this.labelMesh = new THREE.Mesh( this.labelPlane, this.labelMaterial );
|
||||
this.labelMesh.position.y = 250;
|
||||
this.lightMapContainers[ 0 ].object.parent.add( this.labelMesh );
|
||||
|
||||
}
|
||||
|
||||
if ( position != undefined ) {
|
||||
|
||||
this.labelMesh.position.copy( position );
|
||||
|
||||
}
|
||||
|
||||
this.labelMesh.visible = visible;
|
||||
|
||||
}
|
||||
/**
|
||||
* INTERNAL Creates the Blurring Plane
|
||||
* @param {number} res The square resolution of this object's lightMap.
|
||||
* @param {WebGLRenderTexture} lightMap The lightmap to initialize the plane with.
|
||||
*/
|
||||
|
||||
|
||||
_initializeBlurPlane( res, lightMap = null ) {
|
||||
|
||||
const blurMaterial = new THREE.MeshBasicMaterial();
|
||||
blurMaterial.uniforms = {
|
||||
previousShadowMap: {
|
||||
value: null
|
||||
},
|
||||
pixelOffset: {
|
||||
value: 1.0 / res
|
||||
},
|
||||
polygonOffset: true,
|
||||
polygonOffsetFactor: - 1,
|
||||
polygonOffsetUnits: 3.0
|
||||
};
|
||||
|
||||
blurMaterial.onBeforeCompile = shader => {
|
||||
|
||||
// Vertex Shader: Set Vertex Positions to the Unwrapped UV Positions
|
||||
shader.vertexShader = '#define USE_UV\n' + shader.vertexShader.slice( 0, - 1 ) + ' gl_Position = vec4((uv - 0.5) * 2.0, 1.0, 1.0); }'; // Fragment Shader: Set Pixels to 9-tap box blur the current frame's Shadows
|
||||
|
||||
const bodyStart = shader.fragmentShader.indexOf( 'void main() {' );
|
||||
shader.fragmentShader = '#define USE_UV\n' + shader.fragmentShader.slice( 0, bodyStart ) + ' uniform sampler2D previousShadowMap;\n uniform float pixelOffset;\n' + shader.fragmentShader.slice( bodyStart - 1, - 1 ) + ` gl_FragColor.rgb = (
|
||||
texture2D(previousShadowMap, vUv + vec2( pixelOffset, 0.0 )).rgb +
|
||||
texture2D(previousShadowMap, vUv + vec2( 0.0 , pixelOffset)).rgb +
|
||||
texture2D(previousShadowMap, vUv + vec2( 0.0 , -pixelOffset)).rgb +
|
||||
texture2D(previousShadowMap, vUv + vec2(-pixelOffset, 0.0 )).rgb +
|
||||
texture2D(previousShadowMap, vUv + vec2( pixelOffset, pixelOffset)).rgb +
|
||||
texture2D(previousShadowMap, vUv + vec2(-pixelOffset, pixelOffset)).rgb +
|
||||
texture2D(previousShadowMap, vUv + vec2( pixelOffset, -pixelOffset)).rgb +
|
||||
texture2D(previousShadowMap, vUv + vec2(-pixelOffset, -pixelOffset)).rgb)/8.0;
|
||||
}`; // Set the LightMap Accumulation Buffer
|
||||
|
||||
shader.uniforms.previousShadowMap = {
|
||||
value: lightMap.texture
|
||||
};
|
||||
shader.uniforms.pixelOffset = {
|
||||
value: 0.5 / res
|
||||
};
|
||||
blurMaterial.uniforms = shader.uniforms; // Set the new Shader to this
|
||||
|
||||
blurMaterial.userData.shader = shader;
|
||||
this.compiled = true;
|
||||
|
||||
};
|
||||
|
||||
this.blurringPlane = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), blurMaterial );
|
||||
this.blurringPlane.name = 'Blurring Plane';
|
||||
this.blurringPlane.frustumCulled = false;
|
||||
this.blurringPlane.renderOrder = 0;
|
||||
this.blurringPlane.material.depthWrite = false;
|
||||
this.scene.add( this.blurringPlane );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.ProgressiveLightMap = ProgressiveLightMap;
|
||||
|
||||
} )();
|
||||
437
HTML/ThreeJS/node_modules/three/examples/js/misc/RollerCoaster.js
generated
vendored
Normal file
437
HTML/ThreeJS/node_modules/three/examples/js/misc/RollerCoaster.js
generated
vendored
Normal file
@@ -0,0 +1,437 @@
|
||||
( function () {
|
||||
|
||||
class RollerCoasterGeometry extends THREE.BufferGeometry {
|
||||
|
||||
constructor( curve, divisions ) {
|
||||
|
||||
super();
|
||||
const vertices = [];
|
||||
const normals = [];
|
||||
const colors = [];
|
||||
const color1 = [ 1, 1, 1 ];
|
||||
const color2 = [ 1, 1, 0 ];
|
||||
const up = new THREE.Vector3( 0, 1, 0 );
|
||||
const forward = new THREE.Vector3();
|
||||
const right = new THREE.Vector3();
|
||||
const quaternion = new THREE.Quaternion();
|
||||
const prevQuaternion = new THREE.Quaternion();
|
||||
prevQuaternion.setFromAxisAngle( up, Math.PI / 2 );
|
||||
const point = new THREE.Vector3();
|
||||
const prevPoint = new THREE.Vector3();
|
||||
prevPoint.copy( curve.getPointAt( 0 ) ); // shapes
|
||||
|
||||
const step = [ new THREE.Vector3( - 0.225, 0, 0 ), new THREE.Vector3( 0, - 0.050, 0 ), new THREE.Vector3( 0, - 0.175, 0 ), new THREE.Vector3( 0, - 0.050, 0 ), new THREE.Vector3( 0.225, 0, 0 ), new THREE.Vector3( 0, - 0.175, 0 ) ];
|
||||
const PI2 = Math.PI * 2;
|
||||
let sides = 5;
|
||||
const tube1 = [];
|
||||
|
||||
for ( let i = 0; i < sides; i ++ ) {
|
||||
|
||||
const angle = i / sides * PI2;
|
||||
tube1.push( new THREE.Vector3( Math.sin( angle ) * 0.06, Math.cos( angle ) * 0.06, 0 ) );
|
||||
|
||||
}
|
||||
|
||||
sides = 6;
|
||||
const tube2 = [];
|
||||
|
||||
for ( let i = 0; i < sides; i ++ ) {
|
||||
|
||||
const angle = i / sides * PI2;
|
||||
tube2.push( new THREE.Vector3( Math.sin( angle ) * 0.025, Math.cos( angle ) * 0.025, 0 ) );
|
||||
|
||||
}
|
||||
|
||||
const vector = new THREE.Vector3();
|
||||
const normal = new THREE.Vector3();
|
||||
|
||||
function drawShape( shape, color ) {
|
||||
|
||||
normal.set( 0, 0, - 1 ).applyQuaternion( quaternion );
|
||||
|
||||
for ( let j = 0; j < shape.length; j ++ ) {
|
||||
|
||||
vector.copy( shape[ j ] );
|
||||
vector.applyQuaternion( quaternion );
|
||||
vector.add( point );
|
||||
vertices.push( vector.x, vector.y, vector.z );
|
||||
normals.push( normal.x, normal.y, normal.z );
|
||||
colors.push( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||||
|
||||
}
|
||||
|
||||
normal.set( 0, 0, 1 ).applyQuaternion( quaternion );
|
||||
|
||||
for ( let j = shape.length - 1; j >= 0; j -- ) {
|
||||
|
||||
vector.copy( shape[ j ] );
|
||||
vector.applyQuaternion( quaternion );
|
||||
vector.add( point );
|
||||
vertices.push( vector.x, vector.y, vector.z );
|
||||
normals.push( normal.x, normal.y, normal.z );
|
||||
colors.push( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const vector1 = new THREE.Vector3();
|
||||
const vector2 = new THREE.Vector3();
|
||||
const vector3 = new THREE.Vector3();
|
||||
const vector4 = new THREE.Vector3();
|
||||
const normal1 = new THREE.Vector3();
|
||||
const normal2 = new THREE.Vector3();
|
||||
const normal3 = new THREE.Vector3();
|
||||
const normal4 = new THREE.Vector3();
|
||||
|
||||
function extrudeShape( shape, offset, color ) {
|
||||
|
||||
for ( let j = 0, jl = shape.length; j < jl; j ++ ) {
|
||||
|
||||
const point1 = shape[ j ];
|
||||
const point2 = shape[ ( j + 1 ) % jl ];
|
||||
vector1.copy( point1 ).add( offset );
|
||||
vector1.applyQuaternion( quaternion );
|
||||
vector1.add( point );
|
||||
vector2.copy( point2 ).add( offset );
|
||||
vector2.applyQuaternion( quaternion );
|
||||
vector2.add( point );
|
||||
vector3.copy( point2 ).add( offset );
|
||||
vector3.applyQuaternion( prevQuaternion );
|
||||
vector3.add( prevPoint );
|
||||
vector4.copy( point1 ).add( offset );
|
||||
vector4.applyQuaternion( prevQuaternion );
|
||||
vector4.add( prevPoint );
|
||||
vertices.push( vector1.x, vector1.y, vector1.z );
|
||||
vertices.push( vector2.x, vector2.y, vector2.z );
|
||||
vertices.push( vector4.x, vector4.y, vector4.z );
|
||||
vertices.push( vector2.x, vector2.y, vector2.z );
|
||||
vertices.push( vector3.x, vector3.y, vector3.z );
|
||||
vertices.push( vector4.x, vector4.y, vector4.z ); //
|
||||
|
||||
normal1.copy( point1 );
|
||||
normal1.applyQuaternion( quaternion );
|
||||
normal1.normalize();
|
||||
normal2.copy( point2 );
|
||||
normal2.applyQuaternion( quaternion );
|
||||
normal2.normalize();
|
||||
normal3.copy( point2 );
|
||||
normal3.applyQuaternion( prevQuaternion );
|
||||
normal3.normalize();
|
||||
normal4.copy( point1 );
|
||||
normal4.applyQuaternion( prevQuaternion );
|
||||
normal4.normalize();
|
||||
normals.push( normal1.x, normal1.y, normal1.z );
|
||||
normals.push( normal2.x, normal2.y, normal2.z );
|
||||
normals.push( normal4.x, normal4.y, normal4.z );
|
||||
normals.push( normal2.x, normal2.y, normal2.z );
|
||||
normals.push( normal3.x, normal3.y, normal3.z );
|
||||
normals.push( normal4.x, normal4.y, normal4.z );
|
||||
colors.push( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||||
colors.push( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||||
colors.push( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||||
colors.push( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||||
colors.push( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||||
colors.push( color[ 0 ], color[ 1 ], color[ 2 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const offset = new THREE.Vector3();
|
||||
|
||||
for ( let i = 1; i <= divisions; i ++ ) {
|
||||
|
||||
point.copy( curve.getPointAt( i / divisions ) );
|
||||
up.set( 0, 1, 0 );
|
||||
forward.subVectors( point, prevPoint ).normalize();
|
||||
right.crossVectors( up, forward ).normalize();
|
||||
up.crossVectors( forward, right );
|
||||
const angle = Math.atan2( forward.x, forward.z );
|
||||
quaternion.setFromAxisAngle( up, angle );
|
||||
|
||||
if ( i % 2 === 0 ) {
|
||||
|
||||
drawShape( step, color2 );
|
||||
|
||||
}
|
||||
|
||||
extrudeShape( tube1, offset.set( 0, - 0.125, 0 ), color2 );
|
||||
extrudeShape( tube2, offset.set( 0.2, 0, 0 ), color1 );
|
||||
extrudeShape( tube2, offset.set( - 0.2, 0, 0 ), color1 );
|
||||
prevPoint.copy( point );
|
||||
prevQuaternion.copy( quaternion );
|
||||
|
||||
} // console.log( vertices.length );
|
||||
|
||||
|
||||
this.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
|
||||
this.setAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );
|
||||
this.setAttribute( 'color', new THREE.BufferAttribute( new Float32Array( colors ), 3 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RollerCoasterLiftersGeometry extends THREE.BufferGeometry {
|
||||
|
||||
constructor( curve, divisions ) {
|
||||
|
||||
super();
|
||||
const vertices = [];
|
||||
const normals = [];
|
||||
const quaternion = new THREE.Quaternion();
|
||||
const up = new THREE.Vector3( 0, 1, 0 );
|
||||
const point = new THREE.Vector3();
|
||||
const tangent = new THREE.Vector3(); // shapes
|
||||
|
||||
const tube1 = [ new THREE.Vector3( 0, 0.05, - 0.05 ), new THREE.Vector3( 0, 0.05, 0.05 ), new THREE.Vector3( 0, - 0.05, 0 ) ];
|
||||
const tube2 = [ new THREE.Vector3( - 0.05, 0, 0.05 ), new THREE.Vector3( - 0.05, 0, - 0.05 ), new THREE.Vector3( 0.05, 0, 0 ) ];
|
||||
const tube3 = [ new THREE.Vector3( 0.05, 0, - 0.05 ), new THREE.Vector3( 0.05, 0, 0.05 ), new THREE.Vector3( - 0.05, 0, 0 ) ];
|
||||
const vector1 = new THREE.Vector3();
|
||||
const vector2 = new THREE.Vector3();
|
||||
const vector3 = new THREE.Vector3();
|
||||
const vector4 = new THREE.Vector3();
|
||||
const normal1 = new THREE.Vector3();
|
||||
const normal2 = new THREE.Vector3();
|
||||
const normal3 = new THREE.Vector3();
|
||||
const normal4 = new THREE.Vector3();
|
||||
|
||||
function extrudeShape( shape, fromPoint, toPoint ) {
|
||||
|
||||
for ( let j = 0, jl = shape.length; j < jl; j ++ ) {
|
||||
|
||||
const point1 = shape[ j ];
|
||||
const point2 = shape[ ( j + 1 ) % jl ];
|
||||
vector1.copy( point1 );
|
||||
vector1.applyQuaternion( quaternion );
|
||||
vector1.add( fromPoint );
|
||||
vector2.copy( point2 );
|
||||
vector2.applyQuaternion( quaternion );
|
||||
vector2.add( fromPoint );
|
||||
vector3.copy( point2 );
|
||||
vector3.applyQuaternion( quaternion );
|
||||
vector3.add( toPoint );
|
||||
vector4.copy( point1 );
|
||||
vector4.applyQuaternion( quaternion );
|
||||
vector4.add( toPoint );
|
||||
vertices.push( vector1.x, vector1.y, vector1.z );
|
||||
vertices.push( vector2.x, vector2.y, vector2.z );
|
||||
vertices.push( vector4.x, vector4.y, vector4.z );
|
||||
vertices.push( vector2.x, vector2.y, vector2.z );
|
||||
vertices.push( vector3.x, vector3.y, vector3.z );
|
||||
vertices.push( vector4.x, vector4.y, vector4.z ); //
|
||||
|
||||
normal1.copy( point1 );
|
||||
normal1.applyQuaternion( quaternion );
|
||||
normal1.normalize();
|
||||
normal2.copy( point2 );
|
||||
normal2.applyQuaternion( quaternion );
|
||||
normal2.normalize();
|
||||
normal3.copy( point2 );
|
||||
normal3.applyQuaternion( quaternion );
|
||||
normal3.normalize();
|
||||
normal4.copy( point1 );
|
||||
normal4.applyQuaternion( quaternion );
|
||||
normal4.normalize();
|
||||
normals.push( normal1.x, normal1.y, normal1.z );
|
||||
normals.push( normal2.x, normal2.y, normal2.z );
|
||||
normals.push( normal4.x, normal4.y, normal4.z );
|
||||
normals.push( normal2.x, normal2.y, normal2.z );
|
||||
normals.push( normal3.x, normal3.y, normal3.z );
|
||||
normals.push( normal4.x, normal4.y, normal4.z );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const fromPoint = new THREE.Vector3();
|
||||
const toPoint = new THREE.Vector3();
|
||||
|
||||
for ( let i = 1; i <= divisions; i ++ ) {
|
||||
|
||||
point.copy( curve.getPointAt( i / divisions ) );
|
||||
tangent.copy( curve.getTangentAt( i / divisions ) );
|
||||
const angle = Math.atan2( tangent.x, tangent.z );
|
||||
quaternion.setFromAxisAngle( up, angle ); //
|
||||
|
||||
if ( point.y > 10 ) {
|
||||
|
||||
fromPoint.set( - 0.75, - 0.35, 0 );
|
||||
fromPoint.applyQuaternion( quaternion );
|
||||
fromPoint.add( point );
|
||||
toPoint.set( 0.75, - 0.35, 0 );
|
||||
toPoint.applyQuaternion( quaternion );
|
||||
toPoint.add( point );
|
||||
extrudeShape( tube1, fromPoint, toPoint );
|
||||
fromPoint.set( - 0.7, - 0.3, 0 );
|
||||
fromPoint.applyQuaternion( quaternion );
|
||||
fromPoint.add( point );
|
||||
toPoint.set( - 0.7, - point.y, 0 );
|
||||
toPoint.applyQuaternion( quaternion );
|
||||
toPoint.add( point );
|
||||
extrudeShape( tube2, fromPoint, toPoint );
|
||||
fromPoint.set( 0.7, - 0.3, 0 );
|
||||
fromPoint.applyQuaternion( quaternion );
|
||||
fromPoint.add( point );
|
||||
toPoint.set( 0.7, - point.y, 0 );
|
||||
toPoint.applyQuaternion( quaternion );
|
||||
toPoint.add( point );
|
||||
extrudeShape( tube3, fromPoint, toPoint );
|
||||
|
||||
} else {
|
||||
|
||||
fromPoint.set( 0, - 0.2, 0 );
|
||||
fromPoint.applyQuaternion( quaternion );
|
||||
fromPoint.add( point );
|
||||
toPoint.set( 0, - point.y, 0 );
|
||||
toPoint.applyQuaternion( quaternion );
|
||||
toPoint.add( point );
|
||||
extrudeShape( tube3, fromPoint, toPoint );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
|
||||
this.setAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RollerCoasterShadowGeometry extends THREE.BufferGeometry {
|
||||
|
||||
constructor( curve, divisions ) {
|
||||
|
||||
super();
|
||||
const vertices = [];
|
||||
const up = new THREE.Vector3( 0, 1, 0 );
|
||||
const forward = new THREE.Vector3();
|
||||
const quaternion = new THREE.Quaternion();
|
||||
const prevQuaternion = new THREE.Quaternion();
|
||||
prevQuaternion.setFromAxisAngle( up, Math.PI / 2 );
|
||||
const point = new THREE.Vector3();
|
||||
const prevPoint = new THREE.Vector3();
|
||||
prevPoint.copy( curve.getPointAt( 0 ) );
|
||||
prevPoint.y = 0;
|
||||
const vector1 = new THREE.Vector3();
|
||||
const vector2 = new THREE.Vector3();
|
||||
const vector3 = new THREE.Vector3();
|
||||
const vector4 = new THREE.Vector3();
|
||||
|
||||
for ( let i = 1; i <= divisions; i ++ ) {
|
||||
|
||||
point.copy( curve.getPointAt( i / divisions ) );
|
||||
point.y = 0;
|
||||
forward.subVectors( point, prevPoint );
|
||||
const angle = Math.atan2( forward.x, forward.z );
|
||||
quaternion.setFromAxisAngle( up, angle );
|
||||
vector1.set( - 0.3, 0, 0 );
|
||||
vector1.applyQuaternion( quaternion );
|
||||
vector1.add( point );
|
||||
vector2.set( 0.3, 0, 0 );
|
||||
vector2.applyQuaternion( quaternion );
|
||||
vector2.add( point );
|
||||
vector3.set( 0.3, 0, 0 );
|
||||
vector3.applyQuaternion( prevQuaternion );
|
||||
vector3.add( prevPoint );
|
||||
vector4.set( - 0.3, 0, 0 );
|
||||
vector4.applyQuaternion( prevQuaternion );
|
||||
vector4.add( prevPoint );
|
||||
vertices.push( vector1.x, vector1.y, vector1.z );
|
||||
vertices.push( vector2.x, vector2.y, vector2.z );
|
||||
vertices.push( vector4.x, vector4.y, vector4.z );
|
||||
vertices.push( vector2.x, vector2.y, vector2.z );
|
||||
vertices.push( vector3.x, vector3.y, vector3.z );
|
||||
vertices.push( vector4.x, vector4.y, vector4.z );
|
||||
prevPoint.copy( point );
|
||||
prevQuaternion.copy( quaternion );
|
||||
|
||||
}
|
||||
|
||||
this.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SkyGeometry extends THREE.BufferGeometry {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
const vertices = [];
|
||||
|
||||
for ( let i = 0; i < 100; i ++ ) {
|
||||
|
||||
const x = Math.random() * 800 - 400;
|
||||
const y = Math.random() * 50 + 50;
|
||||
const z = Math.random() * 800 - 400;
|
||||
const size = Math.random() * 40 + 20;
|
||||
vertices.push( x - size, y, z - size );
|
||||
vertices.push( x + size, y, z - size );
|
||||
vertices.push( x - size, y, z + size );
|
||||
vertices.push( x + size, y, z - size );
|
||||
vertices.push( x + size, y, z + size );
|
||||
vertices.push( x - size, y, z + size );
|
||||
|
||||
}
|
||||
|
||||
this.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TreesGeometry extends THREE.BufferGeometry {
|
||||
|
||||
constructor( landscape ) {
|
||||
|
||||
super();
|
||||
const vertices = [];
|
||||
const colors = [];
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.ray.direction.set( 0, - 1, 0 );
|
||||
|
||||
for ( let i = 0; i < 2000; i ++ ) {
|
||||
|
||||
const x = Math.random() * 500 - 250;
|
||||
const z = Math.random() * 500 - 250;
|
||||
raycaster.ray.origin.set( x, 50, z );
|
||||
const intersections = raycaster.intersectObject( landscape );
|
||||
if ( intersections.length === 0 ) continue;
|
||||
const y = intersections[ 0 ].point.y;
|
||||
const height = Math.random() * 5 + 0.5;
|
||||
let angle = Math.random() * Math.PI * 2;
|
||||
vertices.push( x + Math.sin( angle ), y, z + Math.cos( angle ) );
|
||||
vertices.push( x, y + height, z );
|
||||
vertices.push( x + Math.sin( angle + Math.PI ), y, z + Math.cos( angle + Math.PI ) );
|
||||
angle += Math.PI / 2;
|
||||
vertices.push( x + Math.sin( angle ), y, z + Math.cos( angle ) );
|
||||
vertices.push( x, y + height, z );
|
||||
vertices.push( x + Math.sin( angle + Math.PI ), y, z + Math.cos( angle + Math.PI ) );
|
||||
const random = Math.random() * 0.1;
|
||||
|
||||
for ( let j = 0; j < 6; j ++ ) {
|
||||
|
||||
colors.push( 0.2 + random, 0.4 + random, 0 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
|
||||
this.setAttribute( 'color', new THREE.BufferAttribute( new Float32Array( colors ), 3 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
THREE.RollerCoasterGeometry = RollerCoasterGeometry;
|
||||
THREE.RollerCoasterLiftersGeometry = RollerCoasterLiftersGeometry;
|
||||
THREE.RollerCoasterShadowGeometry = RollerCoasterShadowGeometry;
|
||||
THREE.SkyGeometry = SkyGeometry;
|
||||
THREE.TreesGeometry = TreesGeometry;
|
||||
|
||||
} )();
|
||||
161
HTML/ThreeJS/node_modules/three/examples/js/misc/TubePainter.js
generated
vendored
Normal file
161
HTML/ThreeJS/node_modules/three/examples/js/misc/TubePainter.js
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
( function () {
|
||||
|
||||
function TubePainter() {
|
||||
|
||||
const BUFFER_SIZE = 1000000 * 3;
|
||||
const positions = new THREE.BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
|
||||
positions.usage = THREE.DynamicDrawUsage;
|
||||
const normals = new THREE.BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
|
||||
normals.usage = THREE.DynamicDrawUsage;
|
||||
const colors = new THREE.BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
|
||||
colors.usage = THREE.DynamicDrawUsage;
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute( 'position', positions );
|
||||
geometry.setAttribute( 'normal', normals );
|
||||
geometry.setAttribute( 'color', colors );
|
||||
geometry.drawRange.count = 0;
|
||||
const material = new THREE.MeshStandardMaterial( {
|
||||
vertexColors: true
|
||||
} );
|
||||
const mesh = new THREE.Mesh( geometry, material );
|
||||
mesh.frustumCulled = false; //
|
||||
|
||||
function getPoints( size ) {
|
||||
|
||||
const PI2 = Math.PI * 2;
|
||||
const sides = 10;
|
||||
const array = [];
|
||||
const radius = 0.01 * size;
|
||||
|
||||
for ( let i = 0; i < sides; i ++ ) {
|
||||
|
||||
const angle = i / sides * PI2;
|
||||
array.push( new THREE.Vector3( Math.sin( angle ) * radius, Math.cos( angle ) * radius, 0 ) );
|
||||
|
||||
}
|
||||
|
||||
return array;
|
||||
|
||||
} //
|
||||
|
||||
|
||||
const vector1 = new THREE.Vector3();
|
||||
const vector2 = new THREE.Vector3();
|
||||
const vector3 = new THREE.Vector3();
|
||||
const vector4 = new THREE.Vector3();
|
||||
const color = new THREE.Color( 0xffffff );
|
||||
let size = 1;
|
||||
|
||||
function stroke( position1, position2, matrix1, matrix2 ) {
|
||||
|
||||
if ( position1.distanceToSquared( position2 ) === 0 ) return;
|
||||
let count = geometry.drawRange.count;
|
||||
const points = getPoints( size );
|
||||
|
||||
for ( let i = 0, il = points.length; i < il; i ++ ) {
|
||||
|
||||
const vertex1 = points[ i ];
|
||||
const vertex2 = points[ ( i + 1 ) % il ]; // positions
|
||||
|
||||
vector1.copy( vertex1 ).applyMatrix4( matrix2 ).add( position2 );
|
||||
vector2.copy( vertex2 ).applyMatrix4( matrix2 ).add( position2 );
|
||||
vector3.copy( vertex2 ).applyMatrix4( matrix1 ).add( position1 );
|
||||
vector4.copy( vertex1 ).applyMatrix4( matrix1 ).add( position1 );
|
||||
vector1.toArray( positions.array, ( count + 0 ) * 3 );
|
||||
vector2.toArray( positions.array, ( count + 1 ) * 3 );
|
||||
vector4.toArray( positions.array, ( count + 2 ) * 3 );
|
||||
vector2.toArray( positions.array, ( count + 3 ) * 3 );
|
||||
vector3.toArray( positions.array, ( count + 4 ) * 3 );
|
||||
vector4.toArray( positions.array, ( count + 5 ) * 3 ); // normals
|
||||
|
||||
vector1.copy( vertex1 ).applyMatrix4( matrix2 ).normalize();
|
||||
vector2.copy( vertex2 ).applyMatrix4( matrix2 ).normalize();
|
||||
vector3.copy( vertex2 ).applyMatrix4( matrix1 ).normalize();
|
||||
vector4.copy( vertex1 ).applyMatrix4( matrix1 ).normalize();
|
||||
vector1.toArray( normals.array, ( count + 0 ) * 3 );
|
||||
vector2.toArray( normals.array, ( count + 1 ) * 3 );
|
||||
vector4.toArray( normals.array, ( count + 2 ) * 3 );
|
||||
vector2.toArray( normals.array, ( count + 3 ) * 3 );
|
||||
vector3.toArray( normals.array, ( count + 4 ) * 3 );
|
||||
vector4.toArray( normals.array, ( count + 5 ) * 3 ); // colors
|
||||
|
||||
color.toArray( colors.array, ( count + 0 ) * 3 );
|
||||
color.toArray( colors.array, ( count + 1 ) * 3 );
|
||||
color.toArray( colors.array, ( count + 2 ) * 3 );
|
||||
color.toArray( colors.array, ( count + 3 ) * 3 );
|
||||
color.toArray( colors.array, ( count + 4 ) * 3 );
|
||||
color.toArray( colors.array, ( count + 5 ) * 3 );
|
||||
count += 6;
|
||||
|
||||
}
|
||||
|
||||
geometry.drawRange.count = count;
|
||||
|
||||
} //
|
||||
|
||||
|
||||
const up = new THREE.Vector3( 0, 1, 0 );
|
||||
const point1 = new THREE.Vector3();
|
||||
const point2 = new THREE.Vector3();
|
||||
const matrix1 = new THREE.Matrix4();
|
||||
const matrix2 = new THREE.Matrix4();
|
||||
|
||||
function moveTo( position ) {
|
||||
|
||||
point1.copy( position );
|
||||
matrix1.lookAt( point2, point1, up );
|
||||
point2.copy( position );
|
||||
matrix2.copy( matrix1 );
|
||||
|
||||
}
|
||||
|
||||
function lineTo( position ) {
|
||||
|
||||
point1.copy( position );
|
||||
matrix1.lookAt( point2, point1, up );
|
||||
stroke( point1, point2, matrix1, matrix2 );
|
||||
point2.copy( point1 );
|
||||
matrix2.copy( matrix1 );
|
||||
|
||||
}
|
||||
|
||||
function setSize( value ) {
|
||||
|
||||
size = value;
|
||||
|
||||
} //
|
||||
|
||||
|
||||
let count = 0;
|
||||
|
||||
function update() {
|
||||
|
||||
const start = count;
|
||||
const end = geometry.drawRange.count;
|
||||
if ( start === end ) return;
|
||||
positions.updateRange.offset = start * 3;
|
||||
positions.updateRange.count = ( end - start ) * 3;
|
||||
positions.needsUpdate = true;
|
||||
normals.updateRange.offset = start * 3;
|
||||
normals.updateRange.count = ( end - start ) * 3;
|
||||
normals.needsUpdate = true;
|
||||
colors.updateRange.offset = start * 3;
|
||||
colors.updateRange.count = ( end - start ) * 3;
|
||||
colors.needsUpdate = true;
|
||||
count = geometry.drawRange.count;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
mesh: mesh,
|
||||
moveTo: moveTo,
|
||||
lineTo: lineTo,
|
||||
setSize: setSize,
|
||||
update: update
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
THREE.TubePainter = TubePainter;
|
||||
|
||||
} )();
|
||||
458
HTML/ThreeJS/node_modules/three/examples/js/misc/Volume.js
generated
vendored
Normal file
458
HTML/ThreeJS/node_modules/three/examples/js/misc/Volume.js
generated
vendored
Normal file
@@ -0,0 +1,458 @@
|
||||
( function () {
|
||||
|
||||
/**
|
||||
* This class had been written to handle the output of the NRRD loader.
|
||||
* It contains a volume of data and informations about it.
|
||||
* For now it only handles 3 dimensional data.
|
||||
* See the webgl_loader_nrrd.html example and the loaderNRRD.js file to see how to use this class.
|
||||
* @class
|
||||
* @param {number} xLength Width of the volume
|
||||
* @param {number} yLength Length of the volume
|
||||
* @param {number} zLength Depth of the volume
|
||||
* @param {string} type The type of data (uint8, uint16, ...)
|
||||
* @param {ArrayBuffer} arrayBuffer The buffer with volume data
|
||||
*/
|
||||
|
||||
function Volume( xLength, yLength, zLength, type, arrayBuffer ) {
|
||||
|
||||
if ( arguments.length > 0 ) {
|
||||
|
||||
/**
|
||||
* @member {number} xLength Width of the volume in the IJK coordinate system
|
||||
*/
|
||||
this.xLength = Number( xLength ) || 1;
|
||||
/**
|
||||
* @member {number} yLength Height of the volume in the IJK coordinate system
|
||||
*/
|
||||
|
||||
this.yLength = Number( yLength ) || 1;
|
||||
/**
|
||||
* @member {number} zLength Depth of the volume in the IJK coordinate system
|
||||
*/
|
||||
|
||||
this.zLength = Number( zLength ) || 1;
|
||||
/**
|
||||
* @member {Array<string>} The order of the Axis dictated by the NRRD header
|
||||
*/
|
||||
|
||||
this.axisOrder = [ 'x', 'y', 'z' ];
|
||||
/**
|
||||
* @member {TypedArray} data Data of the volume
|
||||
*/
|
||||
|
||||
switch ( type ) {
|
||||
|
||||
case 'Uint8':
|
||||
case 'uint8':
|
||||
case 'uchar':
|
||||
case 'unsigned char':
|
||||
case 'uint8_t':
|
||||
this.data = new Uint8Array( arrayBuffer );
|
||||
break;
|
||||
|
||||
case 'Int8':
|
||||
case 'int8':
|
||||
case 'signed char':
|
||||
case 'int8_t':
|
||||
this.data = new Int8Array( arrayBuffer );
|
||||
break;
|
||||
|
||||
case 'Int16':
|
||||
case 'int16':
|
||||
case 'short':
|
||||
case 'short int':
|
||||
case 'signed short':
|
||||
case 'signed short int':
|
||||
case 'int16_t':
|
||||
this.data = new Int16Array( arrayBuffer );
|
||||
break;
|
||||
|
||||
case 'Uint16':
|
||||
case 'uint16':
|
||||
case 'ushort':
|
||||
case 'unsigned short':
|
||||
case 'unsigned short int':
|
||||
case 'uint16_t':
|
||||
this.data = new Uint16Array( arrayBuffer );
|
||||
break;
|
||||
|
||||
case 'Int32':
|
||||
case 'int32':
|
||||
case 'int':
|
||||
case 'signed int':
|
||||
case 'int32_t':
|
||||
this.data = new Int32Array( arrayBuffer );
|
||||
break;
|
||||
|
||||
case 'Uint32':
|
||||
case 'uint32':
|
||||
case 'uint':
|
||||
case 'unsigned int':
|
||||
case 'uint32_t':
|
||||
this.data = new Uint32Array( arrayBuffer );
|
||||
break;
|
||||
|
||||
case 'longlong':
|
||||
case 'long long':
|
||||
case 'long long int':
|
||||
case 'signed long long':
|
||||
case 'signed long long int':
|
||||
case 'int64':
|
||||
case 'int64_t':
|
||||
case 'ulonglong':
|
||||
case 'unsigned long long':
|
||||
case 'unsigned long long int':
|
||||
case 'uint64':
|
||||
case 'uint64_t':
|
||||
throw new Error( 'Error in Volume constructor : this type is not supported in JavaScript' );
|
||||
break;
|
||||
|
||||
case 'Float32':
|
||||
case 'float32':
|
||||
case 'float':
|
||||
this.data = new Float32Array( arrayBuffer );
|
||||
break;
|
||||
|
||||
case 'Float64':
|
||||
case 'float64':
|
||||
case 'double':
|
||||
this.data = new Float64Array( arrayBuffer );
|
||||
break;
|
||||
|
||||
default:
|
||||
this.data = new Uint8Array( arrayBuffer );
|
||||
|
||||
}
|
||||
|
||||
if ( this.data.length !== this.xLength * this.yLength * this.zLength ) {
|
||||
|
||||
throw new Error( 'Error in Volume constructor, lengths are not matching arrayBuffer size' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* @member {Array} spacing Spacing to apply to the volume from IJK to RAS coordinate system
|
||||
*/
|
||||
|
||||
|
||||
this.spacing = [ 1, 1, 1 ];
|
||||
/**
|
||||
* @member {Array} offset Offset of the volume in the RAS coordinate system
|
||||
*/
|
||||
|
||||
this.offset = [ 0, 0, 0 ];
|
||||
/**
|
||||
* @member {Martrix3} matrix The IJK to RAS matrix
|
||||
*/
|
||||
|
||||
this.matrix = new THREE.Matrix3();
|
||||
this.matrix.identity();
|
||||
/**
|
||||
* @member {Martrix3} inverseMatrix The RAS to IJK matrix
|
||||
*/
|
||||
|
||||
/**
|
||||
* @member {number} lowerThreshold The voxels with values under this threshold won't appear in the slices.
|
||||
* If changed, geometryNeedsUpdate is automatically set to true on all the slices associated to this volume
|
||||
*/
|
||||
|
||||
let lowerThreshold = - Infinity;
|
||||
Object.defineProperty( this, 'lowerThreshold', {
|
||||
get: function () {
|
||||
|
||||
return lowerThreshold;
|
||||
|
||||
},
|
||||
set: function ( value ) {
|
||||
|
||||
lowerThreshold = value;
|
||||
this.sliceList.forEach( function ( slice ) {
|
||||
|
||||
slice.geometryNeedsUpdate = true;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
} );
|
||||
/**
|
||||
* @member {number} upperThreshold The voxels with values over this threshold won't appear in the slices.
|
||||
* If changed, geometryNeedsUpdate is automatically set to true on all the slices associated to this volume
|
||||
*/
|
||||
|
||||
let upperThreshold = Infinity;
|
||||
Object.defineProperty( this, 'upperThreshold', {
|
||||
get: function () {
|
||||
|
||||
return upperThreshold;
|
||||
|
||||
},
|
||||
set: function ( value ) {
|
||||
|
||||
upperThreshold = value;
|
||||
this.sliceList.forEach( function ( slice ) {
|
||||
|
||||
slice.geometryNeedsUpdate = true;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
} );
|
||||
/**
|
||||
* @member {Array} sliceList The list of all the slices associated to this volume
|
||||
*/
|
||||
|
||||
this.sliceList = [];
|
||||
/**
|
||||
* @member {Array} RASDimensions This array holds the dimensions of the volume in the RAS space
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
Volume.prototype = {
|
||||
constructor: Volume,
|
||||
|
||||
/**
|
||||
* @member {Function} getData Shortcut for data[access(i,j,k)]
|
||||
* @memberof Volume
|
||||
* @param {number} i First coordinate
|
||||
* @param {number} j Second coordinate
|
||||
* @param {number} k Third coordinate
|
||||
* @returns {number} value in the data array
|
||||
*/
|
||||
getData: function ( i, j, k ) {
|
||||
|
||||
return this.data[ k * this.xLength * this.yLength + j * this.xLength + i ];
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @member {Function} access compute the index in the data array corresponding to the given coordinates in IJK system
|
||||
* @memberof Volume
|
||||
* @param {number} i First coordinate
|
||||
* @param {number} j Second coordinate
|
||||
* @param {number} k Third coordinate
|
||||
* @returns {number} index
|
||||
*/
|
||||
access: function ( i, j, k ) {
|
||||
|
||||
return k * this.xLength * this.yLength + j * this.xLength + i;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @member {Function} reverseAccess Retrieve the IJK coordinates of the voxel corresponding of the given index in the data
|
||||
* @memberof Volume
|
||||
* @param {number} index index of the voxel
|
||||
* @returns {Array} [x,y,z]
|
||||
*/
|
||||
reverseAccess: function ( index ) {
|
||||
|
||||
const z = Math.floor( index / ( this.yLength * this.xLength ) );
|
||||
const y = Math.floor( ( index - z * this.yLength * this.xLength ) / this.xLength );
|
||||
const x = index - z * this.yLength * this.xLength - y * this.xLength;
|
||||
return [ x, y, z ];
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @member {Function} map Apply a function to all the voxels, be careful, the value will be replaced
|
||||
* @memberof Volume
|
||||
* @param {Function} functionToMap A function to apply to every voxel, will be called with the following parameters :
|
||||
* value of the voxel
|
||||
* index of the voxel
|
||||
* the data (TypedArray)
|
||||
* @param {Object} context You can specify a context in which call the function, default if this Volume
|
||||
* @returns {Volume} this
|
||||
*/
|
||||
map: function ( functionToMap, context ) {
|
||||
|
||||
const length = this.data.length;
|
||||
context = context || this;
|
||||
|
||||
for ( let i = 0; i < length; i ++ ) {
|
||||
|
||||
this.data[ i ] = functionToMap.call( context, this.data[ i ], i, this.data );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @member {Function} extractPerpendicularPlane Compute the orientation of the slice and returns all the information relative to the geometry such as sliceAccess, the plane matrix (orientation and position in RAS coordinate) and the dimensions of the plane in both coordinate system.
|
||||
* @memberof Volume
|
||||
* @param {string} axis the normal axis to the slice 'x' 'y' or 'z'
|
||||
* @param {number} index the index of the slice
|
||||
* @returns {Object} an object containing all the usefull information on the geometry of the slice
|
||||
*/
|
||||
extractPerpendicularPlane: function ( axis, RASIndex ) {
|
||||
|
||||
let firstSpacing, secondSpacing, positionOffset, IJKIndex;
|
||||
const axisInIJK = new THREE.Vector3(),
|
||||
firstDirection = new THREE.Vector3(),
|
||||
secondDirection = new THREE.Vector3(),
|
||||
planeMatrix = new THREE.Matrix4().identity(),
|
||||
volume = this;
|
||||
const dimensions = new THREE.Vector3( this.xLength, this.yLength, this.zLength );
|
||||
|
||||
switch ( axis ) {
|
||||
|
||||
case 'x':
|
||||
axisInIJK.set( 1, 0, 0 );
|
||||
firstDirection.set( 0, 0, - 1 );
|
||||
secondDirection.set( 0, - 1, 0 );
|
||||
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'z' ) ];
|
||||
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'y' ) ];
|
||||
IJKIndex = new THREE.Vector3( RASIndex, 0, 0 );
|
||||
planeMatrix.multiply( new THREE.Matrix4().makeRotationY( Math.PI / 2 ) );
|
||||
positionOffset = ( volume.RASDimensions[ 0 ] - 1 ) / 2;
|
||||
planeMatrix.setPosition( new THREE.Vector3( RASIndex - positionOffset, 0, 0 ) );
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
axisInIJK.set( 0, 1, 0 );
|
||||
firstDirection.set( 1, 0, 0 );
|
||||
secondDirection.set( 0, 0, 1 );
|
||||
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'x' ) ];
|
||||
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'z' ) ];
|
||||
IJKIndex = new THREE.Vector3( 0, RASIndex, 0 );
|
||||
planeMatrix.multiply( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
|
||||
positionOffset = ( volume.RASDimensions[ 1 ] - 1 ) / 2;
|
||||
planeMatrix.setPosition( new THREE.Vector3( 0, RASIndex - positionOffset, 0 ) );
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
default:
|
||||
axisInIJK.set( 0, 0, 1 );
|
||||
firstDirection.set( 1, 0, 0 );
|
||||
secondDirection.set( 0, - 1, 0 );
|
||||
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'x' ) ];
|
||||
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'y' ) ];
|
||||
IJKIndex = new THREE.Vector3( 0, 0, RASIndex );
|
||||
positionOffset = ( volume.RASDimensions[ 2 ] - 1 ) / 2;
|
||||
planeMatrix.setPosition( new THREE.Vector3( 0, 0, RASIndex - positionOffset ) );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
firstDirection.applyMatrix4( volume.inverseMatrix ).normalize();
|
||||
firstDirection.arglet = 'i';
|
||||
secondDirection.applyMatrix4( volume.inverseMatrix ).normalize();
|
||||
secondDirection.arglet = 'j';
|
||||
axisInIJK.applyMatrix4( volume.inverseMatrix ).normalize();
|
||||
const iLength = Math.floor( Math.abs( firstDirection.dot( dimensions ) ) );
|
||||
const jLength = Math.floor( Math.abs( secondDirection.dot( dimensions ) ) );
|
||||
const planeWidth = Math.abs( iLength * firstSpacing );
|
||||
const planeHeight = Math.abs( jLength * secondSpacing );
|
||||
IJKIndex = Math.abs( Math.round( IJKIndex.applyMatrix4( volume.inverseMatrix ).dot( axisInIJK ) ) );
|
||||
const base = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ) ];
|
||||
const iDirection = [ firstDirection, secondDirection, axisInIJK ].find( function ( x ) {
|
||||
|
||||
return Math.abs( x.dot( base[ 0 ] ) ) > 0.9;
|
||||
|
||||
} );
|
||||
const jDirection = [ firstDirection, secondDirection, axisInIJK ].find( function ( x ) {
|
||||
|
||||
return Math.abs( x.dot( base[ 1 ] ) ) > 0.9;
|
||||
|
||||
} );
|
||||
const kDirection = [ firstDirection, secondDirection, axisInIJK ].find( function ( x ) {
|
||||
|
||||
return Math.abs( x.dot( base[ 2 ] ) ) > 0.9;
|
||||
|
||||
} );
|
||||
|
||||
function sliceAccess( i, j ) {
|
||||
|
||||
const si = iDirection === axisInIJK ? IJKIndex : iDirection.arglet === 'i' ? i : j;
|
||||
const sj = jDirection === axisInIJK ? IJKIndex : jDirection.arglet === 'i' ? i : j;
|
||||
const sk = kDirection === axisInIJK ? IJKIndex : kDirection.arglet === 'i' ? i : j; // invert indices if necessary
|
||||
|
||||
const accessI = iDirection.dot( base[ 0 ] ) > 0 ? si : volume.xLength - 1 - si;
|
||||
const accessJ = jDirection.dot( base[ 1 ] ) > 0 ? sj : volume.yLength - 1 - sj;
|
||||
const accessK = kDirection.dot( base[ 2 ] ) > 0 ? sk : volume.zLength - 1 - sk;
|
||||
return volume.access( accessI, accessJ, accessK );
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
iLength: iLength,
|
||||
jLength: jLength,
|
||||
sliceAccess: sliceAccess,
|
||||
matrix: planeMatrix,
|
||||
planeWidth: planeWidth,
|
||||
planeHeight: planeHeight
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @member {Function} extractSlice Returns a slice corresponding to the given axis and index
|
||||
* The coordinate are given in the Right Anterior Superior coordinate format
|
||||
* @memberof Volume
|
||||
* @param {string} axis the normal axis to the slice 'x' 'y' or 'z'
|
||||
* @param {number} index the index of the slice
|
||||
* @returns {VolumeSlice} the extracted slice
|
||||
*/
|
||||
extractSlice: function ( axis, index ) {
|
||||
|
||||
const slice = new THREE.VolumeSlice( this, index, axis );
|
||||
this.sliceList.push( slice );
|
||||
return slice;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @member {Function} repaintAllSlices Call repaint on all the slices extracted from this volume
|
||||
* @see THREE.VolumeSlice.repaint
|
||||
* @memberof Volume
|
||||
* @returns {Volume} this
|
||||
*/
|
||||
repaintAllSlices: function () {
|
||||
|
||||
this.sliceList.forEach( function ( slice ) {
|
||||
|
||||
slice.repaint();
|
||||
|
||||
} );
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @member {Function} computeMinMax Compute the minimum and the maximum of the data in the volume
|
||||
* @memberof Volume
|
||||
* @returns {Array} [min,max]
|
||||
*/
|
||||
computeMinMax: function () {
|
||||
|
||||
let min = Infinity;
|
||||
let max = - Infinity; // buffer the length
|
||||
|
||||
const datasize = this.data.length;
|
||||
let i = 0;
|
||||
|
||||
for ( i = 0; i < datasize; i ++ ) {
|
||||
|
||||
if ( ! isNaN( this.data[ i ] ) ) {
|
||||
|
||||
const value = this.data[ i ];
|
||||
min = Math.min( min, value );
|
||||
max = Math.max( max, value );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
return [ min, max ];
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
THREE.Volume = Volume;
|
||||
|
||||
} )();
|
||||
222
HTML/ThreeJS/node_modules/three/examples/js/misc/VolumeSlice.js
generated
vendored
Normal file
222
HTML/ThreeJS/node_modules/three/examples/js/misc/VolumeSlice.js
generated
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
( function () {
|
||||
|
||||
/**
|
||||
* This class has been made to hold a slice of a volume data
|
||||
* @class
|
||||
* @param {Volume} volume The associated volume
|
||||
* @param {number} [index=0] The index of the slice
|
||||
* @param {string} [axis='z'] For now only 'x', 'y' or 'z' but later it will change to a normal vector
|
||||
* @see Volume
|
||||
*/
|
||||
|
||||
function VolumeSlice( volume, index, axis ) {
|
||||
|
||||
const slice = this;
|
||||
/**
|
||||
* @member {Volume} volume The associated volume
|
||||
*/
|
||||
|
||||
this.volume = volume;
|
||||
/**
|
||||
* @member {Number} index The index of the slice, if changed, will automatically call updateGeometry at the next repaint
|
||||
*/
|
||||
|
||||
index = index || 0;
|
||||
Object.defineProperty( this, 'index', {
|
||||
get: function () {
|
||||
|
||||
return index;
|
||||
|
||||
},
|
||||
set: function ( value ) {
|
||||
|
||||
index = value;
|
||||
slice.geometryNeedsUpdate = true;
|
||||
return index;
|
||||
|
||||
}
|
||||
} );
|
||||
/**
|
||||
* @member {String} axis The normal axis
|
||||
*/
|
||||
|
||||
this.axis = axis || 'z';
|
||||
/**
|
||||
* @member {HTMLCanvasElement} canvas The final canvas used for the texture
|
||||
*/
|
||||
|
||||
/**
|
||||
* @member {CanvasRenderingContext2D} ctx Context of the canvas
|
||||
*/
|
||||
|
||||
this.canvas = document.createElement( 'canvas' );
|
||||
/**
|
||||
* @member {HTMLCanvasElement} canvasBuffer The intermediary canvas used to paint the data
|
||||
*/
|
||||
|
||||
/**
|
||||
* @member {CanvasRenderingContext2D} ctxBuffer Context of the canvas buffer
|
||||
*/
|
||||
|
||||
this.canvasBuffer = document.createElement( 'canvas' );
|
||||
this.updateGeometry();
|
||||
const canvasMap = new THREE.Texture( this.canvas );
|
||||
canvasMap.minFilter = THREE.LinearFilter;
|
||||
canvasMap.wrapS = canvasMap.wrapT = THREE.ClampToEdgeWrapping;
|
||||
const material = new THREE.MeshBasicMaterial( {
|
||||
map: canvasMap,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true
|
||||
} );
|
||||
/**
|
||||
* @member {Mesh} mesh The mesh ready to get used in the scene
|
||||
*/
|
||||
|
||||
this.mesh = new THREE.Mesh( this.geometry, material );
|
||||
this.mesh.matrixAutoUpdate = false;
|
||||
/**
|
||||
* @member {Boolean} geometryNeedsUpdate If set to true, updateGeometry will be triggered at the next repaint
|
||||
*/
|
||||
|
||||
this.geometryNeedsUpdate = true;
|
||||
this.repaint();
|
||||
/**
|
||||
* @member {Number} iLength Width of slice in the original coordinate system, corresponds to the width of the buffer canvas
|
||||
*/
|
||||
|
||||
/**
|
||||
* @member {Number} jLength Height of slice in the original coordinate system, corresponds to the height of the buffer canvas
|
||||
*/
|
||||
|
||||
/**
|
||||
* @member {Function} sliceAccess Function that allow the slice to access right data
|
||||
* @see Volume.extractPerpendicularPlane
|
||||
* @param {Number} i The first coordinate
|
||||
* @param {Number} j The second coordinate
|
||||
* @returns {Number} the index corresponding to the voxel in volume.data of the given position in the slice
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
VolumeSlice.prototype = {
|
||||
constructor: VolumeSlice,
|
||||
|
||||
/**
|
||||
* @member {Function} repaint Refresh the texture and the geometry if geometryNeedsUpdate is set to true
|
||||
* @memberof VolumeSlice
|
||||
*/
|
||||
repaint: function () {
|
||||
|
||||
if ( this.geometryNeedsUpdate ) {
|
||||
|
||||
this.updateGeometry();
|
||||
|
||||
}
|
||||
|
||||
const iLength = this.iLength,
|
||||
jLength = this.jLength,
|
||||
sliceAccess = this.sliceAccess,
|
||||
volume = this.volume,
|
||||
canvas = this.canvasBuffer,
|
||||
ctx = this.ctxBuffer; // get the imageData and pixel array from the canvas
|
||||
|
||||
const imgData = ctx.getImageData( 0, 0, iLength, jLength );
|
||||
const data = imgData.data;
|
||||
const volumeData = volume.data;
|
||||
const upperThreshold = volume.upperThreshold;
|
||||
const lowerThreshold = volume.lowerThreshold;
|
||||
const windowLow = volume.windowLow;
|
||||
const windowHigh = volume.windowHigh; // manipulate some pixel elements
|
||||
|
||||
let pixelCount = 0;
|
||||
|
||||
if ( volume.dataType === 'label' ) {
|
||||
|
||||
//this part is currently useless but will be used when colortables will be handled
|
||||
for ( let j = 0; j < jLength; j ++ ) {
|
||||
|
||||
for ( let i = 0; i < iLength; i ++ ) {
|
||||
|
||||
let label = volumeData[ sliceAccess( i, j ) ];
|
||||
label = label >= this.colorMap.length ? label % this.colorMap.length + 1 : label;
|
||||
const color = this.colorMap[ label ];
|
||||
data[ 4 * pixelCount ] = color >> 24 & 0xff;
|
||||
data[ 4 * pixelCount + 1 ] = color >> 16 & 0xff;
|
||||
data[ 4 * pixelCount + 2 ] = color >> 8 & 0xff;
|
||||
data[ 4 * pixelCount + 3 ] = color & 0xff;
|
||||
pixelCount ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( let j = 0; j < jLength; j ++ ) {
|
||||
|
||||
for ( let i = 0; i < iLength; i ++ ) {
|
||||
|
||||
let value = volumeData[ sliceAccess( i, j ) ];
|
||||
let alpha = 0xff; //apply threshold
|
||||
|
||||
alpha = upperThreshold >= value ? lowerThreshold <= value ? alpha : 0 : 0; //apply window level
|
||||
|
||||
value = Math.floor( 255 * ( value - windowLow ) / ( windowHigh - windowLow ) );
|
||||
value = value > 255 ? 255 : value < 0 ? 0 : value | 0;
|
||||
data[ 4 * pixelCount ] = value;
|
||||
data[ 4 * pixelCount + 1 ] = value;
|
||||
data[ 4 * pixelCount + 2 ] = value;
|
||||
data[ 4 * pixelCount + 3 ] = alpha;
|
||||
pixelCount ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ctx.putImageData( imgData, 0, 0 );
|
||||
this.ctx.drawImage( canvas, 0, 0, iLength, jLength, 0, 0, this.canvas.width, this.canvas.height );
|
||||
this.mesh.material.map.needsUpdate = true;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @member {Function} Refresh the geometry according to axis and index
|
||||
* @see Volume.extractPerpendicularPlane
|
||||
* @memberof VolumeSlice
|
||||
*/
|
||||
updateGeometry: function () {
|
||||
|
||||
const extracted = this.volume.extractPerpendicularPlane( this.axis, this.index );
|
||||
this.sliceAccess = extracted.sliceAccess;
|
||||
this.jLength = extracted.jLength;
|
||||
this.iLength = extracted.iLength;
|
||||
this.matrix = extracted.matrix;
|
||||
this.canvas.width = extracted.planeWidth;
|
||||
this.canvas.height = extracted.planeHeight;
|
||||
this.canvasBuffer.width = this.iLength;
|
||||
this.canvasBuffer.height = this.jLength;
|
||||
this.ctx = this.canvas.getContext( '2d' );
|
||||
this.ctxBuffer = this.canvasBuffer.getContext( '2d' );
|
||||
if ( this.geometry ) this.geometry.dispose(); // dispose existing geometry
|
||||
|
||||
this.geometry = new THREE.PlaneGeometry( extracted.planeWidth, extracted.planeHeight );
|
||||
|
||||
if ( this.mesh ) {
|
||||
|
||||
this.mesh.geometry = this.geometry; //reset mesh matrix
|
||||
|
||||
this.mesh.matrix.identity();
|
||||
this.mesh.applyMatrix4( this.matrix );
|
||||
|
||||
}
|
||||
|
||||
this.geometryNeedsUpdate = false;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
THREE.VolumeSlice = VolumeSlice;
|
||||
|
||||
} )();
|
||||
Reference in New Issue
Block a user