Skip to content

logic export

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

src/logic/export.ts — Export conventions & serialization

Pure logic mapping the groups under the track parent group into Create/Kuayue model file names and generating the matching blockstates JSON. Zero dependencies, Node-testable.

Export modes (see EXPORT_MODES): new_java (1.21.11+, format_version, multi-axis {x,y,z}), classic_java (1.21.11-, no format_version, single-axis only), bedrock (minecraft:geometry), obj (single merged OBJ mesh). Elements that can't be exported fall back to OBJ (see groupNeedsObj).

Export modes

export type ExportMode = 'new_java' | 'classic_java' | 'bedrock' | 'obj';

// id / label / eligibility description (label + description are i18n-localized)
export const EXPORT_MODES: { id: ExportMode; label: string; description: string }[];

Platform-neutral element descriptors

Extracted by the Blockbench layer from live Cubes/Meshes and handed to the pure functions. Face textures use a stable textureKey (the Blockbench layer maps Texture instances to 't0'/'t1'…).

export interface ExportFaceData {
	uv?: [number, number, number, number];
	rotation?: number;
	textureKey?: string;
}

export interface ExportCubeData {
	name?: string;
	from: Vec3;
	to: Vec3;
	rotation?: Vec3;
	origin?: Vec3;
	faces: Partial<Record<CubeFaceDirection, ExportFaceData>>;
}

export interface ExportMeshFaceData {
	vertices: string[];
	uv?: number[] | Record<string, number[]>;  // per-vertex UV: array (in face.vertices order) or object (by vertex id)
	textureKey?: string;
}

export interface ExportMeshData {
	name?: string;
	vertices: Record<string, Vec3>;
	faces: Record<string, ExportMeshFaceData>;
}

export type ExportElement = ({ type: 'cube' } & ExportCubeData) | ({ type: 'mesh' } & ExportMeshData);

export interface ExportTexture {
	key: string;
	resName: string;
	width: number;
	height: number;
	dataUrl?: string;  // base64 data URL (for writing PNGs; not needed by the pure layer)
}

Eligibility & rotation serialization

// whether a group "cannot be exported" in the given mode and must fall back to OBJ:
// obj mode: everything; any mesh element (triangle faces); classic_java + any multi-axis cube;
// bedrock + >1 texture referenced
export function groupNeedsObj(elements: ExportElement[], mode: ExportMode): boolean;

// element rotation → Java model JSON rotation field: no rotation → undefined (omitted);
// new_java with multi-axis or any angle >45° → {x,y,z,origin}; otherwise single-axis → {angle,axis,origin}
export function rotationToJava(
	rotation: Vec3 | undefined,
	origin: Vec3 | undefined,
	mode: ExportMode
): { angle?: number; axis?: 'x' | 'y' | 'z'; x?: number; y?: number; z?: number; origin: Vec3 } | undefined;

Naming

// track-shape group id → exported model file name; null = not exported separately. z_ortho is not
// exported (blockstates rotate x_ortho 90°); ascending_* exports only the south variant ascending.json;
// teleport_x is not exported; all four crossing models are exported (cross_d1_* = positive diagonal,
// cross_d2_* = negative diagonal; zo = base "diagonal + Z straight", xo = it rotated 90° — diagonal + X),
// matching the reference Kuayue standard assets.
export const TRACK_MODEL_FILES: Record<string, string | null>;

export function cleanGroupName(name: string): string;          // strips the (…)/(…) display suffix
export function modelFileName(id: string): string | null;      // file name for a shape id
export function blockstatesFileName(trackId: string): string;  // {trackId}_track.json

// strips extension, lowercases, non-[a-z0-9_] → _, unique within `used` (appends _1/_2… on collision)
export function textureResourceName(name: string, used: Set<string>): string;

// in-model texture path: {namespace}:{texture resource path}/{resName} (default block/track/{trackId})
export function textureResourcePath(namespace: string, trackId: string, resName: string, texturePath?: string): string;

blockstates

// variants combine shape × turn × waterlogged; shape=none → air model, the rest →
// {namespace}:{model resource path}/{model} (default block/track/{trackId}). shape=zo → x_ortho y:90;
// the four crossing shapes reference the four crossing models directly (no y rotation):
// cr_pdx→cross_d1_xo, cr_pdz→cross_d1_zo, cr_ndx→cross_d2_xo, cr_ndz→cross_d2_zo.
export function buildBlockstates(
	namespace: string,
	trackId: string,
	modelPath?: string
): { variants: Record<string, { model: string; y?: number }> };

Java model JSON

// new_java adds format_version 1.21.11 + multi-axis {x,y,z}; classic_java omits format_version and uses
// single-axis rotation only. Elements must already be eligible cubes (meshes fell back to OBJ); UVs are
// converted from pixels to the 16-unit system.
export function buildJavaModelJson(opts: {
	mode: ExportMode;
	elements: ExportElement[];
	textures: ExportTexture[];
	textureSize: [number, number];
	namespace: string;
	trackId: string;
	texturePathOf?: Record<string, string>;  // texture key → resource directory (default block/track/{trackId})
}): Record<string, unknown>;

OBJ reference JSON

The .obj/.mtl serialization itself lives in the build layer (src/build/export.ts → exportGroupAsObj); this pure function builds the forge:obj reference JSON (model + flip_v + textures), matching the Create/Kuayue examples.

export function buildObjReferenceJson(opts: {
	namespace: string;
	trackId: string;
	shape: string;
	textures: ExportTexture[];
	texturePathOf?: Record<string, string>;
	modelPath?: string;   // model resource path (for blockstate references), default block/track/{trackId}
}): Record<string, unknown>;

Bedrock

// cubes → a minecraft:geometry block model. Following Blockbench o6/r6: cube origin[0] negated (X mirror),
// pivot = rotation origin (X-mirrored) with rx/ry negated when rotated; per-face uv with up/down faces
// uv+=size and size negated. Elements must already be eligible cubes (meshes / multi-texture fell back to OBJ).
export function buildBedrockGeometry(opts: {
	identifier: string;
	elements: ExportElement[];
	textureSize: [number, number];
}): Record<string, unknown>;

// blocks.json (behavior pack root, legacy aggregated format): one block per shape,
// identifier {ns}:{trackId}_{shape}; texturePath is relative to textures/ (written to
// textures/{texturePath}.png → "{texturePath}").
export function buildBedrockBlocksJson(opts: {
	namespace: string;
	trackId: string;
	shapes: { id: string; texturePath: string }[];
}): Record<string, unknown>;

← Back to English API Reference

Clone this wiki locally