Skip to content

Commit b1987ba

Browse files
committed
fix(core): make sure renderer work correctly for two DOMs elements that might contain THREE children
1 parent 350d4b6 commit b1987ba

File tree

6 files changed

+108
-11
lines changed

6 files changed

+108
-11
lines changed

Diff for: apps/sandbox/src/app/app.component.ts

+90-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { NgIf } from '@angular/common';
2-
import { CUSTOM_ELEMENTS_SCHEMA, Component, computed, signal } from '@angular/core';
2+
import { CUSTOM_ELEMENTS_SCHEMA, Component, Input, computed, signal } from '@angular/core';
3+
import { Triplet } from '@pmndrs/cannon-worker-api';
34
import { NgtArgs, NgtCanvas, extend } from 'angular-three';
5+
import { NgtcPhysics } from 'angular-three-cannon';
6+
import { NgtcDebug } from 'angular-three-cannon/debug';
7+
import { injectBox, injectPlane } from 'angular-three-cannon/services';
48
import { NgtpBloom, NgtpEffectComposer } from 'angular-three-postprocessing';
59
import { NgtsGrid } from 'angular-three-soba/abstractions';
610
import { NgtsOrbitControls } from 'angular-three-soba/controls';
@@ -10,6 +14,82 @@ import * as THREE from 'three';
1014

1115
extend(THREE);
1216

17+
@Component({
18+
selector: 'app-plane',
19+
standalone: true,
20+
template: `
21+
<ngt-mesh [ref]="planeApi.ref" [receiveShadow]="true">
22+
<ngt-plane-geometry *args="[1000, 1000]" />
23+
<ngt-mesh-standard-material color="#171717" />
24+
</ngt-mesh>
25+
`,
26+
imports: [NgtArgs],
27+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
28+
})
29+
export class Plane {
30+
Math = Math;
31+
@Input() position = [0, 0, 0];
32+
33+
planeApi = injectPlane(() => ({ mass: 0, position: this.position as Triplet, args: [1000, 1000] }));
34+
}
35+
36+
@Component({
37+
selector: 'app-box',
38+
standalone: true,
39+
template: `
40+
<ngt-mesh [ref]="boxApi.ref" [receiveShadow]="true" [castShadow]="true">
41+
<ngt-box-geometry *args="[2, 2, 2]" />
42+
<ngt-mesh-standard-material [roughness]="0.5" color="#575757" />
43+
</ngt-mesh>
44+
`,
45+
imports: [NgtArgs],
46+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
47+
})
48+
export class Box {
49+
@Input() position = [0, 0, 0];
50+
51+
boxApi = injectBox(() => ({ mass: 10000, position: this.position as Triplet, args: [2, 2, 2] }));
52+
}
53+
54+
@Component({
55+
standalone: true,
56+
template: `
57+
<ngt-point-light [position]="[-10, -10, 30]" [intensity]="0.25 * Math.PI" />
58+
<ngt-spot-light
59+
[intensity]="0.3 * Math.PI"
60+
[position]="[30, 30, 50]"
61+
[angle]="0.2"
62+
[penumbra]="1"
63+
[castShadow]="true"
64+
/>
65+
<ngtc-physics [gravity]="[0, 0, -25]" [iterations]="10">
66+
<ngtc-debug color="white" [disabled]="true">
67+
<app-plane [position]="[0, 0, -10]" />
68+
<app-plane *ngIf="showPlane()" />
69+
<app-box [position]="[1, 0, 1]" />
70+
<app-box [position]="[2, 1, 5]" />
71+
<app-box [position]="[0, 0, 6]" />
72+
<app-box [position]="[-1, 1, 8]" />
73+
<app-box [position]="[-2, 2, 13]" />
74+
<app-box [position]="[2, -1, 13]" />
75+
<app-box *ngIf="!showPlane()" [position]="[0.5, 1.0, 20]" />
76+
</ngtc-debug>
77+
</ngtc-physics>
78+
`,
79+
imports: [Box, Plane, NgtcPhysics, NgIf, NgtcDebug],
80+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
81+
})
82+
export class CannonScene {
83+
Math = Math;
84+
showPlane = signal(true);
85+
86+
ngOnInit() {
87+
setTimeout(() => {
88+
this.showPlane.set(false);
89+
}, 5000);
90+
}
91+
}
92+
1393
@Component({
1494
standalone: true,
1595
template: `
@@ -48,8 +128,15 @@ export class Scene {
48128
standalone: true,
49129
imports: [NgtCanvas, NgIf],
50130
selector: 'sandbox-root',
51-
template: ` <ngt-canvas [sceneGraph]="Scene" [camera]="{ position: [0, 1, 3] }" /> `,
131+
template: `
132+
<ngt-canvas
133+
[sceneGraph]="Scene"
134+
[camera]="{ position: [0, 0, 15] }"
135+
[shadows]="true"
136+
[gl]="{ useLegacyLights: true }"
137+
/>
138+
`,
52139
})
53140
export class AppComponent {
54-
readonly Scene = Scene;
141+
readonly Scene = CannonScene;
55142
}

Diff for: libs/cannon/debug/src/lib/debug.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Component, CUSTOM_ELEMENTS_SCHEMA, forwardRef, Input, type OnInit } from '@angular/core';
22
import { propsToBody, type BodyProps, type BodyShapeType } from '@pmndrs/cannon-worker-api';
33
import { createInjectionToken, injectBeforeRender, NgtArgs } from 'angular-three';
4-
import { injectNgtcPhysicsApi, provideNgtcPhysicsApi } from 'angular-three-cannon';
4+
import { injectNgtcPhysicsApi } from 'angular-three-cannon';
55
import type { Body, Quaternion as CQuarternion, Vec3, World } from 'cannon-es';
66
import CannonDebugger from 'cannon-es-debugger';
77
import * as THREE from 'three';
@@ -31,7 +31,7 @@ export const [injectNgtcDebugApi, provideNgtcDebugApi] = createInjectionToken((d
3131
<ngt-primitive *args="[scene]" />
3232
<ng-content />
3333
`,
34-
providers: [provideNgtcPhysicsApi()],
34+
providers: [provideNgtcDebugApi()],
3535
imports: [NgtArgs],
3636
schemas: [CUSTOM_ELEMENTS_SCHEMA],
3737
})

Diff for: libs/cannon/services/src/lib/body.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,15 @@ function injectBody<TBodyProps extends BodyProps, TObject extends THREE.Object3D
248248
// register deps
249249
deps();
250250

251+
const currentWorker = worker();
252+
if (!currentWorker) return;
253+
251254
if (!bodyRef.nativeElement) {
252255
bodyRef.nativeElement = new THREE.Object3D() as TObject;
253256
return;
254257
}
255258

256259
const object = bodyRef.nativeElement;
257-
const currentWorker = worker();
258-
259-
if (!currentWorker) return;
260260

261261
const objectCount =
262262
object instanceof THREE.InstancedMesh

Diff for: libs/core/src/lib/renderer/index.ts

+7
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,15 @@ class NgtRenderer implements Renderer2 {
191191
pRS[NgtRendererClassId.type] === 'dom' &&
192192
(newChild instanceof Text || cRS[NgtRendererClassId.type] === 'dom')
193193
) {
194+
this.store.setParent(newChild, parent);
194195
this.store.addChild(parent, newChild);
195196
this.delegate.appendChild(parent, newChild);
197+
if (this.shouldFindGrandparentInstance(pRS, cRS, newChild)) {
198+
// we'll try to get the grandparent instance here so that we can run appendChild with both instances
199+
const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
200+
if (closestGrandparentInstance) this.appendChild(closestGrandparentInstance, newChild);
201+
return;
202+
}
196203
return;
197204
}
198205

Diff for: libs/core/src/lib/renderer/store.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,10 @@ export class NgtRendererStore {
416416
rS[NgtRendererClassId.parent] = null;
417417
for (const renderChild of rS[NgtRendererClassId.children] || []) {
418418
if (renderChild.__ngt_renderer__?.[NgtRendererClassId.type] === 'three' && parent) {
419-
removeThreeChild(parent, renderChild, true);
419+
const closestInstance = this.getClosestParentWithInstance(parent);
420+
if (closestInstance) {
421+
removeThreeChild(closestInstance, renderChild, true);
422+
}
420423
}
421424
this.destroy(renderChild, parent);
422425
}

Diff for: libs/core/src/lib/renderer/utils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ export function removeThreeChild(parent: NgtInstanceNode, child: NgtInstanceNode
124124
});
125125

126126
// remove child from parent
127-
if (untracked(pLS.objects)) pLS.remove(child, 'objects');
128-
if (untracked(pLS.nonObjects)) pLS.remove(child, 'nonObjects');
127+
if (pLS.objects && untracked(pLS.objects)) pLS.remove(child, 'objects');
128+
if (pLS.nonObjects && untracked(pLS.nonObjects)) pLS.remove(child, 'nonObjects');
129129

130130
if (cLS.attach) {
131131
detach(parent, child, cLS.attach);

0 commit comments

Comments
 (0)