first app vibe
This commit is contained in:
1652
app/node_modules/three/src/objects/BatchedMesh.js
generated
vendored
Normal file
1652
app/node_modules/three/src/objects/BatchedMesh.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
41
app/node_modules/three/src/objects/Bone.js
generated
vendored
Normal file
41
app/node_modules/three/src/objects/Bone.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Object3D } from '../core/Object3D.js';
|
||||
|
||||
/**
|
||||
* A bone which is part of a {@link Skeleton}. The skeleton in turn is used by
|
||||
* the {@link SkinnedMesh}.
|
||||
*
|
||||
* ```js
|
||||
* const root = new THREE.Bone();
|
||||
* const child = new THREE.Bone();
|
||||
*
|
||||
* root.add( child );
|
||||
* child.position.y = 5;
|
||||
* ```
|
||||
*
|
||||
* @augments Object3D
|
||||
*/
|
||||
class Bone extends Object3D {
|
||||
|
||||
/**
|
||||
* Constructs a new bone.
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isBone = true;
|
||||
|
||||
this.type = 'Bone';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Bone };
|
||||
68
app/node_modules/three/src/objects/ClippingGroup.js
generated
vendored
Normal file
68
app/node_modules/three/src/objects/ClippingGroup.js
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Group } from './Group.js';
|
||||
|
||||
/**
|
||||
* In earlier three.js versions, clipping was defined globally
|
||||
* on the renderer or on material level. This special version of
|
||||
* `THREE.Group` allows to encode the clipping state into the scene
|
||||
* graph. Meaning if you create an instance of this group, all
|
||||
* descendant 3D objects will be affected by the respective clipping
|
||||
* planes.
|
||||
*
|
||||
* Note: `ClippingGroup` can only be used with `WebGPURenderer`.
|
||||
*
|
||||
* @augments Group
|
||||
*/
|
||||
class ClippingGroup extends Group {
|
||||
|
||||
/**
|
||||
* Constructs a new clipping group.
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isClippingGroup = true;
|
||||
|
||||
/**
|
||||
* An array with clipping planes.
|
||||
*
|
||||
* @type {Array<Plane>}
|
||||
*/
|
||||
this.clippingPlanes = [];
|
||||
|
||||
/**
|
||||
* Whether clipping should be enabled or not.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.enabled = true;
|
||||
|
||||
/**
|
||||
* Whether the intersection of the clipping planes is used to clip objects, rather than their union.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.clipIntersection = false;
|
||||
|
||||
/**
|
||||
* Whether shadows should be clipped or not.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
this.clipShadows = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ClippingGroup };
|
||||
41
app/node_modules/three/src/objects/Group.js
generated
vendored
Normal file
41
app/node_modules/three/src/objects/Group.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Object3D } from '../core/Object3D.js';
|
||||
|
||||
/**
|
||||
* This is almost identical to an {@link Object3D}. Its purpose is to
|
||||
* make working with groups of objects syntactically clearer.
|
||||
*
|
||||
* ```js
|
||||
* // Create a group and add the two cubes.
|
||||
* // These cubes can now be rotated / scaled etc as a group.
|
||||
* const group = new THREE.Group();
|
||||
*
|
||||
* group.add( meshA );
|
||||
* group.add( meshB );
|
||||
*
|
||||
* scene.add( group );
|
||||
* ```
|
||||
*
|
||||
* @augments Object3D
|
||||
*/
|
||||
class Group extends Object3D {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isGroup = true;
|
||||
|
||||
this.type = 'Group';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Group };
|
||||
395
app/node_modules/three/src/objects/InstancedMesh.js
generated
vendored
Normal file
395
app/node_modules/three/src/objects/InstancedMesh.js
generated
vendored
Normal file
@@ -0,0 +1,395 @@
|
||||
import { InstancedBufferAttribute } from '../core/InstancedBufferAttribute.js';
|
||||
import { Mesh } from './Mesh.js';
|
||||
import { Box3 } from '../math/Box3.js';
|
||||
import { Matrix4 } from '../math/Matrix4.js';
|
||||
import { Sphere } from '../math/Sphere.js';
|
||||
import { DataTexture } from '../textures/DataTexture.js';
|
||||
import { FloatType, RedFormat } from '../constants.js';
|
||||
|
||||
const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4();
|
||||
const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4();
|
||||
|
||||
const _instanceIntersects = [];
|
||||
|
||||
const _box3 = /*@__PURE__*/ new Box3();
|
||||
const _identity = /*@__PURE__*/ new Matrix4();
|
||||
const _mesh = /*@__PURE__*/ new Mesh();
|
||||
const _sphere = /*@__PURE__*/ new Sphere();
|
||||
|
||||
/**
|
||||
* A special version of a mesh with instanced rendering support. Use
|
||||
* this class if you have to render a large number of objects with the same
|
||||
* geometry and material(s) but with different world transformations. The usage
|
||||
* of 'InstancedMesh' will help you to reduce the number of draw calls and thus
|
||||
* improve the overall rendering performance in your application.
|
||||
*
|
||||
* @augments Mesh
|
||||
*/
|
||||
class InstancedMesh extends Mesh {
|
||||
|
||||
/**
|
||||
* Constructs a new instanced mesh.
|
||||
*
|
||||
* @param {BufferGeometry} [geometry] - The mesh geometry.
|
||||
* @param {Material|Array<Material>} [material] - The mesh material.
|
||||
* @param {number} count - The number of instances.
|
||||
*/
|
||||
constructor( geometry, material, count ) {
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isInstancedMesh = true;
|
||||
|
||||
/**
|
||||
* Represents the local transformation of all instances. You have to set its
|
||||
* {@link BufferAttribute#needsUpdate} flag to true if you modify instanced data
|
||||
* via {@link InstancedMesh#setMatrixAt}.
|
||||
*
|
||||
* @type {InstancedBufferAttribute}
|
||||
*/
|
||||
this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 );
|
||||
|
||||
/**
|
||||
* Represents the color of all instances. You have to set its
|
||||
* {@link BufferAttribute#needsUpdate} flag to true if you modify instanced data
|
||||
* via {@link InstancedMesh#setColorAt}.
|
||||
*
|
||||
* @type {?InstancedBufferAttribute}
|
||||
* @default null
|
||||
*/
|
||||
this.instanceColor = null;
|
||||
|
||||
/**
|
||||
* Represents the morph target weights of all instances. You have to set its
|
||||
* {@link Texture#needsUpdate} flag to true if you modify instanced data
|
||||
* via {@link InstancedMesh#setMorphAt}.
|
||||
*
|
||||
* @type {?DataTexture}
|
||||
* @default null
|
||||
*/
|
||||
this.morphTexture = null;
|
||||
|
||||
/**
|
||||
* The number of instances.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
this.count = count;
|
||||
|
||||
/**
|
||||
* The bounding box of the instanced mesh. Can be computed via {@link InstancedMesh#computeBoundingBox}.
|
||||
*
|
||||
* @type {?Box3}
|
||||
* @default null
|
||||
*/
|
||||
this.boundingBox = null;
|
||||
|
||||
/**
|
||||
* The bounding sphere of the instanced mesh. Can be computed via {@link InstancedMesh#computeBoundingSphere}.
|
||||
*
|
||||
* @type {?Sphere}
|
||||
* @default null
|
||||
*/
|
||||
this.boundingSphere = null;
|
||||
|
||||
for ( let i = 0; i < count; i ++ ) {
|
||||
|
||||
this.setMatrixAt( i, _identity );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bounding box of the instanced mesh, and updates {@link InstancedMesh#boundingBox}.
|
||||
* The bounding box is not automatically computed by the engine; this method must be called by your app.
|
||||
* You may need to recompute the bounding box if an instance is transformed via {@link InstancedMesh#setMatrixAt}.
|
||||
*/
|
||||
computeBoundingBox() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
const count = this.count;
|
||||
|
||||
if ( this.boundingBox === null ) {
|
||||
|
||||
this.boundingBox = new Box3();
|
||||
|
||||
}
|
||||
|
||||
if ( geometry.boundingBox === null ) {
|
||||
|
||||
geometry.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
this.boundingBox.makeEmpty();
|
||||
|
||||
for ( let i = 0; i < count; i ++ ) {
|
||||
|
||||
this.getMatrixAt( i, _instanceLocalMatrix );
|
||||
|
||||
_box3.copy( geometry.boundingBox ).applyMatrix4( _instanceLocalMatrix );
|
||||
|
||||
this.boundingBox.union( _box3 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bounding sphere of the instanced mesh, and updates {@link InstancedMesh#boundingSphere}
|
||||
* The engine automatically computes the bounding sphere when it is needed, e.g., for ray casting or view frustum culling.
|
||||
* You may need to recompute the bounding sphere if an instance is transformed via {@link InstancedMesh#setMatrixAt}.
|
||||
*/
|
||||
computeBoundingSphere() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
const count = this.count;
|
||||
|
||||
if ( this.boundingSphere === null ) {
|
||||
|
||||
this.boundingSphere = new Sphere();
|
||||
|
||||
}
|
||||
|
||||
if ( geometry.boundingSphere === null ) {
|
||||
|
||||
geometry.computeBoundingSphere();
|
||||
|
||||
}
|
||||
|
||||
this.boundingSphere.makeEmpty();
|
||||
|
||||
for ( let i = 0; i < count; i ++ ) {
|
||||
|
||||
this.getMatrixAt( i, _instanceLocalMatrix );
|
||||
|
||||
_sphere.copy( geometry.boundingSphere ).applyMatrix4( _instanceLocalMatrix );
|
||||
|
||||
this.boundingSphere.union( _sphere );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
this.instanceMatrix.copy( source.instanceMatrix );
|
||||
|
||||
if ( source.morphTexture !== null ) this.morphTexture = source.morphTexture.clone();
|
||||
if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone();
|
||||
|
||||
this.count = source.count;
|
||||
|
||||
if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone();
|
||||
if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone();
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of the defined instance.
|
||||
*
|
||||
* @param {number} index - The instance index.
|
||||
* @param {Color} color - The target object that is used to store the method's result.
|
||||
*/
|
||||
getColorAt( index, color ) {
|
||||
|
||||
color.fromArray( this.instanceColor.array, index * 3 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local transformation matrix of the defined instance.
|
||||
*
|
||||
* @param {number} index - The instance index.
|
||||
* @param {Matrix4} matrix - The target object that is used to store the method's result.
|
||||
*/
|
||||
getMatrixAt( index, matrix ) {
|
||||
|
||||
matrix.fromArray( this.instanceMatrix.array, index * 16 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the morph target weights of the defined instance.
|
||||
*
|
||||
* @param {number} index - The instance index.
|
||||
* @param {Mesh} object - The target object that is used to store the method's result.
|
||||
*/
|
||||
getMorphAt( index, object ) {
|
||||
|
||||
const objectInfluences = object.morphTargetInfluences;
|
||||
|
||||
const array = this.morphTexture.source.data.data;
|
||||
|
||||
const len = objectInfluences.length + 1; // All influences + the baseInfluenceSum
|
||||
|
||||
const dataIndex = index * len + 1; // Skip the baseInfluenceSum at the beginning
|
||||
|
||||
for ( let i = 0; i < objectInfluences.length; i ++ ) {
|
||||
|
||||
objectInfluences[ i ] = array[ dataIndex + i ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
raycast( raycaster, intersects ) {
|
||||
|
||||
const matrixWorld = this.matrixWorld;
|
||||
const raycastTimes = this.count;
|
||||
|
||||
_mesh.geometry = this.geometry;
|
||||
_mesh.material = this.material;
|
||||
|
||||
if ( _mesh.material === undefined ) return;
|
||||
|
||||
// test with bounding sphere first
|
||||
|
||||
if ( this.boundingSphere === null ) this.computeBoundingSphere();
|
||||
|
||||
_sphere.copy( this.boundingSphere );
|
||||
_sphere.applyMatrix4( matrixWorld );
|
||||
|
||||
if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;
|
||||
|
||||
// now test each instance
|
||||
|
||||
for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) {
|
||||
|
||||
// calculate the world matrix for each instance
|
||||
|
||||
this.getMatrixAt( instanceId, _instanceLocalMatrix );
|
||||
|
||||
_instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix );
|
||||
|
||||
// the mesh represents this single instance
|
||||
|
||||
_mesh.matrixWorld = _instanceWorldMatrix;
|
||||
|
||||
_mesh.raycast( raycaster, _instanceIntersects );
|
||||
|
||||
// process the result of raycast
|
||||
|
||||
for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) {
|
||||
|
||||
const intersect = _instanceIntersects[ i ];
|
||||
intersect.instanceId = instanceId;
|
||||
intersect.object = this;
|
||||
intersects.push( intersect );
|
||||
|
||||
}
|
||||
|
||||
_instanceIntersects.length = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given color to the defined instance. Make sure you set the `needsUpdate` flag of
|
||||
* {@link InstancedMesh#instanceColor} to `true` after updating all the colors.
|
||||
*
|
||||
* @param {number} index - The instance index.
|
||||
* @param {Color} color - The instance color.
|
||||
*/
|
||||
setColorAt( index, color ) {
|
||||
|
||||
if ( this.instanceColor === null ) {
|
||||
|
||||
this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ).fill( 1 ), 3 );
|
||||
|
||||
}
|
||||
|
||||
color.toArray( this.instanceColor.array, index * 3 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given local transformation matrix to the defined instance. Make sure you set the `needsUpdate` flag of
|
||||
* {@link InstancedMesh#instanceMatrix} to `true` after updating all the colors.
|
||||
*
|
||||
* @param {number} index - The instance index.
|
||||
* @param {Matrix4} matrix - The local transformation.
|
||||
*/
|
||||
setMatrixAt( index, matrix ) {
|
||||
|
||||
matrix.toArray( this.instanceMatrix.array, index * 16 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the morph target weights to the defined instance. Make sure you set the `needsUpdate` flag of
|
||||
* {@link InstancedMesh#morphTexture} to `true` after updating all the influences.
|
||||
*
|
||||
* @param {number} index - The instance index.
|
||||
* @param {Mesh} object - A mesh which `morphTargetInfluences` property containing the morph target weights
|
||||
* of a single instance.
|
||||
*/
|
||||
setMorphAt( index, object ) {
|
||||
|
||||
const objectInfluences = object.morphTargetInfluences;
|
||||
|
||||
const len = objectInfluences.length + 1; // morphBaseInfluence + all influences
|
||||
|
||||
if ( this.morphTexture === null ) {
|
||||
|
||||
this.morphTexture = new DataTexture( new Float32Array( len * this.count ), len, this.count, RedFormat, FloatType );
|
||||
|
||||
}
|
||||
|
||||
const array = this.morphTexture.source.data.data;
|
||||
|
||||
let morphInfluencesSum = 0;
|
||||
|
||||
for ( let i = 0; i < objectInfluences.length; i ++ ) {
|
||||
|
||||
morphInfluencesSum += objectInfluences[ i ];
|
||||
|
||||
}
|
||||
|
||||
const morphBaseInfluence = this.geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;
|
||||
|
||||
const dataIndex = len * index;
|
||||
|
||||
array[ dataIndex ] = morphBaseInfluence;
|
||||
|
||||
array.set( objectInfluences, dataIndex + 1 );
|
||||
|
||||
}
|
||||
|
||||
updateMorphTargets() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this
|
||||
* method whenever this instance is no longer used in your app.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this.dispatchEvent( { type: 'dispose' } );
|
||||
|
||||
if ( this.morphTexture !== null ) {
|
||||
|
||||
this.morphTexture.dispose();
|
||||
this.morphTexture = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { InstancedMesh };
|
||||
329
app/node_modules/three/src/objects/LOD.js
generated
vendored
Normal file
329
app/node_modules/three/src/objects/LOD.js
generated
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
import { Vector3 } from '../math/Vector3.js';
|
||||
import { Object3D } from '../core/Object3D.js';
|
||||
|
||||
const _v1 = /*@__PURE__*/ new Vector3();
|
||||
const _v2 = /*@__PURE__*/ new Vector3();
|
||||
|
||||
/**
|
||||
* A component for providing a basic Level of Detail (LOD) mechanism.
|
||||
*
|
||||
* Every LOD level is associated with an object, and rendering can be switched
|
||||
* between them at the distances specified. Typically you would create, say,
|
||||
* three meshes, one for far away (low detail), one for mid range (medium
|
||||
* detail) and one for close up (high detail).
|
||||
*
|
||||
* ```js
|
||||
* const lod = new THREE.LOD();
|
||||
* const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
|
||||
*
|
||||
* //Create spheres with 3 levels of detail and create new LOD levels for them
|
||||
* for( let i = 0; i < 3; i++ ) {
|
||||
*
|
||||
* const geometry = new THREE.IcosahedronGeometry( 10, 3 - i );
|
||||
* const mesh = new THREE.Mesh( geometry, material );
|
||||
* lod.addLevel( mesh, i * 75 );
|
||||
*
|
||||
* }
|
||||
*
|
||||
* scene.add( lod );
|
||||
* ```
|
||||
*
|
||||
* @augments Object3D
|
||||
*/
|
||||
class LOD extends Object3D {
|
||||
|
||||
/**
|
||||
* Constructs a new LOD.
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isLOD = true;
|
||||
|
||||
/**
|
||||
* The current LOD index.
|
||||
*
|
||||
* @private
|
||||
* @type {number}
|
||||
* @default 0
|
||||
*/
|
||||
this._currentLevel = 0;
|
||||
|
||||
this.type = 'LOD';
|
||||
|
||||
Object.defineProperties( this, {
|
||||
/**
|
||||
* This array holds the LOD levels.
|
||||
*
|
||||
* @name LOD#levels
|
||||
* @type {Array<{object:Object3D,distance:number,hysteresis:number}>}
|
||||
*/
|
||||
levels: {
|
||||
enumerable: true,
|
||||
value: []
|
||||
}
|
||||
} );
|
||||
|
||||
/**
|
||||
* Whether the LOD object is updated automatically by the renderer per frame
|
||||
* or not. If set to `false`, you have to call {@link LOD#update} in the
|
||||
* render loop by yourself.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
*/
|
||||
this.autoUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
copy( source ) {
|
||||
|
||||
super.copy( source, false );
|
||||
|
||||
const levels = source.levels;
|
||||
|
||||
for ( let i = 0, l = levels.length; i < l; i ++ ) {
|
||||
|
||||
const level = levels[ i ];
|
||||
|
||||
this.addLevel( level.object.clone(), level.distance, level.hysteresis );
|
||||
|
||||
}
|
||||
|
||||
this.autoUpdate = source.autoUpdate;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mesh that will display at a certain distance and greater. Typically
|
||||
* the further away the distance, the lower the detail on the mesh.
|
||||
*
|
||||
* @param {Object3D} object - The 3D object to display at this level.
|
||||
* @param {number} [distance=0] - The distance at which to display this level of detail.
|
||||
* @param {number} [hysteresis=0] - Threshold used to avoid flickering at LOD boundaries, as a fraction of distance.
|
||||
* @return {LOD} A reference to this instance.
|
||||
*/
|
||||
addLevel( object, distance = 0, hysteresis = 0 ) {
|
||||
|
||||
distance = Math.abs( distance );
|
||||
|
||||
const levels = this.levels;
|
||||
|
||||
let l;
|
||||
|
||||
for ( l = 0; l < levels.length; l ++ ) {
|
||||
|
||||
if ( distance < levels[ l ].distance ) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
levels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } );
|
||||
|
||||
this.add( object );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an existing level, based on the distance from the camera.
|
||||
* Returns `true` when the level has been removed. Otherwise `false`.
|
||||
*
|
||||
* @param {number} distance - Distance of the level to remove.
|
||||
* @return {boolean} Whether the level has been removed or not.
|
||||
*/
|
||||
removeLevel( distance ) {
|
||||
|
||||
const levels = this.levels;
|
||||
|
||||
for ( let i = 0; i < levels.length; i ++ ) {
|
||||
|
||||
if ( levels[ i ].distance === distance ) {
|
||||
|
||||
const removedElements = levels.splice( i, 1 );
|
||||
this.remove( removedElements[ 0 ].object );
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active LOD level index.
|
||||
*
|
||||
* @return {number} The current active LOD level index.
|
||||
*/
|
||||
getCurrentLevel() {
|
||||
|
||||
return this._currentLevel;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the first 3D object that is greater than
|
||||
* the given distance.
|
||||
*
|
||||
* @param {number} distance - The LOD distance.
|
||||
* @return {Object3D|null} The found 3D object. `null` if no 3D object has been found.
|
||||
*/
|
||||
getObjectForDistance( distance ) {
|
||||
|
||||
const levels = this.levels;
|
||||
|
||||
if ( levels.length > 0 ) {
|
||||
|
||||
let i, l;
|
||||
|
||||
for ( i = 1, l = levels.length; i < l; i ++ ) {
|
||||
|
||||
let levelDistance = levels[ i ].distance;
|
||||
|
||||
if ( levels[ i ].object.visible ) {
|
||||
|
||||
levelDistance -= levelDistance * levels[ i ].hysteresis;
|
||||
|
||||
}
|
||||
|
||||
if ( distance < levelDistance ) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return levels[ i - 1 ].object;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes intersection points between a casted ray and this LOD.
|
||||
*
|
||||
* @param {Raycaster} raycaster - The raycaster.
|
||||
* @param {Array<Object>} intersects - The target array that holds the intersection points.
|
||||
*/
|
||||
raycast( raycaster, intersects ) {
|
||||
|
||||
const levels = this.levels;
|
||||
|
||||
if ( levels.length > 0 ) {
|
||||
|
||||
_v1.setFromMatrixPosition( this.matrixWorld );
|
||||
|
||||
const distance = raycaster.ray.origin.distanceTo( _v1 );
|
||||
|
||||
this.getObjectForDistance( distance ).raycast( raycaster, intersects );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the LOD by computing which LOD level should be visible according
|
||||
* to the current distance of the given camera.
|
||||
*
|
||||
* @param {Camera} camera - The camera the scene is rendered with.
|
||||
*/
|
||||
update( camera ) {
|
||||
|
||||
const levels = this.levels;
|
||||
|
||||
if ( levels.length > 1 ) {
|
||||
|
||||
_v1.setFromMatrixPosition( camera.matrixWorld );
|
||||
_v2.setFromMatrixPosition( this.matrixWorld );
|
||||
|
||||
const distance = _v1.distanceTo( _v2 ) / camera.zoom;
|
||||
|
||||
levels[ 0 ].object.visible = true;
|
||||
|
||||
let i, l;
|
||||
|
||||
for ( i = 1, l = levels.length; i < l; i ++ ) {
|
||||
|
||||
let levelDistance = levels[ i ].distance;
|
||||
|
||||
if ( levels[ i ].object.visible ) {
|
||||
|
||||
levelDistance -= levelDistance * levels[ i ].hysteresis;
|
||||
|
||||
}
|
||||
|
||||
if ( distance >= levelDistance ) {
|
||||
|
||||
levels[ i - 1 ].object.visible = false;
|
||||
levels[ i ].object.visible = true;
|
||||
|
||||
} else {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this._currentLevel = i - 1;
|
||||
|
||||
for ( ; i < l; i ++ ) {
|
||||
|
||||
levels[ i ].object.visible = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toJSON( meta ) {
|
||||
|
||||
const data = super.toJSON( meta );
|
||||
|
||||
if ( this.autoUpdate === false ) data.object.autoUpdate = false;
|
||||
|
||||
data.object.levels = [];
|
||||
|
||||
const levels = this.levels;
|
||||
|
||||
for ( let i = 0, l = levels.length; i < l; i ++ ) {
|
||||
|
||||
const level = levels[ i ];
|
||||
|
||||
data.object.levels.push( {
|
||||
object: level.object.uuid,
|
||||
distance: level.distance,
|
||||
hysteresis: level.hysteresis
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export { LOD };
|
||||
328
app/node_modules/three/src/objects/Line.js
generated
vendored
Normal file
328
app/node_modules/three/src/objects/Line.js
generated
vendored
Normal file
@@ -0,0 +1,328 @@
|
||||
import { Sphere } from '../math/Sphere.js';
|
||||
import { Ray } from '../math/Ray.js';
|
||||
import { Matrix4 } from '../math/Matrix4.js';
|
||||
import { Object3D } from '../core/Object3D.js';
|
||||
import { Vector3 } from '../math/Vector3.js';
|
||||
import { LineBasicMaterial } from '../materials/LineBasicMaterial.js';
|
||||
import { BufferGeometry } from '../core/BufferGeometry.js';
|
||||
import { Float32BufferAttribute } from '../core/BufferAttribute.js';
|
||||
|
||||
const _vStart = /*@__PURE__*/ new Vector3();
|
||||
const _vEnd = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _inverseMatrix = /*@__PURE__*/ new Matrix4();
|
||||
const _ray = /*@__PURE__*/ new Ray();
|
||||
const _sphere = /*@__PURE__*/ new Sphere();
|
||||
|
||||
const _intersectPointOnRay = /*@__PURE__*/ new Vector3();
|
||||
const _intersectPointOnSegment = /*@__PURE__*/ new Vector3();
|
||||
|
||||
/**
|
||||
* A continuous line. The line are rendered by connecting consecutive
|
||||
* vertices with straight lines.
|
||||
*
|
||||
* ```js
|
||||
* const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
|
||||
*
|
||||
* const points = [];
|
||||
* points.push( new THREE.Vector3( - 10, 0, 0 ) );
|
||||
* points.push( new THREE.Vector3( 0, 10, 0 ) );
|
||||
* points.push( new THREE.Vector3( 10, 0, 0 ) );
|
||||
*
|
||||
* const geometry = new THREE.BufferGeometry().setFromPoints( points );
|
||||
*
|
||||
* const line = new THREE.Line( geometry, material );
|
||||
* scene.add( line );
|
||||
* ```
|
||||
*
|
||||
* @augments Object3D
|
||||
*/
|
||||
class Line extends Object3D {
|
||||
|
||||
/**
|
||||
* Constructs a new line.
|
||||
*
|
||||
* @param {BufferGeometry} [geometry] - The line geometry.
|
||||
* @param {Material|Array<Material>} [material] - The line material.
|
||||
*/
|
||||
constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isLine = true;
|
||||
|
||||
this.type = 'Line';
|
||||
|
||||
/**
|
||||
* The line geometry.
|
||||
*
|
||||
* @type {BufferGeometry}
|
||||
*/
|
||||
this.geometry = geometry;
|
||||
|
||||
/**
|
||||
* The line material.
|
||||
*
|
||||
* @type {Material|Array<Material>}
|
||||
* @default LineBasicMaterial
|
||||
*/
|
||||
this.material = material;
|
||||
|
||||
/**
|
||||
* A dictionary representing the morph targets in the geometry. The key is the
|
||||
* morph targets name, the value its attribute index. This member is `undefined`
|
||||
* by default and only set when morph targets are detected in the geometry.
|
||||
*
|
||||
* @type {Object<String,number>|undefined}
|
||||
* @default undefined
|
||||
*/
|
||||
this.morphTargetDictionary = undefined;
|
||||
|
||||
/**
|
||||
* An array of weights typically in the range `[0,1]` that specify how much of the morph
|
||||
* is applied. This member is `undefined` by default and only set when morph targets are
|
||||
* detected in the geometry.
|
||||
*
|
||||
* @type {Array<number>|undefined}
|
||||
* @default undefined
|
||||
*/
|
||||
this.morphTargetInfluences = undefined;
|
||||
|
||||
this.updateMorphTargets();
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
this.material = Array.isArray( source.material ) ? source.material.slice() : source.material;
|
||||
this.geometry = source.geometry;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes an array of distance values which are necessary for rendering dashed lines.
|
||||
* For each vertex in the geometry, the method calculates the cumulative length from the
|
||||
* current point to the very beginning of the line.
|
||||
*
|
||||
* @return {Line} A reference to this line.
|
||||
*/
|
||||
computeLineDistances() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
|
||||
// we assume non-indexed geometry
|
||||
|
||||
if ( geometry.index === null ) {
|
||||
|
||||
const positionAttribute = geometry.attributes.position;
|
||||
const lineDistances = [ 0 ];
|
||||
|
||||
for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) {
|
||||
|
||||
_vStart.fromBufferAttribute( positionAttribute, i - 1 );
|
||||
_vEnd.fromBufferAttribute( positionAttribute, i );
|
||||
|
||||
lineDistances[ i ] = lineDistances[ i - 1 ];
|
||||
lineDistances[ i ] += _vStart.distanceTo( _vEnd );
|
||||
|
||||
}
|
||||
|
||||
geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes intersection points between a casted ray and this line.
|
||||
*
|
||||
* @param {Raycaster} raycaster - The raycaster.
|
||||
* @param {Array<Object>} intersects - The target array that holds the intersection points.
|
||||
*/
|
||||
raycast( raycaster, intersects ) {
|
||||
|
||||
const geometry = this.geometry;
|
||||
const matrixWorld = this.matrixWorld;
|
||||
const threshold = raycaster.params.Line.threshold;
|
||||
const drawRange = geometry.drawRange;
|
||||
|
||||
// Checking boundingSphere distance to ray
|
||||
|
||||
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
|
||||
|
||||
_sphere.copy( geometry.boundingSphere );
|
||||
_sphere.applyMatrix4( matrixWorld );
|
||||
_sphere.radius += threshold;
|
||||
|
||||
if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;
|
||||
|
||||
//
|
||||
|
||||
_inverseMatrix.copy( matrixWorld ).invert();
|
||||
_ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );
|
||||
|
||||
const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
|
||||
const localThresholdSq = localThreshold * localThreshold;
|
||||
|
||||
const step = this.isLineSegments ? 2 : 1;
|
||||
|
||||
const index = geometry.index;
|
||||
const attributes = geometry.attributes;
|
||||
const positionAttribute = attributes.position;
|
||||
|
||||
if ( index !== null ) {
|
||||
|
||||
const start = Math.max( 0, drawRange.start );
|
||||
const end = Math.min( index.count, ( drawRange.start + drawRange.count ) );
|
||||
|
||||
for ( let i = start, l = end - 1; i < l; i += step ) {
|
||||
|
||||
const a = index.getX( i );
|
||||
const b = index.getX( i + 1 );
|
||||
|
||||
const intersect = checkIntersection( this, raycaster, _ray, localThresholdSq, a, b, i );
|
||||
|
||||
if ( intersect ) {
|
||||
|
||||
intersects.push( intersect );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( this.isLineLoop ) {
|
||||
|
||||
const a = index.getX( end - 1 );
|
||||
const b = index.getX( start );
|
||||
|
||||
const intersect = checkIntersection( this, raycaster, _ray, localThresholdSq, a, b, end - 1 );
|
||||
|
||||
if ( intersect ) {
|
||||
|
||||
intersects.push( intersect );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const start = Math.max( 0, drawRange.start );
|
||||
const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) );
|
||||
|
||||
for ( let i = start, l = end - 1; i < l; i += step ) {
|
||||
|
||||
const intersect = checkIntersection( this, raycaster, _ray, localThresholdSq, i, i + 1, i );
|
||||
|
||||
if ( intersect ) {
|
||||
|
||||
intersects.push( intersect );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( this.isLineLoop ) {
|
||||
|
||||
const intersect = checkIntersection( this, raycaster, _ray, localThresholdSq, end - 1, start, end - 1 );
|
||||
|
||||
if ( intersect ) {
|
||||
|
||||
intersects.push( intersect );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the values of {@link Line#morphTargetDictionary} and {@link Line#morphTargetInfluences}
|
||||
* to make sure existing morph targets can influence this 3D object.
|
||||
*/
|
||||
updateMorphTargets() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
|
||||
const morphAttributes = geometry.morphAttributes;
|
||||
const keys = Object.keys( morphAttributes );
|
||||
|
||||
if ( keys.length > 0 ) {
|
||||
|
||||
const morphAttribute = morphAttributes[ keys[ 0 ] ];
|
||||
|
||||
if ( morphAttribute !== undefined ) {
|
||||
|
||||
this.morphTargetInfluences = [];
|
||||
this.morphTargetDictionary = {};
|
||||
|
||||
for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
|
||||
|
||||
const name = morphAttribute[ m ].name || String( m );
|
||||
|
||||
this.morphTargetInfluences.push( 0 );
|
||||
this.morphTargetDictionary[ name ] = m;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function checkIntersection( object, raycaster, ray, thresholdSq, a, b, i ) {
|
||||
|
||||
const positionAttribute = object.geometry.attributes.position;
|
||||
|
||||
_vStart.fromBufferAttribute( positionAttribute, a );
|
||||
_vEnd.fromBufferAttribute( positionAttribute, b );
|
||||
|
||||
const distSq = ray.distanceSqToSegment( _vStart, _vEnd, _intersectPointOnRay, _intersectPointOnSegment );
|
||||
|
||||
if ( distSq > thresholdSq ) return;
|
||||
|
||||
_intersectPointOnRay.applyMatrix4( object.matrixWorld ); // Move back to world space for distance calculation
|
||||
|
||||
const distance = raycaster.ray.origin.distanceTo( _intersectPointOnRay );
|
||||
|
||||
if ( distance < raycaster.near || distance > raycaster.far ) return;
|
||||
|
||||
return {
|
||||
|
||||
distance: distance,
|
||||
// What do we want? intersection point on the ray or on the segment??
|
||||
// point: raycaster.ray.at( distance ),
|
||||
point: _intersectPointOnSegment.clone().applyMatrix4( object.matrixWorld ),
|
||||
index: i,
|
||||
face: null,
|
||||
faceIndex: null,
|
||||
barycoord: null,
|
||||
object: object
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export { Line };
|
||||
37
app/node_modules/three/src/objects/LineLoop.js
generated
vendored
Normal file
37
app/node_modules/three/src/objects/LineLoop.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Line } from './Line.js';
|
||||
|
||||
/**
|
||||
* A continuous line. This is nearly the same as {@link Line} the only difference
|
||||
* is that the last vertex is connected with the first vertex in order to close
|
||||
* the line to form a loop.
|
||||
*
|
||||
* @augments Line
|
||||
*/
|
||||
class LineLoop extends Line {
|
||||
|
||||
/**
|
||||
* Constructs a new line loop.
|
||||
*
|
||||
* @param {BufferGeometry} [geometry] - The line geometry.
|
||||
* @param {Material|Array<Material>} [material] - The line material.
|
||||
*/
|
||||
constructor( geometry, material ) {
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isLineLoop = true;
|
||||
|
||||
this.type = 'LineLoop';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { LineLoop };
|
||||
73
app/node_modules/three/src/objects/LineSegments.js
generated
vendored
Normal file
73
app/node_modules/three/src/objects/LineSegments.js
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import { Line } from './Line.js';
|
||||
import { Vector3 } from '../math/Vector3.js';
|
||||
import { Float32BufferAttribute } from '../core/BufferAttribute.js';
|
||||
|
||||
const _start = /*@__PURE__*/ new Vector3();
|
||||
const _end = /*@__PURE__*/ new Vector3();
|
||||
|
||||
/**
|
||||
* A series of lines drawn between pairs of vertices.
|
||||
*
|
||||
* @augments Line
|
||||
*/
|
||||
class LineSegments extends Line {
|
||||
|
||||
/**
|
||||
* Constructs a new line segments.
|
||||
*
|
||||
* @param {BufferGeometry} [geometry] - The line geometry.
|
||||
* @param {Material|Array<Material>} [material] - The line material.
|
||||
*/
|
||||
constructor( geometry, material ) {
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isLineSegments = true;
|
||||
|
||||
this.type = 'LineSegments';
|
||||
|
||||
}
|
||||
|
||||
computeLineDistances() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
|
||||
// we assume non-indexed geometry
|
||||
|
||||
if ( geometry.index === null ) {
|
||||
|
||||
const positionAttribute = geometry.attributes.position;
|
||||
const lineDistances = [];
|
||||
|
||||
for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) {
|
||||
|
||||
_start.fromBufferAttribute( positionAttribute, i );
|
||||
_end.fromBufferAttribute( positionAttribute, i + 1 );
|
||||
|
||||
lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];
|
||||
lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end );
|
||||
|
||||
}
|
||||
|
||||
geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { LineSegments };
|
||||
496
app/node_modules/three/src/objects/Mesh.js
generated
vendored
Normal file
496
app/node_modules/three/src/objects/Mesh.js
generated
vendored
Normal file
@@ -0,0 +1,496 @@
|
||||
import { Vector3 } from '../math/Vector3.js';
|
||||
import { Vector2 } from '../math/Vector2.js';
|
||||
import { Sphere } from '../math/Sphere.js';
|
||||
import { Ray } from '../math/Ray.js';
|
||||
import { Matrix4 } from '../math/Matrix4.js';
|
||||
import { Object3D } from '../core/Object3D.js';
|
||||
import { Triangle } from '../math/Triangle.js';
|
||||
import { BackSide, FrontSide } from '../constants.js';
|
||||
import { MeshBasicMaterial } from '../materials/MeshBasicMaterial.js';
|
||||
import { BufferGeometry } from '../core/BufferGeometry.js';
|
||||
|
||||
const _inverseMatrix = /*@__PURE__*/ new Matrix4();
|
||||
const _ray = /*@__PURE__*/ new Ray();
|
||||
const _sphere = /*@__PURE__*/ new Sphere();
|
||||
const _sphereHitAt = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _vA = /*@__PURE__*/ new Vector3();
|
||||
const _vB = /*@__PURE__*/ new Vector3();
|
||||
const _vC = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _tempA = /*@__PURE__*/ new Vector3();
|
||||
const _morphA = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _intersectionPoint = /*@__PURE__*/ new Vector3();
|
||||
const _intersectionPointWorld = /*@__PURE__*/ new Vector3();
|
||||
|
||||
/**
|
||||
* Class representing triangular polygon mesh based objects.
|
||||
*
|
||||
* ```js
|
||||
* const geometry = new THREE.BoxGeometry( 1, 1, 1 );
|
||||
* const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
|
||||
* const mesh = new THREE.Mesh( geometry, material );
|
||||
* scene.add( mesh );
|
||||
* ```
|
||||
*
|
||||
* @augments Object3D
|
||||
*/
|
||||
class Mesh extends Object3D {
|
||||
|
||||
/**
|
||||
* Constructs a new mesh.
|
||||
*
|
||||
* @param {BufferGeometry} [geometry] - The mesh geometry.
|
||||
* @param {Material|Array<Material>} [material] - The mesh material.
|
||||
*/
|
||||
constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isMesh = true;
|
||||
|
||||
this.type = 'Mesh';
|
||||
|
||||
/**
|
||||
* The mesh geometry.
|
||||
*
|
||||
* @type {BufferGeometry}
|
||||
*/
|
||||
this.geometry = geometry;
|
||||
|
||||
/**
|
||||
* The mesh material.
|
||||
*
|
||||
* @type {Material|Array<Material>}
|
||||
* @default MeshBasicMaterial
|
||||
*/
|
||||
this.material = material;
|
||||
|
||||
/**
|
||||
* A dictionary representing the morph targets in the geometry. The key is the
|
||||
* morph targets name, the value its attribute index. This member is `undefined`
|
||||
* by default and only set when morph targets are detected in the geometry.
|
||||
*
|
||||
* @type {Object<String,number>|undefined}
|
||||
* @default undefined
|
||||
*/
|
||||
this.morphTargetDictionary = undefined;
|
||||
|
||||
/**
|
||||
* An array of weights typically in the range `[0,1]` that specify how much of the morph
|
||||
* is applied. This member is `undefined` by default and only set when morph targets are
|
||||
* detected in the geometry.
|
||||
*
|
||||
* @type {Array<number>|undefined}
|
||||
* @default undefined
|
||||
*/
|
||||
this.morphTargetInfluences = undefined;
|
||||
|
||||
/**
|
||||
* The number of instances of this mesh.
|
||||
* Can only be used with {@link WebGPURenderer}.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
*/
|
||||
this.count = 1;
|
||||
|
||||
this.updateMorphTargets();
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
if ( source.morphTargetInfluences !== undefined ) {
|
||||
|
||||
this.morphTargetInfluences = source.morphTargetInfluences.slice();
|
||||
|
||||
}
|
||||
|
||||
if ( source.morphTargetDictionary !== undefined ) {
|
||||
|
||||
this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );
|
||||
|
||||
}
|
||||
|
||||
this.material = Array.isArray( source.material ) ? source.material.slice() : source.material;
|
||||
this.geometry = source.geometry;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the values of {@link Mesh#morphTargetDictionary} and {@link Mesh#morphTargetInfluences}
|
||||
* to make sure existing morph targets can influence this 3D object.
|
||||
*/
|
||||
updateMorphTargets() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
|
||||
const morphAttributes = geometry.morphAttributes;
|
||||
const keys = Object.keys( morphAttributes );
|
||||
|
||||
if ( keys.length > 0 ) {
|
||||
|
||||
const morphAttribute = morphAttributes[ keys[ 0 ] ];
|
||||
|
||||
if ( morphAttribute !== undefined ) {
|
||||
|
||||
this.morphTargetInfluences = [];
|
||||
this.morphTargetDictionary = {};
|
||||
|
||||
for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
|
||||
|
||||
const name = morphAttribute[ m ].name || String( m );
|
||||
|
||||
this.morphTargetInfluences.push( 0 );
|
||||
this.morphTargetDictionary[ name ] = m;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local-space position of the vertex at the given index, taking into
|
||||
* account the current animation state of both morph targets and skinning.
|
||||
*
|
||||
* @param {number} index - The vertex index.
|
||||
* @param {Vector3} target - The target object that is used to store the method's result.
|
||||
* @return {Vector3} The vertex position in local space.
|
||||
*/
|
||||
getVertexPosition( index, target ) {
|
||||
|
||||
const geometry = this.geometry;
|
||||
const position = geometry.attributes.position;
|
||||
const morphPosition = geometry.morphAttributes.position;
|
||||
const morphTargetsRelative = geometry.morphTargetsRelative;
|
||||
|
||||
target.fromBufferAttribute( position, index );
|
||||
|
||||
const morphInfluences = this.morphTargetInfluences;
|
||||
|
||||
if ( morphPosition && morphInfluences ) {
|
||||
|
||||
_morphA.set( 0, 0, 0 );
|
||||
|
||||
for ( let i = 0, il = morphPosition.length; i < il; i ++ ) {
|
||||
|
||||
const influence = morphInfluences[ i ];
|
||||
const morphAttribute = morphPosition[ i ];
|
||||
|
||||
if ( influence === 0 ) continue;
|
||||
|
||||
_tempA.fromBufferAttribute( morphAttribute, index );
|
||||
|
||||
if ( morphTargetsRelative ) {
|
||||
|
||||
_morphA.addScaledVector( _tempA, influence );
|
||||
|
||||
} else {
|
||||
|
||||
_morphA.addScaledVector( _tempA.sub( target ), influence );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
target.add( _morphA );
|
||||
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes intersection points between a casted ray and this line.
|
||||
*
|
||||
* @param {Raycaster} raycaster - The raycaster.
|
||||
* @param {Array<Object>} intersects - The target array that holds the intersection points.
|
||||
*/
|
||||
raycast( raycaster, intersects ) {
|
||||
|
||||
const geometry = this.geometry;
|
||||
const material = this.material;
|
||||
const matrixWorld = this.matrixWorld;
|
||||
|
||||
if ( material === undefined ) return;
|
||||
|
||||
// test with bounding sphere in world space
|
||||
|
||||
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
|
||||
|
||||
_sphere.copy( geometry.boundingSphere );
|
||||
_sphere.applyMatrix4( matrixWorld );
|
||||
|
||||
// check distance from ray origin to bounding sphere
|
||||
|
||||
_ray.copy( raycaster.ray ).recast( raycaster.near );
|
||||
|
||||
if ( _sphere.containsPoint( _ray.origin ) === false ) {
|
||||
|
||||
if ( _ray.intersectSphere( _sphere, _sphereHitAt ) === null ) return;
|
||||
|
||||
if ( _ray.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return;
|
||||
|
||||
}
|
||||
|
||||
// convert ray to local space of mesh
|
||||
|
||||
_inverseMatrix.copy( matrixWorld ).invert();
|
||||
_ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );
|
||||
|
||||
// test with bounding box in local space
|
||||
|
||||
if ( geometry.boundingBox !== null ) {
|
||||
|
||||
if ( _ray.intersectsBox( geometry.boundingBox ) === false ) return;
|
||||
|
||||
}
|
||||
|
||||
// test for intersections with geometry
|
||||
|
||||
this._computeIntersections( raycaster, intersects, _ray );
|
||||
|
||||
}
|
||||
|
||||
_computeIntersections( raycaster, intersects, rayLocalSpace ) {
|
||||
|
||||
let intersection;
|
||||
|
||||
const geometry = this.geometry;
|
||||
const material = this.material;
|
||||
|
||||
const index = geometry.index;
|
||||
const position = geometry.attributes.position;
|
||||
const uv = geometry.attributes.uv;
|
||||
const uv1 = geometry.attributes.uv1;
|
||||
const normal = geometry.attributes.normal;
|
||||
const groups = geometry.groups;
|
||||
const drawRange = geometry.drawRange;
|
||||
|
||||
if ( index !== null ) {
|
||||
|
||||
// indexed buffer geometry
|
||||
|
||||
if ( Array.isArray( material ) ) {
|
||||
|
||||
for ( let i = 0, il = groups.length; i < il; i ++ ) {
|
||||
|
||||
const group = groups[ i ];
|
||||
const groupMaterial = material[ group.materialIndex ];
|
||||
|
||||
const start = Math.max( group.start, drawRange.start );
|
||||
const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) );
|
||||
|
||||
for ( let j = start, jl = end; j < jl; j += 3 ) {
|
||||
|
||||
const a = index.getX( j );
|
||||
const b = index.getX( j + 1 );
|
||||
const c = index.getX( j + 2 );
|
||||
|
||||
intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );
|
||||
|
||||
if ( intersection ) {
|
||||
|
||||
intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics
|
||||
intersection.face.materialIndex = group.materialIndex;
|
||||
intersects.push( intersection );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const start = Math.max( 0, drawRange.start );
|
||||
const end = Math.min( index.count, ( drawRange.start + drawRange.count ) );
|
||||
|
||||
for ( let i = start, il = end; i < il; i += 3 ) {
|
||||
|
||||
const a = index.getX( i );
|
||||
const b = index.getX( i + 1 );
|
||||
const c = index.getX( i + 2 );
|
||||
|
||||
intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );
|
||||
|
||||
if ( intersection ) {
|
||||
|
||||
intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics
|
||||
intersects.push( intersection );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if ( position !== undefined ) {
|
||||
|
||||
// non-indexed buffer geometry
|
||||
|
||||
if ( Array.isArray( material ) ) {
|
||||
|
||||
for ( let i = 0, il = groups.length; i < il; i ++ ) {
|
||||
|
||||
const group = groups[ i ];
|
||||
const groupMaterial = material[ group.materialIndex ];
|
||||
|
||||
const start = Math.max( group.start, drawRange.start );
|
||||
const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) );
|
||||
|
||||
for ( let j = start, jl = end; j < jl; j += 3 ) {
|
||||
|
||||
const a = j;
|
||||
const b = j + 1;
|
||||
const c = j + 2;
|
||||
|
||||
intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );
|
||||
|
||||
if ( intersection ) {
|
||||
|
||||
intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics
|
||||
intersection.face.materialIndex = group.materialIndex;
|
||||
intersects.push( intersection );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const start = Math.max( 0, drawRange.start );
|
||||
const end = Math.min( position.count, ( drawRange.start + drawRange.count ) );
|
||||
|
||||
for ( let i = start, il = end; i < il; i += 3 ) {
|
||||
|
||||
const a = i;
|
||||
const b = i + 1;
|
||||
const c = i + 2;
|
||||
|
||||
intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c );
|
||||
|
||||
if ( intersection ) {
|
||||
|
||||
intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics
|
||||
intersects.push( intersection );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {
|
||||
|
||||
let intersect;
|
||||
|
||||
if ( material.side === BackSide ) {
|
||||
|
||||
intersect = ray.intersectTriangle( pC, pB, pA, true, point );
|
||||
|
||||
} else {
|
||||
|
||||
intersect = ray.intersectTriangle( pA, pB, pC, ( material.side === FrontSide ), point );
|
||||
|
||||
}
|
||||
|
||||
if ( intersect === null ) return null;
|
||||
|
||||
_intersectionPointWorld.copy( point );
|
||||
_intersectionPointWorld.applyMatrix4( object.matrixWorld );
|
||||
|
||||
const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld );
|
||||
|
||||
if ( distance < raycaster.near || distance > raycaster.far ) return null;
|
||||
|
||||
return {
|
||||
distance: distance,
|
||||
point: _intersectionPointWorld.clone(),
|
||||
object: object
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c ) {
|
||||
|
||||
object.getVertexPosition( a, _vA );
|
||||
object.getVertexPosition( b, _vB );
|
||||
object.getVertexPosition( c, _vC );
|
||||
|
||||
const intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint );
|
||||
|
||||
if ( intersection ) {
|
||||
|
||||
const barycoord = new Vector3();
|
||||
Triangle.getBarycoord( _intersectionPoint, _vA, _vB, _vC, barycoord );
|
||||
|
||||
if ( uv ) {
|
||||
|
||||
intersection.uv = Triangle.getInterpolatedAttribute( uv, a, b, c, barycoord, new Vector2() );
|
||||
|
||||
}
|
||||
|
||||
if ( uv1 ) {
|
||||
|
||||
intersection.uv1 = Triangle.getInterpolatedAttribute( uv1, a, b, c, barycoord, new Vector2() );
|
||||
|
||||
}
|
||||
|
||||
if ( normal ) {
|
||||
|
||||
intersection.normal = Triangle.getInterpolatedAttribute( normal, a, b, c, barycoord, new Vector3() );
|
||||
|
||||
if ( intersection.normal.dot( ray.direction ) > 0 ) {
|
||||
|
||||
intersection.normal.multiplyScalar( - 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const face = {
|
||||
a: a,
|
||||
b: b,
|
||||
c: c,
|
||||
normal: new Vector3(),
|
||||
materialIndex: 0
|
||||
};
|
||||
|
||||
Triangle.getNormal( _vA, _vB, _vC, face.normal );
|
||||
|
||||
intersection.face = face;
|
||||
intersection.barycoord = barycoord;
|
||||
|
||||
}
|
||||
|
||||
return intersection;
|
||||
|
||||
}
|
||||
|
||||
export { Mesh };
|
||||
228
app/node_modules/three/src/objects/Points.js
generated
vendored
Normal file
228
app/node_modules/three/src/objects/Points.js
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
import { Sphere } from '../math/Sphere.js';
|
||||
import { Ray } from '../math/Ray.js';
|
||||
import { Matrix4 } from '../math/Matrix4.js';
|
||||
import { Object3D } from '../core/Object3D.js';
|
||||
import { Vector3 } from '../math/Vector3.js';
|
||||
import { PointsMaterial } from '../materials/PointsMaterial.js';
|
||||
import { BufferGeometry } from '../core/BufferGeometry.js';
|
||||
|
||||
const _inverseMatrix = /*@__PURE__*/ new Matrix4();
|
||||
const _ray = /*@__PURE__*/ new Ray();
|
||||
const _sphere = /*@__PURE__*/ new Sphere();
|
||||
const _position = /*@__PURE__*/ new Vector3();
|
||||
|
||||
/**
|
||||
* A class for displaying points or point clouds.
|
||||
*
|
||||
* @augments Object3D
|
||||
*/
|
||||
class Points extends Object3D {
|
||||
|
||||
/**
|
||||
* Constructs a new point cloud.
|
||||
*
|
||||
* @param {BufferGeometry} [geometry] - The points geometry.
|
||||
* @param {Material|Array<Material>} [material] - The points material.
|
||||
*/
|
||||
constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isPoints = true;
|
||||
|
||||
this.type = 'Points';
|
||||
|
||||
/**
|
||||
* The points geometry.
|
||||
*
|
||||
* @type {BufferGeometry}
|
||||
*/
|
||||
this.geometry = geometry;
|
||||
|
||||
/**
|
||||
* The line material.
|
||||
*
|
||||
* @type {Material|Array<Material>}
|
||||
* @default PointsMaterial
|
||||
*/
|
||||
this.material = material;
|
||||
|
||||
/**
|
||||
* A dictionary representing the morph targets in the geometry. The key is the
|
||||
* morph targets name, the value its attribute index. This member is `undefined`
|
||||
* by default and only set when morph targets are detected in the geometry.
|
||||
*
|
||||
* @type {Object<String,number>|undefined}
|
||||
* @default undefined
|
||||
*/
|
||||
this.morphTargetDictionary = undefined;
|
||||
|
||||
/**
|
||||
* An array of weights typically in the range `[0,1]` that specify how much of the morph
|
||||
* is applied. This member is `undefined` by default and only set when morph targets are
|
||||
* detected in the geometry.
|
||||
*
|
||||
* @type {Array<number>|undefined}
|
||||
* @default undefined
|
||||
*/
|
||||
this.morphTargetInfluences = undefined;
|
||||
|
||||
this.updateMorphTargets();
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
this.material = Array.isArray( source.material ) ? source.material.slice() : source.material;
|
||||
this.geometry = source.geometry;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes intersection points between a casted ray and this point cloud.
|
||||
*
|
||||
* @param {Raycaster} raycaster - The raycaster.
|
||||
* @param {Array<Object>} intersects - The target array that holds the intersection points.
|
||||
*/
|
||||
raycast( raycaster, intersects ) {
|
||||
|
||||
const geometry = this.geometry;
|
||||
const matrixWorld = this.matrixWorld;
|
||||
const threshold = raycaster.params.Points.threshold;
|
||||
const drawRange = geometry.drawRange;
|
||||
|
||||
// Checking boundingSphere distance to ray
|
||||
|
||||
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
|
||||
|
||||
_sphere.copy( geometry.boundingSphere );
|
||||
_sphere.applyMatrix4( matrixWorld );
|
||||
_sphere.radius += threshold;
|
||||
|
||||
if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;
|
||||
|
||||
//
|
||||
|
||||
_inverseMatrix.copy( matrixWorld ).invert();
|
||||
_ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );
|
||||
|
||||
const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
|
||||
const localThresholdSq = localThreshold * localThreshold;
|
||||
|
||||
const index = geometry.index;
|
||||
const attributes = geometry.attributes;
|
||||
const positionAttribute = attributes.position;
|
||||
|
||||
if ( index !== null ) {
|
||||
|
||||
const start = Math.max( 0, drawRange.start );
|
||||
const end = Math.min( index.count, ( drawRange.start + drawRange.count ) );
|
||||
|
||||
for ( let i = start, il = end; i < il; i ++ ) {
|
||||
|
||||
const a = index.getX( i );
|
||||
|
||||
_position.fromBufferAttribute( positionAttribute, a );
|
||||
|
||||
testPoint( _position, a, localThresholdSq, matrixWorld, raycaster, intersects, this );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const start = Math.max( 0, drawRange.start );
|
||||
const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) );
|
||||
|
||||
for ( let i = start, l = end; i < l; i ++ ) {
|
||||
|
||||
_position.fromBufferAttribute( positionAttribute, i );
|
||||
|
||||
testPoint( _position, i, localThresholdSq, matrixWorld, raycaster, intersects, this );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the values of {@link Points#morphTargetDictionary} and {@link Points#morphTargetInfluences}
|
||||
* to make sure existing morph targets can influence this 3D object.
|
||||
*/
|
||||
updateMorphTargets() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
|
||||
const morphAttributes = geometry.morphAttributes;
|
||||
const keys = Object.keys( morphAttributes );
|
||||
|
||||
if ( keys.length > 0 ) {
|
||||
|
||||
const morphAttribute = morphAttributes[ keys[ 0 ] ];
|
||||
|
||||
if ( morphAttribute !== undefined ) {
|
||||
|
||||
this.morphTargetInfluences = [];
|
||||
this.morphTargetDictionary = {};
|
||||
|
||||
for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
|
||||
|
||||
const name = morphAttribute[ m ].name || String( m );
|
||||
|
||||
this.morphTargetInfluences.push( 0 );
|
||||
this.morphTargetDictionary[ name ] = m;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) {
|
||||
|
||||
const rayPointDistanceSq = _ray.distanceSqToPoint( point );
|
||||
|
||||
if ( rayPointDistanceSq < localThresholdSq ) {
|
||||
|
||||
const intersectPoint = new Vector3();
|
||||
|
||||
_ray.closestPointToPoint( point, intersectPoint );
|
||||
intersectPoint.applyMatrix4( matrixWorld );
|
||||
|
||||
const distance = raycaster.ray.origin.distanceTo( intersectPoint );
|
||||
|
||||
if ( distance < raycaster.near || distance > raycaster.far ) return;
|
||||
|
||||
intersects.push( {
|
||||
|
||||
distance: distance,
|
||||
distanceToRay: Math.sqrt( rayPointDistanceSq ),
|
||||
point: intersectPoint,
|
||||
index: index,
|
||||
face: null,
|
||||
faceIndex: null,
|
||||
barycoord: null,
|
||||
object: object
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Points };
|
||||
382
app/node_modules/three/src/objects/Skeleton.js
generated
vendored
Normal file
382
app/node_modules/three/src/objects/Skeleton.js
generated
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
import {
|
||||
RGBAFormat,
|
||||
FloatType
|
||||
} from '../constants.js';
|
||||
import { Bone } from './Bone.js';
|
||||
import { Matrix4 } from '../math/Matrix4.js';
|
||||
import { DataTexture } from '../textures/DataTexture.js';
|
||||
import { generateUUID } from '../math/MathUtils.js';
|
||||
|
||||
const _offsetMatrix = /*@__PURE__*/ new Matrix4();
|
||||
const _identityMatrix = /*@__PURE__*/ new Matrix4();
|
||||
|
||||
/**
|
||||
* Class for representing the armatures in `three.js`. The skeleton
|
||||
* is defined by a hierarchy of bones.
|
||||
*
|
||||
* ```js
|
||||
* const bones = [];
|
||||
*
|
||||
* const shoulder = new THREE.Bone();
|
||||
* const elbow = new THREE.Bone();
|
||||
* const hand = new THREE.Bone();
|
||||
*
|
||||
* shoulder.add( elbow );
|
||||
* elbow.add( hand );
|
||||
*
|
||||
* bones.push( shoulder , elbow, hand);
|
||||
*
|
||||
* shoulder.position.y = -5;
|
||||
* elbow.position.y = 0;
|
||||
* hand.position.y = 5;
|
||||
*
|
||||
* const armSkeleton = new THREE.Skeleton( bones );
|
||||
* ```
|
||||
*/
|
||||
class Skeleton {
|
||||
|
||||
/**
|
||||
* Constructs a new skeleton.
|
||||
*
|
||||
* @param {Array<Bone>} [bones] - An array of bones.
|
||||
* @param {Array<Matrix4>} [boneInverses] - An array of bone inverse matrices.
|
||||
* If not provided, these matrices will be computed automatically via {@link Skeleton#calculateInverses}.
|
||||
*/
|
||||
constructor( bones = [], boneInverses = [] ) {
|
||||
|
||||
this.uuid = generateUUID();
|
||||
|
||||
/**
|
||||
* An array of bones defining the skeleton.
|
||||
*
|
||||
* @type {Array<Bone>}
|
||||
*/
|
||||
this.bones = bones.slice( 0 );
|
||||
|
||||
/**
|
||||
* An array of bone inverse matrices.
|
||||
*
|
||||
* @type {Array<Matrix4>}
|
||||
*/
|
||||
this.boneInverses = boneInverses;
|
||||
|
||||
/**
|
||||
* An array buffer holding the bone data.
|
||||
* Input data for {@link Skeleton#boneTexture}.
|
||||
*
|
||||
* @type {?Float32Array}
|
||||
* @default null
|
||||
*/
|
||||
this.boneMatrices = null;
|
||||
|
||||
/**
|
||||
* A texture holding the bone data for use
|
||||
* in the vertex shader.
|
||||
*
|
||||
* @type {?DataTexture}
|
||||
* @default null
|
||||
*/
|
||||
this.boneTexture = null;
|
||||
|
||||
this.init();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the skeleton. This method gets automatically called by the constructor
|
||||
* but depending on how the skeleton is created it might be necessary to call this method
|
||||
* manually.
|
||||
*/
|
||||
init() {
|
||||
|
||||
const bones = this.bones;
|
||||
const boneInverses = this.boneInverses;
|
||||
|
||||
this.boneMatrices = new Float32Array( bones.length * 16 );
|
||||
|
||||
// calculate inverse bone matrices if necessary
|
||||
|
||||
if ( boneInverses.length === 0 ) {
|
||||
|
||||
this.calculateInverses();
|
||||
|
||||
} else {
|
||||
|
||||
// handle special case
|
||||
|
||||
if ( bones.length !== boneInverses.length ) {
|
||||
|
||||
console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' );
|
||||
|
||||
this.boneInverses = [];
|
||||
|
||||
for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
|
||||
|
||||
this.boneInverses.push( new Matrix4() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bone inverse matrices. This method resets {@link Skeleton#boneInverses}
|
||||
* and fills it with new matrices.
|
||||
*/
|
||||
calculateInverses() {
|
||||
|
||||
this.boneInverses.length = 0;
|
||||
|
||||
for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
|
||||
|
||||
const inverse = new Matrix4();
|
||||
|
||||
if ( this.bones[ i ] ) {
|
||||
|
||||
inverse.copy( this.bones[ i ].matrixWorld ).invert();
|
||||
|
||||
}
|
||||
|
||||
this.boneInverses.push( inverse );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the skeleton to the base pose.
|
||||
*/
|
||||
pose() {
|
||||
|
||||
// recover the bind-time world matrices
|
||||
|
||||
for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
|
||||
|
||||
const bone = this.bones[ i ];
|
||||
|
||||
if ( bone ) {
|
||||
|
||||
bone.matrixWorld.copy( this.boneInverses[ i ] ).invert();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// compute the local matrices, positions, rotations and scales
|
||||
|
||||
for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
|
||||
|
||||
const bone = this.bones[ i ];
|
||||
|
||||
if ( bone ) {
|
||||
|
||||
if ( bone.parent && bone.parent.isBone ) {
|
||||
|
||||
bone.matrix.copy( bone.parent.matrixWorld ).invert();
|
||||
bone.matrix.multiply( bone.matrixWorld );
|
||||
|
||||
} else {
|
||||
|
||||
bone.matrix.copy( bone.matrixWorld );
|
||||
|
||||
}
|
||||
|
||||
bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the skeleton to the base pose.
|
||||
*/
|
||||
update() {
|
||||
|
||||
const bones = this.bones;
|
||||
const boneInverses = this.boneInverses;
|
||||
const boneMatrices = this.boneMatrices;
|
||||
const boneTexture = this.boneTexture;
|
||||
|
||||
// flatten bone matrices to array
|
||||
|
||||
for ( let i = 0, il = bones.length; i < il; i ++ ) {
|
||||
|
||||
// compute the offset between the current and the original transform
|
||||
|
||||
const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix;
|
||||
|
||||
_offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] );
|
||||
_offsetMatrix.toArray( boneMatrices, i * 16 );
|
||||
|
||||
}
|
||||
|
||||
if ( boneTexture !== null ) {
|
||||
|
||||
boneTexture.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new skeleton with copied values from this instance.
|
||||
*
|
||||
* @return {Skeleton} A clone of this instance.
|
||||
*/
|
||||
clone() {
|
||||
|
||||
return new Skeleton( this.bones, this.boneInverses );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a data texture for passing bone data to the vertex shader.
|
||||
*
|
||||
* @return {Skeleton} A reference of this instance.
|
||||
*/
|
||||
computeBoneTexture() {
|
||||
|
||||
// layout (1 matrix = 4 pixels)
|
||||
// RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
|
||||
// with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8)
|
||||
// 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16)
|
||||
// 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32)
|
||||
// 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
|
||||
|
||||
let size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix
|
||||
size = Math.ceil( size / 4 ) * 4;
|
||||
size = Math.max( size, 4 );
|
||||
|
||||
const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel
|
||||
boneMatrices.set( this.boneMatrices ); // copy current values
|
||||
|
||||
const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );
|
||||
boneTexture.needsUpdate = true;
|
||||
|
||||
this.boneMatrices = boneMatrices;
|
||||
this.boneTexture = boneTexture;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches through the skeleton's bone array and returns the first with a
|
||||
* matching name.
|
||||
*
|
||||
* @param {string} name - The name of the bone.
|
||||
* @return {Bone|undefined} The found bone. `undefined` if no bone has been found.
|
||||
*/
|
||||
getBoneByName( name ) {
|
||||
|
||||
for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
|
||||
|
||||
const bone = this.bones[ i ];
|
||||
|
||||
if ( bone.name === name ) {
|
||||
|
||||
return bone;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this
|
||||
* method whenever this instance is no longer used in your app.
|
||||
*/
|
||||
dispose( ) {
|
||||
|
||||
if ( this.boneTexture !== null ) {
|
||||
|
||||
this.boneTexture.dispose();
|
||||
|
||||
this.boneTexture = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups the skeleton by the given JSON and bones.
|
||||
*
|
||||
* @param {Object} json - The skeleton as serialized JSON.
|
||||
* @param {Object<string, Bone>} bones - An array of bones.
|
||||
* @return {Skeleton} A reference of this instance.
|
||||
*/
|
||||
fromJSON( json, bones ) {
|
||||
|
||||
this.uuid = json.uuid;
|
||||
|
||||
for ( let i = 0, l = json.bones.length; i < l; i ++ ) {
|
||||
|
||||
const uuid = json.bones[ i ];
|
||||
let bone = bones[ uuid ];
|
||||
|
||||
if ( bone === undefined ) {
|
||||
|
||||
console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid );
|
||||
bone = new Bone();
|
||||
|
||||
}
|
||||
|
||||
this.bones.push( bone );
|
||||
this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) );
|
||||
|
||||
}
|
||||
|
||||
this.init();
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the skeleton into JSON.
|
||||
*
|
||||
* @return {Object} A JSON object representing the serialized skeleton.
|
||||
* @see {@link ObjectLoader#parse}
|
||||
*/
|
||||
toJSON() {
|
||||
|
||||
const data = {
|
||||
metadata: {
|
||||
version: 4.7,
|
||||
type: 'Skeleton',
|
||||
generator: 'Skeleton.toJSON'
|
||||
},
|
||||
bones: [],
|
||||
boneInverses: []
|
||||
};
|
||||
|
||||
data.uuid = this.uuid;
|
||||
|
||||
const bones = this.bones;
|
||||
const boneInverses = this.boneInverses;
|
||||
|
||||
for ( let i = 0, l = bones.length; i < l; i ++ ) {
|
||||
|
||||
const bone = bones[ i ];
|
||||
data.bones.push( bone.uuid );
|
||||
|
||||
const boneInverse = boneInverses[ i ];
|
||||
data.boneInverses.push( boneInverse.toArray() );
|
||||
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Skeleton };
|
||||
351
app/node_modules/three/src/objects/SkinnedMesh.js
generated
vendored
Normal file
351
app/node_modules/three/src/objects/SkinnedMesh.js
generated
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
import { Mesh } from './Mesh.js';
|
||||
import { Box3 } from '../math/Box3.js';
|
||||
import { Matrix4 } from '../math/Matrix4.js';
|
||||
import { Sphere } from '../math/Sphere.js';
|
||||
import { Vector3 } from '../math/Vector3.js';
|
||||
import { Vector4 } from '../math/Vector4.js';
|
||||
import { Ray } from '../math/Ray.js';
|
||||
import { AttachedBindMode, DetachedBindMode } from '../constants.js';
|
||||
|
||||
const _basePosition = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _skinIndex = /*@__PURE__*/ new Vector4();
|
||||
const _skinWeight = /*@__PURE__*/ new Vector4();
|
||||
|
||||
const _vector3 = /*@__PURE__*/ new Vector3();
|
||||
const _matrix4 = /*@__PURE__*/ new Matrix4();
|
||||
const _vertex = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _sphere = /*@__PURE__*/ new Sphere();
|
||||
const _inverseMatrix = /*@__PURE__*/ new Matrix4();
|
||||
const _ray = /*@__PURE__*/ new Ray();
|
||||
|
||||
/**
|
||||
* A mesh that has a {@link Skeleton} that can then be used to animate the
|
||||
* vertices of the geometry with skinning/skeleton animation.
|
||||
*
|
||||
* Next to a valid skeleton, the skinned mesh requires skin indices and weights
|
||||
* as buffer attributes in its geometry. These attribute define which bones affect a single
|
||||
* vertex to a certain extend.
|
||||
*
|
||||
* Typically skinned meshes are not created manually but loaders like {@link GLTFLoader}
|
||||
* or {@link FBXLoader } import respective models.
|
||||
*
|
||||
* @augments Mesh
|
||||
*/
|
||||
class SkinnedMesh extends Mesh {
|
||||
|
||||
/**
|
||||
* Constructs a new skinned mesh.
|
||||
*
|
||||
* @param {BufferGeometry} [geometry] - The mesh geometry.
|
||||
* @param {Material|Array<Material>} [material] - The mesh material.
|
||||
*/
|
||||
constructor( geometry, material ) {
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isSkinnedMesh = true;
|
||||
|
||||
this.type = 'SkinnedMesh';
|
||||
|
||||
/**
|
||||
* `AttachedBindMode` means the skinned mesh shares the same world space as the skeleton.
|
||||
* This is not true when using `DetachedBindMode` which is useful when sharing a skeleton
|
||||
* across multiple skinned meshes.
|
||||
*
|
||||
* @type {(AttachedBindMode|DetachedBindMode)}
|
||||
* @default AttachedBindMode
|
||||
*/
|
||||
this.bindMode = AttachedBindMode;
|
||||
|
||||
/**
|
||||
* The base matrix that is used for the bound bone transforms.
|
||||
*
|
||||
* @type {Matrix4}
|
||||
*/
|
||||
this.bindMatrix = new Matrix4();
|
||||
|
||||
/**
|
||||
* The base matrix that is used for resetting the bound bone transforms.
|
||||
*
|
||||
* @type {Matrix4}
|
||||
*/
|
||||
this.bindMatrixInverse = new Matrix4();
|
||||
|
||||
/**
|
||||
* The bounding box of the skinned mesh. Can be computed via {@link SkinnedMesh#computeBoundingBox}.
|
||||
*
|
||||
* @type {?Box3}
|
||||
* @default null
|
||||
*/
|
||||
this.boundingBox = null;
|
||||
|
||||
/**
|
||||
* The bounding sphere of the skinned mesh. Can be computed via {@link SkinnedMesh#computeBoundingSphere}.
|
||||
*
|
||||
* @type {?Sphere}
|
||||
* @default null
|
||||
*/
|
||||
this.boundingSphere = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bounding box of the skinned mesh, and updates {@link SkinnedMesh#boundingBox}.
|
||||
* The bounding box is not automatically computed by the engine; this method must be called by your app.
|
||||
* If the skinned mesh is animated, the bounding box should be recomputed per frame in order to reflect
|
||||
* the current animation state.
|
||||
*/
|
||||
computeBoundingBox() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
|
||||
if ( this.boundingBox === null ) {
|
||||
|
||||
this.boundingBox = new Box3();
|
||||
|
||||
}
|
||||
|
||||
this.boundingBox.makeEmpty();
|
||||
|
||||
const positionAttribute = geometry.getAttribute( 'position' );
|
||||
|
||||
for ( let i = 0; i < positionAttribute.count; i ++ ) {
|
||||
|
||||
this.getVertexPosition( i, _vertex );
|
||||
this.boundingBox.expandByPoint( _vertex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the bounding sphere of the skinned mesh, and updates {@link SkinnedMesh#boundingSphere}.
|
||||
* The bounding sphere is automatically computed by the engine once when it is needed, e.g., for ray casting
|
||||
* and view frustum culling. If the skinned mesh is animated, the bounding sphere should be recomputed
|
||||
* per frame in order to reflect the current animation state.
|
||||
*/
|
||||
computeBoundingSphere() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
|
||||
if ( this.boundingSphere === null ) {
|
||||
|
||||
this.boundingSphere = new Sphere();
|
||||
|
||||
}
|
||||
|
||||
this.boundingSphere.makeEmpty();
|
||||
|
||||
const positionAttribute = geometry.getAttribute( 'position' );
|
||||
|
||||
for ( let i = 0; i < positionAttribute.count; i ++ ) {
|
||||
|
||||
this.getVertexPosition( i, _vertex );
|
||||
this.boundingSphere.expandByPoint( _vertex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
this.bindMode = source.bindMode;
|
||||
this.bindMatrix.copy( source.bindMatrix );
|
||||
this.bindMatrixInverse.copy( source.bindMatrixInverse );
|
||||
|
||||
this.skeleton = source.skeleton;
|
||||
|
||||
if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone();
|
||||
if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone();
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
raycast( raycaster, intersects ) {
|
||||
|
||||
const material = this.material;
|
||||
const matrixWorld = this.matrixWorld;
|
||||
|
||||
if ( material === undefined ) return;
|
||||
|
||||
// test with bounding sphere in world space
|
||||
|
||||
if ( this.boundingSphere === null ) this.computeBoundingSphere();
|
||||
|
||||
_sphere.copy( this.boundingSphere );
|
||||
_sphere.applyMatrix4( matrixWorld );
|
||||
|
||||
if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;
|
||||
|
||||
// convert ray to local space of skinned mesh
|
||||
|
||||
_inverseMatrix.copy( matrixWorld ).invert();
|
||||
_ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );
|
||||
|
||||
// test with bounding box in local space
|
||||
|
||||
if ( this.boundingBox !== null ) {
|
||||
|
||||
if ( _ray.intersectsBox( this.boundingBox ) === false ) return;
|
||||
|
||||
}
|
||||
|
||||
// test for intersections with geometry
|
||||
|
||||
this._computeIntersections( raycaster, intersects, _ray );
|
||||
|
||||
}
|
||||
|
||||
getVertexPosition( index, target ) {
|
||||
|
||||
super.getVertexPosition( index, target );
|
||||
|
||||
this.applyBoneTransform( index, target );
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the given skeleton to the skinned mesh.
|
||||
*
|
||||
* @param {Skeleton} skeleton - The skeleton to bind.
|
||||
* @param {Matrix4} [bindMatrix] - The bind matrix. If no bind matrix is provided,
|
||||
* the skinned mesh's world matrix will be used instead.
|
||||
*/
|
||||
bind( skeleton, bindMatrix ) {
|
||||
|
||||
this.skeleton = skeleton;
|
||||
|
||||
if ( bindMatrix === undefined ) {
|
||||
|
||||
this.updateMatrixWorld( true );
|
||||
|
||||
this.skeleton.calculateInverses();
|
||||
|
||||
bindMatrix = this.matrixWorld;
|
||||
|
||||
}
|
||||
|
||||
this.bindMatrix.copy( bindMatrix );
|
||||
this.bindMatrixInverse.copy( bindMatrix ).invert();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the skinned mesh in the rest pose).
|
||||
*/
|
||||
pose() {
|
||||
|
||||
this.skeleton.pose();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the skin weights which are defined as a buffer attribute
|
||||
* in the skinned mesh's geometry.
|
||||
*/
|
||||
normalizeSkinWeights() {
|
||||
|
||||
const vector = new Vector4();
|
||||
|
||||
const skinWeight = this.geometry.attributes.skinWeight;
|
||||
|
||||
for ( let i = 0, l = skinWeight.count; i < l; i ++ ) {
|
||||
|
||||
vector.fromBufferAttribute( skinWeight, i );
|
||||
|
||||
const scale = 1.0 / vector.manhattanLength();
|
||||
|
||||
if ( scale !== Infinity ) {
|
||||
|
||||
vector.multiplyScalar( scale );
|
||||
|
||||
} else {
|
||||
|
||||
vector.set( 1, 0, 0, 0 ); // do something reasonable
|
||||
|
||||
}
|
||||
|
||||
skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateMatrixWorld( force ) {
|
||||
|
||||
super.updateMatrixWorld( force );
|
||||
|
||||
if ( this.bindMode === AttachedBindMode ) {
|
||||
|
||||
this.bindMatrixInverse.copy( this.matrixWorld ).invert();
|
||||
|
||||
} else if ( this.bindMode === DetachedBindMode ) {
|
||||
|
||||
this.bindMatrixInverse.copy( this.bindMatrix ).invert();
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the bone transform associated with the given index to the given
|
||||
* vertex position. Returns the updated vector.
|
||||
*
|
||||
* @param {number} index - The vertex index.
|
||||
* @param {Vector3} target - The target object that is used to store the method's result.
|
||||
* the skinned mesh's world matrix will be used instead.
|
||||
* @return {Vector3} The updated vertex position.
|
||||
*/
|
||||
applyBoneTransform( index, target ) {
|
||||
|
||||
const skeleton = this.skeleton;
|
||||
const geometry = this.geometry;
|
||||
|
||||
_skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index );
|
||||
_skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index );
|
||||
|
||||
_basePosition.copy( target ).applyMatrix4( this.bindMatrix );
|
||||
|
||||
target.set( 0, 0, 0 );
|
||||
|
||||
for ( let i = 0; i < 4; i ++ ) {
|
||||
|
||||
const weight = _skinWeight.getComponent( i );
|
||||
|
||||
if ( weight !== 0 ) {
|
||||
|
||||
const boneIndex = _skinIndex.getComponent( i );
|
||||
|
||||
_matrix4.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] );
|
||||
|
||||
target.addScaledVector( _vector3.copy( _basePosition ).applyMatrix4( _matrix4 ), weight );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return target.applyMatrix4( this.bindMatrixInverse );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { SkinnedMesh };
|
||||
244
app/node_modules/three/src/objects/Sprite.js
generated
vendored
Normal file
244
app/node_modules/three/src/objects/Sprite.js
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
import { Vector2 } from '../math/Vector2.js';
|
||||
import { Vector3 } from '../math/Vector3.js';
|
||||
import { Matrix4 } from '../math/Matrix4.js';
|
||||
import { Triangle } from '../math/Triangle.js';
|
||||
import { Object3D } from '../core/Object3D.js';
|
||||
import { BufferGeometry } from '../core/BufferGeometry.js';
|
||||
import { InterleavedBuffer } from '../core/InterleavedBuffer.js';
|
||||
import { InterleavedBufferAttribute } from '../core/InterleavedBufferAttribute.js';
|
||||
import { SpriteMaterial } from '../materials/SpriteMaterial.js';
|
||||
|
||||
let _geometry;
|
||||
|
||||
const _intersectPoint = /*@__PURE__*/ new Vector3();
|
||||
const _worldScale = /*@__PURE__*/ new Vector3();
|
||||
const _mvPosition = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _alignedPosition = /*@__PURE__*/ new Vector2();
|
||||
const _rotatedPosition = /*@__PURE__*/ new Vector2();
|
||||
const _viewWorldMatrix = /*@__PURE__*/ new Matrix4();
|
||||
|
||||
const _vA = /*@__PURE__*/ new Vector3();
|
||||
const _vB = /*@__PURE__*/ new Vector3();
|
||||
const _vC = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _uvA = /*@__PURE__*/ new Vector2();
|
||||
const _uvB = /*@__PURE__*/ new Vector2();
|
||||
const _uvC = /*@__PURE__*/ new Vector2();
|
||||
|
||||
/**
|
||||
* A sprite is a plane that always faces towards the camera, generally with a
|
||||
* partially transparent texture applied.
|
||||
*
|
||||
* Sprites do not cast shadows, setting {@link Object3D#castShadow} to `true` will
|
||||
* have no effect.
|
||||
*
|
||||
* ```js
|
||||
* const map = new THREE.TextureLoader().load( 'sprite.png' );
|
||||
* const material = new THREE.SpriteMaterial( { map: map } );
|
||||
*
|
||||
* const sprite = new THREE.Sprite( material );
|
||||
* scene.add( sprite );
|
||||
* ```
|
||||
*
|
||||
* @augments Object3D
|
||||
*/
|
||||
class Sprite extends Object3D {
|
||||
|
||||
/**
|
||||
* Constructs a new sprite.
|
||||
*
|
||||
* @param {SpriteMaterial} [material] - The sprite material.
|
||||
*/
|
||||
constructor( material = new SpriteMaterial() ) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* This flag can be used for type testing.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
* @default true
|
||||
*/
|
||||
this.isSprite = true;
|
||||
|
||||
this.type = 'Sprite';
|
||||
|
||||
if ( _geometry === undefined ) {
|
||||
|
||||
_geometry = new BufferGeometry();
|
||||
|
||||
const float32Array = new Float32Array( [
|
||||
- 0.5, - 0.5, 0, 0, 0,
|
||||
0.5, - 0.5, 0, 1, 0,
|
||||
0.5, 0.5, 0, 1, 1,
|
||||
- 0.5, 0.5, 0, 0, 1
|
||||
] );
|
||||
|
||||
const interleavedBuffer = new InterleavedBuffer( float32Array, 5 );
|
||||
|
||||
_geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] );
|
||||
_geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) );
|
||||
_geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The sprite geometry.
|
||||
*
|
||||
* @type {BufferGeometry}
|
||||
*/
|
||||
this.geometry = _geometry;
|
||||
|
||||
/**
|
||||
* The sprite material.
|
||||
*
|
||||
* @type {SpriteMaterial}
|
||||
*/
|
||||
this.material = material;
|
||||
|
||||
/**
|
||||
* The sprite's anchor point, and the point around which the sprite rotates.
|
||||
* A value of `(0.5, 0.5)` corresponds to the midpoint of the sprite. A value
|
||||
* of `(0, 0)` corresponds to the lower left corner of the sprite.
|
||||
*
|
||||
* @type {Vector2}
|
||||
* @default (0.5,0.5)
|
||||
*/
|
||||
this.center = new Vector2( 0.5, 0.5 );
|
||||
|
||||
/**
|
||||
* The number of instances of this sprite.
|
||||
* Can only be used with {@link WebGPURenderer}.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
*/
|
||||
this.count = 1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes intersection points between a casted ray and this sprite.
|
||||
*
|
||||
* @param {Raycaster} raycaster - The raycaster.
|
||||
* @param {Array<Object>} intersects - The target array that holds the intersection points.
|
||||
*/
|
||||
raycast( raycaster, intersects ) {
|
||||
|
||||
if ( raycaster.camera === null ) {
|
||||
|
||||
console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' );
|
||||
|
||||
}
|
||||
|
||||
_worldScale.setFromMatrixScale( this.matrixWorld );
|
||||
|
||||
_viewWorldMatrix.copy( raycaster.camera.matrixWorld );
|
||||
this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld );
|
||||
|
||||
_mvPosition.setFromMatrixPosition( this.modelViewMatrix );
|
||||
|
||||
if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) {
|
||||
|
||||
_worldScale.multiplyScalar( - _mvPosition.z );
|
||||
|
||||
}
|
||||
|
||||
const rotation = this.material.rotation;
|
||||
let sin, cos;
|
||||
|
||||
if ( rotation !== 0 ) {
|
||||
|
||||
cos = Math.cos( rotation );
|
||||
sin = Math.sin( rotation );
|
||||
|
||||
}
|
||||
|
||||
const center = this.center;
|
||||
|
||||
transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );
|
||||
transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );
|
||||
transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );
|
||||
|
||||
_uvA.set( 0, 0 );
|
||||
_uvB.set( 1, 0 );
|
||||
_uvC.set( 1, 1 );
|
||||
|
||||
// check first triangle
|
||||
let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint );
|
||||
|
||||
if ( intersect === null ) {
|
||||
|
||||
// check second triangle
|
||||
transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );
|
||||
_uvB.set( 0, 1 );
|
||||
|
||||
intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint );
|
||||
if ( intersect === null ) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const distance = raycaster.ray.origin.distanceTo( _intersectPoint );
|
||||
|
||||
if ( distance < raycaster.near || distance > raycaster.far ) return;
|
||||
|
||||
intersects.push( {
|
||||
|
||||
distance: distance,
|
||||
point: _intersectPoint.clone(),
|
||||
uv: Triangle.getInterpolation( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ),
|
||||
face: null,
|
||||
object: this
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
copy( source, recursive ) {
|
||||
|
||||
super.copy( source, recursive );
|
||||
|
||||
if ( source.center !== undefined ) this.center.copy( source.center );
|
||||
|
||||
this.material = source.material;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) {
|
||||
|
||||
// compute position in camera space
|
||||
_alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale );
|
||||
|
||||
// to check if rotation is not zero
|
||||
if ( sin !== undefined ) {
|
||||
|
||||
_rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y );
|
||||
_rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y );
|
||||
|
||||
} else {
|
||||
|
||||
_rotatedPosition.copy( _alignedPosition );
|
||||
|
||||
}
|
||||
|
||||
|
||||
vertexPosition.copy( mvPosition );
|
||||
vertexPosition.x += _rotatedPosition.x;
|
||||
vertexPosition.y += _rotatedPosition.y;
|
||||
|
||||
// transform to world space
|
||||
vertexPosition.applyMatrix4( _viewWorldMatrix );
|
||||
|
||||
}
|
||||
|
||||
export { Sprite };
|
||||
Reference in New Issue
Block a user