@aura3d/engine 1.0.2 → 1.0.5
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/README.md +331 -16
- package/dist/animation/AnimationClipEvents.d.ts +57 -0
- package/dist/animation/AnimationClipEvents.d.ts.map +1 -0
- package/dist/animation/AnimationClipEvents.js +171 -0
- package/dist/animation/AnimationClipEvents.js.map +1 -0
- package/dist/animation/AnimationClipRegistry.d.ts +76 -0
- package/dist/animation/AnimationClipRegistry.d.ts.map +1 -0
- package/dist/animation/AnimationClipRegistry.js +130 -0
- package/dist/animation/AnimationClipRegistry.js.map +1 -0
- package/dist/animation/AnimationController.d.ts +168 -0
- package/dist/animation/AnimationController.d.ts.map +1 -0
- package/dist/animation/AnimationController.js +619 -0
- package/dist/animation/AnimationController.js.map +1 -0
- package/dist/animation/HumanoidRetargeting.d.ts +76 -0
- package/dist/animation/HumanoidRetargeting.d.ts.map +1 -0
- package/dist/animation/HumanoidRetargeting.js +331 -0
- package/dist/animation/HumanoidRetargeting.js.map +1 -0
- package/dist/animation/browser-index.d.ts +18 -0
- package/dist/animation/browser-index.d.ts.map +1 -1
- package/dist/animation/browser-index.js +13 -0
- package/dist/animation/browser-index.js.map +1 -1
- package/dist/animation/index.d.ts +16 -1
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/animation/index.js +11 -1
- package/dist/animation/index.js.map +1 -1
- package/dist/animation/threejs-compatibility/AnimationDiagnostics.d.ts.map +1 -1
- package/dist/animation/threejs-compatibility/AnimationDiagnostics.js +3 -5
- package/dist/animation/threejs-compatibility/AnimationDiagnostics.js.map +1 -1
- package/dist/assets/GLTFAnimationRuntime.js +1 -1
- package/dist/assets/GLTFLoader.js +1 -1
- package/dist/aura3d-cli/cli.js +194 -8
- package/dist/aura3d-cli/cli.js.map +1 -1
- package/dist/aura3d-cli/index.d.ts +280 -3
- package/dist/aura3d-cli/index.d.ts.map +1 -1
- package/dist/aura3d-cli/index.js +886 -4
- package/dist/aura3d-cli/index.js.map +1 -1
- package/dist/aura3d-cli/pull-bridge.d.ts +95 -0
- package/dist/aura3d-cli/pull-bridge.d.ts.map +1 -0
- package/dist/aura3d-cli/pull-bridge.js +247 -0
- package/dist/aura3d-cli/pull-bridge.js.map +1 -0
- package/dist/create-aura3d/index.d.ts +1 -1
- package/dist/create-aura3d/index.d.ts.map +1 -1
- package/dist/create-aura3d/index.js +9 -2
- package/dist/create-aura3d/index.js.map +1 -1
- package/dist/editor-runtime/ProjectSerializer.d.ts +74 -1
- package/dist/editor-runtime/ProjectSerializer.d.ts.map +1 -1
- package/dist/editor-runtime/ProjectSerializer.js +123 -6
- package/dist/editor-runtime/ProjectSerializer.js.map +1 -1
- package/dist/editor-runtime/TimelineModel.d.ts +18 -0
- package/dist/editor-runtime/TimelineModel.d.ts.map +1 -1
- package/dist/editor-runtime/TimelineModel.js +67 -3
- package/dist/editor-runtime/TimelineModel.js.map +1 -1
- package/dist/editor-runtime/TimelineRuntimeBridge.d.ts +98 -0
- package/dist/editor-runtime/TimelineRuntimeBridge.d.ts.map +1 -0
- package/dist/editor-runtime/TimelineRuntimeBridge.js +186 -0
- package/dist/editor-runtime/TimelineRuntimeBridge.js.map +1 -0
- package/dist/editor-runtime/index.d.ts +3 -1
- package/dist/editor-runtime/index.d.ts.map +1 -1
- package/dist/editor-runtime/index.js +1 -0
- package/dist/editor-runtime/index.js.map +1 -1
- package/dist/engine/agent-api/AnimationController.d.ts +607 -0
- package/dist/engine/agent-api/AnimationController.d.ts.map +1 -0
- package/dist/engine/agent-api/AnimationController.js +2192 -0
- package/dist/engine/agent-api/AnimationController.js.map +1 -0
- package/dist/engine/agent-api/AssetEvidence.d.ts +88 -0
- package/dist/engine/agent-api/AssetEvidence.d.ts.map +1 -0
- package/dist/engine/agent-api/AssetEvidence.js +157 -0
- package/dist/engine/agent-api/AssetEvidence.js.map +1 -0
- package/dist/engine/agent-api/AuraAppHandle.d.ts +55 -0
- package/dist/engine/agent-api/AuraAppHandle.d.ts.map +1 -0
- package/dist/engine/agent-api/AuraAppHandle.js +15 -0
- package/dist/engine/agent-api/AuraAppHandle.js.map +1 -0
- package/dist/engine/agent-api/AuraVoiceBridge.d.ts +96 -0
- package/dist/engine/agent-api/AuraVoiceBridge.d.ts.map +1 -0
- package/dist/engine/agent-api/AuraVoiceBridge.js +370 -0
- package/dist/engine/agent-api/AuraVoiceBridge.js.map +1 -0
- package/dist/engine/agent-api/CartoonDirector.d.ts +95 -0
- package/dist/engine/agent-api/CartoonDirector.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonDirector.js +342 -0
- package/dist/engine/agent-api/CartoonDirector.js.map +1 -0
- package/dist/engine/agent-api/CartoonPerformance.d.ts +149 -0
- package/dist/engine/agent-api/CartoonPerformance.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonPerformance.js +317 -0
- package/dist/engine/agent-api/CartoonPerformance.js.map +1 -0
- package/dist/engine/agent-api/CartoonRenderQueue.d.ts +132 -0
- package/dist/engine/agent-api/CartoonRenderQueue.d.ts.map +1 -0
- package/dist/engine/agent-api/CartoonRenderQueue.js +385 -0
- package/dist/engine/agent-api/CartoonRenderQueue.js.map +1 -0
- package/dist/engine/agent-api/CharacterAssembly.d.ts +126 -0
- package/dist/engine/agent-api/CharacterAssembly.d.ts.map +1 -0
- package/dist/engine/agent-api/CharacterAssembly.js +280 -0
- package/dist/engine/agent-api/CharacterAssembly.js.map +1 -0
- package/dist/engine/agent-api/DialoguePerformance.d.ts +150 -0
- package/dist/engine/agent-api/DialoguePerformance.d.ts.map +1 -0
- package/dist/engine/agent-api/DialoguePerformance.js +335 -0
- package/dist/engine/agent-api/DialoguePerformance.js.map +1 -0
- package/dist/engine/agent-api/FrameLoop.d.ts +70 -0
- package/dist/engine/agent-api/FrameLoop.d.ts.map +1 -0
- package/dist/engine/agent-api/FrameLoop.js +165 -0
- package/dist/engine/agent-api/FrameLoop.js.map +1 -0
- package/dist/engine/agent-api/GameAssetValidation.d.ts +279 -0
- package/dist/engine/agent-api/GameAssetValidation.d.ts.map +1 -0
- package/dist/engine/agent-api/GameAssetValidation.js +719 -0
- package/dist/engine/agent-api/GameAssetValidation.js.map +1 -0
- package/dist/engine/agent-api/GameEvidence.d.ts +148 -0
- package/dist/engine/agent-api/GameEvidence.d.ts.map +1 -0
- package/dist/engine/agent-api/GameEvidence.js +269 -0
- package/dist/engine/agent-api/GameEvidence.js.map +1 -0
- package/dist/engine/agent-api/GameRuntime.d.ts +931 -0
- package/dist/engine/agent-api/GameRuntime.d.ts.map +1 -0
- package/dist/engine/agent-api/GameRuntime.js +2229 -0
- package/dist/engine/agent-api/GameRuntime.js.map +1 -0
- package/dist/engine/agent-api/GameSceneBridge.d.ts +54 -0
- package/dist/engine/agent-api/GameSceneBridge.d.ts.map +1 -0
- package/dist/engine/agent-api/GameSceneBridge.js +110 -0
- package/dist/engine/agent-api/GameSceneBridge.js.map +1 -0
- package/dist/engine/agent-api/PromptAnimationContract.d.ts +278 -0
- package/dist/engine/agent-api/PromptAnimationContract.d.ts.map +1 -0
- package/dist/engine/agent-api/PromptAnimationContract.js +238 -0
- package/dist/engine/agent-api/PromptAnimationContract.js.map +1 -0
- package/dist/engine/agent-api/PromptAnimationEvidence.d.ts +183 -0
- package/dist/engine/agent-api/PromptAnimationEvidence.d.ts.map +1 -0
- package/dist/engine/agent-api/PromptAnimationEvidence.js +454 -0
- package/dist/engine/agent-api/PromptAnimationEvidence.js.map +1 -0
- package/dist/engine/agent-api/RuntimeNodeHandle.d.ts +100 -0
- package/dist/engine/agent-api/RuntimeNodeHandle.d.ts.map +1 -0
- package/dist/engine/agent-api/RuntimeNodeHandle.js +36 -0
- package/dist/engine/agent-api/RuntimeNodeHandle.js.map +1 -0
- package/dist/engine/agent-api/ShotTimeline.d.ts +179 -0
- package/dist/engine/agent-api/ShotTimeline.d.ts.map +1 -0
- package/dist/engine/agent-api/ShotTimeline.js +264 -0
- package/dist/engine/agent-api/ShotTimeline.js.map +1 -0
- package/dist/engine/agent-api/VisemeController.d.ts +89 -0
- package/dist/engine/agent-api/VisemeController.d.ts.map +1 -0
- package/dist/engine/agent-api/VisemeController.js +207 -0
- package/dist/engine/agent-api/VisemeController.js.map +1 -0
- package/dist/engine/agent-api/game-kits/fighting.d.ts +123 -0
- package/dist/engine/agent-api/game-kits/fighting.d.ts.map +1 -0
- package/dist/engine/agent-api/game-kits/fighting.js +483 -0
- package/dist/engine/agent-api/game-kits/fighting.js.map +1 -0
- package/dist/engine/agent-api/game-kits/index.d.ts +15 -0
- package/dist/engine/agent-api/game-kits/index.d.ts.map +1 -0
- package/dist/engine/agent-api/game-kits/index.js +6 -0
- package/dist/engine/agent-api/game-kits/index.js.map +1 -0
- package/dist/engine/agent-api/humanoid-walk-runtime.d.ts +18 -81
- package/dist/engine/agent-api/humanoid-walk-runtime.d.ts.map +1 -1
- package/dist/engine/agent-api/humanoid-walk-runtime.js +4 -279
- package/dist/engine/agent-api/humanoid-walk-runtime.js.map +1 -1
- package/dist/engine/agent-api/index.d.ts +490 -4
- package/dist/engine/agent-api/index.d.ts.map +1 -1
- package/dist/engine/agent-api/index.js +759 -1802
- package/dist/engine/agent-api/index.js.map +1 -1
- package/dist/engine/agent-api/particle-fountain-runtime.d.ts +5 -80
- package/dist/engine/agent-api/particle-fountain-runtime.d.ts.map +1 -1
- package/dist/engine/agent-api/particle-fountain-runtime.js +7 -291
- package/dist/engine/agent-api/particle-fountain-runtime.js.map +1 -1
- package/dist/engine/agent-api/product-viewer-runtime.d.ts +17 -107
- package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
- package/dist/engine/agent-api/product-viewer-runtime.js +4 -330
- package/dist/engine/agent-api/product-viewer-runtime.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/physics/CollisionVolumes.d.ts +57 -0
- package/dist/physics/CollisionVolumes.d.ts.map +1 -0
- package/dist/physics/CollisionVolumes.js +159 -0
- package/dist/physics/CollisionVolumes.js.map +1 -0
- package/dist/physics/HitboxWorld.d.ts +229 -0
- package/dist/physics/HitboxWorld.d.ts.map +1 -0
- package/dist/physics/HitboxWorld.js +640 -0
- package/dist/physics/HitboxWorld.js.map +1 -0
- package/dist/physics/KinematicBody.d.ts +157 -0
- package/dist/physics/KinematicBody.d.ts.map +1 -0
- package/dist/physics/KinematicBody.js +405 -0
- package/dist/physics/KinematicBody.js.map +1 -0
- package/dist/physics/KinematicWorld.d.ts +58 -0
- package/dist/physics/KinematicWorld.d.ts.map +1 -0
- package/dist/physics/KinematicWorld.js +246 -0
- package/dist/physics/KinematicWorld.js.map +1 -0
- package/dist/physics/index.d.ts +4 -0
- package/dist/physics/index.d.ts.map +1 -1
- package/dist/physics/index.js +4 -0
- package/dist/physics/index.js.map +1 -1
- package/dist/rendering/ForwardPass.js +2 -2
- package/dist/rendering/ShaderLibrary.js +2 -2
- package/dist/rendering/SkinnedLitMaterial.js +3 -3
- package/dist/rendering/SkinnedUnlitMaterial.js +3 -3
- package/dist/scene/Renderable.js +2 -2
- package/dist/scripting/VisualGraph.d.ts +2 -1
- package/dist/scripting/VisualGraph.d.ts.map +1 -1
- package/dist/scripting/VisualGraph.js +118 -1
- package/dist/scripting/VisualGraph.js.map +1 -1
- package/dist/scripting/VisualGraphContext.d.ts +123 -0
- package/dist/scripting/VisualGraphContext.d.ts.map +1 -0
- package/dist/scripting/VisualGraphContext.js +2 -0
- package/dist/scripting/VisualGraphContext.js.map +1 -0
- package/dist/scripting/VisualGraphExecutor.d.ts +6 -1
- package/dist/scripting/VisualGraphExecutor.d.ts.map +1 -1
- package/dist/scripting/VisualGraphExecutor.js +364 -7
- package/dist/scripting/VisualGraphExecutor.js.map +1 -1
- package/dist/scripting/VisualNodeCatalog.d.ts +1 -1
- package/dist/scripting/VisualNodeCatalog.d.ts.map +1 -1
- package/dist/scripting/VisualNodeCatalog.js +61 -1
- package/dist/scripting/VisualNodeCatalog.js.map +1 -1
- package/dist/scripting/index.d.ts +1 -0
- package/dist/scripting/index.d.ts.map +1 -1
- package/dist/scripting/index.js.map +1 -1
- package/package.json +193 -121
- package/templates/product-viewer/src/main.ts +1 -0
- package/dist/three-compat/ThreeApiInventory.d.ts +0 -18
- package/dist/three-compat/ThreeApiInventory.d.ts.map +0 -1
- package/dist/three-compat/ThreeApiInventory.js +0 -99
- package/dist/three-compat/ThreeApiInventory.js.map +0 -1
- package/dist/three-compat/ThreeCompatibilityMatrix.d.ts +0 -30
- package/dist/three-compat/ThreeCompatibilityMatrix.d.ts.map +0 -1
- package/dist/three-compat/ThreeCompatibilityMatrix.js +0 -69
- package/dist/three-compat/ThreeCompatibilityMatrix.js.map +0 -1
- package/dist/three-compat/animation/index.d.ts +0 -3
- package/dist/three-compat/animation/index.d.ts.map +0 -1
- package/dist/three-compat/animation/index.js +0 -2
- package/dist/three-compat/animation/index.js.map +0 -1
- package/dist/three-compat/cameras/index.d.ts +0 -24
- package/dist/three-compat/cameras/index.d.ts.map +0 -1
- package/dist/three-compat/cameras/index.js +0 -44
- package/dist/three-compat/cameras/index.js.map +0 -1
- package/dist/three-compat/controls/index.d.ts +0 -3
- package/dist/three-compat/controls/index.d.ts.map +0 -1
- package/dist/three-compat/controls/index.js +0 -2
- package/dist/three-compat/controls/index.js.map +0 -1
- package/dist/three-compat/core/Object3DCompat.d.ts +0 -62
- package/dist/three-compat/core/Object3DCompat.d.ts.map +0 -1
- package/dist/three-compat/core/Object3DCompat.js +0 -112
- package/dist/three-compat/core/Object3DCompat.js.map +0 -1
- package/dist/three-compat/core/RaycasterCompat.d.ts +0 -18
- package/dist/three-compat/core/RaycasterCompat.d.ts.map +0 -1
- package/dist/three-compat/core/RaycasterCompat.js +0 -28
- package/dist/three-compat/core/RaycasterCompat.js.map +0 -1
- package/dist/three-compat/core/SceneCompat.d.ts +0 -8
- package/dist/three-compat/core/SceneCompat.d.ts.map +0 -1
- package/dist/three-compat/core/SceneCompat.js +0 -8
- package/dist/three-compat/core/SceneCompat.js.map +0 -1
- package/dist/three-compat/geometries/index.d.ts +0 -67
- package/dist/three-compat/geometries/index.d.ts.map +0 -1
- package/dist/three-compat/geometries/index.js +0 -114
- package/dist/three-compat/geometries/index.js.map +0 -1
- package/dist/three-compat/helpers/index.d.ts +0 -40
- package/dist/three-compat/helpers/index.d.ts.map +0 -1
- package/dist/three-compat/helpers/index.js +0 -129
- package/dist/three-compat/helpers/index.js.map +0 -1
- package/dist/three-compat/index.d.ts +0 -32
- package/dist/three-compat/index.d.ts.map +0 -1
- package/dist/three-compat/index.js +0 -22
- package/dist/three-compat/index.js.map +0 -1
- package/dist/three-compat/lights/index.d.ts +0 -35
- package/dist/three-compat/lights/index.d.ts.map +0 -1
- package/dist/three-compat/lights/index.js +0 -39
- package/dist/three-compat/lights/index.js.map +0 -1
- package/dist/three-compat/loaders/index.d.ts +0 -54
- package/dist/three-compat/loaders/index.d.ts.map +0 -1
- package/dist/three-compat/loaders/index.js +0 -79
- package/dist/three-compat/loaders/index.js.map +0 -1
- package/dist/three-compat/materials/index.d.ts +0 -70
- package/dist/three-compat/materials/index.d.ts.map +0 -1
- package/dist/three-compat/materials/index.js +0 -85
- package/dist/three-compat/materials/index.js.map +0 -1
- package/dist/three-compat/math/index.d.ts +0 -34
- package/dist/three-compat/math/index.d.ts.map +0 -1
- package/dist/three-compat/math/index.js +0 -90
- package/dist/three-compat/math/index.js.map +0 -1
- package/dist/three-compat/migration/CompatibilityWarnings.d.ts +0 -6
- package/dist/three-compat/migration/CompatibilityWarnings.d.ts.map +0 -1
- package/dist/three-compat/migration/CompatibilityWarnings.js +0 -8
- package/dist/three-compat/migration/CompatibilityWarnings.js.map +0 -1
- package/dist/three-compat/migration/ImportMap.d.ts +0 -2
- package/dist/three-compat/migration/ImportMap.d.ts.map +0 -1
- package/dist/three-compat/migration/ImportMap.js +0 -10
- package/dist/three-compat/migration/ImportMap.js.map +0 -1
- package/dist/three-compat/migration/ThreeToA3DAdapter.d.ts +0 -8
- package/dist/three-compat/migration/ThreeToA3DAdapter.d.ts.map +0 -1
- package/dist/three-compat/migration/ThreeToA3DAdapter.js +0 -20
- package/dist/three-compat/migration/ThreeToA3DAdapter.js.map +0 -1
- package/dist/three-compat/postprocessing/index.d.ts +0 -2
- package/dist/three-compat/postprocessing/index.d.ts.map +0 -1
- package/dist/three-compat/postprocessing/index.js +0 -2
- package/dist/three-compat/postprocessing/index.js.map +0 -1
- package/dist/three-compat/render-targets/index.d.ts +0 -17
- package/dist/three-compat/render-targets/index.d.ts.map +0 -1
- package/dist/three-compat/render-targets/index.js +0 -29
- package/dist/three-compat/render-targets/index.js.map +0 -1
- package/dist/three-compat/shaders/index.d.ts +0 -3
- package/dist/three-compat/shaders/index.d.ts.map +0 -1
- package/dist/three-compat/shaders/index.js +0 -3
- package/dist/three-compat/shaders/index.js.map +0 -1
- package/dist/three-compat/textures/index.d.ts +0 -19
- package/dist/three-compat/textures/index.d.ts.map +0 -1
- package/dist/three-compat/textures/index.js +0 -24
- package/dist/three-compat/textures/index.js.map +0 -1
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
export const gameAssetValidationContractVersion = "aura-game-asset-validation/1.0.5";
|
|
2
|
+
export const fightingGameAnimationRoles = [
|
|
3
|
+
"idle",
|
|
4
|
+
"walk-forward",
|
|
5
|
+
"walk-back",
|
|
6
|
+
"jump",
|
|
7
|
+
"land",
|
|
8
|
+
"dash",
|
|
9
|
+
"guard",
|
|
10
|
+
"light",
|
|
11
|
+
"heavy",
|
|
12
|
+
"special",
|
|
13
|
+
"hit",
|
|
14
|
+
"stun",
|
|
15
|
+
"knockdown",
|
|
16
|
+
"win",
|
|
17
|
+
"lose"
|
|
18
|
+
];
|
|
19
|
+
export const quaterniusGameReadyFighterValidationContract = {
|
|
20
|
+
kind: "aura3d-quaternius-game-ready-fighter-validation-contract",
|
|
21
|
+
contractId: gameAssetValidationContractVersion,
|
|
22
|
+
sourceFamily: "quaternius",
|
|
23
|
+
intendedUse: "fighter",
|
|
24
|
+
requiredChecks: [
|
|
25
|
+
"typed Aura model asset reference",
|
|
26
|
+
"Quaternius-derived provenance",
|
|
27
|
+
"GLB or glTF model format",
|
|
28
|
+
"browser-sized payload",
|
|
29
|
+
"humanoid skeleton and retargetable rig metadata",
|
|
30
|
+
"grounded pivot and fighting-game bounds",
|
|
31
|
+
"forward-facing orientation before runtime mirroring",
|
|
32
|
+
"readable visible materials",
|
|
33
|
+
"texture dimension budget",
|
|
34
|
+
"thumbnail or first-frame capture path",
|
|
35
|
+
"named fighting animation clips with non-empty tracks",
|
|
36
|
+
"clip events for attack active frames when events are required"
|
|
37
|
+
],
|
|
38
|
+
requiredAnimationRoles: fightingGameAnimationRoles,
|
|
39
|
+
evidenceBoundary: "This is a source validation contract. A Quaternius-derived fighter is not evidence-ready until validate-game output, typed asset metadata, and retained runtime/browser evidence are archived."
|
|
40
|
+
};
|
|
41
|
+
export function defineGameAssetReadinessManifest(manifest) {
|
|
42
|
+
return manifest;
|
|
43
|
+
}
|
|
44
|
+
export function createGameAssetReadinessManifest(asset, options = {}) {
|
|
45
|
+
const metadata = asset.metadata;
|
|
46
|
+
const file = {
|
|
47
|
+
url: options.file?.url ?? asset.url,
|
|
48
|
+
format: options.file?.format ?? asset.format,
|
|
49
|
+
sizeBytes: options.file?.sizeBytes ?? asset.sizeBytes,
|
|
50
|
+
hash: options.file?.hash ?? asset.hash
|
|
51
|
+
};
|
|
52
|
+
const provenance = options.provenance ?? provenanceFromAsset(asset);
|
|
53
|
+
const thumbnail = options.thumbnail ?? (metadata?.thumbnailUrl ? { url: metadata.thumbnailUrl } : undefined);
|
|
54
|
+
return {
|
|
55
|
+
kind: "aura-game-asset-readiness-manifest",
|
|
56
|
+
contractId: gameAssetValidationContractVersion,
|
|
57
|
+
asset,
|
|
58
|
+
displayName: options.displayName,
|
|
59
|
+
file,
|
|
60
|
+
provenance,
|
|
61
|
+
thumbnail,
|
|
62
|
+
bounds: normalizeGameAssetBounds(options.bounds ?? asset.bounds, "asset-metadata"),
|
|
63
|
+
orientation: options.orientation,
|
|
64
|
+
skeleton: options.skeleton,
|
|
65
|
+
animations: normalizeAnimationClips(options.animations ?? metadata?.animations),
|
|
66
|
+
morphTargets: options.morphTargets,
|
|
67
|
+
materials: normalizeMaterials(options.materials ?? metadata?.materials),
|
|
68
|
+
textures: normalizeTextures(options.textures ?? metadata?.textures),
|
|
69
|
+
intendedUse: options.intendedUse,
|
|
70
|
+
notes: options.notes,
|
|
71
|
+
generatedAt: options.generatedAt
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function validateGameAssetReadiness(manifest, policy = {}) {
|
|
75
|
+
const checks = [];
|
|
76
|
+
const issues = [];
|
|
77
|
+
const assetId = manifest.asset.id;
|
|
78
|
+
pushCheck(checkTypedAsset(manifest), checks, issues);
|
|
79
|
+
pushCheck(checkModelFormat(manifest), checks, issues);
|
|
80
|
+
pushCheck(checkFileSize(manifest, policy), checks, issues);
|
|
81
|
+
for (const check of evaluateGameAssetBounds(manifest, policy.bounds, policy.requireBounds))
|
|
82
|
+
pushCheck(check, checks, issues);
|
|
83
|
+
for (const check of evaluateGameAssetOrientation(manifest, policy.orientation, policy.requireOrientation)) {
|
|
84
|
+
pushCheck(check, checks, issues);
|
|
85
|
+
}
|
|
86
|
+
for (const check of evaluateGameAssetAnimationClips(manifest, policy.animation))
|
|
87
|
+
pushCheck(check, checks, issues);
|
|
88
|
+
pushCheck(checkSkeleton(manifest, policy.requireSkeleton), checks, issues);
|
|
89
|
+
pushCheck(checkMaterials(manifest, policy.requireReadableMaterials), checks, issues);
|
|
90
|
+
for (const check of checkTextures(manifest, policy.maxTextureDimension))
|
|
91
|
+
pushCheck(check, checks, issues);
|
|
92
|
+
pushCheck(checkThumbnail(manifest, policy.requireThumbnail), checks, issues);
|
|
93
|
+
pushCheck(checkProvenance(manifest, policy.requireProvenance), checks, issues);
|
|
94
|
+
pushCheck(checkIntendedUse(manifest, policy.requireIntendedUse), checks, issues);
|
|
95
|
+
const errors = issues.filter((issue) => issue.severity === "error").length;
|
|
96
|
+
const warnings = issues.filter((issue) => issue.severity === "warning").length;
|
|
97
|
+
return {
|
|
98
|
+
kind: "aura-game-asset-validation-report",
|
|
99
|
+
contractId: gameAssetValidationContractVersion,
|
|
100
|
+
asset: manifest.asset,
|
|
101
|
+
assetId,
|
|
102
|
+
manifest,
|
|
103
|
+
ready: errors === 0,
|
|
104
|
+
summary: {
|
|
105
|
+
status: errors > 0 ? "fail" : warnings > 0 ? "warn" : "pass",
|
|
106
|
+
checks: checks.length,
|
|
107
|
+
errors,
|
|
108
|
+
warnings,
|
|
109
|
+
animationClips: manifest.animations?.length ?? 0,
|
|
110
|
+
materials: manifest.materials?.length ?? 0,
|
|
111
|
+
textures: manifest.textures?.length ?? 0
|
|
112
|
+
},
|
|
113
|
+
checks,
|
|
114
|
+
issues
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function createQuaterniusGameReadyFighterValidationPolicy(overrides = {}) {
|
|
118
|
+
return {
|
|
119
|
+
maxFileSizeBytes: overrides.maxFileSizeBytes ?? 50 * 1024 * 1024,
|
|
120
|
+
requireFileSize: overrides.requireFileSize ?? true,
|
|
121
|
+
requireBounds: overrides.requireBounds ?? true,
|
|
122
|
+
requireOrientation: overrides.requireOrientation ?? true,
|
|
123
|
+
requireSkeleton: overrides.requireSkeleton ?? true,
|
|
124
|
+
requireThumbnail: overrides.requireThumbnail ?? true,
|
|
125
|
+
requireProvenance: overrides.requireProvenance ?? true,
|
|
126
|
+
requireIntendedUse: overrides.requireIntendedUse ?? true,
|
|
127
|
+
requireReadableMaterials: overrides.requireReadableMaterials ?? true,
|
|
128
|
+
maxTextureDimension: overrides.maxTextureDimension ?? 4096,
|
|
129
|
+
bounds: {
|
|
130
|
+
minWidth: 0.2,
|
|
131
|
+
maxWidth: 4,
|
|
132
|
+
minHeight: 0.6,
|
|
133
|
+
maxHeight: 4,
|
|
134
|
+
minDepth: 0.1,
|
|
135
|
+
maxDepth: 4,
|
|
136
|
+
maxDimension: 4,
|
|
137
|
+
maxOriginOffset: 0.5,
|
|
138
|
+
requireGroundedPivot: true,
|
|
139
|
+
groundTolerance: 0.08,
|
|
140
|
+
...overrides.bounds
|
|
141
|
+
},
|
|
142
|
+
orientation: {
|
|
143
|
+
upAxis: "y",
|
|
144
|
+
forwardAxis: "z",
|
|
145
|
+
pivot: ["feet", "grounded"],
|
|
146
|
+
minUnitScale: 0.001,
|
|
147
|
+
maxUnitScale: 1000,
|
|
148
|
+
...overrides.orientation
|
|
149
|
+
},
|
|
150
|
+
animation: {
|
|
151
|
+
requireNonEmptyClips: true,
|
|
152
|
+
minimumDuration: 1 / 30,
|
|
153
|
+
requiredClips: fightingGameAnimationRoles,
|
|
154
|
+
...overrides.animation
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export function validateQuaterniusGameReadyFighterAsset(manifest, policy = {}) {
|
|
159
|
+
const base = validateGameAssetReadiness(manifest, createQuaterniusGameReadyFighterValidationPolicy(policy));
|
|
160
|
+
const quaterniusCheck = checkQuaterniusProvenance(manifest);
|
|
161
|
+
const checks = [...base.checks, quaterniusCheck];
|
|
162
|
+
const issues = [...base.issues];
|
|
163
|
+
if (quaterniusCheck.status === "fail") {
|
|
164
|
+
issues.push(createGameAssetValidationIssue("error", quaterniusCheck.id, quaterniusCheck.message, { assetId: quaterniusCheck.assetId }));
|
|
165
|
+
}
|
|
166
|
+
else if (quaterniusCheck.status === "warn" || quaterniusCheck.status === "missing") {
|
|
167
|
+
issues.push(createGameAssetValidationIssue("warning", quaterniusCheck.id, quaterniusCheck.message, { assetId: quaterniusCheck.assetId }));
|
|
168
|
+
}
|
|
169
|
+
const errors = issues.filter((issue) => issue.severity === "error").length;
|
|
170
|
+
const warnings = issues.filter((issue) => issue.severity === "warning").length;
|
|
171
|
+
return {
|
|
172
|
+
...base,
|
|
173
|
+
ready: errors === 0,
|
|
174
|
+
summary: {
|
|
175
|
+
...base.summary,
|
|
176
|
+
status: errors > 0 ? "fail" : warnings > 0 ? "warn" : "pass",
|
|
177
|
+
checks: checks.length,
|
|
178
|
+
errors,
|
|
179
|
+
warnings
|
|
180
|
+
},
|
|
181
|
+
checks,
|
|
182
|
+
issues
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
export function evaluateGameAssetBounds(manifest, policy = {}, required = false) {
|
|
186
|
+
const bounds = manifest.bounds;
|
|
187
|
+
if (!bounds) {
|
|
188
|
+
return [
|
|
189
|
+
{
|
|
190
|
+
id: "bounds",
|
|
191
|
+
status: required ? "fail" : "missing",
|
|
192
|
+
assetId: manifest.asset.id,
|
|
193
|
+
message: required
|
|
194
|
+
? "Asset bounds are required for game readiness."
|
|
195
|
+
: "Asset bounds are missing; scale, pivot, and camera framing cannot be proven."
|
|
196
|
+
}
|
|
197
|
+
];
|
|
198
|
+
}
|
|
199
|
+
const checks = [];
|
|
200
|
+
const [width, height, depth] = bounds.size;
|
|
201
|
+
const maxDimension = Math.max(width, height, depth);
|
|
202
|
+
const originOffset = Math.hypot(bounds.center[0], bounds.center[2]);
|
|
203
|
+
const groundTolerance = policy.groundTolerance ?? 0.08;
|
|
204
|
+
const minY = bounds.min?.[1];
|
|
205
|
+
checks.push(rangeCheck("bounds.width", width, policy.minWidth, policy.maxWidth, manifest.asset.id, "width"));
|
|
206
|
+
checks.push(rangeCheck("bounds.height", height, policy.minHeight, policy.maxHeight, manifest.asset.id, "height"));
|
|
207
|
+
checks.push(rangeCheck("bounds.depth", depth, policy.minDepth, policy.maxDepth, manifest.asset.id, "depth"));
|
|
208
|
+
if (policy.maxDimension !== undefined) {
|
|
209
|
+
checks.push(maxCheck("bounds.max-dimension", maxDimension, policy.maxDimension, manifest.asset.id, "largest asset dimension"));
|
|
210
|
+
}
|
|
211
|
+
if (policy.maxOriginOffset !== undefined) {
|
|
212
|
+
checks.push(maxCheck("bounds.origin-offset", originOffset, policy.maxOriginOffset, manifest.asset.id, "horizontal origin offset"));
|
|
213
|
+
}
|
|
214
|
+
if (policy.requireGroundedPivot) {
|
|
215
|
+
checks.push({
|
|
216
|
+
id: "bounds.grounded-pivot",
|
|
217
|
+
status: minY === undefined ? "missing" : Math.abs(minY) <= groundTolerance ? "pass" : "fail",
|
|
218
|
+
assetId: manifest.asset.id,
|
|
219
|
+
message: minY === undefined
|
|
220
|
+
? "Bounds min.y is missing, so grounded pivot cannot be proven."
|
|
221
|
+
: Math.abs(minY) <= groundTolerance
|
|
222
|
+
? "Asset pivot is close to the ground plane."
|
|
223
|
+
: `Asset appears offset from the ground plane by ${round(minY)}m.`,
|
|
224
|
+
metrics: minY === undefined ? undefined : { minY: round(minY), tolerance: groundTolerance }
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return checks;
|
|
228
|
+
}
|
|
229
|
+
export function evaluateGameAssetOrientation(manifest, policy = {}, required = false) {
|
|
230
|
+
const orientation = manifest.orientation;
|
|
231
|
+
if (!orientation) {
|
|
232
|
+
return [
|
|
233
|
+
{
|
|
234
|
+
id: "orientation",
|
|
235
|
+
status: required ? "fail" : "missing",
|
|
236
|
+
assetId: manifest.asset.id,
|
|
237
|
+
message: required
|
|
238
|
+
? "Asset orientation is required for game readiness."
|
|
239
|
+
: "Asset orientation is missing; facing direction and up axis cannot be proven."
|
|
240
|
+
}
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
const checks = [];
|
|
244
|
+
const expectedUp = policy.upAxis ?? "y";
|
|
245
|
+
const expectedForward = toAxisList(policy.forwardAxis ?? ["z", "-z"]);
|
|
246
|
+
const expectedPivots = toPivotList(policy.pivot ?? ["feet", "grounded", "center"]);
|
|
247
|
+
checks.push({
|
|
248
|
+
id: "orientation.up-axis",
|
|
249
|
+
status: orientation.upAxis === expectedUp ? "pass" : "fail",
|
|
250
|
+
assetId: manifest.asset.id,
|
|
251
|
+
message: orientation.upAxis === expectedUp
|
|
252
|
+
? `Asset up axis is ${expectedUp}.`
|
|
253
|
+
: `Asset up axis is ${orientation.upAxis}; expected ${expectedUp}.`
|
|
254
|
+
});
|
|
255
|
+
checks.push({
|
|
256
|
+
id: "orientation.forward-axis",
|
|
257
|
+
status: expectedForward.includes(orientation.forwardAxis) ? "pass" : "fail",
|
|
258
|
+
assetId: manifest.asset.id,
|
|
259
|
+
message: expectedForward.includes(orientation.forwardAxis)
|
|
260
|
+
? `Asset forward axis is ${orientation.forwardAxis}.`
|
|
261
|
+
: `Asset forward axis is ${orientation.forwardAxis}; expected one of ${expectedForward.join(", ")}.`
|
|
262
|
+
});
|
|
263
|
+
if (orientation.pivot) {
|
|
264
|
+
checks.push({
|
|
265
|
+
id: "orientation.pivot",
|
|
266
|
+
status: expectedPivots.includes(orientation.pivot) ? "pass" : "fail",
|
|
267
|
+
assetId: manifest.asset.id,
|
|
268
|
+
message: expectedPivots.includes(orientation.pivot)
|
|
269
|
+
? `Asset pivot is ${orientation.pivot}.`
|
|
270
|
+
: `Asset pivot is ${orientation.pivot}; expected one of ${expectedPivots.join(", ")}.`
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (orientation.unitScale !== undefined) {
|
|
274
|
+
const minScale = policy.minUnitScale ?? 0.001;
|
|
275
|
+
const maxScale = policy.maxUnitScale ?? 1000;
|
|
276
|
+
checks.push(rangeCheck("orientation.unit-scale", orientation.unitScale, minScale, maxScale, manifest.asset.id, "unit scale"));
|
|
277
|
+
}
|
|
278
|
+
return checks;
|
|
279
|
+
}
|
|
280
|
+
export function evaluateGameAssetAnimationClips(manifest, policy = {}) {
|
|
281
|
+
const clips = manifest.animations ?? [];
|
|
282
|
+
const checks = [];
|
|
283
|
+
const minimumDuration = policy.minimumDuration ?? 1 / 30;
|
|
284
|
+
const requireNonEmpty = policy.requireNonEmptyClips ?? true;
|
|
285
|
+
if (requireNonEmpty) {
|
|
286
|
+
checks.push({
|
|
287
|
+
id: "animation.clips-present",
|
|
288
|
+
status: clips.length > 0 ? "pass" : "missing",
|
|
289
|
+
assetId: manifest.asset.id,
|
|
290
|
+
message: clips.length > 0 ? `Asset declares ${clips.length} animation clips.` : "Asset declares no animation clips."
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
for (const clip of clips) {
|
|
294
|
+
const duration = clip.duration ?? 0;
|
|
295
|
+
checks.push({
|
|
296
|
+
id: `animation.clip.${clip.name}.duration`,
|
|
297
|
+
status: duration > 0 && duration >= minimumDuration ? "pass" : "fail",
|
|
298
|
+
assetId: manifest.asset.id,
|
|
299
|
+
message: duration > 0 && duration >= minimumDuration
|
|
300
|
+
? `Animation clip "${clip.name}" has duration ${round(duration)}s.`
|
|
301
|
+
: `Animation clip "${clip.name}" is empty or shorter than ${round(minimumDuration)}s.`,
|
|
302
|
+
metrics: { duration: round(duration), minimumDuration: round(minimumDuration) }
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
for (const requirement of policy.requiredClips ?? []) {
|
|
306
|
+
const required = normalizeClipRequirement(requirement);
|
|
307
|
+
const clip = findClip(clips, required);
|
|
308
|
+
const label = required.role ?? required.name ?? "clip";
|
|
309
|
+
if (!clip) {
|
|
310
|
+
checks.push({
|
|
311
|
+
id: `animation.required.${label}`,
|
|
312
|
+
status: "fail",
|
|
313
|
+
assetId: manifest.asset.id,
|
|
314
|
+
message: `Missing required animation clip "${label}".`
|
|
315
|
+
});
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
checks.push({
|
|
319
|
+
id: `animation.required.${label}`,
|
|
320
|
+
status: "pass",
|
|
321
|
+
assetId: manifest.asset.id,
|
|
322
|
+
message: `Required animation clip "${label}" is present as "${clip.name}".`
|
|
323
|
+
});
|
|
324
|
+
if (required.minimumDuration !== undefined) {
|
|
325
|
+
const duration = clip.duration ?? 0;
|
|
326
|
+
checks.push({
|
|
327
|
+
id: `animation.required.${label}.duration`,
|
|
328
|
+
status: duration >= required.minimumDuration ? "pass" : "fail",
|
|
329
|
+
assetId: manifest.asset.id,
|
|
330
|
+
message: duration >= required.minimumDuration
|
|
331
|
+
? `Required clip "${label}" meets duration policy.`
|
|
332
|
+
: `Required clip "${label}" is ${round(duration)}s; expected at least ${round(required.minimumDuration)}s.`
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
for (const eventName of required.requireEvents ?? []) {
|
|
336
|
+
const hasEvent = Boolean(clip.events?.some((event) => event.name === eventName));
|
|
337
|
+
checks.push({
|
|
338
|
+
id: `animation.required.${label}.event.${eventName}`,
|
|
339
|
+
status: hasEvent ? "pass" : "fail",
|
|
340
|
+
assetId: manifest.asset.id,
|
|
341
|
+
message: hasEvent
|
|
342
|
+
? `Required clip "${label}" has event "${eventName}".`
|
|
343
|
+
: `Required clip "${label}" is missing event "${eventName}".`
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return checks;
|
|
348
|
+
}
|
|
349
|
+
export function createGameAssetValidationIssue(severity, code, message, details = {}) {
|
|
350
|
+
return {
|
|
351
|
+
severity,
|
|
352
|
+
code,
|
|
353
|
+
message,
|
|
354
|
+
...details
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
export function isAuraGameModelAssetRef(value) {
|
|
358
|
+
const candidate = value;
|
|
359
|
+
return Boolean(candidate && candidate.kind === "aura-asset-ref" && candidate.type === "model" && candidate.id);
|
|
360
|
+
}
|
|
361
|
+
export function gameAssetBoundsFromSize(size, source = "manual") {
|
|
362
|
+
return normalizeGameAssetBounds(size, source) ?? {
|
|
363
|
+
center: [0, 0, 0],
|
|
364
|
+
size: [0, 0, 0],
|
|
365
|
+
min: [0, 0, 0],
|
|
366
|
+
max: [0, 0, 0],
|
|
367
|
+
radius: 0,
|
|
368
|
+
source
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function checkTypedAsset(manifest) {
|
|
372
|
+
const asset = manifest.asset;
|
|
373
|
+
const valid = isAuraGameModelAssetRef(asset);
|
|
374
|
+
return {
|
|
375
|
+
id: "asset.typed-ref",
|
|
376
|
+
status: valid ? "pass" : "fail",
|
|
377
|
+
assetId: asset.id,
|
|
378
|
+
message: valid
|
|
379
|
+
? `Asset "${asset.id}" is a typed Aura model asset reference.`
|
|
380
|
+
: "Asset must be a typed Aura model asset reference, for example model(assets.fighter)."
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function checkModelFormat(manifest) {
|
|
384
|
+
const format = normalizeModelFormat(manifest.file.format);
|
|
385
|
+
const supported = format === "glb" || format === "gltf";
|
|
386
|
+
return {
|
|
387
|
+
id: "asset.format",
|
|
388
|
+
status: supported ? "pass" : "fail",
|
|
389
|
+
assetId: manifest.asset.id,
|
|
390
|
+
message: supported
|
|
391
|
+
? `Asset format "${format}" is supported.`
|
|
392
|
+
: `Asset format "${manifest.file.format}" is not supported for game readiness; export GLB or glTF.`
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function checkFileSize(manifest, policy) {
|
|
396
|
+
const sizeBytes = manifest.file.sizeBytes;
|
|
397
|
+
if (sizeBytes === undefined) {
|
|
398
|
+
return {
|
|
399
|
+
id: "asset.file-size",
|
|
400
|
+
status: policy.requireFileSize ? "fail" : "missing",
|
|
401
|
+
assetId: manifest.asset.id,
|
|
402
|
+
message: policy.requireFileSize
|
|
403
|
+
? "Asset file size is required."
|
|
404
|
+
: "Asset file size is missing; download budget cannot be proven."
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const maxFileSizeBytes = policy.maxFileSizeBytes;
|
|
408
|
+
if (maxFileSizeBytes === undefined) {
|
|
409
|
+
return {
|
|
410
|
+
id: "asset.file-size",
|
|
411
|
+
status: "pass",
|
|
412
|
+
assetId: manifest.asset.id,
|
|
413
|
+
message: `Asset file size is ${sizeBytes} bytes.`,
|
|
414
|
+
metrics: { sizeBytes }
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
id: "asset.file-size",
|
|
419
|
+
status: sizeBytes <= maxFileSizeBytes ? "pass" : "fail",
|
|
420
|
+
assetId: manifest.asset.id,
|
|
421
|
+
message: sizeBytes <= maxFileSizeBytes
|
|
422
|
+
? `Asset file size is within budget (${sizeBytes}/${maxFileSizeBytes} bytes).`
|
|
423
|
+
: `Asset file size exceeds budget (${sizeBytes}/${maxFileSizeBytes} bytes).`,
|
|
424
|
+
metrics: { sizeBytes, maxFileSizeBytes }
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function checkSkeleton(manifest, required = false) {
|
|
428
|
+
const skeleton = manifest.skeleton;
|
|
429
|
+
if (!skeleton) {
|
|
430
|
+
return {
|
|
431
|
+
id: "skeleton",
|
|
432
|
+
status: required ? "fail" : "missing",
|
|
433
|
+
assetId: manifest.asset.id,
|
|
434
|
+
message: required ? "Skeleton readiness is required." : "Skeleton readiness is not declared."
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
const missingRequiredBones = skeleton.missingRequiredBones ?? [];
|
|
438
|
+
return {
|
|
439
|
+
id: "skeleton",
|
|
440
|
+
status: skeleton.present && missingRequiredBones.length === 0 ? "pass" : "fail",
|
|
441
|
+
assetId: manifest.asset.id,
|
|
442
|
+
message: skeleton.present && missingRequiredBones.length === 0
|
|
443
|
+
? `Skeleton is present${skeleton.joints ? ` with ${skeleton.joints} joints` : ""}.`
|
|
444
|
+
: `Skeleton is not ready${missingRequiredBones.length ? `; missing ${missingRequiredBones.join(", ")}` : ""}.`
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
function checkMaterials(manifest, requireReadable = false) {
|
|
448
|
+
const materials = manifest.materials ?? [];
|
|
449
|
+
if (materials.length === 0) {
|
|
450
|
+
return {
|
|
451
|
+
id: "materials",
|
|
452
|
+
status: requireReadable ? "fail" : "missing",
|
|
453
|
+
assetId: manifest.asset.id,
|
|
454
|
+
message: requireReadable ? "Material summary is required." : "Material summary is missing."
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const invisible = materials.filter((material) => material.visible === false || material.opacity === 0);
|
|
458
|
+
const unreadable = materials.filter((material) => material.readable === false);
|
|
459
|
+
return {
|
|
460
|
+
id: "materials",
|
|
461
|
+
status: invisible.length === 0 && unreadable.length === 0 ? "pass" : "fail",
|
|
462
|
+
assetId: manifest.asset.id,
|
|
463
|
+
message: invisible.length === 0 && unreadable.length === 0
|
|
464
|
+
? `Asset declares ${materials.length} readable materials.`
|
|
465
|
+
: `Asset has material issues: ${[...invisible.map((material) => `${material.name} invisible`), ...unreadable.map((material) => `${material.name} unreadable`)].join(", ")}.`
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function checkTextures(manifest, maxTextureDimension) {
|
|
469
|
+
const textures = manifest.textures ?? [];
|
|
470
|
+
if (textures.length === 0) {
|
|
471
|
+
return [
|
|
472
|
+
{
|
|
473
|
+
id: "textures",
|
|
474
|
+
status: "missing",
|
|
475
|
+
assetId: manifest.asset.id,
|
|
476
|
+
message: "Texture summary is missing."
|
|
477
|
+
}
|
|
478
|
+
];
|
|
479
|
+
}
|
|
480
|
+
const checks = [
|
|
481
|
+
{
|
|
482
|
+
id: "textures",
|
|
483
|
+
status: "pass",
|
|
484
|
+
assetId: manifest.asset.id,
|
|
485
|
+
message: `Asset declares ${textures.length} textures.`
|
|
486
|
+
}
|
|
487
|
+
];
|
|
488
|
+
if (maxTextureDimension !== undefined) {
|
|
489
|
+
for (const texture of textures) {
|
|
490
|
+
const width = texture.width ?? 0;
|
|
491
|
+
const height = texture.height ?? 0;
|
|
492
|
+
const dimension = Math.max(width, height);
|
|
493
|
+
checks.push({
|
|
494
|
+
id: `textures.${texture.name}.dimension`,
|
|
495
|
+
status: dimension === 0 ? "missing" : dimension <= maxTextureDimension ? "pass" : "fail",
|
|
496
|
+
assetId: manifest.asset.id,
|
|
497
|
+
message: dimension === 0
|
|
498
|
+
? `Texture "${texture.name}" dimensions are missing.`
|
|
499
|
+
: dimension <= maxTextureDimension
|
|
500
|
+
? `Texture "${texture.name}" is within dimension budget.`
|
|
501
|
+
: `Texture "${texture.name}" exceeds ${maxTextureDimension}px.`
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return checks;
|
|
506
|
+
}
|
|
507
|
+
function checkThumbnail(manifest, required = false) {
|
|
508
|
+
const thumbnail = manifest.thumbnail;
|
|
509
|
+
return {
|
|
510
|
+
id: "thumbnail",
|
|
511
|
+
status: thumbnail?.url || thumbnail?.path ? "pass" : required ? "fail" : "missing",
|
|
512
|
+
assetId: manifest.asset.id,
|
|
513
|
+
message: thumbnail?.url || thumbnail?.path
|
|
514
|
+
? "Asset thumbnail is declared."
|
|
515
|
+
: required
|
|
516
|
+
? "Asset thumbnail is required."
|
|
517
|
+
: "Asset thumbnail is missing."
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function checkProvenance(manifest, required = false) {
|
|
521
|
+
const provenance = manifest.provenance;
|
|
522
|
+
const hasLicense = Boolean(provenance?.license || provenance?.licenseUrl);
|
|
523
|
+
const approved = provenance?.approvalStatus === "approved";
|
|
524
|
+
const ready = Boolean(provenance && hasLicense && provenance.commercialUse !== false && provenance.approvalStatus !== "rejected");
|
|
525
|
+
return {
|
|
526
|
+
id: "provenance",
|
|
527
|
+
status: ready ? (approved ? "pass" : "warn") : required ? "fail" : "missing",
|
|
528
|
+
assetId: manifest.asset.id,
|
|
529
|
+
message: ready
|
|
530
|
+
? approved
|
|
531
|
+
? "Asset provenance is approved."
|
|
532
|
+
: "Asset provenance is present but not marked approved."
|
|
533
|
+
: required
|
|
534
|
+
? "Asset provenance, license, and commercial-use status are required."
|
|
535
|
+
: "Asset provenance is missing or incomplete."
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
function checkIntendedUse(manifest, required = false) {
|
|
539
|
+
const intendedUse = manifest.intendedUse;
|
|
540
|
+
return {
|
|
541
|
+
id: "intended-use",
|
|
542
|
+
status: intendedUse ? "pass" : required ? "fail" : "missing",
|
|
543
|
+
assetId: manifest.asset.id,
|
|
544
|
+
message: intendedUse ? `Asset intended use is "${intendedUse.kind}".` : "Asset intended route/game usage is missing."
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function checkQuaterniusProvenance(manifest) {
|
|
548
|
+
const provenance = manifest.provenance;
|
|
549
|
+
const source = [
|
|
550
|
+
provenance?.title,
|
|
551
|
+
provenance?.creator,
|
|
552
|
+
provenance?.sourceName,
|
|
553
|
+
provenance?.sourceUrl,
|
|
554
|
+
provenance?.attribution,
|
|
555
|
+
...(provenance?.notes ?? [])
|
|
556
|
+
]
|
|
557
|
+
.filter((value) => typeof value === "string")
|
|
558
|
+
.join(" ");
|
|
559
|
+
const quaternius = /quaternius/i.test(source);
|
|
560
|
+
return {
|
|
561
|
+
id: "quaternius.source-family",
|
|
562
|
+
status: quaternius ? "pass" : "fail",
|
|
563
|
+
assetId: manifest.asset.id,
|
|
564
|
+
message: quaternius
|
|
565
|
+
? "Asset provenance identifies a Quaternius-derived source."
|
|
566
|
+
: "Quaternius-derived fighter validation requires provenance that identifies Quaternius as the source family."
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function pushCheck(check, checks, issues) {
|
|
570
|
+
checks.push(check);
|
|
571
|
+
if (check.status === "fail") {
|
|
572
|
+
issues.push(createGameAssetValidationIssue("error", check.id, check.message, { assetId: check.assetId }));
|
|
573
|
+
}
|
|
574
|
+
if (check.status === "warn" || check.status === "missing") {
|
|
575
|
+
issues.push(createGameAssetValidationIssue("warning", check.id, check.message, { assetId: check.assetId }));
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function normalizeGameAssetBounds(bounds, source) {
|
|
579
|
+
if (!bounds)
|
|
580
|
+
return undefined;
|
|
581
|
+
if (Array.isArray(bounds)) {
|
|
582
|
+
const size = [safeNumber(bounds[0]), safeNumber(bounds[1]), safeNumber(bounds[2])];
|
|
583
|
+
const center = [0, size[1] / 2, 0];
|
|
584
|
+
const half = [size[0] / 2, size[1] / 2, size[2] / 2];
|
|
585
|
+
return {
|
|
586
|
+
center,
|
|
587
|
+
size,
|
|
588
|
+
min: [-half[0], 0, -half[2]],
|
|
589
|
+
max: [half[0], size[1], half[2]],
|
|
590
|
+
radius: Math.hypot(half[0], half[1], half[2]),
|
|
591
|
+
source
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
return bounds;
|
|
595
|
+
}
|
|
596
|
+
function provenanceFromAsset(asset) {
|
|
597
|
+
if (!asset.metadata?.license && !asset.hash)
|
|
598
|
+
return undefined;
|
|
599
|
+
return {
|
|
600
|
+
license: asset.metadata?.license,
|
|
601
|
+
fileHash: asset.hash,
|
|
602
|
+
approvalStatus: asset.optional ? "needs-review" : "draft"
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
function normalizeAnimationClips(clips) {
|
|
606
|
+
if (!clips)
|
|
607
|
+
return undefined;
|
|
608
|
+
return clips.map((clip) => (typeof clip === "string" ? { name: clip } : clip));
|
|
609
|
+
}
|
|
610
|
+
function normalizeMaterials(materials) {
|
|
611
|
+
if (!materials)
|
|
612
|
+
return undefined;
|
|
613
|
+
return materials.map((material) => (typeof material === "string" ? { name: material, visible: true, readable: true } : material));
|
|
614
|
+
}
|
|
615
|
+
function normalizeTextures(textures) {
|
|
616
|
+
if (!textures)
|
|
617
|
+
return undefined;
|
|
618
|
+
return textures.map((texture) => (typeof texture === "string" ? { name: texture } : texture));
|
|
619
|
+
}
|
|
620
|
+
function normalizeModelFormat(format) {
|
|
621
|
+
const normalized = format.toLowerCase().replace(/^\./, "");
|
|
622
|
+
if (normalized.includes("gltf-binary"))
|
|
623
|
+
return "glb";
|
|
624
|
+
if (normalized.includes("gltf+json"))
|
|
625
|
+
return "gltf";
|
|
626
|
+
return normalized;
|
|
627
|
+
}
|
|
628
|
+
function rangeCheck(id, value, min, max, assetId, label) {
|
|
629
|
+
const aboveMin = min === undefined || value >= min;
|
|
630
|
+
const belowMax = max === undefined || value <= max;
|
|
631
|
+
return {
|
|
632
|
+
id,
|
|
633
|
+
status: aboveMin && belowMax ? "pass" : "fail",
|
|
634
|
+
assetId,
|
|
635
|
+
message: aboveMin && belowMax
|
|
636
|
+
? `Asset ${label} is ${round(value)}m.`
|
|
637
|
+
: `Asset ${label} is ${round(value)}m; expected ${min ?? "-inf"} to ${max ?? "+inf"}m.`,
|
|
638
|
+
metrics: { value: round(value), ...(min !== undefined ? { min } : {}), ...(max !== undefined ? { max } : {}) }
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function maxCheck(id, value, max, assetId, label) {
|
|
642
|
+
return {
|
|
643
|
+
id,
|
|
644
|
+
status: value <= max ? "pass" : "fail",
|
|
645
|
+
assetId,
|
|
646
|
+
message: value <= max ? `Asset ${label} is within budget.` : `Asset ${label} is ${round(value)}; expected at most ${max}.`,
|
|
647
|
+
metrics: { value: round(value), max }
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function normalizeClipRequirement(requirement) {
|
|
651
|
+
if (typeof requirement !== "string")
|
|
652
|
+
return requirement;
|
|
653
|
+
return isAnimationRole(requirement) ? { role: requirement } : { name: requirement };
|
|
654
|
+
}
|
|
655
|
+
function findClip(clips, requirement) {
|
|
656
|
+
const requiredRole = requirement.role;
|
|
657
|
+
const requiredName = requirement.name?.toLowerCase();
|
|
658
|
+
return clips.find((clip) => {
|
|
659
|
+
if (requiredRole && clip.role === requiredRole)
|
|
660
|
+
return true;
|
|
661
|
+
if (requiredName && clip.name.toLowerCase() === requiredName)
|
|
662
|
+
return true;
|
|
663
|
+
if (requiredRole && clip.name.toLowerCase().includes(requiredRole))
|
|
664
|
+
return true;
|
|
665
|
+
return false;
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
function isAnimationRole(value) {
|
|
669
|
+
return [
|
|
670
|
+
"idle",
|
|
671
|
+
"walk",
|
|
672
|
+
"walk-forward",
|
|
673
|
+
"walk-back",
|
|
674
|
+
"run",
|
|
675
|
+
"jump",
|
|
676
|
+
"land",
|
|
677
|
+
"dash",
|
|
678
|
+
"guard",
|
|
679
|
+
"light",
|
|
680
|
+
"heavy",
|
|
681
|
+
"special",
|
|
682
|
+
"super",
|
|
683
|
+
"hit",
|
|
684
|
+
"stun",
|
|
685
|
+
"knockdown",
|
|
686
|
+
"win",
|
|
687
|
+
"lose",
|
|
688
|
+
"speak",
|
|
689
|
+
"emote"
|
|
690
|
+
].includes(value);
|
|
691
|
+
}
|
|
692
|
+
function toAxisList(axis) {
|
|
693
|
+
return Array.isArray(axis) ? [...axis] : [axis];
|
|
694
|
+
}
|
|
695
|
+
function toPivotList(pivot) {
|
|
696
|
+
return Array.isArray(pivot) ? [...pivot] : [pivot];
|
|
697
|
+
}
|
|
698
|
+
function safeNumber(value) {
|
|
699
|
+
return Number.isFinite(value) ? value : 0;
|
|
700
|
+
}
|
|
701
|
+
function round(value) {
|
|
702
|
+
return Number(value.toFixed(4));
|
|
703
|
+
}
|
|
704
|
+
export const gameAssetValidation = {
|
|
705
|
+
contractVersion: gameAssetValidationContractVersion,
|
|
706
|
+
defineManifest: defineGameAssetReadinessManifest,
|
|
707
|
+
createManifest: createGameAssetReadinessManifest,
|
|
708
|
+
validate: validateGameAssetReadiness,
|
|
709
|
+
quaterniusGameReadyFighter: quaterniusGameReadyFighterValidationContract,
|
|
710
|
+
createQuaterniusGameReadyFighterPolicy: createQuaterniusGameReadyFighterValidationPolicy,
|
|
711
|
+
validateQuaterniusGameReadyFighter: validateQuaterniusGameReadyFighterAsset,
|
|
712
|
+
evaluateBounds: evaluateGameAssetBounds,
|
|
713
|
+
evaluateOrientation: evaluateGameAssetOrientation,
|
|
714
|
+
evaluateAnimationClips: evaluateGameAssetAnimationClips,
|
|
715
|
+
boundsFromSize: gameAssetBoundsFromSize,
|
|
716
|
+
isModelAssetRef: isAuraGameModelAssetRef,
|
|
717
|
+
fightingAnimationRoles: fightingGameAnimationRoles
|
|
718
|
+
};
|
|
719
|
+
//# sourceMappingURL=GameAssetValidation.js.map
|