Skip to content

logic parts

laurel Lin edited this page Aug 22, 2026 · 3 revisions

src/logic/parts.ts — Part parsing & normalization

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.

Raw element structures

// 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[];
}

Helpers

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

Elements → CubeSpec / MeshSpec

// 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[];

Bounding box

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 };

Normalization

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;

Textures

.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;

Format selection & parse entry points

// 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;

← Back to English API Reference

Clone this wiki locally