Custom objects and global dependencies
THREE, Selectable, transform methods, and persistence and export boundaries for custom plugin objects.
A plugin can create custom geometry and explore the editor’s internals.
Use the stable editorAPI for built-in operations. The global editor
and editor classes provide deeper access, but their implementation may
change between versions.
A visible Three.js object, an editable BDEngine object, and a Minecraft entity have different contracts. Adding a mesh to the scene addresses only the first.
THREE and GLTFExporter
The following globals are available when the plugin system initializes:
| Global value | Purpose |
|---|---|
window.editorAPI |
Public plugin API |
window.editor |
Current editor instance and its internal subsystems |
window.THREE |
Three.js from the editor build |
window.THREE.GLTFExporter |
glTF exporter class added to THREE |
window.Selectable |
Base class for editor objects, extending THREE.Group |
window.libs.JSZip |
JSZip provided by the editor |
gT |
Retrieves translated text by key |
Here, GLTFExporter is specifically available as THREE.GLTFExporter;
a separate global GLTFExporter is not created. Use the editor’s Three.js
instance so that objects, materials, and the exporter use the same version.
console.log('Three.js revision:', THREE.REVISION);const exporter = new THREE.GLTFExporter();const zip = new window.libs.JSZip();Library versions are tied to the BDEngine build. Do not extend editorAPI’s
stability guarantees to every internal class or dependency method.
Available globals do not necessarily mean that the UI is ready:
see Plugin startup order.
Dictionary registration and gT are covered in the
Translations reference.
Selectable and a custom class
The new Selectable(editor) constructor creates a group with transform
methods, selection state, and a link to the editor. It does not create
a Minecraft display, load a model, or assign a serializer to a custom type.
The following example creates a temporary visual marker. It is custom Three.js geometry for the current session, not an exportable Minecraft block.
class PreviewMarker extends Selectable { constructor(editor) { super(editor); this.name = 'Plugin marker'; this.markerMesh = new THREE.Mesh( new THREE.BoxGeometry(0.25, 0.25, 0.25), new THREE.MeshBasicMaterial({ color: 0xffaa00 }) ); this.add(this.markerMesh); }
disposePreview() { if (this.parent) this.delete(); this.markerMesh.geometry.dispose(); this.markerMesh.material.dispose(); }}Selectable provides basic selection, but does not include every feature
of a built-in type: a custom group does not automatically get an entry in
the object panel, Minecraft export, or support from every tool. For that
integration, explore the relevant editor subsystem and verify its contract.
Transform methods and selected
The setters and getters below are synchronous. Position, rotation, and scale are local to the parent. By default, setters mark changed geometry, selection frames, and raycast data for an update, but do not create an Undo command, broadcast the transform to Share Party, or update every panel.
The setters have a skipDirty = false parameter. Keep the default in a typical
plugin. Setting it to true skips internal change tracking and requires you
to manage updates yourself.
setPosition, setRotation, and setScale accept a partial set of axes:
an omitted axis or one set to undefined remains unchanged.
Supplied values must be finite numbers. Strings, NaN, and infinities
throw a ProjectValidationError; invalid input does not apply a partial
set of changes.
setPosition
object.setPosition({ x, y, z }, skipDirty = false) changes the local position.
Values use BDEngine scene units; for regular displays, one unit corresponds
to one Minecraft block. Values are rounded to object.stepPosition
(default 0.000625). Returns undefined.
object.setPosition({ x: 2, z: -1 }); // Y stays unchanged.getPosition
object.getPosition() returns a new plain { x, y, z } object containing
rounded local coordinates. Changing this result does not change the original
position. The getter itself does not overwrite the position.
const position = object.getPosition();object.setPosition({ y: position.y + 1 });setRotation
object.setRotation({ x, y, z }, skipDirty = false) takes degrees,
rounds to object.stepRotation (default 0.01), and updates the corresponding
Three.js angles. Returns undefined.
object.setRotation({ y: 90 });By contrast, the standard Three.js object.rotation property contains radians.
Do not assign degrees directly to object.rotation.y.
getRotation
object.getRotation() returns { x, y, z } in degrees.
If the internal representation has not been created yet, it is calculated
from the current rotation.
Unlike getPosition(), this returns a reference to the internal
customRotation. Copy it when you need a snapshot. Manually changing this
reference does not recalculate the Three.js rotation or notify the editor
of a change:
const rotation = { ...object.getRotation() };object.setRotation({ y: rotation.y + 15 });Angles may retain accumulated turns. Do not assume that they are always
normalized to the range from -180 to 180.
setQuaternionRotation
object.setQuaternionRotation(quaternion, skipDirty = false) copies the
quaternion and updates the rotation representation in degrees using the change
relative to the previous quaternion. Returns undefined; an empty argument
does not change anything.
Pass a normalized THREE.Quaternion. Validation requires finite x/y/z/w
components and a nonzero length, but the method does not automatically
normalize the assigned quaternion. An invalid quaternion throws
a ProjectValidationError.
const quaternion = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3(0, 1, 0), Math.PI / 2);object.setQuaternionRotation(quaternion);The angle in setFromAxisAngle is in radians, while getRotation() still
returns degrees. A quaternion describes orientation, but does not itself
store the number of complete turns, so do not use it as a replacement
for animation keys with multiple revolutions.
setScale
object.setScale({ x, y, z }, skipDirty = false) sets scale factors.
Values are rounded to stepScale (default 0.0001) and clamped to a minimum
of minScale (default 0.0011). Returns undefined.
For a particle, it also updates the scale of its gizmo.
object.setScale({ x: 2, y: 2, z: 2 });A zero or negative scale does not create a zero size or a reflection:
it is raised to the minimum positive value. Supported objects use the
separate setMirrorX method for reflection.
getScale
object.getScale() returns a new { x, y, z } object.
The getter may change the original object: if the current scale does not
meet the rounding or minimum constraints, it calls setScale and updates
the local and world matrices. It is therefore not always a read without
side effects.
selected
object.selected is the selection state property. Assign a boolean.
Enabling it creates a visual frame; disabling it removes that frame.
The editor also updates its selection revision.
object.selected = true;editorAPI.update(true);The assignment does not deselect other objects, create selection history,
or perform the controller’s full selection workflow on its own. After a
batch of state changes, use editorAPI.update(true).
Shear, reflection, and matrices
Affine transforms are enabled for objects whose isDisplay or isCollection
is true. They are not enabled for a plain custom Selectable subclass.
Do not assign these flags just to get the effect: other editor systems
expect the corresponding built-in type.
| Method | Contract |
|---|---|
supportsAffineTransform() |
Returns a boolean indicating whether shear and reflection are supported |
getShear() |
Returns a copy of { xy, xz, yz }, with angles in radians |
setShear({ xy, xz, yz }, skipDirty = false, updateMatrix = true) |
Updates the supplied components. Each must be a finite angle strictly between -Math.PI / 2 and Math.PI / 2; an invalid angle returns false without a change, success returns true |
getMirrorX() |
Returns a boolean indicating reflection along local X |
setMirrorX(value, skipDirty = false, updateMatrix = true) |
Converts the value to a boolean and updates the matrix and internal state according to the flags. Returns undefined |
setFromAffineMatrix(matrix, options = {}) |
Decomposes a THREE.Matrix4 into components and returns the object itself |
applyMatrix4(matrix) |
Premultiplies the local matrix by the supplied matrix and returns the object itself |
updateMatrix() |
Rebuilds the local matrix, including supported shear and reflection; returns undefined |
setShear accepts a partial set of axes. Values very close to the boundary
are additionally clamped to a magnitude of Math.PI / 2 - 0.000001.
setFromAffineMatrix accepts referenceRotation = null (a reference
THREE.Euler), skipDirty = false, and updateWorld = true.
For an object without affine support, the matrix is decomposed into standard
position, quaternion, and scale.
if (object.supportsAffineTransform()) { object.setShear({ xy: THREE.MathUtils.degToRad(10) }); object.setMirrorX(true); editorAPI.update(true);}Add an object to the scene
Add the instance through
editorAPI.addObject.
This example uses the PreviewMarker class above:
const marker = new PreviewMarker(editor);marker.setPosition({ x: 0, y: 1, z: 0 });editorAPI.addObject(marker);marker.selected = true;editorAPI.update(true);
// When the preview is no longer needed:// marker.disposePreview();// editorAPI.update(true);Selectable itself has add(...objects) and remove(...objects),
inherited from the group and extended to update the editor’s internal indices.
They change the hierarchy and return the parent object, but do not create
history. object.delete() deselects descendants and detaches the object
from its parent; it returns undefined and requires the parent to exist.
Direct removal in this example is appropriate for a temporary preview. For standard project objects, use the editorAPI deletion operations.
Persistence, export, and resources
The project saves types known to the editor using their own representation.
Extending Selectable is not enough to serialize an arbitrary class,
load it again, or restore it through Undo. In particular, addObject
does not register a format or add a history command.
THREE.Mesh geometry does not automatically become a Minecraft display either.
For a custom export format, you can obtain a
mesh snapshot and
process it with your own logic. Fully integrating a type into the project
requires additional editor mechanisms and separate checks for saving
and restoring it.
The plugin is responsible for disposing of resources it creates: geometry, materials, its own textures, event handlers, and timers. Removing a DOM window or detaching a Three.js object does not clean up all of them. Dispose only of resources you own; shared editor textures and materials may still be used by other objects.
When to use a built-in object
If the result must remain an editable display and export to Minecraft,
start with editorAPI.add for BlockDisplay, ItemDisplay, or TextDisplay.
The editor already implements their loading and standard serialization.
Custom geometry is useful for previews, visual tools, and other plugin features where persistence and in-game export are handled separately. For a practical approach, see Create a generator of built-in objects.