feat: initialize project with core dependencies and game entry point
This commit is contained in:
456
node_modules/three/examples/jsm/tsl/shadows/TileShadowNode.js
generated
vendored
Normal file
456
node_modules/three/examples/jsm/tsl/shadows/TileShadowNode.js
generated
vendored
Normal file
@@ -0,0 +1,456 @@
|
||||
import {
|
||||
Vector3,
|
||||
Object3D,
|
||||
ShadowBaseNode,
|
||||
Plane,
|
||||
Line3,
|
||||
DepthTexture,
|
||||
LessCompare,
|
||||
Vector2,
|
||||
RedFormat,
|
||||
ArrayCamera,
|
||||
VSMShadowMap,
|
||||
RendererUtils,
|
||||
Quaternion
|
||||
} from 'three/webgpu';
|
||||
|
||||
import { min, Fn, shadow, NodeUpdateType, getShadowMaterial, getShadowRenderObjectFunction } from 'three/tsl';
|
||||
|
||||
const { resetRendererAndSceneState, restoreRendererAndSceneState } = RendererUtils;
|
||||
let _rendererState;
|
||||
|
||||
const _cameraLayers = [];
|
||||
const _vec3Temp1 = /*@__PURE__*/ new Vector3();
|
||||
const _vec3Temp2 = /*@__PURE__*/ new Vector3();
|
||||
const _vec3Temp3 = /*@__PURE__*/ new Vector3();
|
||||
const _quatTemp1 = /*@__PURE__*/ new Quaternion();
|
||||
|
||||
class LwLight extends Object3D {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this.target = new Object3D();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that extends `ShadowBaseNode` to implement tiled shadow mapping.
|
||||
* This allows splitting a shadow map into multiple tiles, each with its own light and camera,
|
||||
* to improve shadow quality and performance for large scenes.
|
||||
*
|
||||
* **Note:** This class does not support `VSMShadowMap` at the moment.
|
||||
*
|
||||
* @class
|
||||
* @augments ShadowBaseNode
|
||||
* @three_import import { TileShadowNode } from 'three/addons/tsl/shadows/TileShadowNode.js';
|
||||
*/
|
||||
class TileShadowNode extends ShadowBaseNode {
|
||||
|
||||
/**
|
||||
* Creates an instance of `TileShadowNode`.
|
||||
*
|
||||
* @param {Light} light - The original light source used for shadow mapping.
|
||||
* @param {Object} [options={}] - Configuration options for the tiled shadow node.
|
||||
* @param {number} [options.tilesX=2] - The number of tiles along the X-axis.
|
||||
* @param {number} [options.tilesY=2] - The number of tiles along the Y-axis.
|
||||
* @param {Object} [options.resolution] - The resolution of the shadow map.
|
||||
* @param {boolean} [options.debug=false] - Whether to enable debug mode.
|
||||
*/
|
||||
constructor( light, options = {} ) {
|
||||
|
||||
super( light );
|
||||
|
||||
// Default configuration with sensible defaults
|
||||
this.config = {
|
||||
tilesX: options.tilesX || 2,
|
||||
tilesY: options.tilesY || 2,
|
||||
resolution: options.resolution || light.shadow.mapSize,
|
||||
debug: options.debug !== undefined ? options.debug : false
|
||||
};
|
||||
|
||||
this.debug = this.config.debug;
|
||||
|
||||
this.originalLight = light;
|
||||
this.lightPlane = new Plane( new Vector3( 0, 1, 0 ), 0 );
|
||||
this.line = new Line3();
|
||||
|
||||
this.initialLightDirection = new Vector3();
|
||||
this.updateLightDirection();
|
||||
|
||||
this._cameraFrameId = new WeakMap();
|
||||
|
||||
this.shadowSize = {
|
||||
top: light.shadow.camera.top,
|
||||
bottom: light.shadow.camera.bottom,
|
||||
left: light.shadow.camera.left,
|
||||
right: light.shadow.camera.right,
|
||||
};
|
||||
|
||||
this.lights = [];
|
||||
this._shadowNodes = [];
|
||||
|
||||
this.tiles = this.generateTiles( this.config.tilesX, this.config.tilesY );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the tiles for the shadow map based on the specified number of tiles along the X and Y axes.
|
||||
*
|
||||
* @param {number} tilesX - The number of tiles along the X-axis.
|
||||
* @param {number} tilesY - The number of tiles along the Y-axis.
|
||||
* @returns {Array<Object>} An array of tile objects, each containing the tile's bounds and index.
|
||||
*/
|
||||
generateTiles( tilesX, tilesY ) {
|
||||
|
||||
const tiles = [];
|
||||
const tileWidth = 1 / tilesX;
|
||||
const tileHeight = 1 / tilesY;
|
||||
|
||||
for ( let y = 0; y < tilesY; y ++ ) {
|
||||
|
||||
for ( let x = 0; x < tilesX; x ++ ) {
|
||||
|
||||
tiles.push( {
|
||||
x: [ x * tileWidth, ( x + 1 ) * tileWidth ],
|
||||
y: [ ( tilesY - 1 - y ) * tileHeight, ( tilesY - y ) * tileHeight ], // Start from top row
|
||||
index: y * tilesX + x
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return tiles;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the initial light direction based on the light's target position.
|
||||
*/
|
||||
updateLightDirection() {
|
||||
|
||||
this.initialLightDirection.subVectors(
|
||||
this.originalLight.target.getWorldPosition( new Vector3() ),
|
||||
this.originalLight.getWorldPosition( new Vector3() )
|
||||
).normalize();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the tiled shadow node by creating lights, cameras, and shadow maps for each tile.
|
||||
*
|
||||
* @param {Builder} builder - The builder used to create render targets and other resources.
|
||||
*/
|
||||
init( builder ) {
|
||||
|
||||
const light = this.originalLight;
|
||||
const parent = light.parent;
|
||||
|
||||
const width = this.shadowSize.right - this.shadowSize.left;
|
||||
const height = this.shadowSize.top - this.shadowSize.bottom;
|
||||
|
||||
const tileCount = this.tiles.length;
|
||||
const shadowWidth = this.config.resolution.width;
|
||||
const shadowHeight = this.config.resolution.height;
|
||||
|
||||
// Clear existing lights/nodes if re-initializing
|
||||
this.disposeLightsAndNodes();
|
||||
|
||||
const depthTexture = new DepthTexture( shadowWidth, shadowHeight, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, tileCount );
|
||||
depthTexture.compareFunction = LessCompare;
|
||||
depthTexture.name = 'ShadowDepthArrayTexture';
|
||||
const shadowMap = builder.createRenderTarget( shadowWidth, shadowHeight, { format: RedFormat, depth: tileCount } );
|
||||
shadowMap.depthTexture = depthTexture;
|
||||
shadowMap.texture.name = 'ShadowTexture';
|
||||
this.shadowMap = shadowMap;
|
||||
const cameras = [];
|
||||
|
||||
|
||||
// Create lights, one for each tile
|
||||
for ( let i = 0; i < tileCount; i ++ ) {
|
||||
|
||||
const lwLight = new LwLight();
|
||||
lwLight.castShadow = true;
|
||||
const lShadow = light.shadow.clone();
|
||||
lShadow.filterNode = light.shadow.filterNode;
|
||||
const tile = this.tiles[ i ];
|
||||
lShadow.camera.left = this.shadowSize.left + width * tile.x[ 0 ];
|
||||
lShadow.camera.right = this.shadowSize.left + width * tile.x[ 1 ];
|
||||
lShadow.camera.top = this.shadowSize.bottom + height * tile.y[ 1 ];
|
||||
lShadow.camera.bottom = this.shadowSize.bottom + height * tile.y[ 0 ];
|
||||
lShadow.bias = light.shadow.bias;
|
||||
lShadow.camera.near = light.shadow.camera.near;
|
||||
lShadow.camera.far = light.shadow.camera.far;
|
||||
lShadow.camera.userData.tileIndex = i;
|
||||
lwLight.shadow = lShadow;
|
||||
|
||||
if ( parent ) {
|
||||
|
||||
parent.add( lwLight );
|
||||
parent.add( lwLight.target );
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'TileShadowNode: Original light has no parent during init. Tile lights not added to scene graph directly.' );
|
||||
|
||||
}
|
||||
|
||||
this.syncLightTransformation( lwLight, light );
|
||||
|
||||
this.lights.push( lwLight );
|
||||
lShadow.camera.updateMatrixWorld();
|
||||
|
||||
cameras.push( lShadow.camera );
|
||||
const shadowNode = shadow( lwLight, lShadow );
|
||||
shadowNode.depthLayer = i;
|
||||
shadowNode.updateBeforeType = NodeUpdateType.NONE;
|
||||
|
||||
shadowNode.setupRenderTarget = () => {
|
||||
|
||||
return { shadowMap, depthTexture };
|
||||
|
||||
};
|
||||
|
||||
this._shadowNodes.push( shadowNode );
|
||||
|
||||
}
|
||||
|
||||
const cameraArray = new ArrayCamera( cameras );
|
||||
this.cameraArray = cameraArray;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the light transformations and shadow cameras for each tile.
|
||||
*/
|
||||
update() {
|
||||
|
||||
const light = this.originalLight;
|
||||
|
||||
const shadowCam = light.shadow.camera;
|
||||
const lsMin = new Vector2( shadowCam.left, shadowCam.bottom );
|
||||
const lsMax = new Vector2( shadowCam.right, shadowCam.top );
|
||||
const fullWidth = lsMax.x - lsMin.x;
|
||||
const fullHeight = lsMax.y - lsMin.y;
|
||||
|
||||
for ( let i = 0; i < this.lights.length; i ++ ) {
|
||||
|
||||
const lwLight = this.lights[ i ];
|
||||
const tile = this.tiles[ i ];
|
||||
this.syncLightTransformation( lwLight, light );
|
||||
const lShadow = lwLight.shadow;
|
||||
const tileLeft = lsMin.x + tile.x[ 0 ] * fullWidth;
|
||||
const tileRight = lsMin.x + tile.x[ 1 ] * fullWidth;
|
||||
const tileBottom = lsMin.y + tile.y[ 0 ] * fullHeight;
|
||||
const tileTop = lsMin.y + tile.y[ 1 ] * fullHeight;
|
||||
lShadow.camera.left = tileLeft;
|
||||
lShadow.camera.right = tileRight;
|
||||
lShadow.camera.bottom = tileBottom;
|
||||
lShadow.camera.top = tileTop;
|
||||
lShadow.camera.near = light.shadow.camera.near;
|
||||
lShadow.camera.far = light.shadow.camera.far;
|
||||
lShadow.camera.updateProjectionMatrix();
|
||||
lShadow.camera.updateWorldMatrix( true, false );
|
||||
lShadow.camera.updateMatrixWorld( true );
|
||||
this._shadowNodes[ i ].shadow.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the shadow map rendering.
|
||||
* @param {NodeFrame} frame - A reference to the current node frame.
|
||||
*/
|
||||
updateShadow( frame ) {
|
||||
|
||||
const { shadowMap, light } = this;
|
||||
const { renderer, scene, camera } = frame;
|
||||
const shadowType = renderer.shadowMap.type;
|
||||
const depthVersion = shadowMap.depthTexture.version;
|
||||
this._depthVersionCached = depthVersion;
|
||||
const currentRenderObjectFunction = renderer.getRenderObjectFunction();
|
||||
const currentMRT = renderer.getMRT();
|
||||
const useVelocity = currentMRT ? currentMRT.has( 'velocity' ) : false;
|
||||
|
||||
_rendererState = resetRendererAndSceneState( renderer, scene, _rendererState );
|
||||
scene.overrideMaterial = getShadowMaterial( light );
|
||||
renderer.setRenderTarget( this.shadowMap );
|
||||
|
||||
|
||||
for ( let index = 0; index < this.lights.length; index ++ ) {
|
||||
|
||||
const light = this.lights[ index ];
|
||||
const shadow = light.shadow;
|
||||
|
||||
const _shadowCameraLayer = shadow.camera.layers.mask;
|
||||
_cameraLayers.push( _shadowCameraLayer );
|
||||
|
||||
if ( ( shadow.camera.layers.mask & 0xFFFFFFFE ) === 0 ) {
|
||||
|
||||
shadow.camera.layers.mask = camera.layers.mask;
|
||||
|
||||
}
|
||||
|
||||
shadow.updateMatrices( light );
|
||||
|
||||
renderer.setRenderObjectFunction( getShadowRenderObjectFunction( renderer, shadow, shadowType, useVelocity ) );
|
||||
this.shadowMap.setSize( shadow.mapSize.width, shadow.mapSize.height, shadowMap.depth );
|
||||
|
||||
}
|
||||
|
||||
renderer.render( scene, this.cameraArray );
|
||||
renderer.setRenderObjectFunction( currentRenderObjectFunction );
|
||||
|
||||
if ( light.isPointLight !== true && shadowType === VSMShadowMap ) {
|
||||
|
||||
console.warn( 'THREE.TileShadowNode: VSM shadow map is not supported yet.' );
|
||||
// this.vsmPass( renderer );
|
||||
|
||||
}
|
||||
|
||||
restoreRendererAndSceneState( renderer, scene, _rendererState );
|
||||
|
||||
for ( let index = 0; index < this.lights.length; index ++ ) {
|
||||
|
||||
const light = this.lights[ index ];
|
||||
const shadow = light.shadow;
|
||||
|
||||
shadow.camera.layers.mask = _cameraLayers[ index ];
|
||||
|
||||
}
|
||||
|
||||
_cameraLayers.length = 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation performs the update of the shadow map if necessary.
|
||||
*
|
||||
* @param {NodeFrame} frame - A reference to the current node frame.
|
||||
*/
|
||||
updateBefore( frame ) {
|
||||
|
||||
const shadow = this.originalLight.shadow;
|
||||
|
||||
let needsUpdate = shadow.needsUpdate || shadow.autoUpdate;
|
||||
|
||||
if ( needsUpdate ) {
|
||||
|
||||
if ( this._cameraFrameId[ frame.camera ] === frame.frameId ) {
|
||||
|
||||
needsUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
this._cameraFrameId[ frame.camera ] = frame.frameId;
|
||||
|
||||
}
|
||||
|
||||
if ( needsUpdate ) {
|
||||
|
||||
this.update();
|
||||
this.updateShadow( frame );
|
||||
|
||||
if ( this.shadowMap.depthTexture.version === this._depthVersionCached ) {
|
||||
|
||||
shadow.needsUpdate = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes the transformation of a tile light with the source light.
|
||||
*
|
||||
* @param {LwLight} lwLight - The tile light to synchronize.
|
||||
* @param {Light} sourceLight - The source light to copy transformations from.
|
||||
*/
|
||||
syncLightTransformation( lwLight, sourceLight ) {
|
||||
|
||||
const sourceWorldPos = sourceLight.getWorldPosition( _vec3Temp1 );
|
||||
const targetWorldPos = sourceLight.target.getWorldPosition( _vec3Temp2 );
|
||||
const forward = _vec3Temp3.subVectors( targetWorldPos, sourceWorldPos );
|
||||
const targetDistance = forward.length();
|
||||
forward.normalize();
|
||||
lwLight.position.copy( sourceWorldPos );
|
||||
lwLight.target.position.copy( sourceWorldPos ).add( forward.multiplyScalar( targetDistance ) );
|
||||
lwLight.quaternion.copy( sourceLight.getWorldQuaternion( _quatTemp1 ) );
|
||||
lwLight.scale.copy( sourceLight.scale );
|
||||
lwLight.updateMatrix();
|
||||
lwLight.updateMatrixWorld( true );
|
||||
lwLight.target.updateMatrix();
|
||||
lwLight.target.updateMatrixWorld( true );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the shadow node for rendering.
|
||||
*
|
||||
* @param {Builder} builder - The builder used to set up the shadow node.
|
||||
* @returns {Node} A node representing the shadow value.
|
||||
*/
|
||||
setup( builder ) {
|
||||
|
||||
if ( this.lights.length === 0 ) {
|
||||
|
||||
this.init( builder );
|
||||
|
||||
}
|
||||
|
||||
return Fn( ( builder ) => {
|
||||
|
||||
this.setupShadowPosition( builder );
|
||||
return min( ...this._shadowNodes ).toVar( 'shadowValue' );
|
||||
|
||||
} )();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to remove lights and associated nodes/targets.
|
||||
* Used internally during dispose and potential re-initialization.
|
||||
*/
|
||||
disposeLightsAndNodes() {
|
||||
|
||||
for ( const light of this.lights ) {
|
||||
|
||||
const parent = light.parent;
|
||||
if ( parent ) {
|
||||
|
||||
parent.remove( light.target );
|
||||
parent.remove( light );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.lights = [];
|
||||
this._shadowNodes = [];
|
||||
|
||||
if ( this.shadowMap ) {
|
||||
|
||||
this.shadowMap.dispose(); // Disposes render target and textures
|
||||
this.shadowMap = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
dispose() {
|
||||
|
||||
// Dispose lights, nodes, and shadow map
|
||||
this.disposeLightsAndNodes();
|
||||
super.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { TileShadowNode };
|
||||
212
node_modules/three/examples/jsm/tsl/shadows/TileShadowNodeHelper.js
generated
vendored
Normal file
212
node_modules/three/examples/jsm/tsl/shadows/TileShadowNodeHelper.js
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
import { Group, NodeMaterial, Mesh, PlaneGeometry, DoubleSide, CameraHelper } from 'three/webgpu';
|
||||
import { Fn, vec4, vec3, texture, uv, positionLocal, vec2, float, screenSize } from 'three/tsl';
|
||||
|
||||
/**
|
||||
* Helper class to manage and display debug visuals for TileShadowNode.
|
||||
*
|
||||
* @augments Group
|
||||
* @three_import import { TileShadowNodeHelper } from 'three/addons/tsl/shadows/TileShadowNodeHelper.js';
|
||||
*/
|
||||
class TileShadowNodeHelper extends Group {
|
||||
|
||||
/**
|
||||
* @param {TileShadowNode} tileShadowNode The TileShadowNode instance to debug.
|
||||
*/
|
||||
constructor( tileShadowNode ) {
|
||||
|
||||
super();
|
||||
|
||||
if ( ! tileShadowNode ) {
|
||||
|
||||
throw new Error( 'TileShadowNode instance is required for TileShadowNodeHelper.' );
|
||||
|
||||
}
|
||||
|
||||
this.tileShadowNode = tileShadowNode;
|
||||
this.config = tileShadowNode.config;
|
||||
this.tiles = tileShadowNode.tiles;
|
||||
this._debugMeshes = [];
|
||||
this._shadowCamHelpers = [];
|
||||
|
||||
this.initialized = false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the debug displays (planes and camera helpers).
|
||||
* Should be called after TileShadowNode has initialized its lights and shadow nodes.
|
||||
*/
|
||||
init() {
|
||||
|
||||
if ( this.tileShadowNode._shadowNodes.length !== this.tiles.length ) {
|
||||
|
||||
console.error( 'Cannot initialize TileShadowNodeHelper: Shadow nodes not ready or mismatch count.' );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const tilesX = this.config.tilesX;
|
||||
const tilesY = this.config.tilesY;
|
||||
|
||||
// Clear previous helpers if any (e.g., during a re-init)
|
||||
this.dispose();
|
||||
|
||||
// Create a display for each shadow map tile
|
||||
for ( let i = 0; i < this.tiles.length; i ++ ) {
|
||||
|
||||
// Create display plane
|
||||
const display = new Mesh( new PlaneGeometry( 1, 1 ), new NodeMaterial() );
|
||||
display.renderOrder = 9999999; // Ensure they appear on top
|
||||
display.material.transparent = true;
|
||||
display.frustumCulled = false;
|
||||
display.side = DoubleSide;
|
||||
display.material.depthTest = false; // Disable depth testing
|
||||
display.material.depthWrite = false; // Disable depth writing
|
||||
|
||||
const col = i % tilesX;
|
||||
const row = Math.floor( i / tilesX );
|
||||
|
||||
// Vertex shader logic for positioning the debug quad
|
||||
display.material.vertexNode = Fn( () => {
|
||||
|
||||
const aspectRatio = screenSize.x.div( screenSize.y );
|
||||
const maxTiles = Math.max( tilesX, tilesY );
|
||||
const displaySize = float( 0.8 / maxTiles ); // Size adapts to number of tiles
|
||||
const margin = float( 0.01 );
|
||||
const cornerOffset = float( 0.05 );
|
||||
|
||||
// Position tiles left-to-right, top-to-bottom
|
||||
const xBase = float( - 1.0 ).add( cornerOffset ).add(
|
||||
displaySize.div( 2 ).div( aspectRatio )
|
||||
).add( float( col ).mul( displaySize.div( aspectRatio ).add( margin ) ) );
|
||||
|
||||
const yBase = float( 1.0 ).sub( cornerOffset ).sub(
|
||||
displaySize.div( 2 )
|
||||
).sub( float( row ).mul( displaySize.add( margin ) ) );
|
||||
|
||||
const scaledPos = vec2(
|
||||
positionLocal.x.mul( displaySize.div( aspectRatio ) ),
|
||||
positionLocal.y.mul( displaySize )
|
||||
);
|
||||
|
||||
const finalPos = vec2(
|
||||
scaledPos.x.add( xBase ),
|
||||
scaledPos.y.add( yBase )
|
||||
);
|
||||
|
||||
return vec4( finalPos.x, finalPos.y, 0.0, 1.0 );
|
||||
|
||||
} )();
|
||||
|
||||
display.material.outputNode = Fn( () => {
|
||||
|
||||
// Ensure shadowMap and depthTexture are available
|
||||
if ( ! this.tileShadowNode.shadowMap || ! this.tileShadowNode.shadowMap.depthTexture ) {
|
||||
|
||||
return vec4( 1, 0, 1, 1 ); // Magenta error color
|
||||
|
||||
}
|
||||
|
||||
const sampledDepth = texture( this.tileShadowNode.shadowMap.depthTexture )
|
||||
.sample( uv().flipY() )
|
||||
.depth( float( i ) ) // Sample correct layer
|
||||
.compare( 0.9 ); // Example comparison value
|
||||
|
||||
// Simple tint based on index for visual distinction
|
||||
const r = float( 0.5 + ( i % 3 ) * 0.16 );
|
||||
const g = float( 0.5 + ( i % 2 ) * 0.25 );
|
||||
const b = float( 0.7 + ( i % 4 ) * 0.075 );
|
||||
|
||||
return vec4(
|
||||
vec3( r, g, b )
|
||||
.mul( sampledDepth )
|
||||
.saturate()
|
||||
.rgb,
|
||||
1.0
|
||||
);
|
||||
|
||||
} )();
|
||||
|
||||
this.add( display );
|
||||
this._debugMeshes.push( display );
|
||||
|
||||
if ( this.tileShadowNode._shadowNodes[ i ] && this.tileShadowNode._shadowNodes[ i ].shadow ) {
|
||||
|
||||
const camHelper = new CameraHelper( this.tileShadowNode._shadowNodes[ i ].shadow.camera );
|
||||
camHelper.fog = false;
|
||||
this.add( camHelper );
|
||||
this._shadowCamHelpers.push( camHelper );
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( `TileShadowNodeHelper: Could not create CameraHelper for tile index ${i}. Shadow node or camera missing.` );
|
||||
this._shadowCamHelpers.push( null );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the debug visuals (specifically camera helpers).
|
||||
* Should be called within TileShadowNode's update method.
|
||||
*/
|
||||
update() {
|
||||
|
||||
if ( this.initialized === false ) {
|
||||
|
||||
this.init();
|
||||
|
||||
}
|
||||
|
||||
for ( const helper of this._shadowCamHelpers ) {
|
||||
|
||||
if ( helper ) {
|
||||
|
||||
helper.update(); // Update CameraHelper matrices
|
||||
helper.updateMatrixWorld( true ); // Ensure world matrix is current
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all debug objects (planes and helpers) from the scene.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
if ( this.scene ) {
|
||||
|
||||
for ( const mesh of this._debugMeshes ) {
|
||||
|
||||
mesh.geometry.dispose();
|
||||
mesh.material.dispose();
|
||||
this.scene.remove( mesh );
|
||||
|
||||
}
|
||||
|
||||
for ( const helper of this._shadowCamHelpers ) {
|
||||
|
||||
if ( helper ) {
|
||||
|
||||
this.scene.remove( helper );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this._debugMeshes = [];
|
||||
this._shadowCamHelpers = [];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { TileShadowNodeHelper };
|
||||
Reference in New Issue
Block a user