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
JavaScript
// @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;