123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- import Node, { addNodeClass } from '../core/Node.js';
- import { NodeUpdateType } from '../core/constants.js';
- import { float, nodeProxy, tslFn } from '../shadernode/ShaderNode.js';
- import { uniform } from '../core/UniformNode.js';
- import { reference } from './ReferenceNode.js';
- import { positionLocal } from './PositionNode.js';
- import { normalLocal } from './NormalNode.js';
- import { textureLoad } from './TextureNode.js';
- import { instanceIndex, vertexIndex } from '../core/IndexNode.js';
- import { ivec2, int } from '../shadernode/ShaderNode.js';
- import { DataArrayTexture, Vector2, Vector4, FloatType } from 'three';
- import { loop } from '../utils/LoopNode.js';
- const morphTextures = new WeakMap();
- const morphVec4 = new Vector4();
- const getMorph = tslFn( ( { bufferMap, influence, stride, width, depth, offset } ) => {
- const texelIndex = int( vertexIndex ).mul( stride ).add( offset );
- const y = texelIndex.div( width );
- const x = texelIndex.sub( y.mul( width ) );
- const bufferAttrib = textureLoad( bufferMap, ivec2( x, y ) ).depth( depth );
- return bufferAttrib.mul( influence );
- } );
- function getEntry( geometry ) {
- const hasMorphPosition = geometry.morphAttributes.position !== undefined;
- const hasMorphNormals = geometry.morphAttributes.normal !== undefined;
- const hasMorphColors = geometry.morphAttributes.color !== undefined;
- // instead of using attributes, the WebGL 2 code path encodes morph targets
- // into an array of data textures. Each layer represents a single morph target.
- const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;
- const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;
- let entry = morphTextures.get( geometry );
- if ( entry === undefined || entry.count !== morphTargetsCount ) {
- if ( entry !== undefined ) entry.texture.dispose();
- const morphTargets = geometry.morphAttributes.position || [];
- const morphNormals = geometry.morphAttributes.normal || [];
- const morphColors = geometry.morphAttributes.color || [];
- let vertexDataCount = 0;
- if ( hasMorphPosition === true ) vertexDataCount = 1;
- if ( hasMorphNormals === true ) vertexDataCount = 2;
- if ( hasMorphColors === true ) vertexDataCount = 3;
- let width = geometry.attributes.position.count * vertexDataCount;
- let height = 1;
- const maxTextureSize = 4096; // @TODO: Use 'capabilities.maxTextureSize'
- if ( width > maxTextureSize ) {
- height = Math.ceil( width / maxTextureSize );
- width = maxTextureSize;
- }
- const buffer = new Float32Array( width * height * 4 * morphTargetsCount );
- const bufferTexture = new DataArrayTexture( buffer, width, height, morphTargetsCount );
- bufferTexture.type = FloatType;
- bufferTexture.needsUpdate = true;
- // fill buffer
- const vertexDataStride = vertexDataCount * 4;
- for ( let i = 0; i < morphTargetsCount; i ++ ) {
- const morphTarget = morphTargets[ i ];
- const morphNormal = morphNormals[ i ];
- const morphColor = morphColors[ i ];
- const offset = width * height * 4 * i;
- for ( let j = 0; j < morphTarget.count; j ++ ) {
- const stride = j * vertexDataStride;
- if ( hasMorphPosition === true ) {
- morphVec4.fromBufferAttribute( morphTarget, j );
- buffer[ offset + stride + 0 ] = morphVec4.x;
- buffer[ offset + stride + 1 ] = morphVec4.y;
- buffer[ offset + stride + 2 ] = morphVec4.z;
- buffer[ offset + stride + 3 ] = 0;
- }
- if ( hasMorphNormals === true ) {
- morphVec4.fromBufferAttribute( morphNormal, j );
- buffer[ offset + stride + 4 ] = morphVec4.x;
- buffer[ offset + stride + 5 ] = morphVec4.y;
- buffer[ offset + stride + 6 ] = morphVec4.z;
- buffer[ offset + stride + 7 ] = 0;
- }
- if ( hasMorphColors === true ) {
- morphVec4.fromBufferAttribute( morphColor, j );
- buffer[ offset + stride + 8 ] = morphVec4.x;
- buffer[ offset + stride + 9 ] = morphVec4.y;
- buffer[ offset + stride + 10 ] = morphVec4.z;
- buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morphVec4.w : 1;
- }
- }
- }
- entry = {
- count: morphTargetsCount,
- texture: bufferTexture,
- stride: vertexDataCount,
- size: new Vector2( width, height )
- };
- morphTextures.set( geometry, entry );
- function disposeTexture() {
- bufferTexture.dispose();
- morphTextures.delete( geometry );
- geometry.removeEventListener( 'dispose', disposeTexture );
- }
- geometry.addEventListener( 'dispose', disposeTexture );
- }
- return entry;
- }
- class MorphNode extends Node {
- constructor( mesh ) {
- super( 'void' );
- this.mesh = mesh;
- this.morphBaseInfluence = uniform( 1 );
- this.updateType = NodeUpdateType.OBJECT;
- }
- setup( builder ) {
- const { geometry } = builder;
- const hasMorphPosition = geometry.morphAttributes.position !== undefined;
- const hasMorphNormals = geometry.morphAttributes.normal !== undefined;
- const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;
- const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;
- // nodes
- const { texture: bufferMap, stride, size } = getEntry( geometry );
- if ( hasMorphPosition === true ) positionLocal.mulAssign( this.morphBaseInfluence );
- if ( hasMorphNormals === true ) normalLocal.mulAssign( this.morphBaseInfluence );
- const width = int( size.width );
- loop( morphTargetsCount, ( { i } ) => {
- const influence = float( 0 ).toVar();
- if ( this.mesh.count > 1 && ( this.mesh.morphTexture !== null && this.mesh.morphTexture !== undefined ) ) {
- influence.assign( textureLoad( this.mesh.morphTexture, ivec2( int( i ).add( 1 ), int( instanceIndex ) ) ).r );
- } else {
- influence.assign( reference( 'morphTargetInfluences', 'float' ).element( i ).toVar() );
- }
- if ( hasMorphPosition === true ) {
- positionLocal.addAssign( getMorph( {
- bufferMap,
- influence,
- stride,
- width,
- depth: i,
- offset: int( 0 )
- } ) );
- }
- if ( hasMorphNormals === true ) {
- normalLocal.addAssign( getMorph( {
- bufferMap,
- influence,
- stride,
- width,
- depth: i,
- offset: int( 1 )
- } ) );
- }
- } );
- }
- update() {
- const morphBaseInfluence = this.morphBaseInfluence;
- if ( this.mesh.geometry.morphTargetsRelative ) {
- morphBaseInfluence.value = 1;
- } else {
- morphBaseInfluence.value = 1 - this.mesh.morphTargetInfluences.reduce( ( a, b ) => a + b, 0 );
- }
- }
- }
- export default MorphNode;
- export const morphReference = nodeProxy( MorphNode );
- addNodeClass( 'MorphNode', MorphNode );
|