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

@@ -1,4 +1,5 @@
import type { ErrorReport } from "./error";
import type { EditorContextEdit, GraphInterpolation } from "./editor-workflow";
import type { ProgressEvent } from "./progress";
import type { NonMeshCurveSplineType, NonMeshFontCharacterIR, NonMeshFontLinksIR, NonMeshFontPropertiesIR, NonMeshFontTextBoxIR, SceneSnapshotIR } from "./scene-ir";
import type { SceneDelta } from "./scene-delta";
@@ -18,7 +19,7 @@ import type {
PhysicsSimulationPlaybackSettings,
PhysicsSoftBodySettings,
} from "./physics-simulation";
import type { ShaderGraphIR } from "./shader-graph";
import type { ShaderGraphIR, ShaderNodeEdit } from "./shader-graph";
import type { ShaderCompileReport } from "./shader-compiler";
import type { NlaMoveStripCommand, NlaTrackIR } from "./nla";
import type { RenderCapabilityRequest } from "./render-capabilities";
@@ -89,7 +90,7 @@ export interface MeshGeometryDelta {
export type MeshElementMode = "VERT" | "EDGE" | "FACE";
export type MeshPrimitive = "CUBE" | "PLANE" | "UV_SPHERE" | "ICO_SPHERE" | "CYLINDER" | "CONE" | "TORUS";
export type MeshEditOperation = "MERGE" | "DISSOLVE" | "EXTRUDE" | "INSET" | "BEVEL" | "LOOP_CUT" | "SUBDIVIDE" | "FILL" | "TRIANGULATE" | "RECALCULATE_NORMALS" | "BRIDGE_LOOPS" | "DELETE";
export type FCurveInterpolation = "CONSTANT" | "LINEAR" | "BEZIER";
export type FCurveInterpolation = GraphInterpolation;
export type KeyframeHandle = [number, number];
export interface FCurvePoint {
frame: number;
@@ -112,17 +113,22 @@ export type ArmatureEditOperation =
| { operation: "RENAME"; boneName: string; name: string }
| { operation: "EXTRUDE"; boneName: string; name: string; tail: [number, number, number] }
| { operation: "DUPLICATE"; boneName: string; name: string; offset?: [number, number, number] };
export type PoseConstraintType = "COPY_LOCATION" | "COPY_ROTATION" | "COPY_SCALE" | "IK" | "TRACK_TO";
export type PoseConstraintType = "COPY_LOCATION" | "COPY_ROTATION" | "COPY_SCALE" | "IK" | "TRACK_TO" |
"LIMIT_LOCATION" | "LIMIT_ROTATION" | "LIMIT_SCALE" | "DAMPED_TRACK" | "ACTION" | "CHILD_OF" |
"COPY_TRANSFORMS" | "LIMIT_DISTANCE" | "FOLLOW_PATH" | "LOCK_TRACK" | "STRETCH_TO" | "MINMAX" | "CLAMP_TO" | "TRANSFORM" | "SHRINKWRAP" |
"SAME_VOLUME" | "SPLINE_IK" | "PIVOT" | "FOLLOW_TRACK" | "CAMERA_SOLVER" | "OBJECT_SOLVER" |
"TRANSFORM_CACHE" | "ARMATURE" | "GEOMETRY_ATTRIBUTE";
export type WebEngineEditCommand =
| { type: "setFrame"; frame: number }
| { type: "setObjectVisibility"; objectId: string; visible: boolean }
| { type: "setModifierEnabled"; meshId: string; modifierUuid: string; enabled: boolean }
| { type: "setModifierVisibility"; meshId: string; modifierUuid: string; showViewport: boolean; showRender: boolean; showEditMode: boolean; showOnCage: boolean }
| { type: "addModifier"; meshId: string; modifierType: string; name?: string }
| { type: "removeModifier"; meshId: string; modifierUuid: string }
| { type: "moveModifier"; meshId: string; modifierUuid: string; direction?: "UP" | "DOWN"; index?: number }
| { type: "setModifierParameters"; meshId: string; modifierUuid: string; parameters: Record<string, unknown> }
| { type: "setModifierEnabled"; objectId?: string; baseRevision?: number; meshId: string; modifierUuid: string; enabled: boolean }
| { type: "setModifierVisibility"; objectId?: string; baseRevision?: number; meshId: string; modifierUuid: string; showViewport: boolean; showRender: boolean; showEditMode: boolean; showOnCage: boolean }
| { type: "addModifier"; objectId?: string; baseRevision?: number; meshId: string; modifierType: string; name?: string }
| { type: "removeModifier"; objectId?: string; baseRevision?: number; meshId: string; modifierUuid: string }
| { type: "moveModifier"; objectId?: string; baseRevision?: number; meshId: string; modifierUuid: string; direction?: "UP" | "DOWN"; index?: number }
| { type: "setModifierParameters"; curveGesture?: { id: string; phase: "begin" | "update" | "commit" | "cancel" }; objectId?: string; baseRevision?: number; meshId: string; modifierUuid: string; parameters: Record<string, unknown> }
| { type: "renameModifier"; objectId?: string; baseRevision?: number; meshId: string; modifierUuid: string; name: string }
| { type: "decimateMesh"; meshId: string; profile: SimplifyProfile }
| { type: "previewDecimateMesh"; meshId: string; profile: SimplifyProfile }
| { type: "createPrimitive"; primitive: MeshPrimitive; name?: string; location: [number, number, number] }
@@ -135,7 +141,9 @@ export type WebEngineEditCommand =
| { type: "setBoneRoll"; objectId: string; boneName: string; roll: number }
| { type: "setBoneSymmetry"; objectId: string; enabled: boolean }
| { type: "setBoneParenting"; objectId: string; boneName: string; parentName: string | null; connected: boolean }
| { type: "setPoseConstraint"; objectId: string; boneName: string; constraintName: string; constraintType: PoseConstraintType | null; targetObjectId?: string | null; targetBoneName?: string; influence?: number; enabled?: boolean }
| { type: "setPoseConstraint"; baseRevision?: number; objectId: string; boneName: string; constraintName: string; constraintType: PoseConstraintType | null; targetObjectId?: string | null; targetBoneName?: string; influence?: number; enabled?: boolean }
| { type: "editConstraint"; baseRevision?: number; objectId: string; constraintName: string; constraintType: PoseConstraintType | null; targetObjectId?: string | null; targetBoneName?: string; influence?: number; enabled?: boolean }
| { type: "renameConstraint"; baseRevision?: number; objectId: string; constraintName: string; newName: string; boneName?: string }
| { type: "translateMeshVertices"; meshId: string; vertexIndices: number[]; offset: [number, number, number] }
| { type: "meshEdit"; meshId: string; operation: MeshEditOperation; selectionMode: MeshElementMode; elementIndices: number[]; offset?: [number, number, number]; amount?: number; segments?: number }
| { type: "meshSubdivide"; meshId: string; selectionMode: MeshElementMode; elementIndices: number[]; segments?: number }
@@ -155,7 +163,7 @@ export type WebEngineEditCommand =
| { type: "setMaterialImageNode"; materialId: string; imageId: string; usage: "BASE_COLOR" | "NORMAL"; uvMap?: string }
| { type: "insertObjectKeyframe"; objectId: string; frame: number; property: "LOCATION" | "ROTATION_EULER" | "SCALE"; interpolation?: FCurveInterpolation }
| { type: "deleteObjectKeyframe"; objectId: string; frame: number; property?: "LOCATION" | "ROTATION_EULER" | "SCALE" }
| { type: "createAction"; name: string; objectId?: string }
| { type: "createAction"; name: string; objectId?: string; useFakeUser?: boolean }
| { type: "removeAction"; actionId: string }
| { type: "setKeyframe"; animationId: string; path: string; frame: number; value?: number[]; handleLeft?: KeyframeHandle; handleRight?: KeyframeHandle; interpolation?: FCurveInterpolation }
| { type: "setFCurveKeyframes"; animationId: string; path: string; points: FCurvePoint[] }
@@ -166,10 +174,13 @@ export type WebEngineEditCommand =
| { type: "setTimelineMarkers"; dataId: string; markers: Array<{ frame: number; name: string }> }
| { type: "setFCurveInterpolation"; animationId: string; path: string; interpolation: FCurveInterpolation }
| { type: "setActiveAction"; objectId: string; actionId: string | null }
| { type: "setConstraint"; objectId: string; constraintName: string; enabled: boolean; influence: number }
| { type: "setConstraint"; baseRevision?: number; objectId: string; constraintName: string; enabled: boolean; influence: number }
| { type: "moveConstraint"; baseRevision?: number; objectId: string; constraintName: string; direction: "UP" | "DOWN"; boneName?: string }
| { type: "setParent"; objectId: string; parentId: string | null; keepTransform?: boolean }
| { type: "setParents"; objectIds: string[]; parentId: string | null; keepTransform?: boolean }
| { type: "createCollection"; name: string; parentCollectionId?: string | null }
| { type: "moveObjectToCollection"; objectId: string; collectionId: string }
| { type: "moveObjectToCollection"; objectId: string; collectionId: string; keepExistingLinks?: boolean }
| { type: "moveCollection"; collectionId: string; parentCollectionId?: string | null }
| { type: "renameId"; id: string; name: string }
| { type: "joinObjects"; activeObjectId: string; objectIds: string[] }
| { type: "separateMeshFaces"; objectId: string; faceIndices: number[]; name?: string }
@@ -192,6 +203,9 @@ export type WebEngineEditCommand =
| { type: "importVFont"; schemaVersion: 1; projectId: string; assetId: string; assetPath: string; sourcePath: string; name: string; format: "TTF" | "OTF" | "PFB"; mimeType: string; byteLength: number; sha256: string; base64: string }
| { type: "setFontLinks"; dataId: string; links: NonMeshFontLinksIR }
| { type: "setTextSource"; dataId: string; source: string }
| { type: "createText"; name:string; source:string }
| { type: "editEditorContext"; edit: EditorContextEdit; baseRevision: number }
| { type: "editShaderNode"; materialId:string; edit:ShaderNodeEdit; baseRevision:number }
| { type: "setSoundProperties"; dataId: string; properties: { volume?: number; attenuation?: number; pitch?: number; minGain?: number; maxGain?: number; distance?: number; offsetTime?: number } }
| { type: "setSpeakerProperties"; dataId: string; properties: { volumeMax?: number; volumeMin?: number; distanceMax?: number; distanceReference?: number; attenuation?: number; coneAngleOuter?: number; coneAngleInner?: number; coneVolumeOuter?: number; volume?: number; pitch?: number } }
| { type: "setParticleSettings"; dataId: string; properties: { type?: number; from?: number; distribution?: number; physicsType?: number; totalParticles?: number; start?: number; end?: number; lifetime?: number; size?: number; drawSize?: number } }
@@ -249,8 +263,8 @@ export type WebEngineEditCommand =
| { type: "setMaskPoint"; maskId: string; layerId: string; splineId: string; point: MaskPointIR }
| { type: "setSceneLineSettings"; dataId: string; properties: { mode?: 1 | 2; thickness?: number } }
| { type: "setSceneViewLayerPassSettings"; dataId: string; layerName: string; properties: { passMask?: number; cryptomatteMask?: number; cryptomatteLevels?: number; alphaThreshold?: number } }
| { type: "setSceneViewLayerAOVs"; dataId: string; layerName: string; aovs: Array<{ name: string; type: 0 | 1 }> }
| { type: "setSceneViewLayerLightgroups"; dataId: string; layerName: string; lightgroups: string[] }
| { type: "setSceneViewLayerAOVs"; baseRevision?: number; dataId: string; layerName: string; aovs: Array<{ name: string; type: 0 | 1 }> }
| { type: "setSceneViewLayerLightgroups"; baseRevision?: number; dataId: string; layerName: string; lightgroups: string[] }
| { type: "clearObjectTransform"; objectId: string; channels: Array<"LOCATION" | "ROTATION" | "SCALE"> }
| { type: "createObjectData"; objectType: "CAMERA" | "LIGHT" | "EMPTY" | "ARMATURE"; name?: string }
| { type: "setObjectSelection"; objectId: string; selected: boolean; active?: boolean }
@@ -269,6 +283,8 @@ export type WebEngineEditCommand =
| { type: "setSimulationPlayback"; settings: PhysicsSimulationPlaybackSettings }
| { type: "setShaderGraph"; materialId: string; graph: ShaderGraphIR }
| { type: "setNLAStack"; objectId: string; tracks: NlaTrackIR[]; baseRevision?: number }
| { type: "setNLAProperty"; objectId: string; trackId: string; target: "TRACK"; property: "solo"; value: boolean; baseRevision?: number }
| { type: "setNLAProperty"; objectId: string; trackId: string; target: "STRIP"; stripId: string; property: "frameStart" | "frameEnd" | "actionFrameStart" | "actionFrameEnd" | "actionSlotHandle" | "useSyncLength" | "useAnimatedInfluence" | "useAutoBlend" | "scale" | "repeat" | "blendIn" | "blendOut" | "influence" | "blendMode" | "extrapolation" | "muted" | "reverse"; value: number | boolean | string; baseRevision?: number }
| NlaMoveStripCommand
| { type: "undo" }
| { type: "redo" };
@@ -281,8 +297,11 @@ export type WebEngineOperatorInvocation = BlenderOperatorInvocation;
export type RNAPropertyValue = boolean | number | string | null;
export interface RNADataBlockDescriptor {
/** Stable opaque identity for selecting one ID when libraries share a name. */
dataId?: string;
type: string;
name: string;
library?: string | null;
libraryLinked: boolean;
editable: boolean;
}
@@ -315,6 +334,15 @@ export type WebEngineJsonCommand =
| "applyCommand"
| "undo"
| "redo"
| "history"
| "historyJump"
| "ndofDecode"
| "ndofNavigate"
| "viewportCamera"
| "viewportFromCamera"
| "pythonExec"
| "pythonReset"
| "timelineFrame"
| "listBlenderOperators"
| "invokeBlenderOperator"
| "listBlenderMacros"
@@ -341,10 +369,16 @@ export type WebEngineRequest =
| { requestId: string; command: { type: "init" } }
| { requestId: string; command: { type: "loadWasmModule"; id: string } }
| { requestId: string; command: { type: "crashForTest" } }
| { requestId: string; command: { type: "openBlend"; buffer: ArrayBuffer }; }
| { requestId: string; command: { type: "openBlend"; buffer: ArrayBuffer; replacements?: Array<{ sourcePath: string; idCode: number; missingName: string; replacementName: string; replacementSourcePath?: string }>; libraries?: Array<{ path: string; buffer: ArrayBuffer }> }; }
| { requestId: string; command: { type: "applyServerBlendResult"; buffer: ArrayBuffer; baseRevision: number }; }
| { requestId: string; command: { type: "cancelOpen"; targetRequestId: string } }
| { requestId: string; command: { type: "openResourceStatus" } }
| { requestId: string; command: { type: "snapshot" } }
| { requestId: string; command: { type: "editorArchiveUpload"; path: string; buffer: ArrayBuffer } }
| { requestId: string; command: { type: "editorResourceDownload"; path: string } }
| { requestId: string; command: { type: "editorResourceList"; path: string } }
| { requestId: string; command: { type: "editorResourcePrepare"; path: string } }
| { requestId: string; command: WebEngineGPUCommand }
| { requestId: string; command: { type: "jsonCommand"; cmd: WebEngineJsonCommand; params?: Record<string, unknown> } }
| { requestId: string; command: { type: "applyCommand"; payload: WebEngineEditCommand } }
| { requestId: string; command: { type: "appendLibraryObject"; request: LibraryMainAppendRequestIR; source: ArrayBuffer } }
@@ -400,6 +434,7 @@ export interface WebEngineLODLevelResult {
export interface WebEngineLODResult {
manifest: LODManifest;
sourceBlendSha256?: string;
levels: WebEngineLODLevelResult[];
errors?: WebEngineLODError[];
}
@@ -410,8 +445,63 @@ export interface WebEngineLODError {
message: string;
}
/** Native WM cursor raster; JS only transports/displays these original pixels. */
export interface WebEngineGPUCursor {
revision: number;
visible: boolean;
width: number;
height: number;
hotspotX: number;
hotspotY: number;
rgba: Uint8Array;
}
export interface WebEngineGPUInput {
type: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11;
x: number;
y: number;
value: number;
key: number;
modifiers: number;
codepoint?: number;
location?: number;
/** Absolute pen samples; tilt uses GHOST's normalized [-1, 1] axes. */
tablet?: { mode: 1 | 2; pressure: number; tiltX: number; tiltY: number };
/** Types 1-3 from Pointer Lock; native GHOST owns continuous coordinates.
* Type 11 reports loss of a browser grab and cancels native modal handlers. */
relative?: boolean;
/** Type 9: relative paths already staged in this desktop's /browser directory. */
paths?: string[];
/** Type 10: key 0 = wheel ticks, 1 = trackpad pan, 2 = magnify.
* Deltas use GHOST directions; trackpad units are backing pixels.
* Relative types 1-3 use backing-pixel motion, zero for button transitions. */
deltaX?: number;
deltaY?: number;
}
export type WebEngineGPUCommand =
| { type: "gpuUI"; op: "files"; session: string; action: "list" }
| { type: "gpuUI"; op: "files"; session: string; action: "upload"; path: string; buffer: ArrayBuffer }
| { type: "gpuUI"; op: "files"; session: string; action: "download"; path: string }
| { type: "gpuUI"; op: "capabilities" }
| { type: "gpuUI"; op: "attach"; session: string; canvas: OffscreenCanvas; width: number; height: number; pixelRatio: number }
// Shared native operation state: 0 idle, 1 cancellable, 2 cancellation requested.
| { type: "gpuUI"; op: "frame"; session: string; ownerGeneration: number; width: number; height: number; pixelRatio: number; events: WebEngineGPUInput[]; interrupt?: SharedArrayBuffer; fullscreen?: boolean }
| { type: "gpuUI"; op: "invoke"; session: string; ownerGeneration: number; operator: string; properties: Record<string, BlenderOperatorPropertyValue>; areaType: string; mode: string; interrupt?: SharedArrayBuffer }
| { type: "gpuUI"; op: "detach"; session: string };
export interface WebEngineHistory {
schemaVersion: 1;
revision: number;
cursor: number;
limit: number;
entries: Array<{ index: number; label: string }>;
}
export interface WebEngineResult {
status: WebEngineStatus;
desktopFiles?: { root: string; entries: Array<{ path: string; size: number }>; path?: string; buffer?: ArrayBuffer };
gpuUI?: { surfaceAvailable: boolean; keyboardTextAvailable?: boolean; tabletInputAvailable?: boolean; cursorGrabAvailable?: boolean; normalGrabAvailable?: boolean; cursorGrab?: number; cursor?: WebEngineGPUCursor; nativeUI: false; attached: boolean; modalActive?: boolean; contextLost?: boolean; session?: string; ownerGeneration?: number; inputDiscarded?: boolean; displayAcknowledged?: boolean; exportPath?: string; exportSaveCopy?: boolean; exportSaveAs?: boolean; exportRevision?: number; error?: ErrorReport };
snapshot?: SceneSnapshotIR;
geometryBuffers?: MeshGeometryBuffer[];
nonMeshGeometryBuffers?: NonMeshGeometryChunk[];
@@ -423,6 +513,9 @@ export interface WebEngineResult {
capabilityGate?: CapabilityGateResult;
json?: unknown;
shaderCompile?: ShaderCompileReport;
commandFeedback?: string;
/** False preserves data history/persistence while transient editor context can refresh. */
commandChanged?: boolean;
depsgraph?: DepsgraphEvaluationIR;
blend?: ArrayBuffer;
openResources?: WebEngineOpenResourceStatus;
@@ -432,7 +525,8 @@ export interface WebEngineResult {
}
export type WebEngineResponse =
| { kind: "audioOutput"; port: MessagePort }
| { kind: "progress"; requestId: string; progress: ProgressEvent }
| { kind: "result"; requestId: string; ok: true; result: WebEngineResult }
| { kind: "result"; requestId: string; ok: false; error: ErrorReport };
| { kind: "result"; requestId: string; ok: false; error: ErrorReport; mainChanged?: boolean; recovery?: WebEngineResult; history?: WebEngineHistory };
import type { CameraSolveIR, MaskPointIR, MaskSplineIR, TrackingMarkerIR } from "./tracking-mask";