Merge branch 'develop' into feat/main-feature

This commit is contained in:
Tom Boullay
2026-04-29 15:01:17 +02:00
committed by GitHub
22 changed files with 797 additions and 34 deletions
+44 -3
View File
@@ -1,4 +1,6 @@
import { useEffect, useMemo, useState, useRef } from "react";
import type { ReactNode } from "react";
import { Component } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useGLTF } from "@react-three/drei";
import * as THREE from "three";
import { useOctreeGraphNode } from "@/hooks/useOctreeGraphNode";
@@ -6,6 +8,42 @@ import { loadMapSceneData } from "@/utils/loadMapSceneData";
import type { OctreeReadyHandler } from "@/types/three";
import type { MapNode } from "@/types/editor";
interface ErrorBoundaryProps {
children: ReactNode;
fallback?: ReactNode;
}
interface ErrorBoundaryState {
hasError: boolean;
}
class ModelErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
static getDerivedStateFromError(_error: Error): ErrorBoundaryState {
return { hasError: true };
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
componentDidCatch(_error: Error): void {
console.warn(`Failed to load model`);
}
render(): ReactNode {
if (this.state.hasError) {
return this.props.fallback ?? null;
}
return this.props.children;
}
}
interface GameMapProps {
onOctreeReady: OctreeReadyHandler;
}
@@ -54,14 +92,17 @@ export function GameMap({ onOctreeReady }: GameMapProps): React.JSX.Element {
<group ref={groupRef}>
{!isLoading &&
mapNodes.map((node, index) => (
<ModelInstance key={index} node={node} />
<ModelErrorBoundary key={index}>
<ModelInstance node={node} />
</ModelErrorBoundary>
))}
</group>
);
}
function ModelInstance({ node }: { node: MapNode }): React.JSX.Element {
function ModelInstance({ node }: { node: MapNode }): React.JSX.Element | null {
const modelPath = `/models/${node.name}/model.gltf`;
const groupRef = useRef<THREE.Group>(null);
const { scene } = useGLTF(modelPath);
const sceneInstance = useMemo(() => scene.clone(true), [scene]);
+9 -1
View File
@@ -1,8 +1,9 @@
import { useRef } from "react";
import { Physics, RigidBody, CuboidCollider } from "@react-three/rapier";
import * as THREE from "three";
import { Physics, RigidBody, CuboidCollider } from "@react-three/rapier";
import { GrabbableObject } from "@/components/three/GrabbableObject";
import { TriggerObject } from "@/components/three/TriggerObject";
import { AnimatedModel } from "@/components/three/AnimatedModel";
import {
TEST_SCENE_FLOOR_COLLIDER_HALF_EXTENTS,
TEST_SCENE_FLOOR_POSITION,
@@ -86,6 +87,13 @@ export function TestScene({
</mesh>
</TriggerObject>
</Physics>
<AnimatedModel
modelPath="/models/elec/model.gltf"
defaultAnimation="Idle"
position={[0, 0, -5]}
scale={1}
/>
</>
);
}