perf: AntiquityViewer render theo nhu cau + gate ngoai man hinh + pixelRatio 1.5 + meshopt
This commit is contained in:
@@ -5,6 +5,8 @@ import * as THREE from "three";
|
|||||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
||||||
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
|
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
|
||||||
|
import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.js";
|
||||||
|
import { createRenderGate } from "@/app/lib/render-gate";
|
||||||
import { Rotate3d, Pause, Play, Maximize2 } from "lucide-react";
|
import { Rotate3d, Pause, Play, Maximize2 } from "lucide-react";
|
||||||
|
|
||||||
type Status = "loading" | "ready" | "error";
|
type Status = "loading" | "ready" | "error";
|
||||||
@@ -35,7 +37,7 @@ export function AntiquityViewer({ src }: { src: string }) {
|
|||||||
let disposed = false;
|
let disposed = false;
|
||||||
|
|
||||||
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, powerPreference: "high-performance" });
|
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, powerPreference: "high-performance" });
|
||||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
|
||||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
||||||
renderer.toneMappingExposure = 1.08;
|
renderer.toneMappingExposure = 1.08;
|
||||||
renderer.domElement.style.cssText = "width:100%;height:100%;display:block;cursor:grab;";
|
renderer.domElement.style.cssText = "width:100%;height:100%;display:block;cursor:grab;";
|
||||||
@@ -70,6 +72,18 @@ export function AntiquityViewer({ src }: { src: string }) {
|
|||||||
});
|
});
|
||||||
controlsRef.current = controls;
|
controlsRef.current = controls;
|
||||||
|
|
||||||
|
let needsRender = true;
|
||||||
|
let active = true;
|
||||||
|
const requestRender = () => {
|
||||||
|
needsRender = true;
|
||||||
|
};
|
||||||
|
controls.addEventListener("change", requestRender);
|
||||||
|
|
||||||
|
const stopGate = createRenderGate(mount, (isActive) => {
|
||||||
|
active = isActive;
|
||||||
|
if (isActive) requestRender();
|
||||||
|
});
|
||||||
|
|
||||||
const resize = () => {
|
const resize = () => {
|
||||||
const w = mount.clientWidth;
|
const w = mount.clientWidth;
|
||||||
const h = mount.clientHeight;
|
const h = mount.clientHeight;
|
||||||
@@ -77,12 +91,14 @@ export function AntiquityViewer({ src }: { src: string }) {
|
|||||||
renderer.setSize(w, h, false);
|
renderer.setSize(w, h, false);
|
||||||
camera.aspect = w / h;
|
camera.aspect = w / h;
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
|
needsRender = true;
|
||||||
};
|
};
|
||||||
const observer = new ResizeObserver(resize);
|
const observer = new ResizeObserver(resize);
|
||||||
observer.observe(mount);
|
observer.observe(mount);
|
||||||
resize();
|
resize();
|
||||||
|
|
||||||
const loader = new GLTFLoader();
|
const loader = new GLTFLoader();
|
||||||
|
loader.setMeshoptDecoder(MeshoptDecoder);
|
||||||
loader.load(
|
loader.load(
|
||||||
src,
|
src,
|
||||||
(gltf) => {
|
(gltf) => {
|
||||||
@@ -108,6 +124,7 @@ export function AntiquityViewer({ src }: { src: string }) {
|
|||||||
|
|
||||||
homeRef.current = { pos: camera.position.clone(), target: controls.target.clone() };
|
homeRef.current = { pos: camera.position.clone(), target: controls.target.clone() };
|
||||||
setStatus("ready");
|
setStatus("ready");
|
||||||
|
requestRender();
|
||||||
},
|
},
|
||||||
(event) => {
|
(event) => {
|
||||||
if (event.total) setProgress(Math.round((event.loaded / event.total) * 100));
|
if (event.total) setProgress(Math.round((event.loaded / event.total) * 100));
|
||||||
@@ -118,9 +135,14 @@ export function AntiquityViewer({ src }: { src: string }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
controls.update();
|
|
||||||
renderer.render(scene, camera);
|
|
||||||
raf = requestAnimationFrame(tick);
|
raf = requestAnimationFrame(tick);
|
||||||
|
if (!active) return;
|
||||||
|
// controls.update() trả về true khi camera còn chuyển động (damping/autoRotate)
|
||||||
|
const moving = controls.update();
|
||||||
|
if (needsRender || moving || controls.autoRotate) {
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
needsRender = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
@@ -128,6 +150,8 @@ export function AntiquityViewer({ src }: { src: string }) {
|
|||||||
disposed = true;
|
disposed = true;
|
||||||
cancelAnimationFrame(raf);
|
cancelAnimationFrame(raf);
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
|
stopGate();
|
||||||
|
controls.removeEventListener("change", requestRender);
|
||||||
controls.dispose();
|
controls.dispose();
|
||||||
scene.traverse((object) => {
|
scene.traverse((object) => {
|
||||||
const mesh = object as THREE.Mesh;
|
const mesh = object as THREE.Mesh;
|
||||||
|
|||||||
Reference in New Issue
Block a user