@jgengine/core 0.18.1 → 0.21.0
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/CHANGELOG.md +504 -7
- package/dist/ai/decisionGraph.d.ts +50 -4
- package/dist/ai/decisionGraph.js +159 -23
- package/dist/ai/flock.d.ts +1 -1
- package/dist/ai/flock.js +7 -4
- package/dist/ai/groupAssist.d.ts +10 -0
- package/dist/ai/groupAssist.js +11 -0
- package/dist/ai/pursuit.d.ts +1 -1
- package/dist/ai/pursuit.js +1 -1
- package/dist/ai/threat.d.ts +8 -0
- package/dist/ai/threat.js +10 -0
- package/dist/anim/animGraph.d.ts +43 -1
- package/dist/anim/animGraph.js +326 -10
- package/dist/anim/boneTexture.d.ts +55 -0
- package/dist/anim/boneTexture.js +43 -0
- package/dist/anim/footPlacement.d.ts +57 -0
- package/dist/anim/footPlacement.js +67 -0
- package/dist/anim/locomotionGraph.d.ts +18 -1
- package/dist/anim/locomotionGraph.js +42 -3
- package/dist/audio/audioFalloff.d.ts +6 -0
- package/dist/audio/doppler.d.ts +21 -0
- package/dist/audio/doppler.js +32 -0
- package/dist/audio/engineLayers.d.ts +83 -0
- package/dist/audio/engineLayers.js +115 -0
- package/dist/audio/voiceAllocator.d.ts +46 -0
- package/dist/audio/voiceAllocator.js +76 -0
- package/dist/combat/abilityKit.d.ts +20 -0
- package/dist/combat/abilityKit.js +41 -4
- package/dist/combat/death.d.ts +11 -3
- package/dist/combat/death.js +3 -3
- package/dist/combat/magazine.d.ts +9 -0
- package/dist/combat/magazine.js +24 -2
- package/dist/combat/projectiles.d.ts +94 -2
- package/dist/combat/projectiles.js +282 -61
- package/dist/combat/regenShield.d.ts +14 -0
- package/dist/combat/regenShield.js +23 -7
- package/dist/combat/shotOrigin.d.ts +6 -0
- package/dist/combat/shotOrigin.js +6 -0
- package/dist/combat/weaponFeedback.d.ts +36 -0
- package/dist/combat/weaponFeedback.js +42 -0
- package/dist/combat/weaponHandling.d.ts +161 -0
- package/dist/combat/weaponHandling.js +202 -0
- package/dist/combat/weaponPresentation.d.ts +115 -0
- package/dist/combat/weaponPresentation.js +96 -0
- package/dist/crafting/recipeCatalog.d.ts +16 -0
- package/dist/crafting/recipeCatalog.js +55 -0
- package/dist/devtools/warnOnce.d.ts +8 -0
- package/dist/devtools/warnOnce.js +18 -0
- package/dist/economy/auctionBook.d.ts +1 -1
- package/dist/economy/auctionBook.js +11 -1
- package/dist/economy/listingBook.d.ts +1 -1
- package/dist/economy/listingBook.js +11 -1
- package/dist/economy/resourceLedger.js +3 -3
- package/dist/economy/shopStock.js +7 -6
- package/dist/economy/techTree.js +7 -6
- package/dist/economy/unlockPoints.js +3 -6
- package/dist/editor/commandApply.js +89 -16
- package/dist/editor/commands.d.ts +38 -1
- package/dist/editor/commands.js +320 -3
- package/dist/editor/creatorStorage.d.ts +59 -0
- package/dist/editor/creatorStorage.js +164 -0
- package/dist/editor/document.d.ts +4 -0
- package/dist/editor/document.js +119 -18
- package/dist/editor/environment.js +8 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/liveSync.d.ts +4 -1
- package/dist/editor/liveSync.js +26 -10
- package/dist/editor/materialAuthoring.d.ts +14 -0
- package/dist/editor/materialAuthoring.js +38 -0
- package/dist/editor/modelNodeVisibility.d.ts +11 -0
- package/dist/editor/modelNodeVisibility.js +29 -0
- package/dist/editor/movementCatalog.d.ts +90 -0
- package/dist/editor/movementCatalog.js +144 -0
- package/dist/editor/simulation.d.ts +60 -0
- package/dist/editor/simulation.js +202 -0
- package/dist/editor/staticPrefab.d.ts +17 -0
- package/dist/editor/staticPrefab.js +51 -0
- package/dist/editor/types.d.ts +15 -0
- package/dist/faction/reputation.d.ts +6 -0
- package/dist/faction/reputation.js +11 -0
- package/dist/game/cameraConfig.d.ts +54 -2
- package/dist/game/clipRoles.d.ts +2 -1
- package/dist/game/clipRoles.js +7 -3
- package/dist/game/contentValidation.d.ts +69 -0
- package/dist/game/contentValidation.js +118 -0
- package/dist/game/controlGate.d.ts +8 -0
- package/dist/game/controlGate.js +43 -6
- package/dist/game/defineGame.d.ts +32 -3
- package/dist/game/dialogueGraph.d.ts +20 -0
- package/dist/game/dialogueGraph.js +107 -0
- package/dist/game/events.d.ts +11 -0
- package/dist/game/keyValueStore.d.ts +17 -4
- package/dist/game/keyValueStore.js +35 -23
- package/dist/game/lootModifiers.d.ts +17 -0
- package/dist/game/lootModifiers.js +26 -0
- package/dist/game/lootTable.d.ts +4 -0
- package/dist/game/lootTable.js +16 -0
- package/dist/game/narrativeAuthoring.d.ts +67 -0
- package/dist/game/narrativeAuthoring.js +122 -0
- package/dist/game/playableGame.d.ts +134 -35
- package/dist/game/quest.d.ts +15 -0
- package/dist/game/quest.js +209 -63
- package/dist/game/questCatalog.d.ts +50 -0
- package/dist/game/questCatalog.js +205 -0
- package/dist/game/saveStore.d.ts +6 -5
- package/dist/game/saveStore.js +57 -57
- package/dist/game/systemRuntime.d.ts +2 -2
- package/dist/game/systemRuntime.js +84 -33
- package/dist/game/toasts.d.ts +8 -0
- package/dist/game/toasts.js +7 -0
- package/dist/game/vfxInstance.d.ts +9 -0
- package/dist/game/vfxInstance.js +12 -1
- package/dist/game/viewports.d.ts +43 -0
- package/dist/game/viewports.js +46 -0
- package/dist/game/worldItem.d.ts +5 -1
- package/dist/game/worldItem.js +0 -1
- package/dist/gameplay.d.ts +3 -0
- package/dist/gameplay.js +3 -0
- package/dist/input/actionBindings.d.ts +15 -1
- package/dist/input/actionBindings.js +14 -3
- package/dist/input/actionContexts.d.ts +25 -1
- package/dist/input/actionContexts.js +82 -25
- package/dist/input/axisInput.d.ts +4 -0
- package/dist/input/axisInput.js +4 -0
- package/dist/input/axisShaper.d.ts +73 -0
- package/dist/input/axisShaper.js +105 -0
- package/dist/input/gamepadModel.d.ts +36 -2
- package/dist/input/gamepadModel.js +69 -30
- package/dist/input/haptics.d.ts +50 -0
- package/dist/input/haptics.js +74 -0
- package/dist/input/inputBuffer.d.ts +40 -0
- package/dist/input/inputBuffer.js +59 -0
- package/dist/input/lookChannel.d.ts +10 -0
- package/dist/input/lookChannel.js +6 -0
- package/dist/input/pointer.d.ts +2 -0
- package/dist/input/pointer.js +4 -0
- package/dist/interaction/promptRegistry.d.ts +5 -4
- package/dist/interaction/promptRegistry.js +8 -2
- package/dist/inventory/inventoryModel.js +9 -6
- package/dist/inventory/shapedGrid.d.ts +48 -9
- package/dist/inventory/shapedGrid.js +69 -10
- package/dist/item/affix.d.ts +6 -0
- package/dist/item/affix.js +6 -0
- package/dist/item/durability.js +4 -0
- package/dist/item/itemInstanceRegistry.d.ts +32 -1
- package/dist/item/itemInstanceRegistry.js +111 -7
- package/dist/item/itemgen.d.ts +1 -1
- package/dist/item/itemgen.js +1 -1
- package/dist/item/modularItem.js +7 -1
- package/dist/material/appearanceSignals.d.ts +53 -0
- package/dist/material/appearanceSignals.js +47 -0
- package/dist/material/hairCards.d.ts +47 -0
- package/dist/material/hairCards.js +136 -0
- package/dist/material/materialAsset.d.ts +162 -0
- package/dist/material/materialAsset.js +382 -0
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +444 -1
- package/dist/movement/characterController.d.ts +5 -2
- package/dist/movement/characterController.js +50 -23
- package/dist/movement/freeFlight.d.ts +2 -2
- package/dist/movement/freeFlight.js +3 -3
- package/dist/movement/glideModel.d.ts +3 -0
- package/dist/movement/glideModel.js +6 -0
- package/dist/movement/grappleSwing.d.ts +2 -0
- package/dist/movement/grappleSwing.js +5 -1
- package/dist/movement/movementModel.d.ts +63 -1
- package/dist/movement/movementModel.js +136 -26
- package/dist/movement/movementProbe.d.ts +51 -0
- package/dist/movement/movementProbe.js +119 -0
- package/dist/movement/playerMovement.d.ts +48 -2
- package/dist/movement/playerMovement.js +493 -87
- package/dist/movement/poseState.d.ts +2 -0
- package/dist/movement/poseState.js +29 -1
- package/dist/movement/solidObstacles.d.ts +11 -1
- package/dist/movement/solidObstacles.js +48 -3
- package/dist/movement/terrainGrade.d.ts +13 -0
- package/dist/movement/terrainGrade.js +33 -0
- package/dist/movement/voxelController.d.ts +2 -2
- package/dist/movement/voxelController.js +10 -10
- package/dist/nav/navFromEnvironment.d.ts +9 -3
- package/dist/nav/navFromEnvironment.js +18 -12
- package/dist/nav/navGrid.d.ts +19 -1
- package/dist/nav/navGrid.js +26 -10
- package/dist/nav/navMesh.d.ts +43 -2
- package/dist/nav/navMesh.js +534 -128
- package/dist/nav/solidRoute.d.ts +50 -0
- package/dist/nav/solidRoute.js +144 -0
- package/dist/physics/aircraftDynamics.d.ts +390 -0
- package/dist/physics/aircraftDynamics.js +704 -0
- package/dist/physics/ballisticSweep.d.ts +10 -3
- package/dist/physics/ballisticSweep.js +53 -29
- package/dist/physics/boatDynamics.d.ts +116 -0
- package/dist/physics/boatDynamics.js +160 -0
- package/dist/physics/drivableVehicle.d.ts +22 -8
- package/dist/physics/drivableVehicle.js +4 -2
- package/dist/physics/flightDynamics.d.ts +21 -4
- package/dist/physics/flightDynamics.js +28 -5
- package/dist/physics/forceVolume.d.ts +41 -0
- package/dist/physics/forceVolume.js +88 -0
- package/dist/physics/handlingProbe.d.ts +249 -0
- package/dist/physics/handlingProbe.js +606 -0
- package/dist/physics/kinematicVehicle.d.ts +40 -5
- package/dist/physics/kinematicVehicle.js +17 -2
- package/dist/physics/physicsBackend.d.ts +12 -0
- package/dist/physics/physicsBackendConformance.js +34 -0
- package/dist/physics/physicsWorldBackend.d.ts +1 -0
- package/dist/physics/physicsWorldBackend.js +52 -3
- package/dist/physics/spatialGrid.d.ts +1 -1
- package/dist/physics/spatialGrid.js +8 -9
- package/dist/physics/vehicleBackendLink.d.ts +49 -0
- package/dist/physics/vehicleBackendLink.js +102 -0
- package/dist/physics/vehicleDynamics.d.ts +375 -0
- package/dist/physics/vehicleDynamics.js +737 -0
- package/dist/physics/vehicleObstacles.d.ts +3 -2
- package/dist/physics/vehicleObstacles.js +29 -5
- package/dist/physics/worldColliders.d.ts +1 -1
- package/dist/physics/worldColliders.js +35 -3
- package/dist/puzzle/cellGrid.d.ts +15 -9
- package/dist/puzzle/cellGrid.js +13 -9
- package/dist/render/lookPreset.d.ts +9 -2
- package/dist/render/lookPreset.js +29 -2
- package/dist/render/postProcessing.d.ts +30 -0
- package/dist/render/postProcessing.js +1 -1
- package/dist/runtime/cameraDirector.d.ts +54 -3
- package/dist/runtime/cameraDirector.js +187 -0
- package/dist/runtime/commandDispatch.d.ts +14 -0
- package/dist/runtime/commandDispatch.js +19 -0
- package/dist/runtime/commandInput.d.ts +37 -0
- package/dist/runtime/commandInput.js +58 -0
- package/dist/runtime/commandRunner.d.ts +27 -0
- package/dist/runtime/commandRunner.js +31 -3
- package/dist/runtime/context/combat.d.ts +12 -4
- package/dist/runtime/context/combat.js +179 -11
- package/dist/runtime/context/deathLoot.d.ts +11 -8
- package/dist/runtime/context/deathLoot.js +17 -16
- package/dist/runtime/context/scene.d.ts +2 -0
- package/dist/runtime/context/scene.js +13 -21
- package/dist/runtime/descriptors/baseline.js +34 -3
- package/dist/runtime/descriptors/features.d.ts +2 -1
- package/dist/runtime/descriptors/features.js +18 -0
- package/dist/runtime/gameContext.js +86 -13
- package/dist/runtime/gameContextTypes.d.ts +33 -3
- package/dist/runtime/gameRuntime.d.ts +3 -1
- package/dist/runtime/gameRuntime.js +4 -0
- package/dist/runtime/headlessRunner.d.ts +3 -1
- package/dist/runtime/headlessRunner.js +11 -11
- package/dist/runtime/hostedGameRunner.d.ts +24 -1
- package/dist/runtime/hostedGameRunner.js +156 -10
- package/dist/runtime/hostedWorldSession.d.ts +17 -5
- package/dist/runtime/hostedWorldSession.js +154 -16
- package/dist/runtime/hostedWorldStore.d.ts +2 -2
- package/dist/runtime/inputRecorder.d.ts +1 -1
- package/dist/runtime/inputRecorder.js +21 -1
- package/dist/runtime/inputSnapshot.d.ts +22 -4
- package/dist/runtime/inputSnapshot.js +41 -3
- package/dist/runtime/localPlayers.d.ts +73 -0
- package/dist/runtime/localPlayers.js +119 -0
- package/dist/runtime/runtimeSave.d.ts +10 -6
- package/dist/runtime/runtimeSave.js +11 -5
- package/dist/runtime/simContext.d.ts +8 -3
- package/dist/runtime/simContext.js +7 -3
- package/dist/runtime/simSnapshot.js +2 -0
- package/dist/runtime/transport.d.ts +9 -0
- package/dist/runtime/transport.js +7 -1
- package/dist/runtime/worldReplication.d.ts +2 -0
- package/dist/runtime/worldReplication.js +1 -1
- package/dist/scene/assetCatalog.d.ts +2 -0
- package/dist/scene/behaviorRuntime.d.ts +38 -7
- package/dist/scene/behaviorRuntime.js +293 -30
- package/dist/scene/behaviors.d.ts +35 -2
- package/dist/scene/behaviors.js +29 -0
- package/dist/scene/entityStore.d.ts +14 -3
- package/dist/scene/entityStore.js +45 -9
- package/dist/scene/mount.d.ts +9 -0
- package/dist/scene/mount.js +35 -0
- package/dist/scene/possession.d.ts +2 -0
- package/dist/scene/possession.js +21 -1
- package/dist/scene/sceneKinds.d.ts +9 -0
- package/dist/scene/sceneRaycast.d.ts +7 -0
- package/dist/scene/sceneRaycast.js +55 -0
- package/dist/scene/spatial.d.ts +20 -1
- package/dist/scene/spatial.js +94 -2
- package/dist/scene/vehicleSeat.d.ts +41 -3
- package/dist/scene/vehicleSeat.js +94 -3
- package/dist/sensor/concealment.d.ts +6 -0
- package/dist/sensor/concealment.js +8 -0
- package/dist/sensor/freezeMonitor.d.ts +6 -0
- package/dist/sensor/freezeMonitor.js +8 -0
- package/dist/sensor/perception.d.ts +11 -1
- package/dist/sensor/perception.js +57 -10
- package/dist/sensor/recordingBuffer.d.ts +3 -0
- package/dist/sensor/recordingBuffer.js +8 -0
- package/dist/session/roundState.d.ts +4 -0
- package/dist/session/roundState.js +11 -0
- package/dist/settings/frameRateLimit.d.ts +16 -0
- package/dist/settings/frameRateLimit.js +37 -0
- package/dist/settings/graphicsProfile.d.ts +2 -0
- package/dist/settings/graphicsProfile.js +12 -0
- package/dist/settings/graphicsSettings.d.ts +40 -0
- package/dist/settings/graphicsSettings.js +81 -0
- package/dist/settings/settingsModel.d.ts +9 -1
- package/dist/settings/settingsModel.js +10 -0
- package/dist/stats/accumulatorMeter.d.ts +10 -0
- package/dist/stats/accumulatorMeter.js +6 -0
- package/dist/stats/eventMeter.d.ts +10 -3
- package/dist/stats/eventMeter.js +6 -3
- package/dist/store/defineStore.d.ts +3 -1
- package/dist/store/defineStore.js +3 -1
- package/dist/store/observableKeyedStore.d.ts +1 -1
- package/dist/store/observableKeyedStore.js +8 -6
- package/dist/time/simClock.d.ts +6 -2
- package/dist/time/simClock.js +5 -0
- package/dist/vfx/feedbackMixer.d.ts +61 -0
- package/dist/vfx/feedbackMixer.js +106 -0
- package/dist/vfx/particleDirector.d.ts +58 -6
- package/dist/vfx/particleDirector.js +113 -22
- package/dist/vfx/particles.d.ts +68 -5
- package/dist/vfx/particles.js +284 -32
- package/dist/visibility/assetStreaming.d.ts +21 -3
- package/dist/visibility/assetStreaming.js +108 -24
- package/dist/visibility/settings.d.ts +8 -2
- package/dist/visibility/settings.js +3 -0
- package/dist/visibility/simulationCulling.d.ts +8 -1
- package/dist/visibility/simulationCulling.js +7 -0
- package/dist/visibility/spatialIndex.d.ts +21 -0
- package/dist/visibility/spatialIndex.js +16 -0
- package/dist/visibility/visibilitySystem.js +1 -0
- package/dist/work/expedition.d.ts +110 -0
- package/dist/work/expedition.js +195 -0
- package/dist/work/staffedStation.d.ts +29 -0
- package/dist/work/staffedStation.js +27 -0
- package/dist/world/authoredAnimation.d.ts +38 -0
- package/dist/world/authoredAnimation.js +99 -0
- package/dist/world/authoredObjects.d.ts +24 -4
- package/dist/world/authoredObjects.js +43 -16
- package/dist/world/authoredSimulation.d.ts +74 -0
- package/dist/world/authoredSimulation.js +302 -0
- package/dist/world/authoredSolids.d.ts +46 -0
- package/dist/world/authoredSolids.js +75 -0
- package/dist/world/authoredWeather.d.ts +120 -0
- package/dist/world/authoredWeather.js +314 -0
- package/dist/world/buildingGenerator.js +2 -2
- package/dist/world/buildingKit.d.ts +11 -4
- package/dist/world/buildingKit.js +21 -8
- package/dist/world/buildings.d.ts +34 -2
- package/dist/world/buildings.js +11 -1
- package/dist/world/cityKind.d.ts +7 -0
- package/dist/world/cityKind.js +52 -1
- package/dist/world/environmentForces.d.ts +21 -0
- package/dist/world/environmentForces.js +42 -0
- package/dist/world/features.d.ts +32 -5
- package/dist/world/features.js +6 -0
- package/dist/world/footprintGrid.d.ts +62 -0
- package/dist/world/footprintGrid.js +217 -0
- package/dist/world/interiors.d.ts +1 -0
- package/dist/world/interiors.js +5 -3
- package/dist/world/lod.d.ts +6 -0
- package/dist/world/lod.js +8 -0
- package/dist/world/pathTerrain.d.ts +1 -1
- package/dist/world/terraform.d.ts +9 -1
- package/dist/world/terraform.js +53 -18
- package/dist/world/walls.d.ts +7 -0
- package/dist/world/walls.js +7 -0
- package/dist/world/weather.d.ts +17 -0
- package/dist/world/weather.js +116 -20
- package/dist/world/worldSolids.d.ts +95 -0
- package/dist/world/worldSolids.js +351 -0
- package/dist/world.d.ts +6 -6
- package/dist/world.js +5 -5
- package/package.json +1 -1
- package/skills/ce-handoff/SKILL.md +2 -2
- package/skills/game-design/SKILL.md +2 -2
- package/skills/jgengine/SKILL.md +28 -6
- package/skills/jgengine/api.md +139 -99
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine/recipes/minimal-game.md +10 -2
- package/skills/jgengine-combat/SKILL.md +13 -2
- package/skills/jgengine-combat/api.md +150 -103
- package/skills/jgengine-combat/capabilities.md +29 -1
- package/skills/jgengine-combat/recipes/portable-runtime-state.md +60 -6
- package/skills/jgengine-combat/recipes/portable-weapon-plumbing.md +37 -3
- package/skills/jgengine-combat/recipes/weapon-feel.md +50 -0
- package/skills/jgengine-combat/reference.md +24 -2
- package/skills/jgengine-gameplay/SKILL.md +63 -0
- package/skills/jgengine-gameplay/api.md +583 -463
- package/skills/jgengine-gameplay/capabilities.md +92 -0
- package/skills/jgengine-gameplay/recipes/device-save-status.md +46 -0
- package/skills/jgengine-gameplay/recipes/expeditions.md +108 -0
- package/skills/jgengine-gameplay/recipes/narrative-authoring.md +76 -0
- package/skills/jgengine-gameplay/recipes/paged-item-storage.md +101 -0
- package/skills/jgengine-gameplay/recipes/quest-authoring.md +63 -0
- package/skills/jgengine-gameplay/recipes/shaped-inventory.md +50 -0
- package/skills/jgengine-gameplay/reference-content-validation.md +74 -0
- package/skills/jgengine-gameplay/reference-systems.md +123 -0
- package/skills/jgengine-world/SKILL.md +25 -4
- package/skills/jgengine-world/api.md +1045 -655
- package/skills/jgengine-world/capabilities.md +246 -9
- package/skills/jgengine-world/recipes/character-animation.md +62 -0
- package/skills/jgengine-world/recipes/controls.md +48 -0
- package/skills/jgengine-world/recipes/flight-feel.md +76 -0
- package/skills/jgengine-world/recipes/npc-senses.md +55 -0
- package/skills/jgengine-world/recipes/vehicle-feel.md +87 -0
- package/skills/jgengine-world/reference.md +180 -4
- package/skills/jgengine-world/visibility.md +139 -0
package/dist/editor/document.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { decodeEditorSimulation } from "./simulation.js";
|
|
2
|
+
import { parseMaterialAssignments, validateMaterialAsset, validateMaterialAssignments } from "../material/materialAsset.js";
|
|
3
|
+
import { authoredHiddenNodes } from "./modelNodeVisibility.js";
|
|
4
|
+
import { parseStaticPrefabBake } from "./staticPrefab.js";
|
|
1
5
|
import { cloneEditorUiDocument, decodeEditorUiDocument } from "../ui/hudDocument.js";
|
|
2
6
|
import { migrateTerrainSnapshot } from "../world/terraform.js";
|
|
3
7
|
import { cloneGridLayer, migrateGridLayer, } from "./grid.js";
|
|
@@ -19,6 +23,8 @@ function cloneEnvironment(environment) {
|
|
|
19
23
|
...(environment.zenithColor === undefined ? {} : { zenithColor: environment.zenithColor }),
|
|
20
24
|
...(environment.sunIntensity === undefined ? {} : { sunIntensity: environment.sunIntensity }),
|
|
21
25
|
...(environment.ambientIntensity === undefined ? {} : { ambientIntensity: environment.ambientIntensity }),
|
|
26
|
+
...(environment.sunAzimuth === undefined ? {} : { sunAzimuth: environment.sunAzimuth }),
|
|
27
|
+
...(environment.sunElevation === undefined ? {} : { sunElevation: environment.sunElevation }),
|
|
22
28
|
...(environment.fog === undefined ? {} : { fog: { ...environment.fog } }),
|
|
23
29
|
};
|
|
24
30
|
}
|
|
@@ -34,7 +40,7 @@ function cloneDirective(directive) {
|
|
|
34
40
|
...(directive.region === undefined ? {} : { region: directive.region }),
|
|
35
41
|
...(area === undefined ? {} : { area }),
|
|
36
42
|
...(directive.seed === undefined ? {} : { seed: directive.seed }),
|
|
37
|
-
...(directive.meta === undefined ? {} : { meta:
|
|
43
|
+
...(directive.meta === undefined ? {} : { meta: structuredClone(directive.meta) }),
|
|
38
44
|
};
|
|
39
45
|
if (directive.kind === "scatter") {
|
|
40
46
|
return { ...base, kind: "scatter", asset: directive.asset, density: directive.density,
|
|
@@ -66,6 +72,8 @@ function upsertGrids(base, overlay) {
|
|
|
66
72
|
/** Spread-ready non-placeable fields preserved across document rebuilds. @internal */
|
|
67
73
|
export function editorDocumentExtras(doc) {
|
|
68
74
|
return {
|
|
75
|
+
...(doc.simulation === undefined ? {} : { simulation: structuredClone(doc.simulation) }),
|
|
76
|
+
...(doc.materialAssets === undefined ? {} : { materialAssets: doc.materialAssets }),
|
|
69
77
|
prefabs: doc.prefabs,
|
|
70
78
|
collections: doc.collections,
|
|
71
79
|
catalogs: doc.catalogs,
|
|
@@ -97,14 +105,14 @@ function cloneCatalogEntry(entry) {
|
|
|
97
105
|
return {
|
|
98
106
|
id: entry.id,
|
|
99
107
|
...(entry.label === undefined ? {} : { label: entry.label }),
|
|
100
|
-
...(entry.meta === undefined ? {} : { meta:
|
|
108
|
+
...(entry.meta === undefined ? {} : { meta: structuredClone(entry.meta) }),
|
|
101
109
|
};
|
|
102
110
|
}
|
|
103
111
|
function cloneCatalogs(catalogs) {
|
|
104
112
|
return (catalogs ?? []).map((catalog) => ({
|
|
105
113
|
id: catalog.id,
|
|
106
114
|
...(catalog.label === undefined ? {} : { label: catalog.label }),
|
|
107
|
-
...(catalog.schema === undefined ? {} : { schema: catalog.schema }),
|
|
115
|
+
...(catalog.schema === undefined ? {} : { schema: structuredClone(catalog.schema) }),
|
|
108
116
|
entries: catalog.entries.map(cloneCatalogEntry),
|
|
109
117
|
}));
|
|
110
118
|
}
|
|
@@ -119,38 +127,36 @@ export function cloneEditorDocument(doc) {
|
|
|
119
127
|
const directives = cloneDirectives(doc.directives);
|
|
120
128
|
return {
|
|
121
129
|
version: 1,
|
|
130
|
+
...(doc.simulation === undefined ? {} : { simulation: structuredClone(doc.simulation) }),
|
|
122
131
|
markers: doc.markers.map((marker) => ({
|
|
123
132
|
...marker,
|
|
124
133
|
position: { ...marker.position },
|
|
125
|
-
...(marker.meta === undefined ? {} : { meta:
|
|
134
|
+
...(marker.meta === undefined ? {} : { meta: structuredClone(marker.meta) }),
|
|
126
135
|
})),
|
|
127
136
|
volumes: doc.volumes.map((volume) => ({
|
|
128
137
|
...volume,
|
|
129
138
|
center: { ...volume.center },
|
|
130
139
|
...(volume.halfExtents === undefined ? {} : { halfExtents: { ...volume.halfExtents } }),
|
|
131
|
-
...(volume.meta === undefined ? {} : { meta:
|
|
140
|
+
...(volume.meta === undefined ? {} : { meta: structuredClone(volume.meta) }),
|
|
132
141
|
})),
|
|
133
142
|
paths: doc.paths.map((path) => ({
|
|
134
143
|
...path,
|
|
135
144
|
points: path.points.map((point) => ({ ...point })),
|
|
136
|
-
...(path.meta === undefined ? {} : { meta:
|
|
145
|
+
...(path.meta === undefined ? {} : { meta: structuredClone(path.meta) }),
|
|
137
146
|
})),
|
|
138
147
|
annotations: doc.annotations.map((note) => ({
|
|
139
148
|
...note,
|
|
140
149
|
position: { ...note.position },
|
|
141
|
-
...(note.meta === undefined ? {} : { meta:
|
|
150
|
+
...(note.meta === undefined ? {} : { meta: structuredClone(note.meta) }),
|
|
142
151
|
})),
|
|
143
152
|
prefabs: doc.prefabs.map((prefab) => ({
|
|
144
153
|
...prefab,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
volumes: prefab.fragment.volumes.map((volume) => ({ ...volume, center: { ...volume.center } })),
|
|
148
|
-
paths: prefab.fragment.paths.map((path) => ({ ...path, points: path.points.map((point) => ({ ...point })) })),
|
|
149
|
-
annotations: prefab.fragment.annotations.map((note) => ({ ...note, position: { ...note.position } })),
|
|
150
|
-
},
|
|
154
|
+
...(prefab.staticBake === undefined ? {} : { staticBake: parseStaticPrefabBake(prefab.staticBake) }),
|
|
155
|
+
fragment: structuredClone(prefab.fragment),
|
|
151
156
|
})),
|
|
152
157
|
collections: doc.collections.map((collection) => ({ ...collection, memberIds: [...collection.memberIds] })),
|
|
153
158
|
catalogs: cloneCatalogs(doc.catalogs),
|
|
159
|
+
...(doc.materialAssets === undefined ? {} : { materialAssets: structuredClone(doc.materialAssets) }),
|
|
154
160
|
...(doc.grids === undefined ? {} : { grids: doc.grids.map(cloneGridLayer) }),
|
|
155
161
|
...(doc.terrain === undefined ? {} : { terrain: doc.terrain }),
|
|
156
162
|
...(ui === undefined ? {} : { ui }),
|
|
@@ -172,7 +178,7 @@ function upsertCatalogs(base, overlay) {
|
|
|
172
178
|
{
|
|
173
179
|
id: catalog.id,
|
|
174
180
|
...(catalog.label === undefined ? {} : { label: catalog.label }),
|
|
175
|
-
...(catalog.schema === undefined ? {} : { schema: catalog.schema }),
|
|
181
|
+
...(catalog.schema === undefined ? {} : { schema: structuredClone(catalog.schema) }),
|
|
176
182
|
entries: catalog.entries.map(cloneCatalogEntry),
|
|
177
183
|
},
|
|
178
184
|
]));
|
|
@@ -202,6 +208,7 @@ export function normalizeEditorLayers(input) {
|
|
|
202
208
|
const directives = cloneDirectives(resolved.directives);
|
|
203
209
|
return {
|
|
204
210
|
version: 1,
|
|
211
|
+
...(resolved.simulation === undefined ? {} : { simulation: decodeEditorSimulation(resolved.simulation, { paths: asArray(resolved.paths) }) }),
|
|
205
212
|
markers: asArray(resolved.markers),
|
|
206
213
|
volumes: asArray(resolved.volumes),
|
|
207
214
|
paths: asArray(resolved.paths),
|
|
@@ -209,6 +216,7 @@ export function normalizeEditorLayers(input) {
|
|
|
209
216
|
prefabs: asArray(resolved.prefabs),
|
|
210
217
|
collections: asArray(resolved.collections),
|
|
211
218
|
catalogs: cloneCatalogs(resolved.catalogs),
|
|
219
|
+
...(resolved.materialAssets === undefined ? {} : { materialAssets: structuredClone(resolved.materialAssets) }),
|
|
212
220
|
...(grids === undefined ? {} : { grids }),
|
|
213
221
|
...(resolved.terrain === undefined ? {} : { terrain: migrateTerrainSnapshot(resolved.terrain) }),
|
|
214
222
|
...(ui === undefined ? {} : { ui }),
|
|
@@ -260,6 +268,8 @@ export function mergeEditorDocuments(...docs) {
|
|
|
260
268
|
out.prefabs.push(...doc.prefabs);
|
|
261
269
|
out.collections.push(...doc.collections);
|
|
262
270
|
out.catalogs = upsertCatalogs(out.catalogs, doc.catalogs);
|
|
271
|
+
if (doc.materialAssets !== undefined)
|
|
272
|
+
out.materialAssets = structuredClone(upsertById(out.materialAssets ?? [], doc.materialAssets));
|
|
263
273
|
const mergedGrids = upsertGrids(out.grids, doc.grids);
|
|
264
274
|
if (mergedGrids !== undefined)
|
|
265
275
|
out.grids = mergedGrids;
|
|
@@ -269,6 +279,8 @@ export function mergeEditorDocuments(...docs) {
|
|
|
269
279
|
out.minimap = cloneMinimapBake(doc.minimap);
|
|
270
280
|
if (doc.environment !== undefined)
|
|
271
281
|
out.environment = cloneEnvironment(doc.environment);
|
|
282
|
+
if (doc.simulation !== undefined)
|
|
283
|
+
out.simulation = structuredClone(doc.simulation);
|
|
272
284
|
if (doc.directives !== undefined) {
|
|
273
285
|
out.directives = upsertById(out.directives ?? [], doc.directives);
|
|
274
286
|
}
|
|
@@ -290,6 +302,7 @@ export function extractEditorFragment(doc, ids) {
|
|
|
290
302
|
const wanted = new Set(ids);
|
|
291
303
|
return cloneEditorDocument({
|
|
292
304
|
version: 1,
|
|
305
|
+
...(doc.materialAssets === undefined ? {} : { materialAssets: doc.materialAssets.filter((asset) => doc.markers.some((marker) => wanted.has(marker.id) && parseMaterialAssignments(marker.meta?.materialAssignments ?? []).some((item) => item.materialId === asset.id))) }),
|
|
293
306
|
markers: doc.markers.filter((marker) => wanted.has(marker.id)),
|
|
294
307
|
volumes: doc.volumes.filter((volume) => wanted.has(volume.id)),
|
|
295
308
|
paths: doc.paths.filter((path) => wanted.has(path.id)),
|
|
@@ -433,6 +446,7 @@ export function createPrefabFragment(doc, ids) {
|
|
|
433
446
|
z: point.z - origin.z,
|
|
434
447
|
});
|
|
435
448
|
return {
|
|
449
|
+
...(extracted.materialAssets === undefined ? {} : { materialAssets: extracted.materialAssets }),
|
|
436
450
|
markers: extracted.markers.map((marker) => ({ ...marker, position: shift(marker.position) })),
|
|
437
451
|
volumes: extracted.volumes.map((volume) => ({ ...volume, center: shift(volume.center) })),
|
|
438
452
|
paths: extracted.paths.map((path) => ({ ...path, points: path.points.map(shift) })),
|
|
@@ -606,7 +620,21 @@ function decodeMeta(value, path, errors) {
|
|
|
606
620
|
errors.push({ path, message: "expected an object" });
|
|
607
621
|
return undefined;
|
|
608
622
|
}
|
|
609
|
-
|
|
623
|
+
if (value.materialAssignments !== undefined) {
|
|
624
|
+
try {
|
|
625
|
+
parseMaterialAssignments(value.materialAssignments);
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
errors.push({ path: `${path}.materialAssignments`, message: String(error) });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
authoredHiddenNodes(value);
|
|
633
|
+
}
|
|
634
|
+
catch (error) {
|
|
635
|
+
errors.push({ path: `${path}.hiddenNodes`, message: String(error) });
|
|
636
|
+
}
|
|
637
|
+
return structuredClone(value);
|
|
610
638
|
}
|
|
611
639
|
function decodeMarker(item, path, errors) {
|
|
612
640
|
if (!isPlainObject(item)) {
|
|
@@ -742,18 +770,55 @@ function decodeNote(item, path, errors) {
|
|
|
742
770
|
note.meta = meta;
|
|
743
771
|
return note;
|
|
744
772
|
}
|
|
773
|
+
function decodeMaterialAssets(value, path, errors) {
|
|
774
|
+
if (!Array.isArray(value)) {
|
|
775
|
+
errors.push({ path, message: "expected material assets" });
|
|
776
|
+
return [];
|
|
777
|
+
}
|
|
778
|
+
const ids = new Set();
|
|
779
|
+
const assets = [];
|
|
780
|
+
value.forEach((asset, index) => {
|
|
781
|
+
const diagnostics = validateMaterialAsset(asset).filter((diagnostic) => diagnostic.severity === "error");
|
|
782
|
+
for (const diagnostic of diagnostics)
|
|
783
|
+
errors.push({ path: `${path}[${index}].${diagnostic.path}`, message: diagnostic.message });
|
|
784
|
+
if (diagnostics.length === 0) {
|
|
785
|
+
if (ids.has(asset.id))
|
|
786
|
+
errors.push({ path: `${path}[${index}].id`, message: "duplicate material ID" });
|
|
787
|
+
ids.add(asset.id);
|
|
788
|
+
assets.push(structuredClone(asset));
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
return assets;
|
|
792
|
+
}
|
|
745
793
|
function decodeFragmentContent(value, path, errors) {
|
|
746
794
|
if (!isPlainObject(value)) {
|
|
747
795
|
errors.push({ path, message: "expected an object" });
|
|
748
796
|
return null;
|
|
749
797
|
}
|
|
798
|
+
const materialAssets = value.materialAssets === undefined ? undefined : decodeMaterialAssets(value.materialAssets, `${path}.materialAssets`, errors);
|
|
799
|
+
const markers = decodeArray(value.markers, `${path}.markers`, decodeMarker, errors);
|
|
800
|
+
validateMarkerMaterialReferences(markers, materialAssets ?? [], path, errors);
|
|
750
801
|
return {
|
|
751
|
-
|
|
802
|
+
...(materialAssets === undefined ? {} : { materialAssets }),
|
|
803
|
+
markers,
|
|
752
804
|
volumes: decodeArray(value.volumes, `${path}.volumes`, decodeVolume, errors),
|
|
753
805
|
paths: decodeArray(value.paths, `${path}.paths`, decodePath, errors),
|
|
754
806
|
annotations: decodeArray(value.annotations, `${path}.annotations`, decodeNote, errors),
|
|
755
807
|
};
|
|
756
808
|
}
|
|
809
|
+
function validateMarkerMaterialReferences(markers, assets, path, errors) {
|
|
810
|
+
for (const [index, marker] of markers.entries()) {
|
|
811
|
+
if (marker.meta?.materialAssignments === undefined)
|
|
812
|
+
continue;
|
|
813
|
+
try {
|
|
814
|
+
for (const diagnostic of validateMaterialAssignments(parseMaterialAssignments(marker.meta.materialAssignments), assets)) {
|
|
815
|
+
if (diagnostic.severity === "error")
|
|
816
|
+
errors.push({ path: `${path}.markers[${index}].meta.${diagnostic.path}`, message: diagnostic.message });
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
catch { }
|
|
820
|
+
}
|
|
821
|
+
}
|
|
757
822
|
function decodePrefab(item, path, errors) {
|
|
758
823
|
if (!isPlainObject(item)) {
|
|
759
824
|
errors.push({ path, message: "expected an object" });
|
|
@@ -766,7 +831,16 @@ function decodePrefab(item, path, errors) {
|
|
|
766
831
|
const fragment = decodeFragmentContent(item.fragment, `${path}.fragment`, errors);
|
|
767
832
|
if (typeof item.id !== "string" || typeof item.name !== "string" || fragment === null)
|
|
768
833
|
return null;
|
|
769
|
-
|
|
834
|
+
const prefab = { id: item.id, name: item.name, fragment };
|
|
835
|
+
if (item.staticBake !== undefined) {
|
|
836
|
+
try {
|
|
837
|
+
prefab.staticBake = parseStaticPrefabBake(item.staticBake);
|
|
838
|
+
}
|
|
839
|
+
catch (error) {
|
|
840
|
+
errors.push({ path: `${path}.staticBake`, message: error instanceof Error ? error.message : String(error) });
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return prefab;
|
|
770
844
|
}
|
|
771
845
|
function decodeCollection(item, path, errors) {
|
|
772
846
|
if (!isPlainObject(item)) {
|
|
@@ -1082,7 +1156,7 @@ function decodeEnvironment(value, path, errors) {
|
|
|
1082
1156
|
failed = true;
|
|
1083
1157
|
}
|
|
1084
1158
|
}
|
|
1085
|
-
for (const key of ["sunIntensity", "ambientIntensity"]) {
|
|
1159
|
+
for (const key of ["sunIntensity", "ambientIntensity", "sunAzimuth", "sunElevation"]) {
|
|
1086
1160
|
if (value[key] === undefined)
|
|
1087
1161
|
continue;
|
|
1088
1162
|
if (typeof value[key] === "number" && Number.isFinite(value[key]))
|
|
@@ -1226,6 +1300,7 @@ export function decodeEditorDocument(raw) {
|
|
|
1226
1300
|
return { ok: false, errors: [{ path: "$", message: "editor document must be an object" }] };
|
|
1227
1301
|
}
|
|
1228
1302
|
const errors = [];
|
|
1303
|
+
const materialAssets = raw.materialAssets === undefined ? undefined : decodeMaterialAssets(raw.materialAssets, "$.materialAssets", errors);
|
|
1229
1304
|
const markers = decodeArray(raw.markers, "$.markers", decodeMarker, errors);
|
|
1230
1305
|
const volumes = decodeArray(raw.volumes, "$.volumes", decodeVolume, errors);
|
|
1231
1306
|
const paths = decodeArray(raw.paths, "$.paths", decodePath, errors);
|
|
@@ -1241,6 +1316,15 @@ export function decodeEditorDocument(raw) {
|
|
|
1241
1316
|
const minimap = raw.minimap === undefined ? undefined : decodeMinimapBake(raw.minimap, "$.minimap", errors);
|
|
1242
1317
|
const bakes = raw.bakes === undefined ? undefined : decodeBakes(raw.bakes, "$.bakes", errors);
|
|
1243
1318
|
const environment = raw.environment === undefined ? undefined : decodeEnvironment(raw.environment, "$.environment", errors);
|
|
1319
|
+
let simulation;
|
|
1320
|
+
if (raw.simulation !== undefined) {
|
|
1321
|
+
try {
|
|
1322
|
+
simulation = decodeEditorSimulation(raw.simulation, { paths });
|
|
1323
|
+
}
|
|
1324
|
+
catch (error) {
|
|
1325
|
+
errors.push({ path: "$.simulation", message: error instanceof Error ? error.message : String(error) });
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1244
1328
|
// Placeable object ids form one document-global namespace (selection, parenting, and removal all
|
|
1245
1329
|
// treat them that way), so a document that reuses an id — even across two different collections —
|
|
1246
1330
|
// is malformed and rejected here with the offending path, rather than silently loading a scene
|
|
@@ -1262,6 +1346,7 @@ export function decodeEditorDocument(raw) {
|
|
|
1262
1346
|
}
|
|
1263
1347
|
});
|
|
1264
1348
|
}
|
|
1349
|
+
validateMarkerMaterialReferences(markers, materialAssets ?? [], "$", errors);
|
|
1265
1350
|
if (errors.length > 0)
|
|
1266
1351
|
return { ok: false, errors };
|
|
1267
1352
|
const terrain = raw.terrain === undefined ? undefined : migrateTerrainSnapshot(raw.terrain);
|
|
@@ -1270,6 +1355,8 @@ export function decodeEditorDocument(raw) {
|
|
|
1270
1355
|
ok: true,
|
|
1271
1356
|
document: {
|
|
1272
1357
|
version: 1,
|
|
1358
|
+
...(simulation === undefined ? {} : { simulation }),
|
|
1359
|
+
...(materialAssets === undefined ? {} : { materialAssets }),
|
|
1273
1360
|
markers,
|
|
1274
1361
|
volumes,
|
|
1275
1362
|
paths,
|
|
@@ -1313,6 +1400,7 @@ export function applyEditorDocumentOverlay(base, overlay) {
|
|
|
1313
1400
|
const terrain = overlay.terrain ?? base.terrain;
|
|
1314
1401
|
const minimap = overlay.minimap ?? base.minimap;
|
|
1315
1402
|
const environment = overlay.environment ?? base.environment;
|
|
1403
|
+
const simulation = overlay.simulation ?? base.simulation;
|
|
1316
1404
|
const ui = overlay.ui === undefined
|
|
1317
1405
|
? cloneEditorUiDocument(base.ui)
|
|
1318
1406
|
: {
|
|
@@ -1324,6 +1412,7 @@ export function applyEditorDocumentOverlay(base, overlay) {
|
|
|
1324
1412
|
const grids = upsertGrids(base.grids, overlay.grids);
|
|
1325
1413
|
return {
|
|
1326
1414
|
version: 1,
|
|
1415
|
+
...(simulation === undefined ? {} : { simulation: structuredClone(simulation) }),
|
|
1327
1416
|
markers: upsertById(base.markers, overlay.markers),
|
|
1328
1417
|
volumes: upsertById(base.volumes, overlay.volumes),
|
|
1329
1418
|
paths: upsertById(base.paths, overlay.paths),
|
|
@@ -1331,6 +1420,7 @@ export function applyEditorDocumentOverlay(base, overlay) {
|
|
|
1331
1420
|
prefabs: upsertById(base.prefabs, overlay.prefabs),
|
|
1332
1421
|
collections: upsertById(base.collections, overlay.collections),
|
|
1333
1422
|
catalogs: upsertCatalogs(base.catalogs, overlay.catalogs),
|
|
1423
|
+
...(base.materialAssets === undefined && overlay.materialAssets === undefined ? {} : { materialAssets: structuredClone(upsertById(base.materialAssets ?? [], overlay.materialAssets ?? [])) }),
|
|
1334
1424
|
...(grids === undefined ? {} : { grids }),
|
|
1335
1425
|
...(terrain === undefined ? {} : { terrain }),
|
|
1336
1426
|
...(ui === undefined ? {} : { ui }),
|
|
@@ -1380,6 +1470,17 @@ export function editorDocumentBounds(doc) {
|
|
|
1380
1470
|
for (const note of doc.annotations) {
|
|
1381
1471
|
include(note.position.x, note.position.y, note.position.z);
|
|
1382
1472
|
}
|
|
1473
|
+
if (doc.terrain !== undefined) {
|
|
1474
|
+
const terrain = doc.terrain;
|
|
1475
|
+
let low = 0;
|
|
1476
|
+
let high = 0;
|
|
1477
|
+
for (const offset of terrain.offsets) {
|
|
1478
|
+
low = Math.min(low, offset);
|
|
1479
|
+
high = Math.max(high, offset);
|
|
1480
|
+
}
|
|
1481
|
+
include(terrain.bounds.minX, low, terrain.bounds.minZ);
|
|
1482
|
+
include(terrain.bounds.maxX, high, terrain.bounds.maxZ);
|
|
1483
|
+
}
|
|
1383
1484
|
if (!any)
|
|
1384
1485
|
return null;
|
|
1385
1486
|
return {
|
|
@@ -25,6 +25,14 @@ export function skyConfigFromEnvironment(env) {
|
|
|
25
25
|
...(env.zenithColor === undefined ? {} : { zenithColor: env.zenithColor }),
|
|
26
26
|
...(env.sunIntensity === undefined ? {} : { sunIntensity: env.sunIntensity }),
|
|
27
27
|
...(env.ambientIntensity === undefined ? {} : { ambientIntensity: env.ambientIntensity }),
|
|
28
|
+
...(env.sunAzimuth === undefined && env.sunElevation === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: {
|
|
31
|
+
sun: {
|
|
32
|
+
...(env.sunAzimuth === undefined ? {} : { azimuth: env.sunAzimuth }),
|
|
33
|
+
...(env.sunElevation === undefined ? {} : { elevation: env.sunElevation }),
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
28
36
|
...(env.fog === undefined ? {} : { fog: { ...env.fog } }),
|
|
29
37
|
};
|
|
30
38
|
}
|
package/dist/editor/index.d.ts
CHANGED
|
@@ -90,6 +90,8 @@ export type { EditorDispatchOptions } from "./commands.js";
|
|
|
90
90
|
export type { EditorSession } from "./commands.js";
|
|
91
91
|
/** The document plus current selection at a point in editor history. */
|
|
92
92
|
export type { EditorSessionState } from "./commands.js";
|
|
93
|
+
/** Atomic command-batch outcome; a failure identifies the zero-based command index. */
|
|
94
|
+
export type { EditorTransactionResult } from "./commands.js";
|
|
93
95
|
export { applyDocumentPatch, applyRuntimeStateDelta, createDocumentLiveSync, getDocumentLiveSync, installDocumentLiveSync, runtimeEntityWriteBackCommand, subscribeDocumentLiveSyncInstall, } from "./liveSync.js";
|
|
94
96
|
/** Versioned document mutation on the editorΓåöruntime live-sync stream. */
|
|
95
97
|
export type { DocumentPatch, ApplyDocumentPatchResult } from "./liveSync.js";
|
|
@@ -4,7 +4,8 @@ import type { EditorDocument, EditorVec3 } from "./types.js";
|
|
|
4
4
|
* One versioned document mutation on the live-sync stream. `snapshot` replaces the whole
|
|
5
5
|
* document; `commands` replays structural editor commands onto the current document.
|
|
6
6
|
* `baseRevision` must match the receiver's current revision unless `force` is set (document
|
|
7
|
-
* authority from the editor).
|
|
7
|
+
* authority from the editor). `revision` preserves the authority cursor when replaying: snapshots
|
|
8
|
+
* may jump forward for reconnect recovery; commands must be the next revision.
|
|
8
9
|
*/
|
|
9
10
|
export type DocumentPatch = {
|
|
10
11
|
type: "snapshot";
|
|
@@ -26,6 +27,7 @@ export type ApplyDocumentPatchResult = {
|
|
|
26
27
|
} | {
|
|
27
28
|
ok: false;
|
|
28
29
|
error: string;
|
|
30
|
+
commandIndex?: number;
|
|
29
31
|
};
|
|
30
32
|
/** One live entity row the runtime may stream to the editor (play-mode inspector feed). */
|
|
31
33
|
export interface RuntimeEntityState {
|
|
@@ -92,6 +94,7 @@ export interface DocumentLiveSync {
|
|
|
92
94
|
}): ApplyDocumentPatchResult;
|
|
93
95
|
/** Document-authoritative full replace (editor session mirror). */
|
|
94
96
|
replaceDocument(document: EditorDocument): DocumentLiveEvent;
|
|
97
|
+
/** Returns a current snapshot when the requested revision predates retained history. */
|
|
95
98
|
pullPatches(sinceRevision: number): DocumentPatch[];
|
|
96
99
|
subscribeDocument(listener: (event: DocumentLiveEvent) => void): () => void;
|
|
97
100
|
getRuntimeState(): RuntimeStateSnapshot;
|
package/dist/editor/liveSync.js
CHANGED
|
@@ -8,6 +8,15 @@ const MAX_RUNTIME_LOG = 128;
|
|
|
8
8
|
* @capability editor-live-sync apply a versioned document patch onto a live scene document
|
|
9
9
|
*/
|
|
10
10
|
export function applyDocumentPatch(document, revision, patch, options) {
|
|
11
|
+
if (!Number.isSafeInteger(patch.baseRevision) || patch.baseRevision < 0) {
|
|
12
|
+
return { ok: false, error: "baseRevision must be a nonnegative safe integer" };
|
|
13
|
+
}
|
|
14
|
+
if (patch.revision !== undefined && (!Number.isSafeInteger(patch.revision) || patch.revision <= revision)) {
|
|
15
|
+
return { ok: false, error: "revision must be a safe integer newer than the current revision" };
|
|
16
|
+
}
|
|
17
|
+
if (patch.type === "commands" && patch.revision !== undefined && patch.revision !== revision + 1) {
|
|
18
|
+
return { ok: false, error: "commands revision must immediately follow the current revision" };
|
|
19
|
+
}
|
|
11
20
|
if (!options?.force && patch.baseRevision !== revision) {
|
|
12
21
|
return {
|
|
13
22
|
ok: false,
|
|
@@ -16,7 +25,7 @@ export function applyDocumentPatch(document, revision, patch, options) {
|
|
|
16
25
|
}
|
|
17
26
|
if (patch.type === "snapshot") {
|
|
18
27
|
const nextDoc = cloneEditorDocument(patch.document);
|
|
19
|
-
const nextRevision = revision + 1;
|
|
28
|
+
const nextRevision = patch.revision ?? revision + 1;
|
|
20
29
|
return {
|
|
21
30
|
ok: true,
|
|
22
31
|
document: nextDoc,
|
|
@@ -24,15 +33,17 @@ export function applyDocumentPatch(document, revision, patch, options) {
|
|
|
24
33
|
patch: { ...patch, revision: nextRevision, document: nextDoc },
|
|
25
34
|
};
|
|
26
35
|
}
|
|
27
|
-
if (patch.commands.length === 0) {
|
|
36
|
+
if (!Array.isArray(patch.commands) || patch.commands.length === 0) {
|
|
28
37
|
return { ok: false, error: "commands patch is empty" };
|
|
29
38
|
}
|
|
30
|
-
const session = createEditorSession(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
39
|
+
const session = createEditorSession(document);
|
|
40
|
+
const before = session.getState().document;
|
|
41
|
+
const result = session.transaction(patch.commands);
|
|
42
|
+
if (!result.ok)
|
|
43
|
+
return result;
|
|
44
|
+
const changed = result.state.document !== before;
|
|
45
|
+
const nextDoc = changed ? result.state.document : document;
|
|
46
|
+
const nextRevision = changed ? revision + 1 : revision;
|
|
36
47
|
return {
|
|
37
48
|
ok: true,
|
|
38
49
|
document: nextDoc,
|
|
@@ -146,7 +157,8 @@ export function createDocumentLiveSync(initial) {
|
|
|
146
157
|
const result = applyDocumentPatch(document, revision, patch, options);
|
|
147
158
|
if (!result.ok)
|
|
148
159
|
return result;
|
|
149
|
-
|
|
160
|
+
if (result.revision !== revision)
|
|
161
|
+
commit(result);
|
|
150
162
|
return result;
|
|
151
163
|
},
|
|
152
164
|
replaceDocument(next) {
|
|
@@ -157,7 +169,11 @@ export function createDocumentLiveSync(initial) {
|
|
|
157
169
|
return commit(result);
|
|
158
170
|
},
|
|
159
171
|
pullPatches(sinceRevision) {
|
|
160
|
-
|
|
172
|
+
const patches = patchLog.filter((patch) => (patch.revision ?? 0) > sinceRevision);
|
|
173
|
+
if (patches.length > 0 && patches[0].baseRevision !== sinceRevision) {
|
|
174
|
+
return [{ type: "snapshot", baseRevision: sinceRevision, revision, document: cloneEditorDocument(document) }];
|
|
175
|
+
}
|
|
176
|
+
return patches;
|
|
161
177
|
},
|
|
162
178
|
subscribeDocument(listener) {
|
|
163
179
|
documentListeners.add(listener);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModelConfig } from "../game/playableGame.js";
|
|
2
|
+
import { type MaterialAsset, type MaterialAssignment, type MaterialSelector } from "../material/materialAsset.js";
|
|
3
|
+
import type { EditorDocument } from "./types.js";
|
|
4
|
+
/** Validate and copy a material asset at the editor boundary. */
|
|
5
|
+
export declare function parseEditorMaterialAsset(value: unknown): MaterialAsset;
|
|
6
|
+
/**
|
|
7
|
+
* Read named assignments from authored marker metadata without inferring whole-model overrides.
|
|
8
|
+
* @capability authored-material-slots read validated material assignments from editor marker metadata
|
|
9
|
+
*/
|
|
10
|
+
export declare function authoredMaterialAssignments(meta: Record<string, unknown> | undefined): MaterialAssignment[];
|
|
11
|
+
/** Compare selector meaning independently of serialized property order. @internal */
|
|
12
|
+
export declare function sameMaterialSelector(a: MaterialSelector, b: MaterialSelector): boolean;
|
|
13
|
+
/** Apply authored references; document assets and selectors override model defaults of the same identity. */
|
|
14
|
+
export declare function modelWithAuthoredMaterials(model: ModelConfig | undefined, document: Pick<EditorDocument, "markers" | "materialAssets">, instanceId: string): ModelConfig | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { parseMaterialAssignments, validateMaterialAsset, validateMaterialAssignments } from "../material/materialAsset.js";
|
|
2
|
+
/** Validate and copy a material asset at the editor boundary. */
|
|
3
|
+
export function parseEditorMaterialAsset(value) {
|
|
4
|
+
const errors = validateMaterialAsset(value).filter((item) => item.severity === "error");
|
|
5
|
+
if (errors.length > 0)
|
|
6
|
+
throw new TypeError(errors.map((item) => `${item.path}: ${item.message}`).join("; "));
|
|
7
|
+
return structuredClone(value);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Read named assignments from authored marker metadata without inferring whole-model overrides.
|
|
11
|
+
* @capability authored-material-slots read validated material assignments from editor marker metadata
|
|
12
|
+
*/
|
|
13
|
+
export function authoredMaterialAssignments(meta) {
|
|
14
|
+
return meta?.materialAssignments === undefined ? [] : parseMaterialAssignments(meta.materialAssignments);
|
|
15
|
+
}
|
|
16
|
+
/** Compare selector meaning independently of serialized property order. @internal */
|
|
17
|
+
export function sameMaterialSelector(a, b) {
|
|
18
|
+
return a.mesh === b.mesh && a.slot === b.slot && a.slotIndex === b.slotIndex;
|
|
19
|
+
}
|
|
20
|
+
/** Apply authored references; document assets and selectors override model defaults of the same identity. */
|
|
21
|
+
export function modelWithAuthoredMaterials(model, document, instanceId) {
|
|
22
|
+
if (model === undefined)
|
|
23
|
+
return model;
|
|
24
|
+
const marker = document.markers.find((item) => item.id === instanceId);
|
|
25
|
+
const assignments = authoredMaterialAssignments(marker?.meta);
|
|
26
|
+
const modelAssignments = model.materialAssignments ?? [];
|
|
27
|
+
if (assignments.length === 0 && modelAssignments.length === 0)
|
|
28
|
+
return model;
|
|
29
|
+
const assetsById = new Map((model.materialAssets ?? []).map((asset) => [asset.id, asset]));
|
|
30
|
+
for (const asset of document.materialAssets ?? [])
|
|
31
|
+
assetsById.set(asset.id, asset);
|
|
32
|
+
const assets = [...assetsById.values()];
|
|
33
|
+
const resolvedAssignments = [...modelAssignments.filter((item) => !assignments.some((authored) => sameMaterialSelector(item.selector, authored.selector))), ...assignments];
|
|
34
|
+
const errors = validateMaterialAssignments(resolvedAssignments, assets).filter((item) => item.severity === "error");
|
|
35
|
+
if (errors.length > 0)
|
|
36
|
+
throw new TypeError(errors.map((item) => `${item.path}: ${item.message}`).join("; "));
|
|
37
|
+
return { ...model, materialAssets: assets, materialAssignments: resolvedAssignments };
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ModelConfig } from "../game/playableGame.js";
|
|
2
|
+
import type { EditorMarker } from "./types.js";
|
|
3
|
+
/** Validate and copy exact node names; an empty list selects no hidden subtrees. */
|
|
4
|
+
export declare function parseModelHiddenNodes(value: unknown): readonly string[] | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* Read marker visibility metadata; an empty list clears model defaults.
|
|
7
|
+
* @capability model-node-visibility read authored per-placement model subtree selections
|
|
8
|
+
*/
|
|
9
|
+
export declare function authoredHiddenNodes(meta: Record<string, unknown> | undefined): readonly string[] | undefined;
|
|
10
|
+
/** Apply a placement's authored visibility without erasing omitted game model defaults. */
|
|
11
|
+
export declare function modelWithAuthoredNodeVisibility(model: ModelConfig | undefined, marker: Pick<EditorMarker, "meta"> | undefined): ModelConfig | undefined;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Validate and copy exact node names; an empty list selects no hidden subtrees. */
|
|
2
|
+
export function parseModelHiddenNodes(value) {
|
|
3
|
+
if (value === undefined)
|
|
4
|
+
return undefined;
|
|
5
|
+
if (!Array.isArray(value)) {
|
|
6
|
+
throw new TypeError("hiddenNodes must be an array of nonblank node names");
|
|
7
|
+
}
|
|
8
|
+
for (const name of value) {
|
|
9
|
+
if (typeof name !== "string" || name.trim().length === 0)
|
|
10
|
+
throw new TypeError("hiddenNodes must be an array of nonblank node names");
|
|
11
|
+
}
|
|
12
|
+
return [...value];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Read marker visibility metadata; an empty list clears model defaults.
|
|
16
|
+
* @capability model-node-visibility read authored per-placement model subtree selections
|
|
17
|
+
*/
|
|
18
|
+
export function authoredHiddenNodes(meta) {
|
|
19
|
+
return parseModelHiddenNodes(meta?.hiddenNodes);
|
|
20
|
+
}
|
|
21
|
+
/** Apply a placement's authored visibility without erasing omitted game model defaults. */
|
|
22
|
+
export function modelWithAuthoredNodeVisibility(model, marker) {
|
|
23
|
+
if (model === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
const hiddenNodes = authoredHiddenNodes(marker?.meta);
|
|
26
|
+
if (hiddenNodes === undefined || (model.hiddenNodes?.length === hiddenNodes.length && hiddenNodes.every((name, index) => model.hiddenNodes[index] === name)))
|
|
27
|
+
return model;
|
|
28
|
+
return { ...model, hiddenNodes };
|
|
29
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { PlayerMovementConfig } from "../game/playableGame.js";
|
|
2
|
+
import type { PhysicsConfig } from "../game/defineGame.js";
|
|
3
|
+
import type { ParamSchema } from "../scene/sceneKinds.js";
|
|
4
|
+
import type { EditorCatalogData } from "./types.js";
|
|
5
|
+
/** Numeric character controls a game elects to author through its own named catalog rows. */
|
|
6
|
+
export interface AuthoredMovementValues {
|
|
7
|
+
walkSpeed?: number;
|
|
8
|
+
gravity?: number;
|
|
9
|
+
jumpVelocity?: number;
|
|
10
|
+
stepHeight?: number;
|
|
11
|
+
/** Optional positive heightfield collision span from feet; independent of model and camera scale. */
|
|
12
|
+
collisionHeight?: number;
|
|
13
|
+
/** Optional heightfield rise/run limit; omitted controls retain the game's movement policy. */
|
|
14
|
+
maxClimbGrade?: number;
|
|
15
|
+
groundAcceleration?: number;
|
|
16
|
+
airAcceleration?: number;
|
|
17
|
+
groundFriction?: number;
|
|
18
|
+
runMultiplier?: number;
|
|
19
|
+
crouchMultiplier?: number;
|
|
20
|
+
jumpBufferMs?: number;
|
|
21
|
+
coyoteMs?: number;
|
|
22
|
+
/** Rising speed retained on early jump release, from 0 to 1. */
|
|
23
|
+
jumpCutFactor?: number;
|
|
24
|
+
apexGravityScale?: number;
|
|
25
|
+
apexSpeed?: number;
|
|
26
|
+
fallGravityScale?: number;
|
|
27
|
+
landingRecoveryMs?: number;
|
|
28
|
+
/** Initial landing speed multiplier; values above 1 retain the existing runtime contract. */
|
|
29
|
+
landingSpeedScale?: number;
|
|
30
|
+
}
|
|
31
|
+
/** Only authored values, ready to overlay on a game's existing movement and physics policy. */
|
|
32
|
+
export interface AuthoredMovementConfig {
|
|
33
|
+
readonly walkSpeed?: number;
|
|
34
|
+
readonly physics?: {
|
|
35
|
+
readonly gravity?: number;
|
|
36
|
+
readonly jumpVelocity?: number;
|
|
37
|
+
};
|
|
38
|
+
readonly movement?: Pick<PlayerMovementConfig, "stepHeight" | "collisionHeight" | "maxClimbGrade" | "feel">;
|
|
39
|
+
}
|
|
40
|
+
/** A persisted movement value rejected before it can replace the game's current setting. */
|
|
41
|
+
export interface AuthoredMovementDiagnostic {
|
|
42
|
+
readonly path: string;
|
|
43
|
+
readonly message: string;
|
|
44
|
+
readonly repair: string;
|
|
45
|
+
}
|
|
46
|
+
/** Valid partial tuning plus located rejected values; absent rows leave game defaults intact. */
|
|
47
|
+
export interface AuthoredMovementResult {
|
|
48
|
+
readonly config: AuthoredMovementConfig;
|
|
49
|
+
readonly diagnostics: readonly AuthoredMovementDiagnostic[];
|
|
50
|
+
}
|
|
51
|
+
/** Structural authored catalog input; an immutable EditorDocument satisfies it. */
|
|
52
|
+
export interface MovementCatalogDocumentLike {
|
|
53
|
+
readonly catalogs: readonly EditorCatalogData[];
|
|
54
|
+
}
|
|
55
|
+
/** Game-owned callbacks, collision and physics settings retained beneath authored numeric controls. */
|
|
56
|
+
export interface AuthoredMovementDefaults {
|
|
57
|
+
readonly movement?: PlayerMovementConfig;
|
|
58
|
+
readonly physics?: PhysicsConfig;
|
|
59
|
+
}
|
|
60
|
+
/** Stable runtime objects whose numeric getters resolve the current authored document. */
|
|
61
|
+
export interface AuthoredMovementBinding {
|
|
62
|
+
readonly movement: PlayerMovementConfig;
|
|
63
|
+
readonly physics: PhysicsConfig;
|
|
64
|
+
readonly diagnostics: readonly AuthoredMovementDiagnostic[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Creates editor/RPC controls only for the values a game supplies, with those game's defaults.
|
|
68
|
+
* Gravity retains the game's sign convention; collision height is positive, other controls nonnegative.
|
|
69
|
+
* Jump release is bounded to 0..1; supplied deeper jump controls share a collapsed response group.
|
|
70
|
+
* @capability editor-movement author game-chosen character movement controls through ordinary catalog rows
|
|
71
|
+
*/
|
|
72
|
+
export declare function createMovementSchema(defaults: AuthoredMovementValues): ParamSchema;
|
|
73
|
+
/**
|
|
74
|
+
* Reads one game-named row as partial runtime tuning, retaining game defaults for omitted or invalid
|
|
75
|
+
* values. Rejected values include their document path and repair; no policy callbacks are replaced.
|
|
76
|
+
* @capability editor-movement resolve saved movement controls without replacing a game's character rules
|
|
77
|
+
*/
|
|
78
|
+
export declare function readAuthoredMovement(document: MovementCatalogDocumentLike, catalogId: string, entryId: string): AuthoredMovementResult;
|
|
79
|
+
/**
|
|
80
|
+
* Memoizes movement resolution by immutable document identity; reads the latest document on every
|
|
81
|
+
* call while catalog lookup and diagnostics run only after authoring changes.
|
|
82
|
+
* @capability editor-movement consume live saved movement tuning with bounded per-frame work
|
|
83
|
+
*/
|
|
84
|
+
export declare function createAuthoredMovementReader(getDocument: () => MovementCatalogDocumentLike, catalogId: string, entryId: string): () => AuthoredMovementResult;
|
|
85
|
+
/**
|
|
86
|
+
* Binds live authored numeric controls onto stable runtime objects, preserving the game's callbacks,
|
|
87
|
+
* collision, backend and other policy. The movement controller reads these getters each step.
|
|
88
|
+
* @capability editor-movement bind live character tuning while preserving distinctive game policy
|
|
89
|
+
*/
|
|
90
|
+
export declare function bindAuthoredMovement(read: () => AuthoredMovementResult, defaults: AuthoredMovementDefaults): AuthoredMovementBinding;
|