@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
272 lines (271 loc) • 11.1 kB
TypeScript
/**
* ModelGeometryUtilities
*
* Shared utility methods for working with Minecraft Bedrock geometry models.
* Contains static methods for:
* - Cube bounding box calculations
* - Bone transform accumulation
* - Point rotation around pivots
* - UV coordinate calculations
* - Depth sorting for rendering
* - Orthographic and isometric 2D projection
*
* These utilities are used by:
* - ModelMeshFactory (3D BabylonJS rendering)
* - Model2DRenderer (2D SVG rendering)
* - BlockbenchModel (format conversion)
*
* COORDINATE SYSTEMS:
* -------------------
* Minecraft Bedrock Edition:
* - Right-handed coordinate system
* - +Y is up, +Z is forward (towards the viewer in default orientation), +X is right
* - Units are in 1/16ths of a block (so divide by 16 for block units)
*
* For 2D rendering (front view):
* - X maps to screen X (positive = right)
* - Y maps to screen Y (positive = up, but SVG Y is inverted)
* - Z is depth (positive = towards viewer, used for sorting)
*
* VIEW DIRECTIONS:
* ----------------
* Orthographic (axis-aligned, shows 1 face):
* - front, back, left, right, top, bottom
*
* Isometric (3D-like, shows 3 faces):
* - iso-front-right: north + east + up faces (classic Minecraft inventory style)
* - iso-front-left: north + west + up faces
* - iso-back-right: south + east + up faces
* - iso-back-left: south + west + up faces
*
* Last Updated: December 2025
*/
import { IGeometry, IGeometryBone, IGeometryBoneCube } from "./IModelGeometry";
/**
* Represents accumulated bone transform including parent transforms
*/
export interface IBoneTransform {
pivot: number[];
rotation: number[];
parentName?: string;
}
/**
* Represents a projected 2D rectangle from a cube face
*/
export interface IProjectedFace {
/** Screen X coordinate of bounding box top-left corner */
x: number;
/** Screen Y coordinate of bounding box top-left corner */
y: number;
/** Width in screen units (bounding box) */
width: number;
/** Height in screen units (bounding box) */
height: number;
/** Depth for z-ordering (larger = closer to viewer) - uses maxDepth by default for painter's algorithm */
depth: number;
/** Minimum depth (closest vertex to camera) */
minDepth: number;
/** Maximum depth (furthest vertex from camera) */
maxDepth: number;
/** Average depth across all vertices */
avgDepth: number;
/** Original cube reference */
cube: IGeometryBoneCube;
/** Bone that contains this cube */
bone: IGeometryBone;
/** Face name (north, south, east, west, up, down) */
face: string;
/** UV coordinates for texture sampling [u, v, width, height] */
uv: number[];
/**
* Projected corner vertices for isometric views.
* Order: bottom-left, bottom-right, top-right, top-left (counter-clockwise)
* Each point is {x, y}. Present only for isometric projections.
*/
vertices?: {
x: number;
y: number;
}[];
}
/**
* Represents a 3D axis-aligned bounding box
*/
export interface IBoundingBox {
minX: number;
maxX: number;
minY: number;
maxY: number;
minZ: number;
maxZ: number;
}
/**
* View direction for 2D projection.
* Standard views: front, back, left, right, top, bottom (orthographic axis-aligned)
* Isometric views: iso-front-right, iso-front-left, iso-back-right, iso-back-left
* These provide a 3D-like view showing 3 faces at once (classic Minecraft inventory style)
*/
export type ViewDirection = "front" | "back" | "left" | "right" | "top" | "bottom" | "iso-front-right" | "iso-front-left" | "iso-back-right" | "iso-back-left";
/**
* Check if a view direction is isometric (shows 3 faces at once)
*/
export declare function isIsometricView(direction: ViewDirection): boolean;
export default class ModelGeometryUtilities {
/**
* Calculate the center point of a cube in world coordinates.
* Cube origin is the minimum corner, so center = origin + size/2.
*/
static getCubeCenter(cube: IGeometryBoneCube): number[];
/**
* Calculate the 8 corner vertices of a cube in world coordinates.
* Returns array of [x, y, z] for each corner.
*/
static getCubeVertices(cube: IGeometryBoneCube): number[][];
/**
* Get the bounding box of a cube (axis-aligned, before rotation).
*/
static getCubeBoundingBox(cube: IGeometryBoneCube): IBoundingBox;
/**
* Get the bounding box of an entire geometry model.
*/
static getGeometryBoundingBox(geometry: IGeometry): IBoundingBox;
/**
* Check if a rotation array has any non-zero rotation.
*/
static hasRotation(rotation: number[] | undefined): boolean;
/**
* Build a map of bone transforms for a geometry.
* Includes bind_pose_rotation and parent relationships.
*/
static buildBoneTransformMap(geometry: IGeometry): Map<string, IBoneTransform>;
/**
* Rotate a point around a pivot by the given rotation (in degrees).
* Applies rotations in Minecraft order: X, then Y, then Z.
*/
static rotatePointAroundPivot(point: number[], pivot: number[], rotationDegrees: number[]): number[];
/**
* Calculate the normal vector of a face from its rotated corner vertices.
* Uses cross product of two edges to get the outward-facing normal.
*
* @param corners Array of 4 corner points [[x,y,z], ...] in counter-clockwise order
* @returns Normalized face normal [nx, ny, nz]
*/
static calculateFaceNormal(corners: number[][]): number[];
/**
* Get the camera/view direction vector for a given view direction.
* This is the direction FROM the camera TO the scene (opposite of camera facing).
*
* For orthographic/isometric rendering, we use a parallel projection, so
* the view direction is constant (not dependent on position).
*/
static getViewDirectionVector(viewDirection: ViewDirection): number[];
/**
* Check if a face should be visible (not backface-culled) given the view direction.
*
* @param faceNormal The normal vector of the face after rotations
* @param viewDirection The current view direction
* @returns true if the face is visible (facing the camera), false if backface-culled
*/
static isFaceVisible(faceNormal: number[], viewDirection: ViewDirection): boolean;
/**
* Calculate UV coordinates for a specific face of a cube.
* Handles both legacy [u, v] format and per-face UV format.
*
* @returns [u, v, width, height] in texture pixels
*/
static getCubeFaceUV(cube: IGeometryBoneCube, face: string, texWidth: number, texHeight: number): {
u: number;
v: number;
width: number;
height: number;
};
/**
* Get the faces that would be visible from a given view direction.
* Returns the face names that should be rendered.
*
* In Minecraft, entities face north (-Z) by default. So:
* - "front" view = looking at their face = we see the "north" face
* - "back" view = looking at their back = we see the "south" face
*
* For isometric views, we see 3 faces at once:
* - "iso-front-right" = north + east + up (viewing from the front-right above)
* - "iso-front-left" = north + west + up (viewing from the front-left above)
* - "iso-back-right" = south + east + up (viewing from the back-right above)
* - "iso-back-left" = south + west + up (viewing from the back-left above)
*/
static getVisibleFaces(viewDirection: ViewDirection): string[];
/**
* Get the secondary faces that would be partially visible from a given view.
* Used for isometric or 3/4 view rendering with depth effects.
* For isometric views, all 3 primary faces are already in getVisibleFaces, so no secondary.
*/
static getSecondaryVisibleFaces(viewDirection: ViewDirection): string[];
/**
* Options for perspective projection in projectPoint.
*/
static perspectiveOptions: {
enabled: boolean;
strength: number;
focalLength: number;
/** Reference depth - points at this depth have no perspective distortion */
referenceDepth: number;
};
/**
* Center offset for isometric rotation.
* When set, isometric rotation is applied around this center point instead of the world origin.
* This ensures models with large Z offsets (like cow.v2) render correctly in isometric views.
* Set to null to disable (rotate around world origin).
*/
static isometricCenterOffset: number[] | null;
/**
* Apply isometric rotation to a 3D point.
* For classic isometric view: rotate around Y-axis, then ~30° around X-axis.
* This produces the familiar Minecraft inventory-style 3D view.
*
* Rotation angles for each view (entity faces north/-Z by default):
* - iso-front-right: -135° (see north + east + up)
* - iso-front-left: +135° (see north + west + up)
* - iso-back-right: -45° (see south + east + up)
* - iso-back-left: +45° (see south + west + up)
*
* @param point 3D point [x, y, z]
* @param yRotation Y-axis rotation in degrees
* @param centerOffset Optional 3D offset to subtract before rotation (for centering)
* @returns Rotated point [x, y, z]
*/
static applyIsometricRotation(point: number[], yRotation: number, centerOffset?: number[]): number[];
/**
* Project a 3D point to 2D screen coordinates.
* When perspectiveOptions.enabled is true, applies perspective projection
* so that points farther from the camera converge toward the center.
*
* @param point 3D point [x, y, z]
* @param viewDirection View direction
* @param scale Scale multiplier
* @returns {x, y, depth} where x/y are screen coordinates and depth is for z-ordering
*/
static projectPoint(point: number[], viewDirection: ViewDirection, scale?: number): {
x: number;
y: number;
depth: number;
};
/**
* Get projected 2D rectangle for a cube face.
* This is the core projection algorithm for 2D rendering.
*/
static projectCubeFace(cube: IGeometryBoneCube, bone: IGeometryBone, face: string, viewDirection: ViewDirection, boneTransforms: Map<string, IBoneTransform>, scale?: number): IProjectedFace | null;
/**
* Get all visible faces from a geometry for a given view direction.
* Sorted by depth (back to front) for proper occlusion.
*/
static getProjectedFaces(geometry: IGeometry, viewDirection: ViewDirection, scale?: number, includeSecondary?: boolean): IProjectedFace[];
/**
* Calculate the normalized UV coordinates for a face.
* Returns coordinates in range [0, 1].
*/
static getNormalizedUV(u: number, v: number, width: number, height: number, texWidth: number, texHeight: number): {
uMin: number;
vMin: number;
uMax: number;
vMax: number;
};
}