@vulfram/engine 0.17.1-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 +3 -3
- package/package.json +9 -4
- package/src/engine/api.ts +12 -25
- package/src/engine/bridge/dispatch.ts +3 -8
- package/src/engine/ecs/components.ts +340 -0
- package/src/engine/ecs/index.ts +3 -661
- 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 +3 -3
- package/src/engine/systems/command-intent.ts +11 -16
- package/src/engine/systems/diagnostics.ts +3 -13
- package/src/engine/systems/input-mirror.ts +156 -18
- package/src/engine/systems/resource-upload.ts +12 -14
- package/src/engine/systems/response-decode.ts +17 -0
- package/src/engine/systems/scene-sync.ts +12 -13
- package/src/engine/systems/ui-bridge.ts +31 -10
- package/src/engine/systems/utils.ts +46 -3
- package/src/engine/systems/world-lifecycle.ts +9 -15
- package/src/engine/world/entities.ts +201 -37
- package/src/engine/world/mount.ts +27 -6
- package/src/engine/world/world-ui.ts +77 -30
- package/src/engine/world/world3d.ts +282 -33
- package/src/helpers/collision.ts +487 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/raycast.ts +442 -0
- package/src/types/cmds/geometry.ts +2 -2
- package/src/types/cmds/index.ts +42 -0
- package/src/types/cmds/input.ts +39 -0
- package/src/types/cmds/material.ts +10 -10
- package/src/types/cmds/realm.ts +0 -2
- package/src/types/cmds/system.ts +10 -0
- package/src/types/cmds/target.ts +14 -0
- package/src/types/events/keyboard.ts +2 -2
- package/src/types/events/pointer.ts +43 -0
- package/src/types/events/system.ts +44 -0
package/README.md
CHANGED
|
@@ -5,10 +5,10 @@ Functional engine for the Vulfram core, focused on a simple API for creating wor
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
bun add @vulfram/engine @vulfram/transport-
|
|
8
|
+
bun add @vulfram/engine @vulfram/transport-browser
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Use the transport that fits your environment (`transport-
|
|
11
|
+
Use the transport that fits your environment (`transport-browser`, `transport-bun`, `transport-napi`).
|
|
12
12
|
|
|
13
13
|
## Simple example
|
|
14
14
|
|
|
@@ -17,7 +17,7 @@ import { initEngine, tick } from '@vulfram/engine/core';
|
|
|
17
17
|
import { createWindow } from '@vulfram/engine/window';
|
|
18
18
|
import { mountWorldToWindow } from '@vulfram/engine/mount';
|
|
19
19
|
import * as World3D from '@vulfram/engine/world3d';
|
|
20
|
-
import { transportWasm } from '@vulfram/transport-
|
|
20
|
+
import { transportWasm } from '@vulfram/transport-browser';
|
|
21
21
|
|
|
22
22
|
initEngine({ transport: transportWasm });
|
|
23
23
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vulfram/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.2-alpha",
|
|
4
4
|
"module": "src/index.ts",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
"import": "./src/types/index.ts",
|
|
48
48
|
"types": "./src/types/index.ts"
|
|
49
49
|
},
|
|
50
|
+
"./helpers": {
|
|
51
|
+
"default": "./src/helpers/index.ts",
|
|
52
|
+
"import": "./src/helpers/index.ts",
|
|
53
|
+
"types": "./src/helpers/index.ts"
|
|
54
|
+
},
|
|
50
55
|
"./package.json": "./package.json"
|
|
51
56
|
},
|
|
52
57
|
"files": [
|
|
@@ -57,12 +62,12 @@
|
|
|
57
62
|
"access": "public"
|
|
58
63
|
},
|
|
59
64
|
"dependencies": {
|
|
60
|
-
"@vulfram/transport-types": "^0.2.
|
|
65
|
+
"@vulfram/transport-types": "^0.2.6",
|
|
61
66
|
"gl-matrix": "^3.4.4",
|
|
62
|
-
"glob": "^13.0.6",
|
|
63
67
|
"msgpackr": "^1.11.8"
|
|
64
68
|
},
|
|
65
69
|
"devDependencies": {
|
|
66
|
-
"@types/bun": "^1.3.10"
|
|
70
|
+
"@types/bun": "^1.3.10",
|
|
71
|
+
"glob": "^13.0.6"
|
|
67
72
|
}
|
|
68
73
|
}
|
package/src/engine/api.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import type { ComponentSchema, System, SystemContext, SystemStep } from './ecs';
|
|
15
15
|
import { EngineError } from './errors';
|
|
16
16
|
import { engineState, REQUIRED_SYSTEMS, type WorldState } from './state';
|
|
17
|
+
import { createIntentStore } from './intents/store';
|
|
17
18
|
import * as CoreSystems from './systems';
|
|
18
19
|
import type { UploadType } from '../types/kinds';
|
|
19
20
|
import type { RealmKind } from '../types/cmds/realm';
|
|
@@ -41,25 +42,10 @@ function recalculateWorldWindowBindings(world: WorldState): void {
|
|
|
41
42
|
world.primaryWindowId = primary;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
function findLowestConfirmedWindowId(): number | undefined {
|
|
45
|
-
if (engineState.confirmedWindowIds.size === 0) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
let minWindowId = Number.POSITIVE_INFINITY;
|
|
49
|
-
for (const windowId of engineState.confirmedWindowIds) {
|
|
50
|
-
if (windowId < minWindowId) {
|
|
51
|
-
minWindowId = windowId;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return Number.isFinite(minWindowId) ? minWindowId : undefined;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
45
|
/**
|
|
58
46
|
* Shared realm creation options used by `createWorld3D` and `createWorldUI`.
|
|
59
47
|
*/
|
|
60
48
|
export type CreateWorldOptions = {
|
|
61
|
-
/** Optional output surface id when provided by host integration. */
|
|
62
|
-
outputSurfaceId?: number;
|
|
63
49
|
/** Core scheduling hint for realm priority. */
|
|
64
50
|
importance?: number;
|
|
65
51
|
/** Core-side cache policy value. */
|
|
@@ -227,14 +213,13 @@ export function uploadBuffer(
|
|
|
227
213
|
}
|
|
228
214
|
}
|
|
229
215
|
|
|
230
|
-
function createRealmWorld(
|
|
216
|
+
function createRealmWorld(
|
|
217
|
+
kind: RealmKind,
|
|
218
|
+
config: CreateWorldOptions = {},
|
|
219
|
+
): WorldId {
|
|
231
220
|
requireInitialized();
|
|
232
221
|
|
|
233
222
|
const worldId = engineState.nextWorldId++;
|
|
234
|
-
// Core compatibility: when at least one window exists, pick the lowest ID as host window
|
|
235
|
-
// if the caller did not provide an explicit surface. This keeps world API window-agnostic
|
|
236
|
-
// while satisfying render graph bootstrap paths that still rely on host-window mapping.
|
|
237
|
-
const inferredHostWindowId = findLowestConfirmedWindowId();
|
|
238
223
|
|
|
239
224
|
const world: WorldState = {
|
|
240
225
|
worldId,
|
|
@@ -246,8 +231,6 @@ function createRealmWorld(kind: RealmKind, config: CreateWorldOptions = {}): Wor
|
|
|
246
231
|
coreRealmId: undefined,
|
|
247
232
|
realmCreateArgs: {
|
|
248
233
|
kind,
|
|
249
|
-
hostWindowId: inferredHostWindowId,
|
|
250
|
-
outputSurfaceId: config.outputSurfaceId,
|
|
251
234
|
importance: config.importance,
|
|
252
235
|
cachePolicy: config.cachePolicy,
|
|
253
236
|
flags: config.flags,
|
|
@@ -266,7 +249,7 @@ function createRealmWorld(kind: RealmKind, config: CreateWorldOptions = {}): Wor
|
|
|
266
249
|
components: new Map(),
|
|
267
250
|
nextCoreId: 100,
|
|
268
251
|
systems: [...REQUIRED_SYSTEMS],
|
|
269
|
-
|
|
252
|
+
intentStore: createIntentStore(),
|
|
270
253
|
internalEvents: [],
|
|
271
254
|
pendingCommands: [],
|
|
272
255
|
pendingCommandsHead: 0,
|
|
@@ -430,7 +413,9 @@ function processGlobalResponses(): void {
|
|
|
430
413
|
const res = engineState.globalInboundResponses[i]!;
|
|
431
414
|
const content = res.content as { success?: boolean; message?: string };
|
|
432
415
|
if (res.type === 'window-create') {
|
|
433
|
-
const pendingWindowId = engineState.pendingWindowCreateByCommandId.get(
|
|
416
|
+
const pendingWindowId = engineState.pendingWindowCreateByCommandId.get(
|
|
417
|
+
res.id,
|
|
418
|
+
);
|
|
434
419
|
if (pendingWindowId !== undefined) {
|
|
435
420
|
if (content.success) {
|
|
436
421
|
engineState.confirmedWindowIds.add(pendingWindowId);
|
|
@@ -440,7 +425,9 @@ function processGlobalResponses(): void {
|
|
|
440
425
|
engineState.pendingWindowCreateByCommandId.delete(res.id);
|
|
441
426
|
}
|
|
442
427
|
} else if (res.type === 'window-close') {
|
|
443
|
-
const pendingWindowId = engineState.pendingWindowCloseByCommandId.get(
|
|
428
|
+
const pendingWindowId = engineState.pendingWindowCloseByCommandId.get(
|
|
429
|
+
res.id,
|
|
430
|
+
);
|
|
444
431
|
if (pendingWindowId !== undefined) {
|
|
445
432
|
if (content.success) {
|
|
446
433
|
engineState.usedWindowIds.delete(pendingWindowId);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { CommandResponseEnvelope, EngineCmd } from '../../types/cmds';
|
|
2
2
|
import type { EngineEvent } from '../../types/events';
|
|
3
|
-
import { EngineError } from '../errors';
|
|
4
3
|
import { engineState } from '../state';
|
|
5
4
|
import { getWorldOrThrow, requireInitialized } from './guards';
|
|
6
5
|
|
|
@@ -88,13 +87,9 @@ export function enqueueCommand<T extends EngineCmd['type']>(
|
|
|
88
87
|
typedContent.windowId = world.primaryWindowId;
|
|
89
88
|
}
|
|
90
89
|
if ('realmId' in typedContent && typedContent.realmId == null) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`World ${worldId} realm is not ready yet. Wait for at least one tick after createWorld.`,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
typedContent.realmId = world.coreRealmId;
|
|
90
|
+
// Avoid host-side readiness guards: keep logical-id flow stable.
|
|
91
|
+
// Core validates realm existence and may apply internal fallbacks.
|
|
92
|
+
typedContent.realmId = world.coreRealmId ?? world.worldId;
|
|
98
93
|
}
|
|
99
94
|
|
|
100
95
|
world.pendingCommands.push({
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import type { ViewPosition } from '../../types/cmds/camera';
|
|
2
|
+
import type { GeometryPrimitiveEntry } from '../../types/cmds/geometry';
|
|
3
|
+
import type { MaterialOptions } from '../../types/cmds/material';
|
|
4
|
+
import type { ForwardAtlasOptions } from '../../types/cmds/texture';
|
|
5
|
+
import type { GamepadEvent, SystemEvent, UiEvent } from '../../types/events';
|
|
6
|
+
import type {
|
|
7
|
+
CameraKind,
|
|
8
|
+
LightKind,
|
|
9
|
+
MaterialKind,
|
|
10
|
+
TextureCreateMode,
|
|
11
|
+
} from '../../types/kinds';
|
|
12
|
+
import type { JsonObject, JsonValue } from '../../types/json';
|
|
13
|
+
|
|
14
|
+
/** Transform component data used to position entities. */
|
|
15
|
+
export interface TransformProps {
|
|
16
|
+
position?: [number, number, number];
|
|
17
|
+
rotation?: [number, number, number, number];
|
|
18
|
+
scale?: [number, number, number];
|
|
19
|
+
layerMask?: number;
|
|
20
|
+
visible?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Fully-resolved transform component stored in the ECS. */
|
|
24
|
+
export interface TransformComponent extends Required<Omit<TransformProps, never>> {
|
|
25
|
+
type: 'Transform';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Parent link data used for hierarchical transforms. */
|
|
29
|
+
export interface ParentProps {
|
|
30
|
+
parentId: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Parent component stored in the ECS. */
|
|
34
|
+
export interface ParentComponent extends ParentProps {
|
|
35
|
+
type: 'Parent';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Camera component configuration. */
|
|
39
|
+
export interface CameraProps {
|
|
40
|
+
kind?: CameraKind;
|
|
41
|
+
near?: number;
|
|
42
|
+
far?: number;
|
|
43
|
+
order?: number;
|
|
44
|
+
viewPosition?: ViewPosition;
|
|
45
|
+
orthoScale?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Camera component stored in the ECS. */
|
|
49
|
+
export interface CameraComponent extends Required<Omit<CameraProps, 'viewPosition'>> {
|
|
50
|
+
type: 'Camera';
|
|
51
|
+
id: number;
|
|
52
|
+
viewPosition?: ViewPosition;
|
|
53
|
+
skipUpdate?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Light component configuration. */
|
|
57
|
+
export interface LightProps {
|
|
58
|
+
kind?: LightKind;
|
|
59
|
+
color?: [number, number, number];
|
|
60
|
+
intensity?: number;
|
|
61
|
+
range?: number;
|
|
62
|
+
castShadow?: boolean;
|
|
63
|
+
direction?: [number, number, number];
|
|
64
|
+
spotInnerOuter?: [number, number];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Light component stored in the ECS. */
|
|
68
|
+
export interface LightComponent extends Required<Omit<LightProps, never>> {
|
|
69
|
+
type: 'Light';
|
|
70
|
+
id: number;
|
|
71
|
+
skipUpdate?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Model component configuration. */
|
|
75
|
+
export interface ModelProps {
|
|
76
|
+
geometryId: number;
|
|
77
|
+
materialId?: number;
|
|
78
|
+
castShadow?: boolean;
|
|
79
|
+
receiveShadow?: boolean;
|
|
80
|
+
castOutline?: boolean;
|
|
81
|
+
outlineColor?: [number, number, number, number];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Model component stored in the ECS. */
|
|
85
|
+
export interface ModelComponent extends Required<Omit<ModelProps, 'materialId'>> {
|
|
86
|
+
type: 'Model';
|
|
87
|
+
id: number;
|
|
88
|
+
materialId?: number;
|
|
89
|
+
skipUpdate?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Tag component configuration. */
|
|
93
|
+
export interface TagProps {
|
|
94
|
+
name?: string;
|
|
95
|
+
labels?: string[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Tag component stored in the ECS. */
|
|
99
|
+
export interface TagComponent {
|
|
100
|
+
type: 'Tag';
|
|
101
|
+
name: string;
|
|
102
|
+
labels: Set<string>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** InputState: Stores the current input state for a world. */
|
|
106
|
+
export interface InputStateComponent {
|
|
107
|
+
type: 'InputState';
|
|
108
|
+
keysPressed: Set<number>;
|
|
109
|
+
keysJustPressed: Set<number>;
|
|
110
|
+
keysJustReleased: Set<number>;
|
|
111
|
+
pointerButtons: Set<number>;
|
|
112
|
+
pointerPosition: [number, number];
|
|
113
|
+
pointerDelta: [number, number];
|
|
114
|
+
pointerJustPressed: Set<number>;
|
|
115
|
+
pointerJustReleased: Set<number>;
|
|
116
|
+
pointerWindowId?: number;
|
|
117
|
+
pointerWindowSize?: [number, number];
|
|
118
|
+
pointerTargetId?: number;
|
|
119
|
+
pointerPositionTarget?: [number, number];
|
|
120
|
+
pointerTargetSize?: [number, number];
|
|
121
|
+
pointerTargetUv?: [number, number];
|
|
122
|
+
pointerTargetDelta?: [number, number];
|
|
123
|
+
scrollDelta: [number, number];
|
|
124
|
+
imeEnabled: boolean;
|
|
125
|
+
imePreeditText?: string;
|
|
126
|
+
imeCursorRange?: [number, number];
|
|
127
|
+
imeCommitText?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** WindowState: Stores window events and state. */
|
|
131
|
+
export interface WindowStateComponent {
|
|
132
|
+
type: 'WindowState';
|
|
133
|
+
focused: boolean;
|
|
134
|
+
size: [number, number];
|
|
135
|
+
position: [number, number];
|
|
136
|
+
scaleFactor: number;
|
|
137
|
+
closeRequested: boolean;
|
|
138
|
+
resizedThisFrame: boolean;
|
|
139
|
+
movedThisFrame: boolean;
|
|
140
|
+
focusChangedThisFrame: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Gamepad state mirrored from core gamepad events. */
|
|
144
|
+
export interface GamepadStateComponent {
|
|
145
|
+
type: 'GamepadState';
|
|
146
|
+
connected: Map<number, { name: string }>;
|
|
147
|
+
buttons: Map<number, Map<number, { pressed: boolean; value: number }>>;
|
|
148
|
+
axes: Map<number, Map<number, number>>;
|
|
149
|
+
eventsThisFrame: GamepadEvent[];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** System events mirrored for host-level integrations. */
|
|
153
|
+
export interface SystemEventStateComponent {
|
|
154
|
+
type: 'SystemEventState';
|
|
155
|
+
eventsThisFrame: SystemEvent[];
|
|
156
|
+
lastError?: {
|
|
157
|
+
scope: string;
|
|
158
|
+
message: string;
|
|
159
|
+
commandId?: number;
|
|
160
|
+
commandType?: string;
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** UI events mirrored from core UI event channel. */
|
|
165
|
+
export interface UiEventStateComponent {
|
|
166
|
+
type: 'UiEventState';
|
|
167
|
+
eventsThisFrame: UiEvent[];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Focus traversal behavior when reaching first/last input. */
|
|
171
|
+
export type UiFocusCycleMode = 'wrap' | 'clamp';
|
|
172
|
+
|
|
173
|
+
/** Form scope state used to control focus traversal. */
|
|
174
|
+
export interface UiFormScope {
|
|
175
|
+
formId: string;
|
|
176
|
+
windowId: number;
|
|
177
|
+
realmId: number;
|
|
178
|
+
documentId: number;
|
|
179
|
+
disabled: boolean;
|
|
180
|
+
cycleMode: UiFocusCycleMode;
|
|
181
|
+
activeFieldsetId?: string;
|
|
182
|
+
activeNodeId?: number;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Fieldset scope metadata. */
|
|
186
|
+
export interface UiFieldsetScope {
|
|
187
|
+
formId: string;
|
|
188
|
+
fieldsetId: string;
|
|
189
|
+
disabled: boolean;
|
|
190
|
+
legendNodeId?: number;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Focusable node metadata for tab ordering. */
|
|
194
|
+
export interface UiFocusableNode {
|
|
195
|
+
formId: string;
|
|
196
|
+
nodeId: number;
|
|
197
|
+
tabIndex: number;
|
|
198
|
+
fieldsetId?: string;
|
|
199
|
+
disabled: boolean;
|
|
200
|
+
orderHint: number;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** UI runtime state mirrored in the ECS world. */
|
|
204
|
+
export interface UiStateComponent {
|
|
205
|
+
type: 'UiState';
|
|
206
|
+
forms: Map<string, UiFormScope>;
|
|
207
|
+
fieldsets: Map<string, UiFieldsetScope>;
|
|
208
|
+
nodes: Map<number, UiFocusableNode>;
|
|
209
|
+
focusByWindow: Map<number, { formId: string; nodeId: number }>;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Base properties shared by resources. */
|
|
213
|
+
export interface BaseResourceProps {
|
|
214
|
+
label?: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Material resource configuration. */
|
|
218
|
+
export interface MaterialProps extends BaseResourceProps {
|
|
219
|
+
kind: MaterialKind;
|
|
220
|
+
options: MaterialOptions;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Options for cube primitive geometry. */
|
|
224
|
+
export interface CubeOptions {
|
|
225
|
+
size?: [number, number, number];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Options for plane primitive geometry. */
|
|
229
|
+
export interface PlaneOptions {
|
|
230
|
+
size?: [number, number, number];
|
|
231
|
+
subdivisions?: number;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Options for sphere primitive geometry. */
|
|
235
|
+
export interface SphereOptions {
|
|
236
|
+
radius?: number;
|
|
237
|
+
sectors?: number;
|
|
238
|
+
stacks?: number;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Options for cylinder primitive geometry. */
|
|
242
|
+
export interface CylinderOptions {
|
|
243
|
+
radius?: number;
|
|
244
|
+
height?: number;
|
|
245
|
+
sectors?: number;
|
|
246
|
+
segments?: number;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Options for torus primitive geometry. */
|
|
250
|
+
export interface TorusOptions {
|
|
251
|
+
majorRadius?: number;
|
|
252
|
+
minorRadius?: number;
|
|
253
|
+
majorSegments?: number;
|
|
254
|
+
minorSegments?: number;
|
|
255
|
+
radialSegments?: number;
|
|
256
|
+
tubularSegments?: number;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Options for pyramid primitive geometry. */
|
|
260
|
+
export interface PyramidOptions {
|
|
261
|
+
size?: [number, number, number];
|
|
262
|
+
subdivisions?: number;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** Geometry resource configuration (primitive or custom). */
|
|
266
|
+
export type GeometryProps = BaseResourceProps & (
|
|
267
|
+
| {
|
|
268
|
+
type: 'custom';
|
|
269
|
+
entries: GeometryPrimitiveEntry[];
|
|
270
|
+
}
|
|
271
|
+
| ({ type: 'primitive' } & (
|
|
272
|
+
| { shape: 'cube'; options?: CubeOptions }
|
|
273
|
+
| { shape: 'plane'; options?: PlaneOptions }
|
|
274
|
+
| { shape: 'sphere'; options?: SphereOptions }
|
|
275
|
+
| { shape: 'cylinder'; options?: CylinderOptions }
|
|
276
|
+
| { shape: 'torus'; options?: TorusOptions }
|
|
277
|
+
| { shape: 'pyramid'; options?: PyramidOptions }
|
|
278
|
+
))
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
/** Texture resource configuration. */
|
|
282
|
+
export interface TextureProps extends BaseResourceProps {
|
|
283
|
+
srgb?: boolean;
|
|
284
|
+
mode?: TextureCreateMode;
|
|
285
|
+
atlasOptions?: ForwardAtlasOptions;
|
|
286
|
+
source:
|
|
287
|
+
| { type: 'buffer'; bufferId: number }
|
|
288
|
+
| { type: 'color'; color: [number, number, number, number] };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Custom component payload stored in the ECS. */
|
|
292
|
+
export interface CustomComponent {
|
|
293
|
+
type: string;
|
|
294
|
+
data: JsonObject;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Union of all built-in component types. */
|
|
298
|
+
export type Component =
|
|
299
|
+
| TransformComponent
|
|
300
|
+
| ParentComponent
|
|
301
|
+
| CameraComponent
|
|
302
|
+
| LightComponent
|
|
303
|
+
| ModelComponent
|
|
304
|
+
| TagComponent
|
|
305
|
+
| InputStateComponent
|
|
306
|
+
| WindowStateComponent
|
|
307
|
+
| GamepadStateComponent
|
|
308
|
+
| SystemEventStateComponent
|
|
309
|
+
| UiEventStateComponent
|
|
310
|
+
| UiStateComponent
|
|
311
|
+
| CustomComponent;
|
|
312
|
+
|
|
313
|
+
/** String literal type for component identifiers. */
|
|
314
|
+
export type ComponentType = Component['type'];
|
|
315
|
+
|
|
316
|
+
/** Supported property types for custom schemas. */
|
|
317
|
+
export type PropertyType =
|
|
318
|
+
| 'number'
|
|
319
|
+
| 'string'
|
|
320
|
+
| 'boolean'
|
|
321
|
+
| 'vec2'
|
|
322
|
+
| 'vec3'
|
|
323
|
+
| 'vec4'
|
|
324
|
+
| 'quat'
|
|
325
|
+
| 'entity'
|
|
326
|
+
| 'resource'
|
|
327
|
+
| 'array'
|
|
328
|
+
| 'object';
|
|
329
|
+
|
|
330
|
+
/** Schema entry describing a custom component field. */
|
|
331
|
+
export interface SchemaProperty {
|
|
332
|
+
type: PropertyType;
|
|
333
|
+
default?: JsonValue;
|
|
334
|
+
optional?: boolean;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Schema definition for a custom component. */
|
|
338
|
+
export interface ComponentSchema {
|
|
339
|
+
[key: string]: SchemaProperty;
|
|
340
|
+
}
|