@series-inc/stowkit-cli 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/app/blob-store.d.ts +9 -0
- package/dist/app/blob-store.js +42 -0
- package/dist/app/disk-project.d.ts +84 -0
- package/dist/app/disk-project.js +70 -0
- package/dist/app/process-cache.d.ts +10 -0
- package/dist/app/process-cache.js +126 -0
- package/dist/app/state.d.ts +38 -0
- package/dist/app/state.js +16 -0
- package/dist/app/stowmat-io.d.ts +6 -0
- package/dist/app/stowmat-io.js +48 -0
- package/dist/app/stowmeta-io.d.ts +14 -0
- package/dist/app/stowmeta-io.js +207 -0
- package/dist/cleanup.d.ts +3 -0
- package/dist/cleanup.js +72 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +148 -0
- package/dist/core/binary.d.ts +41 -0
- package/dist/core/binary.js +118 -0
- package/dist/core/constants.d.ts +64 -0
- package/dist/core/constants.js +65 -0
- package/dist/core/path.d.ts +3 -0
- package/dist/core/path.js +27 -0
- package/dist/core/types.d.ts +204 -0
- package/dist/core/types.js +76 -0
- package/dist/encoders/aac-encoder.d.ts +12 -0
- package/dist/encoders/aac-encoder.js +179 -0
- package/dist/encoders/basis-encoder.d.ts +15 -0
- package/dist/encoders/basis-encoder.js +116 -0
- package/dist/encoders/draco-encoder.d.ts +11 -0
- package/dist/encoders/draco-encoder.js +155 -0
- package/dist/encoders/fbx-loader.d.ts +4 -0
- package/dist/encoders/fbx-loader.js +540 -0
- package/dist/encoders/image-decoder.d.ts +13 -0
- package/dist/encoders/image-decoder.js +33 -0
- package/dist/encoders/interfaces.d.ts +105 -0
- package/dist/encoders/interfaces.js +1 -0
- package/dist/encoders/skinned-mesh-builder.d.ts +7 -0
- package/dist/encoders/skinned-mesh-builder.js +135 -0
- package/dist/format/metadata.d.ts +18 -0
- package/dist/format/metadata.js +381 -0
- package/dist/format/packer.d.ts +8 -0
- package/dist/format/packer.js +87 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +35 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +73 -0
- package/dist/node-fs.d.ts +22 -0
- package/dist/node-fs.js +148 -0
- package/dist/orchestrator.d.ts +20 -0
- package/dist/orchestrator.js +301 -0
- package/dist/pipeline.d.ts +23 -0
- package/dist/pipeline.js +354 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +859 -0
- package/package.json +35 -0
- package/skill.md +211 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ImportedMesh } from './interfaces.js';
|
|
2
|
+
import type { SkinnedMeshMetadata } from '../core/types.js';
|
|
3
|
+
export interface SkinnedMeshBuildResult {
|
|
4
|
+
data: Uint8Array;
|
|
5
|
+
metadata: SkinnedMeshMetadata;
|
|
6
|
+
}
|
|
7
|
+
export declare function buildSkinnedMeshData(imported: ImportedMesh, scaleFactor?: number): SkinnedMeshBuildResult;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { SKINNED_VERTEX_STRIDE, VERTEX_WEIGHTS_SIZE } from '../core/constants.js';
|
|
2
|
+
export function buildSkinnedMeshData(imported, scaleFactor = 1.0) {
|
|
3
|
+
const geometries = [];
|
|
4
|
+
let totalDataSize = 0;
|
|
5
|
+
for (const sub of imported.subMeshes) {
|
|
6
|
+
const vertCount = sub.positions.length / 3;
|
|
7
|
+
const vertexBufSize = vertCount * SKINNED_VERTEX_STRIDE;
|
|
8
|
+
const indexBufSize = sub.indices.length * 4;
|
|
9
|
+
const weightsBufSize = vertCount * VERTEX_WEIGHTS_SIZE;
|
|
10
|
+
geometries.push({
|
|
11
|
+
vertexCount: vertCount,
|
|
12
|
+
indexCount: sub.indices.length,
|
|
13
|
+
hasNormals: sub.normals ? 1 : 0,
|
|
14
|
+
hasUvs: sub.uvs ? 1 : 0,
|
|
15
|
+
vertexBufferOffset: totalDataSize,
|
|
16
|
+
vertexBufferSize: vertexBufSize,
|
|
17
|
+
indexBufferOffset: totalDataSize + vertexBufSize,
|
|
18
|
+
indexBufferSize: indexBufSize,
|
|
19
|
+
weightsOffset: totalDataSize + vertexBufSize + indexBufSize,
|
|
20
|
+
weightsSize: weightsBufSize,
|
|
21
|
+
materialIndex: sub.materialIndex,
|
|
22
|
+
_padding: 0,
|
|
23
|
+
});
|
|
24
|
+
totalDataSize += vertexBufSize + indexBufSize + weightsBufSize;
|
|
25
|
+
}
|
|
26
|
+
const data = new Uint8Array(totalDataSize);
|
|
27
|
+
const view = new DataView(data.buffer);
|
|
28
|
+
let offset = 0;
|
|
29
|
+
for (const sub of imported.subMeshes) {
|
|
30
|
+
const vertCount = sub.positions.length / 3;
|
|
31
|
+
for (let v = 0; v < vertCount; v++) {
|
|
32
|
+
view.setFloat32(offset, sub.positions[v * 3] * scaleFactor, true);
|
|
33
|
+
offset += 4;
|
|
34
|
+
view.setFloat32(offset, sub.positions[v * 3 + 1] * scaleFactor, true);
|
|
35
|
+
offset += 4;
|
|
36
|
+
view.setFloat32(offset, sub.positions[v * 3 + 2] * scaleFactor, true);
|
|
37
|
+
offset += 4;
|
|
38
|
+
if (sub.normals) {
|
|
39
|
+
view.setFloat32(offset, sub.normals[v * 3], true);
|
|
40
|
+
offset += 4;
|
|
41
|
+
view.setFloat32(offset, sub.normals[v * 3 + 1], true);
|
|
42
|
+
offset += 4;
|
|
43
|
+
view.setFloat32(offset, sub.normals[v * 3 + 2], true);
|
|
44
|
+
offset += 4;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
view.setFloat32(offset, 0, true);
|
|
48
|
+
offset += 4;
|
|
49
|
+
view.setFloat32(offset, 1, true);
|
|
50
|
+
offset += 4;
|
|
51
|
+
view.setFloat32(offset, 0, true);
|
|
52
|
+
offset += 4;
|
|
53
|
+
}
|
|
54
|
+
if (sub.uvs) {
|
|
55
|
+
view.setFloat32(offset, sub.uvs[v * 2], true);
|
|
56
|
+
offset += 4;
|
|
57
|
+
view.setFloat32(offset, sub.uvs[v * 2 + 1], true);
|
|
58
|
+
offset += 4;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
view.setFloat32(offset, 0, true);
|
|
62
|
+
offset += 4;
|
|
63
|
+
view.setFloat32(offset, 0, true);
|
|
64
|
+
offset += 4;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (let i = 0; i < sub.indices.length; i++) {
|
|
68
|
+
view.setUint32(offset, sub.indices[i], true);
|
|
69
|
+
offset += 4;
|
|
70
|
+
}
|
|
71
|
+
for (let v = 0; v < vertCount; v++) {
|
|
72
|
+
if (sub.skinData) {
|
|
73
|
+
for (let w = 0; w < 4; w++) {
|
|
74
|
+
view.setUint32(offset, sub.skinData.boneIndices[v * 4 + w], true);
|
|
75
|
+
offset += 4;
|
|
76
|
+
}
|
|
77
|
+
for (let w = 0; w < 4; w++) {
|
|
78
|
+
view.setFloat32(offset, sub.skinData.boneWeights[v * 4 + w], true);
|
|
79
|
+
offset += 4;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
for (let w = 0; w < 8; w++) {
|
|
84
|
+
view.setUint32(offset, 0, true);
|
|
85
|
+
offset += 4;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const materials = imported.materials.map((m) => ({
|
|
91
|
+
name: m.name,
|
|
92
|
+
schemaId: '',
|
|
93
|
+
propertyCount: 0,
|
|
94
|
+
properties: [],
|
|
95
|
+
}));
|
|
96
|
+
const meshIndicesFlat = [];
|
|
97
|
+
const sceneNodes = imported.nodes.map((n) => {
|
|
98
|
+
const firstMeshIndex = meshIndicesFlat.length;
|
|
99
|
+
meshIndicesFlat.push(...n.meshIndices);
|
|
100
|
+
return {
|
|
101
|
+
name: n.name,
|
|
102
|
+
parentIndex: n.parentIndex,
|
|
103
|
+
position: [
|
|
104
|
+
n.position[0] * scaleFactor,
|
|
105
|
+
n.position[1] * scaleFactor,
|
|
106
|
+
n.position[2] * scaleFactor,
|
|
107
|
+
],
|
|
108
|
+
rotation: n.rotation,
|
|
109
|
+
scale: n.scale,
|
|
110
|
+
meshCount: n.meshIndices.length,
|
|
111
|
+
firstMeshIndex,
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
const bones = (imported.bones ?? []).map((b) => {
|
|
115
|
+
const src = b.offsetMatrix ?? [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
116
|
+
const mat = [...src];
|
|
117
|
+
mat[12] *= scaleFactor;
|
|
118
|
+
mat[13] *= scaleFactor;
|
|
119
|
+
mat[14] *= scaleFactor;
|
|
120
|
+
return { name: b.name, parentIndex: b.parentIndex, offsetMatrix: mat };
|
|
121
|
+
});
|
|
122
|
+
const metadata = {
|
|
123
|
+
meshGeometryCount: geometries.length,
|
|
124
|
+
materialCount: materials.length,
|
|
125
|
+
nodeCount: sceneNodes.length,
|
|
126
|
+
boneCount: bones.length,
|
|
127
|
+
stringId: '',
|
|
128
|
+
geometries,
|
|
129
|
+
materials,
|
|
130
|
+
nodes: sceneNodes,
|
|
131
|
+
meshIndices: meshIndicesFlat,
|
|
132
|
+
bones,
|
|
133
|
+
};
|
|
134
|
+
return { data, metadata };
|
|
135
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TextureMetadata, AudioMetadata, MeshMetadata, MaterialSchemaMetadata, AnimationClipMetadata, SkinnedMeshMetadata } from '../core/types.js';
|
|
2
|
+
export declare function serializeTextureMetadata(meta: TextureMetadata): Uint8Array;
|
|
3
|
+
export declare function deserializeTextureMetadata(data: Uint8Array): TextureMetadata;
|
|
4
|
+
export declare function serializeAudioMetadata(meta: AudioMetadata): Uint8Array;
|
|
5
|
+
export declare function deserializeAudioMetadata(data: Uint8Array): AudioMetadata;
|
|
6
|
+
export declare function serializeMaterialSchemaMetadata(meta: MaterialSchemaMetadata): Uint8Array;
|
|
7
|
+
export declare function deserializeMaterialSchemaMetadata(data: Uint8Array): MaterialSchemaMetadata;
|
|
8
|
+
export declare function serializeMeshMetadata(meta: MeshMetadata): Uint8Array;
|
|
9
|
+
export declare function deserializeMeshMetadata(data: Uint8Array): MeshMetadata;
|
|
10
|
+
export declare function serializeAnimationClipMetadata(meta: AnimationClipMetadata): Uint8Array;
|
|
11
|
+
export declare function deserializeAnimationClipMetadata(data: Uint8Array): AnimationClipMetadata;
|
|
12
|
+
export declare function serializeSkinnedMeshMetadata(meta: SkinnedMeshMetadata): Uint8Array;
|
|
13
|
+
export declare function deserializeSkinnedMeshMetadata(data: Uint8Array): SkinnedMeshMetadata;
|
|
14
|
+
export declare function wrapMetadata(assetMetadata: Uint8Array, tags: string[]): Uint8Array;
|
|
15
|
+
export declare function unwrapMetadata(wrapped: Uint8Array): {
|
|
16
|
+
tags: string[];
|
|
17
|
+
assetMetadata: Uint8Array;
|
|
18
|
+
};
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { BinaryReader, BinaryWriter } from '../core/binary.js';
|
|
2
|
+
import { TEXTURE_METADATA_SIZE, AUDIO_METADATA_SIZE, STRING_ID_SIZE, MESH_GEOMETRY_INFO_SIZE, SCENE_NODE_SIZE, MATERIAL_DATA_FIXED_SIZE, MATERIAL_PROPERTY_VALUE_SIZE, MESH_METADATA_FIXED_SIZE, NODE_NAME_SIZE, MATERIAL_NAME_SIZE, MATERIAL_SCHEMA_ID_SIZE, MATERIAL_FIELD_NAME_SIZE, MATERIAL_SCHEMA_NAME_SIZE, MATERIAL_SCHEMA_DEFAULT_TEXTURE_ID_SIZE, MATERIAL_SCHEMA_METADATA_FIXED_SIZE, MATERIAL_SCHEMA_FIELD_SIZE, BONE_NAME_SIZE, SKINNED_MESH_GEOMETRY_INFO_SIZE, SKINNED_MESH_METADATA_FIXED_SIZE, BONE_SIZE, ANIMATION_TRACK_DESCRIPTOR_SIZE, ANIMATION_CLIP_METADATA_FIXED_SIZE, TRACK_NAME_SIZE, ANIMATION_METADATA_VERSION, } from '../core/constants.js';
|
|
3
|
+
// ─── Texture Metadata ───────────────────────────────────────────────────────
|
|
4
|
+
export function serializeTextureMetadata(meta) {
|
|
5
|
+
const w = new BinaryWriter(TEXTURE_METADATA_SIZE);
|
|
6
|
+
w.writeUint32(meta.width);
|
|
7
|
+
w.writeUint32(meta.height);
|
|
8
|
+
w.writeUint32(meta.channels);
|
|
9
|
+
w.writeUint32(meta.channelFormat);
|
|
10
|
+
w.writeFixedString(meta.stringId, STRING_ID_SIZE);
|
|
11
|
+
return w.getUint8Array();
|
|
12
|
+
}
|
|
13
|
+
export function deserializeTextureMetadata(data) {
|
|
14
|
+
const r = new BinaryReader(data);
|
|
15
|
+
return {
|
|
16
|
+
width: r.readUint32(),
|
|
17
|
+
height: r.readUint32(),
|
|
18
|
+
channels: r.readUint32(),
|
|
19
|
+
channelFormat: r.readUint32(),
|
|
20
|
+
stringId: r.readFixedString(STRING_ID_SIZE),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// ─── Audio Metadata ─────────────────────────────────────────────────────────
|
|
24
|
+
export function serializeAudioMetadata(meta) {
|
|
25
|
+
const w = new BinaryWriter(AUDIO_METADATA_SIZE);
|
|
26
|
+
w.writeFixedString(meta.stringId, STRING_ID_SIZE);
|
|
27
|
+
w.writeUint32(meta.sampleRate);
|
|
28
|
+
w.writeUint32(meta.channels);
|
|
29
|
+
w.writeUint32(meta.durationMs);
|
|
30
|
+
return w.getUint8Array();
|
|
31
|
+
}
|
|
32
|
+
export function deserializeAudioMetadata(data) {
|
|
33
|
+
const r = new BinaryReader(data);
|
|
34
|
+
return {
|
|
35
|
+
stringId: r.readFixedString(STRING_ID_SIZE),
|
|
36
|
+
sampleRate: r.readUint32(),
|
|
37
|
+
channels: r.readUint32(),
|
|
38
|
+
durationMs: r.readUint32(),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// ─── Mesh Metadata ──────────────────────────────────────────────────────────
|
|
42
|
+
function serializeGeometryInfo(w, g) {
|
|
43
|
+
w.writeUint32(g.vertexCount);
|
|
44
|
+
w.writeUint32(g.indexCount);
|
|
45
|
+
w.writeUint32(g.hasNormals);
|
|
46
|
+
w.writeUint32(g.hasUvs);
|
|
47
|
+
w.writeUint64FromNumber(g.compressedBufferOffset);
|
|
48
|
+
w.writeUint64FromNumber(g.compressedBufferSize);
|
|
49
|
+
w.writeUint32(g.materialIndex);
|
|
50
|
+
w.writeUint32(0);
|
|
51
|
+
}
|
|
52
|
+
function deserializeGeometryInfo(r) {
|
|
53
|
+
const info = {
|
|
54
|
+
vertexCount: r.readUint32(),
|
|
55
|
+
indexCount: r.readUint32(),
|
|
56
|
+
hasNormals: r.readUint32(),
|
|
57
|
+
hasUvs: r.readUint32(),
|
|
58
|
+
compressedBufferOffset: r.readUint64AsNumber(),
|
|
59
|
+
compressedBufferSize: r.readUint64AsNumber(),
|
|
60
|
+
materialIndex: r.readUint32(),
|
|
61
|
+
};
|
|
62
|
+
r.readUint32();
|
|
63
|
+
return info;
|
|
64
|
+
}
|
|
65
|
+
function serializeNode(w, n) {
|
|
66
|
+
w.writeFixedString(n.name, NODE_NAME_SIZE);
|
|
67
|
+
w.writeInt32(n.parentIndex);
|
|
68
|
+
w.writeFloat32(n.position[0]);
|
|
69
|
+
w.writeFloat32(n.position[1]);
|
|
70
|
+
w.writeFloat32(n.position[2]);
|
|
71
|
+
w.writeFloat32(n.rotation[0]);
|
|
72
|
+
w.writeFloat32(n.rotation[1]);
|
|
73
|
+
w.writeFloat32(n.rotation[2]);
|
|
74
|
+
w.writeFloat32(n.rotation[3]);
|
|
75
|
+
w.writeFloat32(n.scale[0]);
|
|
76
|
+
w.writeFloat32(n.scale[1]);
|
|
77
|
+
w.writeFloat32(n.scale[2]);
|
|
78
|
+
w.writeUint32(n.meshCount);
|
|
79
|
+
w.writeUint32(n.firstMeshIndex);
|
|
80
|
+
}
|
|
81
|
+
function deserializeNode(r) {
|
|
82
|
+
return {
|
|
83
|
+
name: r.readFixedString(NODE_NAME_SIZE),
|
|
84
|
+
parentIndex: r.readInt32(),
|
|
85
|
+
position: [r.readFloat32(), r.readFloat32(), r.readFloat32()],
|
|
86
|
+
rotation: [r.readFloat32(), r.readFloat32(), r.readFloat32(), r.readFloat32()],
|
|
87
|
+
scale: [r.readFloat32(), r.readFloat32(), r.readFloat32()],
|
|
88
|
+
meshCount: r.readUint32(),
|
|
89
|
+
firstMeshIndex: r.readUint32(),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function serializeMaterialProperty(w, p) {
|
|
93
|
+
w.writeFixedString(p.fieldName, MATERIAL_FIELD_NAME_SIZE);
|
|
94
|
+
w.writeFloat32(p.value[0]);
|
|
95
|
+
w.writeFloat32(p.value[1]);
|
|
96
|
+
w.writeFloat32(p.value[2]);
|
|
97
|
+
w.writeFloat32(p.value[3]);
|
|
98
|
+
w.writeFixedString(p.textureId, MATERIAL_FIELD_NAME_SIZE);
|
|
99
|
+
}
|
|
100
|
+
function deserializeMaterialProperty(r) {
|
|
101
|
+
return {
|
|
102
|
+
fieldName: r.readFixedString(MATERIAL_FIELD_NAME_SIZE),
|
|
103
|
+
value: [r.readFloat32(), r.readFloat32(), r.readFloat32(), r.readFloat32()],
|
|
104
|
+
textureId: r.readFixedString(MATERIAL_FIELD_NAME_SIZE),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function serializeMaterialHeader(w, m) {
|
|
108
|
+
w.writeFixedString(m.name, MATERIAL_NAME_SIZE);
|
|
109
|
+
w.writeFixedString(m.schemaId, MATERIAL_SCHEMA_ID_SIZE);
|
|
110
|
+
w.writeUint32(m.propertyCount);
|
|
111
|
+
}
|
|
112
|
+
function deserializeMaterialHeader(r) {
|
|
113
|
+
return {
|
|
114
|
+
name: r.readFixedString(MATERIAL_NAME_SIZE),
|
|
115
|
+
schemaId: r.readFixedString(MATERIAL_SCHEMA_ID_SIZE),
|
|
116
|
+
propertyCount: r.readUint32(),
|
|
117
|
+
properties: [],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function serializeMaterialSchemaField(w, field) {
|
|
121
|
+
w.writeFixedString(field.name, MATERIAL_FIELD_NAME_SIZE);
|
|
122
|
+
w.writeUint32(field.fieldType);
|
|
123
|
+
w.writeUint32(field.previewFlags);
|
|
124
|
+
w.writeFloat32(field.defaultValue[0]);
|
|
125
|
+
w.writeFloat32(field.defaultValue[1]);
|
|
126
|
+
w.writeFloat32(field.defaultValue[2]);
|
|
127
|
+
w.writeFloat32(field.defaultValue[3]);
|
|
128
|
+
w.writeFixedString(field.defaultTextureId, MATERIAL_SCHEMA_DEFAULT_TEXTURE_ID_SIZE);
|
|
129
|
+
}
|
|
130
|
+
function deserializeMaterialSchemaField(r) {
|
|
131
|
+
return {
|
|
132
|
+
name: r.readFixedString(MATERIAL_FIELD_NAME_SIZE),
|
|
133
|
+
fieldType: r.readUint32(),
|
|
134
|
+
previewFlags: r.readUint32(),
|
|
135
|
+
defaultValue: [r.readFloat32(), r.readFloat32(), r.readFloat32(), r.readFloat32()],
|
|
136
|
+
defaultTextureId: r.readFixedString(MATERIAL_SCHEMA_DEFAULT_TEXTURE_ID_SIZE),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
export function serializeMaterialSchemaMetadata(meta) {
|
|
140
|
+
const size = MATERIAL_SCHEMA_METADATA_FIXED_SIZE + meta.fields.length * MATERIAL_SCHEMA_FIELD_SIZE;
|
|
141
|
+
const w = new BinaryWriter(size);
|
|
142
|
+
w.writeFixedString(meta.stringId, STRING_ID_SIZE);
|
|
143
|
+
w.writeFixedString(meta.schemaName, MATERIAL_SCHEMA_NAME_SIZE);
|
|
144
|
+
w.writeUint32(meta.fields.length);
|
|
145
|
+
for (const field of meta.fields)
|
|
146
|
+
serializeMaterialSchemaField(w, field);
|
|
147
|
+
return w.getUint8Array();
|
|
148
|
+
}
|
|
149
|
+
export function deserializeMaterialSchemaMetadata(data) {
|
|
150
|
+
const r = new BinaryReader(data);
|
|
151
|
+
const stringId = r.readFixedString(STRING_ID_SIZE);
|
|
152
|
+
const schemaName = r.readFixedString(MATERIAL_SCHEMA_NAME_SIZE);
|
|
153
|
+
const fieldCount = r.readUint32();
|
|
154
|
+
const fields = [];
|
|
155
|
+
for (let i = 0; i < fieldCount; i++)
|
|
156
|
+
fields.push(deserializeMaterialSchemaField(r));
|
|
157
|
+
return { stringId, schemaName, fieldCount, fields };
|
|
158
|
+
}
|
|
159
|
+
export function serializeMeshMetadata(meta) {
|
|
160
|
+
let size = MESH_METADATA_FIXED_SIZE;
|
|
161
|
+
size += meta.geometries.length * MESH_GEOMETRY_INFO_SIZE;
|
|
162
|
+
size += meta.materials.length * MATERIAL_DATA_FIXED_SIZE;
|
|
163
|
+
size += meta.nodes.length * SCENE_NODE_SIZE;
|
|
164
|
+
size += meta.meshIndices.length * 4;
|
|
165
|
+
for (const m of meta.materials)
|
|
166
|
+
size += m.properties.length * MATERIAL_PROPERTY_VALUE_SIZE;
|
|
167
|
+
const w = new BinaryWriter(size);
|
|
168
|
+
w.writeUint32(meta.meshGeometryCount);
|
|
169
|
+
w.writeUint32(meta.materialCount);
|
|
170
|
+
w.writeUint32(meta.nodeCount);
|
|
171
|
+
w.writeFixedString(meta.stringId, STRING_ID_SIZE);
|
|
172
|
+
for (const g of meta.geometries)
|
|
173
|
+
serializeGeometryInfo(w, g);
|
|
174
|
+
for (const m of meta.materials)
|
|
175
|
+
serializeMaterialHeader(w, m);
|
|
176
|
+
for (const n of meta.nodes)
|
|
177
|
+
serializeNode(w, n);
|
|
178
|
+
for (const idx of meta.meshIndices)
|
|
179
|
+
w.writeUint32(idx);
|
|
180
|
+
for (const m of meta.materials) {
|
|
181
|
+
for (const p of m.properties)
|
|
182
|
+
serializeMaterialProperty(w, p);
|
|
183
|
+
}
|
|
184
|
+
return w.getUint8Array();
|
|
185
|
+
}
|
|
186
|
+
export function deserializeMeshMetadata(data) {
|
|
187
|
+
const r = new BinaryReader(data);
|
|
188
|
+
const meshGeometryCount = r.readUint32();
|
|
189
|
+
const materialCount = r.readUint32();
|
|
190
|
+
const nodeCount = r.readUint32();
|
|
191
|
+
const stringId = r.readFixedString(STRING_ID_SIZE);
|
|
192
|
+
const geometries = [];
|
|
193
|
+
for (let i = 0; i < meshGeometryCount; i++)
|
|
194
|
+
geometries.push(deserializeGeometryInfo(r));
|
|
195
|
+
const materials = [];
|
|
196
|
+
for (let i = 0; i < materialCount; i++)
|
|
197
|
+
materials.push(deserializeMaterialHeader(r));
|
|
198
|
+
const nodes = [];
|
|
199
|
+
for (let i = 0; i < nodeCount; i++)
|
|
200
|
+
nodes.push(deserializeNode(r));
|
|
201
|
+
const totalMeshIndices = nodes.reduce((sum, n) => sum + n.meshCount, 0);
|
|
202
|
+
const meshIndices = [];
|
|
203
|
+
for (let i = 0; i < totalMeshIndices; i++)
|
|
204
|
+
meshIndices.push(r.readUint32());
|
|
205
|
+
for (const m of materials) {
|
|
206
|
+
for (let i = 0; i < m.propertyCount; i++)
|
|
207
|
+
m.properties.push(deserializeMaterialProperty(r));
|
|
208
|
+
}
|
|
209
|
+
return { meshGeometryCount, materialCount, nodeCount, stringId, geometries, materials, nodes, meshIndices };
|
|
210
|
+
}
|
|
211
|
+
// ─── Animation Clip Metadata ────────────────────────────────────────────────
|
|
212
|
+
function serializeTrackDescriptor(w, track) {
|
|
213
|
+
w.writeFixedString(track.trackName, TRACK_NAME_SIZE);
|
|
214
|
+
w.writeUint32(track.keyframeCount);
|
|
215
|
+
w.writeUint32(track.valuesPerKey);
|
|
216
|
+
w.writeUint64FromNumber(track.timesOffset);
|
|
217
|
+
w.writeUint64FromNumber(track.valuesOffset);
|
|
218
|
+
}
|
|
219
|
+
function deserializeTrackDescriptor(r) {
|
|
220
|
+
return {
|
|
221
|
+
trackName: r.readFixedString(TRACK_NAME_SIZE),
|
|
222
|
+
keyframeCount: r.readUint32(),
|
|
223
|
+
valuesPerKey: r.readUint32(),
|
|
224
|
+
timesOffset: r.readUint64AsNumber(),
|
|
225
|
+
valuesOffset: r.readUint64AsNumber(),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
export function serializeAnimationClipMetadata(meta) {
|
|
229
|
+
const size = ANIMATION_CLIP_METADATA_FIXED_SIZE + meta.tracks.length * ANIMATION_TRACK_DESCRIPTOR_SIZE;
|
|
230
|
+
const w = new BinaryWriter(size);
|
|
231
|
+
w.writeFixedString(meta.stringId, STRING_ID_SIZE);
|
|
232
|
+
w.writeFixedString(meta.targetMeshId, STRING_ID_SIZE);
|
|
233
|
+
w.writeUint32(ANIMATION_METADATA_VERSION);
|
|
234
|
+
w.writeFloat32(meta.duration);
|
|
235
|
+
w.writeUint32(meta.trackCount);
|
|
236
|
+
for (const track of meta.tracks)
|
|
237
|
+
serializeTrackDescriptor(w, track);
|
|
238
|
+
return w.getUint8Array();
|
|
239
|
+
}
|
|
240
|
+
export function deserializeAnimationClipMetadata(data) {
|
|
241
|
+
const r = new BinaryReader(data);
|
|
242
|
+
const stringId = r.readFixedString(STRING_ID_SIZE);
|
|
243
|
+
const targetMeshId = r.readFixedString(STRING_ID_SIZE);
|
|
244
|
+
const metadataVersion = r.readUint32();
|
|
245
|
+
const duration = r.readFloat32();
|
|
246
|
+
const trackCount = r.readUint32();
|
|
247
|
+
const tracks = [];
|
|
248
|
+
for (let i = 0; i < trackCount; i++)
|
|
249
|
+
tracks.push(deserializeTrackDescriptor(r));
|
|
250
|
+
return { stringId, targetMeshId, metadataVersion, duration, trackCount, tracks };
|
|
251
|
+
}
|
|
252
|
+
// ─── Skinned Mesh Metadata ───────────────────────────────────────────────────
|
|
253
|
+
function serializeSkinnedGeometryInfo(w, g) {
|
|
254
|
+
w.writeUint32(g.vertexCount);
|
|
255
|
+
w.writeUint32(g.indexCount);
|
|
256
|
+
w.writeUint32(g.hasNormals);
|
|
257
|
+
w.writeUint32(g.hasUvs);
|
|
258
|
+
w.writeUint64FromNumber(g.vertexBufferOffset);
|
|
259
|
+
w.writeUint64FromNumber(g.vertexBufferSize);
|
|
260
|
+
w.writeUint64FromNumber(g.indexBufferOffset);
|
|
261
|
+
w.writeUint64FromNumber(g.indexBufferSize);
|
|
262
|
+
w.writeUint64FromNumber(g.weightsOffset);
|
|
263
|
+
w.writeUint64FromNumber(g.weightsSize);
|
|
264
|
+
w.writeUint32(g.materialIndex);
|
|
265
|
+
w.writeUint32(g._padding);
|
|
266
|
+
}
|
|
267
|
+
function deserializeSkinnedGeometryInfo(r) {
|
|
268
|
+
return {
|
|
269
|
+
vertexCount: r.readUint32(),
|
|
270
|
+
indexCount: r.readUint32(),
|
|
271
|
+
hasNormals: r.readUint32(),
|
|
272
|
+
hasUvs: r.readUint32(),
|
|
273
|
+
vertexBufferOffset: r.readUint64AsNumber(),
|
|
274
|
+
vertexBufferSize: r.readUint64AsNumber(),
|
|
275
|
+
indexBufferOffset: r.readUint64AsNumber(),
|
|
276
|
+
indexBufferSize: r.readUint64AsNumber(),
|
|
277
|
+
weightsOffset: r.readUint64AsNumber(),
|
|
278
|
+
weightsSize: r.readUint64AsNumber(),
|
|
279
|
+
materialIndex: r.readUint32(),
|
|
280
|
+
_padding: r.readUint32(),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function serializeBone(w, bone) {
|
|
284
|
+
w.writeFixedString(bone.name, BONE_NAME_SIZE);
|
|
285
|
+
w.writeInt32(bone.parentIndex);
|
|
286
|
+
const m = bone.offsetMatrix;
|
|
287
|
+
for (let i = 0; i < 16; i++) {
|
|
288
|
+
w.writeFloat32(m?.[i] ?? (i % 5 === 0 ? 1 : 0));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function deserializeBone(r) {
|
|
292
|
+
const name = r.readFixedString(BONE_NAME_SIZE);
|
|
293
|
+
const parentIndex = r.readInt32();
|
|
294
|
+
const offsetMatrix = [];
|
|
295
|
+
for (let i = 0; i < 16; i++)
|
|
296
|
+
offsetMatrix.push(r.readFloat32());
|
|
297
|
+
return { name, parentIndex, offsetMatrix };
|
|
298
|
+
}
|
|
299
|
+
export function serializeSkinnedMeshMetadata(meta) {
|
|
300
|
+
let size = SKINNED_MESH_METADATA_FIXED_SIZE;
|
|
301
|
+
size += meta.geometries.length * SKINNED_MESH_GEOMETRY_INFO_SIZE;
|
|
302
|
+
size += meta.materials.length * MATERIAL_DATA_FIXED_SIZE;
|
|
303
|
+
size += meta.nodes.length * SCENE_NODE_SIZE;
|
|
304
|
+
size += meta.meshIndices.length * 4;
|
|
305
|
+
for (const m of meta.materials)
|
|
306
|
+
size += m.properties.length * MATERIAL_PROPERTY_VALUE_SIZE;
|
|
307
|
+
size += meta.bones.length * BONE_SIZE;
|
|
308
|
+
const w = new BinaryWriter(size);
|
|
309
|
+
w.writeUint32(meta.meshGeometryCount);
|
|
310
|
+
w.writeUint32(meta.materialCount);
|
|
311
|
+
w.writeUint32(meta.nodeCount);
|
|
312
|
+
w.writeUint32(meta.boneCount);
|
|
313
|
+
w.writeFixedString(meta.stringId, STRING_ID_SIZE);
|
|
314
|
+
for (const g of meta.geometries)
|
|
315
|
+
serializeSkinnedGeometryInfo(w, g);
|
|
316
|
+
for (const m of meta.materials)
|
|
317
|
+
serializeMaterialHeader(w, m);
|
|
318
|
+
for (const n of meta.nodes)
|
|
319
|
+
serializeNode(w, n);
|
|
320
|
+
for (const idx of meta.meshIndices)
|
|
321
|
+
w.writeUint32(idx);
|
|
322
|
+
for (const m of meta.materials) {
|
|
323
|
+
for (const p of m.properties)
|
|
324
|
+
serializeMaterialProperty(w, p);
|
|
325
|
+
}
|
|
326
|
+
for (const bone of meta.bones)
|
|
327
|
+
serializeBone(w, bone);
|
|
328
|
+
return w.getUint8Array();
|
|
329
|
+
}
|
|
330
|
+
export function deserializeSkinnedMeshMetadata(data) {
|
|
331
|
+
const r = new BinaryReader(data);
|
|
332
|
+
const meshGeometryCount = r.readUint32();
|
|
333
|
+
const materialCount = r.readUint32();
|
|
334
|
+
const nodeCount = r.readUint32();
|
|
335
|
+
const boneCount = r.readUint32();
|
|
336
|
+
const stringId = r.readFixedString(STRING_ID_SIZE);
|
|
337
|
+
const geometries = [];
|
|
338
|
+
for (let i = 0; i < meshGeometryCount; i++)
|
|
339
|
+
geometries.push(deserializeSkinnedGeometryInfo(r));
|
|
340
|
+
const materials = [];
|
|
341
|
+
for (let i = 0; i < materialCount; i++)
|
|
342
|
+
materials.push(deserializeMaterialHeader(r));
|
|
343
|
+
const nodes = [];
|
|
344
|
+
for (let i = 0; i < nodeCount; i++)
|
|
345
|
+
nodes.push(deserializeNode(r));
|
|
346
|
+
const totalMeshIndices = nodes.reduce((sum, n) => sum + n.meshCount, 0);
|
|
347
|
+
const meshIndices = [];
|
|
348
|
+
for (let i = 0; i < totalMeshIndices; i++)
|
|
349
|
+
meshIndices.push(r.readUint32());
|
|
350
|
+
for (const m of materials) {
|
|
351
|
+
for (let i = 0; i < m.propertyCount; i++)
|
|
352
|
+
m.properties.push(deserializeMaterialProperty(r));
|
|
353
|
+
}
|
|
354
|
+
const bones = [];
|
|
355
|
+
for (let i = 0; i < boneCount; i++)
|
|
356
|
+
bones.push(deserializeBone(r));
|
|
357
|
+
return { meshGeometryCount, materialCount, nodeCount, boneCount, stringId, geometries, materials, nodes, meshIndices, bones };
|
|
358
|
+
}
|
|
359
|
+
// ─── Metadata Wrapping ──────────────────────────────────────────────────────
|
|
360
|
+
export function wrapMetadata(assetMetadata, tags) {
|
|
361
|
+
const tagCsv = tags.length > 0 ? tags.join(',') + '\0' : '\0';
|
|
362
|
+
const tagBytes = new TextEncoder().encode(tagCsv);
|
|
363
|
+
const totalSize = 4 + tagBytes.length + assetMetadata.length;
|
|
364
|
+
const w = new BinaryWriter(totalSize);
|
|
365
|
+
w.writeUint32(tagBytes.length);
|
|
366
|
+
w.writeBytes(tagBytes);
|
|
367
|
+
w.writeBytes(assetMetadata);
|
|
368
|
+
return w.getUint8Array();
|
|
369
|
+
}
|
|
370
|
+
export function unwrapMetadata(wrapped) {
|
|
371
|
+
const r = new BinaryReader(wrapped);
|
|
372
|
+
const tagCsvLength = r.readUint32();
|
|
373
|
+
const tagBytes = r.readBytes(tagCsvLength);
|
|
374
|
+
const tagString = new TextDecoder().decode(tagBytes);
|
|
375
|
+
const tags = tagString
|
|
376
|
+
.replace(/\0/g, '')
|
|
377
|
+
.split(',')
|
|
378
|
+
.filter((t) => t.length > 0);
|
|
379
|
+
const assetMetadata = r.readBytes(r.remaining);
|
|
380
|
+
return { tags, assetMetadata };
|
|
381
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AssetType } from '../core/types.js';
|
|
2
|
+
export declare class StowPacker {
|
|
3
|
+
private assets;
|
|
4
|
+
addAsset(canonicalPath: string, type: AssetType, data: Uint8Array, metadata?: Uint8Array | null, tags?: string[]): void;
|
|
5
|
+
build(): Uint8Array;
|
|
6
|
+
get assetCount(): number;
|
|
7
|
+
clear(): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { BinaryWriter } from '../core/binary.js';
|
|
2
|
+
import { STOW_MAGIC, STOW_VERSION, FILE_HEADER_SIZE, DIRECTORY_ENTRY_SIZE, DATA_ALIGNMENT, } from '../core/constants.js';
|
|
3
|
+
import { djb2Hash, normalizePath, validatePath } from '../core/path.js';
|
|
4
|
+
import { wrapMetadata } from './metadata.js';
|
|
5
|
+
function calculatePadding(offset, alignment) {
|
|
6
|
+
return (alignment - (offset % alignment)) % alignment;
|
|
7
|
+
}
|
|
8
|
+
export class StowPacker {
|
|
9
|
+
assets = [];
|
|
10
|
+
addAsset(canonicalPath, type, data, metadata = null, tags = []) {
|
|
11
|
+
const pathError = validatePath(canonicalPath);
|
|
12
|
+
if (pathError)
|
|
13
|
+
throw new Error(`Invalid path "${canonicalPath}": ${pathError}`);
|
|
14
|
+
const normalized = normalizePath(canonicalPath);
|
|
15
|
+
const uid = djb2Hash(normalized);
|
|
16
|
+
if (this.assets.some((a) => a.uid === uid)) {
|
|
17
|
+
throw new Error(`Duplicate asset UID for path: ${canonicalPath}`);
|
|
18
|
+
}
|
|
19
|
+
const wrappedMetadata = metadata ? wrapMetadata(metadata, tags) : null;
|
|
20
|
+
this.assets.push({
|
|
21
|
+
canonicalPath: normalized,
|
|
22
|
+
type,
|
|
23
|
+
uid,
|
|
24
|
+
data,
|
|
25
|
+
metadata: wrappedMetadata,
|
|
26
|
+
tags,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
build() {
|
|
30
|
+
if (this.assets.length === 0) {
|
|
31
|
+
throw new Error('Cannot build an empty pack');
|
|
32
|
+
}
|
|
33
|
+
const directoryOffset = FILE_HEADER_SIZE;
|
|
34
|
+
const directorySize = this.assets.length * DIRECTORY_ENTRY_SIZE;
|
|
35
|
+
let currentOffset = directoryOffset + directorySize;
|
|
36
|
+
const dataOffsets = [];
|
|
37
|
+
for (const asset of this.assets) {
|
|
38
|
+
currentOffset += calculatePadding(currentOffset, DATA_ALIGNMENT);
|
|
39
|
+
dataOffsets.push(currentOffset);
|
|
40
|
+
currentOffset += asset.data.length;
|
|
41
|
+
}
|
|
42
|
+
const metadataOffsets = [];
|
|
43
|
+
for (const asset of this.assets) {
|
|
44
|
+
if (asset.metadata) {
|
|
45
|
+
currentOffset += calculatePadding(currentOffset, DATA_ALIGNMENT);
|
|
46
|
+
metadataOffsets.push(currentOffset);
|
|
47
|
+
currentOffset += asset.metadata.length;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
metadataOffsets.push(0);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const totalSize = currentOffset;
|
|
54
|
+
const w = new BinaryWriter(totalSize);
|
|
55
|
+
w.writeUint32(STOW_MAGIC);
|
|
56
|
+
w.writeUint32(STOW_VERSION);
|
|
57
|
+
w.writeUint32(this.assets.length);
|
|
58
|
+
w.writeUint64FromNumber(directoryOffset);
|
|
59
|
+
for (let i = 0; i < this.assets.length; i++) {
|
|
60
|
+
const asset = this.assets[i];
|
|
61
|
+
w.writeUint64(asset.uid);
|
|
62
|
+
w.writeUint32(asset.type);
|
|
63
|
+
w.writeUint64FromNumber(dataOffsets[i]);
|
|
64
|
+
w.writeUint64FromNumber(asset.data.length);
|
|
65
|
+
w.writeUint64FromNumber(metadataOffsets[i]);
|
|
66
|
+
w.writeUint32(asset.metadata ? asset.metadata.length : 0);
|
|
67
|
+
}
|
|
68
|
+
for (let i = 0; i < this.assets.length; i++) {
|
|
69
|
+
w.seek(dataOffsets[i]);
|
|
70
|
+
w.writeBytes(this.assets[i].data);
|
|
71
|
+
}
|
|
72
|
+
for (let i = 0; i < this.assets.length; i++) {
|
|
73
|
+
const asset = this.assets[i];
|
|
74
|
+
if (asset.metadata && metadataOffsets[i] > 0) {
|
|
75
|
+
w.seek(metadataOffsets[i]);
|
|
76
|
+
w.writeBytes(asset.metadata);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return w.getUint8Array();
|
|
80
|
+
}
|
|
81
|
+
get assetCount() {
|
|
82
|
+
return this.assets.length;
|
|
83
|
+
}
|
|
84
|
+
clear() {
|
|
85
|
+
this.assets = [];
|
|
86
|
+
}
|
|
87
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export * from './core/types.js';
|
|
2
|
+
export * from './core/constants.js';
|
|
3
|
+
export * from './core/binary.js';
|
|
4
|
+
export * from './core/path.js';
|
|
5
|
+
export { StowPacker } from './format/packer.js';
|
|
6
|
+
export * from './format/metadata.js';
|
|
7
|
+
export * from './app/disk-project.js';
|
|
8
|
+
export { BlobStore } from './app/blob-store.js';
|
|
9
|
+
export * from './app/state.js';
|
|
10
|
+
export * from './app/stowmeta-io.js';
|
|
11
|
+
export * from './app/stowmat-io.js';
|
|
12
|
+
export * from './app/process-cache.js';
|
|
13
|
+
export * from './node-fs.js';
|
|
14
|
+
export * from './encoders/interfaces.js';
|
|
15
|
+
export { NodeBasisEncoder } from './encoders/basis-encoder.js';
|
|
16
|
+
export { NodeDracoEncoder, dracoPresetToSettings } from './encoders/draco-encoder.js';
|
|
17
|
+
export { NodeAacEncoder, NodeAudioDecoder } from './encoders/aac-encoder.js';
|
|
18
|
+
export { NodeFbxImporter } from './encoders/fbx-loader.js';
|
|
19
|
+
export { SharpImageDecoder } from './encoders/image-decoder.js';
|
|
20
|
+
export { buildSkinnedMeshData } from './encoders/skinned-mesh-builder.js';
|
|
21
|
+
export { processAsset, buildPack, buildAnimationDataBlobsV2 } from './pipeline.js';
|
|
22
|
+
export type { ProcessingContext, ProcessResult } from './pipeline.js';
|
|
23
|
+
export { fullBuild, scanProject, showStatus } from './orchestrator.js';
|
|
24
|
+
export type { BuildOptions, ScanReport } from './orchestrator.js';
|
|
25
|
+
export { startServer } from './server.js';
|
|
26
|
+
export type { ServerOptions } from './server.js';
|
|
27
|
+
export { initProject } from './init.js';
|
|
28
|
+
export { cleanupProject } from './cleanup.js';
|