@forgeax/engine-import 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1161 -1092
- package/dist/index.mjs.map +1 -1
- package/dist/scriptable-pack-build.d.ts.map +1 -1
- package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
- package/dist/scriptable-pack.d.ts +6 -1
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/scriptable-pack-build.unit.test.ts +64 -1
- package/src/__tests__/scriptable-pack-host.unit.test.ts +4 -4
- package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +65 -26
- package/src/scriptable-pack-build.ts +55 -3
- package/src/scriptable-pack-output-producers.ts +24 -2
- package/src/scriptable-pack.ts +6 -1
package/dist/index.mjs
CHANGED
|
@@ -779,1217 +779,1286 @@ function projectImportProductForBuild(product) {
|
|
|
779
779
|
}))
|
|
780
780
|
};
|
|
781
781
|
}
|
|
782
|
-
function
|
|
783
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
784
|
-
}
|
|
785
|
-
function isAsset(value) {
|
|
786
|
-
return record(value) && typeof value.kind === "string" && isScriptablePackAssetKind(value.kind);
|
|
787
|
-
}
|
|
788
|
-
function needsMaterialCook(asset) {
|
|
789
|
-
return asset.kind === "material" && Array.isArray(asset.passes) && !isEngineMaterial(asset);
|
|
790
|
-
}
|
|
791
|
-
function clone(value) {
|
|
792
|
-
return structuredClone(value);
|
|
793
|
-
}
|
|
794
|
-
function stable(value) {
|
|
795
|
-
if (value instanceof Uint8Array) return JSON.stringify(Array.from(value));
|
|
796
|
-
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
797
|
-
if (value !== null && typeof value === "object") {
|
|
798
|
-
const object = value;
|
|
799
|
-
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stable(object[key])}`).join(",")}}`;
|
|
800
|
-
}
|
|
801
|
-
return JSON.stringify(value) ?? "null";
|
|
802
|
-
}
|
|
803
|
-
async function digest(value) {
|
|
804
|
-
const crypto = globalThis.crypto?.subtle;
|
|
805
|
-
if (crypto === void 0) throw new Error("Web Crypto API is required for Pack fingerprints");
|
|
806
|
-
const bytes2 = await crypto.digest("SHA-256", new TextEncoder().encode(stable(value)));
|
|
807
|
-
return `sha256:${Array.from(new Uint8Array(bytes2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
808
|
-
}
|
|
809
|
-
function observedReader(source) {
|
|
810
|
-
const reads = /* @__PURE__ */ new Map();
|
|
811
|
-
const reader = {
|
|
812
|
-
async readByGuid(guid) {
|
|
813
|
-
const key = AssetGuid.format(guid).toLowerCase();
|
|
814
|
-
const cached = reads.get(key);
|
|
815
|
-
if (cached !== void 0) return ok(clone(cached.asset));
|
|
816
|
-
if (source === void 0) {
|
|
817
|
-
return err(
|
|
818
|
-
new AssetError({
|
|
819
|
-
code: "asset-not-found",
|
|
820
|
-
expected: `a published Asset snapshot for content dependency ${key}`,
|
|
821
|
-
hint: "publish the dependency or remove the content read from the Pack build",
|
|
822
|
-
detail: { sourcePath: key }
|
|
823
|
-
})
|
|
824
|
-
);
|
|
825
|
-
}
|
|
826
|
-
const result = await source.readByGuid(guid);
|
|
827
|
-
if (!result.ok) return result;
|
|
828
|
-
const asset = clone(result.value.asset);
|
|
829
|
-
reads.set(key, {
|
|
830
|
-
guid: key,
|
|
831
|
-
asset,
|
|
832
|
-
generation: result.value.generation,
|
|
833
|
-
digest: result.value.digest
|
|
834
|
-
});
|
|
835
|
-
return ok(clone(asset));
|
|
836
|
-
}
|
|
837
|
-
};
|
|
838
|
-
return { reader, reads };
|
|
839
|
-
}
|
|
840
|
-
function buildContext(packageId, values, reader) {
|
|
841
|
-
if (values === void 0) return { packageId, readByGuid: reader.readByGuid };
|
|
842
|
-
return { packageId, values, readByGuid: reader.readByGuid };
|
|
843
|
-
}
|
|
844
|
-
function sourceKeyFailure(sourcePath, sourceKey) {
|
|
845
|
-
return {
|
|
846
|
-
code: "pack-source-key-invalid",
|
|
847
|
-
expected: "a sourceKey matching the Pack source-key grammar",
|
|
848
|
-
hint: "return stable lower-case semantic keys instead of paths or output indexes",
|
|
849
|
-
detail: { sourcePath, sourceKey }
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
function outputValueError(sourcePath, sourceKey, expected, actual) {
|
|
782
|
+
function failure(sourceKey, expected, actual) {
|
|
853
783
|
return {
|
|
854
|
-
code: "
|
|
784
|
+
code: "mesh-bin-payload-invalid",
|
|
785
|
+
subject: "mesh-bin",
|
|
786
|
+
sourceKey,
|
|
855
787
|
expected,
|
|
856
|
-
|
|
857
|
-
|
|
788
|
+
actual,
|
|
789
|
+
recovery: "re-cook the source with its Meta sidecar through the build-time importer"
|
|
858
790
|
};
|
|
859
791
|
}
|
|
860
|
-
function
|
|
861
|
-
return {
|
|
862
|
-
code,
|
|
863
|
-
expected: code === "pack-output-reference-missing" ? "every output reference to resolve to a local or published AssetGuid" : "removed output GUIDs to have no incoming references",
|
|
864
|
-
hint: code === "pack-output-reference-missing" ? "build or publish the referenced Pack before verifying this output" : "migrate incoming references before publishing the topology change",
|
|
865
|
-
detail: { sourcePath, guids: [...guids].sort() }
|
|
866
|
-
};
|
|
792
|
+
function asAttributeMap(value) {
|
|
793
|
+
return value ?? {};
|
|
867
794
|
}
|
|
868
|
-
function
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
product
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
if (!Array.isArray(product.refs)) {
|
|
878
|
-
return outputValueError(
|
|
879
|
-
sourcePath,
|
|
880
|
-
sourceKey,
|
|
881
|
-
`producer ${producer.kind} to return a refs array`,
|
|
882
|
-
product.refs
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
if (!record(product.artifacts)) {
|
|
886
|
-
return outputValueError(
|
|
887
|
-
sourcePath,
|
|
888
|
-
sourceKey,
|
|
889
|
-
`producer ${producer.kind} to return an artifacts object`,
|
|
890
|
-
product.artifacts
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
const payload = product.payload;
|
|
894
|
-
if (!record(payload) || payload.kind !== producer.kind) {
|
|
895
|
-
return outputValueError(
|
|
896
|
-
sourcePath,
|
|
897
|
-
sourceKey,
|
|
898
|
-
`producer ${producer.kind} to return a matching payload kind`,
|
|
899
|
-
payload
|
|
795
|
+
function jsonValue(value) {
|
|
796
|
+
if (value instanceof Float32Array || value instanceof Uint16Array) return Array.from(value);
|
|
797
|
+
if (Array.isArray(value)) return value.map(jsonValue);
|
|
798
|
+
if (value !== null && typeof value === "object") {
|
|
799
|
+
return Object.fromEntries(
|
|
800
|
+
Object.entries(value).map(([key, nested]) => [key, jsonValue(nested)])
|
|
900
801
|
);
|
|
901
802
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
)
|
|
803
|
+
return value;
|
|
804
|
+
}
|
|
805
|
+
function refsMeta(payload, refs) {
|
|
806
|
+
const materialSlots = (payload.materialSlots ?? [{ slotName: "Default" }]).map(
|
|
807
|
+
(slot, slotIndex) => {
|
|
808
|
+
const defaultMaterial = slot.defaultMaterial;
|
|
809
|
+
let defaultMaterialRef;
|
|
810
|
+
if (defaultMaterial !== void 0) {
|
|
811
|
+
const guid = AssetGuid$1.format(defaultMaterial);
|
|
812
|
+
defaultMaterialRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
|
|
813
|
+
if (defaultMaterialRef < 0) {
|
|
814
|
+
throw new Error(
|
|
815
|
+
`material slot ${slotIndex} default material ${guid} is absent from refs`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return {
|
|
820
|
+
slotName: slot.slotName,
|
|
821
|
+
...slot.sourceKey === void 0 ? {} : { sourceKey: slot.sourceKey },
|
|
822
|
+
...defaultMaterialRef === void 0 ? {} : { defaultMaterialRef }
|
|
823
|
+
};
|
|
910
824
|
}
|
|
825
|
+
);
|
|
826
|
+
if (payload.lods !== void 0 && payload.lods.length > 7) {
|
|
827
|
+
throw new Error("MeshAsset LOD chain supports at most seven lower-detail levels");
|
|
911
828
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
829
|
+
let previousCoverage = 1;
|
|
830
|
+
const seenLodGuids = /* @__PURE__ */ new Set();
|
|
831
|
+
const lods = payload.lods?.map((lod, lodIndex) => {
|
|
832
|
+
const guid = AssetGuid$1.format(lod.mesh).toLowerCase();
|
|
833
|
+
const meshRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
|
|
834
|
+
if (meshRef < 0) {
|
|
835
|
+
throw new Error(`LOD ${lodIndex} mesh ${guid} is absent from refs`);
|
|
836
|
+
}
|
|
837
|
+
if (seenLodGuids.has(guid)) {
|
|
838
|
+
throw new Error(`LOD ${lodIndex} mesh ${guid} is duplicated`);
|
|
839
|
+
}
|
|
840
|
+
if (!Number.isFinite(lod.screenCoverage) || lod.screenCoverage <= 0 || lod.screenCoverage > 1 || lod.screenCoverage >= previousCoverage) {
|
|
841
|
+
throw new Error(
|
|
842
|
+
`LOD ${lodIndex} screenCoverage must be finite, in (0, 1], and strictly decreasing`
|
|
919
843
|
);
|
|
920
844
|
}
|
|
845
|
+
seenLodGuids.add(guid);
|
|
846
|
+
previousCoverage = lod.screenCoverage;
|
|
847
|
+
return { meshRef, screenCoverage: lod.screenCoverage };
|
|
848
|
+
});
|
|
849
|
+
if (payload.lodHysteresis !== void 0 && (!Number.isFinite(payload.lodHysteresis) || payload.lodHysteresis < 0 || payload.lodHysteresis >= 1)) {
|
|
850
|
+
throw new Error("lodHysteresis must be finite and in [0, 1)");
|
|
921
851
|
}
|
|
922
|
-
return
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
|
|
852
|
+
return {
|
|
853
|
+
submeshes: payload.submeshes === void 0 || payload.submeshes.length === 0 ? [{ indexOffset: 0, indexCount: payload.indices?.length ?? 0, materialSlot: 0 }] : payload.submeshes,
|
|
854
|
+
materialSlots,
|
|
855
|
+
...payload.aabb === void 0 ? {} : { aabb: jsonValue(payload.aabb) },
|
|
856
|
+
...payload.morphTargets === void 0 ? {} : { morphTargets: jsonValue(payload.morphTargets) },
|
|
857
|
+
...payload.morphWeights === void 0 ? {} : { morphWeights: jsonValue(payload.morphWeights) },
|
|
858
|
+
...lods === void 0 ? {} : { lods },
|
|
859
|
+
...payload.lodHysteresis === void 0 ? {} : { lodHysteresis: payload.lodHysteresis }
|
|
860
|
+
};
|
|
929
861
|
}
|
|
930
|
-
|
|
931
|
-
const subjectPackageId = options.subjectPackageId ?? options.definition.packageId;
|
|
932
|
-
const availableGuids = normalizedGuidSet(options.availableGuids);
|
|
933
|
-
const observed = observedReader(options.assetSource);
|
|
934
|
-
let effectiveValues;
|
|
935
|
-
if ("parameters" in options.definition) {
|
|
936
|
-
const resolved = resolvePackParameterValues(
|
|
937
|
-
options.definition,
|
|
938
|
-
options.values ?? {},
|
|
939
|
-
options.inheritedValues
|
|
940
|
-
);
|
|
941
|
-
if (!resolved.ok) return resolved;
|
|
942
|
-
effectiveValues = resolved.value;
|
|
943
|
-
} else if (options.values !== void 0 && Object.keys(options.values).length > 0) {
|
|
944
|
-
return err(
|
|
945
|
-
outputValueError(
|
|
946
|
-
options.sourcePath,
|
|
947
|
-
"$.values",
|
|
948
|
-
"zero-parameter Packs to omit values and instance capabilities",
|
|
949
|
-
options.values
|
|
950
|
-
)
|
|
951
|
-
);
|
|
952
|
-
} else if (options.subjectPackageId !== void 0 && PackageId.format(options.subjectPackageId).toLowerCase() !== PackageId.format(options.definition.packageId).toLowerCase()) {
|
|
953
|
-
return err({
|
|
954
|
-
code: "pack-parent-has-no-parameters",
|
|
955
|
-
expected: "a ScriptablePack source with parameters for an independent instance packageId",
|
|
956
|
-
hint: "use clone for a zero-parameter Pack instead of building it as an instance",
|
|
957
|
-
detail: {
|
|
958
|
-
sourcePath: options.sourcePath,
|
|
959
|
-
rootPackageId: PackageId.format(options.definition.packageId),
|
|
960
|
-
subjectPackageId: PackageId.format(options.subjectPackageId)
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
let built;
|
|
862
|
+
function packMeshBinV4(payload, sourceKey, refs = []) {
|
|
965
863
|
try {
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
} catch (cause) {
|
|
970
|
-
return err(
|
|
971
|
-
new ImportError({
|
|
972
|
-
code: "import-internal-error",
|
|
973
|
-
expected: "Pack build to return a structured Result without throwing",
|
|
974
|
-
hint: "repair the authoring function and return err(...) for expected failures",
|
|
975
|
-
detail: {
|
|
976
|
-
reason: `${options.sourcePath}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
977
|
-
}
|
|
978
|
-
})
|
|
979
|
-
);
|
|
980
|
-
}
|
|
981
|
-
if (!record(built) || typeof built.ok !== "boolean") {
|
|
982
|
-
return err(
|
|
983
|
-
new ImportError({
|
|
984
|
-
code: "import-internal-error",
|
|
985
|
-
expected: "Pack build to return a Result object",
|
|
986
|
-
hint: "return ok(sourceKeyToAsset) or err(structuredError) from the authoring function",
|
|
987
|
-
detail: { reason: `${options.sourcePath}: malformed build result` }
|
|
988
|
-
})
|
|
989
|
-
);
|
|
990
|
-
}
|
|
991
|
-
if (!built.ok) {
|
|
992
|
-
if (errorCode(built.error) !== void 0) return err(built.error);
|
|
993
|
-
return err(
|
|
994
|
-
new ImportError({
|
|
995
|
-
code: "import-internal-error",
|
|
996
|
-
expected: "a structured Pack build error",
|
|
997
|
-
hint: "return an error carrying code, expected, hint and detail",
|
|
998
|
-
detail: { reason: `${options.sourcePath}: ${String(built.error)}` }
|
|
999
|
-
})
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
if (!record(built.value)) {
|
|
1003
|
-
return err(
|
|
1004
|
-
outputValueError(
|
|
1005
|
-
options.sourcePath,
|
|
1006
|
-
"$",
|
|
1007
|
-
"build to return a sourceKey-to-Asset object",
|
|
1008
|
-
built.value
|
|
1009
|
-
)
|
|
1010
|
-
);
|
|
1011
|
-
}
|
|
1012
|
-
const imported = [];
|
|
1013
|
-
const stagedOutputs = [];
|
|
1014
|
-
const localGuids = /* @__PURE__ */ new Set();
|
|
1015
|
-
const materialCookerRegistry = new NativeCookerRegistry();
|
|
1016
|
-
for (const cooker of options.cookers ?? []) materialCookerRegistry.register(cooker);
|
|
1017
|
-
const nativeFingerprints = /* @__PURE__ */ new Map();
|
|
1018
|
-
for (const sourceKey of Object.keys(built.value).sort()) {
|
|
1019
|
-
if (!isValidPackSourceKey(sourceKey))
|
|
1020
|
-
return err(sourceKeyFailure(options.sourcePath, sourceKey));
|
|
1021
|
-
const asset = built.value[sourceKey];
|
|
1022
|
-
if (!isAsset(asset)) {
|
|
864
|
+
const vertices = payload.vertices;
|
|
865
|
+
const indices = payload.indices;
|
|
866
|
+
if (!(vertices instanceof Float32Array)) {
|
|
1023
867
|
return err(
|
|
1024
|
-
|
|
1025
|
-
options.sourcePath,
|
|
1026
|
-
sourceKey,
|
|
1027
|
-
"a concrete Asset with a supported kind",
|
|
1028
|
-
asset
|
|
1029
|
-
)
|
|
868
|
+
failure(sourceKey, "Float32Array interleaved vertices", "vertices is not Float32Array")
|
|
1030
869
|
);
|
|
1031
870
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
code: "pack-guid-collision",
|
|
1037
|
-
expected: "derived output GUIDs to be unique in the global source index",
|
|
1038
|
-
hint: "change the colliding packageId or repair the source index before publishing",
|
|
1039
|
-
detail: { sourcePath: options.sourcePath, guid }
|
|
1040
|
-
});
|
|
871
|
+
if (indices !== void 0 && !(indices instanceof Uint16Array || indices instanceof Uint32Array)) {
|
|
872
|
+
return err(
|
|
873
|
+
failure(sourceKey, "Uint16Array or Uint32Array indices", "indices has an unsupported type")
|
|
874
|
+
);
|
|
1041
875
|
}
|
|
1042
|
-
|
|
1043
|
-
const
|
|
1044
|
-
if (
|
|
876
|
+
const attributes = asAttributeMap(payload.attributes);
|
|
877
|
+
const projection = deriveVertexLayoutProjection(attributes);
|
|
878
|
+
if (projection.attributes.length === 0 || projection.arrayStride === 0) {
|
|
1045
879
|
return err(
|
|
1046
|
-
|
|
1047
|
-
options.sourcePath,
|
|
880
|
+
failure(
|
|
1048
881
|
sourceKey,
|
|
1049
|
-
|
|
1050
|
-
|
|
882
|
+
"a non-empty canonical geometry projection",
|
|
883
|
+
"projection has no attributes"
|
|
1051
884
|
)
|
|
1052
885
|
);
|
|
1053
886
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
produced = await producer.produce({ guid, sourceKey, asset });
|
|
1057
|
-
} catch (cause) {
|
|
887
|
+
const vertexCount = payload.vertexCount ?? vertices.byteLength / projection.arrayStride;
|
|
888
|
+
if (!Number.isSafeInteger(vertexCount) || vertexCount < 0) {
|
|
1058
889
|
return err(
|
|
1059
|
-
|
|
1060
|
-
code: "import-internal-error",
|
|
1061
|
-
expected: `producer ${producer.kind} to return a structured Result without throwing`,
|
|
1062
|
-
hint: "repair the output producer and return err(...) for expected failures",
|
|
1063
|
-
detail: {
|
|
1064
|
-
reason: `${options.sourcePath}:${sourceKey}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
1065
|
-
}
|
|
1066
|
-
})
|
|
890
|
+
failure(sourceKey, "a non-negative safe vertex cardinality", `vertexCount=${vertexCount}`)
|
|
1067
891
|
);
|
|
1068
892
|
}
|
|
1069
|
-
if (
|
|
893
|
+
if (vertices.byteLength !== vertexCount * projection.arrayStride) {
|
|
1070
894
|
return err(
|
|
1071
|
-
|
|
1072
|
-
options.sourcePath,
|
|
895
|
+
failure(
|
|
1073
896
|
sourceKey,
|
|
1074
|
-
`
|
|
1075
|
-
|
|
897
|
+
`vertices.byteLength=${vertexCount * projection.arrayStride}`,
|
|
898
|
+
`vertices.byteLength=${vertices.byteLength}; stride=${projection.arrayStride}`
|
|
1076
899
|
)
|
|
1077
900
|
);
|
|
1078
901
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
hint: "return an error carrying code, expected, hint and detail",
|
|
1086
|
-
detail: { reason: `${options.sourcePath}:${sourceKey}: ${String(produced.error)}` }
|
|
1087
|
-
})
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
const invalidProduct2 = productError(options.sourcePath, sourceKey, producer, produced.value);
|
|
1091
|
-
if (invalidProduct2 !== void 0) return err(invalidProduct2);
|
|
1092
|
-
const product2 = produced.value;
|
|
1093
|
-
let payload = product2.payload;
|
|
1094
|
-
let artifacts2 = product2.artifacts;
|
|
1095
|
-
if (needsMaterialCook(asset) && materialCookerRegistry.get("material") !== void 0) {
|
|
1096
|
-
const cooked = await materialCookerRegistry.runDraft("material", {
|
|
1097
|
-
guid,
|
|
1098
|
-
source: asset,
|
|
1099
|
-
sourceKey,
|
|
1100
|
-
sourcePath: options.sourcePath,
|
|
1101
|
-
refs: product2.refs.map((reference) => reference.guid)
|
|
1102
|
-
});
|
|
1103
|
-
if (!cooked.ok) return err(cooked.error);
|
|
1104
|
-
if (cooked.value.guid.toLowerCase() !== normalizedGuid) {
|
|
1105
|
-
return err({
|
|
1106
|
-
code: "pack-source-output-invalid",
|
|
1107
|
-
expected: "the authored material cooker to preserve the derived AssetGuid",
|
|
1108
|
-
hint: "repair the native material cooker output GUID and rebuild the Pack",
|
|
1109
|
-
detail: {
|
|
1110
|
-
sourcePath: options.sourcePath,
|
|
902
|
+
for (const attribute of projection.attributes) {
|
|
903
|
+
const value = attributes[attribute.key];
|
|
904
|
+
const components = attribute.byteLength / (attribute.format === "uint16x4" ? 2 : 4);
|
|
905
|
+
if (value === void 0 || !(value instanceof Float32Array) && !(value instanceof Uint16Array) || value.length !== vertexCount * components) {
|
|
906
|
+
return err(
|
|
907
|
+
failure(
|
|
1111
908
|
sourceKey,
|
|
1112
|
-
|
|
1113
|
-
|
|
909
|
+
`${attribute.key} cardinality=${vertexCount * components}`,
|
|
910
|
+
`${attribute.key} cardinality=${value?.byteLength ?? "missing"}`
|
|
911
|
+
)
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
const interleaved = new Uint8Array(vertexCount * projection.arrayStride);
|
|
916
|
+
const interleavedView = new DataView(interleaved.buffer);
|
|
917
|
+
for (const attribute of projection.attributes) {
|
|
918
|
+
const value = attributes[attribute.key];
|
|
919
|
+
if (value === void 0) continue;
|
|
920
|
+
const components = attribute.byteLength / (attribute.format === "uint16x4" ? 2 : 4);
|
|
921
|
+
for (let vertex = 0; vertex < vertexCount; vertex++) {
|
|
922
|
+
for (let component = 0; component < components; component++) {
|
|
923
|
+
const sourceIndex = vertex * components + component;
|
|
924
|
+
const targetOffset = vertex * projection.arrayStride + attribute.offset + component * (attribute.format === "uint16x4" ? 2 : 4);
|
|
925
|
+
if (attribute.format === "uint16x4") {
|
|
926
|
+
interleavedView.setUint16(targetOffset, value[sourceIndex] ?? 0, true);
|
|
927
|
+
} else {
|
|
928
|
+
interleavedView.setFloat32(
|
|
929
|
+
targetOffset,
|
|
930
|
+
value[sourceIndex] ?? 0,
|
|
931
|
+
true
|
|
932
|
+
);
|
|
1114
933
|
}
|
|
1115
|
-
}
|
|
934
|
+
}
|
|
1116
935
|
}
|
|
1117
|
-
payload = cooked.value.payload;
|
|
1118
|
-
artifacts2 = cooked.value.artifacts;
|
|
1119
|
-
nativeFingerprints.set(sourceKey, cooked.value.inputFingerprint);
|
|
1120
936
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
digest:
|
|
1133
|
-
|
|
937
|
+
const indexCount = indices?.length ?? 0;
|
|
938
|
+
const indexWidth = indices === void 0 || indexCount === 0 ? 0 : indices.BYTES_PER_ELEMENT;
|
|
939
|
+
const indexBytes = indexCount * indexWidth;
|
|
940
|
+
if (!Number.isSafeInteger(indexBytes) || indexBytes > 4294967295) {
|
|
941
|
+
return err(failure(sourceKey, "safe index payload byte length", `indexBytes=${indexBytes}`));
|
|
942
|
+
}
|
|
943
|
+
const meta = new TextEncoder().encode(JSON.stringify(refsMeta(payload, refs)));
|
|
944
|
+
const header = {
|
|
945
|
+
version: 4,
|
|
946
|
+
projectionVersion: projection.schemaVersion,
|
|
947
|
+
mask: projection.mask,
|
|
948
|
+
digest: projection.digest,
|
|
949
|
+
stride: projection.arrayStride,
|
|
950
|
+
vertexCount,
|
|
951
|
+
vertexBytes: interleaved.byteLength,
|
|
952
|
+
indexCount,
|
|
953
|
+
indexWidth,
|
|
954
|
+
indexBytes,
|
|
955
|
+
jsonBytes: meta.byteLength
|
|
956
|
+
};
|
|
957
|
+
const total = MESH_BIN_HEADER_V4_BYTES + interleaved.byteLength + indexBytes + meta.byteLength;
|
|
958
|
+
if (!Number.isSafeInteger(total) || total > 4294967295) {
|
|
959
|
+
return err(failure(sourceKey, "safe mesh binary byte length", `total=${total}`));
|
|
960
|
+
}
|
|
961
|
+
const out = new Uint8Array(total);
|
|
962
|
+
writeMeshBinHeader(header, out);
|
|
963
|
+
let offset = MESH_BIN_HEADER_V4_BYTES;
|
|
964
|
+
out.set(interleaved, offset);
|
|
965
|
+
offset += interleaved.byteLength;
|
|
966
|
+
if (indices !== void 0 && indexBytes > 0) {
|
|
967
|
+
out.set(new Uint8Array(indices.buffer, indices.byteOffset, indices.byteLength), offset);
|
|
968
|
+
offset += indexBytes;
|
|
969
|
+
}
|
|
970
|
+
out.set(meta, offset);
|
|
971
|
+
return ok(out);
|
|
972
|
+
} catch (error) {
|
|
973
|
+
return err(
|
|
974
|
+
failure(
|
|
975
|
+
sourceKey,
|
|
976
|
+
"valid canonical mesh payload",
|
|
977
|
+
error instanceof Error ? error.message : String(error)
|
|
978
|
+
)
|
|
979
|
+
);
|
|
1134
980
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// src/scriptable-pack.ts
|
|
984
|
+
var AssetOutputProducerRegistry = class {
|
|
985
|
+
producers = /* @__PURE__ */ new Map();
|
|
986
|
+
register(producer) {
|
|
987
|
+
if (producer.kind.trim().length === 0 || producer.version.trim().length === 0) {
|
|
988
|
+
throw new TypeError("Pack output producer kind and version must be non-empty");
|
|
989
|
+
}
|
|
990
|
+
if (typeof producer.produce !== "function") {
|
|
991
|
+
throw new TypeError(`Pack output producer ${producer.kind} must expose produce`);
|
|
1140
992
|
}
|
|
993
|
+
this.producers.set(producer.kind, producer);
|
|
1141
994
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
995
|
+
get(kind) {
|
|
996
|
+
return this.producers.get(kind);
|
|
997
|
+
}
|
|
998
|
+
versions() {
|
|
999
|
+
return Object.fromEntries(
|
|
1000
|
+
[...this.producers.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([kind, producer]) => [kind, producer.version])
|
|
1145
1001
|
);
|
|
1146
|
-
if (missing.length > 0)
|
|
1147
|
-
return err(referenceError("pack-output-reference-missing", options.sourcePath, missing));
|
|
1148
1002
|
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
packageId: PackageId.format(subjectPackageId),
|
|
1159
|
-
sourcePath: options.sourcePath,
|
|
1160
|
-
sourceClosure: options.sourceClosure ?? [],
|
|
1161
|
-
values: effectiveValues,
|
|
1162
|
-
externalEvidence: externalEvidence.map(({ guid, usage, digest: evidenceDigest }) => ({
|
|
1163
|
-
guid,
|
|
1164
|
-
usage,
|
|
1165
|
-
digest: evidenceDigest
|
|
1166
|
-
})),
|
|
1167
|
-
authoringContractVersion: options.authoringContractVersion ?? "scriptable-pack/1",
|
|
1168
|
-
producerVersions: options.outputs.versions()
|
|
1169
|
-
});
|
|
1170
|
-
const inputFingerprint = nativeFingerprints.size === 0 ? authoredInputFingerprint : await digest({
|
|
1171
|
-
sourceRevision: authoredInputFingerprint,
|
|
1172
|
-
nativeCookers: [...nativeFingerprints.entries()].sort(
|
|
1173
|
-
([left], [right]) => left.localeCompare(right)
|
|
1174
|
-
)
|
|
1175
|
-
});
|
|
1176
|
-
const refs = imported.flatMap((asset) => asset.refs);
|
|
1177
|
-
const artifacts = Object.fromEntries(
|
|
1178
|
-
imported.flatMap(
|
|
1179
|
-
(asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [`${asset.guid}/${key}`, artifact])
|
|
1180
|
-
)
|
|
1181
|
-
);
|
|
1182
|
-
const product = createImportProduct({
|
|
1183
|
-
assets: imported,
|
|
1184
|
-
sourceDependencies: (options.sourceClosure ?? []).map((entry) => entry.path),
|
|
1185
|
-
refs,
|
|
1186
|
-
artifacts,
|
|
1187
|
-
receipts: imported.map((asset) => ({
|
|
1188
|
-
guid: asset.guid,
|
|
1189
|
-
origin: "authoredPack",
|
|
1190
|
-
status: "succeeded",
|
|
1191
|
-
inputFingerprint
|
|
1192
|
-
})),
|
|
1193
|
-
diagnostics: [],
|
|
1194
|
-
sourceRevision: inputFingerprint,
|
|
1195
|
-
sourceKey: options.sourcePath
|
|
1196
|
-
});
|
|
1197
|
-
if (!product.ok) return err(product.error);
|
|
1198
|
-
return ok({
|
|
1199
|
-
product: product.value,
|
|
1200
|
-
stagedOutputs,
|
|
1201
|
-
externalEvidence,
|
|
1202
|
-
inputFingerprint,
|
|
1203
|
-
...options.publication === void 0 ? {} : { publication: options.publication }
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
// src/scriptable-pack-output-producers.ts
|
|
1006
|
+
function producerError(input, reason) {
|
|
1007
|
+
return new ImportError({
|
|
1008
|
+
code: "import-internal-error",
|
|
1009
|
+
expected: `ScriptablePack ${input.asset.kind} output ${input.sourceKey} to satisfy its domain producer contract`,
|
|
1010
|
+
hint: "fix the generated Asset payload and rebuild the ScriptablePack",
|
|
1011
|
+
detail: { reason: reason instanceof Error ? reason.message : String(reason) }
|
|
1204
1012
|
});
|
|
1205
1013
|
}
|
|
1206
|
-
function
|
|
1014
|
+
function formatGuid(value) {
|
|
1015
|
+
if (typeof value === "string") {
|
|
1016
|
+
const parsed = AssetGuid$1.parse(value);
|
|
1017
|
+
if (!parsed.ok) throw parsed.error;
|
|
1018
|
+
return AssetGuid$1.format(parsed.value);
|
|
1019
|
+
}
|
|
1020
|
+
return AssetGuid$1.format(value);
|
|
1021
|
+
}
|
|
1022
|
+
function canonical(value) {
|
|
1023
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
1024
|
+
if (value !== null && typeof value === "object") {
|
|
1025
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`).join(",")}}`;
|
|
1026
|
+
}
|
|
1027
|
+
return JSON.stringify(value) ?? "null";
|
|
1028
|
+
}
|
|
1029
|
+
function particleProgramArtifact(effect) {
|
|
1030
|
+
const artifactProgram = { format: effect.program.format, emitters: effect.program.emitters };
|
|
1031
|
+
const bytes2 = new TextEncoder().encode(canonical(artifactProgram));
|
|
1032
|
+
const fingerprint = `sha256:${bytesToHex(sha256(bytes2))}`;
|
|
1207
1033
|
return {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
if (local !== void 0) {
|
|
1212
|
-
return ok({
|
|
1213
|
-
asset: clone(local.asset),
|
|
1214
|
-
generation: 1,
|
|
1215
|
-
digest: local.digest ?? "sha256:staged"
|
|
1216
|
-
});
|
|
1217
|
-
}
|
|
1218
|
-
if (fallback === void 0) {
|
|
1219
|
-
return err({
|
|
1220
|
-
code: "asset-not-found",
|
|
1221
|
-
expected: "a staged or published content dependency",
|
|
1222
|
-
hint: "wait for the dependency subject to materialize",
|
|
1223
|
-
detail: { guid: key }
|
|
1224
|
-
});
|
|
1225
|
-
}
|
|
1226
|
-
return fallback.readByGuid(guid);
|
|
1227
|
-
}
|
|
1034
|
+
program: { ...effect.program, fingerprint },
|
|
1035
|
+
bytes: bytes2,
|
|
1036
|
+
fingerprint
|
|
1228
1037
|
};
|
|
1229
1038
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
...BUILTIN_MESH_ASSETS.map((asset) => asset.guid.toLowerCase())
|
|
1242
|
-
]);
|
|
1243
|
-
const maxPasses = options.maxPasses ?? Math.max(1, options.subjects.length + 1);
|
|
1244
|
-
let iterations = 0;
|
|
1245
|
-
for (; iterations < maxPasses && pending.size > 0; iterations += 1) {
|
|
1246
|
-
let progress = false;
|
|
1247
|
-
const waiting = /* @__PURE__ */ new Set();
|
|
1248
|
-
for (const [key, subject] of pending) {
|
|
1249
|
-
const result = await buildScriptablePack({
|
|
1250
|
-
definition: subject.definition,
|
|
1251
|
-
sourcePath: subject.sourcePath,
|
|
1252
|
-
...subject.subjectPackageId === void 0 ? {} : { subjectPackageId: subject.subjectPackageId },
|
|
1253
|
-
...subject.values === void 0 ? {} : { values: subject.values },
|
|
1254
|
-
...subject.inheritedValues === void 0 ? {} : { inheritedValues: subject.inheritedValues },
|
|
1255
|
-
...subject.sourceClosure === void 0 ? {} : { sourceClosure: subject.sourceClosure },
|
|
1256
|
-
outputs: options.outputs,
|
|
1257
|
-
...options.cookers === void 0 ? {} : { cookers: options.cookers },
|
|
1258
|
-
assetSource: stagedSource(staged, options.assetSource),
|
|
1259
|
-
availableGuids: /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]),
|
|
1260
|
-
deferReferenceValidation: true
|
|
1261
|
-
});
|
|
1262
|
-
if (result.ok) {
|
|
1263
|
-
results.set(key, result.value);
|
|
1264
|
-
for (const output of result.value.stagedOutputs)
|
|
1265
|
-
staged.set(AssetGuid.format(output.guid).toLowerCase(), output);
|
|
1266
|
-
pending.delete(key);
|
|
1267
|
-
progress = true;
|
|
1268
|
-
continue;
|
|
1269
|
-
}
|
|
1270
|
-
if (errorCode(result.error) === "asset-not-found") {
|
|
1271
|
-
const detail = record(result.error) && record(result.error.detail) ? result.error.detail : void 0;
|
|
1272
|
-
const guid = detail !== void 0 && typeof detail.guid === "string" ? detail.guid : void 0;
|
|
1273
|
-
if (guid !== void 0) waiting.add(guid.toLowerCase());
|
|
1274
|
-
continue;
|
|
1275
|
-
}
|
|
1276
|
-
return result;
|
|
1039
|
+
function materialProduct(input) {
|
|
1040
|
+
if (input.asset.kind !== "material") throw new TypeError("expected MaterialAsset");
|
|
1041
|
+
const material = input.asset;
|
|
1042
|
+
const refs = [];
|
|
1043
|
+
const addRef = (guid, sourceField) => {
|
|
1044
|
+
refs.push({ guid: formatGuid(guid), sourceField });
|
|
1045
|
+
return refs.length - 1;
|
|
1046
|
+
};
|
|
1047
|
+
const addMaterialRef = (value, sourceField) => {
|
|
1048
|
+
if (typeof value === "number") {
|
|
1049
|
+
throw new TypeError("material texture references must be GUIDs, not runtime handles");
|
|
1277
1050
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1051
|
+
return addRef(value, sourceField);
|
|
1052
|
+
};
|
|
1053
|
+
const textureFields = material.parameters === void 0 ? new Set(MATERIAL_TEXTURE_SLOTS) : new Set(
|
|
1054
|
+
material.parameters.filter(
|
|
1055
|
+
(parameter) => parameter.type === "texture" || parameter.type === "texture_cube"
|
|
1056
|
+
).map((parameter) => parameter.name)
|
|
1057
|
+
);
|
|
1058
|
+
const parent = material.parent === void 0 ? void 0 : addRef(material.parent, { fieldName: "parent" });
|
|
1059
|
+
const values = {};
|
|
1060
|
+
for (const fieldName of Object.keys(material.values ?? {}).sort()) {
|
|
1061
|
+
const value = material.values?.[fieldName];
|
|
1062
|
+
if (typeof value === "string" && textureFields.has(fieldName)) {
|
|
1063
|
+
values[fieldName] = {
|
|
1064
|
+
texture: addMaterialRef(value, { componentName: "<material>", fieldName })
|
|
1065
|
+
};
|
|
1066
|
+
} else if (value !== null && typeof value === "object" && !Array.isArray(value) && "texture" in value) {
|
|
1067
|
+
const texture = value;
|
|
1068
|
+
values[fieldName] = {
|
|
1069
|
+
...texture,
|
|
1070
|
+
texture: addMaterialRef(texture.texture, { componentName: "<material>", fieldName }),
|
|
1071
|
+
...texture.sampler === void 0 ? {} : {
|
|
1072
|
+
sampler: addMaterialRef(texture.sampler, {
|
|
1073
|
+
componentName: "<material>",
|
|
1074
|
+
fieldName: `${fieldName}.sampler`
|
|
1075
|
+
})
|
|
1288
1076
|
}
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
if (pending.size > 0) {
|
|
1293
|
-
return err({
|
|
1294
|
-
code: "pack-content-dependency-stalled",
|
|
1295
|
-
expected: "the content dependency worklist to finish within its bounded retry budget",
|
|
1296
|
-
hint: "inspect pendingSubjects and waitingGuids, then repair the dependency graph",
|
|
1297
|
-
detail: {
|
|
1298
|
-
pendingSubjects: [...pending.values()].map((subject) => subject.sourcePath).sort(),
|
|
1299
|
-
waitingGuids: [],
|
|
1300
|
-
iterations
|
|
1301
|
-
}
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
|
-
const knownGuids = /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]);
|
|
1305
|
-
const missingReferences = /* @__PURE__ */ new Set();
|
|
1306
|
-
for (const product of results.values()) {
|
|
1307
|
-
const contentReads = new Set(product.externalEvidence.map((read) => read.guid.toLowerCase()));
|
|
1308
|
-
for (const asset of product.product.assets) {
|
|
1309
|
-
for (const reference of asset.refs) {
|
|
1310
|
-
const guid = reference.guid.toLowerCase();
|
|
1311
|
-
if (!knownGuids.has(guid) && !contentReads.has(guid)) missingReferences.add(guid);
|
|
1312
|
-
}
|
|
1077
|
+
};
|
|
1078
|
+
} else {
|
|
1079
|
+
values[fieldName] = value;
|
|
1313
1080
|
}
|
|
1314
1081
|
}
|
|
1315
|
-
|
|
1316
|
-
return err(
|
|
1317
|
-
referenceError("pack-output-reference-missing", "worklist", [...missingReferences].sort())
|
|
1318
|
-
);
|
|
1319
|
-
}
|
|
1320
|
-
const removed = [...options.incomingRefs?.keys() ?? []].filter(
|
|
1321
|
-
(guid) => !staged.has(guid.toLowerCase())
|
|
1322
|
-
);
|
|
1323
|
-
const referencedRemoved = removed.filter(
|
|
1324
|
-
(guid) => (options.incomingRefs?.get(guid) ?? []).length > 0
|
|
1325
|
-
);
|
|
1326
|
-
if (referencedRemoved.length > 0)
|
|
1327
|
-
return err(referenceError("pack-output-reference-conflict", "worklist", referencedRemoved));
|
|
1328
|
-
return ok({
|
|
1329
|
-
products: [...results.values()].map((result) => result.product),
|
|
1330
|
-
buildProducts: [...results.values()],
|
|
1331
|
-
stagedOutputs: [...staged.values()],
|
|
1332
|
-
iterations: pending.size === 0 && options.subjects.length > 0 ? iterations + 1 : iterations
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
function failure(sourceKey, expected, actual) {
|
|
1082
|
+
const { parent: _parent, values: _values, ...materialShape } = material;
|
|
1336
1083
|
return {
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1084
|
+
payload: {
|
|
1085
|
+
...materialShape,
|
|
1086
|
+
...parent === void 0 ? {} : { parent },
|
|
1087
|
+
...material.values === void 0 ? {} : { values }
|
|
1088
|
+
},
|
|
1089
|
+
refs,
|
|
1090
|
+
artifacts: {}
|
|
1343
1091
|
};
|
|
1344
1092
|
}
|
|
1345
|
-
function
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1093
|
+
function meshProduct(input) {
|
|
1094
|
+
if (input.asset.kind !== "mesh") throw new TypeError("expected MeshAsset");
|
|
1095
|
+
const mesh = input.asset;
|
|
1096
|
+
const refs = [];
|
|
1097
|
+
for (let slotIndex = 0; slotIndex < mesh.materialSlots.length; slotIndex++) {
|
|
1098
|
+
const defaultMaterial = mesh.materialSlots[slotIndex]?.defaultMaterial;
|
|
1099
|
+
if (defaultMaterial === void 0) continue;
|
|
1100
|
+
refs.push({
|
|
1101
|
+
guid: formatGuid(defaultMaterial),
|
|
1102
|
+
sourceField: { fieldName: "materialSlots", arrayIndex: slotIndex }
|
|
1103
|
+
});
|
|
1355
1104
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
(
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1105
|
+
const seenRefs = new Set(refs.map((reference) => reference.guid.toLowerCase()));
|
|
1106
|
+
for (const [lodIndex, lod] of (mesh.lods ?? []).entries()) {
|
|
1107
|
+
const guid = formatGuid(lod.mesh);
|
|
1108
|
+
if (seenRefs.has(guid.toLowerCase())) continue;
|
|
1109
|
+
seenRefs.add(guid.toLowerCase());
|
|
1110
|
+
refs.push({ guid, sourceField: { fieldName: "lods", arrayIndex: lodIndex } });
|
|
1111
|
+
}
|
|
1112
|
+
return {
|
|
1113
|
+
payload: mesh,
|
|
1114
|
+
refs,
|
|
1115
|
+
artifacts: {
|
|
1116
|
+
body: {
|
|
1117
|
+
mediaType: "application/x-forgeax-mesh",
|
|
1118
|
+
assetCodec: { name: "mesh-binary", version: "4" },
|
|
1119
|
+
bytes: (() => {
|
|
1120
|
+
const packed = packMeshBinV4(
|
|
1121
|
+
mesh,
|
|
1122
|
+
input.sourceKey,
|
|
1123
|
+
refs.map((reference) => reference.guid)
|
|
1369
1124
|
);
|
|
1370
|
-
|
|
1125
|
+
if (!packed.ok) {
|
|
1126
|
+
throw packed.error;
|
|
1127
|
+
}
|
|
1128
|
+
return packed.value;
|
|
1129
|
+
})()
|
|
1371
1130
|
}
|
|
1372
|
-
return {
|
|
1373
|
-
slotName: slot.slotName,
|
|
1374
|
-
...slot.sourceKey === void 0 ? {} : { sourceKey: slot.sourceKey },
|
|
1375
|
-
...defaultMaterialRef === void 0 ? {} : { defaultMaterialRef }
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
);
|
|
1379
|
-
if (payload.lods !== void 0 && payload.lods.length > 7) {
|
|
1380
|
-
throw new Error("MeshAsset LOD chain supports at most seven lower-detail levels");
|
|
1381
|
-
}
|
|
1382
|
-
let previousCoverage = 1;
|
|
1383
|
-
const seenLodGuids = /* @__PURE__ */ new Set();
|
|
1384
|
-
const lods = payload.lods?.map((lod, lodIndex) => {
|
|
1385
|
-
const guid = AssetGuid$1.format(lod.mesh).toLowerCase();
|
|
1386
|
-
const meshRef = refs.findIndex((candidate) => candidate.toLowerCase() === guid);
|
|
1387
|
-
if (meshRef < 0) {
|
|
1388
|
-
throw new Error(`LOD ${lodIndex} mesh ${guid} is absent from refs`);
|
|
1389
|
-
}
|
|
1390
|
-
if (seenLodGuids.has(guid)) {
|
|
1391
|
-
throw new Error(`LOD ${lodIndex} mesh ${guid} is duplicated`);
|
|
1392
1131
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
function sceneProduct(input, components) {
|
|
1135
|
+
if (input.asset.kind !== "scene") throw new TypeError("expected SceneAsset");
|
|
1136
|
+
const externalized = externalizeSceneAsset(input.asset, (componentName) => {
|
|
1137
|
+
const schema = components.get(componentName);
|
|
1138
|
+
if (schema === void 0) {
|
|
1139
|
+
throw new TypeError(
|
|
1140
|
+
`ScriptablePack scene component ${componentName} is missing from sceneComponents`
|
|
1396
1141
|
);
|
|
1397
1142
|
}
|
|
1398
|
-
|
|
1399
|
-
previousCoverage = lod.screenCoverage;
|
|
1400
|
-
return { meshRef, screenCoverage: lod.screenCoverage };
|
|
1143
|
+
return schema;
|
|
1401
1144
|
});
|
|
1402
|
-
if (
|
|
1403
|
-
|
|
1145
|
+
if (!externalized.ok) throw new TypeError(`scene field ${externalized.error.field} is invalid`);
|
|
1146
|
+
return {
|
|
1147
|
+
payload: { kind: "scene", ...externalized.value.payload },
|
|
1148
|
+
refs: externalized.value.refs,
|
|
1149
|
+
artifacts: {}
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
function containsAssetGuid(value) {
|
|
1153
|
+
if (typeof value === "string") return AssetGuid$1.parse(value).ok;
|
|
1154
|
+
if (Array.isArray(value)) return value.some(containsAssetGuid);
|
|
1155
|
+
if (value !== null && typeof value === "object") {
|
|
1156
|
+
return Object.values(value).some(containsAssetGuid);
|
|
1157
|
+
}
|
|
1158
|
+
return false;
|
|
1159
|
+
}
|
|
1160
|
+
function preExternalizedSceneProduct(input) {
|
|
1161
|
+
if (input.asset.kind !== "scene") throw new TypeError("expected SceneAsset");
|
|
1162
|
+
if (containsAssetGuid(input.asset)) {
|
|
1163
|
+
throw new TypeError("direct scene payload must use explicit refs with runtime indices");
|
|
1404
1164
|
}
|
|
1405
1165
|
return {
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
...payload.morphTargets === void 0 ? {} : { morphTargets: jsonValue(payload.morphTargets) },
|
|
1410
|
-
...payload.morphWeights === void 0 ? {} : { morphWeights: jsonValue(payload.morphWeights) },
|
|
1411
|
-
...lods === void 0 ? {} : { lods },
|
|
1412
|
-
...payload.lodHysteresis === void 0 ? {} : { lodHysteresis: payload.lodHysteresis }
|
|
1166
|
+
payload: { ...input.asset, kind: "scene" },
|
|
1167
|
+
refs: [],
|
|
1168
|
+
artifacts: {}
|
|
1413
1169
|
};
|
|
1414
1170
|
}
|
|
1415
|
-
function
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
)
|
|
1171
|
+
function createSafeProducer(kind, version, product) {
|
|
1172
|
+
return {
|
|
1173
|
+
kind,
|
|
1174
|
+
version,
|
|
1175
|
+
produce(input) {
|
|
1176
|
+
try {
|
|
1177
|
+
return ok(product(input));
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
return err(producerError(input, error));
|
|
1180
|
+
}
|
|
1423
1181
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
function jsonArtifact(value) {
|
|
1185
|
+
return {
|
|
1186
|
+
mediaType: "application/json",
|
|
1187
|
+
assetCodec: { name: "forgeax-json", version: "1" },
|
|
1188
|
+
bytes: new TextEncoder().encode(JSON.stringify(value))
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
function ref(guid, fieldName, arrayIndex) {
|
|
1192
|
+
return {
|
|
1193
|
+
guid: formatGuid(guid),
|
|
1194
|
+
sourceField: { fieldName, ...arrayIndex === void 0 ? {} : { arrayIndex } }
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
function bytes(value) {
|
|
1198
|
+
return Uint8Array.from(value);
|
|
1199
|
+
}
|
|
1200
|
+
function textureProduct(input) {
|
|
1201
|
+
if (input.asset.kind !== "texture") throw new TypeError("expected TextureAsset");
|
|
1202
|
+
const texture = input.asset;
|
|
1203
|
+
const layout = deriveTextureLayout({
|
|
1204
|
+
shape: texture.shape,
|
|
1205
|
+
format: texture.format,
|
|
1206
|
+
mips: texture.mips,
|
|
1207
|
+
actualByteLength: texture.data.byteLength,
|
|
1208
|
+
order: "mip-major,image-major,row-major"
|
|
1209
|
+
});
|
|
1210
|
+
if (!layout.ok) {
|
|
1211
|
+
const expectedBytes = layout.error.code === "texture-packing-invalid" ? layout.error.detail.expectedBytes : texture.data.byteLength;
|
|
1212
|
+
const actualBytes = layout.error.code === "texture-packing-invalid" ? layout.error.detail.actualBytes : texture.data.byteLength;
|
|
1213
|
+
throw new TypeError(
|
|
1214
|
+
`texture data is not canonical: expected ${expectedBytes} bytes, got ${actualBytes}`
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
return {
|
|
1218
|
+
payload: texture,
|
|
1219
|
+
refs: [],
|
|
1220
|
+
artifacts: {
|
|
1221
|
+
body: {
|
|
1222
|
+
mediaType: texture.format === "r8unorm" ? "application/x-forgeax-r8" : `application/x-forgeax-${texture.format}`,
|
|
1223
|
+
assetCodec: { name: texture.format, version: "1" },
|
|
1224
|
+
bytes: bytes(texture.data)
|
|
1225
|
+
}
|
|
1428
1226
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
function ordinaryPodProduct(input) {
|
|
1230
|
+
const asset = input.asset;
|
|
1231
|
+
switch (asset.kind) {
|
|
1232
|
+
case "texture":
|
|
1233
|
+
return textureProduct(input);
|
|
1234
|
+
case "equirect": {
|
|
1235
|
+
const equirect = asset;
|
|
1236
|
+
return {
|
|
1237
|
+
payload: equirect,
|
|
1238
|
+
refs: [],
|
|
1239
|
+
artifacts: {
|
|
1240
|
+
body: {
|
|
1241
|
+
mediaType: "image/raw",
|
|
1242
|
+
assetCodec: { name: "raw-image", version: "1" },
|
|
1243
|
+
bytes: bytes(equirect.data)
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1439
1247
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
return
|
|
1443
|
-
failure(sourceKey, "a non-negative safe vertex cardinality", `vertexCount=${vertexCount}`)
|
|
1444
|
-
);
|
|
1248
|
+
case "sampler": {
|
|
1249
|
+
const sampler = asset;
|
|
1250
|
+
return { payload: sampler, refs: [], artifacts: { body: jsonArtifact(sampler) } };
|
|
1445
1251
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1252
|
+
case "font": {
|
|
1253
|
+
const font = asset;
|
|
1254
|
+
const atlas = ref(font.atlas, "atlas");
|
|
1255
|
+
const sampler = ref(font.sampler, "sampler");
|
|
1256
|
+
return {
|
|
1257
|
+
payload: {
|
|
1258
|
+
kind: font.kind,
|
|
1259
|
+
glyphs: font.glyphs,
|
|
1260
|
+
common: font.common,
|
|
1261
|
+
atlasGuid: atlas.guid,
|
|
1262
|
+
samplerGuid: sampler.guid
|
|
1263
|
+
},
|
|
1264
|
+
refs: [atlas, sampler],
|
|
1265
|
+
artifacts: { body: jsonArtifact(font) }
|
|
1266
|
+
};
|
|
1454
1267
|
}
|
|
1455
|
-
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1268
|
+
case "render-pipeline": {
|
|
1269
|
+
const pipeline = asset;
|
|
1270
|
+
return {
|
|
1271
|
+
payload: pipeline,
|
|
1272
|
+
refs: [],
|
|
1273
|
+
artifacts: { body: jsonArtifact(pipeline) }
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
case "tileset": {
|
|
1277
|
+
const tileset = asset;
|
|
1278
|
+
const refs = tileset.atlases.map((atlas, index) => {
|
|
1279
|
+
const parsed = AssetGuid$1.parse(atlas);
|
|
1280
|
+
if (!parsed.ok) throw parsed.error;
|
|
1281
|
+
return ref(parsed.value, "atlases", index);
|
|
1282
|
+
});
|
|
1283
|
+
return {
|
|
1284
|
+
payload: { ...tileset, atlases: refs.map((_entry, index) => index) },
|
|
1285
|
+
refs,
|
|
1286
|
+
artifacts: { body: jsonArtifact(tileset) }
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
case "video": {
|
|
1290
|
+
const video = asset;
|
|
1291
|
+
try {
|
|
1292
|
+
const url = new URL(video.url);
|
|
1293
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
1294
|
+
throw new Error("unsupported URL scheme");
|
|
1295
|
+
} catch (error) {
|
|
1296
|
+
throw new TypeError(
|
|
1297
|
+
`video URL is invalid: ${error instanceof Error ? error.message : String(error)}`
|
|
1465
1298
|
);
|
|
1466
1299
|
}
|
|
1300
|
+
return { payload: video, refs: [], artifacts: {} };
|
|
1467
1301
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
for (let component = 0; component < components; component++) {
|
|
1476
|
-
const sourceIndex = vertex * components + component;
|
|
1477
|
-
const targetOffset = vertex * projection.arrayStride + attribute.offset + component * (attribute.format === "uint16x4" ? 2 : 4);
|
|
1478
|
-
if (attribute.format === "uint16x4") {
|
|
1479
|
-
interleavedView.setUint16(targetOffset, value[sourceIndex] ?? 0, true);
|
|
1480
|
-
} else {
|
|
1481
|
-
interleavedView.setFloat32(
|
|
1482
|
-
targetOffset,
|
|
1483
|
-
value[sourceIndex] ?? 0,
|
|
1484
|
-
true
|
|
1485
|
-
);
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1302
|
+
case "skeleton": {
|
|
1303
|
+
const skeleton = asset;
|
|
1304
|
+
return {
|
|
1305
|
+
payload: skeleton,
|
|
1306
|
+
refs: [],
|
|
1307
|
+
artifacts: { body: jsonArtifact(skeleton) }
|
|
1308
|
+
};
|
|
1489
1309
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
return
|
|
1310
|
+
case "skin": {
|
|
1311
|
+
const skin = asset;
|
|
1312
|
+
const skeletonGuid = AssetGuid$1.parse(skin.skeletonGuid);
|
|
1313
|
+
if (!skeletonGuid.ok) throw skeletonGuid.error;
|
|
1314
|
+
return {
|
|
1315
|
+
payload: skin,
|
|
1316
|
+
refs: [ref(skeletonGuid.value, "skeletonGuid")],
|
|
1317
|
+
artifacts: { body: jsonArtifact(skin) }
|
|
1318
|
+
};
|
|
1495
1319
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
return
|
|
1320
|
+
case "animation-clip":
|
|
1321
|
+
return {
|
|
1322
|
+
payload: asset,
|
|
1323
|
+
refs: [],
|
|
1324
|
+
artifacts: { body: jsonArtifact(asset) }
|
|
1325
|
+
};
|
|
1326
|
+
case "animation-graph": {
|
|
1327
|
+
const graph = asset;
|
|
1328
|
+
const refs = [];
|
|
1329
|
+
const nodes = graph.nodes.map((node, index) => {
|
|
1330
|
+
if (node.type !== "clip") return node;
|
|
1331
|
+
const parsed = AssetGuid$1.parse(node.clip);
|
|
1332
|
+
if (!parsed.ok) throw parsed.error;
|
|
1333
|
+
const referenceIndex = refs.push(ref(parsed.value, "nodes", index)) - 1;
|
|
1334
|
+
return { ...node, clip: referenceIndex };
|
|
1335
|
+
});
|
|
1336
|
+
return {
|
|
1337
|
+
payload: { kind: graph.kind, root: graph.root, nodes },
|
|
1338
|
+
refs,
|
|
1339
|
+
artifacts: { body: jsonArtifact(graph) }
|
|
1340
|
+
};
|
|
1513
1341
|
}
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1342
|
+
case "audio": {
|
|
1343
|
+
const audio = asset;
|
|
1344
|
+
return {
|
|
1345
|
+
payload: {
|
|
1346
|
+
kind: audio.kind,
|
|
1347
|
+
sourceKey: audio.sourceKey,
|
|
1348
|
+
mediaType: audio.mediaType,
|
|
1349
|
+
bytes: audio.bytes.slice()
|
|
1350
|
+
},
|
|
1351
|
+
refs: [],
|
|
1352
|
+
artifacts: {
|
|
1353
|
+
source: {
|
|
1354
|
+
mediaType: audio.mediaType,
|
|
1355
|
+
assetCodec: { name: "browser-audio", version: "1" },
|
|
1356
|
+
bytes: audio.bytes.slice()
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1522
1360
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1361
|
+
case "particle-effect": {
|
|
1362
|
+
const effect = asset;
|
|
1363
|
+
const cooked = particleProgramArtifact(effect);
|
|
1364
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1365
|
+
for (const emitter of cooked.program.emitters) {
|
|
1366
|
+
for (const renderer of emitter.renderers) {
|
|
1367
|
+
if (!("material" in renderer) || typeof renderer.material !== "string") {
|
|
1368
|
+
throw new Error(`Particle emitter '${emitter.id}' requires a renderer Material GUID`);
|
|
1369
|
+
}
|
|
1370
|
+
refs.add(formatGuid(renderer.material));
|
|
1371
|
+
if ("kind" in renderer && renderer.kind === "mesh") {
|
|
1372
|
+
if (!("mesh" in renderer) || typeof renderer.mesh !== "string") {
|
|
1373
|
+
throw new Error(`Particle emitter '${emitter.id}' requires a renderer Mesh GUID`);
|
|
1374
|
+
}
|
|
1375
|
+
refs.add(formatGuid(renderer.mesh));
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
return {
|
|
1380
|
+
payload: { ...effect, programFingerprint: cooked.fingerprint, program: cooked.program },
|
|
1381
|
+
refs: [...refs].sort().map((guid) => ({ guid })),
|
|
1382
|
+
artifacts: {
|
|
1383
|
+
"particle-effect/program.json": {
|
|
1384
|
+
mediaType: "application/json",
|
|
1385
|
+
assetCodec: { name: "forgeax-vfx-program", version: effect.program.format },
|
|
1386
|
+
bytes: cooked.bytes
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1542
1390
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1391
|
+
case "ies-profile": {
|
|
1392
|
+
const profile = asset;
|
|
1393
|
+
return {
|
|
1394
|
+
payload: profile,
|
|
1395
|
+
refs: [],
|
|
1396
|
+
artifacts: {
|
|
1397
|
+
body: {
|
|
1398
|
+
mediaType: "application/octet-stream",
|
|
1399
|
+
assetCodec: { name: "forgeax-ies-profile", version: "1" },
|
|
1400
|
+
bytes: bytes(profile.data)
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1545
1404
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
}
|
|
1551
|
-
versions() {
|
|
1552
|
-
return Object.fromEntries(
|
|
1553
|
-
[...this.producers.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([kind, producer]) => [kind, producer.version])
|
|
1554
|
-
);
|
|
1405
|
+
case "material":
|
|
1406
|
+
case "mesh":
|
|
1407
|
+
case "scene":
|
|
1408
|
+
throw new TypeError(`ordinary producer received already-owned ${asset.kind} asset`);
|
|
1555
1409
|
}
|
|
1556
|
-
};
|
|
1557
|
-
|
|
1558
|
-
// src/scriptable-pack-output-producers.ts
|
|
1559
|
-
function producerError(input, reason) {
|
|
1560
|
-
return new ImportError({
|
|
1561
|
-
code: "import-internal-error",
|
|
1562
|
-
expected: `ScriptablePack ${input.asset.kind} output ${input.sourceKey} to satisfy its domain producer contract`,
|
|
1563
|
-
hint: "fix the generated Asset payload and rebuild the ScriptablePack",
|
|
1564
|
-
detail: { reason: reason instanceof Error ? reason.message : String(reason) }
|
|
1565
|
-
});
|
|
1566
1410
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1411
|
+
var materialAssetOutputProducer = createSafeProducer(
|
|
1412
|
+
"material",
|
|
1413
|
+
"material-pack/2",
|
|
1414
|
+
materialProduct
|
|
1415
|
+
);
|
|
1416
|
+
var meshAssetOutputProducer = createSafeProducer("mesh", "mesh-binary/4", meshProduct);
|
|
1417
|
+
var textureAssetOutputProducer = createSafeProducer(
|
|
1418
|
+
"texture",
|
|
1419
|
+
"texture-pack/1",
|
|
1420
|
+
textureProduct
|
|
1421
|
+
);
|
|
1422
|
+
function createSceneAssetOutputProducer(sceneComponents = []) {
|
|
1423
|
+
const schemas = new Map(
|
|
1424
|
+
sceneComponents.map((component) => [component.name, component.fields])
|
|
1425
|
+
);
|
|
1426
|
+
return createSafeProducer("scene", "scene-pack/3", (input) => sceneProduct(input, schemas));
|
|
1574
1427
|
}
|
|
1575
|
-
function
|
|
1576
|
-
|
|
1577
|
-
if (value !== null && typeof value === "object") {
|
|
1578
|
-
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => `${JSON.stringify(key)}:${canonical(child)}`).join(",")}}`;
|
|
1579
|
-
}
|
|
1580
|
-
return JSON.stringify(value) ?? "null";
|
|
1428
|
+
function createPreExternalizedSceneAssetOutputProducer() {
|
|
1429
|
+
return createSafeProducer("scene", "scene-pack/3", preExternalizedSceneProduct);
|
|
1581
1430
|
}
|
|
1582
|
-
function
|
|
1583
|
-
const
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
)
|
|
1610
|
-
);
|
|
1611
|
-
const parent = material.parent === void 0 ? void 0 : addRef(material.parent, { fieldName: "parent" });
|
|
1612
|
-
const values = {};
|
|
1613
|
-
for (const fieldName of Object.keys(material.values ?? {}).sort()) {
|
|
1614
|
-
const value = material.values?.[fieldName];
|
|
1615
|
-
if (typeof value === "string" && textureFields.has(fieldName)) {
|
|
1616
|
-
values[fieldName] = {
|
|
1617
|
-
texture: addMaterialRef(value, { componentName: "<material>", fieldName })
|
|
1618
|
-
};
|
|
1619
|
-
} else if (value !== null && typeof value === "object" && !Array.isArray(value) && "texture" in value) {
|
|
1620
|
-
const texture = value;
|
|
1621
|
-
values[fieldName] = {
|
|
1622
|
-
...texture,
|
|
1623
|
-
texture: addMaterialRef(texture.texture, { componentName: "<material>", fieldName }),
|
|
1624
|
-
...texture.sampler === void 0 ? {} : {
|
|
1625
|
-
sampler: addMaterialRef(texture.sampler, {
|
|
1626
|
-
componentName: "<material>",
|
|
1627
|
-
fieldName: `${fieldName}.sampler`
|
|
1628
|
-
})
|
|
1629
|
-
}
|
|
1630
|
-
};
|
|
1631
|
-
} else {
|
|
1632
|
-
values[fieldName] = value;
|
|
1633
|
-
}
|
|
1431
|
+
function createStandardAssetOutputProducerRegistry(sceneComponents = []) {
|
|
1432
|
+
const registry = new AssetOutputProducerRegistry();
|
|
1433
|
+
registry.register(materialAssetOutputProducer);
|
|
1434
|
+
registry.register(meshAssetOutputProducer);
|
|
1435
|
+
registry.register(textureAssetOutputProducer);
|
|
1436
|
+
registry.register(createSceneAssetOutputProducer(sceneComponents));
|
|
1437
|
+
for (const kind of [
|
|
1438
|
+
"equirect",
|
|
1439
|
+
"sampler",
|
|
1440
|
+
"font",
|
|
1441
|
+
"render-pipeline",
|
|
1442
|
+
"tileset",
|
|
1443
|
+
"video",
|
|
1444
|
+
"skeleton",
|
|
1445
|
+
"skin",
|
|
1446
|
+
"animation-clip",
|
|
1447
|
+
"animation-graph",
|
|
1448
|
+
"audio",
|
|
1449
|
+
"particle-effect",
|
|
1450
|
+
"ies-profile"
|
|
1451
|
+
]) {
|
|
1452
|
+
registry.register(
|
|
1453
|
+
createSafeProducer(
|
|
1454
|
+
kind,
|
|
1455
|
+
kind === "particle-effect" ? "particle-effect/2" : "ordinary-pod/1",
|
|
1456
|
+
ordinaryPodProduct
|
|
1457
|
+
)
|
|
1458
|
+
);
|
|
1634
1459
|
}
|
|
1635
|
-
|
|
1636
|
-
return {
|
|
1637
|
-
payload: {
|
|
1638
|
-
...materialShape,
|
|
1639
|
-
...parent === void 0 ? {} : { parent },
|
|
1640
|
-
...material.values === void 0 ? {} : { values }
|
|
1641
|
-
},
|
|
1642
|
-
refs,
|
|
1643
|
-
artifacts: {}
|
|
1644
|
-
};
|
|
1460
|
+
return registry;
|
|
1645
1461
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
for (let slotIndex = 0; slotIndex < mesh.materialSlots.length; slotIndex++) {
|
|
1651
|
-
const defaultMaterial = mesh.materialSlots[slotIndex]?.defaultMaterial;
|
|
1652
|
-
if (defaultMaterial === void 0) continue;
|
|
1653
|
-
refs.push({
|
|
1654
|
-
guid: formatGuid(defaultMaterial),
|
|
1655
|
-
sourceField: { fieldName: "materialSlots", arrayIndex: slotIndex }
|
|
1656
|
-
});
|
|
1657
|
-
}
|
|
1658
|
-
const seenRefs = new Set(refs.map((reference) => reference.guid.toLowerCase()));
|
|
1659
|
-
for (const [lodIndex, lod] of (mesh.lods ?? []).entries()) {
|
|
1660
|
-
const guid = formatGuid(lod.mesh);
|
|
1661
|
-
if (seenRefs.has(guid.toLowerCase())) continue;
|
|
1662
|
-
seenRefs.add(guid.toLowerCase());
|
|
1663
|
-
refs.push({ guid, sourceField: { fieldName: "lods", arrayIndex: lodIndex } });
|
|
1664
|
-
}
|
|
1665
|
-
return {
|
|
1666
|
-
payload: mesh,
|
|
1667
|
-
refs,
|
|
1668
|
-
artifacts: {
|
|
1669
|
-
body: {
|
|
1670
|
-
mediaType: "application/x-forgeax-mesh",
|
|
1671
|
-
assetCodec: { name: "mesh-binary", version: "4" },
|
|
1672
|
-
bytes: (() => {
|
|
1673
|
-
const packed = packMeshBinV4(
|
|
1674
|
-
mesh,
|
|
1675
|
-
input.sourceKey,
|
|
1676
|
-
refs.map((reference) => reference.guid)
|
|
1677
|
-
);
|
|
1678
|
-
if (!packed.ok) {
|
|
1679
|
-
throw packed.error;
|
|
1680
|
-
}
|
|
1681
|
-
return packed.value;
|
|
1682
|
-
})()
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
};
|
|
1462
|
+
|
|
1463
|
+
// src/scriptable-pack-build.ts
|
|
1464
|
+
function record(value) {
|
|
1465
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1686
1466
|
}
|
|
1687
|
-
function
|
|
1688
|
-
|
|
1689
|
-
const externalized = externalizeSceneAsset(input.asset, (componentName) => {
|
|
1690
|
-
const schema = components.get(componentName);
|
|
1691
|
-
if (schema === void 0) {
|
|
1692
|
-
throw new TypeError(
|
|
1693
|
-
`ScriptablePack scene component ${componentName} is missing from sceneComponents`
|
|
1694
|
-
);
|
|
1695
|
-
}
|
|
1696
|
-
return schema;
|
|
1697
|
-
});
|
|
1698
|
-
if (!externalized.ok) throw new TypeError(`scene field ${externalized.error.field} is invalid`);
|
|
1699
|
-
return {
|
|
1700
|
-
payload: { kind: "scene", ...externalized.value.payload },
|
|
1701
|
-
refs: externalized.value.refs,
|
|
1702
|
-
artifacts: {}
|
|
1703
|
-
};
|
|
1467
|
+
function isAsset(value) {
|
|
1468
|
+
return record(value) && typeof value.kind === "string" && isScriptablePackAssetKind(value.kind);
|
|
1704
1469
|
}
|
|
1705
|
-
function
|
|
1706
|
-
|
|
1707
|
-
|
|
1470
|
+
function needsMaterialCook(asset) {
|
|
1471
|
+
return asset.kind === "material" && Array.isArray(asset.passes) && !isEngineMaterial(asset);
|
|
1472
|
+
}
|
|
1473
|
+
function clone(value) {
|
|
1474
|
+
return structuredClone(value);
|
|
1475
|
+
}
|
|
1476
|
+
function stable(value) {
|
|
1477
|
+
if (value instanceof Uint8Array) return JSON.stringify(Array.from(value));
|
|
1478
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
1708
1479
|
if (value !== null && typeof value === "object") {
|
|
1709
|
-
|
|
1480
|
+
const object = value;
|
|
1481
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stable(object[key])}`).join(",")}}`;
|
|
1710
1482
|
}
|
|
1711
|
-
return
|
|
1483
|
+
return JSON.stringify(value) ?? "null";
|
|
1712
1484
|
}
|
|
1713
|
-
function
|
|
1714
|
-
|
|
1715
|
-
if (
|
|
1716
|
-
|
|
1717
|
-
}
|
|
1718
|
-
return {
|
|
1719
|
-
payload: { ...input.asset, kind: "scene" },
|
|
1720
|
-
refs: [],
|
|
1721
|
-
artifacts: {}
|
|
1722
|
-
};
|
|
1485
|
+
async function digest(value) {
|
|
1486
|
+
const crypto = globalThis.crypto?.subtle;
|
|
1487
|
+
if (crypto === void 0) throw new Error("Web Crypto API is required for Pack fingerprints");
|
|
1488
|
+
const bytes2 = await crypto.digest("SHA-256", new TextEncoder().encode(stable(value)));
|
|
1489
|
+
return `sha256:${Array.from(new Uint8Array(bytes2), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
1723
1490
|
}
|
|
1724
|
-
function
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
return err(
|
|
1491
|
+
function observedReader(source) {
|
|
1492
|
+
const reads = /* @__PURE__ */ new Map();
|
|
1493
|
+
const reader = {
|
|
1494
|
+
async readByGuid(guid) {
|
|
1495
|
+
const key = AssetGuid.format(guid).toLowerCase();
|
|
1496
|
+
const cached = reads.get(key);
|
|
1497
|
+
if (cached !== void 0) return ok(clone(cached.asset));
|
|
1498
|
+
if (source === void 0) {
|
|
1499
|
+
return err(
|
|
1500
|
+
new AssetError({
|
|
1501
|
+
code: "asset-not-found",
|
|
1502
|
+
expected: `a published Asset snapshot for content dependency ${key}`,
|
|
1503
|
+
hint: "publish the dependency or remove the content read from the Pack build",
|
|
1504
|
+
detail: { sourcePath: key }
|
|
1505
|
+
})
|
|
1506
|
+
);
|
|
1733
1507
|
}
|
|
1508
|
+
const result = await source.readByGuid(guid);
|
|
1509
|
+
if (!result.ok) return result;
|
|
1510
|
+
const asset = clone(result.value.asset);
|
|
1511
|
+
reads.set(key, {
|
|
1512
|
+
guid: key,
|
|
1513
|
+
asset,
|
|
1514
|
+
generation: result.value.generation,
|
|
1515
|
+
digest: result.value.digest
|
|
1516
|
+
});
|
|
1517
|
+
return ok(clone(asset));
|
|
1734
1518
|
}
|
|
1735
1519
|
};
|
|
1520
|
+
return { reader, reads };
|
|
1736
1521
|
}
|
|
1737
|
-
function
|
|
1522
|
+
function buildContext(packageId, values, reader) {
|
|
1523
|
+
if (values === void 0) return { packageId, readByGuid: reader.readByGuid };
|
|
1524
|
+
return { packageId, values, readByGuid: reader.readByGuid };
|
|
1525
|
+
}
|
|
1526
|
+
function sourceKeyFailure(sourcePath, sourceKey) {
|
|
1738
1527
|
return {
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1528
|
+
code: "pack-source-key-invalid",
|
|
1529
|
+
expected: "a sourceKey matching the Pack source-key grammar",
|
|
1530
|
+
hint: "return stable lower-case semantic keys instead of paths or output indexes",
|
|
1531
|
+
detail: { sourcePath, sourceKey }
|
|
1742
1532
|
};
|
|
1743
1533
|
}
|
|
1744
|
-
function
|
|
1534
|
+
function outputValueError(sourcePath, sourceKey, expected, actual) {
|
|
1745
1535
|
return {
|
|
1746
|
-
|
|
1747
|
-
|
|
1536
|
+
code: "pack-parameter-invalid",
|
|
1537
|
+
expected,
|
|
1538
|
+
hint: "repair the build output and rebuild the Pack from a fresh generation",
|
|
1539
|
+
detail: { sourcePath, sourceKey, actual: typeof actual === "string" ? actual : typeof actual }
|
|
1748
1540
|
};
|
|
1749
1541
|
}
|
|
1750
|
-
function
|
|
1751
|
-
return Uint8Array.from(value);
|
|
1752
|
-
}
|
|
1753
|
-
function textureProduct(input) {
|
|
1754
|
-
if (input.asset.kind !== "texture") throw new TypeError("expected TextureAsset");
|
|
1755
|
-
const texture = input.asset;
|
|
1756
|
-
const layout = deriveTextureLayout({
|
|
1757
|
-
shape: texture.shape,
|
|
1758
|
-
format: texture.format,
|
|
1759
|
-
mips: texture.mips,
|
|
1760
|
-
actualByteLength: texture.data.byteLength,
|
|
1761
|
-
order: "mip-major,image-major,row-major"
|
|
1762
|
-
});
|
|
1763
|
-
if (!layout.ok) {
|
|
1764
|
-
const expectedBytes = layout.error.code === "texture-packing-invalid" ? layout.error.detail.expectedBytes : texture.data.byteLength;
|
|
1765
|
-
const actualBytes = layout.error.code === "texture-packing-invalid" ? layout.error.detail.actualBytes : texture.data.byteLength;
|
|
1766
|
-
throw new TypeError(
|
|
1767
|
-
`texture data is not canonical: expected ${expectedBytes} bytes, got ${actualBytes}`
|
|
1768
|
-
);
|
|
1769
|
-
}
|
|
1542
|
+
function referenceError(code, sourcePath, guids) {
|
|
1770
1543
|
return {
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
mediaType: texture.format === "r8unorm" ? "application/x-forgeax-r8" : `application/x-forgeax-${texture.format}`,
|
|
1776
|
-
assetCodec: { name: texture.format, version: "1" },
|
|
1777
|
-
bytes: bytes(texture.data)
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1544
|
+
code,
|
|
1545
|
+
expected: code === "pack-output-reference-missing" ? "every output reference to resolve to a local or published AssetGuid" : "removed output GUIDs to have no incoming references",
|
|
1546
|
+
hint: code === "pack-output-reference-missing" ? "build or publish the referenced Pack before verifying this output" : "migrate incoming references before publishing the topology change",
|
|
1547
|
+
detail: { sourcePath, guids: [...guids].sort() }
|
|
1780
1548
|
};
|
|
1781
1549
|
}
|
|
1782
|
-
function
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1550
|
+
function productError(sourcePath, sourceKey, producer, product) {
|
|
1551
|
+
if (!record(product)) {
|
|
1552
|
+
return outputValueError(
|
|
1553
|
+
sourcePath,
|
|
1554
|
+
sourceKey,
|
|
1555
|
+
`producer ${producer.kind} to return an asset product object`,
|
|
1556
|
+
product
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
if (!Array.isArray(product.refs)) {
|
|
1560
|
+
return outputValueError(
|
|
1561
|
+
sourcePath,
|
|
1562
|
+
sourceKey,
|
|
1563
|
+
`producer ${producer.kind} to return a refs array`,
|
|
1564
|
+
product.refs
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
if (!record(product.artifacts)) {
|
|
1568
|
+
return outputValueError(
|
|
1569
|
+
sourcePath,
|
|
1570
|
+
sourceKey,
|
|
1571
|
+
`producer ${producer.kind} to return an artifacts object`,
|
|
1572
|
+
product.artifacts
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
const payload = product.payload;
|
|
1576
|
+
if (!record(payload) || payload.kind !== producer.kind) {
|
|
1577
|
+
return outputValueError(
|
|
1578
|
+
sourcePath,
|
|
1579
|
+
sourceKey,
|
|
1580
|
+
`producer ${producer.kind} to return a matching payload kind`,
|
|
1581
|
+
payload
|
|
1582
|
+
);
|
|
1583
|
+
}
|
|
1584
|
+
for (const ref2 of product.refs) {
|
|
1585
|
+
if (!record(ref2) || typeof ref2.guid !== "string" || !AssetGuid.parse(ref2.guid).ok) {
|
|
1586
|
+
return outputValueError(
|
|
1587
|
+
sourcePath,
|
|
1588
|
+
sourceKey,
|
|
1589
|
+
"producer refs to contain valid AssetGuid values",
|
|
1590
|
+
ref2
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
for (const [key, artifact] of Object.entries(product.artifacts)) {
|
|
1595
|
+
if (key.length === 0 || key.startsWith("/") || key.includes("..") || key.includes("\\") || !record(artifact) || typeof artifact.mediaType !== "string" || !(artifact.bytes instanceof Uint8Array)) {
|
|
1596
|
+
return outputValueError(
|
|
1597
|
+
sourcePath,
|
|
1598
|
+
sourceKey,
|
|
1599
|
+
"asset-local artifacts with safe keys and bytes",
|
|
1600
|
+
key
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return void 0;
|
|
1605
|
+
}
|
|
1606
|
+
function errorCode(value) {
|
|
1607
|
+
return record(value) && typeof value.code === "string" ? value.code : void 0;
|
|
1608
|
+
}
|
|
1609
|
+
function normalizedGuidSet(value) {
|
|
1610
|
+
return value === void 0 ? void 0 : new Set([...value].map((guid) => guid.toLowerCase()));
|
|
1611
|
+
}
|
|
1612
|
+
async function buildScriptablePack(options) {
|
|
1613
|
+
const subjectPackageId = options.subjectPackageId ?? options.definition.packageId;
|
|
1614
|
+
const availableGuids = normalizedGuidSet(options.availableGuids);
|
|
1615
|
+
const observed = observedReader(options.assetSource);
|
|
1616
|
+
let effectiveValues;
|
|
1617
|
+
if ("parameters" in options.definition) {
|
|
1618
|
+
const resolved = resolvePackParameterValues(
|
|
1619
|
+
options.definition,
|
|
1620
|
+
options.values ?? {},
|
|
1621
|
+
options.inheritedValues
|
|
1622
|
+
);
|
|
1623
|
+
if (!resolved.ok) return resolved;
|
|
1624
|
+
effectiveValues = resolved.value;
|
|
1625
|
+
} else if (options.values !== void 0 && Object.keys(options.values).length > 0) {
|
|
1626
|
+
return err(
|
|
1627
|
+
outputValueError(
|
|
1628
|
+
options.sourcePath,
|
|
1629
|
+
"$.values",
|
|
1630
|
+
"zero-parameter Packs to omit values and instance capabilities",
|
|
1631
|
+
options.values
|
|
1632
|
+
)
|
|
1633
|
+
);
|
|
1634
|
+
} else if (options.subjectPackageId !== void 0 && PackageId.format(options.subjectPackageId).toLowerCase() !== PackageId.format(options.definition.packageId).toLowerCase()) {
|
|
1635
|
+
return err({
|
|
1636
|
+
code: "pack-parent-has-no-parameters",
|
|
1637
|
+
expected: "a ScriptablePack source with parameters for an independent instance packageId",
|
|
1638
|
+
hint: "use clone for a zero-parameter Pack instead of building it as an instance",
|
|
1639
|
+
detail: {
|
|
1640
|
+
sourcePath: options.sourcePath,
|
|
1641
|
+
rootPackageId: PackageId.format(options.definition.packageId),
|
|
1642
|
+
subjectPackageId: PackageId.format(options.subjectPackageId)
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
let built;
|
|
1647
|
+
try {
|
|
1648
|
+
const context = buildContext(subjectPackageId, effectiveValues, observed.reader);
|
|
1649
|
+
built = options.definition.build(context);
|
|
1650
|
+
built = await built;
|
|
1651
|
+
} catch (cause) {
|
|
1652
|
+
return err(
|
|
1653
|
+
new ImportError({
|
|
1654
|
+
code: "import-internal-error",
|
|
1655
|
+
expected: "Pack build to return a structured Result without throwing",
|
|
1656
|
+
hint: "repair the authoring function and return err(...) for expected failures",
|
|
1657
|
+
detail: {
|
|
1658
|
+
reason: `${options.sourcePath}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
1798
1659
|
}
|
|
1799
|
-
}
|
|
1660
|
+
})
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
if (!record(built) || typeof built.ok !== "boolean") {
|
|
1664
|
+
return err(
|
|
1665
|
+
new ImportError({
|
|
1666
|
+
code: "import-internal-error",
|
|
1667
|
+
expected: "Pack build to return a Result object",
|
|
1668
|
+
hint: "return ok(sourceKeyToAsset) or err(structuredError) from the authoring function",
|
|
1669
|
+
detail: { reason: `${options.sourcePath}: malformed build result` }
|
|
1670
|
+
})
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
if (!built.ok) {
|
|
1674
|
+
if (errorCode(built.error) !== void 0) return err(built.error);
|
|
1675
|
+
return err(
|
|
1676
|
+
new ImportError({
|
|
1677
|
+
code: "import-internal-error",
|
|
1678
|
+
expected: "a structured Pack build error",
|
|
1679
|
+
hint: "return an error carrying code, expected, hint and detail",
|
|
1680
|
+
detail: { reason: `${options.sourcePath}: ${String(built.error)}` }
|
|
1681
|
+
})
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
if (!record(built.value)) {
|
|
1685
|
+
return err(
|
|
1686
|
+
outputValueError(
|
|
1687
|
+
options.sourcePath,
|
|
1688
|
+
"$",
|
|
1689
|
+
"build to return a sourceKey-to-Asset object",
|
|
1690
|
+
built.value
|
|
1691
|
+
)
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
const imported = [];
|
|
1695
|
+
const stagedOutputs = [];
|
|
1696
|
+
const localGuids = /* @__PURE__ */ new Set();
|
|
1697
|
+
const materialCookerRegistry = new NativeCookerRegistry();
|
|
1698
|
+
for (const cooker of options.cookers ?? []) materialCookerRegistry.register(cooker);
|
|
1699
|
+
const nativeFingerprints = /* @__PURE__ */ new Map();
|
|
1700
|
+
for (const sourceKey of Object.keys(built.value).sort()) {
|
|
1701
|
+
if (!isValidPackSourceKey(sourceKey))
|
|
1702
|
+
return err(sourceKeyFailure(options.sourcePath, sourceKey));
|
|
1703
|
+
const asset = built.value[sourceKey];
|
|
1704
|
+
if (!isAsset(asset)) {
|
|
1705
|
+
return err(
|
|
1706
|
+
outputValueError(
|
|
1707
|
+
options.sourcePath,
|
|
1708
|
+
sourceKey,
|
|
1709
|
+
"a concrete Asset with a supported kind",
|
|
1710
|
+
asset
|
|
1711
|
+
)
|
|
1712
|
+
);
|
|
1800
1713
|
}
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1714
|
+
const guid = AssetGuid.format(AssetGuid.derive(subjectPackageId, sourceKey));
|
|
1715
|
+
const normalizedGuid = guid.toLowerCase();
|
|
1716
|
+
if (localGuids.has(normalizedGuid) || availableGuids?.has(normalizedGuid)) {
|
|
1717
|
+
return err({
|
|
1718
|
+
code: "pack-guid-collision",
|
|
1719
|
+
expected: "derived output GUIDs to be unique in the global source index",
|
|
1720
|
+
hint: "change the colliding packageId or repair the source index before publishing",
|
|
1721
|
+
detail: { sourcePath: options.sourcePath, guid }
|
|
1722
|
+
});
|
|
1804
1723
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
},
|
|
1817
|
-
refs: [atlas, sampler],
|
|
1818
|
-
artifacts: { body: jsonArtifact(font) }
|
|
1819
|
-
};
|
|
1724
|
+
localGuids.add(normalizedGuid);
|
|
1725
|
+
const producer = options.outputs.get(asset.kind);
|
|
1726
|
+
if (producer === void 0) {
|
|
1727
|
+
return err(
|
|
1728
|
+
outputValueError(
|
|
1729
|
+
options.sourcePath,
|
|
1730
|
+
sourceKey,
|
|
1731
|
+
`a registered output producer for ${asset.kind}`,
|
|
1732
|
+
asset.kind
|
|
1733
|
+
)
|
|
1734
|
+
);
|
|
1820
1735
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1736
|
+
let produced;
|
|
1737
|
+
try {
|
|
1738
|
+
produced = await producer.produce({ guid, sourceKey, asset });
|
|
1739
|
+
} catch (cause) {
|
|
1740
|
+
return err(
|
|
1741
|
+
new ImportError({
|
|
1742
|
+
code: "import-internal-error",
|
|
1743
|
+
expected: `producer ${producer.kind} to return a structured Result without throwing`,
|
|
1744
|
+
hint: "repair the output producer and return err(...) for expected failures",
|
|
1745
|
+
detail: {
|
|
1746
|
+
reason: `${options.sourcePath}:${sourceKey}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
1747
|
+
}
|
|
1748
|
+
})
|
|
1749
|
+
);
|
|
1828
1750
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1751
|
+
if (!record(produced) || typeof produced.ok !== "boolean") {
|
|
1752
|
+
return err(
|
|
1753
|
+
outputValueError(
|
|
1754
|
+
options.sourcePath,
|
|
1755
|
+
sourceKey,
|
|
1756
|
+
`producer ${producer.kind} to return a Result`,
|
|
1757
|
+
produced
|
|
1758
|
+
)
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
if (!produced.ok) {
|
|
1762
|
+
if (errorCode(produced.error) !== void 0) return err(produced.error);
|
|
1763
|
+
return err(
|
|
1764
|
+
new ImportError({
|
|
1765
|
+
code: "import-internal-error",
|
|
1766
|
+
expected: `producer ${producer.kind} to return a structured error`,
|
|
1767
|
+
hint: "return an error carrying code, expected, hint and detail",
|
|
1768
|
+
detail: { reason: `${options.sourcePath}:${sourceKey}: ${String(produced.error)}` }
|
|
1769
|
+
})
|
|
1770
|
+
);
|
|
1771
|
+
}
|
|
1772
|
+
const invalidProduct2 = productError(options.sourcePath, sourceKey, producer, produced.value);
|
|
1773
|
+
if (invalidProduct2 !== void 0) return err(invalidProduct2);
|
|
1774
|
+
const product2 = produced.value;
|
|
1775
|
+
let payload = product2.payload;
|
|
1776
|
+
let artifacts2 = product2.artifacts;
|
|
1777
|
+
let refs2 = product2.refs;
|
|
1778
|
+
if (needsMaterialCook(asset) && materialCookerRegistry.get("material") !== void 0) {
|
|
1779
|
+
const cooked = await materialCookerRegistry.runDraft("material", {
|
|
1780
|
+
guid,
|
|
1781
|
+
source: asset,
|
|
1782
|
+
sourceKey,
|
|
1783
|
+
sourcePath: options.sourcePath,
|
|
1784
|
+
refs: product2.refs.map((reference) => reference.guid)
|
|
1835
1785
|
});
|
|
1836
|
-
return
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1786
|
+
if (!cooked.ok) return err(cooked.error);
|
|
1787
|
+
if (cooked.value.guid.toLowerCase() !== normalizedGuid) {
|
|
1788
|
+
return err({
|
|
1789
|
+
code: "pack-source-output-invalid",
|
|
1790
|
+
expected: "the authored material cooker to preserve the derived AssetGuid",
|
|
1791
|
+
hint: "repair the native material cooker output GUID and rebuild the Pack",
|
|
1792
|
+
detail: {
|
|
1793
|
+
sourcePath: options.sourcePath,
|
|
1794
|
+
sourceKey,
|
|
1795
|
+
expectedGuid: guid,
|
|
1796
|
+
actualGuid: cooked.value.guid
|
|
1797
|
+
}
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
const cookedAsset = cooked.value.payload;
|
|
1801
|
+
if (!isAsset(cookedAsset) || cookedAsset.kind !== "material") {
|
|
1802
|
+
return err({
|
|
1803
|
+
code: "pack-source-output-invalid",
|
|
1804
|
+
expected: "the authored material cooker to return a material payload",
|
|
1805
|
+
hint: "repair the native material cooker payload and rebuild the Pack",
|
|
1806
|
+
detail: { sourcePath: options.sourcePath, sourceKey }
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
const projected = await materialAssetOutputProducer.produce({
|
|
1810
|
+
guid,
|
|
1811
|
+
sourceKey,
|
|
1812
|
+
asset: cookedAsset
|
|
1813
|
+
});
|
|
1814
|
+
if (!projected.ok) return err(projected.error);
|
|
1815
|
+
const projectedInvalid = productError(
|
|
1816
|
+
options.sourcePath,
|
|
1817
|
+
sourceKey,
|
|
1818
|
+
materialAssetOutputProducer,
|
|
1819
|
+
projected.value
|
|
1820
|
+
);
|
|
1821
|
+
if (projectedInvalid !== void 0) return err(projectedInvalid);
|
|
1822
|
+
payload = projected.value.payload;
|
|
1823
|
+
artifacts2 = {
|
|
1824
|
+
...product2.artifacts,
|
|
1825
|
+
...cooked.value.artifacts,
|
|
1826
|
+
...projected.value.artifacts
|
|
1840
1827
|
};
|
|
1828
|
+
const mergedRefs = [...projected.value.refs];
|
|
1829
|
+
const seenRefs = new Set(mergedRefs.map((reference) => reference.guid.toLowerCase()));
|
|
1830
|
+
for (const cookerRef of cooked.value.refs) {
|
|
1831
|
+
const parsed = AssetGuid.parse(cookerRef);
|
|
1832
|
+
if (!parsed.ok) {
|
|
1833
|
+
return err({
|
|
1834
|
+
code: "pack-source-output-invalid",
|
|
1835
|
+
expected: "the authored material cooker refs to contain valid AssetGuid values",
|
|
1836
|
+
hint: "repair the native material cooker refs and rebuild the Pack",
|
|
1837
|
+
detail: { sourcePath: options.sourcePath, sourceKey, ref: cookerRef }
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
const formatted = AssetGuid.format(parsed.value);
|
|
1841
|
+
const normalizedRef = formatted.toLowerCase();
|
|
1842
|
+
if (seenRefs.has(normalizedRef)) continue;
|
|
1843
|
+
seenRefs.add(normalizedRef);
|
|
1844
|
+
mergedRefs.push({ guid: formatted });
|
|
1845
|
+
}
|
|
1846
|
+
refs2 = mergedRefs;
|
|
1847
|
+
nativeFingerprints.set(sourceKey, cooked.value.inputFingerprint);
|
|
1841
1848
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1849
|
+
imported.push({
|
|
1850
|
+
guid,
|
|
1851
|
+
kind: asset.kind,
|
|
1852
|
+
payload,
|
|
1853
|
+
refs: refs2,
|
|
1854
|
+
artifacts: artifacts2
|
|
1855
|
+
});
|
|
1856
|
+
stagedOutputs.push({
|
|
1857
|
+
guid: AssetGuid.derive(subjectPackageId, sourceKey),
|
|
1858
|
+
sourceKey,
|
|
1859
|
+
asset: clone(asset),
|
|
1860
|
+
digest: await digest(asset)
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
1864
|
+
for (const asset of imported) {
|
|
1865
|
+
for (const ref2 of asset.refs) {
|
|
1866
|
+
const guid = ref2.guid.toLowerCase();
|
|
1867
|
+
if (!localGuids.has(guid)) referenced.add(guid);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
if (options.deferReferenceValidation !== true) {
|
|
1871
|
+
const missing = [...referenced].filter(
|
|
1872
|
+
(guid) => availableGuids !== void 0 && !availableGuids.has(guid)
|
|
1873
|
+
);
|
|
1874
|
+
if (missing.length > 0)
|
|
1875
|
+
return err(referenceError("pack-output-reference-missing", options.sourcePath, missing));
|
|
1876
|
+
}
|
|
1877
|
+
const externalEvidence = [...observed.reads.values()].sort((left, right) => left.guid.localeCompare(right.guid)).map(
|
|
1878
|
+
(read) => ({
|
|
1879
|
+
guid: read.guid,
|
|
1880
|
+
usage: referenced.has(read.guid) ? "both" : "content",
|
|
1881
|
+
generation: read.generation,
|
|
1882
|
+
digest: read.digest
|
|
1883
|
+
})
|
|
1884
|
+
);
|
|
1885
|
+
const authoredInputFingerprint = await digest({
|
|
1886
|
+
packageId: PackageId.format(subjectPackageId),
|
|
1887
|
+
sourcePath: options.sourcePath,
|
|
1888
|
+
sourceClosure: options.sourceClosure ?? [],
|
|
1889
|
+
values: effectiveValues,
|
|
1890
|
+
externalEvidence: externalEvidence.map(({ guid, usage, digest: evidenceDigest }) => ({
|
|
1891
|
+
guid,
|
|
1892
|
+
usage,
|
|
1893
|
+
digest: evidenceDigest
|
|
1894
|
+
})),
|
|
1895
|
+
authoringContractVersion: options.authoringContractVersion ?? "scriptable-pack/1",
|
|
1896
|
+
producerVersions: options.outputs.versions()
|
|
1897
|
+
});
|
|
1898
|
+
const inputFingerprint = nativeFingerprints.size === 0 ? authoredInputFingerprint : await digest({
|
|
1899
|
+
sourceRevision: authoredInputFingerprint,
|
|
1900
|
+
nativeCookers: [...nativeFingerprints.entries()].sort(
|
|
1901
|
+
([left], [right]) => left.localeCompare(right)
|
|
1902
|
+
)
|
|
1903
|
+
});
|
|
1904
|
+
const refs = imported.flatMap((asset) => asset.refs);
|
|
1905
|
+
const artifacts = Object.fromEntries(
|
|
1906
|
+
imported.flatMap(
|
|
1907
|
+
(asset) => Object.entries(asset.artifacts).map(([key, artifact]) => [`${asset.guid}/${key}`, artifact])
|
|
1908
|
+
)
|
|
1909
|
+
);
|
|
1910
|
+
const product = createImportProduct({
|
|
1911
|
+
assets: imported,
|
|
1912
|
+
sourceDependencies: (options.sourceClosure ?? []).map((entry) => entry.path),
|
|
1913
|
+
refs,
|
|
1914
|
+
artifacts,
|
|
1915
|
+
receipts: imported.map((asset) => ({
|
|
1916
|
+
guid: asset.guid,
|
|
1917
|
+
origin: "authoredPack",
|
|
1918
|
+
status: "succeeded",
|
|
1919
|
+
inputFingerprint
|
|
1920
|
+
})),
|
|
1921
|
+
diagnostics: [],
|
|
1922
|
+
sourceRevision: inputFingerprint,
|
|
1923
|
+
sourceKey: options.sourcePath
|
|
1924
|
+
});
|
|
1925
|
+
if (!product.ok) return err(product.error);
|
|
1926
|
+
return ok({
|
|
1927
|
+
product: product.value,
|
|
1928
|
+
stagedOutputs,
|
|
1929
|
+
externalEvidence,
|
|
1930
|
+
inputFingerprint,
|
|
1931
|
+
...options.publication === void 0 ? {} : { publication: options.publication }
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
function stagedSource(staged, fallback) {
|
|
1935
|
+
return {
|
|
1936
|
+
async readByGuid(guid) {
|
|
1937
|
+
const key = AssetGuid.format(guid).toLowerCase();
|
|
1938
|
+
const local = staged.get(key);
|
|
1939
|
+
if (local !== void 0) {
|
|
1940
|
+
return ok({
|
|
1941
|
+
asset: clone(local.asset),
|
|
1942
|
+
generation: 1,
|
|
1943
|
+
digest: local.digest ?? "sha256:staged"
|
|
1944
|
+
});
|
|
1852
1945
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
}
|
|
1863
|
-
case "skin": {
|
|
1864
|
-
const skin = asset;
|
|
1865
|
-
const skeletonGuid = AssetGuid$1.parse(skin.skeletonGuid);
|
|
1866
|
-
if (!skeletonGuid.ok) throw skeletonGuid.error;
|
|
1867
|
-
return {
|
|
1868
|
-
payload: skin,
|
|
1869
|
-
refs: [ref(skeletonGuid.value, "skeletonGuid")],
|
|
1870
|
-
artifacts: { body: jsonArtifact(skin) }
|
|
1871
|
-
};
|
|
1946
|
+
if (fallback === void 0) {
|
|
1947
|
+
return err({
|
|
1948
|
+
code: "asset-not-found",
|
|
1949
|
+
expected: "a staged or published content dependency",
|
|
1950
|
+
hint: "wait for the dependency subject to materialize",
|
|
1951
|
+
detail: { guid: key }
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
return fallback.readByGuid(guid);
|
|
1872
1955
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
async function buildScriptablePackWorklist(options) {
|
|
1959
|
+
const orderedSubjects = [...options.subjects].sort(
|
|
1960
|
+
(left, right) => left.sourcePath.localeCompare(right.sourcePath)
|
|
1961
|
+
);
|
|
1962
|
+
const pending = new Map(
|
|
1963
|
+
orderedSubjects.map((subject, index) => [`${index}:${subject.sourcePath}`, subject])
|
|
1964
|
+
);
|
|
1965
|
+
const staged = /* @__PURE__ */ new Map();
|
|
1966
|
+
const results = /* @__PURE__ */ new Map();
|
|
1967
|
+
const availableGuids = /* @__PURE__ */ new Set([
|
|
1968
|
+
...normalizedGuidSet(options.availableGuids) ?? [],
|
|
1969
|
+
...BUILTIN_MESH_ASSETS.map((asset) => asset.guid.toLowerCase())
|
|
1970
|
+
]);
|
|
1971
|
+
const maxPasses = options.maxPasses ?? Math.max(1, options.subjects.length + 1);
|
|
1972
|
+
let iterations = 0;
|
|
1973
|
+
for (; iterations < maxPasses && pending.size > 0; iterations += 1) {
|
|
1974
|
+
let progress = false;
|
|
1975
|
+
const waiting = /* @__PURE__ */ new Set();
|
|
1976
|
+
for (const [key, subject] of pending) {
|
|
1977
|
+
const result = await buildScriptablePack({
|
|
1978
|
+
definition: subject.definition,
|
|
1979
|
+
sourcePath: subject.sourcePath,
|
|
1980
|
+
...subject.subjectPackageId === void 0 ? {} : { subjectPackageId: subject.subjectPackageId },
|
|
1981
|
+
...subject.values === void 0 ? {} : { values: subject.values },
|
|
1982
|
+
...subject.inheritedValues === void 0 ? {} : { inheritedValues: subject.inheritedValues },
|
|
1983
|
+
...subject.sourceClosure === void 0 ? {} : { sourceClosure: subject.sourceClosure },
|
|
1984
|
+
outputs: options.outputs,
|
|
1985
|
+
...options.cookers === void 0 ? {} : { cookers: options.cookers },
|
|
1986
|
+
assetSource: stagedSource(staged, options.assetSource),
|
|
1987
|
+
availableGuids: /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]),
|
|
1988
|
+
deferReferenceValidation: true
|
|
1888
1989
|
});
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
refs: [],
|
|
1905
|
-
artifacts: {
|
|
1906
|
-
source: {
|
|
1907
|
-
mediaType: audio.mediaType,
|
|
1908
|
-
assetCodec: { name: "browser-audio", version: "1" },
|
|
1909
|
-
bytes: audio.bytes.slice()
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
};
|
|
1990
|
+
if (result.ok) {
|
|
1991
|
+
results.set(key, result.value);
|
|
1992
|
+
for (const output of result.value.stagedOutputs)
|
|
1993
|
+
staged.set(AssetGuid.format(output.guid).toLowerCase(), output);
|
|
1994
|
+
pending.delete(key);
|
|
1995
|
+
progress = true;
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
if (errorCode(result.error) === "asset-not-found") {
|
|
1999
|
+
const detail = record(result.error) && record(result.error.detail) ? result.error.detail : void 0;
|
|
2000
|
+
const guid = detail !== void 0 && typeof detail.guid === "string" ? detail.guid : void 0;
|
|
2001
|
+
if (guid !== void 0) waiting.add(guid.toLowerCase());
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
2004
|
+
return result;
|
|
1913
2005
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
bytes: cooked.bytes
|
|
1925
|
-
}
|
|
2006
|
+
if (pending.size === 0) break;
|
|
2007
|
+
if (!progress) {
|
|
2008
|
+
return err({
|
|
2009
|
+
code: "pack-content-dependency-stalled",
|
|
2010
|
+
expected: "the content dependency worklist to make progress",
|
|
2011
|
+
hint: "inspect waitingGuids and repair the missing output or content-read cycle, then rebuild",
|
|
2012
|
+
detail: {
|
|
2013
|
+
waitingGuids: [...waiting].sort(),
|
|
2014
|
+
pendingSubjects: [...pending.values()].map((subject) => subject.sourcePath).sort(),
|
|
2015
|
+
iterations: iterations + 1
|
|
1926
2016
|
}
|
|
1927
|
-
};
|
|
2017
|
+
});
|
|
1928
2018
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
2019
|
+
}
|
|
2020
|
+
if (pending.size > 0) {
|
|
2021
|
+
return err({
|
|
2022
|
+
code: "pack-content-dependency-stalled",
|
|
2023
|
+
expected: "the content dependency worklist to finish within its bounded retry budget",
|
|
2024
|
+
hint: "inspect pendingSubjects and waitingGuids, then repair the dependency graph",
|
|
2025
|
+
detail: {
|
|
2026
|
+
pendingSubjects: [...pending.values()].map((subject) => subject.sourcePath).sort(),
|
|
2027
|
+
waitingGuids: [],
|
|
2028
|
+
iterations
|
|
2029
|
+
}
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
const knownGuids = /* @__PURE__ */ new Set([...availableGuids, ...staged.keys()]);
|
|
2033
|
+
const missingReferences = /* @__PURE__ */ new Set();
|
|
2034
|
+
for (const product of results.values()) {
|
|
2035
|
+
const contentReads = new Set(product.externalEvidence.map((read) => read.guid.toLowerCase()));
|
|
2036
|
+
for (const asset of product.product.assets) {
|
|
2037
|
+
for (const reference of asset.refs) {
|
|
2038
|
+
const guid = reference.guid.toLowerCase();
|
|
2039
|
+
if (!knownGuids.has(guid) && !contentReads.has(guid)) missingReferences.add(guid);
|
|
2040
|
+
}
|
|
1942
2041
|
}
|
|
1943
|
-
case "material":
|
|
1944
|
-
case "mesh":
|
|
1945
|
-
case "scene":
|
|
1946
|
-
throw new TypeError(`ordinary producer received already-owned ${asset.kind} asset`);
|
|
1947
2042
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
materialProduct
|
|
1953
|
-
);
|
|
1954
|
-
var meshAssetOutputProducer = createSafeProducer("mesh", "mesh-binary/4", meshProduct);
|
|
1955
|
-
var textureAssetOutputProducer = createSafeProducer(
|
|
1956
|
-
"texture",
|
|
1957
|
-
"texture-pack/1",
|
|
1958
|
-
textureProduct
|
|
1959
|
-
);
|
|
1960
|
-
function createSceneAssetOutputProducer(sceneComponents = []) {
|
|
1961
|
-
const schemas = new Map(
|
|
1962
|
-
sceneComponents.map((component) => [component.name, component.fields])
|
|
1963
|
-
);
|
|
1964
|
-
return createSafeProducer("scene", "scene-pack/3", (input) => sceneProduct(input, schemas));
|
|
1965
|
-
}
|
|
1966
|
-
function createPreExternalizedSceneAssetOutputProducer() {
|
|
1967
|
-
return createSafeProducer("scene", "scene-pack/3", preExternalizedSceneProduct);
|
|
1968
|
-
}
|
|
1969
|
-
function createStandardAssetOutputProducerRegistry(sceneComponents = []) {
|
|
1970
|
-
const registry = new AssetOutputProducerRegistry();
|
|
1971
|
-
registry.register(materialAssetOutputProducer);
|
|
1972
|
-
registry.register(meshAssetOutputProducer);
|
|
1973
|
-
registry.register(textureAssetOutputProducer);
|
|
1974
|
-
registry.register(createSceneAssetOutputProducer(sceneComponents));
|
|
1975
|
-
for (const kind of [
|
|
1976
|
-
"equirect",
|
|
1977
|
-
"sampler",
|
|
1978
|
-
"font",
|
|
1979
|
-
"render-pipeline",
|
|
1980
|
-
"tileset",
|
|
1981
|
-
"video",
|
|
1982
|
-
"skeleton",
|
|
1983
|
-
"skin",
|
|
1984
|
-
"animation-clip",
|
|
1985
|
-
"animation-graph",
|
|
1986
|
-
"audio",
|
|
1987
|
-
"particle-effect",
|
|
1988
|
-
"ies-profile"
|
|
1989
|
-
]) {
|
|
1990
|
-
registry.register(createSafeProducer(kind, "ordinary-pod/1", ordinaryPodProduct));
|
|
2043
|
+
if (missingReferences.size > 0) {
|
|
2044
|
+
return err(
|
|
2045
|
+
referenceError("pack-output-reference-missing", "worklist", [...missingReferences].sort())
|
|
2046
|
+
);
|
|
1991
2047
|
}
|
|
1992
|
-
|
|
2048
|
+
const removed = [...options.incomingRefs?.keys() ?? []].filter(
|
|
2049
|
+
(guid) => !staged.has(guid.toLowerCase())
|
|
2050
|
+
);
|
|
2051
|
+
const referencedRemoved = removed.filter(
|
|
2052
|
+
(guid) => (options.incomingRefs?.get(guid) ?? []).length > 0
|
|
2053
|
+
);
|
|
2054
|
+
if (referencedRemoved.length > 0)
|
|
2055
|
+
return err(referenceError("pack-output-reference-conflict", "worklist", referencedRemoved));
|
|
2056
|
+
return ok({
|
|
2057
|
+
products: [...results.values()].map((result) => result.product),
|
|
2058
|
+
buildProducts: [...results.values()],
|
|
2059
|
+
stagedOutputs: [...staged.values()],
|
|
2060
|
+
iterations: pending.size === 0 && options.subjects.length > 0 ? iterations + 1 : iterations
|
|
2061
|
+
});
|
|
1993
2062
|
}
|
|
1994
2063
|
function parseProducerReadiness(value) {
|
|
1995
2064
|
if (value === void 0 || value === "before-consume" || value === "on-demand") {
|