@vulfram/engine 0.14.8-alpha → 0.19.2-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 +106 -0
- package/package.json +60 -4
- package/src/core.ts +14 -0
- package/src/ecs.ts +1 -0
- package/src/engine/api.ts +222 -24
- package/src/engine/bridge/dispatch.ts +260 -40
- package/src/engine/bridge/guards.ts +4 -1
- package/src/engine/bridge/protocol.ts +69 -52
- package/src/engine/ecs/components.ts +340 -0
- package/src/engine/ecs/index.ts +3 -518
- package/src/engine/ecs/intents.ts +184 -0
- package/src/engine/ecs/systems.ts +26 -0
- package/src/engine/intents/store.ts +72 -0
- package/src/engine/state.ts +136 -5
- package/src/engine/systems/command-intent.ts +159 -14
- package/src/engine/systems/constraint-solve.ts +167 -0
- package/src/engine/systems/core-command-builder.ts +9 -268
- package/src/engine/systems/diagnostics.ts +20 -29
- package/src/engine/systems/index.ts +3 -1
- package/src/engine/systems/input-mirror.ts +257 -21
- package/src/engine/systems/resource-upload.ts +108 -58
- package/src/engine/systems/response-decode.ts +86 -15
- package/src/engine/systems/scene-sync.ts +305 -0
- package/src/engine/systems/ui-bridge.ts +381 -0
- package/src/engine/systems/utils.ts +86 -1
- package/src/engine/systems/world-lifecycle.ts +43 -114
- package/src/engine/window/manager.ts +168 -0
- package/src/engine/world/entities.ts +998 -91
- package/src/engine/world/mount.ts +195 -0
- package/src/engine/world/types.ts +71 -0
- package/src/engine/world/world-ui.ts +313 -0
- package/src/engine/world/world3d.ts +529 -0
- package/src/helpers/collision.ts +487 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/raycast.ts +442 -0
- package/src/index.ts +30 -1
- package/src/mount.ts +2 -0
- package/src/types/cmds/audio.ts +73 -48
- package/src/types/cmds/camera.ts +12 -8
- package/src/types/cmds/environment.ts +9 -3
- package/src/types/cmds/geometry.ts +15 -16
- package/src/types/cmds/index.ts +234 -162
- package/src/types/cmds/input.ts +39 -0
- package/src/types/cmds/light.ts +12 -11
- package/src/types/cmds/material.ts +19 -21
- package/src/types/cmds/model.ts +17 -15
- package/src/types/cmds/realm.ts +23 -0
- package/src/types/cmds/system.ts +29 -0
- package/src/types/cmds/target.ts +96 -0
- package/src/types/cmds/texture.ts +13 -3
- package/src/types/cmds/ui.ts +220 -0
- package/src/types/cmds/window.ts +41 -204
- package/src/types/events/index.ts +4 -1
- package/src/types/events/keyboard.ts +2 -2
- package/src/types/events/pointer.ts +85 -13
- package/src/types/events/system.ts +188 -30
- package/src/types/events/ui.ts +21 -0
- package/src/types/index.ts +1 -0
- package/src/types/json.ts +15 -0
- package/src/window.ts +8 -0
- package/src/world-ui.ts +2 -0
- package/src/world3d.ts +10 -0
- package/tsconfig.json +0 -29
|
@@ -1,268 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
TransformComponent,
|
|
11
|
-
} from '../ecs';
|
|
12
|
-
import { getEntityTransformMatrix, toQuat, toVec2, toVec3, toVec4 } from './utils';
|
|
13
|
-
|
|
14
|
-
export const CoreCommandBuilderSystem: System = (world, context) => {
|
|
15
|
-
const intentsToRemove: number[] = [];
|
|
16
|
-
|
|
17
|
-
for (let i = 0; i < world.pendingIntents.length; i++) {
|
|
18
|
-
const intent = world.pendingIntents[i];
|
|
19
|
-
if (!intent) continue;
|
|
20
|
-
|
|
21
|
-
if (intent.type === 'attach-model') {
|
|
22
|
-
const modelId = world.nextCoreId++;
|
|
23
|
-
const transform = getEntityTransformMatrix(world, intent.entityId);
|
|
24
|
-
const castShadow = intent.props.castShadow ?? true;
|
|
25
|
-
const receiveShadow = intent.props.receiveShadow ?? true;
|
|
26
|
-
const castOutline = intent.props.castOutline ?? false;
|
|
27
|
-
const outlineColor = intent.props.outlineColor ?? ([0, 0, 0, 0] as const);
|
|
28
|
-
|
|
29
|
-
enqueueCommand(context.worldId, 'cmd-model-create', {
|
|
30
|
-
windowId: context.worldId,
|
|
31
|
-
modelId,
|
|
32
|
-
geometryId: intent.props.geometryId,
|
|
33
|
-
materialId: intent.props.materialId,
|
|
34
|
-
transform: Array.from(transform),
|
|
35
|
-
layerMask: 0x7fffffff,
|
|
36
|
-
castShadow,
|
|
37
|
-
receiveShadow,
|
|
38
|
-
castOutline,
|
|
39
|
-
outlineColor: [...outlineColor] as [number, number, number, number],
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
let store = world.components.get(intent.entityId);
|
|
43
|
-
if (!store) {
|
|
44
|
-
store = new Map();
|
|
45
|
-
world.components.set(intent.entityId, store);
|
|
46
|
-
}
|
|
47
|
-
store.set('Model', {
|
|
48
|
-
type: 'Model',
|
|
49
|
-
id: modelId,
|
|
50
|
-
geometryId: intent.props.geometryId,
|
|
51
|
-
materialId: intent.props.materialId,
|
|
52
|
-
castShadow: intent.props.castShadow ?? true,
|
|
53
|
-
receiveShadow: intent.props.receiveShadow ?? true,
|
|
54
|
-
castOutline,
|
|
55
|
-
outlineColor: [...outlineColor] as [number, number, number, number],
|
|
56
|
-
skipUpdate: true,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
intentsToRemove.push(i);
|
|
60
|
-
} else if (intent.type === 'attach-camera') {
|
|
61
|
-
const cameraId = world.nextCoreId++;
|
|
62
|
-
const transform = getEntityTransformMatrix(world, intent.entityId);
|
|
63
|
-
|
|
64
|
-
enqueueCommand(context.worldId, 'cmd-camera-create', {
|
|
65
|
-
cameraId,
|
|
66
|
-
label: `Cam ${cameraId}`,
|
|
67
|
-
kind: intent.props.kind ?? ('perspective' as CameraKind),
|
|
68
|
-
flags: 0,
|
|
69
|
-
nearFar: [intent.props.near ?? 0.1, intent.props.far ?? 1000],
|
|
70
|
-
order: intent.props.order ?? 0,
|
|
71
|
-
transform: Array.from(transform),
|
|
72
|
-
layerMask: 0x7fffffff,
|
|
73
|
-
orthoScale: 1.0,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
let store = world.components.get(intent.entityId);
|
|
77
|
-
if (!store) {
|
|
78
|
-
store = new Map();
|
|
79
|
-
world.components.set(intent.entityId, store);
|
|
80
|
-
}
|
|
81
|
-
store.set('Camera', {
|
|
82
|
-
type: 'Camera',
|
|
83
|
-
id: cameraId,
|
|
84
|
-
kind: intent.props.kind ?? ('perspective' as CameraKind),
|
|
85
|
-
near: intent.props.near ?? 0.1,
|
|
86
|
-
far: intent.props.far ?? 1000,
|
|
87
|
-
order: intent.props.order ?? 0,
|
|
88
|
-
orthoScale: 1.0,
|
|
89
|
-
skipUpdate: true,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
intentsToRemove.push(i);
|
|
93
|
-
} else if (intent.type === 'attach-light') {
|
|
94
|
-
const lightId = world.nextCoreId++;
|
|
95
|
-
const transform = getEntityTransformMatrix(world, intent.entityId);
|
|
96
|
-
|
|
97
|
-
const pos = vec3.create();
|
|
98
|
-
mat4.getTranslation(pos, transform);
|
|
99
|
-
const direction = intent.props.direction
|
|
100
|
-
? toVec3(intent.props.direction)
|
|
101
|
-
: ([0, 0, -1] as [number, number, number]);
|
|
102
|
-
const color = intent.props.color
|
|
103
|
-
? toVec3(intent.props.color)
|
|
104
|
-
: ([1, 1, 1] as [number, number, number]);
|
|
105
|
-
|
|
106
|
-
const lightCmd: {
|
|
107
|
-
windowId: number;
|
|
108
|
-
lightId: number;
|
|
109
|
-
kind: LightKind;
|
|
110
|
-
color: [number, number, number, number];
|
|
111
|
-
intensity: number;
|
|
112
|
-
range: number;
|
|
113
|
-
castShadow: boolean;
|
|
114
|
-
position: [number, number, number, number];
|
|
115
|
-
layerMask: number;
|
|
116
|
-
direction?: [number, number, number, number];
|
|
117
|
-
spotInnerOuter?: [number, number];
|
|
118
|
-
} = {
|
|
119
|
-
windowId: context.worldId,
|
|
120
|
-
lightId,
|
|
121
|
-
kind: intent.props.kind ?? ('directional' as LightKind),
|
|
122
|
-
color: [...color, 1] as [number, number, number, number],
|
|
123
|
-
intensity: intent.props.intensity ?? 1.0,
|
|
124
|
-
range: intent.props.range ?? 10.0,
|
|
125
|
-
castShadow: intent.props.castShadow ?? true,
|
|
126
|
-
position: [pos[0], pos[1], pos[2], 1],
|
|
127
|
-
layerMask: 0x7fffffff,
|
|
128
|
-
};
|
|
129
|
-
if (direction) {
|
|
130
|
-
const [dirX, dirY, dirZ] = direction;
|
|
131
|
-
lightCmd.direction = [dirX, dirY, dirZ, 0];
|
|
132
|
-
}
|
|
133
|
-
if (intent.props.spotInnerOuter) {
|
|
134
|
-
lightCmd.spotInnerOuter = toVec2(intent.props.spotInnerOuter);
|
|
135
|
-
}
|
|
136
|
-
enqueueCommand(context.worldId, 'cmd-light-create', lightCmd);
|
|
137
|
-
|
|
138
|
-
let store = world.components.get(intent.entityId);
|
|
139
|
-
if (!store) {
|
|
140
|
-
store = new Map();
|
|
141
|
-
world.components.set(intent.entityId, store);
|
|
142
|
-
}
|
|
143
|
-
store.set('Light', {
|
|
144
|
-
type: 'Light',
|
|
145
|
-
id: lightId,
|
|
146
|
-
kind: intent.props.kind ?? ('directional' as LightKind),
|
|
147
|
-
color,
|
|
148
|
-
intensity: intent.props.intensity ?? 1.0,
|
|
149
|
-
range: intent.props.range ?? 10.0,
|
|
150
|
-
castShadow: intent.props.castShadow ?? true,
|
|
151
|
-
direction,
|
|
152
|
-
spotInnerOuter: intent.props.spotInnerOuter
|
|
153
|
-
? toVec2(intent.props.spotInnerOuter)
|
|
154
|
-
: [0.2, 0.6],
|
|
155
|
-
skipUpdate: true,
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
intentsToRemove.push(i);
|
|
159
|
-
} else if (intent.type === 'update-transform') {
|
|
160
|
-
const store = world.components.get(intent.entityId);
|
|
161
|
-
if (store) {
|
|
162
|
-
const transform = store.get('Transform') as
|
|
163
|
-
| TransformComponent
|
|
164
|
-
| undefined;
|
|
165
|
-
if (transform) {
|
|
166
|
-
const nextProps = { ...intent.props };
|
|
167
|
-
if (nextProps.position) {
|
|
168
|
-
nextProps.position = toVec3(nextProps.position);
|
|
169
|
-
}
|
|
170
|
-
if (nextProps.rotation) {
|
|
171
|
-
nextProps.rotation = toQuat(nextProps.rotation);
|
|
172
|
-
}
|
|
173
|
-
if (nextProps.scale) {
|
|
174
|
-
nextProps.scale = toVec3(nextProps.scale);
|
|
175
|
-
}
|
|
176
|
-
Object.assign(transform, nextProps);
|
|
177
|
-
const matrix = getEntityTransformMatrix(world, intent.entityId);
|
|
178
|
-
const matrixArray = Array.from(matrix);
|
|
179
|
-
|
|
180
|
-
const model = store.get('Model') as ModelComponent | undefined;
|
|
181
|
-
if (model) {
|
|
182
|
-
if (model.skipUpdate) {
|
|
183
|
-
model.skipUpdate = false;
|
|
184
|
-
} else {
|
|
185
|
-
enqueueCommand(context.worldId, 'cmd-model-update', {
|
|
186
|
-
windowId: context.worldId,
|
|
187
|
-
modelId: model.id,
|
|
188
|
-
transform: matrixArray,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
const camera = store.get('Camera') as CameraComponent | undefined;
|
|
193
|
-
if (camera) {
|
|
194
|
-
if (camera.skipUpdate) {
|
|
195
|
-
camera.skipUpdate = false;
|
|
196
|
-
} else {
|
|
197
|
-
enqueueCommand(context.worldId, 'cmd-camera-update', {
|
|
198
|
-
cameraId: camera.id,
|
|
199
|
-
transform: matrixArray,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
const light = store.get('Light') as LightComponent | undefined;
|
|
204
|
-
if (light) {
|
|
205
|
-
if (light.skipUpdate) {
|
|
206
|
-
light.skipUpdate = false;
|
|
207
|
-
} else {
|
|
208
|
-
const pos = vec3.create();
|
|
209
|
-
mat4.getTranslation(pos, matrix);
|
|
210
|
-
enqueueCommand(context.worldId, 'cmd-light-update', {
|
|
211
|
-
windowId: context.worldId,
|
|
212
|
-
lightId: light.id,
|
|
213
|
-
position: [pos[0], pos[1], pos[2], 1],
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
intentsToRemove.push(i);
|
|
220
|
-
} else if (intent.type === 'detach-component') {
|
|
221
|
-
const store = world.components.get(intent.entityId);
|
|
222
|
-
if (store) {
|
|
223
|
-
const comp = store.get(intent.componentType) as Component | undefined;
|
|
224
|
-
if (comp && 'id' in comp) {
|
|
225
|
-
if (intent.componentType === 'Model') {
|
|
226
|
-
const modelComp = comp as ModelComponent;
|
|
227
|
-
enqueueCommand(context.worldId, 'cmd-model-dispose', {
|
|
228
|
-
windowId: context.worldId,
|
|
229
|
-
modelId: modelComp.id,
|
|
230
|
-
});
|
|
231
|
-
} else if (intent.componentType === 'Camera') {
|
|
232
|
-
const cameraComp = comp as CameraComponent;
|
|
233
|
-
enqueueCommand(context.worldId, 'cmd-camera-dispose', {
|
|
234
|
-
cameraId: cameraComp.id,
|
|
235
|
-
});
|
|
236
|
-
} else if (intent.componentType === 'Light') {
|
|
237
|
-
const lightComp = comp as LightComponent;
|
|
238
|
-
enqueueCommand(context.worldId, 'cmd-light-dispose', {
|
|
239
|
-
windowId: context.worldId,
|
|
240
|
-
lightId: lightComp.id,
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
store.delete(intent.componentType);
|
|
245
|
-
}
|
|
246
|
-
intentsToRemove.push(i);
|
|
247
|
-
} else if (intent.type === 'gizmo-draw-line') {
|
|
248
|
-
enqueueCommand(context.worldId, 'cmd-gizmo-draw-line', {
|
|
249
|
-
start: toVec3(intent.start),
|
|
250
|
-
end: toVec3(intent.end),
|
|
251
|
-
color: toVec4(intent.color),
|
|
252
|
-
});
|
|
253
|
-
intentsToRemove.push(i);
|
|
254
|
-
} else if (intent.type === 'gizmo-draw-aabb') {
|
|
255
|
-
enqueueCommand(context.worldId, 'cmd-gizmo-draw-aabb', {
|
|
256
|
-
min: toVec3(intent.min),
|
|
257
|
-
max: toVec3(intent.max),
|
|
258
|
-
color: toVec4(intent.color),
|
|
259
|
-
});
|
|
260
|
-
intentsToRemove.push(i);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
for (let i = intentsToRemove.length - 1; i >= 0; i--) {
|
|
265
|
-
const idx = intentsToRemove[i];
|
|
266
|
-
if (idx !== undefined) world.pendingIntents.splice(idx, 1);
|
|
267
|
-
}
|
|
268
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Backward-compatible module shim.
|
|
3
|
+
*
|
|
4
|
+
* The canonical implementation now lives in `scene-sync.ts`.
|
|
5
|
+
*/
|
|
6
|
+
export {
|
|
7
|
+
CoreCommandBuilderSystem,
|
|
8
|
+
SceneSyncSystem,
|
|
9
|
+
} from './scene-sync';
|
|
@@ -2,41 +2,32 @@ import type { EngineCmd } from '../../types/cmds';
|
|
|
2
2
|
import { enqueueCommand } from '../bridge/dispatch';
|
|
3
3
|
import type { System } from '../ecs';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Handles diagnostic intents that query core-side resource lists.
|
|
7
|
+
*
|
|
8
|
+
* This system is intentionally narrow: it transforms
|
|
9
|
+
* `request-resource-list` intents into typed `cmd-*-list` commands.
|
|
10
|
+
*/
|
|
5
11
|
export const DiagnosticsSystem: System = (world, context) => {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const intent = world.pendingIntents[i];
|
|
12
|
+
const intents = world.intentStore.take('request-resource-list');
|
|
13
|
+
for (let i = 0; i < intents.length; i++) {
|
|
14
|
+
const intent = intents[i];
|
|
10
15
|
if (intent?.type === 'request-resource-list') {
|
|
16
|
+
let windowId = world.primaryWindowId;
|
|
17
|
+
if (windowId === undefined) {
|
|
18
|
+
for (const boundWindowId of world.targetWindowBindings.values()) {
|
|
19
|
+
windowId = boundWindowId;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (windowId === undefined) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
11
26
|
const type = intent.resourceType;
|
|
12
27
|
const cmdType = `cmd-${type}-list` as EngineCmd['type'];
|
|
13
28
|
enqueueCommand(context.worldId, cmdType, {
|
|
14
|
-
windowId
|
|
29
|
+
windowId,
|
|
15
30
|
});
|
|
16
|
-
intentsToRemove.push(i);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
for (let i = intentsToRemove.length - 1; i >= 0; i--) {
|
|
21
|
-
const idx = intentsToRemove[i];
|
|
22
|
-
if (idx !== undefined) world.pendingIntents.splice(idx, 1);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Simple diagnostic log every 600 frames
|
|
26
|
-
if (world.entities.size > 0 && world.nextCoreId > 2) {
|
|
27
|
-
// We only log if there is something interesting
|
|
28
|
-
const globalCounters = globalThis as unknown as Record<string, number>;
|
|
29
|
-
const frame = globalCounters.vulframFrameCount || 0;
|
|
30
|
-
globalCounters.vulframFrameCount = frame + 1;
|
|
31
|
-
|
|
32
|
-
if (frame % 600 === 0) {
|
|
33
|
-
console.debug(
|
|
34
|
-
`[Diagnostics] World ${context.worldId} | Entities: ${
|
|
35
|
-
world.entities.size
|
|
36
|
-
} | Intents: ${world.pendingIntents.length} | Core objects: ${
|
|
37
|
-
world.nextCoreId - 2
|
|
38
|
-
}`,
|
|
39
|
-
);
|
|
40
31
|
}
|
|
41
32
|
}
|
|
42
33
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { InputMirrorSystem } from './input-mirror';
|
|
2
2
|
export { CommandIntentSystem } from './command-intent';
|
|
3
|
-
export {
|
|
3
|
+
export { ConstraintSolveSystem } from './constraint-solve';
|
|
4
|
+
export { SceneSyncSystem, CoreCommandBuilderSystem } from './scene-sync';
|
|
4
5
|
export { ResourceUploadSystem } from './resource-upload';
|
|
6
|
+
export { UiBridgeSystem } from './ui-bridge';
|
|
5
7
|
export { ResponseDecodeSystem } from './response-decode';
|
|
6
8
|
export { WorldLifecycleSystem } from './world-lifecycle';
|
|
7
9
|
export { DiagnosticsSystem } from './diagnostics';
|