Files
stalendeuren/components/configurator/door-3d-enhanced.tsx
Ubuntu 3d788740cb feat: Latest production version with interior scene and glass
Includes room interior with floor, walls, glass you can see through,
and all uncommitted production changes that were running live.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:50:31 +00:00

297 lines
8.7 KiB
TypeScript

"use client";
import { useRef, useMemo, Suspense } from "react";
import { useConfiguratorStore, type GlassColor, type Finish } from "@/lib/store";
import { RoundedBox, useTexture } from "@react-three/drei";
import * as THREE from "three";
import {
Beugelgreep,
Hoekgreep,
Maangreep,
Ovaalgreep,
Klink,
UGreep,
} from "./handles-3d";
import {
createStandardGlass,
createRoundedCornerGlass,
createInvertedUGlass,
createNormalUGlass,
} from "@/lib/glass-patterns";
import {
generateDoorAssembly,
mmToMeters,
getDividerPositions,
PROFILE_CORNER_RADIUS,
type PhysicalPart,
} from "@/lib/door-models";
// ============================================
// FRAME COLOR MAPPING
// ============================================
const FRAME_COLORS: Record<Finish, string> = {
zwart: "#1a1a1a",
brons: "#8B6F47",
grijs: "#525252",
goud: "#B8860B",
beige: "#C8B88A",
ral: "#4A6741",
};
const FRAME_TEXTURE_PATHS: Record<Finish, string> = {
zwart: "/textures/proinn/proinn-metaalkleur-zwart.jpg",
brons: "/textures/proinn/proinn-metaalkleur-brons.jpg",
grijs: "/textures/proinn/proinn-metaalkleur-antraciet.jpg",
goud: "/textures/proinn/proinn-metaalkleur-goud.jpg",
beige: "/textures/proinn/proinn-metaalkleur-beige.jpg",
ral: "/textures/proinn/proinn-metaalkleur-ral-keuze.jpg",
};
// ============================================
// GLASS COLOR MAPPING
// ============================================
interface GlassColorProps {
color: string;
transmission: number;
roughness: number;
}
const GLASS_COLOR_MAP: Record<GlassColor, GlassColorProps> = {
helder: { color: "#eff6ff", transmission: 0.98, roughness: 0.05 },
grijs: { color: "#3a3a3a", transmission: 0.85, roughness: 0.1 },
brons: { color: "#8B6F47", transmission: 0.85, roughness: 0.1 },
"mat-blank": { color: "#e8e8e8", transmission: 0.7, roughness: 0.3 },
"mat-brons": { color: "#A0845C", transmission: 0.6, roughness: 0.35 },
"mat-zwart": { color: "#1a1a1a", transmission: 0.5, roughness: 0.4 },
};
// ============================================
// PHOTOREALISTIC MATERIALS
// ============================================
function SteelMaterialTextured({ color, finish }: { color: string; finish: Finish }) {
try {
const texturePath = FRAME_TEXTURE_PATHS[finish];
const texture = useTexture(texturePath);
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(0.5, 3);
texture.colorSpace = THREE.SRGBColorSpace;
return (
<meshStandardMaterial
map={texture}
color={color}
roughness={0.7}
metalness={0.6}
envMapIntensity={1.5}
/>
);
} catch {
return <SteelMaterialFallback color={color} />;
}
}
function SteelMaterialFallback({ color }: { color: string }) {
return (
<meshStandardMaterial
color={color}
roughness={0.7}
metalness={0.6}
envMapIntensity={1.5}
/>
);
}
function GlassMaterial({ glassColor }: { glassColor: GlassColor }) {
const props = GLASS_COLOR_MAP[glassColor];
return (
<meshPhysicalMaterial
transmission={props.transmission}
roughness={props.roughness}
thickness={0.007}
ior={1.5}
color={props.color}
transparent
opacity={0.98}
envMapIntensity={1.0}
/>
);
}
// ============================================
// PHYSICAL PART RENDERER
// ============================================
function PhysicalPartComponent({
part,
frameColor,
finish,
glassColor,
}: {
part: PhysicalPart;
frameColor: string;
finish: Finish;
glassColor: GlassColor;
}) {
const x = mmToMeters(part.x);
const y = mmToMeters(part.y);
const z = mmToMeters(part.z);
const width = mmToMeters(part.width);
const height = mmToMeters(part.height);
const depth = mmToMeters(part.depth);
if (part.isGlass) {
return (
<mesh position={[x, y, z]} castShadow receiveShadow>
<boxGeometry args={[width, height, depth]} />
<GlassMaterial glassColor={glassColor} />
</mesh>
);
}
const cornerRadius = mmToMeters(PROFILE_CORNER_RADIUS);
return (
<RoundedBox
args={[width, height, depth]}
radius={cornerRadius}
smoothness={4}
position={[x, y, z]}
castShadow
receiveShadow
>
<Suspense fallback={<SteelMaterialFallback color={frameColor} />}>
<SteelMaterialTextured color={frameColor} finish={finish} />
</Suspense>
</RoundedBox>
);
}
// ============================================
// MAIN DOOR COMPONENT
// ============================================
export function Door3DEnhanced() {
const { doorType, gridType, finish, handle, glassPattern, glassColor, doorLeafWidth, height } =
useConfiguratorStore();
const doorRef = useRef<THREE.Group>(null);
const frameColor = FRAME_COLORS[finish] || "#1a1a1a";
const doorAssembly = useMemo(
() => generateDoorAssembly(doorType, gridType, doorLeafWidth, height),
[doorType, gridType, doorLeafWidth, height]
);
const doorWidth = mmToMeters(doorLeafWidth);
const doorHeight = mmToMeters(height);
const stileWidth = mmToMeters(40);
const railDepth = mmToMeters(40);
const dividerPositions = getDividerPositions(gridType, height);
return (
<group ref={doorRef} position={[0, doorHeight / 2, 0]}>
{/* RENDER ALL PHYSICAL PARTS */}
{doorAssembly.parts.map((part, index) => (
<PhysicalPartComponent
key={`${part.type}-${index}`}
part={part}
frameColor={frameColor}
finish={finish}
glassColor={glassColor}
/>
))}
{/* GLASS PANELS WITH PATTERNS */}
{glassPattern !== "standard" && (
<group position={[0, 0, 0.005]}>
{glassPattern === "dt9-rounded" && (
<mesh castShadow receiveShadow>
<extrudeGeometry
args={[
createRoundedCornerGlass(
doorWidth - stileWidth * 2,
doorHeight - stileWidth * 2,
0.12
),
{ depth: 0.01, bevelEnabled: false },
]}
/>
<GlassMaterial glassColor={glassColor} />
</mesh>
)}
{glassPattern === "dt10-ushape" && dividerPositions.length > 0 && (
<>
<mesh
position={[0, (doorHeight / 4 + dividerPositions[0]) / 2, 0]}
castShadow
receiveShadow
>
<extrudeGeometry
args={[
createInvertedUGlass(
doorWidth - stileWidth * 2,
Math.abs(doorHeight / 2 - stileWidth - dividerPositions[0])
),
{ depth: 0.01, bevelEnabled: false },
]}
/>
<GlassMaterial glassColor={glassColor} />
</mesh>
<mesh
position={[
0,
(-doorHeight / 4 + dividerPositions[dividerPositions.length - 1]) / 2,
0,
]}
castShadow
receiveShadow
>
<extrudeGeometry
args={[
createNormalUGlass(
doorWidth - stileWidth * 2,
Math.abs(
-doorHeight / 2 +
stileWidth -
dividerPositions[dividerPositions.length - 1]
)
),
{ depth: 0.01, bevelEnabled: false },
]}
/>
<GlassMaterial glassColor={glassColor} />
</mesh>
</>
)}
</group>
)}
{/* PROFESSIONAL 3D HANDLES */}
{handle === "beugelgreep" && (
<Beugelgreep finish={finish} doorWidth={doorWidth} doorHeight={doorHeight} railDepth={railDepth} stileWidth={stileWidth} />
)}
{handle === "hoekgreep" && (
<Hoekgreep finish={finish} doorWidth={doorWidth} doorHeight={doorHeight} railDepth={railDepth} stileWidth={stileWidth} />
)}
{handle === "maangreep" && (
<Maangreep finish={finish} doorWidth={doorWidth} doorHeight={doorHeight} railDepth={railDepth} stileWidth={stileWidth} />
)}
{handle === "ovaalgreep" && (
<Ovaalgreep finish={finish} doorWidth={doorWidth} doorHeight={doorHeight} railDepth={railDepth} stileWidth={stileWidth} />
)}
{handle === "klink" && (
<Klink finish={finish} doorWidth={doorWidth} doorHeight={doorHeight} railDepth={railDepth} stileWidth={stileWidth} />
)}
{handle === "u-greep" && (
<UGreep finish={finish} doorWidth={doorWidth} doorHeight={doorHeight} railDepth={railDepth} stileWidth={stileWidth} />
)}
</group>
);
}