diff --git a/build/three.core.js b/build/three.core.js index 48326e23385880..e83710afe644f1 100644 --- a/build/three.core.js +++ b/build/three.core.js @@ -41728,6 +41728,18 @@ function convertArray( array, type ) { } +/** + * Returns `true` if the given keyframe track settings hold Bezier tangent data. + * + * @param {?Object} settings - The settings of a keyframe track. + * @return {boolean} Whether both tangent arrays are defined or not. + */ +function hasTangents( settings ) { + + return settings !== undefined && settings.inTangents !== undefined && settings.outTangents !== undefined; + +} + /** * Returns an array by which times and values can be sorted. * @@ -42110,6 +42122,19 @@ class AnimationUtils { } + /** + * Returns `true` if the given keyframe track settings hold Bezier tangent data. + * + * @static + * @param {?Object} settings - The settings of a keyframe track. + * @return {boolean} Whether both tangent arrays are defined or not. + */ + static hasTangents( settings ) { + + return hasTangents( settings ); + + } + /** * Returns an array by which times and values can be sorted. * @@ -42939,6 +42964,15 @@ class KeyframeTrack { } + if ( hasTangents( track.settings ) ) { + + json.settings = { + inTangents: convertArray( track.settings.inTangents, Array ), + outTangents: convertArray( track.settings.outTangents, Array ) + }; + + } + } json.type = track.ValueTypeName; // mandatory @@ -43164,6 +43198,13 @@ class KeyframeTrack { } + if ( hasTangents( this.settings ) ) { + + scaleTangentTimes( this.settings.inTangents, timeScale ); + scaleTangentTimes( this.settings.outTangents, timeScale ); + + } + } return this; @@ -43439,12 +43480,33 @@ class KeyframeTrack { // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; + if ( hasTangents( this.settings ) ) { + + track.settings = { + inTangents: this.settings.inTangents.slice(), + outTangents: this.settings.outTangents.slice() + }; + + } + return track; } } +function scaleTangentTimes( tangents, timeScale ) { + + // tangents are [ time, value ] pairs, so only every second entry is a time + + for ( let i = 0, n = tangents.length; i !== n; i += 2 ) { + + tangents[ i ] *= timeScale; + + } + +} + /** * The value type name. * @@ -44225,18 +44287,31 @@ function parseKeyframeTrack( json ) { } + let track; + // derived classes can define a static parse method if ( trackType.parse !== undefined ) { - return trackType.parse( json ); + track = trackType.parse( json ); } else { // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); + track = new trackType( json.name, json.times, json.values, json.interpolation ); } + if ( hasTangents( json.settings ) ) { + + track.settings = { + inTangents: convertArray( json.settings.inTangents, Float32Array ), + outTangents: convertArray( json.settings.outTangents, Float32Array ) + }; + + } + + return track; + } /** diff --git a/build/three.module.js b/build/three.module.js index 3a56b819c4e19f..ebf2c9f90d21a2 100644 --- a/build/three.module.js +++ b/build/three.module.js @@ -17556,9 +17556,9 @@ class WebGLRenderer { const materialProperties = properties.get( material ); const program = materialProperties.currentProgram; - if ( program.isReady() ) { + if ( program === undefined || program.isReady() ) { - // remove any programs that report they're ready to use from the list + // stop waiting for materials that are ready to use or have been disposed materials.delete( material ); } diff --git a/build/three.tsl.js b/build/three.tsl.js index 7cab6721b991d1..b09f120b4cce9a 100644 --- a/build/three.tsl.js +++ b/build/three.tsl.js @@ -24,6 +24,9 @@ const Fn = TSL.Fn; const HALF_PI = TSL.HALF_PI; const INFINITY = TSL.INFINITY; const If = TSL.If; +const LTC_Evaluate = TSL.LTC_Evaluate; +const LTC_Evaluate_Volume = TSL.LTC_Evaluate_Volume; +const LTC_Uv = TSL.LTC_Uv; const Loop = TSL.Loop; const NodeAccess = TSL.NodeAccess; const NodeShaderStage = TSL.NodeShaderStage; @@ -676,4 +679,4 @@ const workgroupId = TSL.workgroupId; const workingToColorSpace = TSL.workingToColorSpace; const xor = TSL.xor; -export { BRDF_GGX, BRDF_Lambert, BRDF_Sheen, BasicPointShadowFilter, BasicShadowFilter, Break, Const, Continue, DFGLUT, D_GGX, D_GGX_Anisotropic, Discard, EPSILON, EnvironmentBRDF, F_Schlick, Fn, HALF_PI, INFINITY, If, Loop, NodeAccess, NodeShaderStage, NodeType, NodeUpdateType, OnAfterObjectUpdate, OnAfterRenderPipeline, OnBeforeFrameUpdate, OnBeforeMaterialUpdate, OnBeforeObjectUpdate, OnBeforeRenderPipeline, OnFrameUpdate, OnMaterialUpdate, OnObjectUpdate, PCFShadowFilter, PI, PI2, PointShadowFilter, Return, Schlick_to_F0, ShaderNode, Stack, Switch, TBNViewMatrix, TWO_PI, VSMShadowFilter, V_GGX_SmithCorrelated, V_GGX_SmithCorrelated_Anisotropic, Var, VarIntent, abs, acesFilmicToneMapping, acos, acosh, add, addMethodChaining, addNodeElement, agxToneMapping, all, alphaT, ambientOcclusion, and, anisotropy, anisotropyB, anisotropyT, any, array, asin, asinh, assign, atan, atanh, atomicAdd, atomicAnd, atomicFunc, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, attenuationColor, attenuationDistance, attribute, attributeArray, backgroundBlurriness, backgroundIntensity, backgroundRotation, batch, batchColor, batchIndirectIndex, bentNormalView, billboarding, bitAnd, bitNot, bitOr, bitXor, bitangentGeometry, bitangentLocal, bitangentView, bitangentViewFrame, bitangentWorld, bitcast, blendBurn, blendColor, blendDodge, blendOverlay, blendScreen, bool, buffer, bufferAttribute, builtin, builtinAOContext, builtinShadowContext, bumpMap, bvec2, bvec3, bvec4, bypass, cache, call, cameraFar, cameraIndex, cameraNear, cameraNormalMatrix, cameraPosition, cameraProjectionMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraViewport, cameraWorldMatrix, cbrt, cdl, ceil, checker, cineonToneMapping, clamp, clearcoat, clearcoatNormalView, clearcoatRoughness, clipSpace, clipping, clippingAlpha, code, color, colorSpaceToWorking, colorToDirection, compute, computeKernel, computeSkinning, context, convert, convertColorSpace, convertToTexture, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, cubeTexture, cubeTextureBase, dFdx, dFdy, dashSize, debug, decrement, decrementBefore, defaultBuildStages, defaultShaderStages, defined, degrees, deltaTime, densityFogFactor, depth, depthPass, determinant, difference, diffuseColor, diffuseContribution, directPointLight, directionToColor, directionToFaceDirection, dispersion, distance, div, dot, drawIndex, dynamicBufferAttribute, element, emissive, equal, equirectDirection, equirectUV, exp, exp2, exponentialHeightFogFactor, expression, faceDirection, faceForward, faceforward, float, floatBitsToInt, floatBitsToUint, floor, fog, fract, frameGroup, frameId, frontFacing, fwidth, gain, gapSize, getConstNodeType, getCurrentStack, getDistanceAttenuation, getGeometryRoughness, getNormalFromDepth, getParallaxCorrectNormal, getRoughness, getScreenPosition, getScreenPositionFromClip, getShIrradianceAt, getTextureIndex, getViewPosition, globalId, glsl, glslFn, grayscale, greaterThan, greaterThanEqual, hardwareClipping, hash, highpModelNormalViewMatrix, highpModelViewMatrix, hue, increment, incrementBefore, inspect, instance, instanceColor, instanceIndex, instancedArray, instancedBufferAttribute, instancedDynamicBufferAttribute, instancedMesh, int, intBitsToFloat, interleavedGradientNoise, inverse, inverseSqrt, inversesqrt, invocationLocalIndex, invocationSubgroupIndex, ior, iridescence, iridescenceIOR, iridescenceThickness, isolate, ivec2, ivec3, ivec4, js, label, length, lengthSq, lessThan, lessThanEqual, lightPosition, lightProjectionUV, lightShadowMatrix, lightTargetDirection, lightTargetPosition, lightViewPosition, lightingContext, lights, linearDepth, linearToneMapping, localId, log, log2, logarithmicDepthToViewZ, luminance, mat2, mat3, mat4, matcapUV, materialAO, materialAlphaTest, materialAnisotropy, materialAnisotropyVector, materialAttenuationColor, materialAttenuationDistance, materialClearcoat, materialClearcoatNormal, materialClearcoatRoughness, materialColor, materialDispersion, materialEmissive, materialEnvIntensity, materialEnvRotation, materialIOR, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLightMap, materialLineDashOffset, materialLineDashSize, materialLineGapSize, materialLineScale, materialLineWidth, materialMetalness, materialNormal, materialOpacity, materialPointSize, materialReference, materialReflectivity, materialRefractionRatio, materialRetroreflectivity, materialRotation, materialRoughness, materialSheen, materialSheenRoughness, materialShininess, materialSpecular, materialSpecularColor, materialSpecularIntensity, materialSpecularStrength, materialThickness, materialTransmission, max, maxMipLevel, mediumpModelViewMatrix, metalness, min, mix, mixElement, mod, modelDirection, modelNormalMatrix, modelPosition, modelRadius, modelScale, modelViewMatrix, modelViewPosition, modelViewProjection, modelWorldMatrix, modelWorldMatrixInverse, morphReference, mrt, mul, mx_aastep, mx_add, mx_atan2, mx_cell_noise_float, mx_cell_noise_vec3, mx_contrast, mx_divide, mx_fractal_noise_float, mx_fractal_noise_float_2d, mx_fractal_noise_vec2, mx_fractal_noise_vec3, mx_fractal_noise_vec4, mx_frame, mx_heighttonormal, mx_hsvtorgb, mx_ifequal, mx_ifgreater, mx_ifgreatereq, mx_invert, mx_modulo, mx_multiply, mx_noise_float, mx_noise_vec3, mx_noise_vec4, mx_place2d, mx_power, mx_ramp4, mx_ramplr, mx_ramptb, mx_rgbtohsv, mx_rotate2d, mx_rotate3d, mx_safepower, mx_separate, mx_smoothstep, mx_splitlr, mx_splittb, mx_srgb_texture_to_lin_rec709, mx_subtract, mx_timer, mx_transform_uv, mx_unifiednoise2d, mx_unifiednoise3d, mx_worley_noise_float, mx_worley_noise_float_2d, mx_worley_noise_float_3d, mx_worley_noise_vec2, mx_worley_noise_vec3, mx_worley_noise_vec3_style, negate, negateOnBackSide, neutralToneMapping, nodeArray, nodeImmutable, nodeObject, nodeObjectIntent, nodeObjects, nodeProxy, nodeProxyConstructor, nodeProxyIntent, normalFlat, normalGeometry, normalLocal, normalMap, normalView, normalViewGeometry, normalWorld, normalWorldGeometry, normalize, not, notEqual, numWorkgroups, objectDirection, objectGroup, objectPosition, objectRadius, objectScale, objectViewPosition, objectWorldMatrix, oneMinus, or, orthographicDepthToViewZ, oscSawtooth, oscSine, oscSquare, oscTriangle, output, outputStruct, overloadingFn, overrideNode, overrideNodes, packHalf2x16, packNormalToRGB, packSnorm2x16, packSnorm4x8, packUnorm2x16, packUnorm4x8, parabola, parallaxDirection, parallaxUV, parameter, pass, passTexture, pcurve, perspectiveDepthToViewZ, pmremTexture, pointShadow, pointUV, pointWidth, positionGeometry, positionLocal, positionPrevious, positionView, positionViewDirection, positionWorld, positionWorldDirection, posterize, pow, pow2, pow3, pow4, premultiplyAlpha, property, quadBroadcast, quadSwapDiagonal, quadSwapX, quadSwapY, radians, rand, range, rangeFogFactor, reciprocal, reference, referenceBuffer, reflect, reflectVector, reflectView, reflector, refract, refractVector, refractView, reinhardToneMapping, remap, remapClamp, renderGroup, renderOutput, rendererReference, replaceDefaultUV, retroreflectivity, rotate, rotateUV, roughness, round, rtt, sRGBTransferEOTF, sRGBTransferOETF, sample, sampler, samplerComparison, saturate, saturation, screenCoordinate, screenDPR, screenSize, screenUV, select, setCurrentStack, setName, shaderStages, shadow, shadowPositionWorld, shapeCircle, sharedUniformGroup, sheen, sheenRoughness, shiftLeft, shiftRight, shininess, sign, sin, sinc, sinh, skinning, smoothstep, smoothstepElement, specularColor, specularColorBlended, specularF90, spherizeUV, split, spritesheetUV, sqrt, stack, step, stepElement, storage, storageBarrier, storageElement, storageTexture, storageTexture3D, struct, sub, subBuild, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupIndex, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupSize, subgroupXor, tan, tangentGeometry, tangentLocal, tangentView, tangentViewFrame, tangentWorld, tanh, texture, texture3D, texture3DLevel, texture3DLoad, textureBarrier, textureBicubic, textureBicubicLevel, textureLevel, textureLoad, textureSize, textureStore, thickness, time, toneMapping, toneMappingExposure, toonOutlinePass, transformDirection, transformNormal, transformNormalByInverseViewMatrix, transformNormalByViewMatrix, transformNormalToView, transformedClearcoatNormalView, transformedNormalView, transformedNormalWorld, transmission, transpose, triNoise3D, triplanarTexture, triplanarTextures, trunc, uint, uintBitsToFloat, uniform, uniformArray, uniformCubeTexture, uniformFlow, uniformGroup, uniformTexture, unpackHalf2x16, unpackNormal, unpackRGBToNormal, unpackSnorm2x16, unpackSnorm4x8, unpackUnorm2x16, unpackUnorm4x8, unpremultiplyAlpha, userData, uv, uvec2, uvec3, uvec4, varying, varyingProperty, vec2, vec3, vec4, vectorComponents, velocity, vertexColor, vertexIndex, vertexStage, vibrance, viewZToLogarithmicDepth, viewZToOrthographicDepth, viewZToPerspectiveDepth, viewZToReversedOrthographicDepth, viewZToReversedPerspectiveDepth, viewport, viewportCoordinate, viewportDepthTexture, viewportLinearDepth, viewportMipTexture, viewportOpaqueMipTexture, viewportSafeUV, viewportSharedTexture, viewportSize, viewportTexture, viewportUV, vogelDiskSample, wgsl, wgslFn, workgroupArray, workgroupBarrier, workgroupId, workingToColorSpace, xor }; +export { BRDF_GGX, BRDF_Lambert, BRDF_Sheen, BasicPointShadowFilter, BasicShadowFilter, Break, Const, Continue, DFGLUT, D_GGX, D_GGX_Anisotropic, Discard, EPSILON, EnvironmentBRDF, F_Schlick, Fn, HALF_PI, INFINITY, If, LTC_Evaluate, LTC_Evaluate_Volume, LTC_Uv, Loop, NodeAccess, NodeShaderStage, NodeType, NodeUpdateType, OnAfterObjectUpdate, OnAfterRenderPipeline, OnBeforeFrameUpdate, OnBeforeMaterialUpdate, OnBeforeObjectUpdate, OnBeforeRenderPipeline, OnFrameUpdate, OnMaterialUpdate, OnObjectUpdate, PCFShadowFilter, PI, PI2, PointShadowFilter, Return, Schlick_to_F0, ShaderNode, Stack, Switch, TBNViewMatrix, TWO_PI, VSMShadowFilter, V_GGX_SmithCorrelated, V_GGX_SmithCorrelated_Anisotropic, Var, VarIntent, abs, acesFilmicToneMapping, acos, acosh, add, addMethodChaining, addNodeElement, agxToneMapping, all, alphaT, ambientOcclusion, and, anisotropy, anisotropyB, anisotropyT, any, array, asin, asinh, assign, atan, atanh, atomicAdd, atomicAnd, atomicFunc, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, attenuationColor, attenuationDistance, attribute, attributeArray, backgroundBlurriness, backgroundIntensity, backgroundRotation, batch, batchColor, batchIndirectIndex, bentNormalView, billboarding, bitAnd, bitNot, bitOr, bitXor, bitangentGeometry, bitangentLocal, bitangentView, bitangentViewFrame, bitangentWorld, bitcast, blendBurn, blendColor, blendDodge, blendOverlay, blendScreen, bool, buffer, bufferAttribute, builtin, builtinAOContext, builtinShadowContext, bumpMap, bvec2, bvec3, bvec4, bypass, cache, call, cameraFar, cameraIndex, cameraNear, cameraNormalMatrix, cameraPosition, cameraProjectionMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraViewport, cameraWorldMatrix, cbrt, cdl, ceil, checker, cineonToneMapping, clamp, clearcoat, clearcoatNormalView, clearcoatRoughness, clipSpace, clipping, clippingAlpha, code, color, colorSpaceToWorking, colorToDirection, compute, computeKernel, computeSkinning, context, convert, convertColorSpace, convertToTexture, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, cubeTexture, cubeTextureBase, dFdx, dFdy, dashSize, debug, decrement, decrementBefore, defaultBuildStages, defaultShaderStages, defined, degrees, deltaTime, densityFogFactor, depth, depthPass, determinant, difference, diffuseColor, diffuseContribution, directPointLight, directionToColor, directionToFaceDirection, dispersion, distance, div, dot, drawIndex, dynamicBufferAttribute, element, emissive, equal, equirectDirection, equirectUV, exp, exp2, exponentialHeightFogFactor, expression, faceDirection, faceForward, faceforward, float, floatBitsToInt, floatBitsToUint, floor, fog, fract, frameGroup, frameId, frontFacing, fwidth, gain, gapSize, getConstNodeType, getCurrentStack, getDistanceAttenuation, getGeometryRoughness, getNormalFromDepth, getParallaxCorrectNormal, getRoughness, getScreenPosition, getScreenPositionFromClip, getShIrradianceAt, getTextureIndex, getViewPosition, globalId, glsl, glslFn, grayscale, greaterThan, greaterThanEqual, hardwareClipping, hash, highpModelNormalViewMatrix, highpModelViewMatrix, hue, increment, incrementBefore, inspect, instance, instanceColor, instanceIndex, instancedArray, instancedBufferAttribute, instancedDynamicBufferAttribute, instancedMesh, int, intBitsToFloat, interleavedGradientNoise, inverse, inverseSqrt, inversesqrt, invocationLocalIndex, invocationSubgroupIndex, ior, iridescence, iridescenceIOR, iridescenceThickness, isolate, ivec2, ivec3, ivec4, js, label, length, lengthSq, lessThan, lessThanEqual, lightPosition, lightProjectionUV, lightShadowMatrix, lightTargetDirection, lightTargetPosition, lightViewPosition, lightingContext, lights, linearDepth, linearToneMapping, localId, log, log2, logarithmicDepthToViewZ, luminance, mat2, mat3, mat4, matcapUV, materialAO, materialAlphaTest, materialAnisotropy, materialAnisotropyVector, materialAttenuationColor, materialAttenuationDistance, materialClearcoat, materialClearcoatNormal, materialClearcoatRoughness, materialColor, materialDispersion, materialEmissive, materialEnvIntensity, materialEnvRotation, materialIOR, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLightMap, materialLineDashOffset, materialLineDashSize, materialLineGapSize, materialLineScale, materialLineWidth, materialMetalness, materialNormal, materialOpacity, materialPointSize, materialReference, materialReflectivity, materialRefractionRatio, materialRetroreflectivity, materialRotation, materialRoughness, materialSheen, materialSheenRoughness, materialShininess, materialSpecular, materialSpecularColor, materialSpecularIntensity, materialSpecularStrength, materialThickness, materialTransmission, max, maxMipLevel, mediumpModelViewMatrix, metalness, min, mix, mixElement, mod, modelDirection, modelNormalMatrix, modelPosition, modelRadius, modelScale, modelViewMatrix, modelViewPosition, modelViewProjection, modelWorldMatrix, modelWorldMatrixInverse, morphReference, mrt, mul, mx_aastep, mx_add, mx_atan2, mx_cell_noise_float, mx_cell_noise_vec3, mx_contrast, mx_divide, mx_fractal_noise_float, mx_fractal_noise_float_2d, mx_fractal_noise_vec2, mx_fractal_noise_vec3, mx_fractal_noise_vec4, mx_frame, mx_heighttonormal, mx_hsvtorgb, mx_ifequal, mx_ifgreater, mx_ifgreatereq, mx_invert, mx_modulo, mx_multiply, mx_noise_float, mx_noise_vec3, mx_noise_vec4, mx_place2d, mx_power, mx_ramp4, mx_ramplr, mx_ramptb, mx_rgbtohsv, mx_rotate2d, mx_rotate3d, mx_safepower, mx_separate, mx_smoothstep, mx_splitlr, mx_splittb, mx_srgb_texture_to_lin_rec709, mx_subtract, mx_timer, mx_transform_uv, mx_unifiednoise2d, mx_unifiednoise3d, mx_worley_noise_float, mx_worley_noise_float_2d, mx_worley_noise_float_3d, mx_worley_noise_vec2, mx_worley_noise_vec3, mx_worley_noise_vec3_style, negate, negateOnBackSide, neutralToneMapping, nodeArray, nodeImmutable, nodeObject, nodeObjectIntent, nodeObjects, nodeProxy, nodeProxyConstructor, nodeProxyIntent, normalFlat, normalGeometry, normalLocal, normalMap, normalView, normalViewGeometry, normalWorld, normalWorldGeometry, normalize, not, notEqual, numWorkgroups, objectDirection, objectGroup, objectPosition, objectRadius, objectScale, objectViewPosition, objectWorldMatrix, oneMinus, or, orthographicDepthToViewZ, oscSawtooth, oscSine, oscSquare, oscTriangle, output, outputStruct, overloadingFn, overrideNode, overrideNodes, packHalf2x16, packNormalToRGB, packSnorm2x16, packSnorm4x8, packUnorm2x16, packUnorm4x8, parabola, parallaxDirection, parallaxUV, parameter, pass, passTexture, pcurve, perspectiveDepthToViewZ, pmremTexture, pointShadow, pointUV, pointWidth, positionGeometry, positionLocal, positionPrevious, positionView, positionViewDirection, positionWorld, positionWorldDirection, posterize, pow, pow2, pow3, pow4, premultiplyAlpha, property, quadBroadcast, quadSwapDiagonal, quadSwapX, quadSwapY, radians, rand, range, rangeFogFactor, reciprocal, reference, referenceBuffer, reflect, reflectVector, reflectView, reflector, refract, refractVector, refractView, reinhardToneMapping, remap, remapClamp, renderGroup, renderOutput, rendererReference, replaceDefaultUV, retroreflectivity, rotate, rotateUV, roughness, round, rtt, sRGBTransferEOTF, sRGBTransferOETF, sample, sampler, samplerComparison, saturate, saturation, screenCoordinate, screenDPR, screenSize, screenUV, select, setCurrentStack, setName, shaderStages, shadow, shadowPositionWorld, shapeCircle, sharedUniformGroup, sheen, sheenRoughness, shiftLeft, shiftRight, shininess, sign, sin, sinc, sinh, skinning, smoothstep, smoothstepElement, specularColor, specularColorBlended, specularF90, spherizeUV, split, spritesheetUV, sqrt, stack, step, stepElement, storage, storageBarrier, storageElement, storageTexture, storageTexture3D, struct, sub, subBuild, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupIndex, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupSize, subgroupXor, tan, tangentGeometry, tangentLocal, tangentView, tangentViewFrame, tangentWorld, tanh, texture, texture3D, texture3DLevel, texture3DLoad, textureBarrier, textureBicubic, textureBicubicLevel, textureLevel, textureLoad, textureSize, textureStore, thickness, time, toneMapping, toneMappingExposure, toonOutlinePass, transformDirection, transformNormal, transformNormalByInverseViewMatrix, transformNormalByViewMatrix, transformNormalToView, transformedClearcoatNormalView, transformedNormalView, transformedNormalWorld, transmission, transpose, triNoise3D, triplanarTexture, triplanarTextures, trunc, uint, uintBitsToFloat, uniform, uniformArray, uniformCubeTexture, uniformFlow, uniformGroup, uniformTexture, unpackHalf2x16, unpackNormal, unpackRGBToNormal, unpackSnorm2x16, unpackSnorm4x8, unpackUnorm2x16, unpackUnorm4x8, unpremultiplyAlpha, userData, uv, uvec2, uvec3, uvec4, varying, varyingProperty, vec2, vec3, vec4, vectorComponents, velocity, vertexColor, vertexIndex, vertexStage, vibrance, viewZToLogarithmicDepth, viewZToOrthographicDepth, viewZToPerspectiveDepth, viewZToReversedOrthographicDepth, viewZToReversedPerspectiveDepth, viewport, viewportCoordinate, viewportDepthTexture, viewportLinearDepth, viewportMipTexture, viewportOpaqueMipTexture, viewportSafeUV, viewportSharedTexture, viewportSize, viewportTexture, viewportUV, vogelDiskSample, wgsl, wgslFn, workgroupArray, workgroupBarrier, workgroupId, workingToColorSpace, xor }; diff --git a/build/three.webgpu.js b/build/three.webgpu.js index 7223dccdb9126d..4921c208d6357a 100644 --- a/build/three.webgpu.js +++ b/build/three.webgpu.js @@ -793,11 +793,15 @@ class NodeMaterialObserver { for ( let i = 0; i < lightsData.length; i ++ ) { const lightData = renderObjectData.lights[ i ]; + const currentLightData = lightsData[ i ]; - if ( lightData.map !== lightsData[ i ].map || lightData.cacheVersion !== lightsData[ i ].cacheVersion ) { + if ( lightData.map !== currentLightData.map || lightData.cacheVersion !== currentLightData.cacheVersion || + lightData.shadowMapWidth !== currentLightData.shadowMapWidth || lightData.shadowMapHeight !== currentLightData.shadowMapHeight ) { - lightData.map = lightsData[ i ].map; - lightData.cacheVersion = lightsData[ i ].cacheVersion; + lightData.map = currentLightData.map; + lightData.cacheVersion = currentLightData.cacheVersion; + lightData.shadowMapWidth = currentLightData.shadowMapWidth; + lightData.shadowMapHeight = currentLightData.shadowMapHeight; return false; @@ -863,14 +867,30 @@ class NodeMaterialObserver { for ( const light of materialLights ) { + let data = null; + if ( light.isSpotLight === true && light.map !== null ) { // only add lights that have a map - lights.push( { map: light.map.version, cacheVersion: this.getTextureData( light.map )._version } ); + data = { map: light.map.version, cacheVersion: this.getTextureData( light.map )._version }; + + } + + if ( light.castShadow === true && light.shadow !== undefined ) { + + // resizing a shadow map recreates its textures so the bindings + // of all related render objects must be updated + + if ( data === null ) data = {}; + + data.shadowMapWidth = light.shadow.mapSize.width; + data.shadowMapHeight = light.shadow.mapSize.height; } + if ( data !== null ) lights.push( data ); + } return lights; @@ -2149,9 +2169,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#update}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateType() { + getUpdateType( /*frame*/ ) { return this.updateType; @@ -2160,9 +2181,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#updateBefore}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateBeforeType() { + getUpdateBeforeType( /*frame*/ ) { return this.updateBeforeType; @@ -2171,9 +2193,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#updateAfter}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateAfterType() { + getUpdateAfterType( /*frame*/ ) { return this.updateAfterType; @@ -7453,7 +7476,7 @@ class MathNode extends TempNode { const method = this.method; - if ( method === MathNode.LENGTH || method === MathNode.DISTANCE || method === MathNode.DOT ) { + if ( method === MathNode.LENGTH || method === MathNode.DISTANCE || method === MathNode.DOT || method === MathNode.DETERMINANT ) { return 'float'; @@ -7605,7 +7628,7 @@ class MathNode extends TempNode { if ( builder.shaderStage !== 'fragment' && ( method === MathNode.DFDX || method === MathNode.DFDY ) ) { - warn( `TSL: '${ method }' is not supported in the ${ builder.shaderStage } stage.`, this.stackTrace ); + error( `TSL: '${ method }' is not supported in the ${ builder.shaderStage } stage.`, this.stackTrace ); method = '/*' + method + '*/'; @@ -10923,7 +10946,7 @@ const instancedBufferAttribute = ( array, type = null, stride = 0, offset = 0 ) */ const instancedDynamicBufferAttribute = ( array, type = null, stride = 0, offset = 0 ) => createBufferAttribute( array, type, stride, offset, DynamicDrawUsage, true ); -addMethodChaining( 'toAttribute', ( bufferNode ) => bufferAttribute( bufferNode.value ) ); +addMethodChaining( 'toAttribute', ( bufferNode ) => bufferAttribute( bufferNode.value, bufferNode.bufferType ) ); /** * This class represents shader indices of different types. The following predefined node @@ -11163,15 +11186,6 @@ class ComputeNode extends Node { */ this.name = ''; - /** - * The `updateBeforeType` is set to `NodeUpdateType.OBJECT` since {@link ComputeNode#updateBefore} - * is executed once per object by default. - * - * @type {string} - * @default 'object' - */ - this.updateBeforeType = NodeUpdateType.OBJECT; - /** * A callback executed when the compute node finishes initialization. * @@ -11187,6 +11201,15 @@ class ComputeNode extends Node { */ this.countNode = null; + /** + * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node computes + * once per frame. + * + * @type {string} + * @default 'frame' + */ + this.updateBeforeType = NodeUpdateType.FRAME; + } /** @@ -11241,6 +11264,18 @@ class ComputeNode extends Node { } + /** + * Returns the update type of {@link Node#updateBefore}. + * + * @param {NodeFrame} [frame] - The current node frame. + * @return {NodeUpdateType} The update type. + */ + getUpdateBeforeType( frame ) { + + return frame.compute !== this ? this.updateBeforeType : NodeUpdateType.NONE; + + } + /** * The method execute the compute for this node. * @@ -12085,11 +12120,6 @@ class InspectorBase extends EventDispatcher { } - /** - * Initializes the inspector. - */ - init() { } - /** * Called when a frame begins. */ @@ -12171,6 +12201,17 @@ class InspectorBase extends EventDispatcher { */ copyFramebufferToTexture( /*framebufferTexture*/ ) { } + /** + * Frees all internal resources of the inspector. + */ + dispose() { + + if ( this.isRunning === true ) this.finish(); + + this.dispatchEvent( { type: 'dispose' } ); + + } + } /** @@ -14323,27 +14364,6 @@ class ScreenNode extends Node { } - /** - * This method is overwritten since the node's update type depends on the selected scope. - * - * @return {NodeUpdateType} The update type. - */ - getUpdateType() { - - let updateType = NodeUpdateType.NONE; - - if ( this.scope === ScreenNode.SIZE || this.scope === ScreenNode.VIEWPORT ) { - - updateType = NodeUpdateType.RENDER; - - } - - this.updateType = updateType; - - return updateType; - - } - /** * `ScreenNode` implements {@link Node#update} to retrieve viewport and size information * from the current renderer. @@ -14405,6 +14425,20 @@ class ScreenNode extends Node { } + // + + let updateType = NodeUpdateType.NONE; + + if ( this.scope === ScreenNode.SIZE || this.scope === ScreenNode.VIEWPORT ) { + + updateType = NodeUpdateType.RENDER; + + } + + this.updateType = updateType; + + // + return output; } @@ -15164,6 +15198,8 @@ const objectViewPosition = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.V */ const objectRadius = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.RADIUS ).setParameterLength( 1 ); +const _modelViewMatrix = /*@__PURE__*/ new Matrix4(); + /** * This type of node is a specialized version of `Object3DNode` * with larger set of model related metrics. Unlike `Object3DNode`, @@ -15306,11 +15342,46 @@ const highpModelViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => { builder.context.isHighPrecisionModelViewMatrix = true; - return uniform( 'mat4' ).onObjectUpdate( ( { object, camera } ) => { + const camera = builder.camera; - return object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + let highpModelViewMatrix; - } ); + if ( camera.isArrayCamera && camera.cameras.length > 0 ) { + + const matrices = []; + + for ( let i = 0; i < camera.cameras.length; i ++ ) { + + matrices.push( new Matrix4() ); + + } + + const modelViewMatrices = uniformArray( matrices ).onObjectUpdate( ( { object, camera }, self ) => { + + const subCameras = camera.cameras; + const array = self.array; + + for ( let i = 0, l = subCameras.length; i < l; i ++ ) { + + array[ i ].multiplyMatrices( subCameras[ i ].matrixWorldInverse, object.matrixWorld ); + + } + + } ); + + highpModelViewMatrix = modelViewMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); + + } else { + + highpModelViewMatrix = uniform( 'mat4' ).onObjectUpdate( ( { object, camera } ) => { + + return object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + + } ); + + } + + return highpModelViewMatrix; } ).once() )().toVar( 'highpModelViewMatrix' ); @@ -15325,17 +15396,54 @@ const highpModelNormalViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => { const isHighPrecisionModelViewMatrix = builder.context.isHighPrecisionModelViewMatrix; - return uniform( 'mat3' ).onObjectUpdate( ( { object, camera } ) => { + const camera = builder.camera; + + let highpModelNormalViewMatrix; + + if ( camera.isArrayCamera && camera.cameras.length > 0 ) { + + const matrices = []; - if ( isHighPrecisionModelViewMatrix !== true ) { + for ( let i = 0; i < camera.cameras.length; i ++ ) { - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + matrices.push( new Matrix3() ); } - return object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + const normalViewMatrices = uniformArray( matrices ).onObjectUpdate( ( { object, camera }, self ) => { - } ); + const subCameras = camera.cameras; + const array = self.array; + + for ( let i = 0, l = subCameras.length; i < l; i ++ ) { + + _modelViewMatrix.multiplyMatrices( subCameras[ i ].matrixWorldInverse, object.matrixWorld ); + + array[ i ].getNormalMatrix( _modelViewMatrix ); + + } + + } ); + + highpModelNormalViewMatrix = normalViewMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); + + } else { + + highpModelNormalViewMatrix = uniform( 'mat3' ).onObjectUpdate( ( { object, camera } ) => { + + if ( isHighPrecisionModelViewMatrix !== true ) { + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + + } + + return object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + + } ); + + } + + return highpModelNormalViewMatrix; } ).once() )().toVar( 'highpModelNormalViewMatrix' ); @@ -25050,7 +25158,7 @@ const LTC_Evaluate_Volume = /*@__PURE__*/ Fn( ( { P, p0, p1, p2, p3 } ) => { return result; } ).setLayout( { - name: 'LTC_Evaluate', + name: 'LTC_Evaluate_Volume', type: 'vec3', inputs: [ { name: 'P', type: 'vec3' }, @@ -28968,8 +29076,9 @@ class RotateNode extends TempNode { * @param {Node} positionNode - The position node. * @param {Node} rotationNode - Represents the rotation that is applied to the position node. Depending * on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. + * @param {string} [order='XYZ'] - The Euler rotation order. Only used for 3D rotation. */ - constructor( positionNode, rotationNode ) { + constructor( positionNode, rotationNode, order = 'XYZ' ) { super(); @@ -28981,13 +29090,59 @@ class RotateNode extends TempNode { this.positionNode = positionNode; /** - * Represents the rotation that is applied to the position node. - * Depending on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. + * Represents the rotation that is applied to the position node. + * Depending on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. * * @type {Node} */ this.rotationNode = rotationNode; + /** + * The Euler rotation order. + * + * @private + * @type {string} + * @default 'XYZ' + */ + this._order = order; + + } + + /** + * Overwrites the default `customCacheKey()` implementation by including the + * Euler order into the cache key. + * + * @return {number} The hash. + */ + customCacheKey() { + + return hashString( this._order ); + + } + + /** + * Sets the Euler rotation order. + * + * @param {string} value - The Euler rotation order. + * @return {RotateNode} A reference to this node. + */ + setOrder( value ) { + + this._order = value; + + return this; + + } + + /** + * Gets the Euler rotation order. + * + * @return {string} The Euler rotation order. + */ + getOrder() { + + return this._order; + } /** @@ -29023,16 +29178,44 @@ class RotateNode extends TempNode { } else { const rotation = rotationNode; - const rotationXMatrix = mat4( vec4( 1.0, 0.0, 0.0, 0.0 ), vec4( 0.0, cos( rotation.x ), sin( rotation.x ).negate(), 0.0 ), vec4( 0.0, sin( rotation.x ), cos( rotation.x ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); - const rotationYMatrix = mat4( vec4( cos( rotation.y ), 0.0, sin( rotation.y ), 0.0 ), vec4( 0.0, 1.0, 0.0, 0.0 ), vec4( sin( rotation.y ).negate(), 0.0, cos( rotation.y ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); - const rotationZMatrix = mat4( vec4( cos( rotation.z ), sin( rotation.z ).negate(), 0.0, 0.0 ), vec4( sin( rotation.z ), cos( rotation.z ), 0.0, 0.0 ), vec4( 0.0, 0.0, 1.0, 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); + const order = this._order; + + const rotationXMatrix = mat4( vec4( 1.0, 0.0, 0.0, 0.0 ), vec4( 0.0, cos( rotation.x ), sin( rotation.x ), 0.0 ), vec4( 0.0, sin( rotation.x ).negate(), cos( rotation.x ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); + const rotationYMatrix = mat4( vec4( cos( rotation.y ), 0.0, sin( rotation.y ).negate(), 0.0 ), vec4( 0.0, 1.0, 0.0, 0.0 ), vec4( sin( rotation.y ), 0.0, cos( rotation.y ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); + const rotationZMatrix = mat4( vec4( cos( rotation.z ), sin( rotation.z ), 0.0, 0.0 ), vec4( sin( rotation.z ).negate(), cos( rotation.z ), 0.0, 0.0 ), vec4( 0.0, 0.0, 1.0, 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); + + const matrixMap = { + 'X': rotationXMatrix, + 'Y': rotationYMatrix, + 'Z': rotationZMatrix + }; + + const matrixChain = matrixMap[ order.charAt( 0 ) ] + .mul( matrixMap[ order.charAt( 1 ) ] ) + .mul( matrixMap[ order.charAt( 2 ) ] ); - return rotationXMatrix.mul( rotationYMatrix ).mul( rotationZMatrix ).mul( vec4( positionNode, 1.0 ) ).xyz; + return matrixChain.mul( vec4( positionNode, 1.0 ) ).xyz; } } + serialize( data ) { + + super.serialize( data ); + + data.order = this._order; + + } + + deserialize( data ) { + + super.deserialize( data ); + + this._order = data.order; + + } + } /** @@ -29043,9 +29226,10 @@ class RotateNode extends TempNode { * @param {Node} positionNode - The position node. * @param {Node} rotationNode - Represents the rotation that is applied to the position node. Depending * on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. + * @param {string} [order='XYZ'] - The Euler rotation order. Only used for 3D rotation. * @returns {RotateNode} */ -const rotate = /*@__PURE__*/ nodeProxy( RotateNode ).setParameterLength( 2 ); +const rotate = /*@__PURE__*/ nodeProxy( RotateNode ).setParameterLength( 2, 3 ); const _defaultValues$2 = /*@__PURE__*/ new SpriteMaterial(); @@ -33562,6 +33746,8 @@ class Bindings extends DataMap { } + cacheKey += attribute.id + ','; + } if ( binding.isUniformBuffer ) { @@ -37085,6 +37271,12 @@ class BitcountNode extends MathNode { const fnDef = Fn( ( [ value ] ) => { + If( value.equal( uint( 0 ) ), () => { + + return uint( 32 ); + + } ); + const v = uint( 0.0 ); this._resolveElementType( value, v, elementType ); @@ -37443,7 +37635,11 @@ const parabola = ( x, k ) => pow( mul( 4.0, x.mul( sub( 1.0, x ) ) ), k ); * @param {Node} k - `k=1` is the identity curve,`k<1` produces the classic `gain()` shape, and `k>1` produces "s" shaped curves. * @return {Node} The remapped value. */ -const gain = ( x, k ) => x.lessThan( 0.5 ) ? parabola( x.mul( 2.0 ), k ).div( 2.0 ) : sub( 1.0, parabola( mul( sub( 1.0, x ), 2.0 ), k ).div( 2.0 ) ); +const gain = ( x, k ) => select( + x.lessThan( 0.5 ), + pow( mul( 2.0, x ), k ).mul( 0.5 ), + sub( 1.0, pow( mul( 2.0, sub( 1.0, x ) ), k ).mul( 0.5 ) ) +); /** * A function that remaps the `[0,1]` interval into the `[0,1]` interval. @@ -37457,7 +37653,7 @@ const gain = ( x, k ) => x.lessThan( 0.5 ) ? parabola( x.mul( 2.0 ), k ).div( 2. * @param {Node} b - Second control parameter. * @return {Node} The remapped value. */ -const pcurve = ( x, a, b ) => pow( div( pow( x, a ), add( pow( x, a ), pow( sub( 1.0, x ), b ) ) ), 1.0 / a ); +const pcurve = ( x, a, b ) => pow( div( pow( x, a ), add( pow( x, a ), pow( sub( 1.0, x ), b ) ) ), div( 1.0, a ) ); /** * A phase shifted sinus curve that starts at zero and ends at zero, with bouncing behavior. @@ -37469,7 +37665,13 @@ const pcurve = ( x, a, b ) => pow( div( pow( x, a ), add( pow( x, a ), pow( sub( * @param {Node} k - Controls the amount of bounces. * @return {Node} The result value. */ -const sinc = ( x, k ) => sin( PI.mul( k.mul( x ).sub( 1.0 ) ) ).div( PI.mul( k.mul( x ).sub( 1.0 ) ) ); +const sinc = ( x, k ) => { + + const arg = abs( PI.mul( k.mul( x ).sub( 1.0 ) ) ).max( 1e-6 ).toConst(); + + return sin( arg ).div( arg ); + +}; /** * This node represents an operation that packs floating-point values of a vector into an unsigned 32-bit integer @@ -38685,7 +38887,7 @@ class ReflectorBaseNode extends Node { if ( renderTarget === undefined ) { - renderTarget = new RenderTarget( 0, 0, { type: HalfFloatType, samples: this.samples } ); + renderTarget = new RenderTarget( 1, 1, { type: HalfFloatType, samples: this.samples } ); if ( this.generateMipmaps === true ) { @@ -39020,8 +39222,222 @@ class QuadMesh extends Mesh { } +/** + * Saves the state of the given renderer and stores it into the given state object. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function saveRendererState( renderer, state = {} ) { + + state.toneMapping = renderer.toneMapping; + state.toneMappingExposure = renderer.toneMappingExposure; + state.outputColorSpace = renderer.outputColorSpace; + state.renderTarget = renderer.getRenderTarget(); + state.activeCubeFace = renderer.getActiveCubeFace(); + state.activeMipmapLevel = renderer.getActiveMipmapLevel(); + state.renderObjectFunction = renderer.getRenderObjectFunction(); + state.pixelRatio = renderer.getPixelRatio(); + state.mrt = renderer.getMRT(); + state.clearColor = renderer.getClearColor( state.clearColor || new Color() ); + state.clearAlpha = renderer.getClearAlpha(); + state.autoClear = renderer.autoClear; + state.scissorTest = renderer.getScissorTest(); + + return state; + +} + +/** + * Saves the state of the given renderer and stores it into the given state object. + * Besides, the function also resets the state of the renderer to its default values. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function resetRendererState( renderer, state ) { + + state = saveRendererState( renderer, state ); + + renderer.setMRT( null ); + renderer.setRenderObjectFunction( null ); + renderer.setClearColor( 0x000000, 1 ); + renderer.autoClear = true; + + return state; + +} + +/** + * Restores the state of the given renderer from the given state object. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Object} state - The state to restore. + */ +function restoreRendererState( renderer, state ) { + + renderer.toneMapping = state.toneMapping; + renderer.toneMappingExposure = state.toneMappingExposure; + renderer.outputColorSpace = state.outputColorSpace; + renderer.setRenderTarget( state.renderTarget, state.activeCubeFace, state.activeMipmapLevel ); + renderer.setRenderObjectFunction( state.renderObjectFunction ); + renderer.setPixelRatio( state.pixelRatio ); + renderer.setMRT( state.mrt ); + renderer.setClearColor( state.clearColor, state.clearAlpha ); + renderer.autoClear = state.autoClear; + renderer.setScissorTest( state.scissorTest ); + +} + +/** + * Saves the state of the given scene and stores it into the given state object. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Scene} scene - The scene. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function saveSceneState( scene, state = {} ) { + + state.background = scene.background; + state.backgroundNode = scene.backgroundNode; + state.overrideMaterial = scene.overrideMaterial; + + return state; + +} + +/** + * Saves the state of the given scene and stores it into the given state object. + * Besides, the function also resets the state of the scene to its default values. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Scene} scene - The scene. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function resetSceneState( scene, state ) { + + state = saveSceneState( scene, state ); + + scene.background = null; + scene.backgroundNode = null; + scene.overrideMaterial = null; + + return state; + +} + +/** + * Restores the state of the given scene from the given state object. + * + * @private + * @function + * @param {Scene} scene - The scene. + * @param {Object} state - The state to restore. + */ +function restoreSceneState( scene, state ) { + + scene.background = state.background; + scene.backgroundNode = state.backgroundNode; + scene.overrideMaterial = state.overrideMaterial; + +} + +/** + * Saves the state of the given renderer and scene and stores it into the given state object. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Scene} scene - The scene. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function saveRendererAndSceneState( renderer, scene, state = {} ) { + + state = saveRendererState( renderer, state ); + state = saveSceneState( scene, state ); + + return state; + +} + +/** + * Saves the state of the given renderer and scene and stores it into the given state object. + * Besides, the function also resets the state of the renderer and scene to its default values. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Scene} scene - The scene. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function resetRendererAndSceneState( renderer, scene, state ) { + + state = resetRendererState( renderer, state ); + state = resetSceneState( scene, state ); + + return state; + +} + +/** + * Restores the state of the given renderer and scene from the given state object. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Scene} scene - The scene. + * @param {Object} state - The state to restore. + */ +function restoreRendererAndSceneState( renderer, scene, state ) { + + restoreRendererState( renderer, state ); + restoreSceneState( scene, state ); + +} + +var RendererUtils = /*#__PURE__*/Object.freeze({ + __proto__: null, + resetRendererAndSceneState: resetRendererAndSceneState, + resetRendererState: resetRendererState, + resetSceneState: resetSceneState, + restoreRendererAndSceneState: restoreRendererAndSceneState, + restoreRendererState: restoreRendererState, + restoreSceneState: restoreSceneState, + saveRendererAndSceneState: saveRendererAndSceneState, + saveRendererState: saveRendererState, + saveSceneState: saveSceneState +}); + const _size$1 = /*@__PURE__*/ new Vector2(); +let _rendererState$1; + /** * `RTTNode` takes another node and uses it with a `QuadMesh` to render into a texture (RTT). * This module is especially relevant in context of post processing where certain nodes require @@ -39042,7 +39458,7 @@ class RTTNode extends TextureNode { * Constructs a new RTT node. * * @param {Node} node - The node to render a texture with. - * @param {?number} [width=null] - The width of the internal render target. If not width is applied, the render target is automatically resized. + * @param {?number} [width=null] - The width of the internal render target. If no width is applied, the render target is automatically resized. * @param {?number} [height=null] - The height of the internal render target. * @param {Object} [options={}] - The options for the internal render target. * @param {number} [options.type=HalfFloatType] - The texture type. @@ -39056,7 +39472,7 @@ class RTTNode extends TextureNode { resolutionScale = 1 } = options; - const renderTarget = new RenderTarget( width, height, { type: HalfFloatType, ...options } ); + const renderTarget = new RenderTarget( width ?? 1, height ?? 1, { type: HalfFloatType, ...options } ); super( renderTarget.texture, uv$1() ); @@ -39134,13 +39550,13 @@ class RTTNode extends TextureNode { this._quadMesh = new QuadMesh( new NodeMaterial() ); /** - * The `updateBeforeType` is set to `NodeUpdateType.RENDER` since the node updates - * the texture once per render in its {@link RTTNode#updateBefore} method. + * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node updates + * the texture once per frame in its {@link RTTNode#updateBefore} method. * * @type {string} - * @default 'render' + * @default 'frame' */ - this.updateBeforeType = NodeUpdateType.RENDER; + this.updateBeforeType = NodeUpdateType.FRAME; } @@ -39169,10 +39585,10 @@ class RTTNode extends TextureNode { } /** - * Sets the size of the internal render target + * Sets the size of the internal render target. * * @param {number} width - The width to set. - * @param {number} height - The width to set. + * @param {number} height - The height to set. */ setSize( width, height ) { @@ -39217,7 +39633,42 @@ class RTTNode extends TextureNode { } - updateBefore( { renderer } ) { + /** + * Overwritten since the value is defined by the internal render target. + * + * @param {Texture} value - The texture value. + */ + set value( value ) { + + if ( this.renderTarget && value !== this.renderTarget.texture ) { + + error( 'TSL: "rtt()" does not allow overwriting the value.' ); + + } + + } + + /** + * The texture of the internal render target. + * + * @type {Texture} + */ + get value() { + + return this.renderTarget ? this.renderTarget.texture : null; + + } + + /** + * Renders the node's output into the internal render target before the main render pass. + * Handles automatic resizing of the render target when `autoResize` is enabled, + * and skips rendering if neither `textureNeedsUpdate` nor `autoUpdate` is true. + * + * @param {NodeFrame} frame - The current node frame, providing access to the renderer and other frame data. + */ + updateBefore( frame ) { + + const { renderer } = frame; if ( this.textureNeedsUpdate === false && this.autoUpdate === false ) return; @@ -39225,8 +39676,6 @@ class RTTNode extends TextureNode { // - const currentRenderTarget = renderer.getRenderTarget(); - if ( this.autoResize === true ) { const size = renderer.getDrawingBufferSize( _size$1 ); @@ -39248,9 +39697,11 @@ class RTTNode extends TextureNode { let name = 'RTT'; - if ( this.node.name ) { + const callName = this.name || this.node.name; - name = this.node.name + ' [ ' + name + ' ]'; + if ( callName ) { + + name = callName + ' [ ' + name + ' ]'; } @@ -39258,11 +39709,13 @@ class RTTNode extends TextureNode { // + _rendererState$1 = resetRendererState( renderer, _rendererState$1 ); + renderer.setRenderTarget( this.renderTarget ); this._quadMesh.render( renderer ); - renderer.setRenderTarget( currentRenderTarget ); + restoreRendererState( renderer, _rendererState$1 ); } @@ -39276,6 +39729,18 @@ class RTTNode extends TextureNode { } + /** + * Frees internal resources. Should be called when the node is no longer in use. + */ + dispose() { + + this.renderTarget.dispose(); + this._quadMesh.material.dispose(); + + super.dispose(); + + } + } /** @@ -39284,7 +39749,7 @@ class RTTNode extends TextureNode { * @tsl * @function * @param {Node} node - The node to render a texture with. - * @param {?number} [width=null] - The width of the internal render target. If not width is applied, the render target is automatically resized. + * @param {?number} [width=null] - The width of the internal render target. If no width is applied, the render target is automatically resized. * @param {?number} [height=null] - The height of the internal render target. * @param {Object} [options={}] - The options for the internal render target. * @param {number} [options.type=HalfFloatType] - The texture type. @@ -39300,7 +39765,7 @@ const rtt = ( node, ...params ) => new RTTNode( nodeObject( node ), ...params ); * @tsl * @function * @param {Node} node - The node to render a texture with. - * @param {?number} [width=null] - The width of the internal render target. If not width is applied, the render target is automatically resized. + * @param {?number} [width=null] - The width of the internal render target. If no width is applied, the render target is automatically resized. * @param {?number} [height=null] - The height of the internal render target. * @param {Object} [options={}] - The options for the internal render target. * @param {number} [options.type=HalfFloatType] - The texture type. @@ -43322,7 +43787,7 @@ class ComputeBuiltinNode extends Node { } else { - warn( `ComputeBuiltinNode: Compute built-in value ${builtinName} can not be accessed in the ${builder.shaderStage} stage` ); + warn( `TSL: Compute built-in value "${builtinName}" can not be accessed in the ${builder.shaderStage} stage` ); return builder.generateConst( nodeType ); } @@ -43469,15 +43934,23 @@ class BarrierNode extends Node { generate( builder ) { const { scope } = this; - const { renderer } = builder; + const { renderer, shaderStage } = builder; + + const barrierMethod = `${scope}Barrier`; + + if ( shaderStage !== 'compute' ) { + + error( `TSL: "${barrierMethod}" is not supported in the ${shaderStage} stage and can only be executed in compute.` ); + + } if ( renderer.backend.isWebGLBackend === true ) { - builder.addFlowCode( `\t// ${scope}Barrier \n` ); + builder.addFlowCode( `\t// ${barrierMethod}() \n` ); } else { - builder.addLineFlowCode( `${scope}Barrier()`, this ); + builder.addLineFlowCode( `${barrierMethod}()`, this ); } @@ -43734,6 +44207,12 @@ class WorkgroupInfoNode extends Node { generate( builder ) { + if ( builder.shaderStage !== 'compute' ) { + + error( 'TSL: "workgroupArray()" can only be executed within the compute shader stage' ); + + } + const name = ( this.name !== '' ) ? this.name : `${this.scope}Array_${this.id}`; return builder.getScopedArray( name, this.scope.toLowerCase(), this.bufferType, this.bufferCount ); @@ -43842,11 +44321,17 @@ class AtomicFunctionNode extends Node { generate( builder ) { + const method = this.method; + + if ( builder.shaderStage === 'vertex' ) { + + error( `TSL: "${this.method}" is not supported in the vertex stage.` ); + + } + const properties = builder.getNodeProperties( this ); const parents = properties.parents; - const method = this.method; - const type = this.getNodeType( builder ); const inputType = this.getInputType( builder ); @@ -44164,6 +44649,12 @@ class SubgroupFunctionNode extends TempNode { const method = this.method; + if ( builder.shaderStage === 'vertex' ) { + + error( `TSL: "${this.method}" is not supported in the vertex shader stage.` ); + + } + const type = this.getNodeType( builder ); const inputType = this.getInputType( builder ); @@ -45494,218 +45985,6 @@ class ShadowBaseNode extends Node { */ const shadowPositionWorld = /*@__PURE__*/ property( 'vec3', 'shadowPositionWorld' ); -/** - * Saves the state of the given renderer and stores it into the given state object. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function saveRendererState( renderer, state = {} ) { - - state.toneMapping = renderer.toneMapping; - state.toneMappingExposure = renderer.toneMappingExposure; - state.outputColorSpace = renderer.outputColorSpace; - state.renderTarget = renderer.getRenderTarget(); - state.activeCubeFace = renderer.getActiveCubeFace(); - state.activeMipmapLevel = renderer.getActiveMipmapLevel(); - state.renderObjectFunction = renderer.getRenderObjectFunction(); - state.pixelRatio = renderer.getPixelRatio(); - state.mrt = renderer.getMRT(); - state.clearColor = renderer.getClearColor( state.clearColor || new Color() ); - state.clearAlpha = renderer.getClearAlpha(); - state.autoClear = renderer.autoClear; - state.scissorTest = renderer.getScissorTest(); - - return state; - -} - -/** - * Saves the state of the given renderer and stores it into the given state object. - * Besides, the function also resets the state of the renderer to its default values. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function resetRendererState( renderer, state ) { - - state = saveRendererState( renderer, state ); - - renderer.setMRT( null ); - renderer.setRenderObjectFunction( null ); - renderer.setClearColor( 0x000000, 1 ); - renderer.autoClear = true; - - return state; - -} - -/** - * Restores the state of the given renderer from the given state object. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Object} state - The state to restore. - */ -function restoreRendererState( renderer, state ) { - - renderer.toneMapping = state.toneMapping; - renderer.toneMappingExposure = state.toneMappingExposure; - renderer.outputColorSpace = state.outputColorSpace; - renderer.setRenderTarget( state.renderTarget, state.activeCubeFace, state.activeMipmapLevel ); - renderer.setRenderObjectFunction( state.renderObjectFunction ); - renderer.setPixelRatio( state.pixelRatio ); - renderer.setMRT( state.mrt ); - renderer.setClearColor( state.clearColor, state.clearAlpha ); - renderer.autoClear = state.autoClear; - renderer.setScissorTest( state.scissorTest ); - -} - -/** - * Saves the state of the given scene and stores it into the given state object. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Scene} scene - The scene. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function saveSceneState( scene, state = {} ) { - - state.background = scene.background; - state.backgroundNode = scene.backgroundNode; - state.overrideMaterial = scene.overrideMaterial; - - return state; - -} - -/** - * Saves the state of the given scene and stores it into the given state object. - * Besides, the function also resets the state of the scene to its default values. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Scene} scene - The scene. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function resetSceneState( scene, state ) { - - state = saveSceneState( scene, state ); - - scene.background = null; - scene.backgroundNode = null; - scene.overrideMaterial = null; - - return state; - -} - -/** - * Restores the state of the given scene from the given state object. - * - * @private - * @function - * @param {Scene} scene - The scene. - * @param {Object} state - The state to restore. - */ -function restoreSceneState( scene, state ) { - - scene.background = state.background; - scene.backgroundNode = state.backgroundNode; - scene.overrideMaterial = state.overrideMaterial; - -} - -/** - * Saves the state of the given renderer and scene and stores it into the given state object. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Scene} scene - The scene. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function saveRendererAndSceneState( renderer, scene, state = {} ) { - - state = saveRendererState( renderer, state ); - state = saveSceneState( scene, state ); - - return state; - -} - -/** - * Saves the state of the given renderer and scene and stores it into the given state object. - * Besides, the function also resets the state of the renderer and scene to its default values. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Scene} scene - The scene. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function resetRendererAndSceneState( renderer, scene, state ) { - - state = resetRendererState( renderer, state ); - state = resetSceneState( scene, state ); - - return state; - -} - -/** - * Restores the state of the given renderer and scene from the given state object. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Scene} scene - The scene. - * @param {Object} state - The state to restore. - */ -function restoreRendererAndSceneState( renderer, scene, state ) { - - restoreRendererState( renderer, state ); - restoreSceneState( scene, state ); - -} - -var RendererUtils = /*#__PURE__*/Object.freeze({ - __proto__: null, - resetRendererAndSceneState: resetRendererAndSceneState, - resetRendererState: resetRendererState, - resetSceneState: resetSceneState, - restoreRendererAndSceneState: restoreRendererAndSceneState, - restoreRendererState: restoreRendererState, - restoreSceneState: restoreSceneState, - saveRendererAndSceneState: saveRendererAndSceneState, - saveRendererState: saveRendererState, - saveSceneState: saveSceneState -}); - /** * A shadow filtering function performing basic filtering. This is in fact an unfiltered version of the shadow map * with a binary `[0,1]` result. @@ -49553,6 +49832,9 @@ var TSL = /*#__PURE__*/Object.freeze({ HALF_PI: HALF_PI, INFINITY: INFINITY, If: If, + LTC_Evaluate: LTC_Evaluate, + LTC_Evaluate_Volume: LTC_Evaluate_Volume, + LTC_Uv: LTC_Uv, Loop: Loop, NodeAccess: NodeAccess, NodeShaderStage: NodeShaderStage, @@ -51875,6 +52157,14 @@ class NodeBuilder { */ this.geometry = ( object && object.geometry ) || null; + /** + * The compute node, if building for compute. + * + * @type {?ComputeNode} + * @default null + */ + this.compute = null; + /** * The current renderer. * @@ -52582,8 +52872,8 @@ class NodeBuilder { */ addSequentialNode( node ) { - const updateBeforeType = node.getUpdateBeforeType(); - const updateAfterType = node.getUpdateAfterType(); + const updateBeforeType = node.updateBeforeType; + const updateAfterType = node.updateAfterType; if ( updateBeforeType !== NodeUpdateType.NONE || updateAfterType !== NodeUpdateType.NONE ) { @@ -52600,7 +52890,7 @@ class NodeBuilder { for ( const node of this.nodes ) { - const updateType = node.getUpdateType(); + const updateType = node.updateType; if ( updateType !== NodeUpdateType.NONE ) { @@ -52612,8 +52902,8 @@ class NodeBuilder { for ( const node of this.sequentialNodes ) { - const updateBeforeType = node.getUpdateBeforeType(); - const updateAfterType = node.getUpdateAfterType(); + const updateBeforeType = node.updateBeforeType; + const updateAfterType = node.updateAfterType; if ( updateBeforeType !== NodeUpdateType.NONE ) { @@ -54862,7 +55152,7 @@ class NodeBuilder { */ prebuild() { - const { object, renderer, material } = this; + const { renderer, material } = this; // < renderer.contextNode > @@ -54910,7 +55200,7 @@ class NodeBuilder { } else { - this.addFlow( 'compute', object ); + this.addFlow( 'compute', this.compute ); } @@ -55208,7 +55498,7 @@ class NodeBuilder { const mrt = this.renderer.getMRT(); - return ( mrt && mrt.has( 'velocity' ) ) || getDataFromObject( this.object ).useVelocity === true; + return ( mrt && mrt.has( 'velocity' ) ) || ( this.object !== null && getDataFromObject( this.object ).useVelocity === true ); } @@ -55320,6 +55610,14 @@ class NodeFrame { */ this.scene = null; + /** + * A reference to the current compute node. + * + * @type {?ComputeNode} + * @default null + */ + this.compute = null; + } /** @@ -55360,7 +55658,7 @@ class NodeFrame { */ updateBeforeNode( node ) { - const updateType = node.getUpdateBeforeType(); + const updateType = node.getUpdateBeforeType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { @@ -55417,7 +55715,7 @@ class NodeFrame { */ updateAfterNode( node ) { - const updateType = node.getUpdateAfterType(); + const updateType = node.getUpdateAfterType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { @@ -55466,7 +55764,7 @@ class NodeFrame { */ updateNode( node ) { - const updateType = node.getUpdateType(); + const updateType = node.getUpdateType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { @@ -56339,7 +56637,7 @@ const parse$1 = ( source ) => { const pragmaMainIndex = source.indexOf( pragmaMain ); - const mainCode = pragmaMainIndex !== -1 ? source.slice( pragmaMainIndex + pragmaMain.length ) : source; + const mainCode = ( pragmaMainIndex !== -1 ? source.slice( pragmaMainIndex + pragmaMain.length ) : source ).replace( /^(?:\s*\/\/[^\r\n]*|\s*\/\*[\s\S]*?\*\/|\s*)+/, '' ); const declaration = mainCode.match( declarationRegexp$1 ); @@ -56963,7 +57261,9 @@ class NodeManager extends DataMap { if ( nodeBuilderState === undefined || computeData.version !== computeNode.version ) { - const nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer ); + const nodeBuilder = this.backend.createNodeBuilder( null, this.renderer ); + nodeBuilder.compute = computeNode; + const onNodeBuilderCreated = this.renderer.debug.onNodeBuilderCreated; if ( onNodeBuilderCreated !== null ) onNodeBuilderCreated( nodeBuilder, computeNode ); @@ -57410,7 +57710,18 @@ class NodeManager extends DataMap { } - getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null ) { + /** + * Returns a node frame configured with the given parameters. + * + * @param {Renderer} [renderer=this.renderer] - The renderer. + * @param {?Scene} [scene=null] - The scene. + * @param {?Object3D} [object=null] - The object. + * @param {?Camera} [camera=null] - The camera. + * @param {?Material} [material=null] - The material. + * @param {?Node} [compute=null] - The compute node. + * @return {NodeFrame} The node frame. + */ + getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null, compute = null ) { const nodeFrame = this.nodeFrame; nodeFrame.renderer = renderer; @@ -57418,17 +57729,36 @@ class NodeManager extends DataMap { nodeFrame.object = object; nodeFrame.camera = camera; nodeFrame.material = material; + nodeFrame.compute = compute; return nodeFrame; } + /** + * Returns a node frame configured for the given render object. + * + * @param {RenderObject} renderObject - The render object. + * @return {NodeFrame} The node frame. + */ getNodeFrameForRender( renderObject ) { return this.getNodeFrame( renderObject.renderer, renderObject.scene, renderObject.object, renderObject.camera, renderObject.material ); } + /** + * Returns a node frame configured for the given compute node. + * + * @param {Node} computeNode - The compute node. + * @return {NodeFrame} The node frame. + */ + getNodeFrameForCompute( computeNode ) { + + return this.getNodeFrame( this.renderer, null, null, null, null, computeNode ); + + } + /** * Returns the current output cache key. * @@ -57529,6 +57859,46 @@ class NodeManager extends DataMap { } + /** + * Triggers the call of `updateBefore()` methods + * for all nodes of the given compute node. + * + * @param {Node} computeNode - The compute node. + */ + updateBeforeForCompute( computeNode ) { + + const nodeBuilder = this.getForCompute( computeNode ); + + for ( const node of nodeBuilder.updateBeforeNodes ) { + + // update frame state for each node + + this.getNodeFrameForCompute( computeNode ).updateBeforeNode( node ); + + } + + } + + /** + * Triggers the call of `updateAfter()` methods + * for all nodes of the given compute node. + * + * @param {Node} computeNode - The compute node. + */ + updateAfterForCompute( computeNode ) { + + const nodeBuilder = this.getForCompute( computeNode ); + + for ( const node of nodeBuilder.updateAfterNodes ) { + + // update frame state for each node + + this.getNodeFrameForCompute( computeNode ).updateAfterNode( node ); + + } + + } + /** * Triggers the call of `update()` methods * for all nodes of the given compute node. @@ -61479,12 +61849,6 @@ class Renderer { this._animation.start(); this._initialized = true; - // - - this._inspector.init(); - - // - resolve( this ); } ); @@ -61806,6 +62170,7 @@ class Renderer { await nodes.getForComputeAsync( computeNode ); + nodes.updateBeforeForCompute( computeNode ); nodes.updateForCompute( computeNode ); bindings.updateForCompute( computeNode ); @@ -61815,6 +62180,8 @@ class Renderer { pipelines.getForCompute( computeNode, computeBindings, compilationPromises ); await Promise.all( compilationPromises ); + nodes.updateAfterForCompute( computeNode ); + loaded ++; if ( onProgress !== null ) { @@ -63333,12 +63700,13 @@ class Renderer { * Frees all internal resources of the renderer. Call this method if the renderer * is no longer in use by your app. */ - dispose() { + async dispose() { if ( this._initialized === true ) { this.info.dispose(); + this._inspector.dispose(); this._animation.dispose(); this._objects.dispose(); this._geometries.dispose(); @@ -63355,13 +63723,7 @@ class Renderer { } - Object.values( this.backend.timestampQueryPool ).forEach( queryPool => { - - if ( queryPool !== null ) queryPool.dispose(); - - } ); - - this.backend.dispose(); + await this.backend.dispose(); } @@ -63525,7 +63887,7 @@ class Renderer { if ( this._initialized === false ) { - warn( 'Renderer: .compute() called before the backend is initialized. Try using .computeAsync() instead.' ); + warn( 'Renderer: ".compute()" called before the backend is initialized. Try using ".computeAsync()" instead.' ); return this.computeAsync( computeNodes, dispatchSize ); @@ -63598,6 +63960,7 @@ class Renderer { } + nodes.updateBeforeForCompute( computeNode ); nodes.updateForCompute( computeNode ); bindings.updateForCompute( computeNode ); @@ -63606,6 +63969,8 @@ class Renderer { backend.compute( computeNodes, computeNode, computeBindings, computePipeline, dispatchSize ); + nodes.updateAfterForCompute( computeNode ); + } backend.finishCompute( computeNodes ); @@ -67178,7 +67543,7 @@ ${ flowData.code } */ getInvocationLocalIndex() { - const workgroupSize = this.object.workgroupSize; + const workgroupSize = this.compute.workgroupSize; const size = workgroupSize.reduce( ( acc, curr ) => acc * curr, 1 ); @@ -68250,7 +68615,7 @@ class Backend { * Updates a unique identifier for the given render context that can be used * to allocate resources like occlusion queries or timestamp queries. * - * @param {RenderContext|ComputeNode} abstractRenderContext - The render context. + * @param {RenderContext|ComputeNode|Array} abstractRenderContext - The render context. */ updateTimeStampUID( abstractRenderContext ) { @@ -68259,7 +68624,15 @@ class Backend { let prefix; - if ( abstractRenderContext.isComputeNode === true ) { + let id = abstractRenderContext.id; + + if ( Array.isArray( abstractRenderContext ) ) { + + id = abstractRenderContext.map( c => c.id ).join( ',' ); + + prefix = 'c:' + this.renderer.info.compute.frameCalls; + + } else if ( abstractRenderContext.isComputeNode === true ) { prefix = 'c:' + this.renderer.info.compute.frameCalls; @@ -68269,7 +68642,7 @@ class Backend { } - contextData.timestampUID = prefix + ':' + abstractRenderContext.id + ':f' + frame; + contextData.timestampUID = prefix + ':' + id + ':f' + frame; } @@ -68594,7 +68967,19 @@ class Backend { * * @abstract */ - dispose() { } + async dispose() { + + for ( const queryPool of Object.values( this.timestampQueryPool ) ) { + + if ( queryPool !== null ) { + + await queryPool.dispose(); + + } + + } + + } } @@ -75890,7 +76275,9 @@ class WebGLBackend extends Backend { /** * Frees internal resources. */ - dispose() { + async dispose() { + + await super.dispose(); if ( this.textureUtils !== null ) this.textureUtils.dispose(); @@ -80210,7 +80597,7 @@ const wgslTypeLib$1 = { const parse = ( source ) => { - source = source.trim(); + source = source.replace( /^(?:\s*\/\/[^\r\n]*|\s*\/\*[\s\S]*?\*\/|\s*)+/, '' ); const declaration = source.match( declarationRegexp ); @@ -80548,6 +80935,7 @@ const wgslMethods = { inverse_mat3: 'tsl_inverse_mat3', inverse_mat4: 'tsl_inverse_mat4', inversesqrt: 'inverseSqrt', + faceforward: 'faceForward', bitcast: 'bitcast', floatpack_snorm_2x16: 'pack2x16snorm', floatpack_unorm_2x16: 'pack2x16unorm', @@ -82725,8 +83113,7 @@ ${ flowData.code } } else { // Early strictly validated in computeNode - - const workgroupSize = this.object.workgroupSize; + const workgroupSize = this.compute.workgroupSize; this.computeShader = this._getWGSLComputeCode( shadersData.compute, workgroupSize ); @@ -89156,7 +89543,9 @@ class WebGPUBackend extends Backend { } - dispose() { + async dispose() { + + await super.dispose(); this.bindingUtils.dispose(); this.textureUtils.dispose(); @@ -89173,16 +89562,6 @@ class WebGPUBackend extends Backend { } - if ( this.timestampQueryPool ) { - - for ( const queryPool of Object.values( this.timestampQueryPool ) ) { - - if ( queryPool !== null ) queryPool.dispose(); - - } - - } - if ( this.parameters.device === undefined && this.device !== null ) { this.device.destroy(); diff --git a/build/three.webgpu.nodes.js b/build/three.webgpu.nodes.js index 4ec0dc6477924d..378d79f687616b 100644 --- a/build/three.webgpu.nodes.js +++ b/build/three.webgpu.nodes.js @@ -793,11 +793,15 @@ class NodeMaterialObserver { for ( let i = 0; i < lightsData.length; i ++ ) { const lightData = renderObjectData.lights[ i ]; + const currentLightData = lightsData[ i ]; - if ( lightData.map !== lightsData[ i ].map || lightData.cacheVersion !== lightsData[ i ].cacheVersion ) { + if ( lightData.map !== currentLightData.map || lightData.cacheVersion !== currentLightData.cacheVersion || + lightData.shadowMapWidth !== currentLightData.shadowMapWidth || lightData.shadowMapHeight !== currentLightData.shadowMapHeight ) { - lightData.map = lightsData[ i ].map; - lightData.cacheVersion = lightsData[ i ].cacheVersion; + lightData.map = currentLightData.map; + lightData.cacheVersion = currentLightData.cacheVersion; + lightData.shadowMapWidth = currentLightData.shadowMapWidth; + lightData.shadowMapHeight = currentLightData.shadowMapHeight; return false; @@ -863,14 +867,30 @@ class NodeMaterialObserver { for ( const light of materialLights ) { + let data = null; + if ( light.isSpotLight === true && light.map !== null ) { // only add lights that have a map - lights.push( { map: light.map.version, cacheVersion: this.getTextureData( light.map )._version } ); + data = { map: light.map.version, cacheVersion: this.getTextureData( light.map )._version }; + + } + + if ( light.castShadow === true && light.shadow !== undefined ) { + + // resizing a shadow map recreates its textures so the bindings + // of all related render objects must be updated + + if ( data === null ) data = {}; + + data.shadowMapWidth = light.shadow.mapSize.width; + data.shadowMapHeight = light.shadow.mapSize.height; } + if ( data !== null ) lights.push( data ); + } return lights; @@ -2149,9 +2169,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#update}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateType() { + getUpdateType( /*frame*/ ) { return this.updateType; @@ -2160,9 +2181,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#updateBefore}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateBeforeType() { + getUpdateBeforeType( /*frame*/ ) { return this.updateBeforeType; @@ -2171,9 +2193,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#updateAfter}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateAfterType() { + getUpdateAfterType( /*frame*/ ) { return this.updateAfterType; @@ -7453,7 +7476,7 @@ class MathNode extends TempNode { const method = this.method; - if ( method === MathNode.LENGTH || method === MathNode.DISTANCE || method === MathNode.DOT ) { + if ( method === MathNode.LENGTH || method === MathNode.DISTANCE || method === MathNode.DOT || method === MathNode.DETERMINANT ) { return 'float'; @@ -7605,7 +7628,7 @@ class MathNode extends TempNode { if ( builder.shaderStage !== 'fragment' && ( method === MathNode.DFDX || method === MathNode.DFDY ) ) { - warn( `TSL: '${ method }' is not supported in the ${ builder.shaderStage } stage.`, this.stackTrace ); + error( `TSL: '${ method }' is not supported in the ${ builder.shaderStage } stage.`, this.stackTrace ); method = '/*' + method + '*/'; @@ -10923,7 +10946,7 @@ const instancedBufferAttribute = ( array, type = null, stride = 0, offset = 0 ) */ const instancedDynamicBufferAttribute = ( array, type = null, stride = 0, offset = 0 ) => createBufferAttribute( array, type, stride, offset, DynamicDrawUsage, true ); -addMethodChaining( 'toAttribute', ( bufferNode ) => bufferAttribute( bufferNode.value ) ); +addMethodChaining( 'toAttribute', ( bufferNode ) => bufferAttribute( bufferNode.value, bufferNode.bufferType ) ); /** * This class represents shader indices of different types. The following predefined node @@ -11163,15 +11186,6 @@ class ComputeNode extends Node { */ this.name = ''; - /** - * The `updateBeforeType` is set to `NodeUpdateType.OBJECT` since {@link ComputeNode#updateBefore} - * is executed once per object by default. - * - * @type {string} - * @default 'object' - */ - this.updateBeforeType = NodeUpdateType.OBJECT; - /** * A callback executed when the compute node finishes initialization. * @@ -11187,6 +11201,15 @@ class ComputeNode extends Node { */ this.countNode = null; + /** + * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node computes + * once per frame. + * + * @type {string} + * @default 'frame' + */ + this.updateBeforeType = NodeUpdateType.FRAME; + } /** @@ -11241,6 +11264,18 @@ class ComputeNode extends Node { } + /** + * Returns the update type of {@link Node#updateBefore}. + * + * @param {NodeFrame} [frame] - The current node frame. + * @return {NodeUpdateType} The update type. + */ + getUpdateBeforeType( frame ) { + + return frame.compute !== this ? this.updateBeforeType : NodeUpdateType.NONE; + + } + /** * The method execute the compute for this node. * @@ -12085,11 +12120,6 @@ class InspectorBase extends EventDispatcher { } - /** - * Initializes the inspector. - */ - init() { } - /** * Called when a frame begins. */ @@ -12171,6 +12201,17 @@ class InspectorBase extends EventDispatcher { */ copyFramebufferToTexture( /*framebufferTexture*/ ) { } + /** + * Frees all internal resources of the inspector. + */ + dispose() { + + if ( this.isRunning === true ) this.finish(); + + this.dispatchEvent( { type: 'dispose' } ); + + } + } /** @@ -14323,27 +14364,6 @@ class ScreenNode extends Node { } - /** - * This method is overwritten since the node's update type depends on the selected scope. - * - * @return {NodeUpdateType} The update type. - */ - getUpdateType() { - - let updateType = NodeUpdateType.NONE; - - if ( this.scope === ScreenNode.SIZE || this.scope === ScreenNode.VIEWPORT ) { - - updateType = NodeUpdateType.RENDER; - - } - - this.updateType = updateType; - - return updateType; - - } - /** * `ScreenNode` implements {@link Node#update} to retrieve viewport and size information * from the current renderer. @@ -14405,6 +14425,20 @@ class ScreenNode extends Node { } + // + + let updateType = NodeUpdateType.NONE; + + if ( this.scope === ScreenNode.SIZE || this.scope === ScreenNode.VIEWPORT ) { + + updateType = NodeUpdateType.RENDER; + + } + + this.updateType = updateType; + + // + return output; } @@ -15164,6 +15198,8 @@ const objectViewPosition = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.V */ const objectRadius = /*@__PURE__*/ nodeProxy( Object3DNode, Object3DNode.RADIUS ).setParameterLength( 1 ); +const _modelViewMatrix = /*@__PURE__*/ new Matrix4(); + /** * This type of node is a specialized version of `Object3DNode` * with larger set of model related metrics. Unlike `Object3DNode`, @@ -15306,11 +15342,46 @@ const highpModelViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => { builder.context.isHighPrecisionModelViewMatrix = true; - return uniform( 'mat4' ).onObjectUpdate( ( { object, camera } ) => { + const camera = builder.camera; - return object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + let highpModelViewMatrix; - } ); + if ( camera.isArrayCamera && camera.cameras.length > 0 ) { + + const matrices = []; + + for ( let i = 0; i < camera.cameras.length; i ++ ) { + + matrices.push( new Matrix4() ); + + } + + const modelViewMatrices = uniformArray( matrices ).onObjectUpdate( ( { object, camera }, self ) => { + + const subCameras = camera.cameras; + const array = self.array; + + for ( let i = 0, l = subCameras.length; i < l; i ++ ) { + + array[ i ].multiplyMatrices( subCameras[ i ].matrixWorldInverse, object.matrixWorld ); + + } + + } ); + + highpModelViewMatrix = modelViewMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); + + } else { + + highpModelViewMatrix = uniform( 'mat4' ).onObjectUpdate( ( { object, camera } ) => { + + return object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + + } ); + + } + + return highpModelViewMatrix; } ).once() )().toVar( 'highpModelViewMatrix' ); @@ -15325,17 +15396,54 @@ const highpModelNormalViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => { const isHighPrecisionModelViewMatrix = builder.context.isHighPrecisionModelViewMatrix; - return uniform( 'mat3' ).onObjectUpdate( ( { object, camera } ) => { + const camera = builder.camera; + + let highpModelNormalViewMatrix; + + if ( camera.isArrayCamera && camera.cameras.length > 0 ) { + + const matrices = []; - if ( isHighPrecisionModelViewMatrix !== true ) { + for ( let i = 0; i < camera.cameras.length; i ++ ) { - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + matrices.push( new Matrix3() ); } - return object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + const normalViewMatrices = uniformArray( matrices ).onObjectUpdate( ( { object, camera }, self ) => { - } ); + const subCameras = camera.cameras; + const array = self.array; + + for ( let i = 0, l = subCameras.length; i < l; i ++ ) { + + _modelViewMatrix.multiplyMatrices( subCameras[ i ].matrixWorldInverse, object.matrixWorld ); + + array[ i ].getNormalMatrix( _modelViewMatrix ); + + } + + } ); + + highpModelNormalViewMatrix = normalViewMatrices.element( camera.isMultiViewCamera ? builtin( 'gl_ViewID_OVR' ) : cameraIndex ); + + } else { + + highpModelNormalViewMatrix = uniform( 'mat3' ).onObjectUpdate( ( { object, camera } ) => { + + if ( isHighPrecisionModelViewMatrix !== true ) { + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + + } + + return object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + + } ); + + } + + return highpModelNormalViewMatrix; } ).once() )().toVar( 'highpModelNormalViewMatrix' ); @@ -25050,7 +25158,7 @@ const LTC_Evaluate_Volume = /*@__PURE__*/ Fn( ( { P, p0, p1, p2, p3 } ) => { return result; } ).setLayout( { - name: 'LTC_Evaluate', + name: 'LTC_Evaluate_Volume', type: 'vec3', inputs: [ { name: 'P', type: 'vec3' }, @@ -28968,8 +29076,9 @@ class RotateNode extends TempNode { * @param {Node} positionNode - The position node. * @param {Node} rotationNode - Represents the rotation that is applied to the position node. Depending * on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. + * @param {string} [order='XYZ'] - The Euler rotation order. Only used for 3D rotation. */ - constructor( positionNode, rotationNode ) { + constructor( positionNode, rotationNode, order = 'XYZ' ) { super(); @@ -28981,13 +29090,59 @@ class RotateNode extends TempNode { this.positionNode = positionNode; /** - * Represents the rotation that is applied to the position node. - * Depending on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. + * Represents the rotation that is applied to the position node. + * Depending on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. * * @type {Node} */ this.rotationNode = rotationNode; + /** + * The Euler rotation order. + * + * @private + * @type {string} + * @default 'XYZ' + */ + this._order = order; + + } + + /** + * Overwrites the default `customCacheKey()` implementation by including the + * Euler order into the cache key. + * + * @return {number} The hash. + */ + customCacheKey() { + + return hashString( this._order ); + + } + + /** + * Sets the Euler rotation order. + * + * @param {string} value - The Euler rotation order. + * @return {RotateNode} A reference to this node. + */ + setOrder( value ) { + + this._order = value; + + return this; + + } + + /** + * Gets the Euler rotation order. + * + * @return {string} The Euler rotation order. + */ + getOrder() { + + return this._order; + } /** @@ -29023,16 +29178,44 @@ class RotateNode extends TempNode { } else { const rotation = rotationNode; - const rotationXMatrix = mat4( vec4( 1.0, 0.0, 0.0, 0.0 ), vec4( 0.0, cos( rotation.x ), sin( rotation.x ).negate(), 0.0 ), vec4( 0.0, sin( rotation.x ), cos( rotation.x ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); - const rotationYMatrix = mat4( vec4( cos( rotation.y ), 0.0, sin( rotation.y ), 0.0 ), vec4( 0.0, 1.0, 0.0, 0.0 ), vec4( sin( rotation.y ).negate(), 0.0, cos( rotation.y ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); - const rotationZMatrix = mat4( vec4( cos( rotation.z ), sin( rotation.z ).negate(), 0.0, 0.0 ), vec4( sin( rotation.z ), cos( rotation.z ), 0.0, 0.0 ), vec4( 0.0, 0.0, 1.0, 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); + const order = this._order; + + const rotationXMatrix = mat4( vec4( 1.0, 0.0, 0.0, 0.0 ), vec4( 0.0, cos( rotation.x ), sin( rotation.x ), 0.0 ), vec4( 0.0, sin( rotation.x ).negate(), cos( rotation.x ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); + const rotationYMatrix = mat4( vec4( cos( rotation.y ), 0.0, sin( rotation.y ).negate(), 0.0 ), vec4( 0.0, 1.0, 0.0, 0.0 ), vec4( sin( rotation.y ), 0.0, cos( rotation.y ), 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); + const rotationZMatrix = mat4( vec4( cos( rotation.z ), sin( rotation.z ), 0.0, 0.0 ), vec4( sin( rotation.z ).negate(), cos( rotation.z ), 0.0, 0.0 ), vec4( 0.0, 0.0, 1.0, 0.0 ), vec4( 0.0, 0.0, 0.0, 1.0 ) ); + + const matrixMap = { + 'X': rotationXMatrix, + 'Y': rotationYMatrix, + 'Z': rotationZMatrix + }; + + const matrixChain = matrixMap[ order.charAt( 0 ) ] + .mul( matrixMap[ order.charAt( 1 ) ] ) + .mul( matrixMap[ order.charAt( 2 ) ] ); - return rotationXMatrix.mul( rotationYMatrix ).mul( rotationZMatrix ).mul( vec4( positionNode, 1.0 ) ).xyz; + return matrixChain.mul( vec4( positionNode, 1.0 ) ).xyz; } } + serialize( data ) { + + super.serialize( data ); + + data.order = this._order; + + } + + deserialize( data ) { + + super.deserialize( data ); + + this._order = data.order; + + } + } /** @@ -29043,9 +29226,10 @@ class RotateNode extends TempNode { * @param {Node} positionNode - The position node. * @param {Node} rotationNode - Represents the rotation that is applied to the position node. Depending * on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. + * @param {string} [order='XYZ'] - The Euler rotation order. Only used for 3D rotation. * @returns {RotateNode} */ -const rotate = /*@__PURE__*/ nodeProxy( RotateNode ).setParameterLength( 2 ); +const rotate = /*@__PURE__*/ nodeProxy( RotateNode ).setParameterLength( 2, 3 ); const _defaultValues$2 = /*@__PURE__*/ new SpriteMaterial(); @@ -33562,6 +33746,8 @@ class Bindings extends DataMap { } + cacheKey += attribute.id + ','; + } if ( binding.isUniformBuffer ) { @@ -37085,6 +37271,12 @@ class BitcountNode extends MathNode { const fnDef = Fn( ( [ value ] ) => { + If( value.equal( uint( 0 ) ), () => { + + return uint( 32 ); + + } ); + const v = uint( 0.0 ); this._resolveElementType( value, v, elementType ); @@ -37443,7 +37635,11 @@ const parabola = ( x, k ) => pow( mul( 4.0, x.mul( sub( 1.0, x ) ) ), k ); * @param {Node} k - `k=1` is the identity curve,`k<1` produces the classic `gain()` shape, and `k>1` produces "s" shaped curves. * @return {Node} The remapped value. */ -const gain = ( x, k ) => x.lessThan( 0.5 ) ? parabola( x.mul( 2.0 ), k ).div( 2.0 ) : sub( 1.0, parabola( mul( sub( 1.0, x ), 2.0 ), k ).div( 2.0 ) ); +const gain = ( x, k ) => select( + x.lessThan( 0.5 ), + pow( mul( 2.0, x ), k ).mul( 0.5 ), + sub( 1.0, pow( mul( 2.0, sub( 1.0, x ) ), k ).mul( 0.5 ) ) +); /** * A function that remaps the `[0,1]` interval into the `[0,1]` interval. @@ -37457,7 +37653,7 @@ const gain = ( x, k ) => x.lessThan( 0.5 ) ? parabola( x.mul( 2.0 ), k ).div( 2. * @param {Node} b - Second control parameter. * @return {Node} The remapped value. */ -const pcurve = ( x, a, b ) => pow( div( pow( x, a ), add( pow( x, a ), pow( sub( 1.0, x ), b ) ) ), 1.0 / a ); +const pcurve = ( x, a, b ) => pow( div( pow( x, a ), add( pow( x, a ), pow( sub( 1.0, x ), b ) ) ), div( 1.0, a ) ); /** * A phase shifted sinus curve that starts at zero and ends at zero, with bouncing behavior. @@ -37469,7 +37665,13 @@ const pcurve = ( x, a, b ) => pow( div( pow( x, a ), add( pow( x, a ), pow( sub( * @param {Node} k - Controls the amount of bounces. * @return {Node} The result value. */ -const sinc = ( x, k ) => sin( PI.mul( k.mul( x ).sub( 1.0 ) ) ).div( PI.mul( k.mul( x ).sub( 1.0 ) ) ); +const sinc = ( x, k ) => { + + const arg = abs( PI.mul( k.mul( x ).sub( 1.0 ) ) ).max( 1e-6 ).toConst(); + + return sin( arg ).div( arg ); + +}; /** * This node represents an operation that packs floating-point values of a vector into an unsigned 32-bit integer @@ -38685,7 +38887,7 @@ class ReflectorBaseNode extends Node { if ( renderTarget === undefined ) { - renderTarget = new RenderTarget( 0, 0, { type: HalfFloatType, samples: this.samples } ); + renderTarget = new RenderTarget( 1, 1, { type: HalfFloatType, samples: this.samples } ); if ( this.generateMipmaps === true ) { @@ -39020,8 +39222,222 @@ class QuadMesh extends Mesh { } +/** + * Saves the state of the given renderer and stores it into the given state object. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function saveRendererState( renderer, state = {} ) { + + state.toneMapping = renderer.toneMapping; + state.toneMappingExposure = renderer.toneMappingExposure; + state.outputColorSpace = renderer.outputColorSpace; + state.renderTarget = renderer.getRenderTarget(); + state.activeCubeFace = renderer.getActiveCubeFace(); + state.activeMipmapLevel = renderer.getActiveMipmapLevel(); + state.renderObjectFunction = renderer.getRenderObjectFunction(); + state.pixelRatio = renderer.getPixelRatio(); + state.mrt = renderer.getMRT(); + state.clearColor = renderer.getClearColor( state.clearColor || new Color() ); + state.clearAlpha = renderer.getClearAlpha(); + state.autoClear = renderer.autoClear; + state.scissorTest = renderer.getScissorTest(); + + return state; + +} + +/** + * Saves the state of the given renderer and stores it into the given state object. + * Besides, the function also resets the state of the renderer to its default values. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function resetRendererState( renderer, state ) { + + state = saveRendererState( renderer, state ); + + renderer.setMRT( null ); + renderer.setRenderObjectFunction( null ); + renderer.setClearColor( 0x000000, 1 ); + renderer.autoClear = true; + + return state; + +} + +/** + * Restores the state of the given renderer from the given state object. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Object} state - The state to restore. + */ +function restoreRendererState( renderer, state ) { + + renderer.toneMapping = state.toneMapping; + renderer.toneMappingExposure = state.toneMappingExposure; + renderer.outputColorSpace = state.outputColorSpace; + renderer.setRenderTarget( state.renderTarget, state.activeCubeFace, state.activeMipmapLevel ); + renderer.setRenderObjectFunction( state.renderObjectFunction ); + renderer.setPixelRatio( state.pixelRatio ); + renderer.setMRT( state.mrt ); + renderer.setClearColor( state.clearColor, state.clearAlpha ); + renderer.autoClear = state.autoClear; + renderer.setScissorTest( state.scissorTest ); + +} + +/** + * Saves the state of the given scene and stores it into the given state object. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Scene} scene - The scene. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function saveSceneState( scene, state = {} ) { + + state.background = scene.background; + state.backgroundNode = scene.backgroundNode; + state.overrideMaterial = scene.overrideMaterial; + + return state; + +} + +/** + * Saves the state of the given scene and stores it into the given state object. + * Besides, the function also resets the state of the scene to its default values. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Scene} scene - The scene. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function resetSceneState( scene, state ) { + + state = saveSceneState( scene, state ); + + scene.background = null; + scene.backgroundNode = null; + scene.overrideMaterial = null; + + return state; + +} + +/** + * Restores the state of the given scene from the given state object. + * + * @private + * @function + * @param {Scene} scene - The scene. + * @param {Object} state - The state to restore. + */ +function restoreSceneState( scene, state ) { + + scene.background = state.background; + scene.backgroundNode = state.backgroundNode; + scene.overrideMaterial = state.overrideMaterial; + +} + +/** + * Saves the state of the given renderer and scene and stores it into the given state object. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Scene} scene - The scene. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function saveRendererAndSceneState( renderer, scene, state = {} ) { + + state = saveRendererState( renderer, state ); + state = saveSceneState( scene, state ); + + return state; + +} + +/** + * Saves the state of the given renderer and scene and stores it into the given state object. + * Besides, the function also resets the state of the renderer and scene to its default values. + * + * If not state object is provided, the function creates one. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Scene} scene - The scene. + * @param {Object} [state={}] - The state. + * @return {Object} The state. + */ +function resetRendererAndSceneState( renderer, scene, state ) { + + state = resetRendererState( renderer, state ); + state = resetSceneState( scene, state ); + + return state; + +} + +/** + * Restores the state of the given renderer and scene from the given state object. + * + * @private + * @function + * @param {Renderer} renderer - The renderer. + * @param {Scene} scene - The scene. + * @param {Object} state - The state to restore. + */ +function restoreRendererAndSceneState( renderer, scene, state ) { + + restoreRendererState( renderer, state ); + restoreSceneState( scene, state ); + +} + +var RendererUtils = /*#__PURE__*/Object.freeze({ + __proto__: null, + resetRendererAndSceneState: resetRendererAndSceneState, + resetRendererState: resetRendererState, + resetSceneState: resetSceneState, + restoreRendererAndSceneState: restoreRendererAndSceneState, + restoreRendererState: restoreRendererState, + restoreSceneState: restoreSceneState, + saveRendererAndSceneState: saveRendererAndSceneState, + saveRendererState: saveRendererState, + saveSceneState: saveSceneState +}); + const _size$1 = /*@__PURE__*/ new Vector2(); +let _rendererState$1; + /** * `RTTNode` takes another node and uses it with a `QuadMesh` to render into a texture (RTT). * This module is especially relevant in context of post processing where certain nodes require @@ -39042,7 +39458,7 @@ class RTTNode extends TextureNode { * Constructs a new RTT node. * * @param {Node} node - The node to render a texture with. - * @param {?number} [width=null] - The width of the internal render target. If not width is applied, the render target is automatically resized. + * @param {?number} [width=null] - The width of the internal render target. If no width is applied, the render target is automatically resized. * @param {?number} [height=null] - The height of the internal render target. * @param {Object} [options={}] - The options for the internal render target. * @param {number} [options.type=HalfFloatType] - The texture type. @@ -39056,7 +39472,7 @@ class RTTNode extends TextureNode { resolutionScale = 1 } = options; - const renderTarget = new RenderTarget( width, height, { type: HalfFloatType, ...options } ); + const renderTarget = new RenderTarget( width ?? 1, height ?? 1, { type: HalfFloatType, ...options } ); super( renderTarget.texture, uv$1() ); @@ -39134,13 +39550,13 @@ class RTTNode extends TextureNode { this._quadMesh = new QuadMesh( new NodeMaterial() ); /** - * The `updateBeforeType` is set to `NodeUpdateType.RENDER` since the node updates - * the texture once per render in its {@link RTTNode#updateBefore} method. + * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node updates + * the texture once per frame in its {@link RTTNode#updateBefore} method. * * @type {string} - * @default 'render' + * @default 'frame' */ - this.updateBeforeType = NodeUpdateType.RENDER; + this.updateBeforeType = NodeUpdateType.FRAME; } @@ -39169,10 +39585,10 @@ class RTTNode extends TextureNode { } /** - * Sets the size of the internal render target + * Sets the size of the internal render target. * * @param {number} width - The width to set. - * @param {number} height - The width to set. + * @param {number} height - The height to set. */ setSize( width, height ) { @@ -39217,7 +39633,42 @@ class RTTNode extends TextureNode { } - updateBefore( { renderer } ) { + /** + * Overwritten since the value is defined by the internal render target. + * + * @param {Texture} value - The texture value. + */ + set value( value ) { + + if ( this.renderTarget && value !== this.renderTarget.texture ) { + + error( 'TSL: "rtt()" does not allow overwriting the value.' ); + + } + + } + + /** + * The texture of the internal render target. + * + * @type {Texture} + */ + get value() { + + return this.renderTarget ? this.renderTarget.texture : null; + + } + + /** + * Renders the node's output into the internal render target before the main render pass. + * Handles automatic resizing of the render target when `autoResize` is enabled, + * and skips rendering if neither `textureNeedsUpdate` nor `autoUpdate` is true. + * + * @param {NodeFrame} frame - The current node frame, providing access to the renderer and other frame data. + */ + updateBefore( frame ) { + + const { renderer } = frame; if ( this.textureNeedsUpdate === false && this.autoUpdate === false ) return; @@ -39225,8 +39676,6 @@ class RTTNode extends TextureNode { // - const currentRenderTarget = renderer.getRenderTarget(); - if ( this.autoResize === true ) { const size = renderer.getDrawingBufferSize( _size$1 ); @@ -39248,9 +39697,11 @@ class RTTNode extends TextureNode { let name = 'RTT'; - if ( this.node.name ) { + const callName = this.name || this.node.name; - name = this.node.name + ' [ ' + name + ' ]'; + if ( callName ) { + + name = callName + ' [ ' + name + ' ]'; } @@ -39258,11 +39709,13 @@ class RTTNode extends TextureNode { // + _rendererState$1 = resetRendererState( renderer, _rendererState$1 ); + renderer.setRenderTarget( this.renderTarget ); this._quadMesh.render( renderer ); - renderer.setRenderTarget( currentRenderTarget ); + restoreRendererState( renderer, _rendererState$1 ); } @@ -39276,6 +39729,18 @@ class RTTNode extends TextureNode { } + /** + * Frees internal resources. Should be called when the node is no longer in use. + */ + dispose() { + + this.renderTarget.dispose(); + this._quadMesh.material.dispose(); + + super.dispose(); + + } + } /** @@ -39284,7 +39749,7 @@ class RTTNode extends TextureNode { * @tsl * @function * @param {Node} node - The node to render a texture with. - * @param {?number} [width=null] - The width of the internal render target. If not width is applied, the render target is automatically resized. + * @param {?number} [width=null] - The width of the internal render target. If no width is applied, the render target is automatically resized. * @param {?number} [height=null] - The height of the internal render target. * @param {Object} [options={}] - The options for the internal render target. * @param {number} [options.type=HalfFloatType] - The texture type. @@ -39300,7 +39765,7 @@ const rtt = ( node, ...params ) => new RTTNode( nodeObject( node ), ...params ); * @tsl * @function * @param {Node} node - The node to render a texture with. - * @param {?number} [width=null] - The width of the internal render target. If not width is applied, the render target is automatically resized. + * @param {?number} [width=null] - The width of the internal render target. If no width is applied, the render target is automatically resized. * @param {?number} [height=null] - The height of the internal render target. * @param {Object} [options={}] - The options for the internal render target. * @param {number} [options.type=HalfFloatType] - The texture type. @@ -43322,7 +43787,7 @@ class ComputeBuiltinNode extends Node { } else { - warn( `ComputeBuiltinNode: Compute built-in value ${builtinName} can not be accessed in the ${builder.shaderStage} stage` ); + warn( `TSL: Compute built-in value "${builtinName}" can not be accessed in the ${builder.shaderStage} stage` ); return builder.generateConst( nodeType ); } @@ -43469,15 +43934,23 @@ class BarrierNode extends Node { generate( builder ) { const { scope } = this; - const { renderer } = builder; + const { renderer, shaderStage } = builder; + + const barrierMethod = `${scope}Barrier`; + + if ( shaderStage !== 'compute' ) { + + error( `TSL: "${barrierMethod}" is not supported in the ${shaderStage} stage and can only be executed in compute.` ); + + } if ( renderer.backend.isWebGLBackend === true ) { - builder.addFlowCode( `\t// ${scope}Barrier \n` ); + builder.addFlowCode( `\t// ${barrierMethod}() \n` ); } else { - builder.addLineFlowCode( `${scope}Barrier()`, this ); + builder.addLineFlowCode( `${barrierMethod}()`, this ); } @@ -43734,6 +44207,12 @@ class WorkgroupInfoNode extends Node { generate( builder ) { + if ( builder.shaderStage !== 'compute' ) { + + error( 'TSL: "workgroupArray()" can only be executed within the compute shader stage' ); + + } + const name = ( this.name !== '' ) ? this.name : `${this.scope}Array_${this.id}`; return builder.getScopedArray( name, this.scope.toLowerCase(), this.bufferType, this.bufferCount ); @@ -43842,11 +44321,17 @@ class AtomicFunctionNode extends Node { generate( builder ) { + const method = this.method; + + if ( builder.shaderStage === 'vertex' ) { + + error( `TSL: "${this.method}" is not supported in the vertex stage.` ); + + } + const properties = builder.getNodeProperties( this ); const parents = properties.parents; - const method = this.method; - const type = this.getNodeType( builder ); const inputType = this.getInputType( builder ); @@ -44164,6 +44649,12 @@ class SubgroupFunctionNode extends TempNode { const method = this.method; + if ( builder.shaderStage === 'vertex' ) { + + error( `TSL: "${this.method}" is not supported in the vertex shader stage.` ); + + } + const type = this.getNodeType( builder ); const inputType = this.getInputType( builder ); @@ -45494,218 +45985,6 @@ class ShadowBaseNode extends Node { */ const shadowPositionWorld = /*@__PURE__*/ property( 'vec3', 'shadowPositionWorld' ); -/** - * Saves the state of the given renderer and stores it into the given state object. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function saveRendererState( renderer, state = {} ) { - - state.toneMapping = renderer.toneMapping; - state.toneMappingExposure = renderer.toneMappingExposure; - state.outputColorSpace = renderer.outputColorSpace; - state.renderTarget = renderer.getRenderTarget(); - state.activeCubeFace = renderer.getActiveCubeFace(); - state.activeMipmapLevel = renderer.getActiveMipmapLevel(); - state.renderObjectFunction = renderer.getRenderObjectFunction(); - state.pixelRatio = renderer.getPixelRatio(); - state.mrt = renderer.getMRT(); - state.clearColor = renderer.getClearColor( state.clearColor || new Color() ); - state.clearAlpha = renderer.getClearAlpha(); - state.autoClear = renderer.autoClear; - state.scissorTest = renderer.getScissorTest(); - - return state; - -} - -/** - * Saves the state of the given renderer and stores it into the given state object. - * Besides, the function also resets the state of the renderer to its default values. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function resetRendererState( renderer, state ) { - - state = saveRendererState( renderer, state ); - - renderer.setMRT( null ); - renderer.setRenderObjectFunction( null ); - renderer.setClearColor( 0x000000, 1 ); - renderer.autoClear = true; - - return state; - -} - -/** - * Restores the state of the given renderer from the given state object. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Object} state - The state to restore. - */ -function restoreRendererState( renderer, state ) { - - renderer.toneMapping = state.toneMapping; - renderer.toneMappingExposure = state.toneMappingExposure; - renderer.outputColorSpace = state.outputColorSpace; - renderer.setRenderTarget( state.renderTarget, state.activeCubeFace, state.activeMipmapLevel ); - renderer.setRenderObjectFunction( state.renderObjectFunction ); - renderer.setPixelRatio( state.pixelRatio ); - renderer.setMRT( state.mrt ); - renderer.setClearColor( state.clearColor, state.clearAlpha ); - renderer.autoClear = state.autoClear; - renderer.setScissorTest( state.scissorTest ); - -} - -/** - * Saves the state of the given scene and stores it into the given state object. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Scene} scene - The scene. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function saveSceneState( scene, state = {} ) { - - state.background = scene.background; - state.backgroundNode = scene.backgroundNode; - state.overrideMaterial = scene.overrideMaterial; - - return state; - -} - -/** - * Saves the state of the given scene and stores it into the given state object. - * Besides, the function also resets the state of the scene to its default values. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Scene} scene - The scene. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function resetSceneState( scene, state ) { - - state = saveSceneState( scene, state ); - - scene.background = null; - scene.backgroundNode = null; - scene.overrideMaterial = null; - - return state; - -} - -/** - * Restores the state of the given scene from the given state object. - * - * @private - * @function - * @param {Scene} scene - The scene. - * @param {Object} state - The state to restore. - */ -function restoreSceneState( scene, state ) { - - scene.background = state.background; - scene.backgroundNode = state.backgroundNode; - scene.overrideMaterial = state.overrideMaterial; - -} - -/** - * Saves the state of the given renderer and scene and stores it into the given state object. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Scene} scene - The scene. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function saveRendererAndSceneState( renderer, scene, state = {} ) { - - state = saveRendererState( renderer, state ); - state = saveSceneState( scene, state ); - - return state; - -} - -/** - * Saves the state of the given renderer and scene and stores it into the given state object. - * Besides, the function also resets the state of the renderer and scene to its default values. - * - * If not state object is provided, the function creates one. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Scene} scene - The scene. - * @param {Object} [state={}] - The state. - * @return {Object} The state. - */ -function resetRendererAndSceneState( renderer, scene, state ) { - - state = resetRendererState( renderer, state ); - state = resetSceneState( scene, state ); - - return state; - -} - -/** - * Restores the state of the given renderer and scene from the given state object. - * - * @private - * @function - * @param {Renderer} renderer - The renderer. - * @param {Scene} scene - The scene. - * @param {Object} state - The state to restore. - */ -function restoreRendererAndSceneState( renderer, scene, state ) { - - restoreRendererState( renderer, state ); - restoreSceneState( scene, state ); - -} - -var RendererUtils = /*#__PURE__*/Object.freeze({ - __proto__: null, - resetRendererAndSceneState: resetRendererAndSceneState, - resetRendererState: resetRendererState, - resetSceneState: resetSceneState, - restoreRendererAndSceneState: restoreRendererAndSceneState, - restoreRendererState: restoreRendererState, - restoreSceneState: restoreSceneState, - saveRendererAndSceneState: saveRendererAndSceneState, - saveRendererState: saveRendererState, - saveSceneState: saveSceneState -}); - /** * A shadow filtering function performing basic filtering. This is in fact an unfiltered version of the shadow map * with a binary `[0,1]` result. @@ -49553,6 +49832,9 @@ var TSL = /*#__PURE__*/Object.freeze({ HALF_PI: HALF_PI, INFINITY: INFINITY, If: If, + LTC_Evaluate: LTC_Evaluate, + LTC_Evaluate_Volume: LTC_Evaluate_Volume, + LTC_Uv: LTC_Uv, Loop: Loop, NodeAccess: NodeAccess, NodeShaderStage: NodeShaderStage, @@ -51875,6 +52157,14 @@ class NodeBuilder { */ this.geometry = ( object && object.geometry ) || null; + /** + * The compute node, if building for compute. + * + * @type {?ComputeNode} + * @default null + */ + this.compute = null; + /** * The current renderer. * @@ -52582,8 +52872,8 @@ class NodeBuilder { */ addSequentialNode( node ) { - const updateBeforeType = node.getUpdateBeforeType(); - const updateAfterType = node.getUpdateAfterType(); + const updateBeforeType = node.updateBeforeType; + const updateAfterType = node.updateAfterType; if ( updateBeforeType !== NodeUpdateType.NONE || updateAfterType !== NodeUpdateType.NONE ) { @@ -52600,7 +52890,7 @@ class NodeBuilder { for ( const node of this.nodes ) { - const updateType = node.getUpdateType(); + const updateType = node.updateType; if ( updateType !== NodeUpdateType.NONE ) { @@ -52612,8 +52902,8 @@ class NodeBuilder { for ( const node of this.sequentialNodes ) { - const updateBeforeType = node.getUpdateBeforeType(); - const updateAfterType = node.getUpdateAfterType(); + const updateBeforeType = node.updateBeforeType; + const updateAfterType = node.updateAfterType; if ( updateBeforeType !== NodeUpdateType.NONE ) { @@ -54862,7 +55152,7 @@ class NodeBuilder { */ prebuild() { - const { object, renderer, material } = this; + const { renderer, material } = this; // < renderer.contextNode > @@ -54910,7 +55200,7 @@ class NodeBuilder { } else { - this.addFlow( 'compute', object ); + this.addFlow( 'compute', this.compute ); } @@ -55208,7 +55498,7 @@ class NodeBuilder { const mrt = this.renderer.getMRT(); - return ( mrt && mrt.has( 'velocity' ) ) || getDataFromObject( this.object ).useVelocity === true; + return ( mrt && mrt.has( 'velocity' ) ) || ( this.object !== null && getDataFromObject( this.object ).useVelocity === true ); } @@ -55320,6 +55610,14 @@ class NodeFrame { */ this.scene = null; + /** + * A reference to the current compute node. + * + * @type {?ComputeNode} + * @default null + */ + this.compute = null; + } /** @@ -55360,7 +55658,7 @@ class NodeFrame { */ updateBeforeNode( node ) { - const updateType = node.getUpdateBeforeType(); + const updateType = node.getUpdateBeforeType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { @@ -55417,7 +55715,7 @@ class NodeFrame { */ updateAfterNode( node ) { - const updateType = node.getUpdateAfterType(); + const updateType = node.getUpdateAfterType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { @@ -55466,7 +55764,7 @@ class NodeFrame { */ updateNode( node ) { - const updateType = node.getUpdateType(); + const updateType = node.getUpdateType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { @@ -56339,7 +56637,7 @@ const parse$1 = ( source ) => { const pragmaMainIndex = source.indexOf( pragmaMain ); - const mainCode = pragmaMainIndex !== -1 ? source.slice( pragmaMainIndex + pragmaMain.length ) : source; + const mainCode = ( pragmaMainIndex !== -1 ? source.slice( pragmaMainIndex + pragmaMain.length ) : source ).replace( /^(?:\s*\/\/[^\r\n]*|\s*\/\*[\s\S]*?\*\/|\s*)+/, '' ); const declaration = mainCode.match( declarationRegexp$1 ); @@ -56963,7 +57261,9 @@ class NodeManager extends DataMap { if ( nodeBuilderState === undefined || computeData.version !== computeNode.version ) { - const nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer ); + const nodeBuilder = this.backend.createNodeBuilder( null, this.renderer ); + nodeBuilder.compute = computeNode; + const onNodeBuilderCreated = this.renderer.debug.onNodeBuilderCreated; if ( onNodeBuilderCreated !== null ) onNodeBuilderCreated( nodeBuilder, computeNode ); @@ -57410,7 +57710,18 @@ class NodeManager extends DataMap { } - getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null ) { + /** + * Returns a node frame configured with the given parameters. + * + * @param {Renderer} [renderer=this.renderer] - The renderer. + * @param {?Scene} [scene=null] - The scene. + * @param {?Object3D} [object=null] - The object. + * @param {?Camera} [camera=null] - The camera. + * @param {?Material} [material=null] - The material. + * @param {?Node} [compute=null] - The compute node. + * @return {NodeFrame} The node frame. + */ + getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null, compute = null ) { const nodeFrame = this.nodeFrame; nodeFrame.renderer = renderer; @@ -57418,17 +57729,36 @@ class NodeManager extends DataMap { nodeFrame.object = object; nodeFrame.camera = camera; nodeFrame.material = material; + nodeFrame.compute = compute; return nodeFrame; } + /** + * Returns a node frame configured for the given render object. + * + * @param {RenderObject} renderObject - The render object. + * @return {NodeFrame} The node frame. + */ getNodeFrameForRender( renderObject ) { return this.getNodeFrame( renderObject.renderer, renderObject.scene, renderObject.object, renderObject.camera, renderObject.material ); } + /** + * Returns a node frame configured for the given compute node. + * + * @param {Node} computeNode - The compute node. + * @return {NodeFrame} The node frame. + */ + getNodeFrameForCompute( computeNode ) { + + return this.getNodeFrame( this.renderer, null, null, null, null, computeNode ); + + } + /** * Returns the current output cache key. * @@ -57529,6 +57859,46 @@ class NodeManager extends DataMap { } + /** + * Triggers the call of `updateBefore()` methods + * for all nodes of the given compute node. + * + * @param {Node} computeNode - The compute node. + */ + updateBeforeForCompute( computeNode ) { + + const nodeBuilder = this.getForCompute( computeNode ); + + for ( const node of nodeBuilder.updateBeforeNodes ) { + + // update frame state for each node + + this.getNodeFrameForCompute( computeNode ).updateBeforeNode( node ); + + } + + } + + /** + * Triggers the call of `updateAfter()` methods + * for all nodes of the given compute node. + * + * @param {Node} computeNode - The compute node. + */ + updateAfterForCompute( computeNode ) { + + const nodeBuilder = this.getForCompute( computeNode ); + + for ( const node of nodeBuilder.updateAfterNodes ) { + + // update frame state for each node + + this.getNodeFrameForCompute( computeNode ).updateAfterNode( node ); + + } + + } + /** * Triggers the call of `update()` methods * for all nodes of the given compute node. @@ -61479,12 +61849,6 @@ class Renderer { this._animation.start(); this._initialized = true; - // - - this._inspector.init(); - - // - resolve( this ); } ); @@ -61806,6 +62170,7 @@ class Renderer { await nodes.getForComputeAsync( computeNode ); + nodes.updateBeforeForCompute( computeNode ); nodes.updateForCompute( computeNode ); bindings.updateForCompute( computeNode ); @@ -61815,6 +62180,8 @@ class Renderer { pipelines.getForCompute( computeNode, computeBindings, compilationPromises ); await Promise.all( compilationPromises ); + nodes.updateAfterForCompute( computeNode ); + loaded ++; if ( onProgress !== null ) { @@ -63333,12 +63700,13 @@ class Renderer { * Frees all internal resources of the renderer. Call this method if the renderer * is no longer in use by your app. */ - dispose() { + async dispose() { if ( this._initialized === true ) { this.info.dispose(); + this._inspector.dispose(); this._animation.dispose(); this._objects.dispose(); this._geometries.dispose(); @@ -63355,13 +63723,7 @@ class Renderer { } - Object.values( this.backend.timestampQueryPool ).forEach( queryPool => { - - if ( queryPool !== null ) queryPool.dispose(); - - } ); - - this.backend.dispose(); + await this.backend.dispose(); } @@ -63525,7 +63887,7 @@ class Renderer { if ( this._initialized === false ) { - warn( 'Renderer: .compute() called before the backend is initialized. Try using .computeAsync() instead.' ); + warn( 'Renderer: ".compute()" called before the backend is initialized. Try using ".computeAsync()" instead.' ); return this.computeAsync( computeNodes, dispatchSize ); @@ -63598,6 +63960,7 @@ class Renderer { } + nodes.updateBeforeForCompute( computeNode ); nodes.updateForCompute( computeNode ); bindings.updateForCompute( computeNode ); @@ -63606,6 +63969,8 @@ class Renderer { backend.compute( computeNodes, computeNode, computeBindings, computePipeline, dispatchSize ); + nodes.updateAfterForCompute( computeNode ); + } backend.finishCompute( computeNodes ); @@ -67178,7 +67543,7 @@ ${ flowData.code } */ getInvocationLocalIndex() { - const workgroupSize = this.object.workgroupSize; + const workgroupSize = this.compute.workgroupSize; const size = workgroupSize.reduce( ( acc, curr ) => acc * curr, 1 ); @@ -68250,7 +68615,7 @@ class Backend { * Updates a unique identifier for the given render context that can be used * to allocate resources like occlusion queries or timestamp queries. * - * @param {RenderContext|ComputeNode} abstractRenderContext - The render context. + * @param {RenderContext|ComputeNode|Array} abstractRenderContext - The render context. */ updateTimeStampUID( abstractRenderContext ) { @@ -68259,7 +68624,15 @@ class Backend { let prefix; - if ( abstractRenderContext.isComputeNode === true ) { + let id = abstractRenderContext.id; + + if ( Array.isArray( abstractRenderContext ) ) { + + id = abstractRenderContext.map( c => c.id ).join( ',' ); + + prefix = 'c:' + this.renderer.info.compute.frameCalls; + + } else if ( abstractRenderContext.isComputeNode === true ) { prefix = 'c:' + this.renderer.info.compute.frameCalls; @@ -68269,7 +68642,7 @@ class Backend { } - contextData.timestampUID = prefix + ':' + abstractRenderContext.id + ':f' + frame; + contextData.timestampUID = prefix + ':' + id + ':f' + frame; } @@ -68594,7 +68967,19 @@ class Backend { * * @abstract */ - dispose() { } + async dispose() { + + for ( const queryPool of Object.values( this.timestampQueryPool ) ) { + + if ( queryPool !== null ) { + + await queryPool.dispose(); + + } + + } + + } } @@ -75890,7 +76275,9 @@ class WebGLBackend extends Backend { /** * Frees internal resources. */ - dispose() { + async dispose() { + + await super.dispose(); if ( this.textureUtils !== null ) this.textureUtils.dispose(); @@ -80210,7 +80597,7 @@ const wgslTypeLib$1 = { const parse = ( source ) => { - source = source.trim(); + source = source.replace( /^(?:\s*\/\/[^\r\n]*|\s*\/\*[\s\S]*?\*\/|\s*)+/, '' ); const declaration = source.match( declarationRegexp ); @@ -80548,6 +80935,7 @@ const wgslMethods = { inverse_mat3: 'tsl_inverse_mat3', inverse_mat4: 'tsl_inverse_mat4', inversesqrt: 'inverseSqrt', + faceforward: 'faceForward', bitcast: 'bitcast', floatpack_snorm_2x16: 'pack2x16snorm', floatpack_unorm_2x16: 'pack2x16unorm', @@ -82725,8 +83113,7 @@ ${ flowData.code } } else { // Early strictly validated in computeNode - - const workgroupSize = this.object.workgroupSize; + const workgroupSize = this.compute.workgroupSize; this.computeShader = this._getWGSLComputeCode( shadersData.compute, workgroupSize ); @@ -89156,7 +89543,9 @@ class WebGPUBackend extends Backend { } - dispose() { + async dispose() { + + await super.dispose(); this.bindingUtils.dispose(); this.textureUtils.dispose(); @@ -89173,16 +89562,6 @@ class WebGPUBackend extends Backend { } - if ( this.timestampQueryPool ) { - - for ( const queryPool of Object.values( this.timestampQueryPool ) ) { - - if ( queryPool !== null ) queryPool.dispose(); - - } - - } - if ( this.parameters.device === undefined && this.device !== null ) { this.device.destroy(); diff --git a/examples/jsm/inspector/RendererInspector.js b/examples/jsm/inspector/RendererInspector.js index 172351ca0fb7a0..df66e88916ab0d 100644 --- a/examples/jsm/inspector/RendererInspector.js +++ b/examples/jsm/inspector/RendererInspector.js @@ -57,7 +57,9 @@ class ComputeStats extends ObjectStats { constructor( uid, computeNode ) { - super( uid, computeNode.name ); + const name = computeNode.name || ( computeNode.isComputeNode ? 'Compute' : 'Compute Group' ); + + super( uid, name ); this.computeNode = computeNode; @@ -74,7 +76,9 @@ export class RendererInspector extends InspectorBase { super(); this.currentFrame = null; + this.currentContext = null; this.currentRender = null; + this.currentCompute = null; this.currentNodes = null; this.lastFrame = null; @@ -95,7 +99,7 @@ export class RendererInspector extends InspectorBase { getParent() { - return this.currentRender || this.getFrame(); + return this.currentContext || this.getFrame(); } @@ -104,7 +108,9 @@ export class RendererInspector extends InspectorBase { super.begin(); this.currentFrame = this._createFrame(); - this.currentRender = this.currentFrame; + this.currentContext = this.currentFrame; + this.currentRender = null; + this.currentCompute = null; this.currentNodes = []; } @@ -126,7 +132,9 @@ export class RendererInspector extends InspectorBase { this.lastFrame = frame; this.currentFrame = null; + this.currentContext = null; this.currentRender = null; + this.currentCompute = null; this.currentNodes = null; this._lastFinishTime = now; @@ -500,21 +508,13 @@ export class RendererInspector extends InspectorBase { const currentCompute = new ComputeStats( uid, computeNode ); currentCompute.timestamp = performance.now(); - currentCompute.parent = this.currentCompute || this.getParent(); + currentCompute.parent = this.getParent(); frame.computes.push( currentCompute ); - - if ( this.currentRender !== null ) { - - this.currentRender.children.push( currentCompute ); - - } else { - - frame.children.push( currentCompute ); - - } + currentCompute.parent.children.push( currentCompute ); this.currentCompute = currentCompute; + this.currentContext = currentCompute; } @@ -527,7 +527,8 @@ export class RendererInspector extends InspectorBase { const currentCompute = this.currentCompute; currentCompute.cpu = performance.now() - currentCompute.timestamp; - this.currentCompute = currentCompute.parent.isComputeStats ? currentCompute.parent : null; + this.currentContext = currentCompute.parent; + this.currentCompute = currentCompute.parent && currentCompute.parent.isComputeStats ? currentCompute.parent : null; } @@ -542,18 +543,10 @@ export class RendererInspector extends InspectorBase { currentRender.parent = this.getParent(); frame.renders.push( currentRender ); - - if ( this.currentRender !== null ) { - - this.currentRender.children.push( currentRender ); - - } else { - - frame.children.push( currentRender ); - - } + currentRender.parent.children.push( currentRender ); this.currentRender = currentRender; + this.currentContext = currentRender; } @@ -566,7 +559,8 @@ export class RendererInspector extends InspectorBase { const currentRender = this.currentRender; currentRender.cpu = performance.now() - currentRender.timestamp; - this.currentRender = currentRender.parent; + this.currentContext = currentRender.parent; + this.currentRender = currentRender.parent && currentRender.parent.isRenderStats ? currentRender.parent : null; } diff --git a/src/nodes/core/Node.js b/src/nodes/core/Node.js index 03e7e58fa38cc6..4bd7b03a16a795 100644 --- a/src/nodes/core/Node.js +++ b/src/nodes/core/Node.js @@ -501,9 +501,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#update}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateType() { + getUpdateType( /*frame*/ ) { return this.updateType; @@ -512,9 +513,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#updateBefore}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateBeforeType() { + getUpdateBeforeType( /*frame*/ ) { return this.updateBeforeType; @@ -523,9 +525,10 @@ class Node extends EventDispatcher { /** * Returns the update type of {@link Node#updateAfter}. * + * @param {NodeFrame} [frame] - The current node frame. * @return {NodeUpdateType} The update type. */ - getUpdateAfterType() { + getUpdateAfterType( /*frame*/ ) { return this.updateAfterType; diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js index d37e05c8f99598..7ee73c20b025ea 100644 --- a/src/nodes/core/NodeBuilder.js +++ b/src/nodes/core/NodeBuilder.js @@ -123,6 +123,14 @@ class NodeBuilder { */ this.geometry = ( object && object.geometry ) || null; + /** + * The compute node, if building for compute. + * + * @type {?ComputeNode} + * @default null + */ + this.compute = null; + /** * The current renderer. * @@ -830,8 +838,8 @@ class NodeBuilder { */ addSequentialNode( node ) { - const updateBeforeType = node.getUpdateBeforeType(); - const updateAfterType = node.getUpdateAfterType(); + const updateBeforeType = node.updateBeforeType; + const updateAfterType = node.updateAfterType; if ( updateBeforeType !== NodeUpdateType.NONE || updateAfterType !== NodeUpdateType.NONE ) { @@ -848,7 +856,7 @@ class NodeBuilder { for ( const node of this.nodes ) { - const updateType = node.getUpdateType(); + const updateType = node.updateType; if ( updateType !== NodeUpdateType.NONE ) { @@ -860,8 +868,8 @@ class NodeBuilder { for ( const node of this.sequentialNodes ) { - const updateBeforeType = node.getUpdateBeforeType(); - const updateAfterType = node.getUpdateAfterType(); + const updateBeforeType = node.updateBeforeType; + const updateAfterType = node.updateAfterType; if ( updateBeforeType !== NodeUpdateType.NONE ) { @@ -3110,7 +3118,7 @@ class NodeBuilder { */ prebuild() { - const { object, renderer, material } = this; + const { renderer, material } = this; // < renderer.contextNode > @@ -3158,7 +3166,7 @@ class NodeBuilder { } else { - this.addFlow( 'compute', object ); + this.addFlow( 'compute', this.compute ); } @@ -3456,7 +3464,7 @@ class NodeBuilder { const mrt = this.renderer.getMRT(); - return ( mrt && mrt.has( 'velocity' ) ) || getDataFromObject( this.object ).useVelocity === true; + return ( mrt && mrt.has( 'velocity' ) ) || ( this.object !== null && getDataFromObject( this.object ).useVelocity === true ); } diff --git a/src/nodes/core/NodeFrame.js b/src/nodes/core/NodeFrame.js index a58c94fb1beec9..31423851f5cd75 100644 --- a/src/nodes/core/NodeFrame.js +++ b/src/nodes/core/NodeFrame.js @@ -106,6 +106,14 @@ class NodeFrame { */ this.scene = null; + /** + * A reference to the current compute node. + * + * @type {?ComputeNode} + * @default null + */ + this.compute = null; + } /** @@ -146,7 +154,7 @@ class NodeFrame { */ updateBeforeNode( node ) { - const updateType = node.getUpdateBeforeType(); + const updateType = node.getUpdateBeforeType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { @@ -203,7 +211,7 @@ class NodeFrame { */ updateAfterNode( node ) { - const updateType = node.getUpdateAfterType(); + const updateType = node.getUpdateAfterType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { @@ -252,7 +260,7 @@ class NodeFrame { */ updateNode( node ) { - const updateType = node.getUpdateType(); + const updateType = node.getUpdateType( this ); const reference = node.updateReference( this ); if ( updateType === NodeUpdateType.FRAME ) { diff --git a/src/nodes/display/ScreenNode.js b/src/nodes/display/ScreenNode.js index 3e4afb2389801b..4c664f4d182cb1 100644 --- a/src/nodes/display/ScreenNode.js +++ b/src/nodes/display/ScreenNode.js @@ -67,27 +67,6 @@ class ScreenNode extends Node { } - /** - * This method is overwritten since the node's update type depends on the selected scope. - * - * @return {NodeUpdateType} The update type. - */ - getUpdateType() { - - let updateType = NodeUpdateType.NONE; - - if ( this.scope === ScreenNode.SIZE || this.scope === ScreenNode.VIEWPORT ) { - - updateType = NodeUpdateType.RENDER; - - } - - this.updateType = updateType; - - return updateType; - - } - /** * `ScreenNode` implements {@link Node#update} to retrieve viewport and size information * from the current renderer. @@ -149,6 +128,20 @@ class ScreenNode extends Node { } + // + + let updateType = NodeUpdateType.NONE; + + if ( this.scope === ScreenNode.SIZE || this.scope === ScreenNode.VIEWPORT ) { + + updateType = NodeUpdateType.RENDER; + + } + + this.updateType = updateType; + + // + return output; } diff --git a/src/nodes/gpgpu/ComputeNode.js b/src/nodes/gpgpu/ComputeNode.js index 5c19a94454a945..949d4b71566411 100644 --- a/src/nodes/gpgpu/ComputeNode.js +++ b/src/nodes/gpgpu/ComputeNode.js @@ -84,15 +84,6 @@ class ComputeNode extends Node { */ this.name = ''; - /** - * The `updateBeforeType` is set to `NodeUpdateType.OBJECT` since {@link ComputeNode#updateBefore} - * is executed once per object by default. - * - * @type {string} - * @default 'object' - */ - this.updateBeforeType = NodeUpdateType.OBJECT; - /** * A callback executed when the compute node finishes initialization. * @@ -108,6 +99,15 @@ class ComputeNode extends Node { */ this.countNode = null; + /** + * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node computes + * once per frame. + * + * @type {string} + * @default 'frame' + */ + this.updateBeforeType = NodeUpdateType.FRAME; + } /** @@ -162,6 +162,18 @@ class ComputeNode extends Node { } + /** + * Returns the update type of {@link Node#updateBefore}. + * + * @param {NodeFrame} [frame] - The current node frame. + * @return {NodeUpdateType} The update type. + */ + getUpdateBeforeType( frame ) { + + return frame.compute !== this ? this.updateBeforeType : NodeUpdateType.NONE; + + } + /** * The method execute the compute for this node. * diff --git a/src/renderers/common/Backend.js b/src/renderers/common/Backend.js index cad0d427bc726a..58204b4a9a4605 100644 --- a/src/renderers/common/Backend.js +++ b/src/renderers/common/Backend.js @@ -470,7 +470,7 @@ class Backend { * Updates a unique identifier for the given render context that can be used * to allocate resources like occlusion queries or timestamp queries. * - * @param {RenderContext|ComputeNode} abstractRenderContext - The render context. + * @param {RenderContext|ComputeNode|Array} abstractRenderContext - The render context. */ updateTimeStampUID( abstractRenderContext ) { @@ -479,7 +479,15 @@ class Backend { let prefix; - if ( abstractRenderContext.isComputeNode === true ) { + let id = abstractRenderContext.id; + + if ( Array.isArray( abstractRenderContext ) ) { + + id = abstractRenderContext.map( c => c.id ).join( ',' ); + + prefix = 'c:' + this.renderer.info.compute.frameCalls; + + } else if ( abstractRenderContext.isComputeNode === true ) { prefix = 'c:' + this.renderer.info.compute.frameCalls; @@ -489,7 +497,7 @@ class Backend { } - contextData.timestampUID = prefix + ':' + abstractRenderContext.id + ':f' + frame; + contextData.timestampUID = prefix + ':' + id + ':f' + frame; } diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 398f653252ed73..c05aa68471f07a 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -1163,6 +1163,7 @@ class Renderer { await nodes.getForComputeAsync( computeNode ); + nodes.updateBeforeForCompute( computeNode ); nodes.updateForCompute( computeNode ); bindings.updateForCompute( computeNode ); @@ -1172,6 +1173,8 @@ class Renderer { pipelines.getForCompute( computeNode, computeBindings, compilationPromises ); await Promise.all( compilationPromises ); + nodes.updateAfterForCompute( computeNode ); + loaded ++; if ( onProgress !== null ) { @@ -2877,7 +2880,7 @@ class Renderer { if ( this._initialized === false ) { - warn( 'Renderer: .compute() called before the backend is initialized. Try using .computeAsync() instead.' ); + warn( 'Renderer: ".compute()" called before the backend is initialized. Try using ".computeAsync()" instead.' ); return this.computeAsync( computeNodes, dispatchSize ); @@ -2950,6 +2953,7 @@ class Renderer { } + nodes.updateBeforeForCompute( computeNode ); nodes.updateForCompute( computeNode ); bindings.updateForCompute( computeNode ); @@ -2958,6 +2962,8 @@ class Renderer { backend.compute( computeNodes, computeNode, computeBindings, computePipeline, dispatchSize ); + nodes.updateAfterForCompute( computeNode ); + } backend.finishCompute( computeNodes ); diff --git a/src/renderers/common/nodes/NodeManager.js b/src/renderers/common/nodes/NodeManager.js index 13f023ce9f3538..47a4b2ca4672aa 100644 --- a/src/renderers/common/nodes/NodeManager.js +++ b/src/renderers/common/nodes/NodeManager.js @@ -461,7 +461,9 @@ class NodeManager extends DataMap { if ( nodeBuilderState === undefined || computeData.version !== computeNode.version ) { - const nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer ); + const nodeBuilder = this.backend.createNodeBuilder( null, this.renderer ); + nodeBuilder.compute = computeNode; + const onNodeBuilderCreated = this.renderer.debug.onNodeBuilderCreated; if ( onNodeBuilderCreated !== null ) onNodeBuilderCreated( nodeBuilder, computeNode ); @@ -908,7 +910,18 @@ class NodeManager extends DataMap { } - getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null ) { + /** + * Returns a node frame configured with the given parameters. + * + * @param {Renderer} [renderer=this.renderer] - The renderer. + * @param {?Scene} [scene=null] - The scene. + * @param {?Object3D} [object=null] - The object. + * @param {?Camera} [camera=null] - The camera. + * @param {?Material} [material=null] - The material. + * @param {?Node} [compute=null] - The compute node. + * @return {NodeFrame} The node frame. + */ + getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null, compute = null ) { const nodeFrame = this.nodeFrame; nodeFrame.renderer = renderer; @@ -916,17 +929,36 @@ class NodeManager extends DataMap { nodeFrame.object = object; nodeFrame.camera = camera; nodeFrame.material = material; + nodeFrame.compute = compute; return nodeFrame; } + /** + * Returns a node frame configured for the given render object. + * + * @param {RenderObject} renderObject - The render object. + * @return {NodeFrame} The node frame. + */ getNodeFrameForRender( renderObject ) { return this.getNodeFrame( renderObject.renderer, renderObject.scene, renderObject.object, renderObject.camera, renderObject.material ); } + /** + * Returns a node frame configured for the given compute node. + * + * @param {Node} computeNode - The compute node. + * @return {NodeFrame} The node frame. + */ + getNodeFrameForCompute( computeNode ) { + + return this.getNodeFrame( this.renderer, null, null, null, null, computeNode ); + + } + /** * Returns the current output cache key. * @@ -1027,6 +1059,46 @@ class NodeManager extends DataMap { } + /** + * Triggers the call of `updateBefore()` methods + * for all nodes of the given compute node. + * + * @param {Node} computeNode - The compute node. + */ + updateBeforeForCompute( computeNode ) { + + const nodeBuilder = this.getForCompute( computeNode ); + + for ( const node of nodeBuilder.updateBeforeNodes ) { + + // update frame state for each node + + this.getNodeFrameForCompute( computeNode ).updateBeforeNode( node ); + + } + + } + + /** + * Triggers the call of `updateAfter()` methods + * for all nodes of the given compute node. + * + * @param {Node} computeNode - The compute node. + */ + updateAfterForCompute( computeNode ) { + + const nodeBuilder = this.getForCompute( computeNode ); + + for ( const node of nodeBuilder.updateAfterNodes ) { + + // update frame state for each node + + this.getNodeFrameForCompute( computeNode ).updateAfterNode( node ); + + } + + } + /** * Triggers the call of `update()` methods * for all nodes of the given compute node. diff --git a/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js b/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js index ce4329bc8c57cc..8cadcaaf910b41 100644 --- a/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +++ b/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js @@ -1257,7 +1257,7 @@ ${ flowData.code } */ getInvocationLocalIndex() { - const workgroupSize = this.object.workgroupSize; + const workgroupSize = this.compute.workgroupSize; const size = workgroupSize.reduce( ( acc, curr ) => acc * curr, 1 ); diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js index d3f4a6b3c01506..6a7b1d8da99011 100644 --- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -2409,8 +2409,7 @@ ${ flowData.code } } else { // Early strictly validated in computeNode - - const workgroupSize = this.object.workgroupSize; + const workgroupSize = this.compute.workgroupSize; this.computeShader = this._getWGSLComputeCode( shadersData.compute, workgroupSize );