- Remove unreliable Box3.setFromObject scaling (broke for skinned meshes,
control rigs, and scene-containing GLBs)
- Use measured vertex extents (scripts/measure-models.mjs) as modelLength
- Clownfish: hide Surface/TopLight/Bubbles/Mirrored nodes (GLB is a full
scene, not just a fish) — eliminates the white box + giant fish bug
- All 10 species now have accurate scale factors
Box3.setFromObject measures skinned geometry in skeleton space (and may
use a stale cached bounding box), which does not match the GPU's actual
rendered extent (mesh.matrixWorld * skinning * position). Add a
per-species baseScale correction factor (rendered / box3) to FishSpec and
fold it into buildFish's scale calc so each GLB normalizes to its true
size.
Measured per GLB with three.js's exact skinning formula: most models
are 1.0. The skinned blendswap oscar (1.35) and knifefish (2.58) deviate
and are corrected. The clownfish GLB is a full scene (water 'Surface'
45.3, 'TopLight' 15, 'Bubbles' 3.4) plus a small skinned body (~8.3);
box3 is dominated by the water plane, so a baseScale can't fix it without
blowing up the scene geometry - left at 1.0 pending a clean re-export.
The three Poly Pizza models (betta, blue_tang, flower_horn) rendered
giant (filling the tank) because each GLB had two 100x-scaled nodes
(Fish_Armature bone-parent + the skinned mesh node). three.js binds
skinned meshes with an identity bindMatrix and uses the GLB's
inverseBindMatrices verbatim, so the 100x node scales inflated the
rendered skinned extent ~100x relative to what Box3.setFromObject
reports. GLTFish's 'scale = targetLength / longest' therefore scaled
~100x too big.
Fix: pure GLB JSON-chunk edit (BIN chunk + all 6 animations preserved)
setting the bone-parent node scale to S=1/base and the mesh node scale
to 1, so Box3 == rendered extent == 1.0 and targetLength directly
controls fish size, matching the working models.
- GLTFish: dynamic addFish/removeFish with fade, model caching, genetics applied
(color hue/saturation shift, rarity emissive glow on fish meshes)
- Buy: spawns fish in 3D scene with random genetics + rarity roll
- Sell: fades out fish, removes from scene, credits money
- Breed: 1-3 babies with blended genetics + mutation, 60s cooldown
- Tank upgrade: resizes bounds, moves camera, updates capacity
- Fish growth: age-based value multiplier (baby/juvenile/adult)
- Full game loop: earn → breed for rarity → sell for profit → upgrade tank
- Game state with localStorage save/load + reset (type DELETE to confirm)
- Alpine.js + Tailwind CSS + Lucide UI overlay (HUD bar, shop modal, settings, toasts)
- Economy: fish prices, tank tier costs/capacities, rarity multipliers, sell value formula
- Genetic system: hue/saturation/size/speed/pattern/rarity/gender + breeding inheritance
- Fish selection via raycaster (click fish = select, click empty = drop food)
- Buy/Sell/Breed/Upgrade actions wired to game state
- Bubbles.ts: rising bubble particles from 2 air stones (near plants),
wobble + growth + fade near surface, 50-mesh pool
- Food.ts: auto-dropping food pellets (every 6-14s, 3-6 pieces) that
float down and settle on the floor; manual drop via click
- GLTFish.ts: fish now notice food within 1.2 units, seek at 1.4x speed
with faster turn rate, eat pellets within 0.15 units; faster tail beat
while chasing
- main.ts: wires food + bubbles into render loop; click drops 4 pellets
- 6 tropical fish (angelfish x2, goldfish x2, guppy x2) from maya's-aquarium-3d
- 4 Quaternius cute fish (Fish1 x2, Fish2, Fish3) with swim animations
- 2 plants + 3 rocks on the gravel floor
- Wandering AI with frame-rate-independent smoothing
- Quaternius fish rotated to match local +X forward axis
- .gitignore: add .env