| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- 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 };
|