@galfus/engine 0.0.0-stage → 0.31.0-alpha
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 +190 -2
- package/package.json +59 -4
- package/src/anchors/cache.ts +101 -0
- package/src/anchors/component.ts +123 -0
- package/src/anchors/hierarchy.ts +192 -0
- package/src/anchors/index.ts +8 -0
- package/src/anchors/solver.ts +247 -0
- package/src/anchors/strategy.ts +142 -0
- package/src/animation/blending.ts +163 -0
- package/src/animation/clip.ts +132 -0
- package/src/animation/coordinator.ts +568 -0
- package/src/animation/index.ts +7 -0
- package/src/animation/native-targets.ts +37 -0
- package/src/animation/player.ts +287 -0
- package/src/audio/index.ts +4 -0
- package/src/audio/manager.ts +371 -0
- package/src/collision/components.ts +211 -0
- package/src/collision/index.ts +16 -0
- package/src/collision/query2d.ts +167 -0
- package/src/collision/query3d.ts +170 -0
- package/src/ecs/component.ts +130 -0
- package/src/ecs/entity.ts +75 -0
- package/src/ecs/index.ts +9 -0
- package/src/ecs/intent.ts +162 -0
- package/src/ecs/metadata.ts +44 -0
- package/src/ecs/system.ts +224 -0
- package/src/ecs/world.ts +291 -0
- package/src/index.ts +19 -0
- package/src/interaction/focus.ts +327 -0
- package/src/interaction/index.ts +9 -0
- package/src/interaction/listener.ts +58 -0
- package/src/interaction/snapshot2d.ts +210 -0
- package/src/interaction/snapshot3d.ts +214 -0
- package/src/interaction/system.ts +544 -0
- package/src/interaction/types.ts +27 -0
- package/src/math/common.ts +67 -0
- package/src/math/index.ts +17 -0
- package/src/math/mat2d.ts +320 -0
- package/src/math/mat4.ts +576 -0
- package/src/math/quat.ts +438 -0
- package/src/math/vec2.ts +161 -0
- package/src/math/vec3.ts +242 -0
- package/src/math/vec4.ts +171 -0
- package/src/multiverse/coordinator.ts +540 -0
- package/src/multiverse/index.ts +13 -0
- package/src/multiverse/presentation-graph.ts +306 -0
- package/src/multiverse/presentation-links.ts +134 -0
- package/src/multiverse/realm.ts +49 -0
- package/src/platform/index.ts +2 -0
- package/src/platform/platform.ts +149 -0
- package/src/platform/window.ts +181 -0
- package/src/realm2d/components.ts +254 -0
- package/src/realm2d/index.ts +12 -0
- package/src/realm2d/presets/index.ts +36 -0
- package/src/realm2d/realm.ts +522 -0
- package/src/realm2d/sync.ts +284 -0
- package/src/realm2d/utils/index.ts +32 -0
- package/src/realm3d/components.ts +341 -0
- package/src/realm3d/geometry.ts +224 -0
- package/src/realm3d/index.ts +12 -0
- package/src/realm3d/presets/index.ts +67 -0
- package/src/realm3d/realm.ts +588 -0
- package/src/realm3d/resource-ids.ts +62 -0
- package/src/realm3d/skinning.ts +128 -0
- package/src/realm3d/sync.ts +442 -0
- package/src/realm3d/utils/index.ts +26 -0
- package/src/resources/index.ts +4 -0
- package/src/resources/manager.ts +92 -0
- package/src/runtime.ts +588 -0
- package/src/spatial/dirty_registry.ts +217 -0
- package/src/spatial/flags.ts +14 -0
- package/src/spatial/index.ts +6 -0
- package/src/spatial/metrics.ts +124 -0
- package/src/text/editor.ts +224 -0
- package/src/text/encoding.ts +38 -0
- package/src/text/index.ts +13 -0
- package/src/text/input.ts +160 -0
- package/src/text/keyboard.ts +50 -0
- package/src/text/svg-decorations.ts +147 -0
- package/src/types.ts +85 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multiverse coordinator managing targets, invocations, and realm orchestration.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AntiAliasingMode,
|
|
7
|
+
GalfusEngine,
|
|
8
|
+
InvocationDesc,
|
|
9
|
+
InvocationFilter,
|
|
10
|
+
MultiverseSchedule,
|
|
11
|
+
TargetDesc,
|
|
12
|
+
TargetTextureBinding,
|
|
13
|
+
ViewportRect
|
|
14
|
+
} from '@galfus/transport-types';
|
|
15
|
+
import { assertValidLogicalId } from '@galfus/transport-types';
|
|
16
|
+
import type { SystemContext } from '../ecs/system';
|
|
17
|
+
import { IdAllocator, type InvocationId, type RealmId, type TargetId } from '../types';
|
|
18
|
+
import {
|
|
19
|
+
PresentationGraph,
|
|
20
|
+
type PresentationGraphView,
|
|
21
|
+
type PresentationSurface
|
|
22
|
+
} from './presentation-graph';
|
|
23
|
+
import { PresentationLinks } from './presentation-links';
|
|
24
|
+
import type { RealmConnector } from './realm';
|
|
25
|
+
|
|
26
|
+
export { type InvocationId, type TargetId } from '../types';
|
|
27
|
+
|
|
28
|
+
export interface InvocationUpdateProps {
|
|
29
|
+
viewport?: ViewportRect;
|
|
30
|
+
zIndex?: number;
|
|
31
|
+
cameraSlot?: number | null;
|
|
32
|
+
filters?: InvocationFilter[];
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
clearColor?: [number, number, number, number] | null;
|
|
35
|
+
antiAliasing?: AntiAliasingMode | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class MultiverseCoordinator {
|
|
39
|
+
private _engine: GalfusEngine | null = null;
|
|
40
|
+
private readonly _targetIdAllocator = new IdAllocator<TargetId>(1);
|
|
41
|
+
private readonly _invocationIdAllocator = new IdAllocator<InvocationId>(1);
|
|
42
|
+
private readonly _realms = new Map<RealmId, RealmConnector>();
|
|
43
|
+
private readonly _targets = new Map<TargetId, TargetDesc>();
|
|
44
|
+
private readonly _invocations = new Map<InvocationId, InvocationDesc>();
|
|
45
|
+
private readonly _dirtyInvocations = new Map<InvocationId, InvocationUpdateProps>();
|
|
46
|
+
private readonly _presentation = new PresentationGraph();
|
|
47
|
+
private readonly _presentationLinks = new PresentationLinks();
|
|
48
|
+
private readonly _boundTargets = new Set<TargetId>();
|
|
49
|
+
private readonly _boundRealms = new Set<RealmId>();
|
|
50
|
+
private readonly _boundInvocations = new Set<InvocationId>();
|
|
51
|
+
private readonly _boundBindings = new Map<RealmId, Map<string, number>>();
|
|
52
|
+
|
|
53
|
+
get presentationGraph(): PresentationGraphView {
|
|
54
|
+
return this._presentation.view;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get engine(): GalfusEngine | null {
|
|
58
|
+
return this._engine;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Binds the coordinator to the active native or WASM GalfusEngine driver.
|
|
63
|
+
*/
|
|
64
|
+
bindEngine(engine: GalfusEngine): void {
|
|
65
|
+
if (this._engine && this._engine !== engine)
|
|
66
|
+
throw new Error('Multiverse is already bound to another engine.');
|
|
67
|
+
this._engine = engine;
|
|
68
|
+
for (const [targetId, desc] of this._targets) {
|
|
69
|
+
if (this._boundTargets.has(targetId)) continue;
|
|
70
|
+
if (desc.kind.type === 'window') {
|
|
71
|
+
engine.multiverseAttachWindow?.(desc.kind.windowId);
|
|
72
|
+
}
|
|
73
|
+
engine.multiverseCreateTarget(targetId, desc);
|
|
74
|
+
this._boundTargets.add(targetId);
|
|
75
|
+
}
|
|
76
|
+
for (const [id, realm] of this._realms) {
|
|
77
|
+
if (this._boundRealms.has(id)) continue;
|
|
78
|
+
realm.bindEngine(engine);
|
|
79
|
+
this._boundRealms.add(id);
|
|
80
|
+
}
|
|
81
|
+
for (const [id, desc] of this._invocations) {
|
|
82
|
+
if (this._boundInvocations.has(id)) continue;
|
|
83
|
+
engine.multiverseCreateInvocation(id, desc);
|
|
84
|
+
this._boundInvocations.add(id);
|
|
85
|
+
this._dirtyInvocations.delete(id);
|
|
86
|
+
}
|
|
87
|
+
for (const binding of this._presentationLinks.getBindings()) {
|
|
88
|
+
const realmId = binding.realmId as RealmId;
|
|
89
|
+
if (this._boundBindings.get(realmId)?.get(binding.slot) === binding.sourceTargetId) continue;
|
|
90
|
+
engine.multiverseBindTargetTexture(binding);
|
|
91
|
+
this._recordBoundBinding(binding);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private _recordBoundBinding(binding: TargetTextureBinding): void {
|
|
96
|
+
const realmId = binding.realmId as RealmId;
|
|
97
|
+
let slots = this._boundBindings.get(realmId);
|
|
98
|
+
if (!slots) {
|
|
99
|
+
slots = new Map();
|
|
100
|
+
this._boundBindings.set(realmId, slots);
|
|
101
|
+
}
|
|
102
|
+
slots.set(binding.slot, binding.sourceTargetId);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Registers a target (Window or Texture) in the multiverse, returning its TargetId.
|
|
107
|
+
*/
|
|
108
|
+
createTarget(desc: TargetDesc): TargetId;
|
|
109
|
+
createTarget(targetId: TargetId | number, desc: TargetDesc): TargetId;
|
|
110
|
+
createTarget(arg1: TargetId | number | TargetDesc, arg2?: TargetDesc): TargetId {
|
|
111
|
+
let targetId: TargetId;
|
|
112
|
+
let desc: TargetDesc;
|
|
113
|
+
|
|
114
|
+
if (typeof arg1 === 'number') {
|
|
115
|
+
targetId = arg1 as TargetId;
|
|
116
|
+
desc = arg2!;
|
|
117
|
+
} else {
|
|
118
|
+
targetId = this._targetIdAllocator.allocate('TargetId');
|
|
119
|
+
desc = arg1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
assertValidLogicalId(targetId, 'TargetId');
|
|
123
|
+
if (this._targets.has(targetId)) {
|
|
124
|
+
throw new Error(`Multiverse target ${targetId} is already created.`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const storedDesc = structuredClone(desc);
|
|
128
|
+
if (this._engine) {
|
|
129
|
+
if (desc.kind.type === 'window') {
|
|
130
|
+
this._engine.multiverseAttachWindow?.(desc.kind.windowId);
|
|
131
|
+
}
|
|
132
|
+
this._engine.multiverseCreateTarget(targetId, storedDesc);
|
|
133
|
+
this._boundTargets.add(targetId);
|
|
134
|
+
}
|
|
135
|
+
this._targets.set(targetId, storedDesc);
|
|
136
|
+
this._targetIdAllocator.markUsed(targetId, 'TargetId');
|
|
137
|
+
return targetId;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Destroys a render target.
|
|
142
|
+
*/
|
|
143
|
+
destroyTarget(targetId: TargetId | number): void {
|
|
144
|
+
const id = targetId as TargetId;
|
|
145
|
+
if (!this._targets.has(id)) return;
|
|
146
|
+
if (this._engine && this._boundTargets.has(id)) {
|
|
147
|
+
this._engine.multiverseDestroyTarget(id);
|
|
148
|
+
}
|
|
149
|
+
for (const [invId, inv] of this._invocations) {
|
|
150
|
+
if (inv.targetId === id) this._forgetInvocation(invId);
|
|
151
|
+
}
|
|
152
|
+
for (const binding of this._presentationLinks.getBindingsForTarget(id)) {
|
|
153
|
+
this._presentationLinks.unbind(binding.realmId, binding.slot);
|
|
154
|
+
this._boundBindings.get(binding.realmId as RealmId)?.delete(binding.slot);
|
|
155
|
+
}
|
|
156
|
+
this._boundTargets.delete(id);
|
|
157
|
+
this._targets.delete(id);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Retrieves a registered target description by ID.
|
|
162
|
+
*/
|
|
163
|
+
getTarget(targetId: TargetId | number): Readonly<TargetDesc> | undefined {
|
|
164
|
+
return this._targets.get(targetId as TargetId);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Registers a specialized realm connector (e.g. Realm2D or Realm3D).
|
|
169
|
+
*/
|
|
170
|
+
registerRealm(realm: RealmConnector): void {
|
|
171
|
+
const realmId = realm.realmId as RealmId;
|
|
172
|
+
assertValidLogicalId(realmId, 'RealmId');
|
|
173
|
+
if (this._realms.has(realmId)) {
|
|
174
|
+
throw new Error(`Realm ${realmId} is already registered in Multiverse.`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (this._engine) {
|
|
178
|
+
realm.bindEngine(this._engine);
|
|
179
|
+
this._boundRealms.add(realmId);
|
|
180
|
+
}
|
|
181
|
+
this._realms.set(realmId, realm);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Unregisters and disposes a realm connector, removing any dependent invocations.
|
|
186
|
+
*/
|
|
187
|
+
unregisterRealm(realmId: RealmId | number): void {
|
|
188
|
+
const id = realmId as RealmId;
|
|
189
|
+
const realm = this._realms.get(id);
|
|
190
|
+
if (!realm) return;
|
|
191
|
+
|
|
192
|
+
for (const [invId, inv] of this._invocations) {
|
|
193
|
+
if (inv.realmId === id) this.destroyInvocation(invId);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
realm.dispose();
|
|
197
|
+
if (
|
|
198
|
+
this._engine &&
|
|
199
|
+
this._boundRealms.has(id) &&
|
|
200
|
+
realm.realmKind !== '2d' &&
|
|
201
|
+
realm.realmKind !== '3d'
|
|
202
|
+
) {
|
|
203
|
+
this._engine.multiverseUnregisterRealm?.(id);
|
|
204
|
+
}
|
|
205
|
+
for (const binding of this._presentationLinks.getBindingsForRealm(id))
|
|
206
|
+
this._presentationLinks.unbind(id, binding.slot);
|
|
207
|
+
this._boundBindings.delete(id);
|
|
208
|
+
this._boundRealms.delete(id);
|
|
209
|
+
this._realms.delete(id);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Retrieves a registered realm connector by ID.
|
|
214
|
+
*/
|
|
215
|
+
getRealm<T extends RealmConnector = RealmConnector>(realmId: RealmId | number): T | undefined {
|
|
216
|
+
return this._realms.get(realmId as RealmId) as T | undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Returns all registered realm connectors.
|
|
221
|
+
*/
|
|
222
|
+
getRealms(): Iterable<RealmConnector> {
|
|
223
|
+
return this._realms.values();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Creates an invocation linking a realm to a target with viewport parameters, returning InvocationId.
|
|
228
|
+
*/
|
|
229
|
+
createInvocation(desc: InvocationDesc): InvocationId;
|
|
230
|
+
createInvocation(invocationId: InvocationId | number, desc: InvocationDesc): InvocationId;
|
|
231
|
+
createInvocation(
|
|
232
|
+
arg1: InvocationId | number | InvocationDesc,
|
|
233
|
+
arg2?: InvocationDesc
|
|
234
|
+
): InvocationId {
|
|
235
|
+
let invocationId: InvocationId;
|
|
236
|
+
let desc: InvocationDesc;
|
|
237
|
+
|
|
238
|
+
if (typeof arg1 === 'number') {
|
|
239
|
+
invocationId = arg1 as InvocationId;
|
|
240
|
+
desc = arg2!;
|
|
241
|
+
} else {
|
|
242
|
+
invocationId = this._invocationIdAllocator.allocate('InvocationId');
|
|
243
|
+
desc = arg1;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
assertValidLogicalId(invocationId, 'InvocationId');
|
|
247
|
+
if (this._invocations.has(invocationId)) {
|
|
248
|
+
throw new Error(`Invocation ${invocationId} is already created.`);
|
|
249
|
+
}
|
|
250
|
+
if (!this._realms.has(desc.realmId as RealmId))
|
|
251
|
+
throw new Error(`Realm ${desc.realmId} is not registered.`);
|
|
252
|
+
if (!this._targets.has(desc.targetId as TargetId))
|
|
253
|
+
throw new Error(`Target ${desc.targetId} is not registered.`);
|
|
254
|
+
|
|
255
|
+
const storedDesc: InvocationDesc = structuredClone(desc);
|
|
256
|
+
storedDesc.viewport ??= { width: { vw: 100 }, height: { vh: 100 } };
|
|
257
|
+
storedDesc.filters ??= [];
|
|
258
|
+
|
|
259
|
+
if (this._engine) {
|
|
260
|
+
this._engine.multiverseCreateInvocation(invocationId, storedDesc);
|
|
261
|
+
this._boundInvocations.add(invocationId);
|
|
262
|
+
}
|
|
263
|
+
this._invocations.set(invocationId, storedDesc);
|
|
264
|
+
this._invocationIdAllocator.markUsed(invocationId, 'InvocationId');
|
|
265
|
+
return invocationId;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Retrieves the current stored presentation descriptor of an invocation.
|
|
270
|
+
*/
|
|
271
|
+
getInvocation(invocationId: InvocationId | number): InvocationDesc | undefined {
|
|
272
|
+
const desc = this._invocations.get(invocationId as InvocationId);
|
|
273
|
+
return desc ? structuredClone(desc) : undefined;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Updates an invocation's presentation descriptor (viewport, filters, clearColor, etc.).
|
|
278
|
+
*/
|
|
279
|
+
updateInvocation(
|
|
280
|
+
invocationId: InvocationId | number,
|
|
281
|
+
patch: Partial<InvocationUpdateProps>
|
|
282
|
+
): void {
|
|
283
|
+
const id = invocationId as InvocationId;
|
|
284
|
+
const inv = this._invocations.get(id);
|
|
285
|
+
if (!inv) {
|
|
286
|
+
throw new Error(`Cannot update non-existent invocation ${id}.`);
|
|
287
|
+
}
|
|
288
|
+
if ('realmId' in patch || 'targetId' in patch) {
|
|
289
|
+
throw new Error('Cannot mutate realmId or targetId of an existing invocation.');
|
|
290
|
+
}
|
|
291
|
+
if (
|
|
292
|
+
patch.zIndex !== undefined &&
|
|
293
|
+
(!Number.isInteger(patch.zIndex) || patch.zIndex < -2147483648 || patch.zIndex > 2147483647)
|
|
294
|
+
) {
|
|
295
|
+
throw new Error('Invocation zIndex must be a signed 32-bit integer.');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (patch.viewport !== undefined) inv.viewport = structuredClone(patch.viewport);
|
|
299
|
+
if (patch.zIndex !== undefined) inv.zIndex = patch.zIndex;
|
|
300
|
+
if (patch.cameraSlot !== undefined) inv.cameraSlot = patch.cameraSlot ?? undefined;
|
|
301
|
+
if (patch.filters !== undefined) inv.filters = structuredClone(patch.filters);
|
|
302
|
+
if (patch.enabled !== undefined) inv.enabled = patch.enabled;
|
|
303
|
+
if (patch.clearColor !== undefined)
|
|
304
|
+
inv.clearColor = patch.clearColor ? [...patch.clearColor] : undefined;
|
|
305
|
+
if (patch.antiAliasing !== undefined) inv.antiAliasing = patch.antiAliasing ?? undefined;
|
|
306
|
+
|
|
307
|
+
const existing = this._dirtyInvocations.get(id) ?? {};
|
|
308
|
+
this._dirtyInvocations.set(id, { ...existing, ...patch });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Flushes all coalesced dirty invocation updates to the native engine driver.
|
|
313
|
+
*/
|
|
314
|
+
flushUpdates(): void {
|
|
315
|
+
if (!this._engine || this._dirtyInvocations.size === 0) return;
|
|
316
|
+
for (const [id, patch] of this._dirtyInvocations) {
|
|
317
|
+
if (!this._invocations.has(id)) continue;
|
|
318
|
+
const inv = this._invocations.get(id)!;
|
|
319
|
+
if (
|
|
320
|
+
patch.viewport !== undefined &&
|
|
321
|
+
inv.viewport &&
|
|
322
|
+
this._engine.multiverseSetInvocationViewport
|
|
323
|
+
) {
|
|
324
|
+
this._engine.multiverseSetInvocationViewport(id, inv.viewport);
|
|
325
|
+
}
|
|
326
|
+
if (patch.enabled !== undefined && this._engine.multiverseSetInvocationEnabled) {
|
|
327
|
+
this._engine.multiverseSetInvocationEnabled(id, inv.enabled ?? true);
|
|
328
|
+
}
|
|
329
|
+
if (patch.zIndex !== undefined && this._engine.multiverseSetInvocationZIndex) {
|
|
330
|
+
this._engine.multiverseSetInvocationZIndex(id, inv.zIndex ?? 0);
|
|
331
|
+
}
|
|
332
|
+
if (patch.filters !== undefined && this._engine.multiverseSetInvocationFilters) {
|
|
333
|
+
this._engine.multiverseSetInvocationFilters(id, inv.filters ?? []);
|
|
334
|
+
}
|
|
335
|
+
if (patch.clearColor !== undefined && this._engine.multiverseSetInvocationClearColor) {
|
|
336
|
+
const [r, g, b, a] = inv.clearColor ?? [0, 0, 0, 0];
|
|
337
|
+
this._engine.multiverseSetInvocationClearColor(id, r, g, b, a, !!inv.clearColor);
|
|
338
|
+
}
|
|
339
|
+
if (patch.cameraSlot !== undefined && this._engine.multiverseSetInvocationCameraSlot) {
|
|
340
|
+
this._engine.multiverseSetInvocationCameraSlot(
|
|
341
|
+
id,
|
|
342
|
+
inv.cameraSlot ?? 0,
|
|
343
|
+
inv.cameraSlot !== undefined && inv.cameraSlot !== null
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
this._dirtyInvocations.clear();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Destroys an existing invocation.
|
|
352
|
+
*/
|
|
353
|
+
destroyInvocation(invocationId: InvocationId | number): void {
|
|
354
|
+
const id = invocationId as InvocationId;
|
|
355
|
+
const desc = this._invocations.get(id);
|
|
356
|
+
if (!desc) return;
|
|
357
|
+
if (this._engine && this._boundInvocations.has(id)) {
|
|
358
|
+
this._engine.multiverseDestroyInvocation(id);
|
|
359
|
+
}
|
|
360
|
+
this._forgetInvocation(id);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private _forgetInvocation(id: InvocationId): void {
|
|
364
|
+
this._dirtyInvocations.delete(id);
|
|
365
|
+
this._invocations.delete(id);
|
|
366
|
+
this._boundInvocations.delete(id);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Binds an output target texture to a realm input slot (e.g. portal or render-to-texture).
|
|
371
|
+
*/
|
|
372
|
+
bindTargetTexture(binding: TargetTextureBinding): void {
|
|
373
|
+
const target = this._targets.get(binding.sourceTargetId as TargetId);
|
|
374
|
+
if (!target || target.kind.type !== 'texture')
|
|
375
|
+
throw new Error('Texture binding requires a registered texture target.');
|
|
376
|
+
if (!this._realms.has(binding.realmId as RealmId))
|
|
377
|
+
throw new Error(`Realm ${binding.realmId} is not registered.`);
|
|
378
|
+
if (this._engine) {
|
|
379
|
+
this._engine.multiverseBindTargetTexture(binding);
|
|
380
|
+
this._recordBoundBinding(binding);
|
|
381
|
+
}
|
|
382
|
+
this._presentationLinks.bind(binding);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Unbinds a target texture from a realm input slot.
|
|
387
|
+
*/
|
|
388
|
+
unbindTargetTexture(realmId: RealmId | number, slot: string): void {
|
|
389
|
+
if (this._engine && this._boundBindings.get(realmId as RealmId)?.has(slot)) {
|
|
390
|
+
this._engine.multiverseUnbindTargetTexture(realmId as RealmId, slot);
|
|
391
|
+
}
|
|
392
|
+
this._boundBindings.get(realmId as RealmId)?.delete(slot);
|
|
393
|
+
this._presentationLinks.unbind(realmId, slot);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** @internal Called only after a surface-to-texture mapping has been verified. */
|
|
397
|
+
connectPresentationSurface(surface: PresentationSurface): void {
|
|
398
|
+
assertValidLogicalId(surface.entityId, 'EntityId');
|
|
399
|
+
this._presentationLinks.connectSurface(surface);
|
|
400
|
+
}
|
|
401
|
+
/** @internal Removes a verified surface mapping. */
|
|
402
|
+
disconnectPresentationSurface(surface: PresentationSurface): void {
|
|
403
|
+
this._presentationLinks.disconnectSurface(surface);
|
|
404
|
+
}
|
|
405
|
+
/** @internal Called after the platform window has actually closed. */
|
|
406
|
+
windowClosed(windowId: number): void {
|
|
407
|
+
this._presentation.windowClosed(windowId);
|
|
408
|
+
}
|
|
409
|
+
/** @internal Called when a platform window becomes available. */
|
|
410
|
+
windowOpened(windowId: number): void {
|
|
411
|
+
this._presentation.windowOpened(windowId);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Registers a custom WGSL post-processing shader.
|
|
416
|
+
*/
|
|
417
|
+
registerShader(shaderId: number, source: string): void {
|
|
418
|
+
assertValidLogicalId(shaderId, 'ShaderId');
|
|
419
|
+
if (this._engine) {
|
|
420
|
+
this._engine.multiverseRegisterShader(shaderId, source);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Updates anti-aliasing configuration on a target.
|
|
426
|
+
*/
|
|
427
|
+
setAntiAliasing(_targetId: TargetId | number, mode: AntiAliasingMode): void {
|
|
428
|
+
if (this._engine) {
|
|
429
|
+
this._engine.multiverseSetAntiAliasing(mode);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Steps all registered realms through pre-render stages (input, update, preRender).
|
|
435
|
+
*/
|
|
436
|
+
stepRealmsPreRender(context: SystemContext): void {
|
|
437
|
+
for (const realm of this._realms.values()) {
|
|
438
|
+
if (typeof realm.preRender === 'function') {
|
|
439
|
+
realm.preRender(context);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Steps all registered realms through post-render stage (postRender) after dispatch.
|
|
446
|
+
*/
|
|
447
|
+
stepRealmsPostRender(context: SystemContext): void {
|
|
448
|
+
for (const realm of this._realms.values()) {
|
|
449
|
+
if (typeof realm.postRender === 'function') {
|
|
450
|
+
realm.postRender(context);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Synchronizes all realm states into the native/WASM core.
|
|
457
|
+
*/
|
|
458
|
+
syncRealms(context?: SystemContext): void {
|
|
459
|
+
for (const realm of this._realms.values()) {
|
|
460
|
+
(realm as any).sync(context);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Resolves responsive multiverse viewports and scheduling before dispatch.
|
|
466
|
+
*/
|
|
467
|
+
resolve(): any {
|
|
468
|
+
this.flushUpdates();
|
|
469
|
+
this._purgeDeadPresentationSurfaces();
|
|
470
|
+
if (this._engine && typeof (this._engine as any).multiverseResolve === 'function') {
|
|
471
|
+
return (this._engine as any).multiverseResolve();
|
|
472
|
+
}
|
|
473
|
+
return this.getSchedule() ?? { invocations: [] };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
private _purgeDeadPresentationSurfaces(): void {
|
|
477
|
+
this._presentationLinks.purgeDeadSurfaces((realmId, entityId) => {
|
|
478
|
+
const realm = this._realms.get(realmId as RealmId);
|
|
479
|
+
if (!realm || (realm as any).isDisposed) return false;
|
|
480
|
+
return (realm as any).world?.isAlive(entityId) ?? true;
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Dispatches the native multiverse rendering pass for all active invocations.
|
|
486
|
+
*/
|
|
487
|
+
dispatch(frameId?: number, dt?: number): void {
|
|
488
|
+
if (!this._engine) return;
|
|
489
|
+
const schedule = this.resolve();
|
|
490
|
+
this._presentation.stage(schedule, this._targets, this._presentationLinks.snapshot());
|
|
491
|
+
try {
|
|
492
|
+
this._engine.multiverseDispatch(frameId ?? 0, dt ?? 0);
|
|
493
|
+
this._presentation.commit();
|
|
494
|
+
} catch (error) {
|
|
495
|
+
this._presentation.discard();
|
|
496
|
+
throw error;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Queries the current active schedule of all realms, targets, and invocations.
|
|
502
|
+
*/
|
|
503
|
+
getSchedule(): MultiverseSchedule | undefined {
|
|
504
|
+
return (this._engine as any)?.multiverseGetSchedule?.();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Resets all invocations, targets, and realms.
|
|
509
|
+
*/
|
|
510
|
+
reset(): void {
|
|
511
|
+
for (const invocationId of [...this._invocations.keys()]) {
|
|
512
|
+
this.destroyInvocation(invocationId);
|
|
513
|
+
}
|
|
514
|
+
for (const realm of [...this._realms.values()]) {
|
|
515
|
+
this.unregisterRealm(realm.realmId);
|
|
516
|
+
}
|
|
517
|
+
for (const targetId of [...this._targets.keys()]) {
|
|
518
|
+
this.destroyTarget(targetId);
|
|
519
|
+
}
|
|
520
|
+
this._realms.clear();
|
|
521
|
+
this._targets.clear();
|
|
522
|
+
this._invocations.clear();
|
|
523
|
+
this._dirtyInvocations.clear();
|
|
524
|
+
this._presentation.clear();
|
|
525
|
+
this._presentationLinks.clear();
|
|
526
|
+
this._boundTargets.clear();
|
|
527
|
+
this._boundRealms.clear();
|
|
528
|
+
this._boundInvocations.clear();
|
|
529
|
+
this._boundBindings.clear();
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Releases all multiverse resources.
|
|
534
|
+
*/
|
|
535
|
+
dispose(): void {
|
|
536
|
+
this.reset();
|
|
537
|
+
this._presentation.dispose();
|
|
538
|
+
this._engine = null;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multiverse orchestration subsystem.
|
|
3
|
+
*/
|
|
4
|
+
export * from './coordinator';
|
|
5
|
+
export type {
|
|
6
|
+
PresentedInvocation,
|
|
7
|
+
PresentationBinding,
|
|
8
|
+
PresentationConnection,
|
|
9
|
+
PresentationGraphView,
|
|
10
|
+
PresentationSurface,
|
|
11
|
+
WindowPresentation
|
|
12
|
+
} from './presentation-graph';
|
|
13
|
+
export * from './realm';
|