Skip to content

Build a generator with built-in objects

Generate a parameterized row of built-in BDEngine objects with input validation and failure handling.

We will create a row of stone Block Display objects with a chosen count and spacing. The result consists of ordinary built-in objects that can be edited and saved with the project.

You should know how to install and run a plugin. Start with an empty test project in ordinary modeling mode.

What we are generating

Download the complete generate.js plugin. After installing and restarting, Create a row of stone displays appears in the dock menu. By default, the plugin creates 5 stone blocks spaced 1.25 blocks apart along world X, starting at (0, 0, 0).

The generator adds objects to the current project root. It does not implicitly use the selected group as their parent, or change existing objects. Running it again adds another row at the same coordinates, so rows may overlap visually.

The example runs in modeling mode outside a shared session. Objects are added through editorAPI; checks for the mode, project root and history generation use the internal editor. This is an explicit, limited dependency on the editor implementation, not an additional stable editorAPI contract.

Parameters and validation

Before creating any objects, the example validates:

Parameter Example rule
Count An integer from 1 to 24.
Spacing A finite number from 1 to 16, in blocks.
Block stone, found in editorAPI.getBlockList().

These numeric limits prevent accidental input from creating a huge scene. They are not editor limits. Canceling either dialog ends the run without changes. Check an empty string separately: Number('') equals zero and does not identify empty input by itself.

If you extend the generator to let users choose a block, get valid identifiers from getBlockList() and validate the selection before generation starts. The list contains block-state strings; do not construct an unknown state from a typed name alone.

Add objects asynchronously

Await every add call:

const object = await editorAPI.add(
'stone',
'BlockDisplay',
root,
true,
new window.THREE.Vector3(index * gap, 0, 0)
);
if (!object) throw new Error('The display was not created.');

Here, root is the stored root of the current project, index is the zero-based element number, and gap is the validated spacing. The plugin environment provides the global THREE.

add can return an object, false or undefined: the absence of an exception does not prove success. For example, creation can be rejected in an unsuitable mode or interrupted by a project switch. The complete example uses await for each call in sequence, checks the result and only then creates the next element.

Do not use array.forEach(async ...) expecting to await completion of the whole row. A regular for loop works for sequential creation. The busy flag prevents another run of the generator itself, but does not stop the user from using other editor commands.

The save: true argument allows the add command to be recorded in history. noReply is left as false: the current implementation records history only when save && !noReply. Do not use noReply: true as though it merely hid the loading indicator. See the add contract.

Transforms and grouping

objPos must be a THREE.Vector3, not an array or a plain {x, y, z} object. When adding the object, the editor converts the world position to the parent’s coordinate system. The row uses an ordinary stone block with no extra offsets.

The position is supplied during creation, so replaying the add command uses the same parameter instead of relying on a separate position change after the history entry was recorded. Do not substitute an arbitrary THREE.Mesh for a built-in object: a visual mesh does not automatically become a Block Display or gain its saving and Minecraft export behavior.

If an object needs further changes after creation, supported types provide setPosition, setRotation and setScale. Position uses local coordinates, and these rotation methods use degrees. A separate change like this needs its own history handling. See transforms for built-in and custom objects.

The example does not create a group programmatically: Collection is not a supported add type. After checking the result, select the generated displays in the object tree and group them with the editor’s standard action. This produces an ordinary group containing ordinary displays, without a special generator object.

Repeat runs and partial failures

An asynchronous model load can finish after the user has switched projects. The complete example stores the root, the history object and its generation, then checks them before and after each add call. If they no longer match, generation stops. Review checks of internal fields when updating the editor.

If several displays have already been added, they do not automatically form one transaction. The plugin reports how many objects it created. As long as the project and mode are unchanged, it offers to delete only the displays created by this run. Before deletion, it checks that the objects are still in the current project and does not pass an empty array to delete.

Each successful add creates a separate history step. Cleanup through delete adds another step. Do not promise that one Ctrl+Z will undo the entire row or that history will return to its initial state after a partial failure. If the project changes, the plugin does not delete objects in the new project.

Check the following in the editor:

  1. Use a count of 1, then 5. The object tree should gain exactly that many new Block Display objects.
  2. With spacing 1.25, check X coordinates: 0, 1.25, 2.5, 3.75, 5. Y and Z should be zero.
  3. Try canceling a dialog, empty input, a fractional count and values above the limits. No objects should be created before the parameters are valid.
  4. Click the entry again quickly. One run must finish before another starts.
  5. Undo and redo the history commands in sequence: each undo removes one addition, and redo should restore its position.
  6. Save and reopen the project, then use the ordinary export workflow for a small model.

Slow loading, project switching and unavailable resources require separate tests in the editor. A JavaScript syntax check does not verify rendering or the result’s in-game export.

Reuse the result

After grouping, give the group a clear name. You can reuse it as a starting point in other projects without installing the generator: the displays already belong to the project data. The plugin does not store count and spacing parameters in the group, so subsequent changes use ordinary editing tools or a new generator run.

Next: save the generated result as an asset.