most entitys working backup

This commit is contained in:
2026-08-04 13:26:54 -05:00
parent 10e4dffe1e
commit 97ff275467
3 changed files with 213 additions and 6 deletions
+4 -2
View File
@@ -185,7 +185,9 @@
var bone = bones[name];
if (!bone) return;
var pivot = bone.pivot || [0, 0, 0];
var rot = (bone.rotation || [0, 0, 0]).map(function (d) { return d * DEG; });
// Bedrock/GeckoLib rotations are opposite the Three.js default
// direction, so negate the Euler angles (identity models unaffected).
var rot = (bone.rotation || [0, 0, 0]).map(function (d) { return -d * DEG; });
var local = multiply(
translation(pivot[0], pivot[1], pivot[2]),
multiply(
@@ -199,7 +201,7 @@
(bone.cubes || []).forEach(function (c) {
var origin = c.origin || [0, 0, 0];
var size = c.size || [1, 1, 1];
var cRot = (c.rotation || [0, 0, 0]).map(function (d) { return d * DEG; });
var cRot = (c.rotation || [0, 0, 0]).map(function (d) { return -d * DEG; });
var cubeWorld = multiply(
world,
multiply(
+172
View File
@@ -0,0 +1,172 @@
{
"afw_bone_textures": {
"cylinder": "needsofnature:textures/entity/wolf/wolf_features.png",
"bulb": "needsofnature:textures/entity/wolf/wolf_features.png"
},
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.unknown",
"texture_width": 64,
"texture_height": 32,
"visible_bounds_width": 3,
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
"name": "root",
"pivot": [0, 0, 0]
},
{
"name": "wolf",
"parent": "root",
"pivot": [-1, 10.5, -7]
},
{
"name": "frontbody",
"parent": "wolf",
"pivot": [-1, 10.5, -7]
},
{
"name": "head",
"parent": "frontbody",
"pivot": [-1, 10.5, -7],
"cubes": [
{"origin": [-4, 7.5, -9], "size": [6, 6, 4], "uv": [0, 0]},
{"origin": [-4, 13.5, -7], "size": [2, 2, 1], "uv": [16, 14]},
{"origin": [0, 13.5, -7], "size": [2, 2, 1], "uv": [16, 14]},
{"origin": [-2.5, 7.51563, -12], "size": [3, 3, 4], "uv": [0, 10]}
]
},
{
"name": "mane",
"parent": "frontbody",
"pivot": [-1, 10, 2],
"rotation": [90, 0, 0],
"cubes": [
{"origin": [-5, 12, -1], "size": [8, 6, 7], "uv": [21, 0]}
]
},
{
"name": "leg3",
"parent": "frontbody",
"pivot": [-2.5, 8, -4],
"cubes": [
{"origin": [-3.5, 0, -5], "size": [2, 8, 2], "uv": [0, 18]}
]
},
{
"name": "leg4",
"parent": "frontbody",
"pivot": [0.5, 8, -4],
"cubes": [
{"origin": [-0.5, 0, -5], "size": [2, 8, 2], "uv": [0, 18]}
]
},
{
"name": "body",
"parent": "frontbody",
"pivot": [0, 10, 2],
"rotation": [90, 0, 0],
"cubes": [
{"origin": [-4, 3, -1], "size": [6, 9, 6], "uv": [18, 14]}
]
},
{
"name": "d",
"parent": "body",
"pivot": [-1, 5, -0.2],
"rotation": [30.36119, -40.78947, -20.94102],
"cubes": [
{"origin": [-2, 4, -1.2], "size": [2, 3, 2], "uv": [20, 22]}
]
},
{
"name": "cylinder",
"parent": "d",
"pivot": [-1.48996, 6.28701, -0.48996],
"cubes": [
{
"origin": [-1.98996, 6.28701, -1.18996],
"size": [1, 5, 1],
"inflate": -0.1,
"uv": {
"north": {"uv": [52, 9], "uv_size": [3, 15]},
"east": {"uv": [49, 9], "uv_size": [3, 15]},
"south": {"uv": [58, 9], "uv_size": [3, 15]},
"west": {"uv": [55, 9], "uv_size": [3, 15]},
"up": {"uv": [52, 6], "uv_size": [3, 3]},
"down": {"uv": [55, 9], "uv_size": [3, -3]}
}
},
{
"origin": [-2.08996, 10.68701, -1.28996],
"size": [1, 1, 1],
"inflate": -0.3,
"uv": {
"north": {"uv": [54, 4], "uv_size": [2, 2]},
"east": {"uv": [52, 4], "uv_size": [2, 2]},
"south": {"uv": [58, 4], "uv_size": [2, 2]},
"west": {"uv": [56, 4], "uv_size": [2, 2]},
"up": {"uv": [54, 2], "uv_size": [2, 2]},
"down": {"uv": [56, 4], "uv_size": [2, -2]}
}
}
]
},
{
"name": "bulb",
"parent": "cylinder",
"pivot": [-1.51197, 8.36355, -0.68726],
"rotation": [0, -47.5, 0],
"cubes": [
{
"origin": [-2.26197, 7.86355, -1.73726],
"size": [1.25, 1, 2.1],
"uv": {
"north": {"uv": [52, 28], "uv_size": [3, 2]},
"east": {"uv": [48, 28], "uv_size": [4, 2]},
"south": {"uv": [59, 28], "uv_size": [3, 2]},
"west": {"uv": [55, 28], "uv_size": [4, 2]},
"up": {"uv": [52, 24], "uv_size": [3, 4]},
"down": {"uv": [55, 28], "uv_size": [3, -4]}
}
}
]
},
{
"name": "backbody",
"parent": "wolf",
"pivot": [0, 10, 2]
},
{
"name": "leg1",
"parent": "backbody",
"pivot": [-2.5, 8, 7],
"cubes": [
{"origin": [-3.5, 0, 6], "size": [2, 8, 2], "uv": [0, 18]}
]
},
{
"name": "leg2",
"parent": "backbody",
"pivot": [0.5, 8, 7],
"cubes": [
{"origin": [-0.5, 0, 6], "size": [2, 8, 2], "uv": [0, 18]}
]
},
{
"name": "tail",
"parent": "backbody",
"pivot": [-1, 12, 8],
"rotation": [55, 0, 0],
"cubes": [
{"origin": [-2, 4, 7], "size": [2, 8, 2], "uv": [9, 18]}
]
}
]
}
]
}
+37 -4
View File
@@ -83,7 +83,8 @@ assert(allInRange, 'all UVs within [0,1]');
// Unmapped bones default to the pack's main texture.
assert(headCube.texture === 'needsofnature:textures/entity/zombie/zombie.png', 'head uses default texture');
// ---------- 2. Synthetic rotated bone: Rz(90°) around pivot (0,10,0) ----------
// ---------- 2. Synthetic rotated bone: Rz(-90°) around pivot (0,10,0) ----------
// Rotations are negated to match the Bedrock/GeckoLib direction.
const rotated = {
'minecraft:geometry': [{
description: { identifier: 'geometry.t', texture_width: 64, texture_height: 64 },
@@ -95,10 +96,10 @@ const rotated = {
};
const rotBuilt = GeoBuilder.build(rotated);
assert(rotBuilt.cubes.length === 1, 'rotated model has one cube');
// cube center = origin + size/2 = (1,11,1); Rz90 around pivot (0,10,0):
// rel (1,1,1) -> (-1,1,1) -> +pivot = (-1,11,1)
// cube center = origin + size/2 = (1,11,1); Rz(-90) around pivot (0,10,0):
// rel (1,1,1) -> (1,-1,1) -> +pivot = (1,9,1)
const rotCenter = worldCenter(rotBuilt.cubes[0].matrix);
assertVec(rotCenter, [-1, 11, 1], 'rotated cube center around pivot (expect (-1,11,1))');
assertVec(rotCenter, [1, 9, 1], 'rotated cube center around pivot (expect (1,9,1))');
// ---------- 3. Nested pivot with rotation ----------
const nested = {
@@ -115,6 +116,38 @@ const nested = {
const nestedBuilt = GeoBuilder.build(nested);
assertVec(worldCenter(nestedBuilt.cubes[0].matrix), [0, 28, 0], 'nested pivot head center (expect (0,28,0))');
// ---------- 4. Real wolf model: rotated bones must produce a quadruped layout ----------
const wolf = require(path.join(__dirname, 'fixtures', 'wolf.geo.json'));
const wolfBuilt = GeoBuilder.build(wolf);
const wolfCenters = wolfBuilt.cubes.map(c => ({ c, center: worldCenter(c.matrix) }));
const wBody = wolfCenters.find(({ c }) => c.size[0] === 8 && c.size[1] === 6 && c.size[2] === 7);
assert(!!wBody, 'wolf body cube found');
if (wBody) {
// Body high (y~10.5) and in the front half (z<2). Under the old rotation
// sign the body landed at the back (z=7) — this catches the regression.
assertVec(wBody.center, [-1, 10.5, -3], 'wolf body center (expect (-1,10.5,-3))');
}
const wHead = wolfCenters.find(({ c }) => c.size[0] === 6 && c.size[1] === 6 && c.size[2] === 4);
assert(!!wHead, 'wolf head cube found');
if (wHead) {
assertVec(wHead.center, [-1, 10.5, -7], 'wolf head center (expect (-1,10.5,-7))');
}
// 4 legs (size 2x8x2 cubes whose y is near the ground).
const wLegs = wolfCenters.filter(({ c, center }) => c.size[0] === 2 && c.size[1] === 8 && c.size[2] === 2 && center[1] < 6);
assert(wLegs.length === 4, `wolf has 4 legs near the ground (got ${wLegs.length})`);
wLegs.forEach(({ center }) => assert(approx(center[1], 4.0, 0.2), `leg at ground level y=${center[1].toFixed(1)}`));
// Tail: the cube with the largest |z|, behind the body.
let wTail = wolfCenters[0];
for (const w of wolfCenters) {
if (Math.abs(w.center[2]) > Math.abs(wTail.center[2])) wTail = w;
}
assert(wTail.center[2] > 8, `wolf tail behind body (z=${wTail.center[2].toFixed(1)})`);
assert(wBody && wTail.center[2] > wBody.center[2], 'tail z > body z');
// ---------- summary ----------
if (failures === 0) {
console.log(`geo_builder tests OK (${checks} checks)`);