169 lines
4.4 KiB
TypeScript
169 lines
4.4 KiB
TypeScript
import type { Vec3 } from "../core/vector";
|
|
|
|
export const PROJECT_ASSET_KINDS = ["model", "image", "audio"] as const;
|
|
|
|
export type ProjectAssetKind = (typeof PROJECT_ASSET_KINDS)[number];
|
|
|
|
export interface ProjectAssetBoundingBox {
|
|
min: Vec3;
|
|
max: Vec3;
|
|
size: Vec3;
|
|
}
|
|
|
|
export interface ModelAssetMetadata {
|
|
kind: "model";
|
|
format: "glb" | "gltf";
|
|
sceneName: string | null;
|
|
nodeCount: number;
|
|
meshCount: number;
|
|
materialNames: string[];
|
|
textureNames: string[];
|
|
animationNames: string[];
|
|
boundingBox: ProjectAssetBoundingBox | null;
|
|
warnings: string[];
|
|
}
|
|
|
|
export interface ImageAssetMetadata {
|
|
kind: "image";
|
|
width: number;
|
|
height: number;
|
|
hasAlpha: boolean;
|
|
warnings: string[];
|
|
}
|
|
|
|
export interface AudioAssetMetadata {
|
|
kind: "audio";
|
|
durationSeconds: number | null;
|
|
channelCount: number | null;
|
|
sampleRateHz: number | null;
|
|
warnings: string[];
|
|
}
|
|
|
|
export type ProjectAssetMetadata = ModelAssetMetadata | ImageAssetMetadata | AudioAssetMetadata;
|
|
|
|
export interface ProjectAssetRecordBase<TKind extends ProjectAssetKind, TMetadata extends ProjectAssetMetadata> {
|
|
id: string;
|
|
kind: TKind;
|
|
sourceName: string;
|
|
mimeType: string;
|
|
storageKey: string;
|
|
byteLength: number;
|
|
metadata: TMetadata;
|
|
}
|
|
|
|
export type ModelAssetRecord = ProjectAssetRecordBase<"model", ModelAssetMetadata>;
|
|
export type ImageAssetRecord = ProjectAssetRecordBase<"image", ImageAssetMetadata>;
|
|
export type AudioAssetRecord = ProjectAssetRecordBase<"audio", AudioAssetMetadata>;
|
|
|
|
export type ProjectAssetRecord = ModelAssetRecord | ImageAssetRecord | AudioAssetRecord;
|
|
|
|
export function createProjectAssetStorageKey(assetId: string): string {
|
|
return `project-asset:${assetId}`;
|
|
}
|
|
|
|
export function isProjectAssetKind(value: unknown): value is ProjectAssetKind {
|
|
return value === "model" || value === "image" || value === "audio";
|
|
}
|
|
|
|
function cloneVec3(vector: Vec3): Vec3 {
|
|
return {
|
|
x: vector.x,
|
|
y: vector.y,
|
|
z: vector.z
|
|
};
|
|
}
|
|
|
|
function cloneBoundingBox(boundingBox: ProjectAssetBoundingBox | null): ProjectAssetBoundingBox | null {
|
|
if (boundingBox === null) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
min: cloneVec3(boundingBox.min),
|
|
max: cloneVec3(boundingBox.max),
|
|
size: cloneVec3(boundingBox.size)
|
|
};
|
|
}
|
|
|
|
function cloneModelAssetMetadata(metadata: ModelAssetMetadata): ModelAssetMetadata {
|
|
return {
|
|
kind: "model",
|
|
format: metadata.format,
|
|
sceneName: metadata.sceneName,
|
|
nodeCount: metadata.nodeCount,
|
|
meshCount: metadata.meshCount,
|
|
materialNames: [...metadata.materialNames],
|
|
textureNames: [...metadata.textureNames],
|
|
animationNames: [...metadata.animationNames],
|
|
boundingBox: cloneBoundingBox(metadata.boundingBox),
|
|
warnings: [...metadata.warnings]
|
|
};
|
|
}
|
|
|
|
function cloneImageAssetMetadata(metadata: ImageAssetMetadata): ImageAssetMetadata {
|
|
return {
|
|
kind: "image",
|
|
width: metadata.width,
|
|
height: metadata.height,
|
|
hasAlpha: metadata.hasAlpha,
|
|
warnings: [...metadata.warnings]
|
|
};
|
|
}
|
|
|
|
function cloneAudioAssetMetadata(metadata: AudioAssetMetadata): AudioAssetMetadata {
|
|
return {
|
|
kind: "audio",
|
|
durationSeconds: metadata.durationSeconds,
|
|
channelCount: metadata.channelCount,
|
|
sampleRateHz: metadata.sampleRateHz,
|
|
warnings: [...metadata.warnings]
|
|
};
|
|
}
|
|
|
|
export function cloneProjectAssetRecord(asset: ProjectAssetRecord): ProjectAssetRecord {
|
|
switch (asset.kind) {
|
|
case "model":
|
|
return {
|
|
id: asset.id,
|
|
kind: "model",
|
|
sourceName: asset.sourceName,
|
|
mimeType: asset.mimeType,
|
|
storageKey: asset.storageKey,
|
|
byteLength: asset.byteLength,
|
|
metadata: cloneModelAssetMetadata(asset.metadata)
|
|
};
|
|
case "image":
|
|
return {
|
|
id: asset.id,
|
|
kind: "image",
|
|
sourceName: asset.sourceName,
|
|
mimeType: asset.mimeType,
|
|
storageKey: asset.storageKey,
|
|
byteLength: asset.byteLength,
|
|
metadata: cloneImageAssetMetadata(asset.metadata)
|
|
};
|
|
case "audio":
|
|
return {
|
|
id: asset.id,
|
|
kind: "audio",
|
|
sourceName: asset.sourceName,
|
|
mimeType: asset.mimeType,
|
|
storageKey: asset.storageKey,
|
|
byteLength: asset.byteLength,
|
|
metadata: cloneAudioAssetMetadata(asset.metadata)
|
|
};
|
|
}
|
|
}
|
|
|
|
export function getProjectAssetKindLabel(kind: ProjectAssetKind): string {
|
|
switch (kind) {
|
|
case "model":
|
|
return "Model";
|
|
case "image":
|
|
return "Image";
|
|
case "audio":
|
|
return "Audio";
|
|
}
|
|
}
|
|
|