memoize more components in the scene graph

This commit is contained in:
Brian Beck 2025-11-25 17:36:41 -08:00
parent 52d0a68d9d
commit b50ce94636
14 changed files with 54 additions and 39 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -2,7 +2,7 @@
2:I[9766,[],""]
3:I[8924,[],""]
4:I[1959,[],"ClientPageRoot"]
5:I[4543,["367","static/chunks/b536a0f1-05ee2c75df4a3b9d.js","831","static/chunks/bd904a5c-3aea2adebde6f067.js","664","static/chunks/a3cd4a83-5c5b758da206345b.js","794","static/chunks/f6211eb1-4f3105d2434536dc.js","413","static/chunks/1329d575-16915d95397758f8.js","749","static/chunks/749-d375ce6a5d05b15b.js","974","static/chunks/app/page-042ee75e42392efb.js"],"default"]
5:I[4543,["367","static/chunks/b536a0f1-05ee2c75df4a3b9d.js","831","static/chunks/bd904a5c-3aea2adebde6f067.js","664","static/chunks/a3cd4a83-5c5b758da206345b.js","794","static/chunks/f6211eb1-4f3105d2434536dc.js","413","static/chunks/1329d575-16915d95397758f8.js","749","static/chunks/749-d375ce6a5d05b15b.js","974","static/chunks/app/page-553e7ac98a9aeb82.js"],"default"]
8:I[4431,[],"OutletBoundary"]
a:I[5278,[],"AsyncMetadataOutlet"]
c:I[4431,[],"ViewportBoundary"]
@ -10,7 +10,7 @@ e:I[4431,[],"MetadataBoundary"]
f:"$Sreact.suspense"
11:I[7150,[],""]
:HL["/t2-mapper/_next/static/css/6a2c9c1ac3df5250.css","style"]
0:{"P":null,"b":"NSw9oc4aWEphTyPPlxQW_","p":"/t2-mapper","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/css/6a2c9c1ac3df5250.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","searchParams":{},"params":{},"promises":["$@6","$@7"]}],null,["$","$L8",null,{"children":["$L9",["$","$La",null,{"promise":"$@b"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Lc",null,{"children":"$Ld"}],null],["$","$Le",null,{"children":["$","div",null,{"hidden":true,"children":["$","$f",null,{"fallback":null,"children":"$L10"}]}]}]]}],false]],"m":"$undefined","G":["$11",[]],"s":false,"S":true}
0:{"P":null,"b":"0uouJz5Zn_Au2A2UeJCIU","p":"/t2-mapper","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/t2-mapper/_next/static/css/6a2c9c1ac3df5250.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","searchParams":{},"params":{},"promises":["$@6","$@7"]}],null,["$","$L8",null,{"children":["$L9",["$","$La",null,{"promise":"$@b"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Lc",null,{"children":"$Ld"}],null],["$","$Le",null,{"children":["$","div",null,{"hidden":true,"children":["$","$f",null,{"fallback":null,"children":"$L10"}]}]}]]}],false]],"m":"$undefined","G":["$11",[]],"s":false,"S":true}
6:{}
7:"$0:f:0:1:2:children:1:props:children:0:props:params"
d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]

View file

@ -1,4 +1,4 @@
import { useEffect, useRef } from "react";
import { memo, useEffect, useRef } from "react";
import { useThree, useFrame } from "@react-three/fiber";
import { PositionalAudio, Vector3 } from "three";
import { ConsoleObject, getPosition, getProperty } from "../mission";
@ -32,7 +32,11 @@ function getCachedAudioBuffer(
}
}
export function AudioEmitter({ object }: { object: ConsoleObject }) {
export const AudioEmitter = memo(function AudioEmitter({
object,
}: {
object: ConsoleObject;
}) {
const { debugMode } = useSettings();
const fileName = getProperty(object, "fileName")?.value ?? "";
const volume = parseFloat(getProperty(object, "volume")?.value ?? "1");
@ -217,4 +221,4 @@ export function AudioEmitter({ object }: { object: ConsoleObject }) {
</FloatingLabel>
</mesh>
) : null;
}
});

View file

@ -1,4 +1,4 @@
import { ReactNode, useEffect, useRef, useState } from "react";
import { memo, ReactNode, useEffect, useRef, useState } from "react";
import { Object3D } from "three";
import { useDistanceFromCamera } from "./useDistanceFromCamera";
import { useFrame } from "@react-three/fiber";
@ -6,7 +6,7 @@ import { Html } from "@react-three/drei";
const DEFAULT_POSITION = [0, 0, 0] as [x: number, y: number, z: number];
export function FloatingLabel({
export const FloatingLabel = memo(function FloatingLabel({
children,
color = "white",
position = DEFAULT_POSITION,
@ -55,4 +55,4 @@ export function FloatingLabel({
) : null}
</group>
);
}
});

View file

@ -1,4 +1,4 @@
import { Suspense, useMemo } from "react";
import { memo, Suspense, useMemo } from "react";
import { useGLTF, useTexture } from "@react-three/drei";
import { BASE_URL, shapeTextureToUrl, shapeToUrl } from "../loaders";
import { filterGeometryByVertexGroups, getHullBoneIndices } from "../meshUtils";
@ -30,7 +30,7 @@ export function ShapeTexture({
shapeName?: string;
}) {
const url = shapeTextureToUrl(material.name, FALLBACK_URL);
const isOrganic = shapeName && /borg|xorg|porg/i.test(shapeName);
const isOrganic = shapeName && /borg|xorg|porg|dorg/i.test(shapeName);
const texture = useTexture(url, (texture) => {
if (!isOrganic) {
@ -69,7 +69,7 @@ export function ShapePlaceholder({ color }: { color: string }) {
export type StaticShapeType = "StaticShape" | "TSStatic" | "Item" | "Turret";
export function ShapeModel() {
export const ShapeModel = memo(function ShapeModel() {
const { shapeName } = useShapeInfo();
const { debugMode } = useSettings();
const { nodes } = useStaticShape(shapeName);
@ -136,4 +136,4 @@ export function ShapeModel() {
{debugMode ? <FloatingLabel>{shapeName}</FloatingLabel> : null}
</group>
);
}
});

View file

@ -80,19 +80,21 @@ function InteriorPlaceholder() {
);
}
export const InteriorInstance = memo(
({ object }: { object: ConsoleObject }) => {
const interiorFile = getProperty(object, "interiorFile").value;
const position = useMemo(() => getPosition(object), [object]);
const scale = useMemo(() => getScale(object), [object]);
const q = useMemo(() => getRotation(object), [object]);
export const InteriorInstance = memo(function InteriorInstance({
object,
}: {
object: ConsoleObject;
}) {
const interiorFile = getProperty(object, "interiorFile").value;
const position = useMemo(() => getPosition(object), [object]);
const scale = useMemo(() => getScale(object), [object]);
const q = useMemo(() => getRotation(object), [object]);
return (
<group position={position} quaternion={q} scale={scale}>
<Suspense fallback={<InteriorPlaceholder />}>
<InteriorModel interiorFile={interiorFile} />
</Suspense>
</group>
);
}
);
return (
<group position={position} quaternion={q} scale={scale}>
<Suspense fallback={<InteriorPlaceholder />}>
<InteriorModel interiorFile={interiorFile} />
</Suspense>
</group>
);
});

View file

@ -1,6 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { loadMission } from "../loaders";
import { renderObject } from "./renderObject";
import { memo } from "react";
function useMission(name: string) {
return useQuery({
@ -9,7 +10,7 @@ function useMission(name: string) {
});
}
export function Mission({ name }: { name: string }) {
export const Mission = memo(function Mission({ name }: { name: string }) {
const { data: mission } = useMission(name);
if (!mission) {
@ -17,4 +18,4 @@ export function Mission({ name }: { name: string }) {
}
return mission.objects.map((object, i) => renderObject(object, i));
}
});

View file

@ -1,4 +1,4 @@
import { Suspense, useCallback, useMemo } from "react";
import { memo, Suspense, useCallback, useMemo } from "react";
import { useQuery } from "@tanstack/react-query";
import {
DataTexture,
@ -201,7 +201,11 @@ function TerrainMaterial({
);
}
export function TerrainBlock({ object }: { object: ConsoleObject }) {
export const TerrainBlock = memo(function TerrainBlock({
object,
}: {
object: ConsoleObject;
}) {
const terrainFile: string = getProperty(object, "terrainFile").value;
const squareSize = useMemo(() => {
@ -265,4 +269,4 @@ export function TerrainBlock({ object }: { object: ConsoleObject }) {
</mesh>
</group>
);
}
});

View file

@ -1,4 +1,4 @@
import { Suspense, useEffect, useMemo } from "react";
import { memo, Suspense, useEffect, useMemo } from "react";
import { useTexture } from "@react-three/drei";
import { BoxGeometry, DoubleSide } from "three";
import { textureToUrl } from "../loaders";
@ -32,7 +32,11 @@ export function WaterMaterial({
);
}
export function WaterBlock({ object }: { object: ConsoleObject }) {
export const WaterBlock = memo(function WaterBlock({
object,
}: {
object: ConsoleObject;
}) {
const position = useMemo(() => getPosition(object), [object]);
const q = useMemo(() => getRotation(object), [object]);
const [scaleX, scaleY, scaleZ] = useMemo(() => getScale(object), [object]);
@ -99,4 +103,4 @@ export function WaterBlock({ object }: { object: ConsoleObject }) {
<meshStandardMaterial attach="material-5" transparent opacity={0} />
</mesh>
);
}
});