Skip to content

Commit 225c75a

Browse files
committed
Enhance SmartCamera and SplatViewer components with camera change rendering
- Introduced a new hook, useRenderOnCameraChange, to optimize rendering when the camera changes in the SmartCamera component. - Updated SmartCamera to utilize the new hook and set app.renderNextFrame to true for immediate rendering. - Modified SplatViewer to also set app.renderNextFrame to true, ensuring consistent rendering behavior across components. - Adjusted Application component in SplatViewer to disable autoRender for better control over rendering flow.
1 parent 6b27c3c commit 225c75a

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-1
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useApp, useFrame } from "@playcanvas/react/hooks";
2+
import { Entity } from "playcanvas";
3+
import { useRef } from "react";
4+
5+
const nearlyEquals = (a: Float32Array, b: Float32Array, epsilon = 1e-4): boolean => {
6+
for (let i = 0; i < a.length; i++) {
7+
if (Math.abs(a[i] - b[i]) >= epsilon) return false;
8+
}
9+
return true;
10+
};
11+
12+
export const useRenderOnCameraChange = (entity: Entity | null) => {
13+
const app = useApp();
14+
const prevWorld = useRef<Float32Array>(new Float32Array(16));
15+
const prevProj = useRef<Float32Array>(new Float32Array(16));
16+
17+
useFrame(() => {
18+
if (!entity) return;
19+
const world = entity.getWorldTransform().data;
20+
const proj = entity.camera?.projectionMatrix?.data;
21+
22+
if (!proj) return;
23+
24+
let changed = false;
25+
26+
if (!app.autoRender && !app.renderNextFrame) {
27+
changed = !nearlyEquals(world, prevWorld.current) || !nearlyEquals(proj, prevProj.current);
28+
29+
if (changed) {
30+
console.log("changed");
31+
app.renderNextFrame = true;
32+
}
33+
}
34+
35+
if (app.renderNextFrame) {
36+
if (changed || app.autoRender) {
37+
prevWorld.current.set(world);
38+
prevProj.current.set(proj);
39+
}
40+
}
41+
});
42+
};

packages/blocks/src/splat-viewer/smart-camera.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import style from "./utils/style";
1414

1515
// @ts-expect-error There is no type definition for the camera-controls script
1616
import { CameraControls } from "playcanvas/scripts/esm/camera-controls.mjs";
17+
import { useRenderOnCameraChange } from "./hooks/use-render-on-camera-change";
1718

1819
type CameraControlsProps = {
1920
/* The focus point of the camera */
@@ -53,11 +54,14 @@ export function SmartCamera({
5354
fov?: number;
5455
animationTrack?: AnimationTrack;
5556
}) {
57+
5658
const entityRef = useRef<pc.Entity>(null);
59+
useRenderOnCameraChange(entityRef.current);
5760
const { subscribe, isPlaying } = useTimeline();
5861
const { mode } = useAssetViewer();
5962
// const [mode] = useState<"interactive" | "transition" | "animation">(isPlaying ? "animation" : "interactive");
6063
const app = useApp();
64+
app.renderNextFrame = true;
6165

6266
const [pose, setPose] = useState<PoseType>({
6367
position: [2, 1, 2],

packages/blocks/src/splat-viewer/splat-viewer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ function SplatComponent({
8383
const { isInteracting } = useAssetViewer();
8484
const { isPlaying } = useTimeline();
8585
const app = useApp();
86+
app.renderNextFrame = true;
8687

8788
// unload the asset when the component is unmounted
8889
useEffect(() => {
@@ -160,7 +161,7 @@ export function SplatViewer( {
160161
setMode={setCameraMode}
161162
>
162163
<Suspense fallback={<PosterComponent poster={poster} />} >
163-
<Application fillMode={FILLMODE_NONE} resolutionMode={RESOLUTION_AUTO} autoRender={true}>
164+
<Application fillMode={FILLMODE_NONE} resolutionMode={RESOLUTION_AUTO} autoRender={false}>
164165
<SplatComponent src={src} {...props} />
165166
</Application>
166167
<TooltipProvider>

0 commit comments

Comments
 (0)