conveyor-engine-assets 0.0.0-stage → 0.1.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/dist/acquisition.d.ts +23 -0
- package/dist/acquisition.js +29 -0
- package/dist/admission.d.ts +38 -0
- package/dist/admission.js +216 -0
- package/dist/animation-profile.d.ts +31 -0
- package/dist/animation-profile.js +49 -0
- package/dist/collision.d.ts +108 -0
- package/dist/collision.js +436 -0
- package/dist/compose.d.ts +48 -0
- package/dist/compose.js +174 -0
- package/dist/fixtures.d.ts +39 -0
- package/dist/fixtures.js +95 -0
- package/dist/glb.d.ts +60 -0
- package/dist/glb.js +223 -0
- package/dist/hash.d.ts +10 -0
- package/dist/hash.js +141 -0
- package/dist/http-fetch.d.ts +30 -0
- package/dist/http-fetch.js +88 -0
- package/dist/http.d.ts +1 -0
- package/dist/http.js +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +18 -0
- package/dist/loader.d.ts +75 -0
- package/dist/loader.js +434 -0
- package/dist/manifest.d.ts +13 -0
- package/dist/manifest.js +57 -0
- package/dist/node-fs.d.ts +15 -0
- package/dist/node-fs.js +73 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.js +1 -0
- package/dist/render-descriptor.d.ts +56 -0
- package/dist/render-descriptor.js +103 -0
- package/dist/render-profile.d.ts +56 -0
- package/dist/render-profile.js +147 -0
- package/dist/schema.d.ts +21 -0
- package/dist/schema.js +417 -0
- package/dist/skinned-fixture.d.ts +6 -0
- package/dist/skinned-fixture.js +63 -0
- package/dist/static-world.d.ts +103 -0
- package/dist/static-world.js +271 -0
- package/dist/types.d.ts +242 -0
- package/dist/types.js +1 -0
- package/package.json +35 -4
- package/src/acquisition.ts +48 -0
- package/src/admission.ts +237 -0
- package/src/animation-profile.ts +81 -0
- package/src/collision.ts +515 -0
- package/src/compose.ts +233 -0
- package/src/fixtures.ts +121 -0
- package/src/glb.ts +288 -0
- package/src/hash.ts +138 -0
- package/src/http-fetch.ts +108 -0
- package/src/http.ts +1 -0
- package/src/index.ts +18 -0
- package/src/loader.ts +535 -0
- package/src/manifest.ts +63 -0
- package/src/node-fs.ts +78 -0
- package/src/node.ts +1 -0
- package/src/render-descriptor.ts +150 -0
- package/src/render-profile.ts +205 -0
- package/src/schema.ts +459 -0
- package/src/skinned-fixture.ts +66 -0
- package/src/static-world.ts +364 -0
- package/src/types.ts +325 -0
- package/README.md +0 -3
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type ByteSource = {
|
|
2
|
+
bytes: Uint8Array;
|
|
3
|
+
contentType?: string;
|
|
4
|
+
uri: string;
|
|
5
|
+
};
|
|
6
|
+
export type AcquisitionContext = {
|
|
7
|
+
uri: string;
|
|
8
|
+
/** Temporary locator supplied by the app layer. Never treated as asset identity. */
|
|
9
|
+
runtimeUrl?: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
};
|
|
14
|
+
export interface AcquisitionProvider {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
accepts?(uri: string): boolean;
|
|
17
|
+
acquire(ctx: AcquisitionContext): Promise<ByteSource>;
|
|
18
|
+
}
|
|
19
|
+
export declare class AcquisitionError extends Error {
|
|
20
|
+
readonly code: string;
|
|
21
|
+
constructor(code: string, message: string);
|
|
22
|
+
}
|
|
23
|
+
export declare function composeProviders(providers: AcquisitionProvider[]): AcquisitionProvider;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class AcquisitionError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(code, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "AcquisitionError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function composeProviders(providers) {
|
|
10
|
+
return {
|
|
11
|
+
id: providers.map((p) => p.id).join("+") || "empty",
|
|
12
|
+
async acquire(ctx) {
|
|
13
|
+
let last;
|
|
14
|
+
for (const p of providers) {
|
|
15
|
+
if (p.accepts && !p.accepts(ctx.runtimeUrl ?? ctx.uri))
|
|
16
|
+
continue;
|
|
17
|
+
try {
|
|
18
|
+
return await p.acquire(ctx);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
last = err;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (last instanceof Error)
|
|
25
|
+
throw last;
|
|
26
|
+
throw new AcquisitionError("provider", `no provider could acquire ${ctx.uri}`);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AssetLoadError, type AssetHandle, type AssetRuntime, type RequestOptions } from "./loader.js";
|
|
2
|
+
import type { AdmissionRecord, AssetId, AuthoritativeCompatibility, EngineManifest } from "./types.js";
|
|
3
|
+
export declare class StaticAdmissionError extends Error {
|
|
4
|
+
readonly missing: AssetId[];
|
|
5
|
+
constructor(missing: AssetId[], message?: string);
|
|
6
|
+
}
|
|
7
|
+
export type StagedAdmission = {
|
|
8
|
+
assetId: AssetId;
|
|
9
|
+
handle?: AssetHandle;
|
|
10
|
+
requestId: string;
|
|
11
|
+
error?: AssetLoadError;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Off-tick staging + tick-boundary take. Does not mutate AuthoritativeWorld.
|
|
15
|
+
*/
|
|
16
|
+
export declare class AssetAdmissionQueue {
|
|
17
|
+
private readonly staged;
|
|
18
|
+
private readonly applied;
|
|
19
|
+
private readonly runtime;
|
|
20
|
+
constructor(runtime: AssetRuntime);
|
|
21
|
+
get manifest(): EngineManifest;
|
|
22
|
+
/**
|
|
23
|
+
* Load and pin every authoritative / authoritative-static asset. Fail closed.
|
|
24
|
+
* Pins last for the session on purpose — call `runtime.unpin(id)` to evict.
|
|
25
|
+
*/
|
|
26
|
+
requireStaticReady(opts?: RequestOptions): Promise<AuthoritativeCompatibility>;
|
|
27
|
+
/** Resolve off the simulation tick. Presentation assets are rejected. */
|
|
28
|
+
stage(id: AssetId, opts?: RequestOptions): Promise<StagedAdmission>;
|
|
29
|
+
peek(): StagedAdmission[];
|
|
30
|
+
history(): AdmissionRecord[];
|
|
31
|
+
/**
|
|
32
|
+
* Admit or reject everything currently staged. Call only on a simulation tick boundary.
|
|
33
|
+
* Records are the replay surface; the caller applies world effects separately.
|
|
34
|
+
*/
|
|
35
|
+
takeAdmitted(tick: bigint, decide?: (staged: StagedAdmission) => AdmissionRecord["outcome"] | void): AdmissionRecord[];
|
|
36
|
+
reject(id: AssetId, tick: bigint, reason?: string): AdmissionRecord | undefined;
|
|
37
|
+
private emit;
|
|
38
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { collectAuthoritativeCompatibility } from "./schema.js";
|
|
2
|
+
import { AssetLoadError } from "./loader.js";
|
|
3
|
+
export class StaticAdmissionError extends Error {
|
|
4
|
+
missing;
|
|
5
|
+
constructor(missing, message) {
|
|
6
|
+
super(message ?? `authoritative static assets not ready: ${missing.join(", ")}`);
|
|
7
|
+
this.name = "StaticAdmissionError";
|
|
8
|
+
this.missing = missing;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function isAdmittedClass(authority) {
|
|
12
|
+
return authority === "authoritative-admitted";
|
|
13
|
+
}
|
|
14
|
+
function isStaticAuth(authority) {
|
|
15
|
+
return authority === "authoritative" || authority === "authoritative-static";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Off-tick staging + tick-boundary take. Does not mutate AuthoritativeWorld.
|
|
19
|
+
*/
|
|
20
|
+
export class AssetAdmissionQueue {
|
|
21
|
+
staged = new Map();
|
|
22
|
+
applied = [];
|
|
23
|
+
runtime;
|
|
24
|
+
constructor(runtime) {
|
|
25
|
+
this.runtime = runtime;
|
|
26
|
+
}
|
|
27
|
+
get manifest() {
|
|
28
|
+
return this.runtime.manifest;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Load and pin every authoritative / authoritative-static asset. Fail closed.
|
|
32
|
+
* Pins last for the session on purpose — call `runtime.unpin(id)` to evict.
|
|
33
|
+
*/
|
|
34
|
+
async requireStaticReady(opts = {}) {
|
|
35
|
+
const staticEntries = this.runtime.manifest.assets.filter((a) => isStaticAuth(a.authority));
|
|
36
|
+
const missing = [];
|
|
37
|
+
for (const entry of staticEntries) {
|
|
38
|
+
try {
|
|
39
|
+
const handle = await this.runtime.request(entry.id, opts);
|
|
40
|
+
this.runtime.pin(entry.id);
|
|
41
|
+
handle.release();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
missing.push(entry.id);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (missing.length)
|
|
48
|
+
throw new StaticAdmissionError(missing);
|
|
49
|
+
return collectAuthoritativeCompatibility(this.runtime.manifest);
|
|
50
|
+
}
|
|
51
|
+
/** Resolve off the simulation tick. Presentation assets are rejected. */
|
|
52
|
+
async stage(id, opts = {}) {
|
|
53
|
+
const entry = this.runtime.getEntry(id);
|
|
54
|
+
if (!entry) {
|
|
55
|
+
const staged = {
|
|
56
|
+
assetId: id,
|
|
57
|
+
requestId: `missing-${id}`,
|
|
58
|
+
error: new AssetLoadError("not-found", `unknown asset ${id}`, {
|
|
59
|
+
requestId: `missing-${id}`,
|
|
60
|
+
assetId: id,
|
|
61
|
+
state: "failed",
|
|
62
|
+
retryCount: 0,
|
|
63
|
+
degraded: false,
|
|
64
|
+
failureCode: "not-found",
|
|
65
|
+
}),
|
|
66
|
+
};
|
|
67
|
+
this.staged.set(id, staged);
|
|
68
|
+
this.runtime.metrics.admissionStagedCount += 1;
|
|
69
|
+
this.emit({
|
|
70
|
+
type: "admission-staged",
|
|
71
|
+
record: {
|
|
72
|
+
assetId: id,
|
|
73
|
+
version: "0",
|
|
74
|
+
contentHash: "sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
|
75
|
+
authority: "authoritative-admitted",
|
|
76
|
+
requestId: staged.requestId,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
return staged;
|
|
80
|
+
}
|
|
81
|
+
if (!isAdmittedClass(entry.authority)) {
|
|
82
|
+
const staged = {
|
|
83
|
+
assetId: id,
|
|
84
|
+
requestId: `policy-${id}`,
|
|
85
|
+
error: new AssetLoadError("policy", `${id} is ${entry.authority}, not authoritative-admitted`, {
|
|
86
|
+
requestId: `policy-${id}`,
|
|
87
|
+
assetId: id,
|
|
88
|
+
version: entry.version,
|
|
89
|
+
contentHash: entry.contentHash,
|
|
90
|
+
state: "failed",
|
|
91
|
+
retryCount: 0,
|
|
92
|
+
degraded: false,
|
|
93
|
+
failureCode: "policy",
|
|
94
|
+
}),
|
|
95
|
+
};
|
|
96
|
+
this.staged.set(id, staged);
|
|
97
|
+
this.runtime.metrics.admissionStagedCount += 1;
|
|
98
|
+
this.emit({
|
|
99
|
+
type: "admission-staged",
|
|
100
|
+
record: {
|
|
101
|
+
assetId: entry.id,
|
|
102
|
+
version: entry.version,
|
|
103
|
+
contentHash: entry.contentHash,
|
|
104
|
+
authority: entry.authority,
|
|
105
|
+
requestId: staged.requestId,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
return staged;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const handle = await this.runtime.request(id, opts);
|
|
112
|
+
const staged = { assetId: id, handle, requestId: handle.requestId };
|
|
113
|
+
this.staged.set(id, staged);
|
|
114
|
+
this.runtime.metrics.admissionStagedCount += 1;
|
|
115
|
+
this.emit({
|
|
116
|
+
type: "admission-staged",
|
|
117
|
+
record: {
|
|
118
|
+
assetId: handle.assetId,
|
|
119
|
+
version: handle.version,
|
|
120
|
+
contentHash: handle.contentHash,
|
|
121
|
+
authority: handle.entry.authority,
|
|
122
|
+
requestId: handle.requestId,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return staged;
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
const error = err instanceof AssetLoadError ? err : undefined;
|
|
129
|
+
const staged = {
|
|
130
|
+
assetId: id,
|
|
131
|
+
requestId: error?.snapshot.requestId ?? `fail-${id}`,
|
|
132
|
+
error: error ?? new AssetLoadError("provider", messageOf(err), {
|
|
133
|
+
requestId: `fail-${id}`,
|
|
134
|
+
assetId: id,
|
|
135
|
+
state: "failed",
|
|
136
|
+
retryCount: 0,
|
|
137
|
+
degraded: false,
|
|
138
|
+
}),
|
|
139
|
+
};
|
|
140
|
+
this.staged.set(id, staged);
|
|
141
|
+
this.runtime.metrics.admissionStagedCount += 1;
|
|
142
|
+
return staged;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
peek() {
|
|
146
|
+
return [...this.staged.values()];
|
|
147
|
+
}
|
|
148
|
+
history() {
|
|
149
|
+
return [...this.applied];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Admit or reject everything currently staged. Call only on a simulation tick boundary.
|
|
153
|
+
* Records are the replay surface; the caller applies world effects separately.
|
|
154
|
+
*/
|
|
155
|
+
takeAdmitted(tick, decide) {
|
|
156
|
+
const records = [];
|
|
157
|
+
const pending = [...this.staged.entries()];
|
|
158
|
+
for (const [id, staged] of pending) {
|
|
159
|
+
const entry = staged.handle?.entry ?? this.runtime.getEntry(id);
|
|
160
|
+
const outcome = staged.error
|
|
161
|
+
? "rejected"
|
|
162
|
+
: (decide?.(staged) ?? "accepted");
|
|
163
|
+
const record = {
|
|
164
|
+
assetId: id,
|
|
165
|
+
version: entry?.version ?? "0",
|
|
166
|
+
contentHash: entry?.contentHash ?? "sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
|
167
|
+
authority: entry?.authority ?? "authoritative-admitted",
|
|
168
|
+
tick,
|
|
169
|
+
outcome,
|
|
170
|
+
requestId: staged.requestId,
|
|
171
|
+
reason: staged.error?.message,
|
|
172
|
+
};
|
|
173
|
+
if (outcome === "accepted")
|
|
174
|
+
this.runtime.metrics.admissionAcceptedCount += 1;
|
|
175
|
+
else
|
|
176
|
+
this.runtime.metrics.admissionRejectedCount += 1;
|
|
177
|
+
if (outcome === "rejected")
|
|
178
|
+
staged.handle?.release();
|
|
179
|
+
else if (staged.handle)
|
|
180
|
+
this.runtime.pin(id);
|
|
181
|
+
records.push(record);
|
|
182
|
+
this.applied.push(record);
|
|
183
|
+
this.emit({ type: "admission-applied", record });
|
|
184
|
+
this.staged.delete(id);
|
|
185
|
+
}
|
|
186
|
+
return records;
|
|
187
|
+
}
|
|
188
|
+
reject(id, tick, reason) {
|
|
189
|
+
const staged = this.staged.get(id);
|
|
190
|
+
if (!staged)
|
|
191
|
+
return undefined;
|
|
192
|
+
staged.handle?.release();
|
|
193
|
+
const entry = staged.handle?.entry ?? this.runtime.getEntry(id);
|
|
194
|
+
const record = {
|
|
195
|
+
assetId: id,
|
|
196
|
+
version: entry?.version ?? "0",
|
|
197
|
+
contentHash: entry?.contentHash ?? "sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
|
198
|
+
authority: entry?.authority ?? "authoritative-admitted",
|
|
199
|
+
tick,
|
|
200
|
+
outcome: "rejected",
|
|
201
|
+
requestId: staged.requestId,
|
|
202
|
+
reason,
|
|
203
|
+
};
|
|
204
|
+
this.runtime.metrics.admissionRejectedCount += 1;
|
|
205
|
+
this.applied.push(record);
|
|
206
|
+
this.staged.delete(id);
|
|
207
|
+
this.emit({ type: "admission-applied", record });
|
|
208
|
+
return record;
|
|
209
|
+
}
|
|
210
|
+
emit(event) {
|
|
211
|
+
this.runtime.emit(event);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function messageOf(err) {
|
|
215
|
+
return err instanceof Error ? err.message : String(err);
|
|
216
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AssetId } from "./types.js";
|
|
2
|
+
import type { GlbValidationReport } from "./glb.js";
|
|
3
|
+
export type AnimationSemantic = "idle" | "walk" | "run" | "action-primary";
|
|
4
|
+
export type AnimationProfile = {
|
|
5
|
+
id: string;
|
|
6
|
+
clips: Partial<Record<AnimationSemantic, string>>;
|
|
7
|
+
rootMotion: "disabled";
|
|
8
|
+
idleWalkFadeMs?: number;
|
|
9
|
+
walkRunFadeMs?: number;
|
|
10
|
+
actionFadeInMs?: number;
|
|
11
|
+
actionFadeOutMs?: number;
|
|
12
|
+
walkSpeed?: number;
|
|
13
|
+
runSpeed?: number;
|
|
14
|
+
minPlayback?: number;
|
|
15
|
+
maxPlayback?: number;
|
|
16
|
+
idleThreshold?: number;
|
|
17
|
+
runThreshold?: number;
|
|
18
|
+
};
|
|
19
|
+
export type AnimationProfileValidation = {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
errors: string[];
|
|
22
|
+
warnings: string[];
|
|
23
|
+
};
|
|
24
|
+
export declare function validateAnimationProfile(profile: AnimationProfile, sniffed?: Pick<GlbValidationReport, "animationNames" | "skinCount" | "animationCount" | "ok">): AnimationProfileValidation;
|
|
25
|
+
export declare const HUMANOID_BASIC_V1: AnimationProfile;
|
|
26
|
+
export type SkinnedModelMeta = {
|
|
27
|
+
assetId: AssetId;
|
|
28
|
+
profileId: string;
|
|
29
|
+
rootMotion: "disabled";
|
|
30
|
+
fallbackAssetId?: AssetId;
|
|
31
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const REQUIRED = ["idle", "walk", "run", "action-primary"];
|
|
2
|
+
export function validateAnimationProfile(profile, sniffed) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
const warnings = [];
|
|
5
|
+
if (!profile.id)
|
|
6
|
+
errors.push("profile.id required");
|
|
7
|
+
if (profile.rootMotion !== "disabled")
|
|
8
|
+
errors.push("A4 requires rootMotion: disabled");
|
|
9
|
+
for (const sem of REQUIRED) {
|
|
10
|
+
if (!profile.clips[sem])
|
|
11
|
+
errors.push(`missing clip alias for ${sem}`);
|
|
12
|
+
}
|
|
13
|
+
if (sniffed) {
|
|
14
|
+
if (!sniffed.ok)
|
|
15
|
+
errors.push("skinned GLB failed structural validation");
|
|
16
|
+
if (sniffed.skinCount < 1)
|
|
17
|
+
errors.push("skinned-model requires at least one skin");
|
|
18
|
+
if (sniffed.animationCount < 1)
|
|
19
|
+
errors.push("animated-model requires at least one animation");
|
|
20
|
+
const names = new Set(sniffed.animationNames ?? []);
|
|
21
|
+
if (names.size) {
|
|
22
|
+
for (const [sem, clip] of Object.entries(profile.clips)) {
|
|
23
|
+
if (clip && !names.has(clip))
|
|
24
|
+
errors.push(`clip alias ${sem} → ${clip} not in GLB`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return { ok: errors.length === 0, errors, warnings };
|
|
29
|
+
}
|
|
30
|
+
export const HUMANOID_BASIC_V1 = {
|
|
31
|
+
id: "humanoid-basic-v1",
|
|
32
|
+
clips: {
|
|
33
|
+
idle: "Idle",
|
|
34
|
+
walk: "Walk",
|
|
35
|
+
run: "Run",
|
|
36
|
+
"action-primary": "Attack",
|
|
37
|
+
},
|
|
38
|
+
rootMotion: "disabled",
|
|
39
|
+
idleWalkFadeMs: 120,
|
|
40
|
+
walkRunFadeMs: 100,
|
|
41
|
+
actionFadeInMs: 80,
|
|
42
|
+
actionFadeOutMs: 120,
|
|
43
|
+
walkSpeed: 2,
|
|
44
|
+
runSpeed: 5,
|
|
45
|
+
minPlayback: 0.5,
|
|
46
|
+
maxPlayback: 1.5,
|
|
47
|
+
idleThreshold: 0.2,
|
|
48
|
+
runThreshold: 4,
|
|
49
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { AssetId, ContentHash, LoadedPayload } from "./types.js";
|
|
2
|
+
export type CollisionRepresentation = "aabb" | "triangle-mesh" | "occupancy-grid" | "primitives";
|
|
3
|
+
export type CollisionCoordinateSystem = "y-up" | "z-up";
|
|
4
|
+
export type CollisionBounds = {
|
|
5
|
+
minX: number;
|
|
6
|
+
maxX: number;
|
|
7
|
+
minY: number;
|
|
8
|
+
maxY: number;
|
|
9
|
+
minZ: number;
|
|
10
|
+
maxZ: number;
|
|
11
|
+
};
|
|
12
|
+
/** Matches conveyor-engine-world StaticObstacle. No render fields. */
|
|
13
|
+
export type CollisionObstacle = {
|
|
14
|
+
id: number;
|
|
15
|
+
minX: number;
|
|
16
|
+
maxX: number;
|
|
17
|
+
minZ: number;
|
|
18
|
+
maxZ: number;
|
|
19
|
+
};
|
|
20
|
+
export type CollisionAabb = CollisionObstacle & {
|
|
21
|
+
minY?: number;
|
|
22
|
+
maxY?: number;
|
|
23
|
+
};
|
|
24
|
+
export type CollisionTriangleMesh = {
|
|
25
|
+
positions: number[];
|
|
26
|
+
indices: number[];
|
|
27
|
+
};
|
|
28
|
+
export type CollisionOccupancyGrid = {
|
|
29
|
+
originX: number;
|
|
30
|
+
originZ: number;
|
|
31
|
+
cellSize: number;
|
|
32
|
+
width: number;
|
|
33
|
+
depth: number;
|
|
34
|
+
/** Row-major occupancy, width * depth. 0 = free, nonzero = blocked. */
|
|
35
|
+
cells: number[];
|
|
36
|
+
};
|
|
37
|
+
export type CollisionPrimitive = {
|
|
38
|
+
type: "box";
|
|
39
|
+
id: number;
|
|
40
|
+
minX: number;
|
|
41
|
+
maxX: number;
|
|
42
|
+
minY?: number;
|
|
43
|
+
maxY?: number;
|
|
44
|
+
minZ: number;
|
|
45
|
+
maxZ: number;
|
|
46
|
+
} | {
|
|
47
|
+
type: "sphere";
|
|
48
|
+
id: number;
|
|
49
|
+
x: number;
|
|
50
|
+
y?: number;
|
|
51
|
+
z: number;
|
|
52
|
+
radius: number;
|
|
53
|
+
} | {
|
|
54
|
+
type: "capsule";
|
|
55
|
+
id: number;
|
|
56
|
+
x: number;
|
|
57
|
+
y?: number;
|
|
58
|
+
z: number;
|
|
59
|
+
radius: number;
|
|
60
|
+
height: number;
|
|
61
|
+
};
|
|
62
|
+
export type CollisionSpawnPoint = {
|
|
63
|
+
id: string;
|
|
64
|
+
x: number;
|
|
65
|
+
y?: number;
|
|
66
|
+
z: number;
|
|
67
|
+
yaw?: number;
|
|
68
|
+
team?: string;
|
|
69
|
+
priority?: number;
|
|
70
|
+
clearance?: number;
|
|
71
|
+
};
|
|
72
|
+
export type CollisionArtifact = {
|
|
73
|
+
formatVersion: 1;
|
|
74
|
+
representation: CollisionRepresentation;
|
|
75
|
+
coordinateSystem: CollisionCoordinateSystem;
|
|
76
|
+
unitScale: number;
|
|
77
|
+
origin: {
|
|
78
|
+
x: number;
|
|
79
|
+
y: number;
|
|
80
|
+
z: number;
|
|
81
|
+
};
|
|
82
|
+
bounds: CollisionBounds;
|
|
83
|
+
visualAssetId?: AssetId;
|
|
84
|
+
contentHash?: ContentHash;
|
|
85
|
+
aabbs?: CollisionAabb[];
|
|
86
|
+
mesh?: CollisionTriangleMesh;
|
|
87
|
+
grid?: CollisionOccupancyGrid;
|
|
88
|
+
primitives?: CollisionPrimitive[];
|
|
89
|
+
spawnPoints?: CollisionSpawnPoint[];
|
|
90
|
+
};
|
|
91
|
+
export type CollisionValidation = {
|
|
92
|
+
ok: boolean;
|
|
93
|
+
errors: string[];
|
|
94
|
+
warnings: string[];
|
|
95
|
+
triangleCount?: number;
|
|
96
|
+
aabbCount?: number;
|
|
97
|
+
primitiveCount?: number;
|
|
98
|
+
occupiedCells?: number;
|
|
99
|
+
};
|
|
100
|
+
export declare class CollisionParseError extends Error {
|
|
101
|
+
readonly issues: string[];
|
|
102
|
+
constructor(issues: string[]);
|
|
103
|
+
}
|
|
104
|
+
export declare function parseCollisionArtifact(input: unknown): CollisionArtifact;
|
|
105
|
+
export declare function collisionFromPayload(payload: LoadedPayload): CollisionArtifact;
|
|
106
|
+
export declare function validateCollisionArtifact(artifact: CollisionArtifact): CollisionValidation;
|
|
107
|
+
/** Flatten any representation into world StaticObstacle-shaped AABBs on XZ. */
|
|
108
|
+
export declare function collisionToObstacles(artifact: CollisionArtifact): CollisionObstacle[];
|