135 lines
3.6 KiB
TypeScript
135 lines
3.6 KiB
TypeScript
import {
|
|
defaultDirectorMotion,
|
|
readDirectorMotion,
|
|
validDirectorPose,
|
|
type DirectorMotion,
|
|
} from "./director-motion";
|
|
export type DirectorVector = [number, number, number];
|
|
export type DirectorObject = {
|
|
id: string;
|
|
name: string;
|
|
kind: "actor" | "box" | "sphere" | "model";
|
|
url?: string;
|
|
position: DirectorVector;
|
|
rotation: DirectorVector;
|
|
scale: DirectorVector;
|
|
};
|
|
export type DirectorCamera = {
|
|
id: string;
|
|
name: string;
|
|
position: DirectorVector;
|
|
target: DirectorVector;
|
|
fov: number;
|
|
};
|
|
export type DirectorSnapshot = {
|
|
id: string;
|
|
title: string;
|
|
url: string;
|
|
width: number;
|
|
height: number;
|
|
cameraId: string;
|
|
};
|
|
export type DirectorScene = {
|
|
objects: DirectorObject[];
|
|
cameras: DirectorCamera[];
|
|
activeCameraId: string;
|
|
ratio: string;
|
|
snapshots: DirectorSnapshot[];
|
|
motion: DirectorMotion;
|
|
};
|
|
export const directorRatios = ["16:9", "9:16", "1:1", "4:3", "3:4", "21:9"];
|
|
export function directorImageSize(ratio: string) {
|
|
const [w, h] = (directorRatios.includes(ratio) ? ratio : "16:9")
|
|
.split(":")
|
|
.map(Number);
|
|
return w >= h
|
|
? { width: 1280, height: Math.round((1280 * h) / w) }
|
|
: { width: Math.round((1280 * w) / h), height: 1280 };
|
|
}
|
|
export function defaultDirectorScene(): DirectorScene {
|
|
return {
|
|
objects: [
|
|
{
|
|
id: "actor-a",
|
|
name: "基础人偶",
|
|
kind: "actor",
|
|
position: [0, 0, 0],
|
|
rotation: [0, 0, 0],
|
|
scale: [1, 1, 1],
|
|
},
|
|
],
|
|
cameras: [
|
|
{
|
|
id: "camera-1",
|
|
name: "机位1",
|
|
position: [4, 3, 7],
|
|
target: [0, 1, 0],
|
|
fov: 50,
|
|
},
|
|
],
|
|
activeCameraId: "camera-1",
|
|
ratio: "16:9",
|
|
snapshots: [],
|
|
motion: defaultDirectorMotion(),
|
|
};
|
|
}
|
|
export function readDirectorScene(value: unknown): DirectorScene {
|
|
const fallback = defaultDirectorScene();
|
|
if (!value || typeof value !== "object") return fallback;
|
|
const v = value as DirectorScene;
|
|
const vector = (a: unknown): a is DirectorVector =>
|
|
Array.isArray(a) &&
|
|
a.length === 3 &&
|
|
a.every((n) => typeof n === "number" && Number.isFinite(n));
|
|
const cameras = Array.isArray(v.cameras)
|
|
? v.cameras.filter(
|
|
(c) =>
|
|
c &&
|
|
typeof c.id === "string" &&
|
|
typeof c.name === "string" &&
|
|
vector(c.position) &&
|
|
vector(c.target) &&
|
|
Number.isFinite(c.fov) &&
|
|
c.fov >= 15 &&
|
|
c.fov <= 90 &&
|
|
validDirectorPose(c),
|
|
)
|
|
: [];
|
|
const objects = Array.isArray(v.objects)
|
|
? v.objects.filter(
|
|
(o) =>
|
|
o &&
|
|
typeof o.id === "string" &&
|
|
typeof o.name === "string" &&
|
|
["actor", "box", "sphere", "model"].includes(o.kind) &&
|
|
vector(o.position) &&
|
|
vector(o.rotation) &&
|
|
vector(o.scale) &&
|
|
o.scale.every((n) => n > 0) &&
|
|
(o.kind !== "model" || /^https?:\/\//i.test(o.url || "")),
|
|
)
|
|
: fallback.objects;
|
|
const snapshots = Array.isArray(v.snapshots)
|
|
? v.snapshots.filter(
|
|
(s) =>
|
|
s &&
|
|
typeof s.id === "string" &&
|
|
typeof s.title === "string" &&
|
|
/^https?:\/\//i.test(s.url) &&
|
|
s.width > 0 &&
|
|
s.height > 0,
|
|
)
|
|
: [];
|
|
const validCameras = cameras.length ? cameras : fallback.cameras;
|
|
return {
|
|
objects,
|
|
cameras: validCameras,
|
|
activeCameraId: validCameras.some((c) => c.id === v.activeCameraId)
|
|
? v.activeCameraId
|
|
: validCameras[0].id,
|
|
ratio: directorRatios.includes(v.ratio) ? v.ratio : "16:9",
|
|
snapshots,
|
|
motion: readDirectorMotion(v.motion),
|
|
};
|
|
}
|