Draw your own simulation with GameView through the instance-buffer contract.
A RenderSource packs instances grouped by mesh: matrices holds a column-major mat4 each, colors an RGBA each, and optional regions a texture rectangle each, in the same order. Only the first count instances draw; capacity sizes the GPU buffers.
ranges holds [first, count] per mesh id: opaque instances at ranges[2 * mesh], transparent ones at ranges[2 * (MAX_MESHES + mesh)]. Meshes must already be registered (built-ins after warmUpEngine, createShape or a loader).
An opaque and a see-through cube
React and React Native · shared engine API
import { MAX_MESHES, type RenderSource } from "@nayan-ui/engine";const CUBE = 0; // built-in cube mesh idexport function twoCubes(): RenderSource {const matrices = new Float32Array(32);matrices.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 1], 0);matrices.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1], 16);const ranges = new Uint32Array(MAX_MESHES * 4);ranges.set([0, 1], 2 * CUBE); // opaque: instance 0ranges.set([1, 1], 2 * (MAX_MESHES + CUBE)); // transparent: instance 1const colors = new Float32Array([1, 0.4, 0.2, 1, 0.2, 0.7, 1, 0.4]);return { matrices, colors, ranges, count: 2, capacity: 2 };}
GameView calls source.update(dt) after onUpdate each frame, unless its updateCount changed during onUpdate or isDisposed is true. setView(viewProj, width, height) receives the camera of each drawn frame for your own picking.
A custom source gets no physics, timers, animation or picking from World, and nativeWorld belongs only to a real World. Prefer World unless you already own a simulation.
A spinning cube source
React and React Native · shared engine API
import { MAX_MESHES, type RenderSource } from "@nayan-ui/engine";export class Spinner implements RenderSource {readonly matrices = new Float32Array(16);readonly colors = new Float32Array([0.2, 0.7, 1, 1]);readonly ranges = new Uint32Array(MAX_MESHES * 4);readonly count = 1;readonly capacity = 1;updateCount = 0;private angle = 0;constructor() {this.ranges.set([0, 1], 0); // built-in cube, instance 0}update(dt: number) {this.updateCount++;this.angle += dt;const c = Math.cos(this.angle), s = Math.sin(this.angle);this.matrices.set([c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 1, 0, 1]); // Y rotation}}
Await warmUpEngine so the built-in meshes exist, then pass the source to GameView with a Camera and Light, like a world. A custom source can also be GameView's overlay.
CustomView.tsx
React and React Native · shared engine API
import { useEffect, useState } from "react";import { GameView, warmUpEngine, type Camera, type Light, type RenderSource } from "@nayan-ui/engine";const camera: Camera = { eye: [0, 3, 6], target: [0, 1, 0], fov: 1 };const light: Light = { direction: [0.4, 0.8, 0.5], ambient: 0.35, color: "white" };export function CustomView({ source }: { source: RenderSource }) {const [ready, setReady] = useState(false);useEffect(() => {void warmUpEngine().then(() => setReady(true));}, []);return ready ? <GameView source={source} camera={camera} light={light} style={{ flex: 1 }} /> : null;}