feat: initialize project with core dependencies and game entry point
This commit is contained in:
599
node_modules/three/examples/jsm/misc/TubePainter.js
generated
vendored
Normal file
599
node_modules/three/examples/jsm/misc/TubePainter.js
generated
vendored
Normal file
@@ -0,0 +1,599 @@
|
||||
import {
|
||||
BufferAttribute,
|
||||
BufferGeometry,
|
||||
Color,
|
||||
DynamicDrawUsage,
|
||||
Matrix4,
|
||||
Mesh,
|
||||
MeshStandardMaterial,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* @classdesc This module can be used to paint tube-like meshes
|
||||
* along a sequence of points. This module is used in a XR
|
||||
* painter demo.
|
||||
*
|
||||
* ```js
|
||||
* const painter = new TubePainter();
|
||||
* scene.add( painter.mesh );
|
||||
* ```
|
||||
*
|
||||
* @name TubePainter
|
||||
* @class
|
||||
* @three_import import { TubePainter } from 'three/addons/misc/TubePainter.js';
|
||||
*/
|
||||
function TubePainter() {
|
||||
|
||||
const BUFFER_SIZE = 1000000 * 3;
|
||||
|
||||
const positions = new BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
|
||||
positions.usage = DynamicDrawUsage;
|
||||
|
||||
const normals = new BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
|
||||
normals.usage = DynamicDrawUsage;
|
||||
|
||||
const colors = new BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
|
||||
colors.usage = DynamicDrawUsage;
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
geometry.setAttribute( 'position', positions );
|
||||
geometry.setAttribute( 'normal', normals );
|
||||
geometry.setAttribute( 'color', colors );
|
||||
geometry.drawRange.count = 0;
|
||||
|
||||
const material = new MeshStandardMaterial( { vertexColors: true } );
|
||||
|
||||
const mesh = new Mesh( geometry, material );
|
||||
mesh.frustumCulled = false;
|
||||
|
||||
//
|
||||
|
||||
function getPoints( size ) {
|
||||
|
||||
const PI2 = Math.PI * 2;
|
||||
|
||||
const sides = 15;
|
||||
const array = [];
|
||||
const radius = 0.01 * size;
|
||||
|
||||
for ( let i = 0; i < sides; i ++ ) {
|
||||
|
||||
const angle = ( i / sides ) * PI2;
|
||||
array.push( new Vector3( Math.sin( angle ) * radius, Math.cos( angle ) * radius, 0 ) );
|
||||
|
||||
}
|
||||
|
||||
return array;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const vector = new Vector3();
|
||||
|
||||
const vector1 = new Vector3();
|
||||
const vector2 = new Vector3();
|
||||
const vector3 = new Vector3();
|
||||
const vector4 = new Vector3();
|
||||
|
||||
const color1 = new Color( 0xffffff );
|
||||
const color2 = new Color( 0xffffff );
|
||||
|
||||
let size1 = 1;
|
||||
let size2 = 1;
|
||||
|
||||
function addCap( position, matrix, isEndCap, capSize ) {
|
||||
|
||||
let count = geometry.drawRange.count;
|
||||
|
||||
const points = getPoints( capSize );
|
||||
const sides = points.length;
|
||||
const radius = 0.01 * capSize;
|
||||
const latSegments = 4;
|
||||
const directionSign = isEndCap ? - 1 : 1;
|
||||
|
||||
for ( let lat = 0; lat < latSegments; lat ++ ) {
|
||||
|
||||
const phi1 = ( lat / latSegments ) * Math.PI * 0.5;
|
||||
const phi2 = ( ( lat + 1 ) / latSegments ) * Math.PI * 0.5;
|
||||
|
||||
const z1 = Math.sin( phi1 ) * radius * directionSign;
|
||||
const r1 = Math.cos( phi1 ) * radius;
|
||||
|
||||
const z2 = Math.sin( phi2 ) * radius * directionSign;
|
||||
const r2 = Math.cos( phi2 ) * radius;
|
||||
|
||||
for ( let i = 0; i < sides; i ++ ) {
|
||||
|
||||
const theta1 = ( i / sides ) * Math.PI * 2;
|
||||
const theta2 = ( ( i + 1 ) / sides ) * Math.PI * 2;
|
||||
|
||||
// First ring
|
||||
const x1 = Math.sin( theta1 ) * r1;
|
||||
const y1 = Math.cos( theta1 ) * r1;
|
||||
|
||||
const x2 = Math.sin( theta2 ) * r1;
|
||||
const y2 = Math.cos( theta2 ) * r1;
|
||||
|
||||
// Second ring
|
||||
const x3 = Math.sin( theta1 ) * r2;
|
||||
const y3 = Math.cos( theta1 ) * r2;
|
||||
|
||||
const x4 = Math.sin( theta2 ) * r2;
|
||||
const y4 = Math.cos( theta2 ) * r2;
|
||||
|
||||
// Transform to world space
|
||||
vector1.set( x1, y1, z1 ).applyMatrix4( matrix ).add( position );
|
||||
vector2.set( x2, y2, z1 ).applyMatrix4( matrix ).add( position );
|
||||
vector3.set( x3, y3, z2 ).applyMatrix4( matrix ).add( position );
|
||||
vector4.set( x4, y4, z2 ).applyMatrix4( matrix ).add( position );
|
||||
|
||||
// First triangle
|
||||
normal.set( x1, y1, z1 ).normalize().transformDirection( matrix );
|
||||
vector.set( x2, y2, z1 ).normalize().transformDirection( matrix );
|
||||
side.set( x3, y3, z2 ).normalize().transformDirection( matrix );
|
||||
|
||||
if ( isEndCap ) {
|
||||
|
||||
vector1.toArray( positions.array, count * 3 );
|
||||
vector2.toArray( positions.array, ( count + 1 ) * 3 );
|
||||
vector3.toArray( positions.array, ( count + 2 ) * 3 );
|
||||
|
||||
normal.toArray( normals.array, count * 3 );
|
||||
vector.toArray( normals.array, ( count + 1 ) * 3 );
|
||||
side.toArray( normals.array, ( count + 2 ) * 3 );
|
||||
|
||||
} else {
|
||||
|
||||
vector1.toArray( positions.array, count * 3 );
|
||||
vector3.toArray( positions.array, ( count + 1 ) * 3 );
|
||||
vector2.toArray( positions.array, ( count + 2 ) * 3 );
|
||||
|
||||
normal.toArray( normals.array, count * 3 );
|
||||
side.toArray( normals.array, ( count + 1 ) * 3 );
|
||||
vector.toArray( normals.array, ( count + 2 ) * 3 );
|
||||
|
||||
}
|
||||
|
||||
color1.toArray( colors.array, count * 3 );
|
||||
color1.toArray( colors.array, ( count + 1 ) * 3 );
|
||||
color1.toArray( colors.array, ( count + 2 ) * 3 );
|
||||
|
||||
count += 3;
|
||||
|
||||
// Second triangle
|
||||
if ( r2 > 0.001 ) {
|
||||
|
||||
normal.set( x2, y2, z1 ).normalize().transformDirection( matrix );
|
||||
vector.set( x4, y4, z2 ).normalize().transformDirection( matrix );
|
||||
side.set( x3, y3, z2 ).normalize().transformDirection( matrix );
|
||||
|
||||
if ( isEndCap ) {
|
||||
|
||||
vector2.toArray( positions.array, count * 3 );
|
||||
vector4.toArray( positions.array, ( count + 1 ) * 3 );
|
||||
vector3.toArray( positions.array, ( count + 2 ) * 3 );
|
||||
|
||||
normal.toArray( normals.array, count * 3 );
|
||||
vector.toArray( normals.array, ( count + 1 ) * 3 );
|
||||
side.toArray( normals.array, ( count + 2 ) * 3 );
|
||||
|
||||
} else {
|
||||
|
||||
vector3.toArray( positions.array, count * 3 );
|
||||
vector4.toArray( positions.array, ( count + 1 ) * 3 );
|
||||
vector2.toArray( positions.array, ( count + 2 ) * 3 );
|
||||
|
||||
side.toArray( normals.array, count * 3 );
|
||||
vector.toArray( normals.array, ( count + 1 ) * 3 );
|
||||
normal.toArray( normals.array, ( count + 2 ) * 3 );
|
||||
|
||||
}
|
||||
|
||||
color1.toArray( colors.array, count * 3 );
|
||||
color1.toArray( colors.array, ( count + 1 ) * 3 );
|
||||
color1.toArray( colors.array, ( count + 2 ) * 3 );
|
||||
|
||||
count += 3;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
geometry.drawRange.count = count;
|
||||
|
||||
}
|
||||
|
||||
function updateEndCap( position, matrix, capSize ) {
|
||||
|
||||
if ( endCapStartIndex === null ) return;
|
||||
|
||||
const points = getPoints( capSize );
|
||||
const sides = points.length;
|
||||
const radius = 0.01 * capSize;
|
||||
const latSegments = 4;
|
||||
|
||||
let count = endCapStartIndex;
|
||||
|
||||
for ( let lat = 0; lat < latSegments; lat ++ ) {
|
||||
|
||||
const phi1 = ( lat / latSegments ) * Math.PI * 0.5;
|
||||
const phi2 = ( ( lat + 1 ) / latSegments ) * Math.PI * 0.5;
|
||||
|
||||
const z1 = - Math.sin( phi1 ) * radius;
|
||||
const r1 = Math.cos( phi1 ) * radius;
|
||||
|
||||
const z2 = - Math.sin( phi2 ) * radius;
|
||||
const r2 = Math.cos( phi2 ) * radius;
|
||||
|
||||
for ( let i = 0; i < sides; i ++ ) {
|
||||
|
||||
const theta1 = ( i / sides ) * Math.PI * 2;
|
||||
const theta2 = ( ( i + 1 ) / sides ) * Math.PI * 2;
|
||||
|
||||
// First ring
|
||||
const x1 = Math.sin( theta1 ) * r1;
|
||||
const y1 = Math.cos( theta1 ) * r1;
|
||||
|
||||
const x2 = Math.sin( theta2 ) * r1;
|
||||
const y2 = Math.cos( theta2 ) * r1;
|
||||
|
||||
// Second ring
|
||||
const x3 = Math.sin( theta1 ) * r2;
|
||||
const y3 = Math.cos( theta1 ) * r2;
|
||||
|
||||
const x4 = Math.sin( theta2 ) * r2;
|
||||
const y4 = Math.cos( theta2 ) * r2;
|
||||
|
||||
// Transform positions to world space
|
||||
vector1.set( x1, y1, z1 ).applyMatrix4( matrix ).add( position );
|
||||
vector2.set( x2, y2, z1 ).applyMatrix4( matrix ).add( position );
|
||||
vector3.set( x3, y3, z2 ).applyMatrix4( matrix ).add( position );
|
||||
vector4.set( x4, y4, z2 ).applyMatrix4( matrix ).add( position );
|
||||
|
||||
// Transform normals to world space
|
||||
normal.set( x1, y1, z1 ).normalize().transformDirection( matrix );
|
||||
vector.set( x2, y2, z1 ).normalize().transformDirection( matrix );
|
||||
side.set( x3, y3, z2 ).normalize().transformDirection( matrix );
|
||||
|
||||
// First triangle
|
||||
vector1.toArray( positions.array, count * 3 );
|
||||
vector2.toArray( positions.array, ( count + 1 ) * 3 );
|
||||
vector3.toArray( positions.array, ( count + 2 ) * 3 );
|
||||
|
||||
normal.toArray( normals.array, count * 3 );
|
||||
vector.toArray( normals.array, ( count + 1 ) * 3 );
|
||||
side.toArray( normals.array, ( count + 2 ) * 3 );
|
||||
|
||||
color1.toArray( colors.array, count * 3 );
|
||||
color1.toArray( colors.array, ( count + 1 ) * 3 );
|
||||
color1.toArray( colors.array, ( count + 2 ) * 3 );
|
||||
|
||||
count += 3;
|
||||
|
||||
// Second triangle
|
||||
if ( r2 > 0.001 ) {
|
||||
|
||||
normal.set( x2, y2, z1 ).normalize().transformDirection( matrix );
|
||||
vector.set( x4, y4, z2 ).normalize().transformDirection( matrix );
|
||||
side.set( x3, y3, z2 ).normalize().transformDirection( matrix );
|
||||
|
||||
vector2.toArray( positions.array, count * 3 );
|
||||
vector4.toArray( positions.array, ( count + 1 ) * 3 );
|
||||
vector3.toArray( positions.array, ( count + 2 ) * 3 );
|
||||
|
||||
normal.toArray( normals.array, count * 3 );
|
||||
vector.toArray( normals.array, ( count + 1 ) * 3 );
|
||||
side.toArray( normals.array, ( count + 2 ) * 3 );
|
||||
|
||||
color1.toArray( colors.array, count * 3 );
|
||||
color1.toArray( colors.array, ( count + 1 ) * 3 );
|
||||
color1.toArray( colors.array, ( count + 2 ) * 3 );
|
||||
|
||||
count += 3;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
positions.addUpdateRange( endCapStartIndex * 3, endCapVertexCount * 3 );
|
||||
normals.addUpdateRange( endCapStartIndex * 3, endCapVertexCount * 3 );
|
||||
colors.addUpdateRange( endCapStartIndex * 3, endCapVertexCount * 3 );
|
||||
|
||||
}
|
||||
|
||||
function stroke( position1, position2, matrix1, matrix2, size1, size2 ) {
|
||||
|
||||
if ( position1.distanceToSquared( position2 ) === 0 ) return;
|
||||
|
||||
let count = geometry.drawRange.count;
|
||||
|
||||
const points1 = getPoints( size1 );
|
||||
const points2 = getPoints( size2 );
|
||||
|
||||
for ( let i = 0, il = points2.length; i < il; i ++ ) {
|
||||
|
||||
const vertex1_2 = points2[ i ];
|
||||
const vertex2_2 = points2[ ( i + 1 ) % il ];
|
||||
const vertex1_1 = points1[ i ];
|
||||
const vertex2_1 = points1[ ( i + 1 ) % il ];
|
||||
|
||||
vector1.copy( vertex1_2 ).applyMatrix4( matrix2 ).add( position2 );
|
||||
vector2.copy( vertex2_2 ).applyMatrix4( matrix2 ).add( position2 );
|
||||
vector3.copy( vertex2_1 ).applyMatrix4( matrix1 ).add( position1 );
|
||||
vector4.copy( vertex1_1 ).applyMatrix4( matrix1 ).add( position1 );
|
||||
|
||||
vector1.toArray( positions.array, ( count + 0 ) * 3 );
|
||||
vector2.toArray( positions.array, ( count + 1 ) * 3 );
|
||||
vector4.toArray( positions.array, ( count + 2 ) * 3 );
|
||||
|
||||
vector2.toArray( positions.array, ( count + 3 ) * 3 );
|
||||
vector3.toArray( positions.array, ( count + 4 ) * 3 );
|
||||
vector4.toArray( positions.array, ( count + 5 ) * 3 );
|
||||
|
||||
vector1.copy( vertex1_2 ).applyMatrix4( matrix2 ).normalize();
|
||||
vector2.copy( vertex2_2 ).applyMatrix4( matrix2 ).normalize();
|
||||
vector3.copy( vertex2_1 ).applyMatrix4( matrix1 ).normalize();
|
||||
vector4.copy( vertex1_1 ).applyMatrix4( matrix1 ).normalize();
|
||||
|
||||
vector1.toArray( normals.array, ( count + 0 ) * 3 );
|
||||
vector2.toArray( normals.array, ( count + 1 ) * 3 );
|
||||
vector4.toArray( normals.array, ( count + 2 ) * 3 );
|
||||
|
||||
vector2.toArray( normals.array, ( count + 3 ) * 3 );
|
||||
vector3.toArray( normals.array, ( count + 4 ) * 3 );
|
||||
vector4.toArray( normals.array, ( count + 5 ) * 3 );
|
||||
|
||||
color2.toArray( colors.array, ( count + 0 ) * 3 );
|
||||
color2.toArray( colors.array, ( count + 1 ) * 3 );
|
||||
color1.toArray( colors.array, ( count + 2 ) * 3 );
|
||||
|
||||
color2.toArray( colors.array, ( count + 3 ) * 3 );
|
||||
color1.toArray( colors.array, ( count + 4 ) * 3 );
|
||||
color1.toArray( colors.array, ( count + 5 ) * 3 );
|
||||
|
||||
count += 6;
|
||||
|
||||
}
|
||||
|
||||
geometry.drawRange.count = count;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const direction = new Vector3();
|
||||
const normal = new Vector3();
|
||||
const side = new Vector3();
|
||||
|
||||
const point1 = new Vector3();
|
||||
const point2 = new Vector3();
|
||||
|
||||
const matrix1 = new Matrix4();
|
||||
const matrix2 = new Matrix4();
|
||||
|
||||
const lastNormal = new Vector3();
|
||||
const prevDirection = new Vector3();
|
||||
const rotationAxis = new Vector3();
|
||||
|
||||
let isFirstSegment = true;
|
||||
|
||||
let endCapStartIndex = null;
|
||||
let endCapVertexCount = 0;
|
||||
|
||||
function calculateRMF() {
|
||||
|
||||
if ( isFirstSegment === true ) {
|
||||
|
||||
if ( Math.abs( direction.y ) < 0.99 ) {
|
||||
|
||||
vector.copy( direction ).multiplyScalar( direction.y );
|
||||
normal.set( 0, 1, 0 ).sub( vector ).normalize();
|
||||
|
||||
} else {
|
||||
|
||||
vector.copy( direction ).multiplyScalar( direction.x );
|
||||
normal.set( 1, 0, 0 ).sub( vector ).normalize();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
rotationAxis.crossVectors( prevDirection, direction );
|
||||
|
||||
const rotAxisLength = rotationAxis.length();
|
||||
|
||||
if ( rotAxisLength > 0.0001 ) {
|
||||
|
||||
rotationAxis.divideScalar( rotAxisLength );
|
||||
vector.addVectors( prevDirection, direction );
|
||||
const c1 = - 2.0 / ( 1.0 + prevDirection.dot( direction ) );
|
||||
const dot = lastNormal.dot( vector );
|
||||
normal.copy( lastNormal ).addScaledVector( vector, c1 * dot );
|
||||
|
||||
} else {
|
||||
|
||||
normal.copy( lastNormal );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
side.crossVectors( direction, normal ).normalize();
|
||||
normal.crossVectors( side, direction ).normalize();
|
||||
|
||||
if ( isFirstSegment === false ) {
|
||||
|
||||
const smoothFactor = 0.3;
|
||||
|
||||
normal.lerp( lastNormal, smoothFactor ).normalize();
|
||||
side.crossVectors( direction, normal ).normalize();
|
||||
normal.crossVectors( side, direction ).normalize();
|
||||
|
||||
}
|
||||
|
||||
lastNormal.copy( normal );
|
||||
prevDirection.copy( direction );
|
||||
|
||||
matrix1.makeBasis( side, normal, vector.copy( direction ).negate() );
|
||||
|
||||
}
|
||||
|
||||
function moveTo( position ) {
|
||||
|
||||
point2.copy( position );
|
||||
|
||||
lastNormal.set( 0, 1, 0 );
|
||||
|
||||
isFirstSegment = true;
|
||||
|
||||
endCapStartIndex = null;
|
||||
endCapVertexCount = 0;
|
||||
|
||||
}
|
||||
|
||||
function lineTo( position ) {
|
||||
|
||||
point1.copy( position );
|
||||
|
||||
direction.subVectors( point1, point2 );
|
||||
|
||||
const length = direction.length();
|
||||
|
||||
if ( length === 0 ) return;
|
||||
|
||||
direction.normalize();
|
||||
|
||||
calculateRMF();
|
||||
|
||||
if ( isFirstSegment === true ) {
|
||||
|
||||
color2.copy( color1 );
|
||||
size2 = size1;
|
||||
|
||||
matrix2.copy( matrix1 );
|
||||
|
||||
addCap( point2, matrix2, false, size2 );
|
||||
|
||||
// End cap is added immediately after start cap and updated in-place
|
||||
endCapStartIndex = geometry.drawRange.count;
|
||||
addCap( point1, matrix1, true, size1 );
|
||||
endCapVertexCount = geometry.drawRange.count - endCapStartIndex;
|
||||
|
||||
}
|
||||
|
||||
stroke( point1, point2, matrix1, matrix2, size1, size2 );
|
||||
|
||||
updateEndCap( point1, matrix1, size1 );
|
||||
|
||||
point2.copy( point1 );
|
||||
matrix2.copy( matrix1 );
|
||||
|
||||
color2.copy( color1 );
|
||||
size2 = size1;
|
||||
|
||||
isFirstSegment = false;
|
||||
|
||||
}
|
||||
|
||||
function setSize( value ) {
|
||||
|
||||
size1 = value;
|
||||
|
||||
}
|
||||
|
||||
function setColor( value ) {
|
||||
|
||||
color1.copy( value );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
let count = 0;
|
||||
|
||||
function update() {
|
||||
|
||||
const start = count;
|
||||
const end = geometry.drawRange.count;
|
||||
|
||||
if ( start === end ) return;
|
||||
|
||||
positions.addUpdateRange( start * 3, ( end - start ) * 3 );
|
||||
positions.needsUpdate = true;
|
||||
|
||||
normals.addUpdateRange( start * 3, ( end - start ) * 3 );
|
||||
normals.needsUpdate = true;
|
||||
|
||||
colors.addUpdateRange( start * 3, ( end - start ) * 3 );
|
||||
colors.needsUpdate = true;
|
||||
|
||||
count = end;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* The "painted" tube mesh. Must be added to the scene.
|
||||
*
|
||||
* @name TubePainter#mesh
|
||||
* @type {Mesh}
|
||||
*/
|
||||
mesh: mesh,
|
||||
|
||||
/**
|
||||
* Moves the current painting position to the given value.
|
||||
*
|
||||
* @method
|
||||
* @name TubePainter#moveTo
|
||||
* @param {Vector3} position The new painting position.
|
||||
*/
|
||||
moveTo: moveTo,
|
||||
|
||||
/**
|
||||
* Draw a stroke from the current position to the given one.
|
||||
* This method extends the tube while drawing with the XR
|
||||
* controllers.
|
||||
*
|
||||
* @method
|
||||
* @name TubePainter#lineTo
|
||||
* @param {Vector3} position The destination position.
|
||||
*/
|
||||
lineTo: lineTo,
|
||||
|
||||
/**
|
||||
* Sets the size of newly rendered tube segments.
|
||||
*
|
||||
* @method
|
||||
* @name TubePainter#setSize
|
||||
* @param {number} size The size.
|
||||
*/
|
||||
setSize: setSize,
|
||||
|
||||
/**
|
||||
* Sets the color of newly rendered tube segments.
|
||||
*
|
||||
* @method
|
||||
* @name TubePainter#setColor
|
||||
* @param {Color} color The color.
|
||||
*/
|
||||
setColor: setColor,
|
||||
|
||||
/**
|
||||
* Updates the internal geometry buffers so the new painted
|
||||
* segments are rendered.
|
||||
*
|
||||
* @method
|
||||
* @name TubePainter#update
|
||||
*/
|
||||
update: update
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export { TubePainter };
|
||||
Reference in New Issue
Block a user