fix: replace Box3 auto-scale with measured modelLength per species
- 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
This commit is contained in:
+26
-25
@@ -60,39 +60,40 @@ function getSpeciesUrl(sp: FishSpecies): string {
|
||||
};
|
||||
return urls[sp];
|
||||
}
|
||||
// Per-species correction factor between Box3.setFromObject's measured extent
|
||||
// and the model's true rendered extent (see FishSpec.baseScale). Box3 measures
|
||||
// skinned geometry in skeleton space (and may use a stale cached bounding box),
|
||||
// which does not match the GPU's actual output (mesh.matrixWorld * skinning *
|
||||
// position). baseScale = rendered / box3, so `targetLength / (box3 * baseScale)`
|
||||
// normalizes each fish to its real rendered size.
|
||||
//
|
||||
// Measured with three.js's exact skinning formula per GLB. Most models are 1.0.
|
||||
// The skinned blendswap oscar/knifefish deviate and are corrected here. 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
|
||||
// until the GLB is re-exported as a clean fish.
|
||||
const SPECIES_SCALE: Record<FishSpecies, number> = {
|
||||
angelfish: 1.0,
|
||||
goldfish: 1.0,
|
||||
guppy: 1.0,
|
||||
betta: 1.0,
|
||||
blue_tang: 1.0,
|
||||
flower_horn: 1.0,
|
||||
neon_tetra: 1.0,
|
||||
oscar: 1.35,
|
||||
knifefish: 2.58,
|
||||
clownfish: 1.0,
|
||||
// Per-species true longest rendered extent (model units), measured
|
||||
// offline with scripts/measure-models.mjs (stride-safe rest-pose +
|
||||
// swim-clip evaluation of the actual GLB data). Fish are scaled by
|
||||
// targetLength / modelLength. This replaces the old
|
||||
// Box3.setFromObject auto-measure, which under-reports armature-scaled
|
||||
// models (oscar, knifefish) and for the clownfish is dominated by its
|
||||
// 45.3-unit water plane (see FishSpec.modelLength).
|
||||
const SPECIES_LENGTH: Record<FishSpecies, number> = {
|
||||
angelfish: 4.2139,
|
||||
goldfish: 1.7386,
|
||||
guppy: 2.8070,
|
||||
betta: 1.0228,
|
||||
blue_tang: 1.0192,
|
||||
flower_horn: 1.0516,
|
||||
neon_tetra: 3.6548,
|
||||
oscar: 8.8411,
|
||||
knifefish: 23.0140,
|
||||
clownfish: 8.2773,
|
||||
};
|
||||
// The clownfish GLB bundles a whole aquarium scene: an untextured white
|
||||
// "Surface" water plane (45.3 units — the "white box"), a "TopLight"
|
||||
// emissive slab, a "Bubbles" prop, and "Original-Mirrored", a mirrored
|
||||
// duplicate of the body that overlaps the real "ClownFish" node. Hide
|
||||
// everything except the fish itself.
|
||||
const HIDDEN_CLOWNFISH_NODES = ["Surface", "TopLight", "Bubbles", "Original-Mirrored"];
|
||||
function fishToSpec(f: FishData): FishSpec {
|
||||
return {
|
||||
url: getSpeciesUrl(f.species),
|
||||
targetLength: f.genetics.sizeMult * 0.5,
|
||||
modelLength: SPECIES_LENGTH[f.species],
|
||||
speed: [0.3 * f.genetics.speedMult, 0.7 * f.genetics.speedMult],
|
||||
orient: isQuaternius(f.species) ? { y: Math.PI / 2 } : undefined,
|
||||
genetics: f.genetics,
|
||||
baseScale: SPECIES_SCALE[f.species],
|
||||
hideNodes: f.species === "clownfish" ? HIDDEN_CLOWNFISH_NODES : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user