Version: 1.0.0 Status: Active
This document is the canonical definition of the Nova64 engine adapter surface.
Any renderer or host backend that wants to work with Nova64 game carts must implement every method listed in this contract and must return a valid capabilities object from getCapabilities().
The contract is intentionally narrow. It covers the minimum operations that the Nova64 runtime and cart API require. Backend-specific features (e.g., Three.js post-processing, Unity coroutines, Babylon.js physics) live outside this contract and are exposed only if the backend declares them through its capabilities.
The contract version is a semver string exported as ADAPTER_CONTRACT_VERSION from runtime/engine-adapter.js.
Adapters must declare the contract version they implement in their getCapabilities() return value via the contractVersion field.
Every conforming adapter must implement all of the following. The conformance test suite at tests/test-adapter-conformance.js validates each entry.
createMaterial(type, opts) → opaque handleCreate a material.
| Parameter | Type | Description |
|---|---|---|
type |
'basic' | 'phong' | 'standard' |
Material model |
opts.color |
number | { r, g, b } |
Base color |
opts.map |
texture handle | Optional texture map |
opts.transparent |
boolean |
Enable alpha blending |
opts.alphaTest |
number (0–1) |
Alpha test cutoff |
opts.side |
'front' | 'back' | 'double' |
Face culling |
opts.roughness |
number |
Roughness (standard only) |
opts.metalness |
number |
Metalness (standard only) |
opts.emissive |
number |
Emissive color (standard only) |
opts.flatShading |
boolean |
Flat shading (standard only) |
Returns an opaque material handle. The handle type is backend-specific. Cart code must not inspect its internals.
createDataTexture(data, width, height, opts) → opaque handleCreate a texture from a raw pixel buffer.
| Parameter | Type | Description |
|---|---|---|
data |
Uint8Array | Uint8ClampedArray |
RGBA pixel data |
width |
number |
Texture width in pixels |
height |
number |
Texture height in pixels |
opts.format |
'rgba' |
Pixel format (default: 'rgba') |
opts.filter |
'nearest' | 'linear' |
Sampling filter |
opts.wrap |
'repeat' | 'clamp' |
UV wrapping mode |
opts.generateMipmaps |
boolean |
Mipmap generation (default: true) |
createCanvasTexture(canvas, opts) → opaque handleCreate a texture from an HTMLCanvasElement.
| Parameter | Type | Description |
|---|---|---|
canvas |
HTMLCanvasElement |
Source canvas |
opts.filter |
'nearest' | 'linear' |
Sampling filter |
opts.wrap |
'repeat' | 'clamp' |
UV wrapping mode |
cloneTexture(textureHandle) → opaque handleClone an existing texture handle.
setTextureRepeat(textureHandle, x, y) → voidSet UV repeat on a texture.
invalidateTexture(textureHandle) → voidMark a texture as dirty so the backend re-uploads it on the next frame.
createPlaneGeometry(width, height, segX, segY) → opaque handleCreate a flat plane geometry.
| Parameter | Type | Default |
|---|---|---|
width |
number |
required |
height |
number |
required |
segX |
number |
1 |
segY |
number |
1 |
setMeshMaterial(meshId, materialHandle) → voidAssign a material to a mesh by ID.
| Parameter | Type | Description |
|---|---|---|
meshId |
number | string |
Mesh identifier |
materialHandle |
opaque handle | Handle returned by createMaterial |
createColor(r, g, b) → opaque handleCreate a color value. r, g, b are floats in the range 0–1.
The returned value may be a THREE.Color, a { r, g, b } plain object, or any opaque handle depending on the backend.
getCameraPosition() → { x: number, y: number, z: number }Return the current camera world position.
The returned object must have finite numeric x, y, z fields. Backends that have not positioned a camera yet must return { x: 0, y: 0, z: 0 }.
getCapabilities() → CapabilitiesReturn a frozen capabilities object describing this backend.
The returned object must conform to the following shape:
interface Capabilities {
backend: string; // Backend identifier: 'threejs', 'unity', 'babylon', 'godot', …
contractVersion: string; // Must equal ADAPTER_CONTRACT_VERSION
adapterVersion: string; // Backend-specific semver
features: readonly string[]; // Feature identifiers declared by this backend
supports(feature: string): boolean; // Returns true iff feature is in the features list
}
Standard feature strings that adapters should use when declaring capabilities. Custom backend-specific features are allowed but must be namespaced with the backend name, e.g. 'threejs:bloom' or 'unity:prefab-loading'.
| Feature string | Description |
|---|---|
material:basic |
MeshBasicMaterial or equivalent |
material:phong |
MeshPhongMaterial or equivalent |
material:standard |
MeshStandardMaterial (PBR) or equivalent |
texture:data |
Raw pixel buffer texture creation |
texture:canvas |
HTMLCanvasElement-backed texture |
texture:repeat |
UV repeat support |
geometry:plane |
Plane geometry primitive |
camera:read |
Camera position read-back |
camera:write |
Camera position/target/fov write |
lights:ambient |
Ambient light control |
lights:point |
Point light creation |
lights:directional |
Directional light control |
shadows |
Shadow map support |
instancing |
Instanced mesh rendering |
audio:sfx |
Sound effect playback |
physics:aabb |
AABB collision and gravity |
input:keyboard |
Keyboard polling |
input:gamepad |
Gamepad polling |
asset:gltf |
GLTF/GLB model loading |
asset:texture-atlas |
Texture atlas loading |
For host bridges (Unity, Godot) where one-call-per-operation is expensive, Nova64 provides createCommandBufferAdapter(innerAdapter, opts).
This wrapper buffers every mutating call into an in-memory queue. The host polling loop calls adapter.flush() once per frame to drain the queue to the inner adapter.
Read-through calls (getCameraPosition, getCapabilities) bypass the buffer entirely.
import { createCommandBufferAdapter, createUnityBridgeAdapter } from 'nova64/runtime';
const bridge = globalThis.__NOVA64_UNITY_BRIDGE__;
const unityAdapter = createUnityBridgeAdapter(bridge);
const buffered = createCommandBufferAdapter(unityAdapter, { maxQueueSize: 512 });
// Boot Nova64 with the buffered transport
setEngineAdapter(buffered);
// In your Unity JS-to-C# host update loop:
function onFrameEnd() {
buffered.flush(); // drains all buffered commands to the Unity bridge in one batch
}
| Method | Description |
|---|---|
flush() |
Drain all queued commands to the inner adapter in registration order |
pendingCount() |
Return the number of commands currently in the buffer |
discardPending() |
Drop all queued commands without executing them |
| Option | Type | Default | Description |
|---|---|---|---|
autoFlush |
boolean |
false |
Execute each call immediately without buffering |
maxQueueSize |
number |
512 |
Log a warning when the queue exceeds this length |
Every adapter must pass runAdapterConformanceTests(adapter, runner, { name }) from tests/test-adapter-conformance.js before being merged.
The conformance suite validates:
getCapabilities() returns a valid, versioned capabilities objectgetCameraPosition() returns a { x, y, z } with finite numberscreateMaterial, createPlaneGeometry, createColor do not throwNew backends should also add their own extended test file for backend-specific behavior.
These capabilities are tracked but not yet in the contract. They will be added in a future minor bump.
| Feature | Phase |
|---|---|
camera:write (setCameraPosition, setCameraTarget, setCameraFOV) |
Phase 1.1 |
lights:ambient, lights:point, lights:directional |
Phase 1.1 |
mesh:create (createMesh, removeMesh) |
Phase 1.1 |
transforms (setPosition, setScale, rotateMesh) |
Phase 1.1 |
asset:gltf |
Phase 3 (Unity) |
audio:sfx |
Phase 1.1 |
input:keyboard, input:gamepad |
Phase 1.1 |
Maintained alongside runtime/engine-adapter.js. Update this document whenever ADAPTER_CONTRACT_VERSION changes.