Skip to content

Mesh Geometry

Editable geometry parameters for primitive meshes (cube, sphere, plane, capsule, cylinder) and the placeholder for model entities. Holds the authoring values plus shadow casting/receiving flags. The mesh-geometry system rebuilds the underlying BufferGeometry when parameters change.

Managed by: MeshGeometrySystem

Properties

PropertyTypeDescription
kindEditableMeshObjectKindPrimitive kind (cube, sphere, plane, capsule, cylinder) or model for loaded assets.
geometryTypestringThree.js geometry class name (e.g. "BoxGeometry", "SphereGeometry"). Derived from kind.
parametersMeshGeometryParametersEditable parameters whose shape depends on kind. The mesh-geometry system rebuilds geometry when these change.
objectTHREE.Object3D | nullLive three.js object whose geometry is being managed. null until the object is built.
modelNamestringHuman-readable model name shown in the inspector for model entities; empty for primitives.
castShadowbooleanWhether the mesh casts shadows in the renderer.
receiveShadowbooleanWhether the mesh receives shadows in the renderer.

API reference

Scripting examples

Component access patterns

Use this.getComponent(X) to access a component on the entity this script is attached to. Use this.world.getEntityByName to find another entity by name, then this.world.getComponent to read its component:

typescript
// Component on the entity this script is on
const geo = this.getComponent(MeshGeometryComponent);

// Component on another entity, found by its scene name
const other = this.world.getEntityByName("Floor");
if (other) {
  const geo = this.world.getComponent(other.entityId, MeshGeometryComponent);
}

Toggle shadow casting at runtime

Let the inspector or game logic control whether the mesh casts and receives shadows.

typescript
import { Behaviour, MeshGeometryComponent } from "@relu-interactives/spatial-ecs";
import type { BooleanInput } from "@relu-interactives/spatial-ecs";

export default class ShadowDriver extends Behaviour {
  data = {
    castShadow: true as BooleanInput,
    receiveShadow: true as BooleanInput,
  };

  protected onUpdate() {
    const geo = this.getComponent(MeshGeometryComponent);
    if (!geo) return;
    geo.castShadow = this.data.castShadow;
    geo.receiveShadow = this.data.receiveShadow;
  }
}

Animate sphere radius

Pulse the radius of a sphere primitive by writing to its geometry parameters each frame. The mesh-geometry system rebuilds the BufferGeometry when parameters change.

typescript
import { Behaviour, MeshGeometryComponent } from "@relu-interactives/spatial-ecs";
import type { FloatInput } from "@relu-interactives/spatial-ecs";

export default class SphereGrow extends Behaviour {
  data = {
    speed: 1 as FloatInput,
    minRadius: 0.3 as FloatInput,
    maxRadius: 1 as FloatInput,
  };

  private elapsed = 0;

  protected onUpdate() {
    this.elapsed += this.deltaTime;
    const geo = this.getComponent(MeshGeometryComponent);
    if (!geo || geo.kind !== "sphere") return;

    const t = (Math.sin(this.elapsed * this.data.speed) + 1) * 0.5;
    const radius = this.data.minRadius + t * (this.data.maxRadius - this.data.minRadius);
    (geo.parameters as { radius: number }).radius = radius;
  }
}

Via ComponentInput in the inspector

You can also pick this component from the inspector using a ComponentInput field. Assign any entity in the inspector and the field resolves to the live MeshGeometryComponent instance at runtime.

typescript
import {
  Behaviour,
  MeshGeometryComponent,
  type ComponentInput,
} from "@relu-interactives/spatial-ecs";

export default class Example extends Behaviour {
  data = {
    targetGeometry: {
      type: "component",
      value: null,
    } as unknown as ComponentInput,
  };

  protected onUpdate() {
    const geo = this.data.targetGeometry.value as MeshGeometryComponent | null;
    if (!geo) return;
    // Use the live component instance.
  }
}

Via EntityInput in the inspector

Alternatively, pick an entity from the inspector using an EntityInput field and then read the component off that entity via world.getComponent:

typescript
import {
  Behaviour,
  MeshGeometryComponent,
  type EntityInput,
  type WorldEntityView,
} from "@relu-interactives/spatial-ecs";

export default class Example extends Behaviour {
  data = {
    targetEntity: { type: "entity", value: null } as unknown as EntityInput,
  };

  protected onUpdate() {
    const entity = this.data.targetEntity.value as WorldEntityView | null;
    if (!entity) return;

    const geo = this.world.getComponent(
      entity.entityId,
      MeshGeometryComponent,
    ) as MeshGeometryComponent | null;
    if (!geo) return;
    // Use the component on the picked entity.
  }
}

Geometry rebuild cost

The mesh-geometry system only rebuilds geometry when parameters actually change. Avoid writing the same value every frame unless the value is genuinely different, to prevent unnecessary geometry allocations.