chore: checkpoint web Blender migration source, tools and docs
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
mes123456
2026-11-10 11:18:01 +08:00
parent 122142c26c
commit f3e21c6b98
6887 changed files with 257154 additions and 12294 deletions

View File

@@ -26,10 +26,11 @@ export const PHYSICS_FAMILIES = [
export type PhysicsFamily = typeof PHYSICS_FAMILIES[number];
export type PhysicsExecutionRequest = "METADATA" | "CACHE_MANIFEST" | "CACHE_PLAYBACK" | "LOCAL_SOLVER" | "SERVER_JOB";
export type PhysicsSettingValue = boolean | number | string | null;
export type PhysicsSettingValue = boolean | number | string | null | [number, number, number];
/** Typed Main-authoring payloads for the physics families exposed by WebEngine. */
export interface PhysicsRigidBodySettings {
substepsPerFrame?: number;
type?: number;
mass?: number;
friction?: number;
@@ -44,6 +45,7 @@ export interface PhysicsRigidBodySettings {
}
export interface PhysicsClothSettings {
pinVertexGroup?: string;
mass?: number;
airDamping?: number;
tensionStiffness?: number;
@@ -62,13 +64,64 @@ export interface PhysicsFluidSettings {
resolution?: number;
timeScale?: number;
viscosity?: number;
gravity?: number;
gravity?: number | [number, number, number];
cacheFrameStart?: number;
cacheFrameEnd?: number;
cacheType?: number;
}
export interface PhysicsSoftBodySettings {
cacheFrameStart?: number;
cacheFrameEnd?: number;
cacheFrameStep?: number;
cacheUseDisk?: boolean;
effectorCollectionId?: string | null;
effectorGravity?: number;
effectorAll?: number;
effectorForce?: number;
effectorVortex?: number;
effectorMagnetic?: number;
effectorWind?: number;
effectorCurveGuide?: number;
effectorTexture?: number;
effectorHarmonic?: number;
effectorCharge?: number;
effectorLennardJones?: number;
effectorBoid?: number;
effectorTurbulence?: number;
effectorDrag?: number;
effectorSmokeFlow?: number;
collisionCollectionId?: string | null;
useSelfCollision?: boolean;
useEdgeCollision?: boolean;
useFaceCollision?: boolean;
collisionType?: 0 | 1 | 2 | 3 | 4;
ballSize?: number;
ballStiffness?: number;
ballDamping?: number;
errorThreshold?: number;
stepMin?: number;
stepMax?: number;
choke?: number;
fuzzy?: number;
useEdges?: boolean;
useStiffQuads?: boolean;
pull?: number;
push?: number;
damping?: number;
bending?: number;
shear?: number;
plasticity?: number;
springLength?: number;
aero?: number;
aerodynamicsType?: 0 | 1;
springVertexGroup?: string;
useGoal?: boolean;
goalDefault?: number;
goalMin?: number;
goalMax?: number;
massVertexGroup?: string;
goalVertexGroup?: string;
nodeMass?: number;
gravity?: number;
mediaFriction?: number;
@@ -118,6 +171,23 @@ export interface PhysicsCacheBindingIR {
status: "COMPLETE" | "PARTIAL";
}
export interface PhysicsNativeCacheTargetIR {
ownerSessionUid: number;
modifierUid: number;
slotIndex: number;
}
export interface PhysicsNativeCacheSlotsIR {
count: number;
items: Array<{ index: number; name: string; baked: boolean }>;
}
export interface PhysicsNativeCacheStateIR {
slots?: PhysicsNativeCacheSlotsIR;
target?: PhysicsNativeCacheTargetIR;
baked: boolean;
baking: boolean;
outdated: boolean;
framesSkipped: boolean;
}
export interface PhysicsSystemIR {
id: string;
family: PhysicsFamily;
@@ -126,6 +196,7 @@ export interface PhysicsSystemIR {
settings: Record<string, PhysicsSettingValue>;
dependencyIds: string[];
cache?: PhysicsCacheBindingIR;
nativeCache?: PhysicsNativeCacheStateIR;
}
export interface PhysicsSimulationManifestIR {
@@ -248,13 +319,15 @@ function parseSettings(value: unknown, systemIndex: number): Record<string, Phys
const settings: Record<string, PhysicsSettingValue> = {};
for (const [name, item] of entries) {
if (name.length === 0 || name.length > 128 || !/^[A-Za-z0-9_.:-]+$/.test(name) ||
!(item === null || typeof item === "boolean" || typeof item === "string" || (typeof item === "number" && Number.isFinite(item)))) {
!((name === "gravity" && Array.isArray(item) && item.length === 3 && item.every(value => typeof value === "number" && Number.isFinite(value))) || item === null || typeof item === "boolean" || typeof item === "string" || (typeof item === "number" && Number.isFinite(item)))) {
throw new PhysicsSimulationValidationError("PHYSICS_MANIFEST_INVALID", `systems[${systemIndex}].settings.${name} is invalid`);
}
if (typeof item === "string" && item.length > 1_024) {
throw new PhysicsSimulationValidationError("PHYSICS_BUDGET_EXCEEDED", `systems[${systemIndex}].settings.${name} exceeds the string budget`);
}
settings[name] = item;
settings[name] = name === "gravity" && Array.isArray(item)
? [item[0] as number, item[1] as number, item[2] as number]
: item as PhysicsSettingValue;
}
if (new TextEncoder().encode(JSON.stringify(settings)).byteLength > PHYSICS_SIMULATION_BUDGET.maxSettingsBytes) {
throw new PhysicsSimulationValidationError("PHYSICS_BUDGET_EXCEEDED", `systems[${systemIndex}].settings exceeds the byte budget`);
@@ -357,6 +430,30 @@ function parseCache(
};
}
function parseNativeCacheTarget(value: unknown, index: number): PhysicsNativeCacheTargetIR | undefined {
if (value === undefined) return undefined;
if (!record(value) || Object.keys(value).length !== 3 || !["ownerSessionUid", "modifierUid", "slotIndex"].every(key => typeof value[key] === "number" && Number.isInteger(value[key]) && value[key] >= -2147483648 && value[key] <= 2147483647) || value.ownerSessionUid === 0 || (value.slotIndex as number) < 0) throw new PhysicsSimulationValidationError("PHYSICS_MANIFEST_INVALID", `systems[${index}].nativeCache.target is invalid`);
return { ownerSessionUid: value.ownerSessionUid as number, modifierUid: value.modifierUid as number, slotIndex: value.slotIndex as number };
}
function parseNativeCacheSlots(value: unknown, index: number): PhysicsNativeCacheSlotsIR | undefined {
if (value === undefined) return undefined;
if (!record(value) || Object.keys(value).length !== 2 || typeof value.count !== "number" || !Number.isInteger(value.count) || value.count < 1 || value.count > 2147483647 || !Array.isArray(value.items) || value.items.length === 0 || value.items.length > Math.min(128, value.count)) throw new PhysicsSimulationValidationError("PHYSICS_MANIFEST_INVALID", `systems[${index}].nativeCache.slots is invalid`);
const seen = new Set<number>();
const count = value.count;
const items = value.items.map(item => {
if (!record(item) || Object.keys(item).length !== 3 || typeof item.index !== "number" || !Number.isInteger(item.index) || item.index < 0 || item.index >= count || seen.has(item.index) || typeof item.name !== "string" || item.name.length > 63 || item.name.includes("\0") || typeof item.baked !== "boolean") throw new PhysicsSimulationValidationError("PHYSICS_MANIFEST_INVALID", `systems[${index}].nativeCache.slots item is invalid`);
seen.add(item.index); return { index: item.index, name: item.name, baked: item.baked };
});
return { count, items };
}
function parseNativeCacheState(value: unknown, index: number): PhysicsNativeCacheStateIR | undefined {
if (value === undefined || value === null) return undefined;
if (!record(value) || Object.keys(value).some(key => !["baked", "baking", "outdated", "framesSkipped", "target", "slots"].includes(key)) || !["baked", "baking", "outdated", "framesSkipped"].every(key => typeof value[key] === "boolean")) throw new PhysicsSimulationValidationError("PHYSICS_MANIFEST_INVALID", `systems[${index}].nativeCache is invalid`);
const target = parseNativeCacheTarget(value.target, index);
const slots = parseNativeCacheSlots(value.slots, index);
if (target && slots && !slots.items.some(item => item.index === target.slotIndex)) throw new PhysicsSimulationValidationError("PHYSICS_MANIFEST_INVALID", `systems[${index}].nativeCache active slot is absent`);
return { baked: value.baked as boolean, baking: value.baking as boolean, outdated: value.outdated as boolean, framesSkipped: value.framesSkipped as boolean, ...(target ? { target } : {}), ...(slots ? { slots } : {}) };
}
export function parsePhysicsSimulationManifest(value: unknown): PhysicsSimulationManifestIR {
if (!record(value) || value.schemaVersion !== PHYSICS_SIMULATION_SCHEMA || !Array.isArray(value.systems)) {
throw new PhysicsSimulationValidationError("PROTOCOL_MISMATCH", "Unsupported PhysicsSimulation manifest schema");
@@ -390,6 +487,7 @@ export function parsePhysicsSimulationManifest(value: unknown): PhysicsSimulatio
settings: parseSettings(item.settings, index),
dependencyIds,
cache: parseCache(item.cache, settingsHash, family, index),
nativeCache: parseNativeCacheState(item.nativeCache, index),
};
});