Compare commits
4 Commits
302972cc6e
...
181dc82f1f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
181dc82f1f | ||
|
|
0daeb1f2c3 | ||
|
|
7fc57934eb | ||
|
|
2757b9e91f |
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# Per-asset verification renders — regenerable, and 3 MB of churn.
|
||||
# The tiled contact_sheet.png IS committed; it's the acceptance evidence.
|
||||
tools/blender/thumbs/
|
||||
|
||||
# PLAN3D §0: raw sources stay in the shared libraries, runtime asset is .glb
|
||||
*.blend
|
||||
*.blend1
|
||||
*.fbx
|
||||
|
||||
.DS_Store
|
||||
__pycache__/
|
||||
15
THREADS.md
15
THREADS.md
@ -4,3 +4,18 @@ One line per landed feature, contract change, or open question.
|
||||
Format: `[lane letter] YYYY-MM-DD — note`
|
||||
|
||||
[.] 2026-07-16 — repo seeded: DESIGN.md (canon), PLAN3D.md (build plan), prototype/ (2D reference, do not modify)
|
||||
[E] 2026-07-16 — ⚠️ PLAN3D §2's asset paths do not exist on this box. We are on m3ultra.local (Apple M3 Ultra); the plan was written against the M1 Ultra (johnking@100.91.239.7), so its "verified" inventory is that box's, not ours. Corrections below — read before you go hunting for a library.
|
||||
[E] 2026-07-16 — ✅ present here: 90sDJsim (~/Documents/90sDJsim/ — vendored three r175, web/world/models/peds/ full fleet, DEVMANUAL.md). 3D-STORE IS here but at ~/Documents/Destroyulater/3D-STORE/ (NOT ~/Documents/3D-STORE) — has build_booth_room_v23.py, racks_to_glb.py, clean_glbs/. Blender 5.1.2.
|
||||
[E] 2026-07-16 — ❌ MISSING here entirely, under any name: ~/Documents/character_kit/, ~/Documents/FBX/, ~/Documents/3D=models/, ~/Documents/mixamo-fetch/ (there is an unrelated ~/Documents/mixamo_upload/). Impact: Lane D's whole anim pipeline (character_kit rigs + FBX clips + mixamo-fetch wishlist) has no source on this machine, and Lane A's `3D=models/street-furniture/building_shell_01.glb` does not exist — use Lane E's house_yardside_v1.glb instead. Either those libs live only on the M1 Ultra and need syncing over Tailscale, or the inventory was aspirational. Flagging, not fixing — not my files (§0).
|
||||
[E] 2026-07-16 — landed tools/blender/build_yard_assets.py: ONE deterministic script → 16 GLBs + grass atlas. Run `blender -b -P tools/blender/build_yard_assets.py` (flags: --only <name> / --no-verify / --no-debris). Proven, not asserted: 17/17 output files byte-identical across two runs; every GLB re-imported from disk and checked for dims-in-range, tri budget, and node-name survival; contact_sheet.png renders each against the 1.7 m ref capsule. Heaviest asset is garden_bed at 2,580 tris — all far under the 15 k budget. Machine-readable manifest: tools/blender/asset_report.json.
|
||||
[E] 2026-07-16 — contract, trees: `trunk` (trunk+branches, rigid) and `canopy_01..03` are SEPARATE nodes — Lane A, sway the canopies only. `branch_anchor_01..03` are empties carrying anchor_type="tree" + rating_hint (thicker limb = higher) for world.anchors / Lane B.
|
||||
[E] 2026-07-16 — contract, house_yardside: `fascia_anchor_01..03` empties carry rating_hint=0.35 and collateral="gutter", and the gutter node carries collateral_of="fascia" — DESIGN.md's "the fascia board is a lie" wired as data, so ripping it can take the gutter with it. Facade only (9.20 × 1.05 × 2.90 m), no interior.
|
||||
[E] 2026-07-16 — contract, sail_post: exported VERTICAL with `rake_pivot` at the footing and `top_anchor` at the head. DESIGN.md says rake the post away from the load — that's a player decision, so rake is a runtime rotation about rake_pivot, NOT baked geometry. Post is 4.0 m. Also: shed_table has `pickup_anchor`, ladder_01 has `ladder_base`/`ladder_top`, gate has `hinge_axis`.
|
||||
[E] 2026-07-16 — contract, hardware: shackle/carabiner/turnbuckle each keep their failure part as its own node — `pin` (unscrews then shears), `gate` (flutters open), `body` (thread strips) — with failure_mode stamped as a custom prop, so a break anim moves just that piece. They read at 1 m; see contact sheet.
|
||||
[E] 2026-07-16 — contract, garden_bed: all 3 damage states ship in ONE glb as sibling nodes `plants_full` / `plants_tattered` / `plants_dead` (full visible, rest hide_render). Lane A: toggle .visible, don't reload — instant swap, no pop-in. Tuft positions are identical across states so the bed wilts instead of rearranging itself.
|
||||
[E] 2026-07-16 — debris in web/world/models/debris/, copied verbatim from the library and scale-checked: BlueCrate_v2 0.36×0.36×0.29, BlackTub_v2 + WhiteTub_v2 0.36×0.54×0.20, WoodenBin_v2 0.35×0.36×0.31 m — all plausible real-world sizes. Plus tramp_01_v1.glb (2.96×2.96×0.78, mass_hint 45) because every Australian storm produces one airborne trampoline. NOTE: §0's *_v1.glb rule beats §5-E's "tramp_01.glb" spelling — Lane C, glob the dir rather than hardcoding names.
|
||||
[E] 2026-07-16 — grass is a texture, not geometry (§5-E item 9): web/world/models/textures/grass_atlas.png, 512², 2×2 tufts, alpha. Lane A instances billboards from it.
|
||||
[E] 2026-07-16 — ⚠️ FOR LANE A + LANE C, bounding boxes: `THREE.Box3.setFromObject(obj)` expands each mesh's LOCAL box by the world matrix, so a node carrying a rotation reports an inflated box — and that's what three uses for frustum culling. Blender's obj.bound_box has the identical trap (it cost me an hour of chasing phantom "failures"). Fixed at source: join_group() now bakes rotation into the vertices, so every node's local box is axis-aligned and tight. Before the fix three reported tramp_01 as 3.29×1.27 m vs its true 2.96×0.78. You can use the default Box3 safely now — but if you ever measure geometry yourself, measure VERTICES, not bound_box corners.
|
||||
[E] 2026-07-16 — landed tools/assetcheck/ (README + assets_in_three.html): loads every GLB through three.js r175 GLTFLoader and asserts Y-up, node survival, and that branch_anchor_01 lands at a sane +Y world position. This exists because the Blender round-trip CANNOT catch an axis bug — it exports Z-up→Y-up and imports Y-up→Z-up, so a broken export_yup flips back and passes. Currently green: 16/16 + anchors, `branch_anchor_01` at (-0.96, 3.64, -1.46). Lane A: once server.py + vendor/ land, this wants to live next to selftest.html — happy for you to fold it in, it reads expectations from asset_report.json so it stays in sync by itself.
|
||||
[E] 2026-07-16 — added a root .gitignore (thumbs/, *.blend, *.fbx per §0). Unowned file — Lane A, fold it into yours if you'd rather own it.
|
||||
[E] 2026-07-16 — open q for Lane A: does house_yardside at 9.20 m wide sit right on the north edge of a 30×20 m yard, or do you want a wider/second variant? Cheap to re-cut — it's one function in the script.
|
||||
|
||||
50
tools/assetcheck/README.md
Normal file
50
tools/assetcheck/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# assets_in_three.html — verify the GLBs in the real consumer
|
||||
|
||||
`build_yard_assets.py` already re-imports every GLB into Blender and asserts
|
||||
dims, tri budget, and node names. That is necessary but **structurally cannot
|
||||
catch an axis error**: Blender exports Z-up→Y-up and imports Y-up→Z-up, so a
|
||||
broken `export_yup` flips back on the way in and round-trips green. Only
|
||||
something that reads glTF natively can prove the file is right.
|
||||
|
||||
This page is that check. It loads each GLB with three.js r175's `GLTFLoader` and
|
||||
asserts:
|
||||
|
||||
- **Y-up**: a Blender asset measuring `(dx, dy, dz)` must arrive as `(dx, dz, dy)`.
|
||||
- **node survival**: `branch_anchor_*`, `fascia_anchor_*`, `plants_*` etc. still
|
||||
exist after the export — glTF has no "empty", anchors arrive as bare
|
||||
`Object3D`, and exporters have been known to prune childless nodes.
|
||||
- **anchors are usable**: `branch_anchor_01` resolves to a sane world position
|
||||
with height on +Y.
|
||||
|
||||
Expectations are read from `tools/blender/asset_report.json`, so this stays in
|
||||
sync with the factory automatically.
|
||||
|
||||
## Why it uses the default (non-precise) Box3
|
||||
|
||||
`THREE.Box3.setFromObject(obj)` expands each mesh's **local** bounding box by the
|
||||
world matrix, so a node carrying a rotation reports an inflated box. That is the
|
||||
same trap Blender's `obj.bound_box` sets, and it is what three uses for frustum
|
||||
culling. `join_group()` therefore bakes rotation into the vertices so the local
|
||||
box is axis-aligned and tight. Passing `precise: true` here would hide exactly
|
||||
the regression this page exists to catch — so don't.
|
||||
|
||||
Before that fix, three reported `tramp_01` as 3.29 × 1.27 m against a true
|
||||
2.96 × 0.78 m.
|
||||
|
||||
## Running it
|
||||
|
||||
Needs three.js on `/world/vendor/` and the models on `/models/`. Once Lane A's
|
||||
`server.py` lands, serve `web/` and this can move next to `selftest.html`
|
||||
(Lane A: happy to fold it in — see THREADS).
|
||||
|
||||
Until then, the standalone recipe:
|
||||
|
||||
```sh
|
||||
D=$(mktemp -d) && mkdir -p "$D/world"
|
||||
ln -s ~/Documents/90sDJsim/web/world/vendor "$D/world/vendor"
|
||||
ln -s "$PWD/web/world/models" "$D/models"
|
||||
ln -s "$PWD/tools/blender/asset_report.json" "$D/asset_report.json"
|
||||
cp tools/assetcheck/assets_in_three.html "$D/index.html"
|
||||
python3 -m http.server 8805 --directory "$D"
|
||||
# open http://127.0.0.1:8805 — look for "SUMMARY: ALL PASS IN THREE.JS"
|
||||
```
|
||||
82
tools/assetcheck/assets_in_three.html
Normal file
82
tools/assetcheck/assets_in_three.html
Normal file
@ -0,0 +1,82 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>Lane E — GLB check in the real consumer (three.js r175)</title>
|
||||
<style>
|
||||
body { background:#14161a; color:#dfe3e8; font:13px/1.5 ui-monospace,Menlo,monospace; padding:16px; }
|
||||
.pass { color:#7fd67f; } .fail { color:#ff6b6b; } h1 { font-size:15px; color:#9fb4c7; }
|
||||
</style>
|
||||
<h1>GLB verification — loaded by three.js GLTFLoader, not Blender</h1>
|
||||
<pre id="out">loading…</pre>
|
||||
|
||||
<script type="importmap">
|
||||
{ "imports": { "three": "/world/vendor/three.module.js",
|
||||
"three/addons/": "/world/vendor/addons/" } }
|
||||
</script>
|
||||
<script type="module">
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
|
||||
const out = document.getElementById('out');
|
||||
const log = [];
|
||||
const say = (s, cls) => {
|
||||
log.push(s);
|
||||
out.innerHTML += `<span class="${cls || ''}">${s}</span>\n`;
|
||||
console.log(s);
|
||||
};
|
||||
|
||||
const report = await (await fetch('/asset_report.json')).json();
|
||||
const loader = new GLTFLoader();
|
||||
let fails = 0;
|
||||
|
||||
say(`three.js r${THREE.REVISION} | ${report.assets.length} assets\n`);
|
||||
|
||||
for (const a of report.assets) {
|
||||
let gltf = null;
|
||||
for (const dir of ['/models/', '/models/debris/']) {
|
||||
try { gltf = await loader.loadAsync(`${dir}${a.name}_v1.glb`); break; } catch (e) {}
|
||||
}
|
||||
if (!gltf) { say(`[FAIL] ${a.name.padEnd(16)} could not load`, 'fail'); fails++; continue; }
|
||||
|
||||
const size = new THREE.Vector3();
|
||||
new THREE.Box3().setFromObject(gltf.scene).getSize(size);
|
||||
|
||||
// The whole point of this page. Blender is Z-up, glTF is Y-up, so a Blender
|
||||
// asset measuring (dx, dy, dz) MUST arrive here as (dx, dz, dy). A Blender
|
||||
// re-import can never catch a broken export_yup — it just flips it back.
|
||||
const [bx, by, bz] = a.dims;
|
||||
const exp = [bx, bz, by];
|
||||
const got = [size.x, size.y, size.z];
|
||||
const axisOk = got.every((v, i) => Math.abs(v - exp[i]) < 0.02);
|
||||
|
||||
const names = [];
|
||||
gltf.scene.traverse(o => names.push(o.name));
|
||||
const missing = a.nodes.filter(n => !names.includes(n));
|
||||
|
||||
const ok = axisOk && missing.length === 0;
|
||||
if (!ok) fails++;
|
||||
say(`[${ok ? 'PASS' : 'FAIL'}] ${a.name.padEnd(16)} ` +
|
||||
`${got.map(v => v.toFixed(2)).join(' x ')} m (Y-up)`, ok ? 'pass' : 'fail');
|
||||
if (!axisOk) say(` axis/scale: expected ${exp.map(v => v.toFixed(2)).join(' x ')}`, 'fail');
|
||||
if (missing.length) say(` nodes lost in three.js: ${missing.join(', ')}`, 'fail');
|
||||
}
|
||||
|
||||
// Empties are the risky part: glTF has no "empty", they arrive as bare Object3D
|
||||
// nodes, and exporters have been known to prune childless ones. Anchors ARE the
|
||||
// contract, so prove one survives with a usable world position.
|
||||
const t = await loader.loadAsync('/models/tree_gum_01_v1.glb');
|
||||
const anchor = t.scene.getObjectByName('branch_anchor_01');
|
||||
if (anchor) {
|
||||
t.scene.updateWorldMatrix(true, true);
|
||||
const p = new THREE.Vector3().setFromMatrixPosition(anchor.matrixWorld);
|
||||
const upright = p.y > 1.0 && p.y < 6.0;
|
||||
if (!upright) fails++;
|
||||
say(`\n[${upright ? 'PASS' : 'FAIL'}] branch_anchor_01 world pos ` +
|
||||
`(${p.x.toFixed(2)}, ${p.y.toFixed(2)}, ${p.z.toFixed(2)}) — ` +
|
||||
`${upright ? 'height is on +Y, anchors are usable' : 'height is NOT on +Y!'}`,
|
||||
upright ? 'pass' : 'fail');
|
||||
} else { fails++; say('\n[FAIL] branch_anchor_01 missing entirely', 'fail'); }
|
||||
|
||||
say(`\nSUMMARY: ${fails === 0 ? 'ALL PASS IN THREE.JS' : fails + ' FAILURES'}`,
|
||||
fails === 0 ? 'pass' : 'fail');
|
||||
window.__done = true; window.__fails = fails;
|
||||
</script>
|
||||
328
tools/blender/asset_report.json
Normal file
328
tools/blender/asset_report.json
Normal file
@ -0,0 +1,328 @@
|
||||
{
|
||||
"blender": "5.1.2",
|
||||
"assets": [
|
||||
{
|
||||
"name": "ref_capsule",
|
||||
"dims": [
|
||||
0.4,
|
||||
0.4,
|
||||
1.7
|
||||
],
|
||||
"tris": 220,
|
||||
"nodes": [
|
||||
"head_height",
|
||||
"ref_capsule",
|
||||
"ref_capsule_mesh"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "tree_gum_01",
|
||||
"dims": [
|
||||
4.5522,
|
||||
4.956,
|
||||
7.9702
|
||||
],
|
||||
"tris": 396,
|
||||
"nodes": [
|
||||
"branch_anchor_01",
|
||||
"branch_anchor_02",
|
||||
"branch_anchor_03",
|
||||
"canopy_01",
|
||||
"canopy_02",
|
||||
"canopy_03",
|
||||
"tree_gum_01",
|
||||
"trunk"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "tree_gum_02",
|
||||
"dims": [
|
||||
3.8871,
|
||||
2.7787,
|
||||
5.4972
|
||||
],
|
||||
"tris": 288,
|
||||
"nodes": [
|
||||
"branch_anchor_01",
|
||||
"branch_anchor_02",
|
||||
"canopy_01",
|
||||
"canopy_02",
|
||||
"tree_gum_02",
|
||||
"trunk"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "fence_post",
|
||||
"dims": [
|
||||
0.13,
|
||||
0.13,
|
||||
2.03
|
||||
],
|
||||
"tris": 24,
|
||||
"nodes": [
|
||||
"fence_post",
|
||||
"post"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "fence_panel",
|
||||
"dims": [
|
||||
2.4,
|
||||
0.054,
|
||||
1.8194
|
||||
],
|
||||
"tris": 324,
|
||||
"nodes": [
|
||||
"fence_panel",
|
||||
"palings",
|
||||
"rails"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "gate",
|
||||
"dims": [
|
||||
1.045,
|
||||
0.0615,
|
||||
1.75
|
||||
],
|
||||
"tris": 220,
|
||||
"nodes": [
|
||||
"gate",
|
||||
"gate_frame",
|
||||
"gate_palings",
|
||||
"hinge_axis",
|
||||
"hinges"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "house_yardside",
|
||||
"dims": [
|
||||
9.2,
|
||||
1.0547,
|
||||
2.9
|
||||
],
|
||||
"tris": 200,
|
||||
"nodes": [
|
||||
"door",
|
||||
"fascia",
|
||||
"fascia_anchor_01",
|
||||
"fascia_anchor_02",
|
||||
"fascia_anchor_03",
|
||||
"gutter",
|
||||
"house_yardside",
|
||||
"roof",
|
||||
"wall",
|
||||
"window"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "shed_01",
|
||||
"dims": [
|
||||
2.58,
|
||||
1.9708,
|
||||
2.2224
|
||||
],
|
||||
"tris": 96,
|
||||
"nodes": [
|
||||
"door_anchor",
|
||||
"doors",
|
||||
"roof",
|
||||
"shed_01",
|
||||
"shell"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "shed_table",
|
||||
"dims": [
|
||||
1.6,
|
||||
0.6,
|
||||
0.9
|
||||
],
|
||||
"tris": 72,
|
||||
"nodes": [
|
||||
"pickup_anchor",
|
||||
"shed_table",
|
||||
"table_frame",
|
||||
"table_top"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "garden_bed",
|
||||
"dims": [
|
||||
3.0,
|
||||
1.2,
|
||||
0.8609
|
||||
],
|
||||
"tris": 2580,
|
||||
"nodes": [
|
||||
"bed",
|
||||
"garden_bed",
|
||||
"plants_dead",
|
||||
"plants_full",
|
||||
"plants_tattered",
|
||||
"soil"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "sail_post",
|
||||
"dims": [
|
||||
0.507,
|
||||
0.52,
|
||||
4.0327
|
||||
],
|
||||
"tris": 528,
|
||||
"nodes": [
|
||||
"footing",
|
||||
"pad_eye",
|
||||
"post",
|
||||
"rake_pivot",
|
||||
"sail_post",
|
||||
"top_anchor"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "ladder_01",
|
||||
"dims": [
|
||||
0.455,
|
||||
0.075,
|
||||
3.0
|
||||
],
|
||||
"tris": 276,
|
||||
"nodes": [
|
||||
"ladder",
|
||||
"ladder_01",
|
||||
"ladder_base",
|
||||
"ladder_top"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "shackle",
|
||||
"dims": [
|
||||
0.0569,
|
||||
0.019,
|
||||
0.0744
|
||||
],
|
||||
"tris": 560,
|
||||
"nodes": [
|
||||
"bow",
|
||||
"pin",
|
||||
"shackle"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "carabiner",
|
||||
"dims": [
|
||||
0.049,
|
||||
0.009,
|
||||
0.1027
|
||||
],
|
||||
"tris": 476,
|
||||
"nodes": [
|
||||
"body",
|
||||
"carabiner",
|
||||
"gate"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "turnbuckle",
|
||||
"dims": [
|
||||
0.0292,
|
||||
0.0341,
|
||||
0.1955
|
||||
],
|
||||
"tris": 728,
|
||||
"nodes": [
|
||||
"body",
|
||||
"eye_a",
|
||||
"eye_b",
|
||||
"turnbuckle"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
},
|
||||
{
|
||||
"name": "tramp_01",
|
||||
"dims": [
|
||||
2.9555,
|
||||
2.9555,
|
||||
0.78
|
||||
],
|
||||
"tris": 976,
|
||||
"nodes": [
|
||||
"legs",
|
||||
"mat",
|
||||
"pad",
|
||||
"rim",
|
||||
"tramp_01"
|
||||
],
|
||||
"status": "PASS",
|
||||
"problems": []
|
||||
}
|
||||
],
|
||||
"debris": [
|
||||
{
|
||||
"file": "BlueCrate_v2.glb",
|
||||
"dims": [
|
||||
0.36,
|
||||
0.36,
|
||||
0.29
|
||||
],
|
||||
"sane": true
|
||||
},
|
||||
{
|
||||
"file": "BlackTub_v2.glb",
|
||||
"dims": [
|
||||
0.36,
|
||||
0.54,
|
||||
0.2
|
||||
],
|
||||
"sane": true
|
||||
},
|
||||
{
|
||||
"file": "WhiteTub_v2.glb",
|
||||
"dims": [
|
||||
0.36,
|
||||
0.54,
|
||||
0.2
|
||||
],
|
||||
"sane": true
|
||||
},
|
||||
{
|
||||
"file": "WoodenBin_v2.glb",
|
||||
"dims": [
|
||||
0.35,
|
||||
0.36,
|
||||
0.31
|
||||
],
|
||||
"sane": true
|
||||
}
|
||||
]
|
||||
}
|
||||
1409
tools/blender/build_yard_assets.py
Normal file
1409
tools/blender/build_yard_assets.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
tools/blender/contact_sheet.png
Normal file
BIN
tools/blender/contact_sheet.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
BIN
web/world/models/carabiner_v1.glb
Normal file
BIN
web/world/models/carabiner_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/debris/BlackTub_v2.glb
Normal file
BIN
web/world/models/debris/BlackTub_v2.glb
Normal file
Binary file not shown.
BIN
web/world/models/debris/BlueCrate_v2.glb
Normal file
BIN
web/world/models/debris/BlueCrate_v2.glb
Normal file
Binary file not shown.
BIN
web/world/models/debris/WhiteTub_v2.glb
Normal file
BIN
web/world/models/debris/WhiteTub_v2.glb
Normal file
Binary file not shown.
BIN
web/world/models/debris/WoodenBin_v2.glb
Normal file
BIN
web/world/models/debris/WoodenBin_v2.glb
Normal file
Binary file not shown.
BIN
web/world/models/debris/tramp_01_v1.glb
Normal file
BIN
web/world/models/debris/tramp_01_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/fence_panel_v1.glb
Normal file
BIN
web/world/models/fence_panel_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/fence_post_v1.glb
Normal file
BIN
web/world/models/fence_post_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/garden_bed_v1.glb
Normal file
BIN
web/world/models/garden_bed_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/gate_v1.glb
Normal file
BIN
web/world/models/gate_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/house_yardside_v1.glb
Normal file
BIN
web/world/models/house_yardside_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/ladder_01_v1.glb
Normal file
BIN
web/world/models/ladder_01_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/ref_capsule_v1.glb
Normal file
BIN
web/world/models/ref_capsule_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/sail_post_v1.glb
Normal file
BIN
web/world/models/sail_post_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/shackle_v1.glb
Normal file
BIN
web/world/models/shackle_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/shed_01_v1.glb
Normal file
BIN
web/world/models/shed_01_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/shed_table_v1.glb
Normal file
BIN
web/world/models/shed_table_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/textures/grass_atlas.png
Normal file
BIN
web/world/models/textures/grass_atlas.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
web/world/models/tree_gum_01_v1.glb
Normal file
BIN
web/world/models/tree_gum_01_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/tree_gum_02_v1.glb
Normal file
BIN
web/world/models/tree_gum_02_v1.glb
Normal file
Binary file not shown.
BIN
web/world/models/turnbuckle_v1.glb
Normal file
BIN
web/world/models/turnbuckle_v1.glb
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user