update: add runtine camera keyframe
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import type {
|
||||
CinematicCameraKeyframe,
|
||||
CinematicDefinition,
|
||||
CinematicManifest,
|
||||
} from "@/types/cinematics/cinematics";
|
||||
import type { Vector3Tuple } from "@/types/three/three";
|
||||
|
||||
export function parseCinematicManifest(data: unknown): CinematicManifest {
|
||||
if (!isRecord(data) || data.version !== 1) {
|
||||
throw new Error("Invalid cinematic manifest version");
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.cinematics)) {
|
||||
throw new Error("Cinematic manifest requires a cinematics array");
|
||||
}
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
cinematics: data.cinematics.map(parseCinematicDefinition),
|
||||
};
|
||||
}
|
||||
|
||||
function parseCinematicDefinition(data: unknown): CinematicDefinition {
|
||||
if (!isRecord(data) || typeof data.id !== "string") {
|
||||
throw new Error("Invalid cinematic definition");
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.cameraKeyframes)) {
|
||||
throw new Error(`Cinematic ${data.id} requires cameraKeyframes`);
|
||||
}
|
||||
|
||||
const cameraKeyframes = data.cameraKeyframes.map(parseCameraKeyframe);
|
||||
if (cameraKeyframes.length < 2) {
|
||||
throw new Error(`Cinematic ${data.id} requires at least two keyframes`);
|
||||
}
|
||||
|
||||
cameraKeyframes.forEach((keyframe, index) => {
|
||||
const previousKeyframe = cameraKeyframes[index - 1];
|
||||
if (previousKeyframe && keyframe.time <= previousKeyframe.time) {
|
||||
throw new Error(`Cinematic ${data.id} keyframe times must increase`);
|
||||
}
|
||||
});
|
||||
|
||||
const cinematic: CinematicDefinition = {
|
||||
id: data.id,
|
||||
cameraKeyframes,
|
||||
};
|
||||
|
||||
if (typeof data.timecode === "number") {
|
||||
cinematic.timecode = data.timecode;
|
||||
}
|
||||
|
||||
return cinematic;
|
||||
}
|
||||
|
||||
function parseCameraKeyframe(data: unknown): CinematicCameraKeyframe {
|
||||
if (!isRecord(data) || typeof data.time !== "number") {
|
||||
throw new Error("Invalid cinematic camera keyframe");
|
||||
}
|
||||
|
||||
return {
|
||||
time: data.time,
|
||||
position: parseVector3(data.position),
|
||||
target: parseVector3(data.target),
|
||||
};
|
||||
}
|
||||
|
||||
function parseVector3(value: unknown): Vector3Tuple {
|
||||
if (
|
||||
!Array.isArray(value) ||
|
||||
value.length !== 3 ||
|
||||
value.some((item) => typeof item !== "number")
|
||||
) {
|
||||
throw new Error("Invalid cinematic vector");
|
||||
}
|
||||
|
||||
return [value[0], value[1], value[2]];
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { CinematicManifest } from "@/types/cinematics/cinematics";
|
||||
import { parseCinematicManifest } from "@/utils/cinematics/cinematicManifestValidation";
|
||||
|
||||
const CINEMATIC_MANIFEST_PATH = "/cinematics.json";
|
||||
|
||||
export async function loadCinematicManifest(): Promise<CinematicManifest | null> {
|
||||
const response = await fetch(CINEMATIC_MANIFEST_PATH);
|
||||
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parseCinematicManifest(await response.json());
|
||||
}
|
||||
Reference in New Issue
Block a user