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,[],""] 2:I[9766,[],""]
3:I[8924,[],""] 3:I[8924,[],""]
4:I[1959,[],"ClientPageRoot"] 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"] 8:I[4431,[],"OutletBoundary"]
a:I[5278,[],"AsyncMetadataOutlet"] a:I[5278,[],"AsyncMetadataOutlet"]
c:I[4431,[],"ViewportBoundary"] c:I[4431,[],"ViewportBoundary"]
@ -10,7 +10,7 @@ e:I[4431,[],"MetadataBoundary"]
f:"$Sreact.suspense" f:"$Sreact.suspense"
11:I[7150,[],""] 11:I[7150,[],""]
:HL["/t2-mapper/_next/static/css/6a2c9c1ac3df5250.css","style"] :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:{} 6:{}
7:"$0:f:0:1:2:children:1:props:children:0:props:params" 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"}]] 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 { useThree, useFrame } from "@react-three/fiber";
import { PositionalAudio, Vector3 } from "three"; import { PositionalAudio, Vector3 } from "three";
import { ConsoleObject, getPosition, getProperty } from "../mission"; 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 { debugMode } = useSettings();
const fileName = getProperty(object, "fileName")?.value ?? ""; const fileName = getProperty(object, "fileName")?.value ?? "";
const volume = parseFloat(getProperty(object, "volume")?.value ?? "1"); const volume = parseFloat(getProperty(object, "volume")?.value ?? "1");
@ -217,4 +221,4 @@ export function AudioEmitter({ object }: { object: ConsoleObject }) {
</FloatingLabel> </FloatingLabel>
</mesh> </mesh>
) : null; ) : 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 { Object3D } from "three";
import { useDistanceFromCamera } from "./useDistanceFromCamera"; import { useDistanceFromCamera } from "./useDistanceFromCamera";
import { useFrame } from "@react-three/fiber"; 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]; const DEFAULT_POSITION = [0, 0, 0] as [x: number, y: number, z: number];
export function FloatingLabel({ export const FloatingLabel = memo(function FloatingLabel({
children, children,
color = "white", color = "white",
position = DEFAULT_POSITION, position = DEFAULT_POSITION,
@ -55,4 +55,4 @@ export function FloatingLabel({
) : null} ) : null}
</group> </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 { useGLTF, useTexture } from "@react-three/drei";
import { BASE_URL, shapeTextureToUrl, shapeToUrl } from "../loaders"; import { BASE_URL, shapeTextureToUrl, shapeToUrl } from "../loaders";
import { filterGeometryByVertexGroups, getHullBoneIndices } from "../meshUtils"; import { filterGeometryByVertexGroups, getHullBoneIndices } from "../meshUtils";
@ -30,7 +30,7 @@ export function ShapeTexture({
shapeName?: string; shapeName?: string;
}) { }) {
const url = shapeTextureToUrl(material.name, FALLBACK_URL); 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) => { const texture = useTexture(url, (texture) => {
if (!isOrganic) { if (!isOrganic) {
@ -69,7 +69,7 @@ export function ShapePlaceholder({ color }: { color: string }) {
export type StaticShapeType = "StaticShape" | "TSStatic" | "Item" | "Turret"; export type StaticShapeType = "StaticShape" | "TSStatic" | "Item" | "Turret";
export function ShapeModel() { export const ShapeModel = memo(function ShapeModel() {
const { shapeName } = useShapeInfo(); const { shapeName } = useShapeInfo();
const { debugMode } = useSettings(); const { debugMode } = useSettings();
const { nodes } = useStaticShape(shapeName); const { nodes } = useStaticShape(shapeName);
@ -136,4 +136,4 @@ export function ShapeModel() {
{debugMode ? <FloatingLabel>{shapeName}</FloatingLabel> : null} {debugMode ? <FloatingLabel>{shapeName}</FloatingLabel> : null}
</group> </group>
); );
} });

View file

@ -80,8 +80,11 @@ function InteriorPlaceholder() {
); );
} }
export const InteriorInstance = memo( export const InteriorInstance = memo(function InteriorInstance({
({ object }: { object: ConsoleObject }) => { object,
}: {
object: ConsoleObject;
}) {
const interiorFile = getProperty(object, "interiorFile").value; const interiorFile = getProperty(object, "interiorFile").value;
const position = useMemo(() => getPosition(object), [object]); const position = useMemo(() => getPosition(object), [object]);
const scale = useMemo(() => getScale(object), [object]); const scale = useMemo(() => getScale(object), [object]);
@ -94,5 +97,4 @@ export const InteriorInstance = memo(
</Suspense> </Suspense>
</group> </group>
); );
} });
);

View file

@ -1,6 +1,7 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { loadMission } from "../loaders"; import { loadMission } from "../loaders";
import { renderObject } from "./renderObject"; import { renderObject } from "./renderObject";
import { memo } from "react";
function useMission(name: string) { function useMission(name: string) {
return useQuery({ 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); const { data: mission } = useMission(name);
if (!mission) { if (!mission) {
@ -17,4 +18,4 @@ export function Mission({ name }: { name: string }) {
} }
return mission.objects.map((object, i) => renderObject(object, i)); 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 { useQuery } from "@tanstack/react-query";
import { import {
DataTexture, 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 terrainFile: string = getProperty(object, "terrainFile").value;
const squareSize = useMemo(() => { const squareSize = useMemo(() => {
@ -265,4 +269,4 @@ export function TerrainBlock({ object }: { object: ConsoleObject }) {
</mesh> </mesh>
</group> </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 { useTexture } from "@react-three/drei";
import { BoxGeometry, DoubleSide } from "three"; import { BoxGeometry, DoubleSide } from "three";
import { textureToUrl } from "../loaders"; 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 position = useMemo(() => getPosition(object), [object]);
const q = useMemo(() => getRotation(object), [object]); const q = useMemo(() => getRotation(object), [object]);
const [scaleX, scaleY, scaleZ] = useMemo(() => getScale(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} /> <meshStandardMaterial attach="material-5" transparent opacity={0} />
</mesh> </mesh>
); );
} });