perf: nén model .glb (meshopt + texture webp), backup bản gốc model-originals/
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,3 +44,6 @@ next-env.d.ts
|
|||||||
|
|
||||||
# ảnh gốc trước khi nén (backup cục bộ)
|
# ảnh gốc trước khi nén (backup cục bộ)
|
||||||
/image-originals
|
/image-originals
|
||||||
|
|
||||||
|
# model .glb gốc trước khi nén (backup cục bộ)
|
||||||
|
/model-originals
|
||||||
|
|||||||
Generated
+1902
-3
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -7,7 +7,8 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"optimize:images": "node scripts/optimize-images.mjs"
|
"optimize:images": "node scripts/optimize-images.mjs",
|
||||||
|
"optimize:models": "node scripts/optimize-models.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@paper-design/shaders-react": "^0.0.76",
|
"@paper-design/shaders-react": "^0.0.76",
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
"three": "^0.184.0"
|
"three": "^0.184.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@gltf-transform/cli": "^4.4.0",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,53 @@
|
|||||||
|
// scripts/optimize-models.mjs
|
||||||
|
// Nén .glb: backup bản gốc rồi tối ưu (meshopt + nén texture webp).
|
||||||
|
// Chạy: node scripts/optimize-models.mjs (an toàn chạy lại — luôn nén từ backup gốc)
|
||||||
|
import { promises as fs } from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { execFile } from "node:child_process";
|
||||||
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
|
const run = promisify(execFile);
|
||||||
|
const ROOT = process.cwd();
|
||||||
|
const MODEL_DIR = path.join(ROOT, "public/antiquity");
|
||||||
|
const BACKUP_DIR = path.join(ROOT, "model-originals");
|
||||||
|
const fmt = (b) => (b / 1024 / 1024).toFixed(2) + "MB";
|
||||||
|
const exists = (p) => fs.access(p).then(() => true).catch(() => false);
|
||||||
|
|
||||||
|
async function optimizeModel(file) {
|
||||||
|
const dest = path.join(MODEL_DIR, file);
|
||||||
|
const backup = path.join(BACKUP_DIR, file);
|
||||||
|
await fs.mkdir(BACKUP_DIR, { recursive: true });
|
||||||
|
if (!(await exists(backup))) {
|
||||||
|
await fs.copyFile(dest, backup);
|
||||||
|
}
|
||||||
|
const before = (await fs.stat(dest)).size;
|
||||||
|
// Luôn tối ưu TỪ backup gốc; ghi đè ra dest.
|
||||||
|
const isWin = process.platform === "win32";
|
||||||
|
const bin = path.join(ROOT, "node_modules/.bin", isWin ? "gltf-transform.cmd" : "gltf-transform");
|
||||||
|
const q = (s) => (isWin ? `"${s}"` : s);
|
||||||
|
await run(
|
||||||
|
q(bin),
|
||||||
|
[
|
||||||
|
"optimize",
|
||||||
|
q(backup),
|
||||||
|
q(dest),
|
||||||
|
"--compress", "meshopt",
|
||||||
|
"--texture-compress", "webp",
|
||||||
|
],
|
||||||
|
// Trên Windows phải chạy .cmd qua shell (Node >=18 ném EINVAL nếu spawn .cmd trực tiếp).
|
||||||
|
{ shell: isWin }
|
||||||
|
);
|
||||||
|
const after = (await fs.stat(dest)).size;
|
||||||
|
console.log(`${file}: ${fmt(before)} -> ${fmt(after)} (-${Math.round((1 - after / before) * 100)}%)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const files = (await fs.readdir(MODEL_DIR)).filter((f) => /\.glb$/i.test(f)).sort();
|
||||||
|
for (const f of files) {
|
||||||
|
await optimizeModel(f);
|
||||||
|
}
|
||||||
|
console.log("Done.");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("optimize-models failed:", err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user