initial migration, not vetted
This commit is contained in:
parent
41a32b8f68
commit
09bc6a5304
29 changed files with 122147 additions and 0 deletions
192
web/lab.html
Normal file
192
web/lab.html
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>RenderLab v1</title>
|
||||
<style>
|
||||
body { margin:0; background:white; }
|
||||
canvas { display:block; }
|
||||
</style>
|
||||
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "./vendor/three/build/three.module.js",
|
||||
"three/addons/": "./vendor/three/addons/"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
import GUI from 'three/addons/libs/lil-gui.module.min.js';
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0xffffff);
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(50, innerWidth/innerHeight, 0.1, 1000);
|
||||
camera.position.set(3,3,3);
|
||||
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
preserveDrawingBuffer: true, // <-- critical for toDataURL on many setups
|
||||
alpha: false // ensure opaque canvas
|
||||
});
|
||||
renderer.setSize(innerWidth, innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setClearColor(0xffffff, 1); // opaque white
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
|
||||
const light = new THREE.DirectionalLight(0xffffff, 1.5);
|
||||
light.position.set(5,5,5);
|
||||
scene.add(light);
|
||||
|
||||
const ambient = new THREE.AmbientLight(0xffffff, 0.6);
|
||||
scene.add(ambient);
|
||||
|
||||
let mesh, edgeLines;
|
||||
|
||||
const loader = new GLTFLoader();
|
||||
loader.load('./glb/part.glb', gltf => {
|
||||
|
||||
mesh = gltf.scene.children[0];
|
||||
|
||||
mesh.material = new THREE.MeshStandardMaterial({
|
||||
color: 0xdddddd,
|
||||
roughness: 0.9,
|
||||
metalness: 0
|
||||
});
|
||||
|
||||
scene.add(mesh);
|
||||
addEdges(30);
|
||||
|
||||
});
|
||||
|
||||
function ts_yyyymmdd_hhmiss () {
|
||||
const d = new Date();
|
||||
const pad = n => String(n).padStart(2,'0');
|
||||
return (
|
||||
d.getFullYear() +
|
||||
pad(d.getMonth()+1) +
|
||||
pad(d.getDate()) + '_' +
|
||||
pad(d.getHours()) +
|
||||
pad(d.getMinutes()) +
|
||||
pad(d.getSeconds())
|
||||
);
|
||||
}
|
||||
|
||||
function downloadTextFile (filename, text) {
|
||||
const blob = new Blob([text], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function addEdges(angle) {
|
||||
if (!mesh) return;
|
||||
if (edgeLines) scene.remove(edgeLines);
|
||||
|
||||
const edges = new THREE.EdgesGeometry(mesh.geometry, angle);
|
||||
edgeLines = new THREE.LineSegments(
|
||||
edges,
|
||||
new THREE.LineBasicMaterial({ color: 0x000000 })
|
||||
);
|
||||
|
||||
scene.add(edgeLines);
|
||||
}
|
||||
|
||||
|
||||
const params = {
|
||||
wireframe: false,
|
||||
edgeAngle: 30,
|
||||
lightIntensity: 1.5,
|
||||
|
||||
savePNG: () => {
|
||||
renderer.render(scene, camera); // recommended before capture
|
||||
const link = document.createElement('a');
|
||||
link.download = 'render.png';
|
||||
link.href = renderer.domElement.toDataURL("image/png");
|
||||
link.click();
|
||||
},
|
||||
|
||||
exportProfile: () => {
|
||||
controls.update(); // ensure target is current
|
||||
|
||||
const profile = {
|
||||
provenance: `lab.html exportProfile ${new Date().toString()}`,
|
||||
|
||||
output: {
|
||||
width: renderer.domElement.width,
|
||||
height: renderer.domElement.height,
|
||||
pixelRatio: renderer.getPixelRatio()
|
||||
},
|
||||
|
||||
scene: {
|
||||
background: scene.background?.getHex?.() ?? null
|
||||
},
|
||||
|
||||
camera: {
|
||||
type: 'PerspectiveCamera',
|
||||
fov: camera.fov,
|
||||
near: camera.near,
|
||||
far: camera.far,
|
||||
position: camera.position.toArray(),
|
||||
up: camera.up.toArray()
|
||||
},
|
||||
|
||||
controls: {
|
||||
target: controls.target.toArray()
|
||||
},
|
||||
|
||||
renderParams: {
|
||||
wireframe: params.wireframe,
|
||||
edgeAngle: params.edgeAngle,
|
||||
lightIntensity: params.lightIntensity
|
||||
},
|
||||
|
||||
lights: {
|
||||
directional: { position: light.position.toArray(), intensity: light.intensity },
|
||||
ambient: { intensity: ambient.intensity }
|
||||
}
|
||||
};
|
||||
|
||||
const fn = `three_profile_${ts_yyyymmdd_hhmiss()}.json`;
|
||||
downloadTextFile(fn, JSON.stringify(profile, null, 2));
|
||||
console.log('Exported profile:', profile);
|
||||
}
|
||||
};
|
||||
|
||||
const gui = new GUI();
|
||||
gui.add(params, 'wireframe').onChange(v => {
|
||||
if (mesh) mesh.material.wireframe = v;
|
||||
});
|
||||
gui.add(params, 'edgeAngle', 1, 90).onChange(v => addEdges(v));
|
||||
gui.add(params, 'lightIntensity', 0, 3).onChange(v => light.intensity = v);
|
||||
gui.add(params, 'savePNG');
|
||||
gui.add(params, 'exportProfile');
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
camera.aspect = innerWidth/innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(innerWidth, innerHeight);
|
||||
});
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
animate();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue