UNPKG

lightswind

Version:

A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.

113 lines (112 loc) 5.42 kB
// @ts-nocheck "use client"; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { Suspense, useRef, useLayoutEffect, useEffect } from "react"; import { Canvas, useFrame, useLoader } from "@react-three/fiber"; import { OrbitControls, useGLTF, useFBX, useProgress, Html, Environment, ContactShadows, Center, } from "@react-three/drei"; import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader"; import * as THREE from "three"; const isTouch = typeof window !== "undefined" && ("ontouchstart" in window || navigator.maxTouchPoints > 0); const deg2rad = (d) => (d * Math.PI) / 180; // --- // Reusable Components // --- const Loader = () => { const { progress } = useProgress(); return (_jsx(Html, { center: true, children: _jsx("div", { className: "text-gray-400 text-lg", children: `${Math.round(progress)} %` }) })); }; // Component for handling GLTF/GLB models const GltfContent = ({ url, onLoaded, }) => { const { scene } = useGLTF(url); useLayoutEffect(() => { if (scene) { scene.traverse((o) => { if (o.isMesh) { o.castShadow = true; o.receiveShadow = true; } }); onLoaded(); } }, [scene, onLoaded]); return _jsx("primitive", { object: scene.clone() }); }; // Component for handling FBX models const FbxContent = ({ url, onLoaded, }) => { const fbx = useFBX(url); useLayoutEffect(() => { if (fbx) { fbx.traverse((o) => { if (o.isMesh) { o.castShadow = true; o.receiveShadow = true; } }); onLoaded(); } }, [fbx, onLoaded]); return _jsx("primitive", { object: fbx.clone() }); }; // Component for handling OBJ models const ObjContent = ({ url, onLoaded, }) => { const obj = useLoader(OBJLoader, url); useLayoutEffect(() => { if (obj) { obj.traverse((o) => { if (o.isMesh) { o.castShadow = true; o.receiveShadow = true; } }); onLoaded(); } }, [obj, onLoaded]); return _jsx("primitive", { object: obj.clone() }); }; const SceneContent = ({ url, autoRotate, autoRotateSpeed, onLoaded }) => { const modelRef = useRef(null); const ext = url.split(".").pop()?.toLowerCase(); useFrame((state, delta) => { if (autoRotate && modelRef.current) { modelRef.current.rotation.y += (autoRotateSpeed || 1) * delta; } }); const onLoadedHandler = () => { onLoaded?.(); }; const ModelComponent = () => { switch (ext) { case "glb": case "gltf": return _jsx(GltfContent, { url: url, onLoaded: onLoadedHandler }); case "fbx": return _jsx(FbxContent, { url: url, onLoaded: onLoadedHandler }); case "obj": return _jsx(ObjContent, { url: url, onLoaded: onLoadedHandler }); default: return null; } }; return (_jsx(Center, { children: _jsx("group", { ref: modelRef, children: _jsx(ModelComponent, {}) }) })); }; // --- // Main Viewer Component // --- export const ModelViewer = ({ url, width = "100%", height = "100%", defaultZoom = 2, minZoomDistance = 0.5, maxZoomDistance = 10, enableManualRotation = true, enableManualZoom = true, ambientIntensity = 0.3, keyLightIntensity = 1, fillLightIntensity = 0.5, rimLightIntensity = 0.8, environmentPreset = "forest", autoRotate = false, autoRotateSpeed = 0.35, onModelLoaded, }) => { // Preload hook calls should also be unconditional. // The 'useGLTF.preload' hook is called here, but if you had other preloaders, // they would need to be handled similarly. // We'll call useGLTF.preload unconditionally, but it's only effective for gltf/glb files. useEffect(() => void useGLTF.preload(url), [url]); return (_jsx("div", { style: { width, height }, className: "relative", children: _jsxs(Canvas, { shadows: true, camera: { fov: 50, position: [0, 0, defaultZoom], near: 0.01, far: 100, }, gl: { toneMapping: THREE.ACESFilmicToneMapping, outputColorSpace: THREE.SRGBColorSpace, }, frameloop: "demand", children: [_jsx(Suspense, { fallback: _jsx(Loader, {}), children: _jsx(SceneContent, { url: url, autoRotate: autoRotate, autoRotateSpeed: deg2rad(autoRotateSpeed), onLoaded: onModelLoaded }) }), environmentPreset !== "none" && (_jsx(Environment, { preset: environmentPreset })), _jsx("ambientLight", { intensity: ambientIntensity }), _jsx("directionalLight", { position: [5, 5, 5], intensity: keyLightIntensity, castShadow: true }), _jsx("directionalLight", { position: [-5, 2, 5], intensity: fillLightIntensity }), _jsx("directionalLight", { position: [0, 4, -5], intensity: rimLightIntensity }), _jsx(ContactShadows, { position: [0, -0.5, 0], opacity: 0.35, scale: 10, blur: 2 }), _jsx(OrbitControls, { makeDefault: true, enablePan: false, enableRotate: enableManualRotation, enableZoom: enableManualZoom, minDistance: minZoomDistance, maxDistance: maxZoomDistance, autoRotate: isTouch ? false : autoRotate, autoRotateSpeed: autoRotateSpeed })] }) })); }; export default ModelViewer;