Skip to content

Commit 676aa16

Browse files
committed
fix(cannon): unwrap afterNextRender from injectBody and injectConstraint
BREAKING CHANGE: This is considered a breaking change because of change in timing. The CIFs no longer handle timing internally via `afterNextRender`. By default, `NgtcPhysics` already handles timing with `afterNextRender` on the component level (i.e: `NgtcPhysics` component) and these cannon CIFs are supposed to be used as content children of `NgtcPhysics`. Additional control is up to the consumers' discretion.
1 parent c3b68e2 commit 676aa16

File tree

3 files changed

+92
-125
lines changed

3 files changed

+92
-125
lines changed

libs/cannon/body/src/lib/body.ts

Lines changed: 60 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
Injector,
44
Signal,
55
WritableSignal,
6-
afterNextRender,
76
computed,
7+
effect,
88
inject,
99
isSignal,
1010
signal,
@@ -15,7 +15,6 @@ import { resolveRef } from 'angular-three';
1515
import { NgtcPhysics } from 'angular-three-cannon';
1616
import { NgtcDebug } from 'angular-three-cannon/debug';
1717
import { assertInjector } from 'ngxtension/assert-injector';
18-
import { injectAutoEffect } from 'ngxtension/auto-effect';
1918
import { DynamicDrawUsage, InstancedMesh, Object3D } from 'three';
2019
import { NgtcArgFn, NgtcBodyPropsMap, NgtcBodyPublicApi, NgtcGetByIndex } from './types';
2120
import { defaultTransformArgs, makeBodyApi, prepare, setupCollision } from './utils';
@@ -46,7 +45,6 @@ function injectBody<TShape extends BodyShapeType, TObject extends Object3D>(
4645
throw new Error(`[NGT Cannon] injectBody was called outside of <ngtc-physics>`);
4746
}
4847

49-
const autoEffect = injectAutoEffect();
5048
const debug = inject(NgtcDebug, { optional: true });
5149

5250
const transform = transformArgs ?? defaultTransformArgs[type];
@@ -61,73 +59,71 @@ function injectBody<TShape extends BodyShapeType, TObject extends Object3D>(
6159
return makeBodyApi(_body, worker(), rest);
6260
});
6361

64-
afterNextRender(() => {
65-
autoEffect(() => {
66-
const currentWorker = physics.api.worker();
67-
if (!currentWorker) return;
62+
effect((onCleanup) => {
63+
const currentWorker = physics.api.worker();
64+
if (!currentWorker) return;
6865

69-
const object = body();
66+
const object = body();
7067

71-
if (!isSignal(ref) && !object) {
72-
untracked(() => {
73-
(bodyRef as WritableSignal<TObject | undefined>).set(resolveRef(ref));
74-
});
75-
return;
76-
}
68+
if (!isSignal(ref) && !object) {
69+
untracked(() => {
70+
(bodyRef as WritableSignal<TObject | undefined>).set(resolveRef(ref));
71+
});
72+
return;
73+
}
7774

78-
if (!object) return;
75+
if (!object) return;
7976

80-
const [uuid, props] = (() => {
81-
let uuids: string[] = [];
82-
let temp: Object3D;
83-
if (object instanceof InstancedMesh) {
84-
object.instanceMatrix.setUsage(DynamicDrawUsage);
85-
uuids = new Array(object.count).fill(0).map((_, i) => `${object.uuid}/${i}`);
86-
temp = new Object3D();
87-
} else {
88-
uuids = [object.uuid];
89-
}
90-
return [
91-
uuids,
92-
uuids.map((id, index) => {
93-
const props = getPropFn(index);
94-
if (temp) {
95-
prepare(temp, props);
96-
(object as unknown as InstancedMesh).setMatrixAt(index, temp.matrix);
97-
(object as unknown as InstancedMesh).instanceMatrix.needsUpdate = true;
98-
} else {
99-
prepare(object, props);
100-
}
101-
physics.api.refs[id] = object;
102-
debug?.add(id, props, type);
103-
setupCollision(physics.api.events, props, id);
104-
// @ts-expect-error - if args is undefined, there's default
105-
return { ...props, args: transform(props.args) };
106-
}),
107-
];
108-
})();
109-
// Register on mount, unregister on unmount
110-
currentWorker.addBodies({
111-
props: props.map(({ onCollide, onCollideBegin, onCollideEnd, ...serializableProps }) => {
112-
return {
113-
onCollide: Boolean(onCollide),
114-
onCollideBegin: Boolean(onCollideBegin),
115-
onCollideEnd: Boolean(onCollideEnd),
116-
...serializableProps,
117-
};
77+
const [uuid, props] = (() => {
78+
let uuids: string[] = [];
79+
let temp: Object3D;
80+
if (object instanceof InstancedMesh) {
81+
object.instanceMatrix.setUsage(DynamicDrawUsage);
82+
uuids = new Array(object.count).fill(0).map((_, i) => `${object.uuid}/${i}`);
83+
temp = new Object3D();
84+
} else {
85+
uuids = [object.uuid];
86+
}
87+
return [
88+
uuids,
89+
uuids.map((id, index) => {
90+
const props = getPropFn(index);
91+
if (temp) {
92+
prepare(temp, props);
93+
(object as unknown as InstancedMesh).setMatrixAt(index, temp.matrix);
94+
(object as unknown as InstancedMesh).instanceMatrix.needsUpdate = true;
95+
} else {
96+
prepare(object, props);
97+
}
98+
physics.api.refs[id] = object;
99+
debug?.add(id, props, type);
100+
setupCollision(physics.api.events, props, id);
101+
// @ts-expect-error - if args is undefined, there's default
102+
return { ...props, args: transform(props.args) };
118103
}),
119-
type,
120-
uuid,
121-
});
104+
];
105+
})();
106+
// Register on mount, unregister on unmount
107+
currentWorker.addBodies({
108+
props: props.map(({ onCollide, onCollideBegin, onCollideEnd, ...serializableProps }) => {
109+
return {
110+
onCollide: Boolean(onCollide),
111+
onCollideBegin: Boolean(onCollideBegin),
112+
onCollideEnd: Boolean(onCollideEnd),
113+
...serializableProps,
114+
};
115+
}),
116+
type,
117+
uuid,
118+
});
122119

123-
return () => {
124-
uuid.forEach((id) => {
125-
delete physics.api.refs[id];
126-
debug?.remove(id);
127-
delete physics.api.events[id];
128-
});
129-
currentWorker.removeBodies({ uuid });
130-
};
120+
onCleanup(() => {
121+
uuid.forEach((id) => {
122+
delete physics.api.refs[id];
123+
debug?.remove(id);
124+
delete physics.api.events[id];
125+
});
126+
currentWorker.removeBodies({ uuid });
131127
});
132128
});
133129

libs/cannon/constraint/src/lib/constraint.ts

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,4 @@
1-
import {
2-
ElementRef,
3-
Injector,
4-
Signal,
5-
afterNextRender,
6-
computed,
7-
inject,
8-
isSignal,
9-
signal,
10-
untracked,
11-
} from '@angular/core';
1+
import { ElementRef, Injector, Signal, computed, effect, inject, isSignal, signal, untracked } from '@angular/core';
122
import {
133
ConeTwistConstraintOpts,
144
ConstraintTypes,
@@ -20,7 +10,6 @@ import {
2010
import { makeId, resolveRef } from 'angular-three';
2111
import { NgtcPhysics } from 'angular-three-cannon';
2212
import { assertInjector } from 'ngxtension/assert-injector';
23-
import { injectAutoEffect } from 'ngxtension/auto-effect';
2413
import { Object3D } from 'three';
2514

2615
export interface NgtcConstraintApi {
@@ -83,7 +72,6 @@ function injectConstraint<
8372
throw new Error(`[NGT Cannon] injectConstraint was called outside of <ngtc-physics>`);
8473
}
8574

86-
const autoEffect = injectAutoEffect();
8775
const worker = physics.api.worker;
8876

8977
const uuid = makeId();
@@ -114,30 +102,25 @@ function injectConstraint<
114102
});
115103

116104
let alreadyDisabled = false;
117-
afterNextRender(() => {
118-
autoEffect(() => {
119-
const currentWorker = worker();
120-
if (!currentWorker) return;
121-
122-
const [a, b] = [bodyAValue(), bodyBValue()];
105+
effect((onCleanup) => {
106+
const currentWorker = worker();
107+
if (!currentWorker) return;
123108

124-
if (a && b) {
125-
currentWorker.addConstraint({
126-
props: [a.uuid, b.uuid, options],
127-
type,
128-
uuid,
129-
});
109+
const [a, b] = [bodyAValue(), bodyBValue()];
110+
if (!a || !b) return;
130111

131-
if (disableOnStart && !alreadyDisabled) {
132-
alreadyDisabled = true;
133-
untracked(api)?.disable();
134-
}
112+
currentWorker.addConstraint({
113+
props: [a.uuid, b.uuid, options],
114+
type,
115+
uuid,
116+
});
135117

136-
return () => currentWorker.removeConstraint({ uuid });
137-
}
118+
if (disableOnStart && !alreadyDisabled) {
119+
alreadyDisabled = true;
120+
untracked(api)?.disable();
121+
}
138122

139-
return;
140-
});
123+
onCleanup(() => currentWorker.removeConstraint({ uuid }));
141124
});
142125

143126
return api;

libs/cannon/src/lib/physics.ts

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import {
33
Component,
44
DestroyRef,
55
EmbeddedViewRef,
6-
Injector,
7-
NgZone,
86
Signal,
97
afterNextRender,
108
computed,
@@ -127,8 +125,6 @@ const defaultOptions: NgtcPhysicsInputs = {
127125
changeDetection: ChangeDetectionStrategy.OnPush,
128126
})
129127
export class NgtcPhysics {
130-
private zone = inject(NgZone);
131-
private injector = inject(Injector);
132128
private autoEffect = injectAutoEffect();
133129
private store = injectStore();
134130

@@ -151,16 +147,22 @@ export class NgtcPhysics {
151147

152148
constructor() {
153149
afterNextRender(() => {
154-
this.zone.runOutsideAngular(() => {
155-
this.worker.set(new CannonWorkerAPI(this.options()));
156-
this.connectWorker();
157-
this.updateWorkerState('axisIndex');
158-
this.updateWorkerState('broadphase');
159-
this.updateWorkerState('gravity');
160-
this.updateWorkerState('iterations');
161-
this.updateWorkerState('tolerance');
162-
this.beforeRender();
163-
});
150+
this.worker.set(new CannonWorkerAPI(this.options()));
151+
this.connectWorker();
152+
this.updateWorkerState('axisIndex');
153+
this.updateWorkerState('broadphase');
154+
this.updateWorkerState('gravity');
155+
this.updateWorkerState('iterations');
156+
this.updateWorkerState('tolerance');
157+
});
158+
159+
let timeSinceLastCalled = 0;
160+
injectBeforeRender(({ delta }) => {
161+
const [{ isPaused, maxSubSteps, stepSize }, worker] = [this.options(), this.worker()];
162+
if (isPaused || !worker || stepSize == null) return;
163+
timeSinceLastCalled += delta;
164+
worker.step({ maxSubSteps, stepSize, timeSinceLastCalled });
165+
timeSinceLastCalled = 0;
164166
});
165167

166168
inject(DestroyRef).onDestroy(() => {
@@ -199,20 +201,6 @@ export class NgtcPhysics {
199201
});
200202
}
201203

202-
private beforeRender() {
203-
let timeSinceLastCalled = 0;
204-
injectBeforeRender(
205-
({ delta }) => {
206-
const [{ isPaused, maxSubSteps, stepSize }, worker] = [this.options(), this.worker()];
207-
if (isPaused || !worker || stepSize == null) return;
208-
timeSinceLastCalled += delta;
209-
worker.step({ maxSubSteps, stepSize, timeSinceLastCalled });
210-
timeSinceLastCalled = 0;
211-
},
212-
{ injector: this.injector },
213-
);
214-
}
215-
216204
private collideHandler({ body, contact: { bi, bj, ...contactRest }, target, ...rest }: WorkerCollideEvent['data']) {
217205
const { events, refs } = this.api;
218206
const cb = events[target]?.collide;

0 commit comments

Comments
 (0)