Add Chromium-only Blender WebEngine parity work
This commit is contained in:
104
web/protocol/lod.ts
Normal file
104
web/protocol/lod.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { parseLODManifest, SimplifyValidationError, type LODManifest, type SimplifyProfile } from "./simplify";
|
||||
|
||||
export type LODPoseState = "REST" | "POSE";
|
||||
|
||||
export interface LODGenerationLevelRequest {
|
||||
profile: SimplifyProfile;
|
||||
triangleBudget: number;
|
||||
maxGeometricError?: number;
|
||||
screenSpaceError?: number;
|
||||
}
|
||||
|
||||
export interface LODGenerationRequest {
|
||||
meshId: string;
|
||||
sourceMeshRevision: number;
|
||||
levels: LODGenerationLevelRequest[];
|
||||
}
|
||||
|
||||
export interface LODCacheKeyInput {
|
||||
objectId: string;
|
||||
meshRevision: number;
|
||||
modifierStackHash: string;
|
||||
profileHash: string;
|
||||
poseOrRestState: LODPoseState;
|
||||
}
|
||||
|
||||
export interface LODCacheRecord extends LODManifest {
|
||||
cacheKey: string;
|
||||
objectId: string;
|
||||
modifierStackHash: string;
|
||||
profileHash: string;
|
||||
poseOrRestState: LODPoseState;
|
||||
generatedAt: string;
|
||||
byteLength?: number;
|
||||
lastAccessAt?: string;
|
||||
}
|
||||
|
||||
function fnv1a(value: string, seed: number): number {
|
||||
let hash = seed >>> 0;
|
||||
for (let index = 0; index < value.length; index++) {
|
||||
hash ^= value.charCodeAt(index);
|
||||
hash = Math.imul(hash, 0x01000193);
|
||||
}
|
||||
return hash >>> 0;
|
||||
}
|
||||
|
||||
function canonicalKey(input: LODCacheKeyInput): string {
|
||||
return JSON.stringify([
|
||||
input.objectId,
|
||||
input.meshRevision,
|
||||
input.modifierStackHash,
|
||||
input.profileHash,
|
||||
input.poseOrRestState,
|
||||
]);
|
||||
}
|
||||
|
||||
/** Stable, path-safe key. It intentionally excludes mutable timestamps. */
|
||||
export function buildLODCacheKey(input: LODCacheKeyInput): string {
|
||||
if (!input.objectId || !Number.isInteger(input.meshRevision) || input.meshRevision < 0) {
|
||||
throw new Error("Invalid LOD cache key input");
|
||||
}
|
||||
if (!input.modifierStackHash || !input.profileHash) throw new Error("LOD cache hashes are required");
|
||||
const value = canonicalKey(input);
|
||||
const first = fnv1a(value, 0x811c9dc5).toString(16).padStart(8, "0");
|
||||
const second = fnv1a(value, 0x9e3779b9).toString(16).padStart(8, "0");
|
||||
return `lod-${first}${second}`;
|
||||
}
|
||||
|
||||
export function lodManifestCacheKey(manifest: LODCacheRecord): string {
|
||||
return buildLODCacheKey({
|
||||
objectId: manifest.objectId,
|
||||
meshRevision: manifest.sourceMeshRevision,
|
||||
modifierStackHash: manifest.modifierStackHash,
|
||||
profileHash: manifest.profileHash,
|
||||
poseOrRestState: manifest.poseOrRestState,
|
||||
});
|
||||
}
|
||||
|
||||
export function parseLODCacheRecord(value: unknown): LODCacheRecord {
|
||||
const manifest = parseLODManifest(value);
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
||||
throw new SimplifyValidationError("INVALID_LOD_MANIFEST", "LOD cache record must be an object");
|
||||
}
|
||||
const input = value as Record<string, unknown>;
|
||||
const fields = ["cacheKey", "objectId", "modifierStackHash", "profileHash", "generatedAt"];
|
||||
if (fields.some((field) => typeof input[field] !== "string" || !input[field])) {
|
||||
throw new SimplifyValidationError("INVALID_LOD_MANIFEST", "LOD cache record metadata is incomplete");
|
||||
}
|
||||
if (input.poseOrRestState !== "REST" && input.poseOrRestState !== "POSE") {
|
||||
throw new SimplifyValidationError("INVALID_LOD_MANIFEST", "LOD poseOrRestState must be REST or POSE");
|
||||
}
|
||||
const record: LODCacheRecord = {
|
||||
...manifest,
|
||||
cacheKey: input.cacheKey as string,
|
||||
objectId: input.objectId as string,
|
||||
modifierStackHash: input.modifierStackHash as string,
|
||||
profileHash: input.profileHash as string,
|
||||
poseOrRestState: input.poseOrRestState,
|
||||
generatedAt: input.generatedAt as string,
|
||||
};
|
||||
if (lodManifestCacheKey(record) !== record.cacheKey) {
|
||||
throw new SimplifyValidationError("INVALID_LOD_MANIFEST", "LOD cacheKey does not match its source inputs");
|
||||
}
|
||||
return record;
|
||||
}
|
||||
Reference in New Issue
Block a user