Add Chromium-only Blender WebEngine parity work
This commit is contained in:
336
web/protocol/depsgraph.ts
Normal file
336
web/protocol/depsgraph.ts
Normal file
@@ -0,0 +1,336 @@
|
||||
export interface DepsgraphMeshEvaluationIR {
|
||||
objectId: string;
|
||||
meshId: string;
|
||||
sourceMeshId: string;
|
||||
vertexCount: number;
|
||||
triangleCount: number;
|
||||
modifierCount: number;
|
||||
modifiers: DepsgraphModifierEvaluationIR[];
|
||||
worldMatrix: number[];
|
||||
positions: number[];
|
||||
indices: number[];
|
||||
}
|
||||
|
||||
export interface DepsgraphModifierEvaluationIR {
|
||||
uuid: string;
|
||||
index: number;
|
||||
persistentUid: number;
|
||||
typeCode: number;
|
||||
type: string;
|
||||
name: string;
|
||||
showViewport: boolean;
|
||||
showRender: boolean;
|
||||
showEditmode: boolean;
|
||||
showOnCage: boolean;
|
||||
status: "EVALUATED" | "DISABLED" | "BLOCKED";
|
||||
reason?: string;
|
||||
error?: string;
|
||||
errorCode?: "UNSUPPORTED_MODIFIER_TYPE" | "MODIFIER_TARGET_MISSING" | "BLENDER_MODIFIER_ERROR";
|
||||
suggestion?: string;
|
||||
targetObjectIds?: string[];
|
||||
dependsOn?: string[];
|
||||
}
|
||||
|
||||
export interface DepsgraphObjectEvaluationIR {
|
||||
objectId: string;
|
||||
type: "MESH" | "CURVE" | "SURFACE" | "FONT" | "METABALL" | "CURVES" | "POINT_CLOUD" | "VOLUME" | "LATTICE" | "ARMATURE" | "GREASE_PENCIL" | "EMPTY" | "OTHER";
|
||||
modifierCount: number;
|
||||
modifiers: DepsgraphModifierEvaluationIR[];
|
||||
}
|
||||
|
||||
export interface DepsgraphConstraintEvaluationIR {
|
||||
name: string;
|
||||
typeCode: number;
|
||||
influence: number;
|
||||
flag: number;
|
||||
targetObjectId?: string;
|
||||
}
|
||||
|
||||
export interface DepsgraphBoneEvaluationIR {
|
||||
id: string;
|
||||
name: string;
|
||||
poseMatrix: number[];
|
||||
constraints: DepsgraphConstraintEvaluationIR[];
|
||||
}
|
||||
|
||||
export interface DepsgraphArmatureEvaluationIR {
|
||||
id: string;
|
||||
objectId: string;
|
||||
bones: DepsgraphBoneEvaluationIR[];
|
||||
}
|
||||
|
||||
export interface DepsgraphNonMeshGeometryIR {
|
||||
objectId: string;
|
||||
sourceDataId: string;
|
||||
sourceType: "CURVE" | "SURFACE" | "FONT" | "METABALL";
|
||||
meshId: string;
|
||||
status: "EVALUATED" | "BLOCKED";
|
||||
errorCode?: "NON_MESH_DATA_BUDGET_EXCEEDED";
|
||||
vertexCount: number;
|
||||
edgeCount: number;
|
||||
triangleCount: number;
|
||||
worldMatrix: number[];
|
||||
positions?: number[];
|
||||
normals?: number[];
|
||||
edgeVertexIndices?: number[];
|
||||
indices?: number[];
|
||||
uvs?: number[];
|
||||
triangleMaterialIndices?: number[];
|
||||
sourceElementIndices?: number[];
|
||||
materialSlotIds: string[];
|
||||
sourceMappingStatus: "EVALUATED_FACE" | "EVALUATED_EDGE";
|
||||
}
|
||||
|
||||
export interface DepsgraphEvaluationIR {
|
||||
engine: "BlenderDepsgraph";
|
||||
status: "EVALUATED";
|
||||
scene: string;
|
||||
viewLayer: string;
|
||||
frame: number;
|
||||
objectCount: number;
|
||||
meshObjectCount: number;
|
||||
objects: DepsgraphObjectEvaluationIR[];
|
||||
meshes: DepsgraphMeshEvaluationIR[];
|
||||
nonMeshGeometries?: DepsgraphNonMeshGeometryIR[];
|
||||
armatures?: DepsgraphArmatureEvaluationIR[];
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function numberField(record: Record<string, unknown>, field: string): number {
|
||||
const value = record[field];
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`Depsgraph field ${field} is invalid`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function stringField(record: Record<string, unknown>, field: string): string {
|
||||
const value = record[field];
|
||||
if (typeof value !== "string") throw new Error(`Depsgraph field ${field} is invalid`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function numberArray(record: Record<string, unknown>, field: string): number[] {
|
||||
const value = record[field];
|
||||
if (!Array.isArray(value) || value.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
|
||||
throw new Error(`Depsgraph field ${field} is invalid`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function countField(record: Record<string, unknown>, field: string): number {
|
||||
const value = numberField(record, field);
|
||||
if (!Number.isSafeInteger(value) || value < 0) throw new Error(`Depsgraph field ${field} is not a non-negative count`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function booleanField(record: Record<string, unknown>, field: string): boolean {
|
||||
const value = record[field];
|
||||
if (typeof value !== "boolean") throw new Error(`Depsgraph field ${field} is invalid`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function constraintReports(record: Record<string, unknown>): DepsgraphConstraintEvaluationIR[] {
|
||||
const value = record.constraints;
|
||||
if (!Array.isArray(value)) throw new Error("Depsgraph bone constraints are invalid");
|
||||
return value.map((candidate) => {
|
||||
if (!isRecord(candidate)) throw new Error("Depsgraph constraint entry is invalid");
|
||||
const targetObjectId = candidate.targetObjectId;
|
||||
if (targetObjectId !== undefined && typeof targetObjectId !== "string") {
|
||||
throw new Error("Depsgraph constraint targetObjectId is invalid");
|
||||
}
|
||||
return {
|
||||
name: stringField(candidate, "name"),
|
||||
typeCode: countField(candidate, "typeCode"),
|
||||
influence: numberField(candidate, "influence"),
|
||||
flag: countField(candidate, "flag"),
|
||||
...(targetObjectId === undefined ? {} : { targetObjectId }),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function armatureReports(value: unknown): DepsgraphArmatureEvaluationIR[] {
|
||||
if (!Array.isArray(value)) throw new Error("Depsgraph armatures are invalid");
|
||||
return value.map((candidate) => {
|
||||
if (!isRecord(candidate)) throw new Error("Depsgraph armature entry is invalid");
|
||||
const bonesValue = candidate.bones;
|
||||
if (!Array.isArray(bonesValue)) throw new Error("Depsgraph armature bones are invalid");
|
||||
const bones = bonesValue.map((boneValue) => {
|
||||
if (!isRecord(boneValue)) throw new Error("Depsgraph bone entry is invalid");
|
||||
const poseMatrix = numberArray(boneValue, "poseMatrix");
|
||||
if (poseMatrix.length !== 16) throw new Error("Depsgraph bone poseMatrix must contain 16 numbers");
|
||||
return {
|
||||
id: stringField(boneValue, "id"),
|
||||
name: stringField(boneValue, "name"),
|
||||
poseMatrix,
|
||||
constraints: constraintReports(boneValue),
|
||||
};
|
||||
});
|
||||
return { id: stringField(candidate, "id"), objectId: stringField(candidate, "objectId"), bones };
|
||||
});
|
||||
}
|
||||
|
||||
function modifierReports(record: Record<string, unknown>): DepsgraphModifierEvaluationIR[] {
|
||||
const value = record.modifiers;
|
||||
if (!Array.isArray(value)) throw new Error(`Depsgraph field modifiers is invalid`);
|
||||
return value.map((candidate) => {
|
||||
if (!isRecord(candidate)) throw new Error("Depsgraph modifier entry is invalid");
|
||||
const status = stringField(candidate, "status");
|
||||
if (status !== "EVALUATED" && status !== "DISABLED" && status !== "BLOCKED") {
|
||||
throw new Error(`Depsgraph modifier status is invalid: ${status}`);
|
||||
}
|
||||
const dependsOn = candidate.dependsOn;
|
||||
if (dependsOn !== undefined && (!Array.isArray(dependsOn) || dependsOn.some((item) => typeof item !== "string"))) {
|
||||
throw new Error("Depsgraph modifier dependsOn is invalid");
|
||||
}
|
||||
const targetObjectIds = candidate.targetObjectIds;
|
||||
if (targetObjectIds !== undefined && (!Array.isArray(targetObjectIds) || targetObjectIds.some((item) => typeof item !== "string"))) {
|
||||
throw new Error("Depsgraph modifier targetObjectIds is invalid");
|
||||
}
|
||||
const reason = candidate.reason;
|
||||
const error = candidate.error;
|
||||
const errorCode = candidate.errorCode;
|
||||
const suggestion = candidate.suggestion;
|
||||
if (reason !== undefined && typeof reason !== "string") throw new Error("Depsgraph modifier reason is invalid");
|
||||
if (error !== undefined && typeof error !== "string") throw new Error("Depsgraph modifier error is invalid");
|
||||
if (errorCode !== undefined && !["UNSUPPORTED_MODIFIER_TYPE", "MODIFIER_TARGET_MISSING", "BLENDER_MODIFIER_ERROR"].includes(errorCode as string)) {
|
||||
throw new Error("Depsgraph modifier errorCode is invalid");
|
||||
}
|
||||
if (suggestion !== undefined && typeof suggestion !== "string") throw new Error("Depsgraph modifier suggestion is invalid");
|
||||
return {
|
||||
uuid: stringField(candidate, "uuid"),
|
||||
index: countField(candidate, "index"),
|
||||
persistentUid: countField(candidate, "persistentUid"),
|
||||
typeCode: countField(candidate, "typeCode"),
|
||||
type: stringField(candidate, "type"),
|
||||
name: stringField(candidate, "name"),
|
||||
showViewport: booleanField(candidate, "showViewport"),
|
||||
showRender: booleanField(candidate, "showRender"),
|
||||
showEditmode: booleanField(candidate, "showEditmode"),
|
||||
showOnCage: booleanField(candidate, "showOnCage"),
|
||||
status,
|
||||
...(dependsOn === undefined ? {} : { dependsOn: dependsOn as string[] }),
|
||||
...(targetObjectIds === undefined ? {} : { targetObjectIds: targetObjectIds as string[] }),
|
||||
...(reason === undefined ? {} : { reason }),
|
||||
...(error === undefined ? {} : { error }),
|
||||
...(errorCode === undefined ? {} : { errorCode: errorCode as DepsgraphModifierEvaluationIR["errorCode"] }),
|
||||
...(suggestion === undefined ? {} : { suggestion }),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function parseDepsgraphEvaluation(value: unknown): DepsgraphEvaluationIR {
|
||||
if (!isRecord(value) || value.engine !== "BlenderDepsgraph" || value.status !== "EVALUATED") {
|
||||
throw new Error("Blender Depsgraph report has an invalid status");
|
||||
}
|
||||
if (!Array.isArray(value.meshes)) throw new Error("Blender Depsgraph report has no mesh evaluations");
|
||||
if (!Array.isArray(value.objects)) throw new Error("Blender Depsgraph report has no object evaluations");
|
||||
const objects = value.objects.map((candidate) => {
|
||||
if (!isRecord(candidate)) throw new Error("Blender Depsgraph object entry is invalid");
|
||||
const type = stringField(candidate, "type");
|
||||
if (!["MESH", "CURVE", "SURFACE", "FONT", "METABALL", "CURVES", "POINT_CLOUD", "VOLUME", "LATTICE", "ARMATURE", "GREASE_PENCIL", "EMPTY", "OTHER"].includes(type)) {
|
||||
throw new Error(`Blender Depsgraph object type is invalid: ${type}`);
|
||||
}
|
||||
const modifiers = modifierReports(candidate);
|
||||
const modifierCount = countField(candidate, "modifierCount");
|
||||
if (modifiers.length !== modifierCount) throw new Error("Blender Depsgraph object modifier count is inconsistent");
|
||||
return {
|
||||
objectId: stringField(candidate, "objectId"),
|
||||
type: type as DepsgraphObjectEvaluationIR["type"],
|
||||
modifierCount,
|
||||
modifiers,
|
||||
};
|
||||
});
|
||||
const meshes = value.meshes.map((candidate) => {
|
||||
if (!isRecord(candidate)) throw new Error("Blender Depsgraph mesh entry is invalid");
|
||||
const vertexCount = countField(candidate, "vertexCount");
|
||||
const triangleCount = countField(candidate, "triangleCount");
|
||||
const modifiers = modifierReports(candidate);
|
||||
const modifierCount = countField(candidate, "modifierCount");
|
||||
if (modifiers.length !== modifierCount) throw new Error("Blender Depsgraph mesh modifier count is inconsistent");
|
||||
const worldMatrix = numberArray(candidate, "worldMatrix");
|
||||
const positions = numberArray(candidate, "positions");
|
||||
const indices = numberArray(candidate, "indices");
|
||||
if (worldMatrix.length !== 16 || positions.length !== vertexCount * 3 || indices.length !== triangleCount * 3) {
|
||||
throw new Error(`Depsgraph mesh ${stringField(candidate, "sourceMeshId")} buffer lengths are inconsistent`);
|
||||
}
|
||||
if (indices.some((index) => !Number.isSafeInteger(index) || index < 0 || index >= vertexCount)) {
|
||||
throw new Error(`Depsgraph mesh ${stringField(candidate, "sourceMeshId")} has an invalid index`);
|
||||
}
|
||||
return {
|
||||
objectId: stringField(candidate, "objectId"),
|
||||
meshId: stringField(candidate, "meshId"),
|
||||
sourceMeshId: stringField(candidate, "sourceMeshId"),
|
||||
vertexCount,
|
||||
triangleCount,
|
||||
modifierCount,
|
||||
modifiers,
|
||||
worldMatrix,
|
||||
positions,
|
||||
indices,
|
||||
};
|
||||
});
|
||||
const objectCount = countField(value, "objectCount");
|
||||
const meshObjectCount = countField(value, "meshObjectCount");
|
||||
if (objects.length !== objectCount) throw new Error("Blender Depsgraph object count is inconsistent");
|
||||
if (meshes.length !== meshObjectCount) throw new Error("Blender Depsgraph mesh object count is inconsistent");
|
||||
const armatures = value.armatures === undefined ? undefined : armatureReports(value.armatures);
|
||||
const nonMeshGeometries = value.nonMeshGeometries === undefined ? undefined : (() => {
|
||||
if (!Array.isArray(value.nonMeshGeometries)) throw new Error("Depsgraph non-mesh geometries are invalid");
|
||||
return value.nonMeshGeometries.map((candidate): DepsgraphNonMeshGeometryIR => {
|
||||
if (!isRecord(candidate)) throw new Error("Depsgraph non-mesh geometry entry is invalid");
|
||||
const sourceType = stringField(candidate, "sourceType");
|
||||
const status = stringField(candidate, "status");
|
||||
if (!["CURVE", "SURFACE", "FONT", "METABALL"].includes(sourceType) || (status !== "EVALUATED" && status !== "BLOCKED")) throw new Error("Depsgraph non-mesh geometry type or status is invalid");
|
||||
const vertexCount = countField(candidate, "vertexCount");
|
||||
const edgeCount = countField(candidate, "edgeCount");
|
||||
const triangleCount = countField(candidate, "triangleCount");
|
||||
const worldMatrix = numberArray(candidate, "worldMatrix");
|
||||
if (worldMatrix.length !== 16) throw new Error("Depsgraph non-mesh world matrix is invalid");
|
||||
const materialSlotIds = candidate.materialSlotIds;
|
||||
if (!Array.isArray(materialSlotIds) || materialSlotIds.some((item) => typeof item !== "string")) throw new Error("Depsgraph non-mesh material slots are invalid");
|
||||
const common = {
|
||||
objectId: stringField(candidate, "objectId"),
|
||||
sourceDataId: stringField(candidate, "sourceDataId"),
|
||||
sourceType: sourceType as DepsgraphNonMeshGeometryIR["sourceType"],
|
||||
meshId: stringField(candidate, "meshId"),
|
||||
status: status as DepsgraphNonMeshGeometryIR["status"],
|
||||
vertexCount,
|
||||
edgeCount,
|
||||
triangleCount,
|
||||
worldMatrix,
|
||||
materialSlotIds: materialSlotIds as string[],
|
||||
sourceMappingStatus: stringField(candidate, "sourceMappingStatus") as DepsgraphNonMeshGeometryIR["sourceMappingStatus"],
|
||||
};
|
||||
if (common.sourceMappingStatus !== "EVALUATED_FACE" && common.sourceMappingStatus !== "EVALUATED_EDGE") throw new Error("Depsgraph non-mesh source mapping status is invalid");
|
||||
if (status === "BLOCKED") {
|
||||
if (candidate.errorCode !== "NON_MESH_DATA_BUDGET_EXCEEDED") throw new Error("Depsgraph blocked non-mesh geometry has no budget error");
|
||||
return { ...common, errorCode: candidate.errorCode };
|
||||
}
|
||||
const positions = numberArray(candidate, "positions");
|
||||
const normals = numberArray(candidate, "normals");
|
||||
const edgeVertexIndices = numberArray(candidate, "edgeVertexIndices");
|
||||
const indices = numberArray(candidate, "indices");
|
||||
const uvs = numberArray(candidate, "uvs");
|
||||
const triangleMaterialIndices = numberArray(candidate, "triangleMaterialIndices");
|
||||
const sourceElementIndices = numberArray(candidate, "sourceElementIndices");
|
||||
if (positions.length !== vertexCount * 3 || normals.length !== vertexCount * 3 || edgeVertexIndices.length !== edgeCount * 2 || indices.length !== triangleCount * 3 || (uvs.length !== 0 && uvs.length !== triangleCount * 3 * 2) || triangleMaterialIndices.length !== triangleCount || sourceElementIndices.length !== triangleCount) throw new Error(`Depsgraph non-mesh ${common.sourceDataId} buffer lengths are inconsistent`);
|
||||
if (edgeVertexIndices.some((item) => !Number.isSafeInteger(item) || item < 0 || item >= vertexCount) || indices.some((item) => !Number.isSafeInteger(item) || item < 0 || item >= vertexCount) || triangleMaterialIndices.some((item) => !Number.isSafeInteger(item) || item < 0) || sourceElementIndices.some((item) => !Number.isSafeInteger(item) || item < 0)) throw new Error(`Depsgraph non-mesh ${common.sourceDataId} indices are invalid`);
|
||||
return { ...common, positions, normals, edgeVertexIndices, indices, uvs, triangleMaterialIndices, sourceElementIndices };
|
||||
});
|
||||
})();
|
||||
return {
|
||||
engine: "BlenderDepsgraph",
|
||||
status: "EVALUATED",
|
||||
scene: stringField(value, "scene"),
|
||||
viewLayer: stringField(value, "viewLayer"),
|
||||
frame: numberField(value, "frame"),
|
||||
objectCount,
|
||||
meshObjectCount,
|
||||
objects,
|
||||
meshes,
|
||||
...(nonMeshGeometries === undefined ? {} : { nonMeshGeometries }),
|
||||
...(armatures === undefined ? {} : { armatures }),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user