Dims, tri counts and node names all pass on a bike leaning the wrong way and
a gutter that never fell. The Blender contact sheet is close but renders
through Blender's view transform — it showed this sprint's jacaranda as
near-white lavender when the material is #8C7FC0, so a colour call made there
is a call about Blender, not about the game.
Smallest thing that answers the question: the game's own vendored three.js,
daylight, a ground plane, and the 1.7 m ref capsule beside whatever you name.
/tools/assetcheck/look.html?a=swing_set_01,tree_jacaranda_01&cam=10,4.5,22
It caught two things this sprint the numbers could not: the lilac reads lilac
in the real renderer (the thumb was lying), and the swing set's wreck lands on
+Z with a 2.9 m reach against a 0.95 m standing footprint — which is now baked
as a placement extra and asserted.
Sized at draw time, every time: a tab that boots hidden reports 0x0 and you
get a correct scene rendered into nothing. That cost me the first screenshot
of this very page, so it is in the MANUAL gotcha list where it already was.
108 lines
4.6 KiB
HTML
108 lines
4.6 KiB
HTML
<!doctype html>
|
||
<meta charset="utf-8">
|
||
<title>assetcheck — LOOK at it, in the real renderer</title>
|
||
<!--
|
||
The verify that dims and node names cannot do.
|
||
|
||
build_yard_assets.py re-imports each GLB and asserts its box, its tri count
|
||
and its node names; assets_in_three.html proves the axis mapping natively.
|
||
Neither can answer "is this the right SHAPE" — a bike leaning the wrong way
|
||
and a gutter that never fell both pass every number in the repo. The Blender
|
||
contact sheet is close, but it renders through Blender's view transform: it
|
||
showed this sprint's jacaranda as near-white lavender when the material is
|
||
#8C7FC0, so a colour judgement made there is a judgement about Blender.
|
||
|
||
This page is deliberately the smallest thing that answers the question: the
|
||
game's own vendored three.js, daylight, a ground plane, and the 1.7 m ref
|
||
capsule standing beside whatever you name.
|
||
|
||
/tools/assetcheck/look.html?a=swing_set_01,tree_jacaranda_01
|
||
...&cam=12,4.5,20&look=0,2,0 camera position / aim
|
||
...&a=tramp_01:debris an asset under models/debris/
|
||
|
||
Assets are laid out along X in the order given, spaced by their own width,
|
||
with the capsule first. Served by server.py from the repo root.
|
||
-->
|
||
<style>body{margin:0;background:#a8bccc;overflow:hidden}
|
||
#hud{position:fixed;left:8px;top:8px;font:12px/1.5 ui-monospace,Menlo,monospace;
|
||
color:#22303a;background:#ffffffaa;padding:6px 9px;border-radius:4px}</style>
|
||
<div id="hud">loading…</div>
|
||
<script type="importmap">
|
||
{ "imports": { "three": "/web/world/vendor/three.module.js",
|
||
"three/addons/": "/web/world/vendor/addons/" } }
|
||
</script>
|
||
<script type="module">
|
||
import * as THREE from 'three';
|
||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||
|
||
const q = new URLSearchParams(location.search);
|
||
const names = (q.get('a') ?? 'swing_set_01,tree_jacaranda_01,tree_gum_01').split(',');
|
||
const vec = (s, d) => (s ?? d).split(',').map(Number);
|
||
|
||
const r = new THREE.WebGLRenderer({ antialias: true });
|
||
document.body.appendChild(r.domElement);
|
||
|
||
const scene = new THREE.Scene();
|
||
scene.background = new THREE.Color('#a8bccc');
|
||
// Roughly the game's daylight: a bright sky dome over grass bounce, plus a sun
|
||
// high and to one side. Not the storm grade — this is for reading a shape, and
|
||
// a shape you can only read in perfect light is a shape that does not work.
|
||
scene.add(new THREE.HemisphereLight('#cfe2f0', '#6d7a52', 1.1));
|
||
const sun = new THREE.DirectionalLight('#fff3df', 2.0);
|
||
sun.position.set(6, 10, 4);
|
||
scene.add(sun);
|
||
const ground = new THREE.Mesh(new THREE.PlaneGeometry(120, 120),
|
||
new THREE.MeshStandardMaterial({ color: '#7e8f5c' }));
|
||
ground.rotation.x = -Math.PI / 2;
|
||
scene.add(ground);
|
||
|
||
const loader = new GLTFLoader();
|
||
const load = async (spec) => {
|
||
const [name, sub] = spec.split(':');
|
||
const dir = sub ? `${sub}/` : '';
|
||
const g = await loader.loadAsync(`/web/world/models/${dir}${name}_v1.glb`);
|
||
// The optional-node flag has to be honoured here too, or the garden bed
|
||
// shows all three wilt states at once and you "see" a bug that isn't one.
|
||
g.scene.traverse((o) => { if (o.userData?.hidden_by_default) o.visible = false; });
|
||
return { name, obj: g.scene };
|
||
};
|
||
|
||
const items = [{ name: 'ref_capsule', obj: (await load('ref_capsule')).obj },
|
||
...(await Promise.all(names.map(load)))];
|
||
|
||
let x = 0;
|
||
const lines = [];
|
||
for (const it of items) {
|
||
const size = new THREE.Box3().setFromObject(it.obj).getSize(new THREE.Vector3());
|
||
x += size.x / 2 + 0.9;
|
||
it.obj.position.x = x;
|
||
x += size.x / 2;
|
||
scene.add(it.obj);
|
||
lines.push(`${it.name.padEnd(22)} ${size.x.toFixed(2)} × ${size.y.toFixed(2)} × ${size.z.toFixed(2)} m`);
|
||
}
|
||
|
||
const cam = new THREE.PerspectiveCamera(38, 1, 0.1, 300);
|
||
cam.position.set(...vec(q.get('cam'), `${x * 0.55},4.0,${x * 0.95 + 6}`));
|
||
cam.lookAt(...vec(q.get('look'), `${x / 2},2,0`));
|
||
|
||
// Size at DRAW time, every time, and never once at module scope: a tab that
|
||
// boots hidden reports 0×0 and you get a correct scene rendered into nothing.
|
||
// (MANUAL, "hidden-canvas boot needs one resize before projections are sane" —
|
||
// it cost me the first screenshot of this very page.)
|
||
const draw = () => {
|
||
r.setSize(innerWidth, innerHeight, false);
|
||
r.domElement.style.width = '100%';
|
||
r.domElement.style.height = '100%';
|
||
cam.aspect = innerWidth / Math.max(innerHeight, 1);
|
||
cam.updateProjectionMatrix();
|
||
r.render(scene, cam);
|
||
};
|
||
addEventListener('resize', draw);
|
||
draw();
|
||
|
||
document.getElementById('hud').textContent =
|
||
`three r${THREE.REVISION} — capsule is 1.70 m\n${lines.join('\n')}`;
|
||
document.getElementById('hud').style.whiteSpace = 'pre';
|
||
window.__ready = true;
|
||
</script>
|