@waica/engine 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/collision-category.js +5 -2
- package/dist/collision-dispatch.d.ts +7 -1
- package/dist/collision-dispatch.js +18 -11
- package/dist/components/animated-sprite.d.ts +2 -0
- package/dist/components/animated-sprite.js +41 -6
- package/dist/components/sprite.d.ts +6 -0
- package/dist/components/sprite.js +52 -4
- package/dist/game.d.ts +2 -0
- package/dist/game.js +9 -1
- package/dist/scene.d.ts +10 -0
- package/dist/spatial-broadphase.d.ts +7 -1
- package/dist/spatial-broadphase.js +16 -45
- package/dist/spatial-pairs.d.ts +17 -0
- package/dist/spatial-pairs.js +79 -0
- package/dist/sprite-batch.d.ts +93 -0
- package/dist/sprite-batch.js +211 -0
- package/dist/sprite-batches.d.ts +107 -0
- package/dist/sprite-batches.js +260 -0
- package/dist/sprite-draw-collector.d.ts +42 -0
- package/dist/sprite-draw-collector.js +107 -0
- package/dist/sprite-runs.d.ts +40 -0
- package/dist/sprite-runs.js +49 -0
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import type { Drawable } from './sprite-runs.js';
|
|
3
|
+
/** A frame drawable: what three sorts it by, the object, and the sprite instance it stands for (null otherwise). */
|
|
4
|
+
export interface FrameDrawable<K, I> extends Drawable<K> {
|
|
5
|
+
object: THREE.Object3D;
|
|
6
|
+
instance: I | null;
|
|
7
|
+
}
|
|
8
|
+
/** How the collector recognises a batched sprite's hidden anchor and its batch key. */
|
|
9
|
+
export interface CollectedSprites<K, I> {
|
|
10
|
+
/** The live sprite instance this object anchors, or undefined for any other object. */
|
|
11
|
+
instanceOf(object: THREE.Object3D): I | undefined;
|
|
12
|
+
keyOf(instance: I): K;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* One frame's transparent drawables, collected the way WebGLRenderer's
|
|
16
|
+
* projectObject walks the scene: visibility, camera layers, Group render
|
|
17
|
+
* order, frustum culling, and the clip-space z it sorts by. Sprite anchors
|
|
18
|
+
* are hidden yet collected when in view; `skip` (the batches' own run
|
|
19
|
+
* meshes) is never walked. The drawables are pooled across frames.
|
|
20
|
+
*/
|
|
21
|
+
export declare class TransparentDrawCollector<K, I> {
|
|
22
|
+
private readonly sprites;
|
|
23
|
+
private readonly skip;
|
|
24
|
+
/** This frame's drawables, in walk order; the same array every frame. */
|
|
25
|
+
readonly drawables: FrameDrawable<K, I>[];
|
|
26
|
+
private readonly pool;
|
|
27
|
+
private readonly projScreen;
|
|
28
|
+
private readonly frustum;
|
|
29
|
+
private readonly point;
|
|
30
|
+
private layers;
|
|
31
|
+
constructor(sprites: CollectedSprites<K, I>, skip: THREE.Object3D);
|
|
32
|
+
/** Walks `scene` for `camera`, whose matrices must be current. */
|
|
33
|
+
collect(scene: THREE.Object3D, camera: THREE.Camera): void;
|
|
34
|
+
private visit;
|
|
35
|
+
/** Files the object itself when three would draw it transparent; returns the group order for its children. */
|
|
36
|
+
private visitOwn;
|
|
37
|
+
/** three's per-object culling: `!frustumCulled || intersectsFrustum`. */
|
|
38
|
+
private inView;
|
|
39
|
+
private push;
|
|
40
|
+
/** The clip-space z three sorts the object by. */
|
|
41
|
+
private sortDepth;
|
|
42
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
function isRenderable(object) {
|
|
3
|
+
const flags = object;
|
|
4
|
+
return flags.isMesh === true || flags.isLine === true || flags.isPoints === true || flags.isSprite === true;
|
|
5
|
+
}
|
|
6
|
+
/** Whether three files any of the object's materials in its transparent list. */
|
|
7
|
+
function drawsTransparent(object) {
|
|
8
|
+
const list = Array.isArray(object.material) ? object.material : [object.material];
|
|
9
|
+
return list.some((material) => {
|
|
10
|
+
const transmission = material.transmission ?? 0;
|
|
11
|
+
return material.visible && material.transparent && transmission <= 0;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/** The bounding-sphere center three sorts a mesh, line or points by: the object's own sphere if it has one. */
|
|
15
|
+
function sortCenter(object) {
|
|
16
|
+
const own = object;
|
|
17
|
+
if (own.boundingSphere === undefined) {
|
|
18
|
+
const geometry = object.geometry;
|
|
19
|
+
if (geometry.boundingSphere === null)
|
|
20
|
+
geometry.computeBoundingSphere();
|
|
21
|
+
return geometry.boundingSphere?.center ?? new THREE.Vector3();
|
|
22
|
+
}
|
|
23
|
+
if (own.boundingSphere === null)
|
|
24
|
+
own.computeBoundingSphere?.();
|
|
25
|
+
return own.boundingSphere?.center ?? new THREE.Vector3();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One frame's transparent drawables, collected the way WebGLRenderer's
|
|
29
|
+
* projectObject walks the scene: visibility, camera layers, Group render
|
|
30
|
+
* order, frustum culling, and the clip-space z it sorts by. Sprite anchors
|
|
31
|
+
* are hidden yet collected when in view; `skip` (the batches' own run
|
|
32
|
+
* meshes) is never walked. The drawables are pooled across frames.
|
|
33
|
+
*/
|
|
34
|
+
export class TransparentDrawCollector {
|
|
35
|
+
sprites;
|
|
36
|
+
skip;
|
|
37
|
+
/** This frame's drawables, in walk order; the same array every frame. */
|
|
38
|
+
drawables = [];
|
|
39
|
+
pool = [];
|
|
40
|
+
projScreen = new THREE.Matrix4();
|
|
41
|
+
frustum = new THREE.Frustum();
|
|
42
|
+
point = new THREE.Vector4();
|
|
43
|
+
layers = new THREE.Layers();
|
|
44
|
+
constructor(sprites, skip) {
|
|
45
|
+
this.sprites = sprites;
|
|
46
|
+
this.skip = skip;
|
|
47
|
+
}
|
|
48
|
+
/** Walks `scene` for `camera`, whose matrices must be current. */
|
|
49
|
+
collect(scene, camera) {
|
|
50
|
+
this.layers = camera.layers;
|
|
51
|
+
this.projScreen.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
52
|
+
this.frustum.setFromProjectionMatrix(this.projScreen, THREE.WebGLCoordinateSystem, camera.reversedDepth);
|
|
53
|
+
this.drawables.length = 0;
|
|
54
|
+
this.visit(scene, 0);
|
|
55
|
+
}
|
|
56
|
+
visit(object, groupOrder) {
|
|
57
|
+
if (object === this.skip)
|
|
58
|
+
return;
|
|
59
|
+
const instance = this.sprites.instanceOf(object);
|
|
60
|
+
if (instance !== undefined) {
|
|
61
|
+
if (object.layers.test(this.layers) && this.inView(object))
|
|
62
|
+
this.push(object, groupOrder, instance);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!object.visible)
|
|
66
|
+
return;
|
|
67
|
+
const order = this.visitOwn(object, groupOrder);
|
|
68
|
+
for (const child of object.children)
|
|
69
|
+
this.visit(child, order);
|
|
70
|
+
}
|
|
71
|
+
/** Files the object itself when three would draw it transparent; returns the group order for its children. */
|
|
72
|
+
visitOwn(object, groupOrder) {
|
|
73
|
+
if (!object.layers.test(this.layers))
|
|
74
|
+
return groupOrder;
|
|
75
|
+
if (object.isGroup === true)
|
|
76
|
+
return object.renderOrder;
|
|
77
|
+
if (isRenderable(object) && drawsTransparent(object) && this.inView(object))
|
|
78
|
+
this.push(object, groupOrder, null);
|
|
79
|
+
return groupOrder;
|
|
80
|
+
}
|
|
81
|
+
/** three's per-object culling: `!frustumCulled || intersectsFrustum`. */
|
|
82
|
+
inView(object) {
|
|
83
|
+
return !object.frustumCulled || object.intersectsFrustum(this.frustum) === true;
|
|
84
|
+
}
|
|
85
|
+
push(object, groupOrder, instance) {
|
|
86
|
+
const index = this.drawables.length;
|
|
87
|
+
const drawable = this.pool[index] ?? { key: null, groupOrder: 0, renderOrder: 0, z: 0, id: 0, object, instance: null };
|
|
88
|
+
this.pool[index] = drawable;
|
|
89
|
+
drawable.key = instance === null ? null : this.sprites.keyOf(instance);
|
|
90
|
+
drawable.groupOrder = groupOrder;
|
|
91
|
+
drawable.renderOrder = object.renderOrder;
|
|
92
|
+
drawable.z = this.sortDepth(object);
|
|
93
|
+
drawable.id = object.id;
|
|
94
|
+
drawable.object = object;
|
|
95
|
+
drawable.instance = instance;
|
|
96
|
+
this.drawables.push(drawable);
|
|
97
|
+
}
|
|
98
|
+
/** The clip-space z three sorts the object by. */
|
|
99
|
+
sortDepth(object) {
|
|
100
|
+
if (object.isSprite === true) {
|
|
101
|
+
return this.point.setFromMatrixPosition(object.matrixWorld).applyMatrix4(this.projScreen).z;
|
|
102
|
+
}
|
|
103
|
+
const center = sortCenter(object);
|
|
104
|
+
// Vector4.copy of a Vector3, as three does it: w becomes 1.
|
|
105
|
+
return this.point.set(center.x, center.y, center.z, 1).applyMatrix4(object.matrixWorld).applyMatrix4(this.projScreen).z;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** One transparent renderable of a frame, with the fields three sorts it by. */
|
|
2
|
+
export interface Drawable<K> {
|
|
3
|
+
/** The sprite's batch key; null for any other renderable (particles, tilemaps, meshes). */
|
|
4
|
+
key: K | null;
|
|
5
|
+
/** The renderOrder of the nearest ancestor Group (three's `groupOrder`). */
|
|
6
|
+
groupOrder: number;
|
|
7
|
+
renderOrder: number;
|
|
8
|
+
/** Clip-space z of the sort point, as three computes it: higher is farther. */
|
|
9
|
+
z: number;
|
|
10
|
+
/** Object3D id: three's last tie-break, so creation order. */
|
|
11
|
+
id: number;
|
|
12
|
+
}
|
|
13
|
+
/** A Sprite Batch run, or a renderable drawn on its own between runs. */
|
|
14
|
+
export type DrawStep<K, T extends Drawable<K>> = {
|
|
15
|
+
kind: 'run';
|
|
16
|
+
key: K;
|
|
17
|
+
groupOrder: number;
|
|
18
|
+
items: T[];
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'other';
|
|
21
|
+
item: T;
|
|
22
|
+
};
|
|
23
|
+
/** three's `reversePainterSortStable`: the order it draws transparent objects in. */
|
|
24
|
+
export declare function compareTransparentDraws<K>(a: Drawable<K>, b: Drawable<K>): number;
|
|
25
|
+
/** What forEachSpriteRun reports, in draw order. */
|
|
26
|
+
export interface SpriteRunVisitor<K, T extends Drawable<K>> {
|
|
27
|
+
/** A run: the drawables `start` (inclusive) to `end` (exclusive) share `key` and one group order. */
|
|
28
|
+
run(key: K, start: number, end: number): void;
|
|
29
|
+
other(item: T): void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Walks drawables already in draw order and reports each maximal sequence of
|
|
33
|
+
* consecutive same-key sprites under one group order as a run. A sprite of
|
|
34
|
+
* another key, any other renderable, or a change of group order ends the
|
|
35
|
+
* run — so batching never changes what is drawn in front of what (ADR 0024).
|
|
36
|
+
* Allocates nothing: the frame pass calls it every rendered frame.
|
|
37
|
+
*/
|
|
38
|
+
export declare function forEachSpriteRun<K, T extends Drawable<K>>(ordered: readonly T[], visitor: SpriteRunVisitor<K, T>): void;
|
|
39
|
+
/** Orders a frame's drawables exactly as three would draw them, then groups them into runs. */
|
|
40
|
+
export declare function buildSpriteRuns<K, T extends Drawable<K>>(drawables: readonly T[]): DrawStep<K, T>[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** three's `reversePainterSortStable`: the order it draws transparent objects in. */
|
|
2
|
+
export function compareTransparentDraws(a, b) {
|
|
3
|
+
if (a.groupOrder !== b.groupOrder)
|
|
4
|
+
return a.groupOrder - b.groupOrder;
|
|
5
|
+
if (a.renderOrder !== b.renderOrder)
|
|
6
|
+
return a.renderOrder - b.renderOrder;
|
|
7
|
+
if (a.z !== b.z)
|
|
8
|
+
return b.z - a.z;
|
|
9
|
+
return a.id - b.id;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Walks drawables already in draw order and reports each maximal sequence of
|
|
13
|
+
* consecutive same-key sprites under one group order as a run. A sprite of
|
|
14
|
+
* another key, any other renderable, or a change of group order ends the
|
|
15
|
+
* run — so batching never changes what is drawn in front of what (ADR 0024).
|
|
16
|
+
* Allocates nothing: the frame pass calls it every rendered frame.
|
|
17
|
+
*/
|
|
18
|
+
export function forEachSpriteRun(ordered, visitor) {
|
|
19
|
+
let start = 0;
|
|
20
|
+
while (start < ordered.length) {
|
|
21
|
+
const first = ordered[start];
|
|
22
|
+
if (!first)
|
|
23
|
+
return;
|
|
24
|
+
const key = first.key;
|
|
25
|
+
if (key === null) {
|
|
26
|
+
visitor.other(first);
|
|
27
|
+
start += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
let end = start + 1;
|
|
31
|
+
while (end < ordered.length && ordered[end]?.key === key && ordered[end]?.groupOrder === first.groupOrder)
|
|
32
|
+
end += 1;
|
|
33
|
+
visitor.run(key, start, end);
|
|
34
|
+
start = end;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Orders a frame's drawables exactly as three would draw them, then groups them into runs. */
|
|
38
|
+
export function buildSpriteRuns(drawables) {
|
|
39
|
+
const ordered = [...drawables].sort(compareTransparentDraws);
|
|
40
|
+
const steps = [];
|
|
41
|
+
forEachSpriteRun(ordered, {
|
|
42
|
+
run: (key, start, end) => {
|
|
43
|
+
const items = ordered.slice(start, end);
|
|
44
|
+
steps.push({ kind: 'run', key, groupOrder: items[0]?.groupOrder ?? 0, items });
|
|
45
|
+
},
|
|
46
|
+
other: (item) => steps.push({ kind: 'other', item }),
|
|
47
|
+
});
|
|
48
|
+
return steps;
|
|
49
|
+
}
|