- blight-lang: TextResolver + EN/DE Sprachpakete (TextReference i18n) - AnimSet: Clips + ActionMap in .animset.json zusammengeführt - EZ-Tree: Branch-Parameter-Fixes (length/radius/children/force nicht senden, twist Grad→Radiant, leaves.size ×5); Ordner-ComboBox mit Auto-Refresh - Logging beim Baum-Export in allen drei Generatoren (EZ-Tree, Blight, Palme) - Atmosphäre-Tools: Emitter, Licht, Wasser, Sound-/Musikbereiche, Spiel-Starten - AnimPreviewState, RetargetingSystem, AnimationLibrary (Animations-Editor) - Terrain-Transparenz-Fix, Schatten-Fix, ThirdPersonCamera-Fix - DayNightState, WeatherState, CloudsNode, JmeConsole - MapIO v6, neue blight-common Modell-Klassen (GameCharacter, NPC, Quests…) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
124 lines
4.3 KiB
JavaScript
124 lines
4.3 KiB
JavaScript
/**
|
|
* Headless ez-tree geometry generator.
|
|
* Usage: node --require ./dom_polyfill.cjs ez_tree_generate.mjs '<JSON>'
|
|
*
|
|
* Input JSON: { preset: "Oak Medium", params: { seed, type, bark, branch, leaves, trellis } }
|
|
* Output JSON: { branches: { positions, normals, uvs, indices }, leaves: { ... } }
|
|
*/
|
|
|
|
import { Tree, TreePreset } from '@dgreenheck/ez-tree';
|
|
|
|
const input = JSON.parse(process.argv[2] ?? '{}');
|
|
const presetName = input.preset ?? 'Oak Medium';
|
|
const params = input.params ?? {};
|
|
|
|
if (!TreePreset[presetName]) {
|
|
process.stderr.write('Unknown preset: ' + presetName + '\n');
|
|
process.exit(1);
|
|
}
|
|
|
|
const options = structuredClone(TreePreset[presetName]);
|
|
applyParams(options, params);
|
|
|
|
const tree = new Tree();
|
|
tree.options.copy(options);
|
|
|
|
try {
|
|
tree.generate();
|
|
} catch (e) {
|
|
// generate() may fail on DOM APIs for materials, but geometry arrays are
|
|
// already populated at that point — continue.
|
|
}
|
|
|
|
const branches = tree.branches;
|
|
const leaves = tree.leaves;
|
|
|
|
if (!branches || !leaves) {
|
|
process.stderr.write('Generation produced no geometry\n');
|
|
process.exit(1);
|
|
}
|
|
|
|
const leafNormals = leaves.normals?.length > 0
|
|
? leaves.normals
|
|
: computeFlatNormals(leaves.verts, leaves.indices);
|
|
|
|
const result = {
|
|
branches: {
|
|
positions: Array.from(branches.verts),
|
|
normals: Array.from(branches.normals),
|
|
uvs: Array.from(branches.uvs),
|
|
indices: Array.from(branches.indices),
|
|
},
|
|
leaves: {
|
|
positions: Array.from(leaves.verts),
|
|
normals: Array.from(leafNormals),
|
|
uvs: Array.from(leaves.uvs),
|
|
indices: Array.from(leaves.indices),
|
|
},
|
|
};
|
|
|
|
process.stdout.write(JSON.stringify(result));
|
|
|
|
// ── Parameter-Overrides ────────────────────────────────────────────────────
|
|
|
|
function applyParams(options, params) {
|
|
if (params.seed !== undefined) options.seed = params.seed;
|
|
if (params.type !== undefined) options.type = params.type;
|
|
|
|
if (params.bark) {
|
|
const b = params.bark;
|
|
if (b.tint !== undefined) options.bark.tint = b.tint;
|
|
if (b.flatShading !== undefined) options.bark.flatShading = b.flatShading;
|
|
if (b.textureScale) {
|
|
if (b.textureScale.x !== undefined) options.bark.textureScale.x = b.textureScale.x;
|
|
if (b.textureScale.y !== undefined) options.bark.textureScale.y = b.textureScale.y;
|
|
}
|
|
}
|
|
|
|
if (params.branch) {
|
|
const br = params.branch;
|
|
if (br.levels !== undefined) options.branch.levels = br.levels;
|
|
if (br.force) {
|
|
if (br.force.strength !== undefined)
|
|
options.branch.force.strength = br.force.strength;
|
|
if (br.force.direction) {
|
|
if (br.force.direction.x !== undefined) options.branch.force.direction.x = br.force.direction.x;
|
|
if (br.force.direction.y !== undefined) options.branch.force.direction.y = br.force.direction.y;
|
|
if (br.force.direction.z !== undefined) options.branch.force.direction.z = br.force.direction.z;
|
|
}
|
|
}
|
|
// Per-level maps: replace entirely when provided (Java sends the full map)
|
|
for (const key of ['angle','children','gnarliness','length','radius',
|
|
'sections','segments','start','taper','twist']) {
|
|
if (br[key] !== undefined) options.branch[key] = br[key];
|
|
}
|
|
}
|
|
|
|
if (params.leaves) {
|
|
Object.assign(options.leaves, params.leaves);
|
|
}
|
|
|
|
if (params.trellis) {
|
|
Object.assign(options.trellis, params.trellis);
|
|
}
|
|
}
|
|
|
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
|
|
function computeFlatNormals(verts, indices) {
|
|
const normals = new Float32Array(verts.length);
|
|
for (let i = 0; i < indices.length; i += 3) {
|
|
const ia = indices[i] * 3, ib = indices[i + 1] * 3, ic = indices[i + 2] * 3;
|
|
const ax = verts[ib] - verts[ia], ay = verts[ib+1] - verts[ia+1], az = verts[ib+2] - verts[ia+2];
|
|
const bx = verts[ic] - verts[ia], by = verts[ic+1] - verts[ia+1], bz = verts[ic+2] - verts[ia+2];
|
|
const nx = ay*bz - az*by, ny = az*bx - ax*bz, nz = ax*by - ay*bx;
|
|
const len = Math.sqrt(nx*nx + ny*ny + nz*nz) || 1;
|
|
for (const idx of [ia, ib, ic]) {
|
|
normals[idx] += nx/len;
|
|
normals[idx+1] += ny/len;
|
|
normals[idx+2] += nz/len;
|
|
}
|
|
}
|
|
return Array.from(normals);
|
|
}
|