diff --git a/examples/jsm/controls/ArcballControls.js b/examples/jsm/controls/ArcballControls.js index d0813b420a4467..e2c1d49370e531 100644 --- a/examples/jsm/controls/ArcballControls.js +++ b/examples/jsm/controls/ArcballControls.js @@ -265,6 +265,10 @@ class ArcballControls extends Controls { * performed trying to maintain the same visible portion given by initial near and far * values. Only works with perspective cameras. * + * This feature only works as expected if the camera's initial state (position, near and far values) + * is correctly configured before creating the controls. Otherwise {@link ArcballControls#setCamera} + * must be called by the application. + * * @type {boolean} * @default false */ diff --git a/examples/jsm/tsl/display/SSAAPassNode.js b/examples/jsm/tsl/display/SSAAPassNode.js index 4d461f590a83e7..444336c4df59e8 100644 --- a/examples/jsm/tsl/display/SSAAPassNode.js +++ b/examples/jsm/tsl/display/SSAAPassNode.js @@ -1,9 +1,8 @@ -import { AdditiveBlending, Color, Vector2, RendererUtils, PassNode, QuadMesh, NodeMaterial } from 'three/webgpu'; +import { AdditiveBlending, Color, Vector2, PassNode, QuadMesh, NodeMaterial } from 'three/webgpu'; import { uniform, mrt, texture, getTextureIndex, unpremultiplyAlpha } from 'three/tsl'; const _size = /*@__PURE__*/ new Vector2(); - -let _rendererState; +const _clearColor = /*@__PURE__*/ new Color(); /** * A special render pass node that renders the scene with SSAA (Supersampling Anti-Aliasing). @@ -61,22 +60,6 @@ class SSAAPassNode extends PassNode { */ this.unbiased = true; - /** - * The clear color of the pass. - * - * @type {Color} - * @default 0x000000 - */ - this.clearColor = new Color( 0x000000 ); - - /** - * The clear alpha of the pass. - * - * @type {number} - * @default 0 - */ - this.clearAlpha = 0; - /** * A uniform node representing the sample weight. * @@ -114,8 +97,6 @@ class SSAAPassNode extends PassNode { const { renderer } = frame; const { scene, camera } = this; - _rendererState = RendererUtils.resetRendererState( renderer, _rendererState ); - // this._pixelRatio = renderer.getPixelRatio(); @@ -130,6 +111,12 @@ class SSAAPassNode extends PassNode { this._cameraNear.value = camera.near; this._cameraFar.value = camera.far; + const currentRenderTarget = renderer.getRenderTarget(); + const currentMRT = renderer.getMRT(); + const currentAutoClear = renderer.autoClear; + const currentClearColor = renderer.getClearColor( _clearColor ); + const currentClearAlpha = renderer.getClearAlpha(); + renderer.setMRT( this.getMRT() ); renderer.autoClear = false; @@ -186,7 +173,6 @@ class SSAAPassNode extends PassNode { } - renderer.setClearColor( this.clearColor, this.clearAlpha ); renderer.setRenderTarget( this._sampleRenderTarget ); renderer.clear(); renderer.render( scene, camera ); @@ -200,6 +186,8 @@ class SSAAPassNode extends PassNode { renderer.setClearColor( 0x000000, 0.0 ); renderer.clear(); + renderer.setClearColor( currentClearColor, currentClearAlpha ); + } this._quadMesh.render( renderer ); @@ -228,9 +216,9 @@ class SSAAPassNode extends PassNode { } - // - - RendererUtils.restoreRendererState( renderer, _rendererState ); + renderer.setRenderTarget( currentRenderTarget ); + renderer.setMRT( currentMRT ); + renderer.autoClear = currentAutoClear; } diff --git a/examples/webgpu_postprocessing_ssaa.html b/examples/webgpu_postprocessing_ssaa.html index 1e05a12960543e..234593f0e829d9 100644 --- a/examples/webgpu_postprocessing_ssaa.html +++ b/examples/webgpu_postprocessing_ssaa.html @@ -194,8 +194,7 @@ } - ssaaRenderPass.clearColor.set( newColor ); - ssaaRenderPass.clearAlpha = params.clearAlpha; + renderer.setClearColor( newColor, params.clearAlpha ); ssaaRenderPass.sampleLevel = params.sampleLevel; diff --git a/test/unit/src/math/interpolants/CustomInterpolant.tests.js b/test/unit/src/math/interpolants/CustomInterpolant.tests.js new file mode 100644 index 00000000000000..b3e07c51775b93 --- /dev/null +++ b/test/unit/src/math/interpolants/CustomInterpolant.tests.js @@ -0,0 +1,117 @@ +import { Interpolant } from '../../../../../src/math/Interpolant.js'; + +export default QUnit.module( 'Maths', () => { + + QUnit.module( 'Interpolants', () => { + + QUnit.module( 'CustomInterpolant', () => { + + // A custom cubic spline interpolant mimicking `GLTFCubicSplineInterpolant` + // from `GLTFLoader`. The keyframe layout for CUBICSPLINE animations is: + // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] + + class CubicSplineInterpolant extends Interpolant { + + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + + } + + copySampleValue_( index ) { + + const result = this.resultBuffer, + values = this.sampleValues, + valueSize = this.valueSize, + offset = index * valueSize * 3 + valueSize; + + for ( let i = 0; i !== valueSize; i ++ ) { + + result[ i ] = values[ offset + i ]; + + } + + return result; + + } + + interpolate_( i1, t0, t, t1 ) { + + const result = this.resultBuffer; + const values = this.sampleValues; + const stride = this.valueSize; + + const stride2 = stride * 2; + const stride3 = stride * 3; + + const td = t1 - t0; + + const p = ( t - t0 ) / td; + const pp = p * p; + const ppp = pp * p; + + const offset1 = i1 * stride3; + const offset0 = offset1 - stride3; + + const s2 = - 2 * ppp + 3 * pp; + const s3 = ppp - pp; + const s0 = 1 - s2; + const s1 = s3 - pp + p; + + for ( let i = 0; i !== stride; i ++ ) { + + const p0 = values[ offset0 + i + stride ]; + const m0 = values[ offset0 + i + stride2 ] * td; + const p1 = values[ offset1 + i + stride ]; + const m1 = values[ offset1 + i ] * td; + + result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; + + } + + return result; + + } + + } + + // INHERITANCE + QUnit.test( 'Extending', ( assert ) => { + + // parameterPositions, sampleValues, sampleSize, resultBuffer + const object = new CubicSplineInterpolant( [ 0, 1 ], [ 0, 0, 0, 0, 0, 0 ], 1, [] ); + assert.strictEqual( + object instanceof Interpolant, true, + 'CubicSplineInterpolant extends from Interpolant' + ); + + } ); + + // PUBLIC + QUnit.test( 'evaluate', ( assert ) => { + + // Two keyframes at t = 0 and t = 1, valueSize = 1. + // Layout: [ in_0, v_0, out_0, in_1, v_1, out_1 ] + // Vertex values 0 -> 1 with non-zero tangents to exercise all spline terms. + const positions = [ 0, 1 ]; + const values = [ 0, 0, 1, - 1, 1, 0 ]; + const interpolant = new CubicSplineInterpolant( positions, values, 1, [ 0 ] ); + + assert.deepEqual( interpolant.evaluate( 0 ), [ 0 ], 'evaluate at first keyframe' ); + assert.deepEqual( interpolant.evaluate( 1 ), [ 1 ], 'evaluate at last keyframe' ); + + // At t = 0.5 with td = 1, p = 0.5 → s0 = 0.5, s1 = 0.125, s2 = 0.5, s3 = -0.125 + // result = 0.5 * 0 + 0.125 * 1 + 0.5 * 1 + ( -0.125 ) * ( -1 ) = 0.75 + assert.deepEqual( interpolant.evaluate( 0.5 ), [ 0.75 ], 'evaluate inside interval' ); + + // Out-of-range queries clamp to the boundary spline vertex. + assert.deepEqual( interpolant.evaluate( - 1 ), [ 0 ], 'evaluate before first keyframe' ); + assert.deepEqual( interpolant.evaluate( 2 ), [ 1 ], 'evaluate after last keyframe' ); + + } ); + + } ); + + } ); + +} ); diff --git a/test/unit/three.source.unit.js b/test/unit/three.source.unit.js index 96c8e2ca0c3602..de1a2212f5aae2 100644 --- a/test/unit/three.source.unit.js +++ b/test/unit/three.source.unit.js @@ -208,6 +208,7 @@ import './src/math/Vector4.tests.js'; //src/math/interpolants import './src/math/interpolants/CubicInterpolant.tests.js'; +import './src/math/interpolants/CustomInterpolant.tests.js'; import './src/math/interpolants/DiscreteInterpolant.tests.js'; import './src/math/interpolants/LinearInterpolant.tests.js'; import './src/math/interpolants/QuaternionLinearInterpolant.tests.js';