|
1 | | -'use client' |
| 1 | +import ClientPage from '../../configurator/ClientPage' |
2 | 2 |
|
3 | | -import { Suspense, useEffect, useMemo, useRef } from 'react' |
4 | | -import { Canvas, useFrame } from '@react-three/fiber' |
5 | | -import { Environment, OrbitControls, useGLTF } from '@react-three/drei' |
6 | | -import * as THREE from 'three' |
7 | | -import type { GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js' |
| 3 | +export const dynamic = 'force-dynamic' |
8 | 4 |
|
9 | | -const DEFAULT_CAMERA_POSITION: [number, number, number] = [2.5, 2.5, 2.5] |
10 | | - |
11 | | -export type MaterialOverride = { |
12 | | - mesh: string |
13 | | - material: number |
14 | | -} |
15 | | - |
16 | | -async function load(gltf: GLTF & { scene: THREE.Group }, overrides: MaterialOverride[]) { |
17 | | - const parser = gltf?.parser |
18 | | - |
19 | | - if (!gltf?.scene || !parser || overrides.length === 0) { |
20 | | - return |
21 | | - } |
22 | | - |
23 | | - await Promise.all( |
24 | | - overrides.map(async (entry) => { |
25 | | - if (typeof entry.material !== 'number') { |
26 | | - return |
27 | | - } |
28 | | - |
29 | | - const mesh = gltf.scene.getObjectByName(entry.mesh) as THREE.Mesh | null |
30 | | - if (!mesh) { |
31 | | - return |
32 | | - } |
33 | | - |
34 | | - const material = (await parser.getDependency('material', entry.material)) as THREE.Material |
35 | | - mesh.material = material |
36 | | - }) |
37 | | - ) |
38 | | -} |
39 | | - |
40 | | -function KitchenModel({ sku }: { sku: string }) { |
41 | | - const src = useMemo(() => `/models/${sku}.glb`, [sku]) |
42 | | - const gltf = useGLTF(src) as unknown as GLTF & { scene: THREE.Group } |
43 | | - const mixerRef = useRef<THREE.AnimationMixer | null>(null) |
44 | | - |
45 | | - useEffect(() => { |
46 | | - if (!gltf || !gltf.animations || gltf.animations.length === 0) { |
47 | | - return |
48 | | - } |
49 | | - |
50 | | - mixerRef.current = new THREE.AnimationMixer(gltf.scene) |
51 | | - gltf.animations.forEach((clip) => { |
52 | | - mixerRef.current?.clipAction(clip).play() |
53 | | - }) |
54 | | - |
55 | | - return () => { |
56 | | - mixerRef.current?.stopAllAction() |
57 | | - mixerRef.current = null |
58 | | - } |
59 | | - }, [gltf]) |
60 | | - |
61 | | - useEffect(() => { |
62 | | - if (!gltf) { |
63 | | - return |
64 | | - } |
65 | | - |
66 | | - const overrides: MaterialOverride[] = [] |
67 | | - |
68 | | - load(gltf, overrides).catch((error) => { |
69 | | - if (process.env.NODE_ENV !== 'production') { |
70 | | - console.error('Failed to apply material overrides', error) |
71 | | - } |
72 | | - }) |
73 | | - }, [gltf]) |
74 | | - |
75 | | - useFrame((state, delta) => { |
76 | | - mixerRef.current?.update(delta) |
77 | | - }) |
78 | | - |
79 | | - if (!gltf?.scene) { |
80 | | - return null |
81 | | - } |
82 | | - |
83 | | - return <primitive object={gltf.scene} /> |
| 5 | +export default function KitchenConfiguratorPage() { |
| 6 | + return <ClientPage /> |
84 | 7 | } |
85 | | - |
86 | | -function KitchenScene({ sku }: { sku: string }) { |
87 | | - return ( |
88 | | - <Canvas camera={{ position: DEFAULT_CAMERA_POSITION, fov: 50 }}> |
89 | | - <Suspense fallback={null}> |
90 | | - <ambientLight intensity={0.6} /> |
91 | | - <directionalLight position={[5, 5, 5]} intensity={0.9} /> |
92 | | - <KitchenModel sku={sku} /> |
93 | | - <Environment preset="apartment" /> |
94 | | - </Suspense> |
95 | | - <OrbitControls makeDefault enableDamping dampingFactor={0.1} /> |
96 | | - </Canvas> |
97 | | - ) |
98 | | -} |
99 | | - |
100 | | -export default function KitchenConfiguratorPage({ params }: { params: { sku: string } }) { |
101 | | - const { sku } = params |
102 | | - |
103 | | - return ( |
104 | | - <div style={{ width: '100%', minHeight: '480px', height: '100%' }}> |
105 | | - <KitchenScene sku={sku} /> |
106 | | - </div> |
107 | | - ) |
108 | | -} |
109 | | - |
110 | | -useGLTF.preload('/models/BaseCabinet600.glb') |
0 commit comments