Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/jsm/loaders/PCDLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class PCDLoader extends Loader {

// remove comments

PCDheader.str = PCDheader.str.replace( /#.*/gi, '' );
PCDheader.str = PCDheader.str.replace( /^#.*/gm, '' );

// parse

Expand Down
Binary file modified examples/screenshots/webgpu_backdrop_area.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions examples/webgpu_backdrop_area.html
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,12 @@

const materials = {
'blurred': blurredBlur,
'depth': depthMaterial,
'checker': checkerMaterial,
'depth': depthMaterial,
'pixel': pixelMaterial
};

const options = { material: 'blurred' };
const options = { material: 'checker' };
box.material = materials[ options.material ];

const gui = renderer.inspector.createParameters( 'Scene settings' );
Expand Down
130 changes: 120 additions & 10 deletions src/nodes/accessors/Skinning.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { Fn, add, uniform, mat3 } from '../tsl/TSLBase.js';
import { Fn, add, uniform, int, ivec2, mat3, mat4 } from '../tsl/TSLBase.js';
import { attribute } from '../core/AttributeNode.js';
import { OnObjectUpdate } from '../utils/EventNode.js';
import { normalLocal } from './Normal.js';
Expand All @@ -8,13 +8,87 @@ import { tangentLocal } from './Tangent.js';
import { reference, referenceBuffer } from './ReferenceNode.js';
import { buffer } from './BufferNode.js';
import { storage } from './StorageBufferNode.js';
import { texture } from './TextureNode.js';
import { textureSize } from './TextureSizeNode.js';
import { instanceIndex } from '../core/IndexNode.js';

import { InstancedBufferAttribute } from '../../core/InstancedBufferAttribute.js';
import { DataTexture } from '../../textures/DataTexture.js';
import { RGBAFormat, FloatType } from '../../constants.js';

const _skeletonsUpdated = /*@__PURE__*/ new WeakMap();
const _previousBoneMatricesData = /*@__PURE__*/ new WeakMap();

/**
* Creates an accessor for bone matrices stored in a bone texture.
*
* @param {TextureNode} boneTexture - The bone texture node.
* @returns {Object} An accessor with the same `element()` interface as a buffer node.
*/
function getBoneTextureMatrices( boneTexture ) {

return {
element: ( i ) => {

const size = int( textureSize( boneTexture ).x ).toConst();
const j = int( i ).mul( 4 ).toConst();
const y = j.div( size ).toConst();
const x = j.sub( y.mul( size ) ).toConst();

return mat4(
boneTexture.load( ivec2( x, y ) ),
boneTexture.load( ivec2( x.add( 1 ), y ) ),
boneTexture.load( ivec2( x.add( 2 ), y ) ),
boneTexture.load( ivec2( x.add( 3 ), y ) )
);

}
};

}

/**
* Creates the bone matrices node. Skeletons that fit within the uniform buffer limit
* use a uniform buffer, larger skeletons fall back to a bone texture.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {Skeleton} skeleton - The skeleton.
* @returns {Object} The bone matrices node.
*/
function getBoneMatricesNode( builder, skeleton ) {

let node;

const uniformBufferSize = skeleton.bones.length * 16 * 4;

if ( uniformBufferSize <= builder.getUniformBufferLimit() ) {

node = referenceBuffer( 'skeleton.boneMatrices', 'mat4', skeleton.bones.length );

} else {

if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture();

const boneTexture = texture( skeleton.boneTexture );

OnObjectUpdate( ( { object } ) => {

const skeleton = object.skeleton;

if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture();

boneTexture.value = skeleton.boneTexture;

} );

node = getBoneTextureMatrices( boneTexture );

}

return node;

}

/**
* Computes the skinned position by applying bone matrices based on weights.
*
Expand Down Expand Up @@ -91,14 +165,15 @@ function getSkinnedNormalAndTangent( boneMatrices, normal, tangent, bindMatrix,
* Retrieves or initializes the previous frame skinned position node for motion vectors.
* Uses a WeakMap to cache previous frame bone matrix arrays and their TSL buffer nodes.
*
* @param {NodeBuilder} builder - The current node builder.
* @param {SkinnedMesh} skinnedMesh - The skinned mesh.
* @param {Node<mat4>} bindMatrixNode - The bind matrix node.
* @param {Node<mat4>} bindMatrixInverseNode - The inverse bind matrix node.
* @param {Node<uvec4>} skinIndexNode - The skin index attribute.
* @param {Node<vec4>} skinWeightNode - The skin weight attribute.
* @returns {Node<vec3>} The skinned position from the previous frame.
*/
function getPreviousSkinnedPosition( skinnedMesh, bindMatrixNode, bindMatrixInverseNode, skinIndexNode, skinWeightNode ) {
function getPreviousSkinnedPosition( builder, skinnedMesh, bindMatrixNode, bindMatrixInverseNode, skinIndexNode, skinWeightNode ) {

const skeleton = skinnedMesh.skeleton;

Expand All @@ -108,12 +183,35 @@ function getPreviousSkinnedPosition( skinnedMesh, bindMatrixNode, bindMatrixInve

skeleton.update();

const previousBoneMatrices = new Float32Array( skeleton.boneMatrices );
const uniformBufferSize = skeleton.bones.length * 16 * 4;

if ( uniformBufferSize <= builder.getUniformBufferLimit() ) {

const previousBoneMatrices = new Float32Array( skeleton.boneMatrices );

data = {
previousBoneMatrices,
previousBoneTexture: null,
node: buffer( previousBoneMatrices, 'mat4', skeleton.bones.length )
};

data = {
previousBoneMatrices,
node: buffer( previousBoneMatrices, 'mat4', skeleton.bones.length )
};
} else {

if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture();

const { width, height } = skeleton.boneTexture.image;

const previousBoneMatrices = new Float32Array( skeleton.boneMatrices );
const previousBoneTexture = new DataTexture( previousBoneMatrices, width, height, RGBAFormat, FloatType );
previousBoneTexture.needsUpdate = true;

data = {
previousBoneMatrices,
previousBoneTexture,
node: getBoneTextureMatrices( texture( previousBoneTexture ) )
};

}

_previousBoneMatricesData.set( skeleton, data );

Expand All @@ -137,7 +235,7 @@ export const skinning = /*@__PURE__*/ Fn( ( [ skinnedMesh ], builder ) => {
const skinWeightNode = attribute( 'skinWeight', 'vec4' );
const bindMatrixNode = reference( 'bindMatrix', 'mat4' );
const bindMatrixInverseNode = reference( 'bindMatrixInverse', 'mat4' );
const boneMatricesNode = referenceBuffer( 'skeleton.boneMatrices', 'mat4', skinnedMesh.skeleton.bones.length );
const boneMatricesNode = getBoneMatricesNode( builder, skinnedMesh.skeleton );

OnObjectUpdate( ( { object, frameId } ) => {

Expand All @@ -153,6 +251,12 @@ export const skinning = /*@__PURE__*/ Fn( ( [ skinnedMesh ], builder ) => {

skeletonData.previousBoneMatrices.set( skeleton.boneMatrices );

if ( skeletonData.previousBoneTexture !== null ) {

skeletonData.previousBoneTexture.needsUpdate = true;

}

}

skeleton.update();
Expand All @@ -163,7 +267,7 @@ export const skinning = /*@__PURE__*/ Fn( ( [ skinnedMesh ], builder ) => {

if ( builder.needsPreviousData() ) {

const previousSkinnedPosition = getPreviousSkinnedPosition( skinnedMesh, bindMatrixNode, bindMatrixInverseNode, skinIndexNode, skinWeightNode );
const previousSkinnedPosition = getPreviousSkinnedPosition( builder, skinnedMesh, bindMatrixNode, bindMatrixInverseNode, skinIndexNode, skinWeightNode );

positionPrevious.assign( previousSkinnedPosition );

Expand Down Expand Up @@ -220,6 +324,12 @@ export const computeSkinning = /*@__PURE__*/ Fn( ( [ skinnedMesh, toPosition = n

state.previousBoneMatrices.set( skeleton.boneMatrices );

if ( state.previousBoneTexture !== null ) {

state.previousBoneTexture.needsUpdate = true;

}

}

skeleton.update();
Expand All @@ -230,7 +340,7 @@ export const computeSkinning = /*@__PURE__*/ Fn( ( [ skinnedMesh, toPosition = n

if ( builder.needsPreviousData() ) {

const previousSkinnedPosition = getPreviousSkinnedPosition( skinnedMesh, bindMatrixNode, bindMatrixInverseNode, skinIndexNode, skinWeightNode );
const previousSkinnedPosition = getPreviousSkinnedPosition( builder, skinnedMesh, bindMatrixNode, bindMatrixInverseNode, skinIndexNode, skinWeightNode );

positionPrevious.assign( previousSkinnedPosition );

Expand Down