A hackable template for creating small and fast browser games.
1/**
2 * # SpatialNode2D
3 *
4 * The `SpatialNode2D` component allows the entity to be part of the 2D scene
5 * graph.
6 *
7 * Only entities with `SpatialNode2D` can be parented to other entities, or be
8 * parents of other entities.
9 */
10
11import {mat2d_create} from "../../lib/mat2d.js";
12import {Mat2D} from "../../lib/math.js";
13import {Entity} from "../../lib/world.js";
14import {FLOATS_PER_INSTANCE} from "../../materials/layout2d.js";
15import {Game} from "../game.js";
16import {Has, World} from "../world.js";
17
18export interface SpatialNode2D {
19 /** Absolute matrix relative to the world. */
20 World: Mat2D;
21 /** World to self matrix. */
22 Self: Mat2D;
23 Parent?: Entity;
24 /** Ignore parent's rotation and scale? */
25 IsGyroscope: boolean;
26}
27
28/**
29 * Add `SpatialNode2D` to an entity.
30 *
31 * In order to be a parent of other entities, or to be a child of another entity,
32 * the entity must also have the `SpatialNode2D` component. It's also required
33 * if you're going to need to switch between the world space and the entity's
34 * self space, or if you're going to query the entity's parent.
35 *
36 * Entities with `LocalTransform2D` and `SpatialNode2D` have their model matrix
37 * computed in sys_transform2d() on the CPU, making them slower than entities
38 * with only `LocalTransform2D`, but more fully-featured.
39 *
40 * @param is_gyroscope Ignore parent's rotation and scale?
41 */
42export function spatial_node2d(is_gyroscope = false) {
43 return (game: Game, entity: Entity) => {
44 game.World.Signature[entity] |= Has.SpatialNode2D | Has.Dirty;
45 game.World.SpatialNode2D[entity] = {
46 World: game.World.InstanceData.subarray(
47 entity * FLOATS_PER_INSTANCE,
48 entity * FLOATS_PER_INSTANCE + 6,
49 ),
50 Self: mat2d_create(),
51 IsGyroscope: is_gyroscope,
52 };
53 };
54}
55
56/**
57 * Yield ascendants matching a component mask. Start at the current entity.
58 *
59 * @param world World object which stores the component data.
60 * @param entity The first entity to test.
61 * @param mask Component mask to look for.
62 */
63export function* query_up(world: World, entity: Entity, mask: Has): IterableIterator<Entity> {
64 if ((world.Signature[entity] & mask) === mask) {
65 yield entity;
66 }
67
68 let parent = world.SpatialNode2D[entity].Parent;
69 if (parent !== undefined) {
70 yield* query_up(world, parent, mask);
71 }
72}