From 24a38e47c3e48d47d9e07912e371ea60765cc594 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 9 Sep 2025 12:29:30 +0530 Subject: [PATCH] added gpu validater and frame limiter --- .../models/model/animator/modelAnimator.tsx | 31 +++++++------ app/src/modules/scene/controls/controls.tsx | 2 +- .../modules/scene/helpers/frameLimiter.tsx | 40 +++++++++++++++++ .../modules/scene/helpers/gpuValidater.tsx | 9 ++++ app/src/modules/scene/setup/setup.tsx | 23 +++++++--- .../instances/animator/pillarJibAnimator.tsx | 44 ++++++++++++------- 6 files changed, 110 insertions(+), 39 deletions(-) create mode 100644 app/src/modules/scene/helpers/frameLimiter.tsx create mode 100644 app/src/modules/scene/helpers/gpuValidater.tsx diff --git a/app/src/modules/builder/asset/models/model/animator/modelAnimator.tsx b/app/src/modules/builder/asset/models/model/animator/modelAnimator.tsx index 88947a3..fb27fce 100644 --- a/app/src/modules/builder/asset/models/model/animator/modelAnimator.tsx +++ b/app/src/modules/builder/asset/models/model/animator/modelAnimator.tsx @@ -1,19 +1,17 @@ -import { useEffect, useRef, useCallback, useState } from 'react'; -import * as THREE from 'three'; -import { useFrame } from '@react-three/fiber'; -import { useSceneContext } from '../../../../../scene/sceneContext'; -import useModuleStore from '../../../../../../store/ui/useModuleStore'; -import { usePauseButtonStore, useAnimationPlaySpeed } from '../../../../../../store/ui/usePlayButtonStore'; +import { useEffect, useRef, useCallback, useState } from "react"; +import * as THREE from "three"; +import { useFrame } from "@react-three/fiber"; +import { useSceneContext } from "../../../../../scene/sceneContext"; +import useModuleStore from "../../../../../../store/ui/useModuleStore"; +import { usePauseButtonStore, useAnimationPlaySpeed } from "../../../../../../store/ui/usePlayButtonStore"; interface ModelAnimatorProps { asset: Asset; gltfScene: THREE.Object3D; } -export function ModelAnimator({ - asset, - gltfScene, -}: ModelAnimatorProps) { +export function ModelAnimator({ asset, gltfScene }: ModelAnimatorProps) { + const clockRef = useRef(new THREE.Clock()); const mixerRef = useRef(); const actions = useRef<{ [name: string]: THREE.AnimationAction }>({}); const [previousAnimation, setPreviousAnimation] = useState(null); @@ -43,7 +41,7 @@ export function ModelAnimator({ actions.current[clip.name] = action; }); - const animationNames = gltfScene.animations.map(clip => clip.name); + const animationNames = gltfScene.animations.map((clip) => clip.name); setAnimations(asset.modelUuid, animationNames); return () => { @@ -71,7 +69,7 @@ export function ModelAnimator({ } currentAction.play(); - mixerRef.current.addEventListener('finished', handleAnimationComplete); + mixerRef.current.addEventListener("finished", handleAnimationComplete); setPreviousAnimation(current); } else { Object.values(actions.current).forEach((action) => action.stop()); @@ -79,14 +77,15 @@ export function ModelAnimator({ return () => { if (mixerRef.current) { - mixerRef.current.removeEventListener('finished', handleAnimationComplete); + mixerRef.current.removeEventListener("finished", handleAnimationComplete); } }; }, [asset.animationState?.current, asset.animationState?.isCompleted, asset.animationState?.isPlaying, isPaused, activeModule]); - useFrame((_, delta) => { - if (mixerRef.current) { - mixerRef.current.update(delta * (activeModule === 'simulation' ? speed : 1)); + useFrame(() => { + if (mixerRef.current && !isPaused) { + const delta = clockRef.current.getDelta(); + mixerRef.current.update(delta * (activeModule === "simulation" ? speed : 1)); } }); diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index 0345a03..0703a0d 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -1,5 +1,5 @@ import { CameraControls } from "@react-three/drei"; -import { useRef, useEffect, useState } from "react"; +import { useRef, useEffect } from "react"; import { useThree } from "@react-three/fiber"; import * as THREE from "three"; import * as CONSTANTS from "../../../types/world/worldConstants"; diff --git a/app/src/modules/scene/helpers/frameLimiter.tsx b/app/src/modules/scene/helpers/frameLimiter.tsx new file mode 100644 index 0000000..45d273e --- /dev/null +++ b/app/src/modules/scene/helpers/frameLimiter.tsx @@ -0,0 +1,40 @@ +import { Stats } from "@react-three/drei"; +import { useThree } from "@react-three/fiber"; +import { useLayoutEffect } from "react"; +import { useControls } from "leva"; + +function FPSLimiter() { + const { advance, set, frameloop } = useThree(); + + const { fps } = useControls("Performance", { + fps: { value: 30, min: 1, max: 60, step: 1 }, + }); + + useLayoutEffect(() => { + let then = 0; + let raf: number; + const interval = 1000 / fps; + + function tick(t: number) { + raf = requestAnimationFrame(tick); + const elapsed = t - then; + + if (elapsed > interval) { + advance(t, true); + then = t - (elapsed % interval); + } + } + + set({ frameloop: "never" }); + raf = requestAnimationFrame(tick); + + return () => { + cancelAnimationFrame(raf); + set({ frameloop }); + }; + }, [fps, advance, set, frameloop]); + + return ; +} + +export default FPSLimiter; diff --git a/app/src/modules/scene/helpers/gpuValidater.tsx b/app/src/modules/scene/helpers/gpuValidater.tsx new file mode 100644 index 0000000..d653689 --- /dev/null +++ b/app/src/modules/scene/helpers/gpuValidater.tsx @@ -0,0 +1,9 @@ +import { useDetectGPU } from "@react-three/drei"; + +function GpuValidater() { + const GPUTier = useDetectGPU(); + + return null; +} + +export default GpuValidater; diff --git a/app/src/modules/scene/setup/setup.tsx b/app/src/modules/scene/setup/setup.tsx index dd17eba..d1dce21 100644 --- a/app/src/modules/scene/setup/setup.tsx +++ b/app/src/modules/scene/setup/setup.tsx @@ -1,10 +1,13 @@ -import Sun from '../environment/sky' -import Shadows from '../environment/shadow' +import Sun from "../environment/sky"; +import Shadows from "../environment/shadow"; // import Clouds from '../clouds/clouds'; -import PostProcessing from '../postProcessing/postProcessing' -import StatsHelper from '../helpers/StatsHelper'; -import Controls from '../controls/controls'; -import { AdaptiveDpr, AdaptiveEvents, Environment } from '@react-three/drei' +import PostProcessing from "../postProcessing/postProcessing"; +import StatsHelper from "../helpers/StatsHelper"; +// import AutoRotate from "../tools/autoRotate"; +import Controls from "../controls/controls"; +import FPSLimiter from "../helpers/frameLimiter"; +import GpuValidater from "../helpers/gpuValidater"; +import { AdaptiveDpr, AdaptiveEvents, Environment } from "@react-three/drei"; import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr"; @@ -23,13 +26,19 @@ function Setup() { + {/* */} + + + + + - ) + ); } export default Setup; diff --git a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx index 995ed5d..6c423c8 100644 --- a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx +++ b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useFrame, useThree } from "@react-three/fiber"; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from "../../../../../store/ui/usePlayButtonStore"; @@ -28,6 +28,7 @@ function PillarJibAnimator({ const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); const { speed } = useAnimationPlaySpeed(); + const clockRef = useRef(new THREE.Clock()); const [clampedPoints, setClampedPoints] = useState<[THREE.Vector3, THREE.Vector3]>(); @@ -66,7 +67,11 @@ function PillarJibAnimator({ const humanAction = getActionByUuid(selectedProduct.productUuid, action?.triggers[0].triggeredAsset?.triggeredAction?.actionUuid || ""); if (humanAction) { - const point = getPointByUuid(selectedProduct.productUuid, humanAction.triggers[0].triggeredAsset?.triggeredModel?.modelUuid || "", humanAction.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid || ""); + const point = getPointByUuid( + selectedProduct.productUuid, + humanAction.triggers[0].triggeredAsset?.triggeredModel?.modelUuid || "", + humanAction.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid || "" + ); const eventModel = scene.getObjectByProperty("uuid", humanAction.triggers[0].triggeredAsset?.triggeredModel?.modelUuid); if (point && eventModel) { const pointLocal = new THREE.Vector3(...point.position); @@ -149,8 +154,13 @@ function PillarJibAnimator({ useFrame(() => { if (!isPlaying || isPaused || !points || !clampedPoints || animationPhase === "idle") return; + const delta = clockRef.current.getDelta(); + stepCrane(delta * speed); + }); + + function stepCrane(dt: number) { const model = scene.getObjectByProperty("uuid", crane.modelUuid); - if (!model) return; + if (!model || !clampedPoints) return; const base = model.getObjectByName("base"); const trolley = model.getObjectByName("trolley"); @@ -164,9 +174,13 @@ function PillarJibAnimator({ const hookWorld = new THREE.Vector3(); hook.getWorldPosition(hookWorld); - const hookSpeed = (model.userData.hookSpeed || 0.01) * speed; - const rotationSpeed = (model.userData.rotationSpeed || 0.005) * speed; - const trolleySpeed = (model.userData.trolleySpeed || 0.01) * speed; + const hookSpeed = (model.userData.fieldData.hookSpeed || 0.1) * speed; + const rotationSpeed = (model.userData.fieldData.rotationSpeed || 0.25) * speed; + const trolleySpeed = (model.userData.fieldData.trolleySpeed || 0.1) * speed; + + const hookStep = hookSpeed * dt; + const rotationStep = rotationSpeed * dt; + const trolleyStep = trolleySpeed * dt; const threshold = Math.max(0.01, 0.05 / speed); @@ -179,7 +193,7 @@ function PillarJibAnimator({ const diff = targetY - hookWorld.y; if (Math.abs(diff) > threshold) { - const step = Math.sign(diff) * hookSpeed; + const step = Math.sign(diff) * hookStep; if (Math.abs(step) > Math.abs(diff)) { const localTarget = hook.parent.worldToLocal(clampedPoints[0].clone()); hook.position.y = localTarget.y; @@ -215,7 +229,7 @@ function PillarJibAnimator({ angleDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff)); if (parseFloat(Math.abs(angleDiff).toFixed(2)) > 0.05) { - const step = Math.sign(angleDiff) * rotationSpeed; + const step = Math.sign(angleDiff) * rotationStep; if (Math.abs(step) > Math.abs(angleDiff)) { base.rotation.y += angleDiff; @@ -240,7 +254,7 @@ function PillarJibAnimator({ const diff = localTarget.x - trolley.position.x; if (Math.abs(diff) > threshold) { - const step = Math.sign(diff) * trolleySpeed; + const step = Math.sign(diff) * trolleyStep; if (Math.abs(step) > Math.abs(diff)) { trolley.position.x = localTarget.x; setAnimationPhase("init-final-hook-adjust"); @@ -262,7 +276,7 @@ function PillarJibAnimator({ const diff = targetY - hookWorld.y; if (Math.abs(diff) > threshold) { - const step = Math.sign(diff) * hookSpeed; + const step = Math.sign(diff) * hookStep; if (Math.abs(step) > Math.abs(diff)) { const localTarget = hook.parent.worldToLocal(clampedPoints[0].clone()); hook.position.y = localTarget.y; @@ -287,7 +301,7 @@ function PillarJibAnimator({ const diff = targetY - hookWorld.y; if (Math.abs(diff) > threshold) { - const step = Math.sign(diff) * hookSpeed; + const step = Math.sign(diff) * hookStep; if (Math.abs(step) > Math.abs(diff)) { const localTarget = hook.parent.worldToLocal(targetWorld.clone()); hook.position.y = localTarget.y; @@ -323,7 +337,7 @@ function PillarJibAnimator({ angleDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff)); if (parseFloat(Math.abs(angleDiff).toFixed(2)) > 0.05) { - const step = Math.sign(angleDiff) * rotationSpeed; + const step = Math.sign(angleDiff) * rotationStep; if (Math.abs(step) > Math.abs(angleDiff)) { base.rotation.y += angleDiff; @@ -348,7 +362,7 @@ function PillarJibAnimator({ const diff = localTarget.x - trolley.position.x; if (Math.abs(diff) > threshold) { - const step = Math.sign(diff) * trolleySpeed; + const step = Math.sign(diff) * trolleyStep; if (Math.abs(step) > Math.abs(diff)) { trolley.position.x = localTarget.x; setAnimationPhase("first-final-hook-adjust"); @@ -370,7 +384,7 @@ function PillarJibAnimator({ const diff = targetY - hookWorld.y; if (Math.abs(diff) > threshold) { - const step = Math.sign(diff) * hookSpeed; + const step = Math.sign(diff) * hookStep; if (Math.abs(step) > Math.abs(diff)) { const localTarget = hook.parent.worldToLocal(clampedPoints[1].clone()); hook.position.y = localTarget.y; @@ -398,7 +412,7 @@ function PillarJibAnimator({ break; } } - }); + } return <>; }