Repository navigation
logic parts
Parses .bbmodel elements / a tab's selected elements into a PartModel, normalized so the bottom face
is at y = 0 and the model's symmetry point at (0,0). Pure functions, Node-testable.
Symmetry point by model format: Java Block/Item (java_block / java_item) → (8,8) (canvas
0..16, origin at a corner); other formats (generic/free) → (0,0) (origin is the canvas center). This
lets Java-mode parts grow within the 0..16 canvas without the "symmetric about zero" limit.
// element rotation: .bbmodel array form [rx,ry,rz] or legacy object form
export type ElementRotation =
| [number, number, number]
| { angle?: number; axis?: 'x' | 'y' | 'z'; origin?: [number, number, number] };
// a volume element (type='cube' or default)
export interface RawCubeElement {
name?: string;
type?: 'cube';
from: [number, number, number];
to: [number, number, number];
rotation?: ElementRotation;
origin?: [number, number, number]; // array-form rotation: origin is a sibling field of rotation
faces?: Partial<Record<CubeFaceDirection, { uv?: [number, number, number, number]; rotation?: number; texture?: string | number }>>;
}
// a mesh element (type='mesh'): vertex table + face table; face texture is the texture-array index
export interface RawMeshElement {
name?: string;
type: 'mesh';
vertices?: Record<string, [number, number, number]>;
faces?: Record<string, { vertices?: string[]; uv?: number[] | Record<string, any>; rotation?: number; texture?: string | number }>;
origin?: [number, number, number];
rotation?: [number, number, number];
}
export type RawElement = RawCubeElement | RawMeshElement;
// an entry of the .bbmodel textures array
export interface RawTexture {
name?: string;
id?: string | number; // texture id (referenced from faces via the `texture` field)
source?: string; // base64 data URL or desktop file path
uv_width?: number;
uv_height?: number;
}
// top-level .bbmodel JSON structure
export interface RawBbModel {
meta?: { model_format?: string; texture_size?: [number, number] };
resolution?: { width?: number; height?: number }; // Blockbench 5 model resolution (texture size)
elements?: RawElement[];
textures?: RawTexture[];
}export function isMeshElement(el: RawElement): el is RawMeshElement;
export function symmetryPointForFormat(format: string | undefined): Vec3; // (8,8) / (0,0)
export function outputOffsetForFormat(format: string | undefined): Vec3; // inverse of the above (build-time translation)
export function examplePartBox(kind: 'rail' | 'tie', format?: string): { from: Vec3; to: Vec3 }; // the Generate Example Rail/Tie box// single element → CubeSpec; supports both rotation forms (array + object)
export function elementToCubeSpec(el: RawElement): CubeSpec;
// cube elements only → CubeSpec[] (meshes skipped)
export function elementsToCubeSpecs(elements: RawElement[]): CubeSpec[];
// mesh groups → MeshSpec[]; face texture refs normalized to string keys; origin/rotation baked into
// vertices and cleared (otherwise a later translate would double-shift non-zero-origin meshes)
export function extractMeshes(elements: RawElement[]): MeshSpec[];export function computeBBox(cubes: CubeSpec[]): { min: Vec3; max: Vec3 };
export function computeMeshBBox(meshes: MeshSpec[]): { min: Vec3; max: Vec3 };
export function partBBox(cubes: CubeSpec[], meshes: MeshSpec[] = []): { min: Vec3; max: Vec3 };normalize shifts the bottom face to y=0 and the symmetry point to (0,0); mesh vertices/origin move by
the same offset, keeping cube↔mesh relative positions. Returns new CubeSpecs (input not mutated).
export function normalize(cubes: CubeSpec[], symmetry?: Vec3, meshes: MeshSpec[] = []): PartModel;.bbmodel faces reference textures via the array index (Blockbench loader Texture.all[face.texture]),
not the texture id, so source texture keys are set to String(index) to align with the face references.
Resolution priority: resolution → meta.texture_size → the shared uv size → undefined.
export function parseBbTextures(
json: RawBbModel
): { textureSize?: [number, number]; textures: SourceTexture[] };
// all parts (left/right/tie) must share the same texture resolution; returns it or null (reject)
export function consistentTextureSize(parts: { textureSize?: [number, number] }[]): [number, number] | null;
// prefixes a part's texture keys (L/0, R/0, T/0) so the three parts don't collide in the
// "source key → imported Texture" map (every part's refs start at index 0). Mutates and returns `part`.
export function scopeTextureKeys(part: PartModel, prefix: string): PartModel;// any part with mesh groups → 'free' (the only format that can hold meshes); all cubes → Java block/item
export function targetFormatForParts(parts: { hasMesh?: boolean }[], currentFormat?: string): string;
export function parseBbModel(json: RawBbModel, format?: string, perTextureUv?: boolean): PartModel;
export function extractFromElements(elements: RawElement[], format?: string): PartModel;