added a step to obj conversion and a viewer.html based on three.js

This commit is contained in:
2026-02-08 11:07:44 +01:00
parent 6350b63742
commit 2f308584c0

178
viewer.html Normal file
View File

@@ -0,0 +1,178 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>OBJ Viewer (three.js modules)</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
html, body { margin: 0; height: 100%; overflow: hidden; background: #111; }
#hud {
position: fixed; left: 12px; top: 12px;
padding: 10px 12px; border-radius: 10px;
background: rgba(0,0,0,0.75); color: #fff;
font: 13px/1.4 system-ui, sans-serif;
max-width: 460px;
white-space: pre-wrap;
}
#hud b { display:block; margin-bottom: 6px; }
code { background: rgba(255,255,255,0.08); padding: 1px 4px; border-radius: 6px; }
</style>
<!-- Import map: map "three" and "three/addons/" to CDN module files -->
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
}
}
</script>
</head>
<body>
<div id="hud"><b>OBJ Viewer</b>Waiting...</div>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { OBJLoader } from "three/addons/loaders/OBJLoader.js";
import { MTLLoader } from "three/addons/loaders/MTLLoader.js";
const OBJ_URL = "./output.obj";
const MTL_URL = "./output.mtl"; // optional
const hud = document.getElementById("hud");
function hudMsg(lines) { hud.textContent = lines.join("\n"); }
hudMsg(["OBJ Viewer", "Booting (modules)..."]);
// Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 1e7);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// Lights
scene.add(new THREE.AmbientLight(0xffffff, 0.6));
const dir = new THREE.DirectionalLight(0xffffff, 1.0);
dir.position.set(3, 5, 4);
scene.add(dir);
function frameObject(obj) {
const box = new THREE.Box3().setFromObject(obj);
const size = box.getSize(new THREE.Vector3());
const center = box.getCenter(new THREE.Vector3());
obj.position.sub(center);
const maxDim = Math.max(size.x, size.y, size.z);
const fov = camera.fov * Math.PI / 180;
let dist = (maxDim / 2) / Math.tan(fov / 2);
dist *= 1.6;
camera.position.set(dist, dist * 0.6, dist);
camera.near = Math.max(maxDim / 1000, 0.01);
camera.far = maxDim * 1000;
camera.updateProjectionMatrix();
controls.target.set(0, 0, 0);
controls.update();
}
async function urlExists(url) {
try {
// Some servers block HEAD; fall back to GET if needed.
let r = await fetch(url, { method: "HEAD" });
if (!r.ok) r = await fetch(url, { method: "GET" });
return r.ok;
} catch {
return false;
}
}
async function load() {
hudMsg(["OBJ Viewer", `Loading: ${OBJ_URL}`]);
const objLoader = new OBJLoader();
// Optional MTL
const hasMtl = await urlExists(MTL_URL);
if (hasMtl) {
hudMsg(["OBJ Viewer", `Loading MTL: ${MTL_URL}`]);
const mtlLoader = new MTLLoader();
const materials = await new Promise((resolve, reject) => {
mtlLoader.load(MTL_URL, resolve, undefined, reject);
});
materials.preload();
objLoader.setMaterials(materials);
}
hudMsg(["OBJ Viewer", `Loading OBJ: ${OBJ_URL}`]);
const obj = await new Promise((resolve, reject) => {
objLoader.load(
OBJ_URL,
resolve,
(xhr) => {
const mb = (xhr.loaded / (1024 * 1024)).toFixed(2);
hudMsg(["OBJ Viewer", `Loading OBJ: ${OBJ_URL}`, `${mb} MB loaded`]);
},
reject
);
});
// Ensure normals/material
obj.traverse((c) => {
if (c.isMesh) {
c.geometry.computeVertexNormals?.();
if (!c.material) c.material = new THREE.MeshStandardMaterial({ color: 0xb0b0b0 });
}
});
scene.add(obj);
frameObject(obj);
hudMsg([
"OBJ Viewer",
"Loaded OK",
hasMtl ? "MTL: used" : "MTL: not used",
"",
"Left drag: rotate",
"Wheel: zoom",
"Right drag: pan"
]);
}
load().catch((e) => {
console.error(e);
hudMsg([
"OBJ Viewer",
"ERROR",
String(e),
"",
"Open DevTools -> Console for details."
]);
});
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>