Portable object packages

The engine and object packages have separate ownership. engine/index.js exports generic services and never imports the object library. Each objects/<package>/ folder contains its own class entry, local base/model/geometry/material code, any assets, and object.json. The folder's imports stay inside that folder. Constructors receive the chosen engine namespace: new Class(api, options).

A consumer copies engine/ and only the selected object folders. It imports the engine and classes at their new local paths. No object hard-codes where the engine lives, and no current package requires a sibling object folder. object.json identifies the package version, entry, exported classes, required object protocol, file-dependency declaration and common interface. Required engine object protocol is currently 1; compatible services must also be available in the injected API.

Available classes

FolderClassesClass-specific behavior and required context
peoplePerson, Bird, PetPerson pose/gaze, walking destination, hand positions and wrist IK; bird/pet activities. Consumer owns broader paths and choreography. People guide.
people/authored (inside the people package)AuthoredPerson, loadAuthoredAssetsDetailed anatomy, source rig and expressions, fitted clothing/hair and two mesh detail levels. Load the included data once and inject it into each constructor. Authored-person guide.
vegetationTree, Forest, GrassPatchTree species/dimensions/foliage; forest placement/lookups; ground-cover density and camera-relative detail. Forest/grass require terrain; wind and camera context are supplied separately. Vegetation guide.
aircraftNavalFighterExterior and real cockpit, camera mounts, pickable controls, moving surfaces, gear, lights and live instruments. Consumer supplies flight state/motion. Aircraft guide.
buildingsBuilding, Bridge, Road, FarmField, AirfieldBuilding style/dimensions, light and door methods; bridge deck height; roads/fields require supplied terrain, road points or field position. Airfield creates local runway/structures and controls structure lights.
beachBeachUmbrella, SunLounger, VolleyballCourt, BeachBall, Sandcastle, BeachTowel, SandBucketCanopy opening/tilt; recline/occupancy; score/terrain conformance; simple ball motion; sandcastle progress and bucket fill. Shared wind is optional. Beach guide.
craftHovercraftA local detailed craft model, turret angle and visible thrust state. The consumer supplies vehicle motion.
propsFlagLocally generated cloth-flag geometry and time-driven movement.

Building supports cottage, farmhouse, barn, warehouse, hangar and lighthouse forms. setLights(boolean), setDoorOpen(0..1) and getCollisionShapes() expose its supported state; returned collision bounds still need consumer registration. Bridge.getDeckHeight() is a nominal deck-height helper. Road needs terrain and {x,z} points; FarmField needs terrain, x and z. These terrain/service references are application context, not a portable serialized asset. Hovercraft.setThrust(0..1) changes the visual engine state, not acceleration. Flag.update(dt,time) advances cloth time.

Common interface and ownership

Every class exposes root, properties, position, rotation, quaternion, scale, setPosition(x,y,z), update, getBounds(), serialize(), restore(data) and idempotent dispose(). The root is the object added to the scene. Its transform getters refer to that root. Use documented class methods for state changes that require geometry or material updates.

Units are metres and radians, and update time is in seconds. Forward axes and anchors are class-specific: people face +Z, the naval fighter faces -Z, and a lounger has its head along -Z. Some update methods take a third context argument for camera, quality or terrain. Read the class guide rather than assuming identical simulation semantics.

An instance owns its model resources and disposes/removes its root. Shared injected services such as an engine, wind field, world or terrain are borrowed unless explicitly documented otherwise. Register object disposal in the consumer's scene scope, or dispose manually when replacing it. Do not continue using a disposed instance. Dispose the engine after its consumers/services are released.

getBounds() is a visual-space bounding box, not automatically a collision shape, aerodynamic envelope or safety clearance. Skinned people and procedural terrain-following objects have specialized bounds. Generic Three.js bounds may include hidden parts. The application chooses an appropriate collision representation and updates it when necessary.

Serialization includes common constructor properties and root transforms; selected classes extend it with control, mechanism, placement or pose state. Generic restore applies transforms to an already constructed object. For classes whose appearance is construction-time state, reconstruct with saved properties and then restore transforms, as demonstrated for Person and BeachUmbrella in the object-demo. Reinject terrain, wind or other services where required. Do not assume arbitrary function/object references in application context become portable JSON, or that every class can reconstruct all dimensions from a generic transform snapshot.

Consumer example

import * as api from './engine/index.js';
import {AuthoredPerson, loadAuthoredAssets} from './objects/people/authored/index.js';

const assets = await loadAuthoredAssets();
const person = new AuthoredPerson(api, {assets, height:1.72, clothing:'beach', seed:38});
scene.add(person.root);
person.setPosition(-1.5, 0, 0).setPose('wave');
// Call from the application's update hook:
person.update(dt, time);
// Capture a snapshot and reconstruct when needed:
const saved = person.serialize();
person.dispose();
const restored = new AuthoredPerson(api, {assets, ...saved.properties}).restore(saved);
scene.add(restored.root);

The self-contained object-demo/ distribution provides this pattern with working controls and a BeachUmbrella. Copy the whole people/ folder, including authored/ and its assets. Its original index.js remains the synchronous procedural entry; the detailed constructor has its own optional entry to avoid an inheritance/import cycle. The demo installs only engine, people and beach. The engine-only physics starter installs no objects. Neither application imports workshop behavior.

Package verification and extension

From the complete development project:

node tools/verify.mjs --root=engine
node tools/verify.mjs --root=objects
node tools/verify.mjs

The verifier assigns each detected engine/object folder its own import boundary, even when several packages are present in one application. An aircraft import of ../people/index.js, an object import of a fixed engine path, or an engine import of a shell/object file fails. Local ?v= query tags are permitted and resolved to the underlying file. Symlinks, missing files, nonlocal imports and runtime documentation dependencies fail. Package closure is separate from real rendering, behavior and cleanup evidence.

To add a package, give it an index.js, local implementation files and a valid object.json; inject engine services and keep imports local. Add all public classes to the manifest. Document required constructor context, methods, units, axes, serialization limits and resource ownership under docs/. The release builder discovers immediate object folders automatically, while consumer templates explicitly select their needed packages. Match the engine object protocol and verify the package in isolation and inside a moved consumer.

These packages combine procedural models, local authored data and reusable behavior. Their class names and feature lists are not claims of photorealism, exact manufacturer reconstruction or complete real-world simulation. Record actual visible quality and remaining limits for the application using them.

Optional Model Studio definitions

Each supplied package now has a local studio.js entry declared by object.json with Studio protocol 1. It describes factories, editable properties and actions for the generic shell viewer. This is optional for other applications: ordinary runtime constructors still receive only the engine API and their options. New compatible packages can be loaded by folder name without editing Model Studio. See MODEL_STUDIO.md for the full contract, loading steps and a working independent example.