feat add model loading diagnostics

This commit is contained in:
Tom Boullay
2026-05-02 00:14:47 +02:00
parent 4d7d2efdcc
commit 1d64582383
13 changed files with 218 additions and 29 deletions
@@ -1,5 +1,4 @@
import { useEffect, useRef } from "react";
import { useGLTF } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import gsap from "gsap";
import * as THREE from "three";
@@ -16,6 +15,7 @@ import {
REPAIR_CASE_ROTATION_RESET_SPEED,
} from "@/data/gameplay/repairCaseConfig";
import { useClonedObject } from "@/hooks/three/useClonedObject";
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
import type { ModelTransformProps } from "@/types/three/three";
import { toVector3Scale } from "@/utils/three/scale";
@@ -42,7 +42,12 @@ export function RepairCaseModel({
scale = 1,
}: RepairCaseModelProps): React.JSX.Element {
const camera = useThree((state) => state.camera);
const { scene } = useGLTF(modelPath);
const { scene } = useLoggedGLTF(modelPath, {
scope: "RepairCaseModel",
position,
rotation,
scale,
});
const model = useClonedObject(scene);
const groupRef = useRef<THREE.Group>(null);
const lidRef = useRef<THREE.Object3D | null>(null);
@@ -8,6 +8,7 @@ import {
} from "@/data/gameplay/repairCaseConfig";
import { AudioManager } from "@/managers/AudioManager";
import type { Vector3Tuple } from "@/types/three/three";
import { logModelLoadError } from "@/utils/three/modelLoadLogger";
interface RepairCaseErrorBoundaryProps {
children: ReactNode;
@@ -31,7 +32,15 @@ class RepairCaseErrorBoundary extends Component<
}
componentDidCatch(error: Error): void {
console.warn("Failed to load repair case model", error);
logModelLoadError(
{
modelPath: REPAIR_CASE_MODEL_PATH,
scope: "RepairCaseObject",
position: [0, -0.45, 0],
scale: 1.5,
},
error,
);
}
render(): ReactNode {
@@ -1,8 +1,8 @@
import { useState } from "react";
import { useGLTF } from "@react-three/drei";
import { RigidBody } from "@react-three/rapier";
import { InteractableObject } from "@/components/three/interaction/InteractableObject";
import { useClonedObject } from "@/hooks/three/useClonedObject";
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
import {
TRIGGER_DEFAULT_COLLIDERS,
TRIGGER_DEFAULT_LABEL,
@@ -38,7 +38,10 @@ function SpawnedModelInstance({
path: string;
position: Vector3Tuple;
}): React.JSX.Element {
const { scene } = useGLTF(path);
const { scene } = useLoggedGLTF(path, {
scope: "TriggerObject.SpawnedModel",
position,
});
const model = useClonedObject(scene);
return <primitive object={model} position={position} />;
@@ -1,11 +1,12 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useGLTF, useAnimations } from "@react-three/drei";
import { useAnimations } from "@react-three/drei";
import type { AnimationAction } from "three";
import * as THREE from "three";
import {
AnimatedModelContext,
type AnimatedModelContextValue,
} from "@/components/three/models/useAnimatedModel";
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
import type { Vector3Tuple } from "@/types/three/three";
export interface AnimatedModelConfig {
@@ -40,7 +41,12 @@ export function AnimatedModel({
children,
}: AnimatedModelProps): React.JSX.Element {
const groupRef = useRef<THREE.Group>(null);
const { scene, animations } = useGLTF(modelPath);
const { scene, animations } = useLoggedGLTF(modelPath, {
scope: "AnimatedModel",
position,
rotation,
scale,
});
const model = useMemo(() => scene.clone(true), [scene]);
const { actions, names, mixer } = useAnimations(animations, groupRef);
@@ -1,14 +1,16 @@
import type { ReactNode } from "react";
import { Component, useEffect, useMemo } from "react";
import { useFrame } from "@react-three/fiber";
import { useGLTF } from "@react-three/drei";
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
import { useClonedObject } from "@/hooks/three/useClonedObject";
import { ExplodedModel } from "@/utils/three/ExplodedModel";
import type { ModelTransformProps, Vector3Tuple } from "@/types/three/three";
import { logModelLoadError } from "@/utils/three/modelLoadLogger";
import { toVector3Scale } from "@/utils/three/scale";
interface ModelErrorBoundaryProps {
children: ReactNode;
modelPath: string;
position?: Vector3Tuple | undefined;
}
@@ -30,7 +32,14 @@ class ModelErrorBoundary extends Component<
}
componentDidCatch(error: Error): void {
console.warn("Failed to load explodable model", error);
logModelLoadError(
{
modelPath: this.props.modelPath,
scope: "ExplodableModel",
position: this.props.position,
},
error,
);
}
render(): ReactNode {
@@ -52,7 +61,11 @@ export function ExplodableModel(
props: ExplodableModelInnerProps,
): React.JSX.Element {
return (
<ModelErrorBoundary key={props.modelPath} position={props.position}>
<ModelErrorBoundary
key={props.modelPath}
modelPath={props.modelPath}
position={props.position}
>
<ExplodableModelInner {...props} />
</ModelErrorBoundary>
);
@@ -66,7 +79,12 @@ function ExplodableModelInner({
scale = 1,
splitDistance = 1.2,
}: ExplodableModelInnerProps): React.JSX.Element {
const { scene } = useGLTF(modelPath);
const { scene } = useLoggedGLTF(modelPath, {
scope: "ExplodableModel",
position,
rotation,
scale,
});
const model = useClonedObject(scene);
const explodedModel = useMemo(
() => new ExplodedModel(model, { distance: splitDistance }),
+7 -2
View File
@@ -1,5 +1,5 @@
import { useMemo } from "react";
import { useGLTF } from "@react-three/drei";
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
import type { Vector3Tuple } from "@/types/three/three";
export interface SimpleModelConfig {
@@ -24,7 +24,12 @@ export function SimpleModel({
receiveShadow = true,
children,
}: SimpleModelProps): React.JSX.Element {
const { scene } = useGLTF(modelPath);
const { scene } = useLoggedGLTF(modelPath, {
scope: "SimpleModel",
position,
rotation,
scale,
});
const model = useMemo(() => scene.clone(true), [scene]);
const parsedScale =
+5 -1
View File
@@ -3,6 +3,7 @@ import { useGLTF } from "@react-three/drei";
import { useRef } from "react";
import * as THREE from "three";
import { useClonedObject } from "@/hooks/three/useClonedObject";
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
interface SkyModelProps {
modelPath: string;
@@ -13,7 +14,10 @@ const SKY_MODEL_SCALE = 1;
export function SkyModel({ modelPath }: SkyModelProps): React.JSX.Element {
const camera = useThree((state) => state.camera);
const groupRef = useRef<THREE.Group>(null);
const { scene } = useGLTF(modelPath);
const { scene } = useLoggedGLTF(modelPath, {
scope: "SkyModel",
scale: SKY_MODEL_SCALE,
});
const model = useClonedObject(scene);
useFrame(() => {