467 lines
22 KiB
TypeScript
467 lines
22 KiB
TypeScript
import {
|
|
GEOMETRY_NODE_FIELD_BUDGET,
|
|
parseGeometryNodeDomainCardinality,
|
|
type GeometryNodeDomainCardinalityIR,
|
|
} from "./geometry-nodes";
|
|
|
|
export interface DepsgraphMeshEvaluationIR {
|
|
objectId: string;
|
|
meshId: string;
|
|
sourceMeshId: string;
|
|
vertexCount: number;
|
|
triangleCount: number;
|
|
modifierCount: number;
|
|
modifiers: DepsgraphModifierEvaluationIR[];
|
|
worldMatrix: number[];
|
|
positions: number[];
|
|
indices: number[];
|
|
domainCardinality?: GeometryNodeDomainCardinalityIR;
|
|
fieldMaterializations?: Array<{
|
|
schemaVersion: 1;
|
|
fieldId: string;
|
|
domain: "POINT";
|
|
dataType: "FLOAT";
|
|
elementCount: number;
|
|
scalarValueCount: number;
|
|
materializedByteLength: number;
|
|
transport: "JSON" | "BINARY_REQUIRED";
|
|
errorCode?: "GN_FIELD_JSON_BUDGET_EXCEEDED";
|
|
}>;
|
|
attributes?: Record<string, {
|
|
domain: "POINT";
|
|
dataType: "FLOAT";
|
|
values: 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" |
|
|
"GEOMETRY_NODES_SIMULATION_UNAVAILABLE" | "GEOMETRY_NODES_EVALUATOR_UNSUPPORTED";
|
|
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 domainCardinality(value: unknown): GeometryNodeDomainCardinalityIR {
|
|
return parseGeometryNodeDomainCardinality(value, "meshes[].domainCardinality");
|
|
}
|
|
|
|
function fieldMaterializations(
|
|
value: unknown,
|
|
cardinality: GeometryNodeDomainCardinalityIR,
|
|
): NonNullable<DepsgraphMeshEvaluationIR["fieldMaterializations"]> {
|
|
if (!Array.isArray(value) || value.length > 64) throw new Error("Depsgraph mesh fieldMaterializations are invalid");
|
|
const fieldIds = new Set<string>();
|
|
return value.map((candidate) => {
|
|
if (!isRecord(candidate) ||
|
|
Object.keys(candidate).some((key) => ![
|
|
"schemaVersion", "fieldId", "domain", "dataType", "elementCount",
|
|
"scalarValueCount", "materializedByteLength", "transport", "errorCode",
|
|
].includes(key)) ||
|
|
candidate.schemaVersion !== 1 || candidate.domain !== "POINT" || candidate.dataType !== "FLOAT" ||
|
|
(candidate.transport !== "JSON" && candidate.transport !== "BINARY_REQUIRED")) {
|
|
throw new Error("Depsgraph mesh field materialization is invalid");
|
|
}
|
|
const fieldId = stringField(candidate, "fieldId");
|
|
if (new TextEncoder().encode(fieldId).byteLength > GEOMETRY_NODE_FIELD_BUDGET.maxIdentifierBytes ||
|
|
fieldIds.has(fieldId)) {
|
|
throw new Error("Depsgraph mesh field materialization identity is invalid");
|
|
}
|
|
fieldIds.add(fieldId);
|
|
const elementCount = countField(candidate, "elementCount");
|
|
const scalarValueCount = countField(candidate, "scalarValueCount");
|
|
const materializedByteLength = countField(candidate, "materializedByteLength");
|
|
if (elementCount !== cardinality.POINT || scalarValueCount !== elementCount ||
|
|
materializedByteLength !== scalarValueCount * Float32Array.BYTES_PER_ELEMENT) {
|
|
throw new Error("Depsgraph mesh field materialization counts are inconsistent");
|
|
}
|
|
const errorCode = candidate.errorCode;
|
|
if ((candidate.transport === "JSON" && errorCode !== undefined) ||
|
|
(candidate.transport === "JSON" && scalarValueCount > GEOMETRY_NODE_FIELD_BUDGET.maxJsonScalarValuesPerField) ||
|
|
(candidate.transport === "BINARY_REQUIRED" &&
|
|
(errorCode !== "GN_FIELD_JSON_BUDGET_EXCEEDED" ||
|
|
scalarValueCount <= GEOMETRY_NODE_FIELD_BUDGET.maxJsonScalarValuesPerField))) {
|
|
throw new Error("Depsgraph mesh field materialization error is inconsistent");
|
|
}
|
|
return {
|
|
schemaVersion: 1 as const,
|
|
fieldId,
|
|
domain: "POINT" as const,
|
|
dataType: "FLOAT" as const,
|
|
elementCount,
|
|
scalarValueCount,
|
|
materializedByteLength,
|
|
transport: candidate.transport,
|
|
...(errorCode === undefined ? {} : { errorCode: errorCode as "GN_FIELD_JSON_BUDGET_EXCEEDED" }),
|
|
};
|
|
});
|
|
}
|
|
|
|
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",
|
|
"GEOMETRY_NODES_SIMULATION_UNAVAILABLE",
|
|
"GEOMETRY_NODES_EVALUATOR_UNSUPPORTED",
|
|
].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`);
|
|
}
|
|
const attributesValue = candidate.attributes;
|
|
const domainCardinalityValue = candidate.domainCardinality;
|
|
const parsedDomainCardinality = domainCardinalityValue === undefined ? undefined : domainCardinality(domainCardinalityValue);
|
|
const fieldMaterializationsValue = candidate.fieldMaterializations;
|
|
if (parsedDomainCardinality !== undefined && parsedDomainCardinality.POINT !== vertexCount) {
|
|
throw new Error("Depsgraph mesh POINT cardinality is inconsistent");
|
|
}
|
|
const parsedFieldMaterializations = fieldMaterializationsValue === undefined ? undefined : (() => {
|
|
if (parsedDomainCardinality === undefined) {
|
|
throw new Error("Depsgraph mesh field materializations require domain cardinality");
|
|
}
|
|
return fieldMaterializations(fieldMaterializationsValue, parsedDomainCardinality);
|
|
})();
|
|
let attributes: DepsgraphMeshEvaluationIR["attributes"];
|
|
if (attributesValue !== undefined) {
|
|
if (!isRecord(attributesValue) || Object.keys(attributesValue).length > 64) {
|
|
throw new Error("Depsgraph mesh attributes are invalid");
|
|
}
|
|
attributes = {};
|
|
for (const [name, attributeValue] of Object.entries(attributesValue)) {
|
|
if (name.length === 0 || name.length > 64 || !isRecord(attributeValue) ||
|
|
Object.keys(attributeValue).some((key) => !["domain", "dataType", "values"].includes(key)) ||
|
|
attributeValue.domain !== "POINT" || attributeValue.dataType !== "FLOAT") {
|
|
throw new Error(`Depsgraph mesh attribute ${name} is invalid`);
|
|
}
|
|
const values = numberArray(attributeValue, "values");
|
|
if (values.length !== vertexCount || parsedDomainCardinality === undefined ||
|
|
parsedDomainCardinality.POINT !== values.length) {
|
|
throw new Error(`Depsgraph mesh attribute ${name} length is inconsistent`);
|
|
}
|
|
const receipt = parsedFieldMaterializations?.find((entry) => entry.fieldId === `attribute:${name}`);
|
|
if (receipt === undefined || receipt.transport !== "JSON" ||
|
|
receipt.materializedByteLength !== values.length * Float32Array.BYTES_PER_ELEMENT) {
|
|
throw new Error(`Depsgraph mesh attribute ${name} has no matching field materialization`);
|
|
}
|
|
attributes[name] = { domain: "POINT", dataType: "FLOAT", values };
|
|
}
|
|
}
|
|
if (parsedFieldMaterializations?.some((entry) => entry.transport === "JSON" &&
|
|
(attributes === undefined || !Object.hasOwn(attributes, entry.fieldId.replace(/^attribute:/, ""))))) {
|
|
throw new Error("Depsgraph mesh JSON field materialization has no matching attribute payload");
|
|
}
|
|
return {
|
|
objectId: stringField(candidate, "objectId"),
|
|
meshId: stringField(candidate, "meshId"),
|
|
sourceMeshId: stringField(candidate, "sourceMeshId"),
|
|
vertexCount,
|
|
triangleCount,
|
|
modifierCount,
|
|
modifiers,
|
|
worldMatrix,
|
|
positions,
|
|
indices,
|
|
...(parsedDomainCardinality === undefined ? {} : { domainCardinality: parsedDomainCardinality }),
|
|
...(parsedFieldMaterializations === undefined ? {} : { fieldMaterializations: parsedFieldMaterializations }),
|
|
...(attributes === undefined ? {} : { attributes }),
|
|
};
|
|
});
|
|
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 }),
|
|
};
|
|
}
|