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 { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.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";
|
||||
|
||||
type Status = "loading" | "ready" | "error";
|
||||
@@ -35,7 +37,7 @@ export function AntiquityViewer({ src }: { src: string }) {
|
||||
let disposed = false;
|
||||
|
||||
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.toneMappingExposure = 1.08;
|
||||
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;
|
||||
|
||||
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 w = mount.clientWidth;
|
||||
const h = mount.clientHeight;
|
||||
@@ -77,12 +91,14 @@ export function AntiquityViewer({ src }: { src: string }) {
|
||||
renderer.setSize(w, h, false);
|
||||
camera.aspect = w / h;
|
||||
camera.updateProjectionMatrix();
|
||||
needsRender = true;
|
||||
};
|
||||
const observer = new ResizeObserver(resize);
|
||||
observer.observe(mount);
|
||||
resize();
|
||||
|
||||
const loader = new GLTFLoader();
|
||||
loader.setMeshoptDecoder(MeshoptDecoder);
|
||||
loader.load(
|
||||
src,
|
||||
(gltf) => {
|
||||
@@ -108,6 +124,7 @@ export function AntiquityViewer({ src }: { src: string }) {
|
||||
|
||||
homeRef.current = { pos: camera.position.clone(), target: controls.target.clone() };
|
||||
setStatus("ready");
|
||||
requestRender();
|
||||
},
|
||||
(event) => {
|
||||
if (event.total) setProgress(Math.round((event.loaded / event.total) * 100));
|
||||
@@ -118,9 +135,14 @@ export function AntiquityViewer({ src }: { src: string }) {
|
||||
);
|
||||
|
||||
const tick = () => {
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
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();
|
||||
|
||||
@@ -128,6 +150,8 @@ export function AntiquityViewer({ src }: { src: string }) {
|
||||
disposed = true;
|
||||
cancelAnimationFrame(raf);
|
||||
observer.disconnect();
|
||||
stopGate();
|
||||
controls.removeEventListener("change", requestRender);
|
||||
controls.dispose();
|
||||
scene.traverse((object) => {
|
||||
const mesh = object as THREE.Mesh;
|
||||
|
||||
Reference in New Issue
Block a user