Skip to content

Commit 9b9b1e2

Browse files
wrangelvidCody Bennett
andcommitted
Layers: Add recursive property
Co-Authored-By: Cody Bennett <cody.j.bennett@icloud.com>
1 parent 90d2cdd commit 9b9b1e2

File tree

15 files changed

+186
-58
lines changed

15 files changed

+186
-58
lines changed

examples/jsm/renderers/CSS2DRenderer.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ class CSS2DRenderer {
211211

212212
}
213213

214-
function renderObject( object, scene, camera ) {
214+
function renderObject( object, scene, camera, inheritedLayers = null ) {
215215

216216
if ( object.visible === false ) {
217217

@@ -221,12 +221,23 @@ class CSS2DRenderer {
221221

222222
}
223223

224+
const effectiveLayers = inheritedLayers !== null ? inheritedLayers : object.layers;
225+
const layerVisible = effectiveLayers.test( camera.layers );
226+
227+
if ( layerVisible === false && effectiveLayers.recursive === true ) {
228+
229+
hideObject( object );
230+
231+
return;
232+
233+
}
234+
224235
if ( object.isCSS2DObject ) {
225236

226237
_vector.setFromMatrixPosition( object.matrixWorld );
227238
_vector.applyMatrix4( _viewProjectionMatrix );
228239

229-
const visible = ( _vector.z >= - 1 && _vector.z <= 1 ) && ( object.layers.test( camera.layers ) === true );
240+
const visible = ( _vector.z >= - 1 && _vector.z <= 1 ) && layerVisible;
230241

231242
const element = object.element;
232243
element.style.display = visible === true ? '' : 'none';
@@ -255,9 +266,11 @@ class CSS2DRenderer {
255266

256267
}
257268

269+
const childInheritedLayers = object.layers.recursive === true ? object.layers : inheritedLayers;
270+
258271
for ( let i = 0, l = object.children.length; i < l; i ++ ) {
259272

260-
renderObject( object.children[ i ], scene, camera );
273+
renderObject( object.children[ i ], scene, camera, childInheritedLayers );
261274

262275
}
263276

examples/jsm/renderers/CSS3DRenderer.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ class CSS3DRenderer {
359359

360360
}
361361

362-
function renderObject( object, scene, camera, cameraCSSMatrix ) {
362+
function renderObject( object, scene, camera, cameraCSSMatrix, inheritedLayers = null ) {
363363

364364
if ( object.visible === false ) {
365365

@@ -369,9 +369,20 @@ class CSS3DRenderer {
369369

370370
}
371371

372+
const effectiveLayers = inheritedLayers !== null ? inheritedLayers : object.layers;
373+
const layerVisible = effectiveLayers.test( camera.layers );
374+
375+
if ( layerVisible === false && effectiveLayers.recursive === true ) {
376+
377+
hideObject( object );
378+
379+
return;
380+
381+
}
382+
372383
if ( object.isCSS3DObject ) {
373384

374-
const visible = ( object.layers.test( camera.layers ) === true );
385+
const visible = layerVisible;
375386

376387
const element = object.element;
377388
element.style.display = visible === true ? '' : 'none';
@@ -431,9 +442,11 @@ class CSS3DRenderer {
431442

432443
}
433444

445+
const childInheritedLayers = object.layers.recursive === true ? object.layers : inheritedLayers;
446+
434447
for ( let i = 0, l = object.children.length; i < l; i ++ ) {
435448

436-
renderObject( object.children[ i ], scene, camera, cameraCSSMatrix );
449+
renderObject( object.children[ i ], scene, camera, cameraCSSMatrix, childInheritedLayers );
437450

438451
}
439452

src/core/Layers.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ class Layers {
2626
*/
2727
this.mask = 1 | 0;
2828

29+
/**
30+
* When set to `true`, this object's layer mask is propagated to all
31+
* descendants during rendering and raycasting traversal. If the layer
32+
* test fails, the entire subtree is skipped. If it passes, children
33+
* inherit this object's layers instead of using their own.
34+
*
35+
* @type {boolean}
36+
* @default false
37+
*/
38+
this.recursive = false;
39+
2940
}
3041

3142
/**

src/core/Object3D.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,37 @@ class Object3D extends EventDispatcher {
10801080

10811081
}
10821082

1083+
/**
1084+
* Like {@link Object3D#traverseVisible}, but the callback will only be executed
1085+
* for visible 3D objects that pass the layer test. Descendants of invisible
1086+
* 3D objects and objects with recursive layers that fail the test are not traversed.
1087+
*
1088+
* Note: Modifying the scene graph inside the callback is discouraged.
1089+
*
1090+
* @param {Layers} cameraLayers - The camera layers to test against.
1091+
* @param {Function} callback - A callback function that allows to process the current 3D object.
1092+
*/
1093+
traverseVisibleWithLayers( cameraLayers, callback, inheritedLayers = null ) {
1094+
1095+
if ( this.visible === false ) return;
1096+
1097+
const effectiveLayers = inheritedLayers ?? this.layers;
1098+
const layerVisible = effectiveLayers.test( cameraLayers );
1099+
1100+
if ( !layerVisible && effectiveLayers.recursive ) return;
1101+
1102+
if ( layerVisible ) callback( this );
1103+
1104+
const childInheritedLayers = this.layers.recursive ? this.layers : inheritedLayers;
1105+
1106+
for ( let i = 0, l = this.children.length; i < l; i ++ ) {
1107+
1108+
this.children[ i ].traverseVisibleWithLayers( cameraLayers, callback, childInheritedLayers );
1109+
1110+
}
1111+
1112+
}
1113+
10831114
/**
10841115
* Like {@link Object3D#traverse}, but the callback will only be executed for all ancestors.
10851116
*

src/core/Raycaster.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,16 @@ function ascSort( a, b ) {
233233

234234
}
235235

236-
function intersect( object, raycaster, intersects, recursive ) {
236+
function intersect( object, raycaster, intersects, recursive, inheritedLayers = null ) {
237+
238+
const effectiveLayers = inheritedLayers !== null ? inheritedLayers : object.layers;
239+
const visible = effectiveLayers.test( raycaster.layers );
240+
241+
if ( visible === false && effectiveLayers.recursive === true ) return;
237242

238243
let propagate = true;
239244

240-
if ( object.layers.test( raycaster.layers ) ) {
245+
if ( visible ) {
241246

242247
const result = object.raycast( raycaster, intersects );
243248

@@ -247,11 +252,13 @@ function intersect( object, raycaster, intersects, recursive ) {
247252

248253
if ( propagate === true && recursive === true ) {
249254

255+
const childInheritedLayers = object.layers.recursive === true ? object.layers : inheritedLayers;
256+
250257
const children = object.children;
251258

252259
for ( let i = 0, l = children.length; i < l; i ++ ) {
253260

254-
intersect( children[ i ], raycaster, intersects, true );
261+
intersect( children[ i ], raycaster, intersects, true, childInheritedLayers );
255262

256263
}
257264

src/renderers/WebGLRenderer.js

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,9 +1373,9 @@ class WebGLRenderer {
13731373

13741374
// gather lights from both the target scene and the new object that will be added to the scene.
13751375

1376-
targetScene.traverseVisible( function ( object ) {
1376+
targetScene.traverseVisibleWithLayers( camera.layers, function ( object ) {
13771377

1378-
if ( object.isLight && object.layers.test( camera.layers ) ) {
1378+
if ( object.isLight ) {
13791379

13801380
currentRenderState.pushLight( object );
13811381

@@ -1391,9 +1391,9 @@ class WebGLRenderer {
13911391

13921392
if ( scene !== targetScene ) {
13931393

1394-
scene.traverseVisible( function ( object ) {
1394+
scene.traverseVisibleWithLayers( camera.layers, function ( object ) {
13951395

1396-
if ( object.isLight && object.layers.test( camera.layers ) ) {
1396+
if ( object.isLight ) {
13971397

13981398
currentRenderState.pushLight( object );
13991399

@@ -1797,11 +1797,14 @@ class WebGLRenderer {
17971797

17981798
};
17991799

1800-
function projectObject( object, camera, groupOrder, sortObjects ) {
1800+
function projectObject( object, camera, groupOrder, sortObjects, inheritedLayers = null ) {
18011801

18021802
if ( object.visible === false ) return;
18031803

1804-
const visible = object.layers.test( camera.layers );
1804+
const effectiveLayers = inheritedLayers !== null ? inheritedLayers : object.layers;
1805+
const visible = effectiveLayers.test( camera.layers );
1806+
1807+
if ( visible === false && effectiveLayers.recursive === true ) return;
18051808

18061809
if ( visible ) {
18071810

@@ -1839,7 +1842,7 @@ class WebGLRenderer {
18391842

18401843
if ( material.visible ) {
18411844

1842-
currentRenderList.push( object, geometry, material, groupOrder, _vector4.z, null );
1845+
currentRenderList.push( object, geometry, material, groupOrder, _vector4.z, null, effectiveLayers );
18431846

18441847
}
18451848

@@ -1883,15 +1886,15 @@ class WebGLRenderer {
18831886

18841887
if ( groupMaterial && groupMaterial.visible ) {
18851888

1886-
currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector4.z, group );
1889+
currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector4.z, group, effectiveLayers );
18871890

18881891
}
18891892

18901893
}
18911894

18921895
} else if ( material.visible ) {
18931896

1894-
currentRenderList.push( object, geometry, material, groupOrder, _vector4.z, null );
1897+
currentRenderList.push( object, geometry, material, groupOrder, _vector4.z, null, effectiveLayers );
18951898

18961899
}
18971900

@@ -1901,11 +1904,13 @@ class WebGLRenderer {
19011904

19021905
}
19031906

1907+
const childInheritedLayers = object.layers.recursive === true ? object.layers : inheritedLayers;
1908+
19041909
const children = object.children;
19051910

19061911
for ( let i = 0, l = children.length; i < l; i ++ ) {
19071912

1908-
projectObject( children[ i ], camera, groupOrder, sortObjects );
1913+
projectObject( children[ i ], camera, groupOrder, sortObjects, childInheritedLayers );
19091914

19101915
}
19111916

@@ -2020,9 +2025,9 @@ class WebGLRenderer {
20202025

20212026
const renderItem = transmissiveObjects[ i ];
20222027

2023-
const { object, geometry, material, group } = renderItem;
2028+
const { object, geometry, material, group, effectiveLayers } = renderItem;
20242029

2025-
if ( material.side === DoubleSide && object.layers.test( camera.layers ) ) {
2030+
if ( material.side === DoubleSide && effectiveLayers.test( camera.layers ) ) {
20262031

20272032
const currentSide = material.side;
20282033

@@ -2067,7 +2072,7 @@ class WebGLRenderer {
20672072

20682073
const renderItem = renderList[ i ];
20692074

2070-
const { object, geometry, group } = renderItem;
2075+
const { object, geometry, group, effectiveLayers } = renderItem;
20712076
let material = renderItem.material;
20722077

20732078
if ( material.allowOverride === true && overrideMaterial !== null ) {
@@ -2076,7 +2081,7 @@ class WebGLRenderer {
20762081

20772082
}
20782083

2079-
if ( object.layers.test( camera.layers ) ) {
2084+
if ( effectiveLayers.test( camera.layers ) ) {
20802085

20812086
renderObject( object, scene, camera, geometry, material, group );
20822087

src/renderers/common/RenderList.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class RenderList {
225225
* @param {ClippingContext} clippingContext - The current clipping context.
226226
* @return {Object} The render item.
227227
*/
228-
getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext ) {
228+
getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext, effectiveLayers ) {
229229

230230
let renderItem = this.renderItems[ this.renderItemsIndex ];
231231

@@ -240,7 +240,8 @@ class RenderList {
240240
renderOrder: object.renderOrder,
241241
z: z,
242242
group: group,
243-
clippingContext: clippingContext
243+
clippingContext: clippingContext,
244+
effectiveLayers: effectiveLayers
244245
};
245246

246247
this.renderItems[ this.renderItemsIndex ] = renderItem;
@@ -256,6 +257,7 @@ class RenderList {
256257
renderItem.z = z;
257258
renderItem.group = group;
258259
renderItem.clippingContext = clippingContext;
260+
renderItem.effectiveLayers = effectiveLayers;
259261

260262
}
261263

@@ -276,10 +278,11 @@ class RenderList {
276278
* @param {number} z - Th 3D object's depth value (z value in clip space).
277279
* @param {?number} group - {?Object} group - Only relevant for objects using multiple materials. This represents a group entry from the respective `BufferGeometry`.
278280
* @param {ClippingContext} clippingContext - The current clipping context.
281+
* @param {Layers} effectiveLayers - The effective layers for layer visibility testing.
279282
*/
280-
push( object, geometry, material, groupOrder, z, group, clippingContext ) {
283+
push( object, geometry, material, groupOrder, z, group, clippingContext, effectiveLayers ) {
281284

282-
const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext );
285+
const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext, effectiveLayers );
283286

284287
if ( object.occlusionTest === true ) this.occlusionQueryCount ++;
285288

@@ -310,10 +313,11 @@ class RenderList {
310313
* @param {number} z - Th 3D object's depth value (z value in clip space).
311314
* @param {?number} group - {?Object} group - Only relevant for objects using multiple materials. This represents a group entry from the respective `BufferGeometry`.
312315
* @param {ClippingContext} clippingContext - The current clipping context.
316+
* @param {Layers} effectiveLayers - The effective layers for layer visibility testing.
313317
*/
314-
unshift( object, geometry, material, groupOrder, z, group, clippingContext ) {
318+
unshift( object, geometry, material, groupOrder, z, group, clippingContext, effectiveLayers ) {
315319

316-
const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext );
320+
const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext, effectiveLayers );
317321

318322
if ( material.transparent === true || material.transmission > 0 ||
319323
( material.transmissionNode && material.transmissionNode.isNode ) ||

src/renderers/common/RenderObject.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,15 @@ class RenderObject {
200200
*/
201201
this.group = null;
202202

203+
/**
204+
* The effective layers for layer visibility testing.
205+
* This accounts for recursive layer inheritance from ancestors.
206+
*
207+
* @type {?Layers}
208+
* @default null
209+
*/
210+
this.effectiveLayers = null;
211+
203212
/**
204213
* An array holding the vertex buffers which can
205214
* be buffer attributes but also interleaved buffers.

0 commit comments

Comments
 (0)