added gpu validater and frame limiter
This commit is contained in:
@@ -1,19 +1,17 @@
|
|||||||
import { useEffect, useRef, useCallback, useState } from 'react';
|
import { useEffect, useRef, useCallback, useState } from "react";
|
||||||
import * as THREE from 'three';
|
import * as THREE from "three";
|
||||||
import { useFrame } from '@react-three/fiber';
|
import { useFrame } from "@react-three/fiber";
|
||||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
import { useSceneContext } from "../../../../../scene/sceneContext";
|
||||||
import useModuleStore from '../../../../../../store/ui/useModuleStore';
|
import useModuleStore from "../../../../../../store/ui/useModuleStore";
|
||||||
import { usePauseButtonStore, useAnimationPlaySpeed } from '../../../../../../store/ui/usePlayButtonStore';
|
import { usePauseButtonStore, useAnimationPlaySpeed } from "../../../../../../store/ui/usePlayButtonStore";
|
||||||
|
|
||||||
interface ModelAnimatorProps {
|
interface ModelAnimatorProps {
|
||||||
asset: Asset;
|
asset: Asset;
|
||||||
gltfScene: THREE.Object3D;
|
gltfScene: THREE.Object3D;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ModelAnimator({
|
export function ModelAnimator({ asset, gltfScene }: ModelAnimatorProps) {
|
||||||
asset,
|
const clockRef = useRef(new THREE.Clock());
|
||||||
gltfScene,
|
|
||||||
}: ModelAnimatorProps) {
|
|
||||||
const mixerRef = useRef<THREE.AnimationMixer>();
|
const mixerRef = useRef<THREE.AnimationMixer>();
|
||||||
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
||||||
const [previousAnimation, setPreviousAnimation] = useState<string | null>(null);
|
const [previousAnimation, setPreviousAnimation] = useState<string | null>(null);
|
||||||
@@ -43,7 +41,7 @@ export function ModelAnimator({
|
|||||||
actions.current[clip.name] = action;
|
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);
|
setAnimations(asset.modelUuid, animationNames);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -71,7 +69,7 @@ export function ModelAnimator({
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentAction.play();
|
currentAction.play();
|
||||||
mixerRef.current.addEventListener('finished', handleAnimationComplete);
|
mixerRef.current.addEventListener("finished", handleAnimationComplete);
|
||||||
setPreviousAnimation(current);
|
setPreviousAnimation(current);
|
||||||
} else {
|
} else {
|
||||||
Object.values(actions.current).forEach((action) => action.stop());
|
Object.values(actions.current).forEach((action) => action.stop());
|
||||||
@@ -79,14 +77,15 @@ export function ModelAnimator({
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (mixerRef.current) {
|
if (mixerRef.current) {
|
||||||
mixerRef.current.removeEventListener('finished', handleAnimationComplete);
|
mixerRef.current.removeEventListener("finished", handleAnimationComplete);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [asset.animationState?.current, asset.animationState?.isCompleted, asset.animationState?.isPlaying, isPaused, activeModule]);
|
}, [asset.animationState?.current, asset.animationState?.isCompleted, asset.animationState?.isPlaying, isPaused, activeModule]);
|
||||||
|
|
||||||
useFrame((_, delta) => {
|
useFrame(() => {
|
||||||
if (mixerRef.current) {
|
if (mixerRef.current && !isPaused) {
|
||||||
mixerRef.current.update(delta * (activeModule === 'simulation' ? speed : 1));
|
const delta = clockRef.current.getDelta();
|
||||||
|
mixerRef.current.update(delta * (activeModule === "simulation" ? speed : 1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CameraControls } from "@react-three/drei";
|
import { CameraControls } from "@react-three/drei";
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef, useEffect } from "react";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||||
|
|||||||
40
app/src/modules/scene/helpers/frameLimiter.tsx
Normal file
40
app/src/modules/scene/helpers/frameLimiter.tsx
Normal file
@@ -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 <Stats />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FPSLimiter;
|
||||||
9
app/src/modules/scene/helpers/gpuValidater.tsx
Normal file
9
app/src/modules/scene/helpers/gpuValidater.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { useDetectGPU } from "@react-three/drei";
|
||||||
|
|
||||||
|
function GpuValidater() {
|
||||||
|
const GPUTier = useDetectGPU();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GpuValidater;
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import Sun from '../environment/sky'
|
import Sun from "../environment/sky";
|
||||||
import Shadows from '../environment/shadow'
|
import Shadows from "../environment/shadow";
|
||||||
// import Clouds from '../clouds/clouds';
|
// import Clouds from '../clouds/clouds';
|
||||||
import PostProcessing from '../postProcessing/postProcessing'
|
import PostProcessing from "../postProcessing/postProcessing";
|
||||||
import StatsHelper from '../helpers/StatsHelper';
|
import StatsHelper from "../helpers/StatsHelper";
|
||||||
import Controls from '../controls/controls';
|
// import AutoRotate from "../tools/autoRotate";
|
||||||
import { AdaptiveDpr, AdaptiveEvents, Environment } from '@react-three/drei'
|
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";
|
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
|
||||||
|
|
||||||
@@ -23,13 +26,19 @@ function Setup() {
|
|||||||
|
|
||||||
<Environment files={background} environmentIntensity={1.5} />
|
<Environment files={background} environmentIntensity={1.5} />
|
||||||
|
|
||||||
|
{/* <AutoRotate /> */}
|
||||||
|
|
||||||
|
<FPSLimiter />
|
||||||
|
|
||||||
|
<GpuValidater />
|
||||||
|
|
||||||
<StatsHelper />
|
<StatsHelper />
|
||||||
|
|
||||||
<AdaptiveEvents />
|
<AdaptiveEvents />
|
||||||
|
|
||||||
<AdaptiveDpr pixelated />
|
<AdaptiveDpr pixelated />
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Setup;
|
export default Setup;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from "../../../../../store/ui/usePlayButtonStore";
|
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from "../../../../../store/ui/usePlayButtonStore";
|
||||||
@@ -28,6 +28,7 @@ function PillarJibAnimator({
|
|||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { isReset } = useResetButtonStore();
|
const { isReset } = useResetButtonStore();
|
||||||
const { speed } = useAnimationPlaySpeed();
|
const { speed } = useAnimationPlaySpeed();
|
||||||
|
const clockRef = useRef(new THREE.Clock());
|
||||||
|
|
||||||
const [clampedPoints, setClampedPoints] = useState<[THREE.Vector3, THREE.Vector3]>();
|
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 || "");
|
const humanAction = getActionByUuid(selectedProduct.productUuid, action?.triggers[0].triggeredAsset?.triggeredAction?.actionUuid || "");
|
||||||
|
|
||||||
if (humanAction) {
|
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);
|
const eventModel = scene.getObjectByProperty("uuid", humanAction.triggers[0].triggeredAsset?.triggeredModel?.modelUuid);
|
||||||
if (point && eventModel) {
|
if (point && eventModel) {
|
||||||
const pointLocal = new THREE.Vector3(...point.position);
|
const pointLocal = new THREE.Vector3(...point.position);
|
||||||
@@ -149,8 +154,13 @@ function PillarJibAnimator({
|
|||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
if (!isPlaying || isPaused || !points || !clampedPoints || animationPhase === "idle") return;
|
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);
|
const model = scene.getObjectByProperty("uuid", crane.modelUuid);
|
||||||
if (!model) return;
|
if (!model || !clampedPoints) return;
|
||||||
|
|
||||||
const base = model.getObjectByName("base");
|
const base = model.getObjectByName("base");
|
||||||
const trolley = model.getObjectByName("trolley");
|
const trolley = model.getObjectByName("trolley");
|
||||||
@@ -164,9 +174,13 @@ function PillarJibAnimator({
|
|||||||
const hookWorld = new THREE.Vector3();
|
const hookWorld = new THREE.Vector3();
|
||||||
hook.getWorldPosition(hookWorld);
|
hook.getWorldPosition(hookWorld);
|
||||||
|
|
||||||
const hookSpeed = (model.userData.hookSpeed || 0.01) * speed;
|
const hookSpeed = (model.userData.fieldData.hookSpeed || 0.1) * speed;
|
||||||
const rotationSpeed = (model.userData.rotationSpeed || 0.005) * speed;
|
const rotationSpeed = (model.userData.fieldData.rotationSpeed || 0.25) * speed;
|
||||||
const trolleySpeed = (model.userData.trolleySpeed || 0.01) * 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);
|
const threshold = Math.max(0.01, 0.05 / speed);
|
||||||
|
|
||||||
@@ -179,7 +193,7 @@ function PillarJibAnimator({
|
|||||||
const diff = targetY - hookWorld.y;
|
const diff = targetY - hookWorld.y;
|
||||||
|
|
||||||
if (Math.abs(diff) > threshold) {
|
if (Math.abs(diff) > threshold) {
|
||||||
const step = Math.sign(diff) * hookSpeed;
|
const step = Math.sign(diff) * hookStep;
|
||||||
if (Math.abs(step) > Math.abs(diff)) {
|
if (Math.abs(step) > Math.abs(diff)) {
|
||||||
const localTarget = hook.parent.worldToLocal(clampedPoints[0].clone());
|
const localTarget = hook.parent.worldToLocal(clampedPoints[0].clone());
|
||||||
hook.position.y = localTarget.y;
|
hook.position.y = localTarget.y;
|
||||||
@@ -215,7 +229,7 @@ function PillarJibAnimator({
|
|||||||
angleDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff));
|
angleDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff));
|
||||||
|
|
||||||
if (parseFloat(Math.abs(angleDiff).toFixed(2)) > 0.05) {
|
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)) {
|
if (Math.abs(step) > Math.abs(angleDiff)) {
|
||||||
base.rotation.y += angleDiff;
|
base.rotation.y += angleDiff;
|
||||||
@@ -240,7 +254,7 @@ function PillarJibAnimator({
|
|||||||
const diff = localTarget.x - trolley.position.x;
|
const diff = localTarget.x - trolley.position.x;
|
||||||
|
|
||||||
if (Math.abs(diff) > threshold) {
|
if (Math.abs(diff) > threshold) {
|
||||||
const step = Math.sign(diff) * trolleySpeed;
|
const step = Math.sign(diff) * trolleyStep;
|
||||||
if (Math.abs(step) > Math.abs(diff)) {
|
if (Math.abs(step) > Math.abs(diff)) {
|
||||||
trolley.position.x = localTarget.x;
|
trolley.position.x = localTarget.x;
|
||||||
setAnimationPhase("init-final-hook-adjust");
|
setAnimationPhase("init-final-hook-adjust");
|
||||||
@@ -262,7 +276,7 @@ function PillarJibAnimator({
|
|||||||
const diff = targetY - hookWorld.y;
|
const diff = targetY - hookWorld.y;
|
||||||
|
|
||||||
if (Math.abs(diff) > threshold) {
|
if (Math.abs(diff) > threshold) {
|
||||||
const step = Math.sign(diff) * hookSpeed;
|
const step = Math.sign(diff) * hookStep;
|
||||||
if (Math.abs(step) > Math.abs(diff)) {
|
if (Math.abs(step) > Math.abs(diff)) {
|
||||||
const localTarget = hook.parent.worldToLocal(clampedPoints[0].clone());
|
const localTarget = hook.parent.worldToLocal(clampedPoints[0].clone());
|
||||||
hook.position.y = localTarget.y;
|
hook.position.y = localTarget.y;
|
||||||
@@ -287,7 +301,7 @@ function PillarJibAnimator({
|
|||||||
const diff = targetY - hookWorld.y;
|
const diff = targetY - hookWorld.y;
|
||||||
|
|
||||||
if (Math.abs(diff) > threshold) {
|
if (Math.abs(diff) > threshold) {
|
||||||
const step = Math.sign(diff) * hookSpeed;
|
const step = Math.sign(diff) * hookStep;
|
||||||
if (Math.abs(step) > Math.abs(diff)) {
|
if (Math.abs(step) > Math.abs(diff)) {
|
||||||
const localTarget = hook.parent.worldToLocal(targetWorld.clone());
|
const localTarget = hook.parent.worldToLocal(targetWorld.clone());
|
||||||
hook.position.y = localTarget.y;
|
hook.position.y = localTarget.y;
|
||||||
@@ -323,7 +337,7 @@ function PillarJibAnimator({
|
|||||||
angleDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff));
|
angleDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff));
|
||||||
|
|
||||||
if (parseFloat(Math.abs(angleDiff).toFixed(2)) > 0.05) {
|
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)) {
|
if (Math.abs(step) > Math.abs(angleDiff)) {
|
||||||
base.rotation.y += angleDiff;
|
base.rotation.y += angleDiff;
|
||||||
@@ -348,7 +362,7 @@ function PillarJibAnimator({
|
|||||||
const diff = localTarget.x - trolley.position.x;
|
const diff = localTarget.x - trolley.position.x;
|
||||||
|
|
||||||
if (Math.abs(diff) > threshold) {
|
if (Math.abs(diff) > threshold) {
|
||||||
const step = Math.sign(diff) * trolleySpeed;
|
const step = Math.sign(diff) * trolleyStep;
|
||||||
if (Math.abs(step) > Math.abs(diff)) {
|
if (Math.abs(step) > Math.abs(diff)) {
|
||||||
trolley.position.x = localTarget.x;
|
trolley.position.x = localTarget.x;
|
||||||
setAnimationPhase("first-final-hook-adjust");
|
setAnimationPhase("first-final-hook-adjust");
|
||||||
@@ -370,7 +384,7 @@ function PillarJibAnimator({
|
|||||||
const diff = targetY - hookWorld.y;
|
const diff = targetY - hookWorld.y;
|
||||||
|
|
||||||
if (Math.abs(diff) > threshold) {
|
if (Math.abs(diff) > threshold) {
|
||||||
const step = Math.sign(diff) * hookSpeed;
|
const step = Math.sign(diff) * hookStep;
|
||||||
if (Math.abs(step) > Math.abs(diff)) {
|
if (Math.abs(step) > Math.abs(diff)) {
|
||||||
const localTarget = hook.parent.worldToLocal(clampedPoints[1].clone());
|
const localTarget = hook.parent.worldToLocal(clampedPoints[1].clone());
|
||||||
hook.position.y = localTarget.y;
|
hook.position.y = localTarget.y;
|
||||||
@@ -398,7 +412,7 @@ function PillarJibAnimator({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user