Selaa lähdekoodia

Object3D: granular control of matrix updates with matrixWorldAutoUpdate (#24028)

* feat(Object3D): object-level `autoUpdate`

* chore: update docs

* fix(Object3D): respect `autoUpdate` in updateMatrixWorld

* fix(renderers): respect `camera.autoUpdate` on render

* Use strict equal codestyle, respect autoUpdate with `updateParents`

* Object3D: rename autoUpdate to matrixWorldAutoUpdate

* Global: respect Object3D.matrixWorldAutoUpdate
Cody Bennett 2 vuotta sitten
vanhempi
commit
d091564e02

+ 6 - 0
docs/api/en/core/Object3D.html

@@ -32,6 +32,12 @@
 		<h3>[property:AnimationClip animations]</h3>
 		<h3>[property:AnimationClip animations]</h3>
 		<p>Array with object's animation clips.</p>
 		<p>Array with object's animation clips.</p>
 
 
+		<h3>[property:Boolean matrixWorldAutoUpdate]</h3>
+		<p>
+		Default is true. If set, then the renderer checks every frame if the object and its children need matrix updates.
+		When it isn't, then you have to maintain all matrices in the object and its children yourself.
+		</p>
+
 		<h3>[property:Boolean castShadow]</h3>
 		<h3>[property:Boolean castShadow]</h3>
 		<p>Whether the object gets rendered into shadow map. Default is `false`.</p>
 		<p>Whether the object gets rendered into shadow map. Default is `false`.</p>
 
 

+ 0 - 6
docs/api/en/scenes/Scene.html

@@ -24,12 +24,6 @@
 
 
 		<h2>Properties</h2>
 		<h2>Properties</h2>
 
 
-		<h3>[property:Boolean autoUpdate]</h3>
-		<p>
-		Default is true. If set, then the renderer checks every frame if the scene and its objects needs matrix updates.
-		When it isn't, then you have to maintain all matrices in the scene yourself.
-		</p>
-
 		<h3>[property:Object background]</h3>
 		<h3>[property:Object background]</h3>
 		<p>
 		<p>
 		If not null, sets the background used when rendering the scene, and is always rendered first.
 		If not null, sets the background used when rendering the scene, and is always rendered first.

+ 6 - 0
docs/api/ko/core/Object3D.html

@@ -30,6 +30,12 @@
 		<h3>[property:AnimationClip animations]</h3>
 		<h3>[property:AnimationClip animations]</h3>
 		<p>객체의 애니메이션 클립 배열입니다.</p>
 		<p>객체의 애니메이션 클립 배열입니다.</p>
 
 
+		<h3>[property:Boolean matrixWorldAutoUpdate]</h3>
+		<p>
+		Default is true. If set, then the renderer checks every frame if the object and its children need matrix updates.
+		When it isn't, then you have to maintain all matrices in the object and its children yourself.
+		</p>
+
 		<h3>[property:Boolean castShadow]</h3>
 		<h3>[property:Boolean castShadow]</h3>
 		<p>객체가 섀도우 맵으로 렌더링 되는지의 여부입니다. 기본값은 *false*입니다.</p>
 		<p>객체가 섀도우 맵으로 렌더링 되는지의 여부입니다. 기본값은 *false*입니다.</p>
 
 

+ 6 - 0
docs/api/zh/core/Object3D.html

@@ -32,6 +32,12 @@
 	<h3>[property:AnimationClip animations]</h3>
 	<h3>[property:AnimationClip animations]</h3>
 	<p>Array with object's animation clips.</p>
 	<p>Array with object's animation clips.</p>
 
 
+	<h3>[property:Boolean matrixWorldAutoUpdate]</h3>
+	<p>
+	Default is true. If set, then the renderer checks every frame if the object and its children need matrix updates.
+	When it isn't, then you have to maintain all matrices in the object and its children yourself.
+	</p>
+
 	<h3>[property:Boolean castShadow]</h3>
 	<h3>[property:Boolean castShadow]</h3>
 	<p>对象是否被渲染到阴影贴图中。默认值为*false*。</p>
 	<p>对象是否被渲染到阴影贴图中。默认值为*false*。</p>
 
 

+ 0 - 6
docs/api/zh/scenes/Scene.html

@@ -25,12 +25,6 @@
 
 
 		<h2>属性</h2>
 		<h2>属性</h2>
 
 
-		<h3>[property:Boolean autoUpdate]</h3>
-		<p>
-			默认值为true,若设置了这个值,则渲染器会检查每一帧是否需要更新场景及其中物体的矩阵。
-			当设为false时,你必须亲自手动维护场景中的矩阵。
-		</p>
-
 		<h3>[property:Object background]</h3>
 		<h3>[property:Object background]</h3>
 		<p>
 		<p>
 		若不为空,在渲染场景的时候将设置背景,且背景总是首先被渲染的。
 		若不为空,在渲染场景的时候将设置背景,且背景总是首先被渲染的。

+ 2 - 2
examples/jsm/effects/AnaglyphEffect.js

@@ -131,9 +131,9 @@ class AnaglyphEffect {
 
 
 			const currentRenderTarget = renderer.getRenderTarget();
 			const currentRenderTarget = renderer.getRenderTarget();
 
 
-			scene.updateMatrixWorld();
+			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
 
 
-			if ( camera.parent === null ) camera.updateMatrixWorld();
+			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 			_stereo.update( camera );
 			_stereo.update( camera );
 
 

+ 3 - 3
examples/jsm/effects/OutlineEffect.js

@@ -450,11 +450,11 @@ class OutlineEffect {
 		this.renderOutline = function ( scene, camera ) {
 		this.renderOutline = function ( scene, camera ) {
 
 
 			const currentAutoClear = renderer.autoClear;
 			const currentAutoClear = renderer.autoClear;
-			const currentSceneAutoUpdate = scene.autoUpdate;
+			const currentSceneAutoUpdate = scene.matrixWorldAutoUpdate;
 			const currentSceneBackground = scene.background;
 			const currentSceneBackground = scene.background;
 			const currentShadowMapEnabled = renderer.shadowMap.enabled;
 			const currentShadowMapEnabled = renderer.shadowMap.enabled;
 
 
-			scene.autoUpdate = false;
+			scene.matrixWorldAutoUpdate = false;
 			scene.background = null;
 			scene.background = null;
 			renderer.autoClear = false;
 			renderer.autoClear = false;
 			renderer.shadowMap.enabled = false;
 			renderer.shadowMap.enabled = false;
@@ -467,7 +467,7 @@ class OutlineEffect {
 
 
 			cleanupCache();
 			cleanupCache();
 
 
-			scene.autoUpdate = currentSceneAutoUpdate;
+			scene.matrixWorldAutoUpdate = currentSceneAutoUpdate;
 			scene.background = currentSceneBackground;
 			scene.background = currentSceneBackground;
 			renderer.autoClear = currentAutoClear;
 			renderer.autoClear = currentAutoClear;
 			renderer.shadowMap.enabled = currentShadowMapEnabled;
 			renderer.shadowMap.enabled = currentShadowMapEnabled;

+ 2 - 2
examples/jsm/effects/ParallaxBarrierEffect.js

@@ -90,9 +90,9 @@ class ParallaxBarrierEffect {
 
 
 		this.render = function ( scene, camera ) {
 		this.render = function ( scene, camera ) {
 
 
-			scene.updateMatrixWorld();
+			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
 
 
-			if ( camera.parent === null ) camera.updateMatrixWorld();
+			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 			_stereo.update( camera );
 			_stereo.update( camera );
 
 

+ 2 - 2
examples/jsm/effects/PeppersGhostEffect.js

@@ -53,9 +53,9 @@ class PeppersGhostEffect {
 
 
 		this.render = function ( scene, camera ) {
 		this.render = function ( scene, camera ) {
 
 
-			scene.updateMatrixWorld();
+			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
 
 
-			if ( camera.parent === null ) camera.updateMatrixWorld();
+			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 			camera.matrixWorld.decompose( _position, _quaternion, _scale );
 			camera.matrixWorld.decompose( _position, _quaternion, _scale );
 
 

+ 2 - 2
examples/jsm/effects/StereoEffect.js

@@ -25,9 +25,9 @@ class StereoEffect {
 
 
 		this.render = function ( scene, camera ) {
 		this.render = function ( scene, camera ) {
 
 
-			scene.updateMatrixWorld();
+			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
 
 
-			if ( camera.parent === null ) camera.updateMatrixWorld();
+			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 			_stereo.update( camera );
 			_stereo.update( camera );
 
 

+ 2 - 2
examples/jsm/renderers/CSS2DRenderer.js

@@ -85,8 +85,8 @@ class CSS2DRenderer {
 
 
 		this.render = function ( scene, camera ) {
 		this.render = function ( scene, camera ) {
 
 
-			if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
-			if ( camera.parent === null ) camera.updateMatrixWorld();
+			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
+			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 			_viewMatrix.copy( camera.matrixWorldInverse );
 			_viewMatrix.copy( camera.matrixWorldInverse );
 			_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
 			_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );

+ 2 - 2
examples/jsm/renderers/CSS3DRenderer.js

@@ -132,8 +132,8 @@ class CSS3DRenderer {
 
 
 			}
 			}
 
 
-			if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
-			if ( camera.parent === null ) camera.updateMatrixWorld();
+			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
+			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 			let tx, ty;
 			let tx, ty;
 
 

+ 2 - 2
examples/jsm/renderers/Projector.js

@@ -411,8 +411,8 @@ class Projector {
 
 
 			_renderData.elements.length = 0;
 			_renderData.elements.length = 0;
 
 
-			if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
-			if ( camera.parent === null ) camera.updateMatrixWorld();
+			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
+			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 			_viewMatrix.copy( camera.matrixWorldInverse );
 			_viewMatrix.copy( camera.matrixWorldInverse );
 			_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
 			_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );

+ 2 - 2
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -242,9 +242,9 @@ class WebGPURenderer {
 
 
 		//
 		//
 
 
-		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+		if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
 
 
-		if ( camera.parent === null ) camera.updateMatrixWorld();
+		if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 		if ( this._info.autoReset === true ) this._info.reset();
 		if ( this._info.autoReset === true ) this._info.reset();
 
 

+ 16 - 3
src/core/Object3D.js

@@ -100,6 +100,8 @@ class Object3D extends EventDispatcher {
 		this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;
 		this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;
 		this.matrixWorldNeedsUpdate = false;
 		this.matrixWorldNeedsUpdate = false;
 
 
+		this.matrixWorldAutoUpdate = Object3D.DefaultMatrixWorldAutoUpdate; // checked by the renderer
+
 		this.layers = new Layers();
 		this.layers = new Layers();
 		this.visible = true;
 		this.visible = true;
 
 
@@ -584,7 +586,11 @@ class Object3D extends EventDispatcher {
 
 
 		for ( let i = 0, l = children.length; i < l; i ++ ) {
 		for ( let i = 0, l = children.length; i < l; i ++ ) {
 
 
-			children[ i ].updateMatrixWorld( force );
+			if ( children[ i ].matrixWorldAutoUpdate === true || force === true ) {
+
+				children[ i ].updateMatrixWorld( force );
+
+			}
 
 
 		}
 		}
 
 
@@ -594,7 +600,7 @@ class Object3D extends EventDispatcher {
 
 
 		const parent = this.parent;
 		const parent = this.parent;
 
 
-		if ( updateParents === true && parent !== null ) {
+		if ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) {
 
 
 			parent.updateWorldMatrix( true, false );
 			parent.updateWorldMatrix( true, false );
 
 
@@ -620,7 +626,11 @@ class Object3D extends EventDispatcher {
 
 
 			for ( let i = 0, l = children.length; i < l; i ++ ) {
 			for ( let i = 0, l = children.length; i < l; i ++ ) {
 
 
-				children[ i ].updateWorldMatrix( false, true );
+				if ( children[ i ].matrixWorldAutoUpdate === true ) {
+
+					children[ i ].updateWorldMatrix( false, true );
+
+				}
 
 
 			}
 			}
 
 
@@ -893,6 +903,8 @@ class Object3D extends EventDispatcher {
 		this.matrixAutoUpdate = source.matrixAutoUpdate;
 		this.matrixAutoUpdate = source.matrixAutoUpdate;
 		this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
 		this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
 
 
+		this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate;
+
 		this.layers.mask = source.layers.mask;
 		this.layers.mask = source.layers.mask;
 		this.visible = source.visible;
 		this.visible = source.visible;
 
 
@@ -923,5 +935,6 @@ class Object3D extends EventDispatcher {
 
 
 Object3D.DefaultUp = /*@__PURE__*/ new Vector3( 0, 1, 0 );
 Object3D.DefaultUp = /*@__PURE__*/ new Vector3( 0, 1, 0 );
 Object3D.DefaultMatrixAutoUpdate = true;
 Object3D.DefaultMatrixAutoUpdate = true;
+Object3D.DefaultMatrixWorldAutoUpdate = true;
 
 
 export { Object3D };
 export { Object3D };

+ 2 - 2
src/renderers/WebGLRenderer.js

@@ -955,11 +955,11 @@ function WebGLRenderer( parameters = {} ) {
 
 
 		// update scene graph
 		// update scene graph
 
 
-		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+		if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
 
 
 		// update camera matrices and frustum
 		// update camera matrices and frustum
 
 
-		if ( camera.parent === null ) camera.updateMatrixWorld();
+		if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
 
 		if ( xr.enabled === true && xr.isPresenting === true ) {
 		if ( xr.enabled === true && xr.isPresenting === true ) {
 
 

+ 0 - 3
src/scenes/Scene.js

@@ -16,8 +16,6 @@ class Scene extends Object3D {
 
 
 		this.overrideMaterial = null;
 		this.overrideMaterial = null;
 
 
-		this.autoUpdate = true; // checked by the renderer
-
 		if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {
 		if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {
 
 
 			__THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) );
 			__THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) );
@@ -36,7 +34,6 @@ class Scene extends Object3D {
 
 
 		if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();
 		if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();
 
 
-		this.autoUpdate = source.autoUpdate;
 		this.matrixAutoUpdate = source.matrixAutoUpdate;
 		this.matrixAutoUpdate = source.matrixAutoUpdate;
 
 
 		return this;
 		return this;

+ 37 - 0
test/unit/src/core/Object3D.tests.js

@@ -786,8 +786,27 @@ export default QUnit.module( 'Core', () => {
 				0, 0, 0, 1
 				0, 0, 0, 1
 			], 'No effect to child world matrix if parent local and world matrices and child local matrix are not updated' );
 			], 'No effect to child world matrix if parent local and world matrices and child local matrix are not updated' );
 
 
+			// -- matrixWorldAutoUpdate = false test
+
+			parent.position.set( 3, 2, 1 );
+			parent.updateMatrix();
+			parent.matrixWorldNeedsUpdate = false;
+
+			child.matrixWorldAutoUpdate = false;
+			parent.updateMatrixWorld();
+
+			assert.deepEqual( child.matrixWorld.elements, [
+				1, 0, 0, 0,
+				0, 1, 0, 0,
+				0, 0, 1, 0,
+				0, 0, 0, 1
+			], 'No effect to child world matrix when matrixWorldAutoUpdate is set to false' );
+
 			// -- Propagation to children world matrices test
 			// -- Propagation to children world matrices test
 
 
+			child.position.set( 0, 0, 0 );
+			parent.position.set( 1, 2, 3 );
+			child.matrixWorldAutoUpdate = true;
 			parent.matrixAutoUpdate = true;
 			parent.matrixAutoUpdate = true;
 			parent.updateMatrixWorld();
 			parent.updateMatrixWorld();
 
 
@@ -1013,6 +1032,24 @@ export default QUnit.module( 'Core', () => {
 				m.setPosition( parent.position ).elements,
 				m.setPosition( parent.position ).elements,
 				'object\'s world matrix is updated even if matrixAutoUpdate is false' );
 				'object\'s world matrix is updated even if matrixAutoUpdate is false' );
 
 
+			// object.matrixWorldAutoUpdate = false test
+
+			parent.matrixWorldAutoUpdate = false;
+			child.matrixWorldAutoUpdate = false;
+
+			child.matrixWorld.identity();
+			parent.matrixWorld.identity();
+
+			object.updateWorldMatrix( true, true );
+
+			assert.deepEqual( child.matrixWorld.elements,
+				m.identity().elements,
+				'No effect to child\'s world matrix if matrixWorldAutoUpdate is false' );
+
+			assert.deepEqual( parent.matrixWorld.elements,
+				m.identity().elements,
+				'No effect to parent\'s world matrix if matrixWorldAutoUpdate is false' );
+
 		} );
 		} );
 
 
 		QUnit.test( 'toJSON', ( assert ) => {
 		QUnit.test( 'toJSON', ( assert ) => {