@jgengine/core 0.10.0 → 0.12.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 +39 -2
- package/LICENSE +203 -661
- package/README.md +1 -1
- package/dist/ai/crowd.d.ts +55 -0
- package/dist/ai/crowd.js +114 -0
- package/dist/ai/flock.d.ts +5 -2
- package/dist/ai/flock.js +5 -2
- package/dist/ai/groupAssist.d.ts +1 -0
- package/dist/ai/groupAssist.js +1 -0
- package/dist/ai/heatSystem.d.ts +76 -0
- package/dist/ai/heatSystem.js +97 -0
- package/dist/ai/interestScheduler.d.ts +107 -0
- package/dist/ai/interestScheduler.js +118 -0
- package/dist/ai/jobBoard.d.ts +1 -0
- package/dist/ai/jobBoard.js +1 -0
- package/dist/ai/laneSelect.d.ts +5 -2
- package/dist/ai/laneSelect.js +5 -2
- package/dist/ai/mobBrain.d.ts +1 -0
- package/dist/ai/mobBrain.js +1 -0
- package/dist/ai/populationDirector.d.ts +103 -0
- package/dist/ai/populationDirector.js +190 -0
- package/dist/ai/pursuit.d.ts +56 -0
- package/dist/ai/pursuit.js +53 -0
- package/dist/ai/spawnDirector.d.ts +7 -2
- package/dist/ai/spawnDirector.js +18 -15
- package/dist/ai/spawnPoint.d.ts +3 -1
- package/dist/ai/spawnPoint.js +3 -1
- package/dist/ai/targetAcquisition.d.ts +84 -0
- package/dist/ai/targetAcquisition.js +92 -0
- package/dist/anim/easing.d.ts +19 -0
- package/dist/anim/easing.js +19 -0
- package/dist/anim/oscillator.d.ts +12 -0
- package/dist/anim/oscillator.js +25 -0
- package/dist/area/areaEffectField.d.ts +127 -0
- package/dist/area/areaEffectField.js +175 -0
- package/dist/area/stackPolicy.d.ts +47 -0
- package/dist/area/stackPolicy.js +99 -0
- package/dist/authoring.d.ts +2 -1
- package/dist/authoring.js +2 -1
- package/dist/board/laneBoard.d.ts +13 -0
- package/dist/board/laneBoard.js +13 -0
- package/dist/board/timelineBoard.d.ts +10 -0
- package/dist/board/timelineBoard.js +10 -0
- package/dist/combat/abilityKit.d.ts +18 -0
- package/dist/combat/abilityKit.js +24 -9
- package/dist/combat/animationState.d.ts +5 -0
- package/dist/combat/animationState.js +5 -0
- package/dist/combat/breakMeters.d.ts +10 -0
- package/dist/combat/breakMeters.js +10 -0
- package/dist/combat/castRunner.d.ts +5 -0
- package/dist/combat/castRunner.js +5 -0
- package/dist/combat/comboPoints.d.ts +5 -0
- package/dist/combat/comboPoints.js +5 -0
- package/dist/combat/comboString.d.ts +5 -0
- package/dist/combat/comboString.js +5 -0
- package/dist/combat/damageInterceptors.d.ts +192 -0
- package/dist/combat/damageInterceptors.js +251 -0
- package/dist/combat/damageMatchup.d.ts +68 -0
- package/dist/combat/damageMatchup.js +42 -0
- package/dist/combat/damageResolution.d.ts +63 -0
- package/dist/combat/damageResolution.js +50 -0
- package/dist/combat/death.d.ts +7 -1
- package/dist/combat/death.js +5 -0
- package/dist/combat/deathReason.d.ts +14 -0
- package/dist/combat/deathReason.js +1 -0
- package/dist/combat/defensiveWindow.d.ts +5 -0
- package/dist/combat/defensiveWindow.js +5 -0
- package/dist/combat/dotField.d.ts +5 -1
- package/dist/combat/dotField.js +5 -1
- package/dist/combat/downed.d.ts +5 -0
- package/dist/combat/downed.js +5 -0
- package/dist/combat/effects.d.ts +5 -0
- package/dist/combat/effects.js +5 -0
- package/dist/combat/encounterSequence.d.ts +258 -0
- package/dist/combat/encounterSequence.js +426 -0
- package/dist/combat/hitReaction.d.ts +57 -1
- package/dist/combat/hitReaction.js +45 -5
- package/dist/combat/magazine.d.ts +54 -0
- package/dist/combat/magazine.js +96 -0
- package/dist/combat/projectiles.d.ts +5 -0
- package/dist/combat/projectiles.js +32 -14
- package/dist/combat/receivedDamage.d.ts +117 -0
- package/dist/combat/receivedDamage.js +92 -0
- package/dist/combat/regenShield.d.ts +5 -1
- package/dist/combat/regenShield.js +5 -1
- package/dist/combat/renderCues.d.ts +51 -0
- package/dist/combat/renderCues.js +44 -0
- package/dist/combat/resourcePool.d.ts +5 -0
- package/dist/combat/resourcePool.js +5 -0
- package/dist/combat/rewardAllocation.d.ts +121 -0
- package/dist/combat/rewardAllocation.js +251 -0
- package/dist/combat/shotOrigin.d.ts +18 -1
- package/dist/combat/shotOrigin.js +39 -0
- package/dist/combat/statusApplication.d.ts +96 -0
- package/dist/combat/statusApplication.js +81 -0
- package/dist/combat/telegraph.d.ts +11 -2
- package/dist/combat/telegraph.js +11 -2
- package/dist/combat/thresholdCrossings.d.ts +85 -0
- package/dist/combat/thresholdCrossings.js +116 -0
- package/dist/combat.d.ts +35 -0
- package/dist/combat.js +35 -0
- package/dist/commands/commandRegistry.d.ts +11 -0
- package/dist/commands/commandRegistry.js +9 -2
- package/dist/crafting/crop.d.ts +18 -0
- package/dist/crafting/crop.js +18 -0
- package/dist/crafting/production.d.ts +5 -0
- package/dist/crafting/production.js +5 -0
- package/dist/devtools/controls.d.ts +38 -0
- package/dist/devtools/controls.js +181 -0
- package/dist/devtools/devtools.d.ts +5 -197
- package/dist/devtools/devtools.js +38 -774
- package/dist/devtools/discover.d.ts +32 -0
- package/dist/devtools/discover.js +240 -0
- package/dist/devtools/fallbackSeams.d.ts +50 -0
- package/dist/devtools/fallbackSeams.js +111 -0
- package/dist/devtools/frame.d.ts +15 -0
- package/dist/devtools/frame.js +247 -0
- package/dist/devtools/logs.d.ts +15 -0
- package/dist/devtools/logs.js +86 -0
- package/dist/devtools/overrides.d.ts +16 -0
- package/dist/devtools/overrides.js +66 -0
- package/dist/devtools/rewriteTunables.d.ts +17 -0
- package/dist/devtools/rewriteTunables.js +276 -0
- package/dist/devtools/saveEndpoint.d.ts +46 -0
- package/dist/devtools/saveEndpoint.js +48 -0
- package/dist/devtools/transformTunables.d.ts +3 -0
- package/dist/devtools/transformTunables.js +3 -0
- package/dist/devtools/tunableSchema.d.ts +31 -0
- package/dist/devtools/tunableSchema.js +31 -0
- package/dist/devtools/types.d.ts +212 -0
- package/dist/devtools/types.js +1 -0
- package/dist/economy/auctionBook.d.ts +122 -0
- package/dist/economy/auctionBook.js +213 -0
- package/dist/economy/currency.d.ts +12 -1
- package/dist/economy/currency.js +12 -1
- package/dist/economy/listingBook.d.ts +91 -0
- package/dist/economy/listingBook.js +144 -0
- package/dist/economy/priceHistory.d.ts +44 -0
- package/dist/economy/priceHistory.js +89 -0
- package/dist/economy/resourceLedger.d.ts +222 -0
- package/dist/economy/resourceLedger.js +353 -0
- package/dist/economy/sharedWallet.d.ts +14 -0
- package/dist/economy/sharedWallet.js +14 -0
- package/dist/economy/techTree.d.ts +12 -0
- package/dist/economy/techTree.js +12 -0
- package/dist/economy/unlockPoints.d.ts +92 -0
- package/dist/economy/unlockPoints.js +103 -0
- package/dist/economy/wallet.d.ts +26 -2
- package/dist/economy/wallet.js +35 -6
- package/dist/editor/commandApply.d.ts +5 -0
- package/dist/editor/commandApply.js +755 -0
- package/dist/editor/commands.d.ts +235 -7
- package/dist/editor/commands.js +82 -124
- package/dist/editor/directives.d.ts +94 -0
- package/dist/editor/directives.js +206 -0
- package/dist/editor/document.d.ts +194 -13
- package/dist/editor/document.js +1166 -19
- package/dist/editor/entityCatalog.d.ts +42 -0
- package/dist/editor/entityCatalog.js +101 -0
- package/dist/editor/environment.d.ts +74 -0
- package/dist/editor/environment.js +169 -0
- package/dist/editor/grid.d.ts +229 -0
- package/dist/editor/grid.js +366 -0
- package/dist/editor/gridAdapters.d.ts +80 -0
- package/dist/editor/gridAdapters.js +134 -0
- package/dist/editor/index.d.ts +98 -6
- package/dist/editor/index.js +13 -2
- package/dist/editor/liveSync.d.ts +127 -0
- package/dist/editor/liveSync.js +249 -0
- package/dist/editor/minimap.d.ts +31 -0
- package/dist/editor/minimap.js +73 -0
- package/dist/editor/runtimeInspector.d.ts +88 -0
- package/dist/editor/runtimeInspector.js +239 -0
- package/dist/editor/streamer.d.ts +66 -0
- package/dist/editor/streamer.js +68 -0
- package/dist/editor/types.d.ts +250 -1
- package/dist/editor/world.d.ts +136 -0
- package/dist/editor/world.js +303 -0
- package/dist/faction/reputation.d.ts +5 -0
- package/dist/faction/reputation.js +5 -0
- package/dist/format/distance.d.ts +17 -0
- package/dist/format/distance.js +18 -0
- package/dist/format/duration.d.ts +27 -0
- package/dist/format/duration.js +45 -0
- package/dist/format/speed.d.ts +17 -0
- package/dist/format/speed.js +25 -0
- package/dist/game/breeding.d.ts +161 -0
- package/dist/game/breeding.js +181 -0
- package/dist/game/cameraConfig.d.ts +451 -0
- package/dist/game/cameraConfig.js +1 -0
- package/dist/game/connectedPlayers.d.ts +6 -4
- package/dist/game/connectedPlayers.js +2 -2
- package/dist/game/cosmetics.d.ts +9 -0
- package/dist/game/cosmetics.js +16 -0
- package/dist/game/defineGame.d.ts +110 -5
- package/dist/game/defineGame.js +49 -4
- package/dist/game/defineSystem.d.ts +90 -0
- package/dist/game/defineSystem.js +47 -0
- package/dist/game/dialogue.d.ts +33 -0
- package/dist/game/dialogue.js +21 -0
- package/dist/game/events.d.ts +63 -13
- package/dist/game/events.js +5 -0
- package/dist/game/feed.d.ts +33 -0
- package/dist/game/feed.js +35 -2
- package/dist/game/gamePhase.d.ts +11 -0
- package/dist/game/gamePhase.js +16 -0
- package/dist/game/leaderboard.d.ts +5 -0
- package/dist/game/leaderboard.js +5 -0
- package/dist/game/loadout.d.ts +5 -0
- package/dist/game/loadout.js +5 -0
- package/dist/game/lootFilter.d.ts +2 -0
- package/dist/game/lootFilter.js +2 -0
- package/dist/game/lootPipeline.d.ts +164 -0
- package/dist/game/lootPipeline.js +216 -0
- package/dist/game/lootTable.d.ts +19 -2
- package/dist/game/lootTable.js +15 -5
- package/dist/game/objectives.d.ts +43 -0
- package/dist/game/objectives.js +36 -0
- package/dist/game/paramLayers.d.ts +148 -0
- package/dist/game/paramLayers.js +185 -0
- package/dist/game/ping.d.ts +5 -0
- package/dist/game/ping.js +5 -0
- package/dist/game/playableGame.d.ts +116 -432
- package/dist/game/playableGame.js +1 -1
- package/dist/game/progression.d.ts +35 -0
- package/dist/game/progression.js +19 -0
- package/dist/game/quest.d.ts +8 -0
- package/dist/game/quest.js +30 -3
- package/dist/game/race.d.ts +141 -0
- package/dist/game/race.js +169 -0
- package/dist/game/recordBook.d.ts +3 -1
- package/dist/game/recordBook.js +4 -1
- package/dist/game/ruleSelection.d.ts +75 -0
- package/dist/game/ruleSelection.js +127 -0
- package/dist/game/runDraft.d.ts +5 -0
- package/dist/game/runDraft.js +5 -0
- package/dist/game/saveStore.d.ts +111 -0
- package/dist/game/saveStore.js +312 -0
- package/dist/game/snapshotHistory.d.ts +67 -0
- package/dist/game/snapshotHistory.js +95 -0
- package/dist/game/social.d.ts +38 -1
- package/dist/game/social.js +71 -0
- package/dist/game/spawnPoints.d.ts +5 -0
- package/dist/game/spawnPoints.js +5 -0
- package/dist/game/systemRuntime.d.ts +50 -0
- package/dist/game/systemRuntime.js +157 -0
- package/dist/game/systemSchedule.d.ts +72 -0
- package/dist/game/systemSchedule.js +242 -0
- package/dist/game/toasts.d.ts +43 -0
- package/dist/game/toasts.js +44 -0
- package/dist/game/trade.d.ts +10 -0
- package/dist/game/trade.js +10 -0
- package/dist/game/tribe.d.ts +184 -0
- package/dist/game/tribe.js +432 -0
- package/dist/game/unlocks.d.ts +8 -0
- package/dist/game/unlocks.js +16 -0
- package/dist/game/vfxInstance.d.ts +138 -0
- package/dist/game/vfxInstance.js +96 -0
- package/dist/game/worldItem.d.ts +17 -4
- package/dist/game/worldItem.js +17 -4
- package/dist/gameplay.d.ts +93 -0
- package/dist/gameplay.js +93 -0
- package/dist/index.d.ts +5 -14
- package/dist/index.js +5 -14
- package/dist/input/actionBindings.d.ts +23 -6
- package/dist/input/actionBindings.js +23 -6
- package/dist/input/axisInput.d.ts +7 -2
- package/dist/input/axisInput.js +7 -2
- package/dist/input/controlGroups.d.ts +67 -0
- package/dist/input/controlGroups.js +41 -0
- package/dist/input/touchScheme.d.ts +82 -6
- package/dist/input/touchScheme.js +123 -10
- package/dist/interaction/lockpick.d.ts +61 -0
- package/dist/interaction/lockpick.js +168 -0
- package/dist/interaction/proximityPrompt.d.ts +2 -0
- package/dist/interaction/proximityPrompt.js +2 -0
- package/dist/interaction/qte.d.ts +5 -0
- package/dist/interaction/qte.js +5 -0
- package/dist/interaction/skillCheck.d.ts +5 -0
- package/dist/interaction/skillCheck.js +5 -0
- package/dist/inventory/encumbrance.d.ts +69 -0
- package/dist/inventory/encumbrance.js +68 -0
- package/dist/inventory/inventoryModel.d.ts +12 -0
- package/dist/inventory/inventoryModel.js +12 -0
- package/dist/inventory/shapedGrid.d.ts +16 -0
- package/dist/inventory/shapedGrid.js +16 -0
- package/dist/inventory/slotModel.d.ts +10 -0
- package/dist/inventory/slotModel.js +10 -0
- package/dist/item/durability.d.ts +5 -0
- package/dist/item/durability.js +6 -3
- package/dist/item/itemIdentity.d.ts +171 -0
- package/dist/item/itemIdentity.js +138 -0
- package/dist/item/itemInstanceRegistry.d.ts +32 -0
- package/dist/item/itemInstanceRegistry.js +34 -0
- package/dist/item/itemgen.d.ts +163 -0
- package/dist/item/itemgen.js +151 -0
- package/dist/item/modularItem.d.ts +5 -0
- package/dist/item/modularItem.js +5 -0
- package/dist/item/use.d.ts +5 -0
- package/dist/item/use.js +5 -0
- package/dist/item/useBehavior.d.ts +142 -0
- package/dist/item/useBehavior.js +105 -0
- package/dist/item/weapon.d.ts +5 -0
- package/dist/item/weapon.js +5 -0
- package/dist/math/scalar.d.ts +25 -0
- package/dist/math/scalar.js +47 -0
- package/dist/meta/changelog.d.ts +4 -1
- package/dist/meta/changelog.js +39 -2
- package/dist/movement/avoidance.d.ts +45 -0
- package/dist/movement/avoidance.js +123 -0
- package/dist/movement/cameraRig.d.ts +8 -0
- package/dist/movement/cameraRig.js +8 -0
- package/dist/movement/dash.d.ts +11 -0
- package/dist/movement/dash.js +11 -0
- package/dist/movement/formation.d.ts +135 -0
- package/dist/movement/formation.js +194 -0
- package/dist/movement/glideModel.d.ts +5 -0
- package/dist/movement/glideModel.js +5 -0
- package/dist/movement/grappleSwing.d.ts +5 -0
- package/dist/movement/grappleSwing.js +5 -0
- package/dist/movement/leaderTrail.d.ts +5 -0
- package/dist/movement/leaderTrail.js +5 -0
- package/dist/movement/movementModel.d.ts +50 -9
- package/dist/movement/movementModel.js +75 -29
- package/dist/movement/playerMovement.js +167 -12
- package/dist/movement/poseState.d.ts +13 -0
- package/dist/movement/poseState.js +16 -0
- package/dist/movement/steering.d.ts +8 -0
- package/dist/movement/steering.js +18 -0
- package/dist/multiplayer/combatSnapshot.d.ts +3 -0
- package/dist/multiplayer/combatSnapshot.js +16 -8
- package/dist/multiplayer/lagCompensation.d.ts +5 -0
- package/dist/multiplayer/lagCompensation.js +5 -0
- package/dist/multiplayer/presenceModel.d.ts +11 -1
- package/dist/multiplayer/presenceModel.js +17 -1
- package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
- package/dist/multiplayer/simultaneousCommit.js +3 -0
- package/dist/multiplayer.d.ts +11 -0
- package/dist/multiplayer.js +11 -0
- package/dist/nav/pathFollow.d.ts +57 -0
- package/dist/nav/pathFollow.js +109 -0
- package/dist/orders/orderKinds.d.ts +156 -0
- package/dist/orders/orderKinds.js +197 -0
- package/dist/orders/orderQueue.d.ts +208 -0
- package/dist/orders/orderQueue.js +211 -0
- package/dist/physics/drivableVehicle.d.ts +31 -0
- package/dist/physics/drivableVehicle.js +16 -0
- package/dist/physics/flightDynamics.d.ts +82 -0
- package/dist/physics/flightDynamics.js +165 -0
- package/dist/physics/gravityField.d.ts +25 -0
- package/dist/physics/gravityField.js +41 -0
- package/dist/physics/kinematicVehicle.d.ts +108 -1
- package/dist/physics/kinematicVehicle.js +212 -15
- package/dist/physics/physicsWorld.d.ts +50 -12
- package/dist/physics/physicsWorld.js +120 -122
- package/dist/physics/radialImpulse.d.ts +25 -0
- package/dist/physics/radialImpulse.js +27 -0
- package/dist/physics/vehicleObstacles.d.ts +44 -0
- package/dist/physics/vehicleObstacles.js +115 -0
- package/dist/procedural.d.ts +3 -0
- package/dist/procedural.js +3 -0
- package/dist/progression/statGraph.d.ts +116 -0
- package/dist/progression/statGraph.js +318 -0
- package/dist/puzzle/cellGrid.d.ts +9 -0
- package/dist/puzzle/cellGrid.js +9 -0
- package/dist/puzzle/fallingPiece.d.ts +9 -0
- package/dist/puzzle/fallingPiece.js +9 -0
- package/dist/puzzle/nonogram.d.ts +39 -0
- package/dist/puzzle/nonogram.js +136 -0
- package/dist/random/nameGen.d.ts +5 -0
- package/dist/random/nameGen.js +5 -0
- package/dist/random/pick.d.ts +8 -0
- package/dist/random/pick.js +31 -0
- package/dist/random/rng.d.ts +26 -1
- package/dist/random/rng.js +32 -9
- package/dist/random/seedLink.d.ts +4 -0
- package/dist/random/seedLink.js +4 -0
- package/dist/relation/keyedValues.d.ts +54 -0
- package/dist/relation/keyedValues.js +115 -0
- package/dist/relation/thresholds.d.ts +62 -0
- package/dist/relation/thresholds.js +62 -0
- package/dist/render/color.d.ts +12 -0
- package/dist/render/color.js +26 -0
- package/dist/render/lookPreset.d.ts +41 -0
- package/dist/render/lookPreset.js +29 -0
- package/dist/render/postProcessing.d.ts +20 -0
- package/dist/render/postProcessing.js +21 -1
- package/dist/rules/predicate.d.ts +53 -0
- package/dist/rules/predicate.js +64 -0
- package/dist/rules/ruleEffects.d.ts +38 -0
- package/dist/rules/ruleEffects.js +37 -0
- package/dist/rules/triggeredRules.d.ts +160 -0
- package/dist/rules/triggeredRules.js +241 -0
- package/dist/runtime/adapter.d.ts +55 -3
- package/dist/runtime/adapter.js +57 -1
- package/dist/runtime/cameraDirector.d.ts +1 -0
- package/dist/runtime/cameraDirector.js +1 -0
- package/dist/runtime/commandRunner.d.ts +1 -0
- package/dist/runtime/commandRunner.js +1 -0
- package/dist/runtime/context/combat.d.ts +51 -0
- package/dist/runtime/context/combat.js +130 -0
- package/dist/runtime/context/combatFx.d.ts +23 -0
- package/dist/runtime/context/combatFx.js +161 -0
- package/dist/runtime/context/deathLoot.d.ts +30 -0
- package/dist/runtime/context/deathLoot.js +39 -0
- package/dist/runtime/context/player.d.ts +48 -0
- package/dist/runtime/context/player.js +125 -0
- package/dist/runtime/context/registries.d.ts +13 -0
- package/dist/runtime/context/registries.js +84 -0
- package/dist/runtime/context/scene.d.ts +59 -0
- package/dist/runtime/context/scene.js +291 -0
- package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
- package/dist/runtime/context/snapshotCodecs.js +26 -0
- package/dist/runtime/context/worldItems.d.ts +21 -0
- package/dist/runtime/context/worldItems.js +37 -0
- package/dist/runtime/descriptors/baseline.d.ts +48 -0
- package/dist/runtime/descriptors/baseline.js +157 -0
- package/dist/runtime/descriptors/features.d.ts +79 -0
- package/dist/runtime/descriptors/features.js +299 -0
- package/dist/runtime/gameContext.d.ts +2 -327
- package/dist/runtime/gameContext.js +273 -778
- package/dist/runtime/gameContextTypes.d.ts +467 -0
- package/dist/runtime/gameContextTypes.js +1 -0
- package/dist/runtime/gameRuntime.d.ts +1 -0
- package/dist/runtime/gameRuntime.js +1 -0
- package/dist/runtime/headlessRunner.d.ts +62 -0
- package/dist/runtime/headlessRunner.js +45 -0
- package/dist/runtime/hostPersistence.d.ts +12 -0
- package/dist/runtime/hostPersistence.js +12 -0
- package/dist/runtime/hostPolicy.d.ts +51 -0
- package/dist/runtime/hostPolicy.js +68 -0
- package/dist/runtime/hostedGameRunner.d.ts +11 -4
- package/dist/runtime/hostedGameRunner.js +27 -3
- package/dist/runtime/hostedWorldSession.d.ts +15 -5
- package/dist/runtime/hostedWorldSession.js +12 -4
- package/dist/runtime/inputSnapshot.d.ts +8 -3
- package/dist/runtime/inputSnapshot.js +6 -2
- package/dist/runtime/motionIntents.d.ts +11 -2
- package/dist/runtime/motionIntents.js +16 -2
- package/dist/runtime/perContext.d.ts +15 -0
- package/dist/runtime/perContext.js +21 -0
- package/dist/runtime/runtimeSave.d.ts +79 -0
- package/dist/runtime/runtimeSave.js +105 -0
- package/dist/runtime/save.d.ts +4 -0
- package/dist/runtime/save.js +4 -0
- package/dist/runtime/snapshot.d.ts +7 -0
- package/dist/runtime/snapshot.js +7 -0
- package/dist/runtime/visibility.d.ts +2 -1
- package/dist/runtime/visibility.js +2 -1
- package/dist/runtime/worldChannel.d.ts +7 -3
- package/dist/runtime/worldChannel.js +51 -6
- package/dist/runtime/worldMirror.d.ts +14 -2
- package/dist/runtime/worldMirror.js +15 -2
- package/dist/runtime/worldProjection.d.ts +47 -0
- package/dist/runtime/worldProjection.js +52 -0
- package/dist/runtime/worldReplication.d.ts +30 -7
- package/dist/runtime/worldReplication.js +41 -9
- package/dist/runtime/worldSnapshot.d.ts +42 -3
- package/dist/runtime/worldSnapshot.js +34 -7
- package/dist/scene/assetCatalog.d.ts +26 -0
- package/dist/scene/assetCatalog.js +4 -0
- package/dist/scene/assetDiagnostics.d.ts +69 -0
- package/dist/scene/assetDiagnostics.js +110 -0
- package/dist/scene/assetGenerator.d.ts +65 -0
- package/dist/scene/assetGenerator.js +64 -0
- package/dist/scene/assetSpace.d.ts +137 -0
- package/dist/scene/assetSpace.js +257 -0
- package/dist/scene/authoredTriggers.d.ts +219 -0
- package/dist/scene/authoredTriggers.js +454 -0
- package/dist/scene/behaviorRuntime.d.ts +75 -0
- package/dist/scene/behaviorRuntime.js +217 -0
- package/dist/scene/behaviors.d.ts +14 -2
- package/dist/scene/behaviors.js +7 -2
- package/dist/scene/bodyBind.d.ts +60 -0
- package/dist/scene/bodyBind.js +51 -0
- package/dist/scene/builtinSceneKinds.d.ts +5 -0
- package/dist/scene/builtinSceneKinds.js +67 -0
- package/dist/scene/colliders.d.ts +71 -1
- package/dist/scene/colliders.js +145 -1
- package/dist/scene/collisionMesh.d.ts +94 -0
- package/dist/scene/collisionMesh.js +699 -0
- package/dist/scene/companion.d.ts +147 -0
- package/dist/scene/companion.js +193 -0
- package/dist/scene/entityStats.d.ts +18 -11
- package/dist/scene/entityStats.js +21 -16
- package/dist/scene/entityStore.d.ts +12 -1
- package/dist/scene/entityStore.js +15 -0
- package/dist/scene/facing.d.ts +18 -0
- package/dist/scene/facing.js +26 -0
- package/dist/scene/modelSockets.d.ts +33 -0
- package/dist/scene/modelSockets.js +29 -0
- package/dist/scene/objectQuery.d.ts +2 -0
- package/dist/scene/objectQuery.js +2 -0
- package/dist/scene/objectStore.d.ts +4 -0
- package/dist/scene/objectStore.js +6 -0
- package/dist/scene/possession.d.ts +8 -0
- package/dist/scene/possession.js +14 -0
- package/dist/scene/roster.d.ts +3 -0
- package/dist/scene/roster.js +12 -0
- package/dist/scene/sceneKinds.d.ts +256 -0
- package/dist/scene/sceneKinds.js +218 -0
- package/dist/scene/sceneOwnership.d.ts +169 -0
- package/dist/scene/sceneOwnership.js +179 -0
- package/dist/scene/sceneRaycast.js +15 -0
- package/dist/scene/selectionBookmarks.d.ts +93 -0
- package/dist/scene/selectionBookmarks.js +122 -0
- package/dist/scene/vehicleSeat.d.ts +68 -0
- package/dist/scene/vehicleSeat.js +59 -0
- package/dist/sensor/concealment.d.ts +3 -0
- package/dist/sensor/concealment.js +3 -0
- package/dist/sensor/freezeMonitor.d.ts +1 -0
- package/dist/sensor/freezeMonitor.js +1 -0
- package/dist/sensor/frustumSensor.d.ts +7 -3
- package/dist/sensor/frustumSensor.js +7 -3
- package/dist/sensor/hiddenStateProbe.d.ts +5 -2
- package/dist/sensor/hiddenStateProbe.js +5 -2
- package/dist/sensor/recordingBuffer.d.ts +2 -1
- package/dist/sensor/recordingBuffer.js +2 -1
- package/dist/sensor/replayLoop.d.ts +6 -2
- package/dist/sensor/replayLoop.js +6 -2
- package/dist/sensor/revealQuery.d.ts +2 -1
- package/dist/sensor/revealQuery.js +2 -1
- package/dist/sensor/visionCone.d.ts +10 -3
- package/dist/sensor/visionCone.js +10 -3
- package/dist/session/contestedChannel.d.ts +1 -0
- package/dist/session/contestedChannel.js +1 -0
- package/dist/session/extraction.d.ts +1 -0
- package/dist/session/extraction.js +1 -0
- package/dist/session/roles.d.ts +6 -0
- package/dist/session/roles.js +6 -0
- package/dist/session/roundState.d.ts +8 -0
- package/dist/session/roundState.js +2 -0
- package/dist/settings/settingsModel.d.ts +1 -0
- package/dist/settings/settingsModel.js +1 -0
- package/dist/stats/accumulatorMeter.d.ts +6 -0
- package/dist/stats/accumulatorMeter.js +6 -0
- package/dist/stats/eventMeter.d.ts +5 -0
- package/dist/stats/eventMeter.js +5 -0
- package/dist/stats/rollCheck.d.ts +5 -0
- package/dist/stats/rollCheck.js +5 -0
- package/dist/stats/spawnLevelStats.d.ts +110 -0
- package/dist/stats/spawnLevelStats.js +140 -0
- package/dist/stats/statModifiers.d.ts +5 -0
- package/dist/stats/statModifiers.js +5 -0
- package/dist/stats/statPool.d.ts +70 -0
- package/dist/stats/statPool.js +59 -0
- package/dist/store/changeSignal.d.ts +3 -0
- package/dist/store/changeSignal.js +2 -0
- package/dist/store/defineKeyedStore.d.ts +38 -0
- package/dist/store/defineKeyedStore.js +42 -0
- package/dist/store/defineStore.d.ts +33 -0
- package/dist/store/defineStore.js +35 -0
- package/dist/store/observableKeyedStore.d.ts +7 -0
- package/dist/store/observableKeyedStore.js +35 -2
- package/dist/survival/decayMeter.d.ts +55 -0
- package/dist/survival/decayMeter.js +130 -57
- package/dist/survival/moodle.d.ts +2 -0
- package/dist/survival/regionHealth.d.ts +6 -0
- package/dist/survival/regionHealth.js +6 -3
- package/dist/tactics/fallingGrid.d.ts +4 -2
- package/dist/tactics/fallingGrid.js +4 -2
- package/dist/tactics/predictiveQuery.d.ts +3 -0
- package/dist/tactics/predictiveQuery.js +3 -0
- package/dist/tactics/snapshot.d.ts +2 -0
- package/dist/tactics/snapshot.js +2 -0
- package/dist/tactics/surface.d.ts +1 -0
- package/dist/tactics/surface.js +1 -0
- package/dist/tactics/tacticalGrid.d.ts +1 -0
- package/dist/tactics/tacticalGrid.js +1 -0
- package/dist/time/beatClock.d.ts +14 -4
- package/dist/time/beatClock.js +14 -4
- package/dist/time/calendarClock.d.ts +3 -0
- package/dist/time/calendarClock.js +3 -0
- package/dist/time/idleProgress.d.ts +4 -0
- package/dist/time/idleProgress.js +4 -0
- package/dist/time/simClock.d.ts +2 -0
- package/dist/time/simClock.js +20 -5
- package/dist/ui/actionModel.d.ts +110 -0
- package/dist/ui/actionModel.js +159 -0
- package/dist/ui/gameLayout.d.ts +30 -11
- package/dist/ui/gameLayout.js +30 -11
- package/dist/ui/hudDocument.d.ts +114 -0
- package/dist/ui/hudDocument.js +220 -0
- package/dist/ui/hudLayout.d.ts +38 -1
- package/dist/ui/hudLayout.js +220 -17
- package/dist/ui/selectionModel.d.ts +92 -0
- package/dist/ui/selectionModel.js +88 -0
- package/dist/ui/tooltipModel.d.ts +63 -0
- package/dist/ui/tooltipModel.js +82 -0
- package/dist/ui.d.ts +15 -0
- package/dist/ui.js +15 -0
- package/dist/visibility/bounds.d.ts +8 -1
- package/dist/visibility/bounds.js +8 -1
- package/dist/visibility/frustum.d.ts +12 -3
- package/dist/visibility/frustum.js +12 -3
- package/dist/work/jobQueue.d.ts +201 -0
- package/dist/work/jobQueue.js +253 -0
- package/dist/work/unitTraining.d.ts +58 -0
- package/dist/work/unitTraining.js +52 -0
- package/dist/world/authoredEntities.d.ts +29 -0
- package/dist/world/authoredEntities.js +29 -0
- package/dist/world/authoredObjects.d.ts +72 -0
- package/dist/world/authoredObjects.js +81 -0
- package/dist/world/authoredSpawn.d.ts +39 -0
- package/dist/world/authoredSpawn.js +37 -0
- package/dist/world/buildSockets.d.ts +152 -0
- package/dist/world/buildSockets.js +188 -0
- package/dist/world/buildingGenerator.d.ts +18 -0
- package/dist/world/buildingGenerator.js +62 -0
- package/dist/world/buildings.d.ts +5 -0
- package/dist/world/buildings.js +5 -0
- package/dist/world/catenary.d.ts +22 -0
- package/dist/world/catenary.js +69 -0
- package/dist/world/cityBlocks.d.ts +152 -0
- package/dist/world/cityBlocks.js +670 -0
- package/dist/world/cityContent.d.ts +77 -0
- package/dist/world/cityContent.js +261 -0
- package/dist/world/cityGeometry.d.ts +114 -0
- package/dist/world/cityGeometry.js +573 -0
- package/dist/world/cityKind.d.ts +314 -0
- package/dist/world/cityKind.js +1578 -0
- package/dist/world/features.d.ts +133 -10
- package/dist/world/features.js +66 -6
- package/dist/world/footprintGrid.d.ts +59 -0
- package/dist/world/footprintGrid.js +141 -0
- package/dist/world/geometry.d.ts +64 -0
- package/dist/world/geometry.js +94 -0
- package/dist/world/grassKind.d.ts +51 -0
- package/dist/world/grassKind.js +101 -0
- package/dist/world/markers.d.ts +50 -4
- package/dist/world/markers.js +49 -0
- package/dist/world/massing.js +1 -1
- package/dist/world/minimapBake.d.ts +78 -0
- package/dist/world/minimapBake.js +208 -0
- package/dist/world/minimapTrack.d.ts +17 -0
- package/dist/world/minimapTrack.js +22 -0
- package/dist/world/pathInstances.d.ts +27 -0
- package/dist/world/pathInstances.js +27 -0
- package/dist/world/pathNetwork.d.ts +159 -0
- package/dist/world/pathNetwork.js +684 -0
- package/dist/world/pathTerrain.d.ts +80 -0
- package/dist/world/pathTerrain.js +142 -0
- package/dist/world/place.d.ts +166 -0
- package/dist/world/place.js +96 -0
- package/dist/world/placeAsset.d.ts +81 -0
- package/dist/world/placeAsset.js +79 -0
- package/dist/world/placement.d.ts +4 -0
- package/dist/world/placement.js +4 -0
- package/dist/world/placementController.d.ts +6 -0
- package/dist/world/placementController.js +6 -0
- package/dist/world/poleLineKind.d.ts +40 -0
- package/dist/world/poleLineKind.js +97 -0
- package/dist/world/polyline.d.ts +34 -0
- package/dist/world/polyline.js +88 -0
- package/dist/world/resourceNode.d.ts +109 -0
- package/dist/world/resourceNode.js +140 -0
- package/dist/world/roads.d.ts +8 -0
- package/dist/world/roads.js +38 -0
- package/dist/world/scatter.js +2 -20
- package/dist/world/scatterRegion.d.ts +164 -0
- package/dist/world/scatterRegion.js +409 -0
- package/dist/world/sceneShapes.d.ts +43 -0
- package/dist/world/sceneShapes.js +1 -0
- package/dist/world/segment.d.ts +27 -0
- package/dist/world/segment.js +43 -0
- package/dist/world/soilKind.d.ts +48 -0
- package/dist/world/soilKind.js +92 -0
- package/dist/world/spatialSample.d.ts +241 -0
- package/dist/world/spatialSample.js +465 -0
- package/dist/world/streets.d.ts +29 -0
- package/dist/world/streets.js +55 -0
- package/dist/world/terraform.d.ts +213 -1
- package/dist/world/terraform.js +627 -22
- package/dist/world/terrain.d.ts +164 -20
- package/dist/world/terrain.js +325 -41
- package/dist/world/terrainGuides.d.ts +182 -0
- package/dist/world/terrainGuides.js +343 -0
- package/dist/world/vec2.d.ts +81 -0
- package/dist/world/vec2.js +128 -0
- package/dist/world/vec3.d.ts +49 -0
- package/dist/world/vec3.js +76 -0
- package/dist/world/vegetation.d.ts +71 -0
- package/dist/world/vegetation.js +151 -0
- package/dist/world/visibilityField.d.ts +154 -0
- package/dist/world/visibilityField.js +271 -0
- package/dist/world/volumetricClouds.d.ts +52 -0
- package/dist/world/volumetricClouds.js +43 -0
- package/dist/world/walls.d.ts +9 -0
- package/dist/world/walls.js +9 -0
- package/dist/world/waterKind.d.ts +55 -0
- package/dist/world/waterKind.js +99 -0
- package/dist/world.d.ts +141 -0
- package/dist/world.js +141 -0
- package/package.json +32 -8
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +54 -0
- package/skills/jgengine/api.md +527 -0
- package/skills/jgengine/capabilities.md +131 -0
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-combat/SKILL.md +38 -0
- package/skills/jgengine-combat/api.md +585 -0
- package/skills/jgengine-combat/capabilities.md +213 -0
- package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
- package/skills/jgengine-combat/reference.md +108 -0
- package/skills/jgengine-gameplay/SKILL.md +39 -0
- package/skills/jgengine-gameplay/api.md +1920 -0
- package/skills/jgengine-gameplay/capabilities.md +454 -0
- package/skills/jgengine-gameplay/reference-systems.md +56 -0
- package/skills/jgengine-world/SKILL.md +51 -0
- package/skills/jgengine-world/api.md +3056 -0
- package/skills/jgengine-world/capabilities.md +476 -0
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +261 -0
- package/dist/cartridge/runtime.d.ts +0 -15
- package/dist/cartridge/runtime.js +0 -556
- package/dist/cartridge/spec.d.ts +0 -188
- package/dist/cartridge/spec.js +0 -19
- package/dist/cartridge/validate.d.ts +0 -2
- package/dist/cartridge/validate.js +0 -78
- package/dist/store/engineStore.d.ts +0 -22
- package/dist/store/engineStore.js +0 -50
- package/dist/store/features.d.ts +0 -10
- package/dist/store/features.js +0 -23
- package/llms.txt +0 -5389
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
2
|
+
const BASE64_INDEX = new Int8Array(128).fill(-1);
|
|
3
|
+
for (let i = 0; i < BASE64.length; i += 1)
|
|
4
|
+
BASE64_INDEX[BASE64.charCodeAt(i)] = i;
|
|
5
|
+
function bytesToBase64(bytes) {
|
|
6
|
+
let out = "";
|
|
7
|
+
let i = 0;
|
|
8
|
+
for (; i + 2 < bytes.length; i += 3) {
|
|
9
|
+
const chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
|
10
|
+
out += BASE64[(chunk >> 18) & 63] + BASE64[(chunk >> 12) & 63] + BASE64[(chunk >> 6) & 63] + BASE64[chunk & 63];
|
|
11
|
+
}
|
|
12
|
+
const rest = bytes.length - i;
|
|
13
|
+
if (rest === 1) {
|
|
14
|
+
const chunk = bytes[i] << 16;
|
|
15
|
+
out += BASE64[(chunk >> 18) & 63] + BASE64[(chunk >> 12) & 63] + "==";
|
|
16
|
+
}
|
|
17
|
+
else if (rest === 2) {
|
|
18
|
+
const chunk = (bytes[i] << 16) | (bytes[i + 1] << 8);
|
|
19
|
+
out += BASE64[(chunk >> 18) & 63] + BASE64[(chunk >> 12) & 63] + BASE64[(chunk >> 6) & 63] + "=";
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function base64ToBytes(text) {
|
|
24
|
+
let length = text.length;
|
|
25
|
+
while (length > 0 && text[length - 1] === "=")
|
|
26
|
+
length -= 1;
|
|
27
|
+
if (text.length % 4 !== 0 && text.length - length !== 0)
|
|
28
|
+
return null;
|
|
29
|
+
const byteLength = Math.floor((length * 3) / 4);
|
|
30
|
+
const out = new Uint8Array(byteLength);
|
|
31
|
+
let outIndex = 0;
|
|
32
|
+
let buffer = 0;
|
|
33
|
+
let bits = 0;
|
|
34
|
+
for (let i = 0; i < length; i += 1) {
|
|
35
|
+
const code = text.charCodeAt(i);
|
|
36
|
+
const value = code < 128 ? BASE64_INDEX[code] : -1;
|
|
37
|
+
if (value < 0)
|
|
38
|
+
return null;
|
|
39
|
+
buffer = (buffer << 6) | value;
|
|
40
|
+
bits += 6;
|
|
41
|
+
if (bits >= 8) {
|
|
42
|
+
bits -= 8;
|
|
43
|
+
out[outIndex] = (buffer >> bits) & 0xff;
|
|
44
|
+
outIndex += 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (outIndex !== byteLength)
|
|
48
|
+
return null;
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Quantize a triangle soup onto the 16-bit grid, weld coincident vertices, and drop triangles that
|
|
53
|
+
* collapse — the reindex-time compressor producing {@link CollisionMeshData}. Returns `null` when no
|
|
54
|
+
* finite, non-degenerate triangle survives.
|
|
55
|
+
*/
|
|
56
|
+
export function encodeCollisionMesh(source) {
|
|
57
|
+
const positions = source.positions;
|
|
58
|
+
const indices = source.indices;
|
|
59
|
+
if (positions.length < 9 || positions.length % 3 !== 0)
|
|
60
|
+
return null;
|
|
61
|
+
if (indices.length < 3 || indices.length % 3 !== 0)
|
|
62
|
+
return null;
|
|
63
|
+
const sourceVertexCount = positions.length / 3;
|
|
64
|
+
let minX = Infinity;
|
|
65
|
+
let minY = Infinity;
|
|
66
|
+
let minZ = Infinity;
|
|
67
|
+
let maxX = -Infinity;
|
|
68
|
+
let maxY = -Infinity;
|
|
69
|
+
let maxZ = -Infinity;
|
|
70
|
+
const referenced = new Uint8Array(sourceVertexCount);
|
|
71
|
+
for (let i = 0; i < indices.length; i += 1) {
|
|
72
|
+
const index = indices[i];
|
|
73
|
+
if (!Number.isInteger(index) || index < 0 || index >= sourceVertexCount)
|
|
74
|
+
continue;
|
|
75
|
+
referenced[index] = 1;
|
|
76
|
+
}
|
|
77
|
+
for (let v = 0; v < sourceVertexCount; v += 1) {
|
|
78
|
+
if (referenced[v] === 0)
|
|
79
|
+
continue;
|
|
80
|
+
const x = positions[v * 3];
|
|
81
|
+
const y = positions[v * 3 + 1];
|
|
82
|
+
const z = positions[v * 3 + 2];
|
|
83
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
|
|
84
|
+
referenced[v] = 0;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (x < minX)
|
|
88
|
+
minX = x;
|
|
89
|
+
if (y < minY)
|
|
90
|
+
minY = y;
|
|
91
|
+
if (z < minZ)
|
|
92
|
+
minZ = z;
|
|
93
|
+
if (x > maxX)
|
|
94
|
+
maxX = x;
|
|
95
|
+
if (y > maxY)
|
|
96
|
+
maxY = y;
|
|
97
|
+
if (z > maxZ)
|
|
98
|
+
maxZ = z;
|
|
99
|
+
}
|
|
100
|
+
if (!Number.isFinite(minX) || !Number.isFinite(maxX))
|
|
101
|
+
return null;
|
|
102
|
+
const spanX = maxX - minX;
|
|
103
|
+
const spanY = maxY - minY;
|
|
104
|
+
const spanZ = maxZ - minZ;
|
|
105
|
+
const quantized = new Int32Array(sourceVertexCount).fill(-1);
|
|
106
|
+
const welded = new Map();
|
|
107
|
+
const weldedQuantized = [];
|
|
108
|
+
for (let v = 0; v < sourceVertexCount; v += 1) {
|
|
109
|
+
if (referenced[v] === 0)
|
|
110
|
+
continue;
|
|
111
|
+
const qx = spanX > 0 ? Math.round(((positions[v * 3] - minX) / spanX) * 65535) : 0;
|
|
112
|
+
const qy = spanY > 0 ? Math.round(((positions[v * 3 + 1] - minY) / spanY) * 65535) : 0;
|
|
113
|
+
const qz = spanZ > 0 ? Math.round(((positions[v * 3 + 2] - minZ) / spanZ) * 65535) : 0;
|
|
114
|
+
const key = qx + qy * 65536 + qz * 4294967296;
|
|
115
|
+
let weldedIndex = welded.get(key);
|
|
116
|
+
if (weldedIndex === undefined) {
|
|
117
|
+
weldedIndex = weldedQuantized.length / 3;
|
|
118
|
+
welded.set(key, weldedIndex);
|
|
119
|
+
weldedQuantized.push(qx, qy, qz);
|
|
120
|
+
}
|
|
121
|
+
quantized[v] = weldedIndex;
|
|
122
|
+
}
|
|
123
|
+
const triangles = [];
|
|
124
|
+
for (let t = 0; t + 2 < indices.length; t += 3) {
|
|
125
|
+
const a = quantized[indices[t]] ?? -1;
|
|
126
|
+
const b = quantized[indices[t + 1]] ?? -1;
|
|
127
|
+
const c = quantized[indices[t + 2]] ?? -1;
|
|
128
|
+
if (a < 0 || b < 0 || c < 0)
|
|
129
|
+
continue;
|
|
130
|
+
if (a === b || b === c || a === c)
|
|
131
|
+
continue;
|
|
132
|
+
triangles.push(a, b, c);
|
|
133
|
+
}
|
|
134
|
+
if (triangles.length === 0)
|
|
135
|
+
return null;
|
|
136
|
+
const vertexCount = weldedQuantized.length / 3;
|
|
137
|
+
const positionBytes = new Uint8Array(vertexCount * 6);
|
|
138
|
+
const positionView = new DataView(positionBytes.buffer);
|
|
139
|
+
for (let i = 0; i < weldedQuantized.length; i += 1) {
|
|
140
|
+
positionView.setUint16(i * 2, weldedQuantized[i], true);
|
|
141
|
+
}
|
|
142
|
+
const wide = vertexCount > 65536;
|
|
143
|
+
const indexBytes = new Uint8Array(triangles.length * (wide ? 4 : 2));
|
|
144
|
+
const indexView = new DataView(indexBytes.buffer);
|
|
145
|
+
for (let i = 0; i < triangles.length; i += 1) {
|
|
146
|
+
if (wide)
|
|
147
|
+
indexView.setUint32(i * 4, triangles[i], true);
|
|
148
|
+
else
|
|
149
|
+
indexView.setUint16(i * 2, triangles[i], true);
|
|
150
|
+
}
|
|
151
|
+
const boxes = voxelizeToBoxes(source, 0.25, 32);
|
|
152
|
+
return {
|
|
153
|
+
min: [minX, minY, minZ],
|
|
154
|
+
max: [maxX, maxY, maxZ],
|
|
155
|
+
vertexCount,
|
|
156
|
+
triangleCount: triangles.length / 3,
|
|
157
|
+
positions: bytesToBase64(positionBytes),
|
|
158
|
+
indices: bytesToBase64(indexBytes),
|
|
159
|
+
...(boxes.length > 0 ? { boxes } : {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/** SAT test: do the three triangle vertices (already translated so the cell center is the origin) overlap
|
|
163
|
+
* an axis-aligned box of half-size `h`? Projects both onto `axis`; returns false only on a separating gap. */
|
|
164
|
+
function triangleSeparatedOnAxis(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z, ax, ay, az, hx, hy, hz) {
|
|
165
|
+
const p0 = v0x * ax + v0y * ay + v0z * az;
|
|
166
|
+
const p1 = v1x * ax + v1y * ay + v1z * az;
|
|
167
|
+
const p2 = v2x * ax + v2y * ay + v2z * az;
|
|
168
|
+
const min = Math.min(p0, p1, p2);
|
|
169
|
+
const max = Math.max(p0, p1, p2);
|
|
170
|
+
const radius = Math.abs(ax) * hx + Math.abs(ay) * hy + Math.abs(az) * hz;
|
|
171
|
+
return min > radius || max < -radius;
|
|
172
|
+
}
|
|
173
|
+
/** Akenine–Möller triangle vs axis-aligned box overlap (13-axis SAT), box given by center + half-size. */
|
|
174
|
+
function triangleOverlapsBox(ax, ay, az, bx, by, bz, cx, cy, cz, centerX, centerY, centerZ, hx, hy, hz) {
|
|
175
|
+
const v0x = ax - centerX, v0y = ay - centerY, v0z = az - centerZ;
|
|
176
|
+
const v1x = bx - centerX, v1y = by - centerY, v1z = bz - centerZ;
|
|
177
|
+
const v2x = cx - centerX, v2y = cy - centerY, v2z = cz - centerZ;
|
|
178
|
+
const e0x = v1x - v0x, e0y = v1y - v0y, e0z = v1z - v0z;
|
|
179
|
+
const e1x = v2x - v1x, e1y = v2y - v1y, e1z = v2z - v1z;
|
|
180
|
+
const e2x = v0x - v2x, e2y = v0y - v2y, e2z = v0z - v2z;
|
|
181
|
+
const edges = [
|
|
182
|
+
[e0x, e0y, e0z],
|
|
183
|
+
[e1x, e1y, e1z],
|
|
184
|
+
[e2x, e2y, e2z],
|
|
185
|
+
];
|
|
186
|
+
// 9 axes: each triangle edge crossed with each box face normal (X, Y, Z).
|
|
187
|
+
for (const [ex, ey, ez] of edges) {
|
|
188
|
+
// edge × X = (0, -ez, ey); edge × Y = (ez, 0, -ex); edge × Z = (-ey, ex, 0)
|
|
189
|
+
if (triangleSeparatedOnAxis(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z, 0, -ez, ey, hx, hy, hz))
|
|
190
|
+
return false;
|
|
191
|
+
if (triangleSeparatedOnAxis(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z, ez, 0, -ex, hx, hy, hz))
|
|
192
|
+
return false;
|
|
193
|
+
if (triangleSeparatedOnAxis(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z, -ey, ex, 0, hx, hy, hz))
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
// 3 box face normals.
|
|
197
|
+
if (triangleSeparatedOnAxis(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z, 1, 0, 0, hx, hy, hz))
|
|
198
|
+
return false;
|
|
199
|
+
if (triangleSeparatedOnAxis(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z, 0, 1, 0, hx, hy, hz))
|
|
200
|
+
return false;
|
|
201
|
+
if (triangleSeparatedOnAxis(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z, 0, 0, 1, hx, hy, hz))
|
|
202
|
+
return false;
|
|
203
|
+
// 1 axis: the triangle's face normal.
|
|
204
|
+
const nx = e0y * e1z - e0z * e1y;
|
|
205
|
+
const ny = e0z * e1x - e0x * e1z;
|
|
206
|
+
const nz = e0x * e1y - e0y * e1x;
|
|
207
|
+
if ((nx !== 0 || ny !== 0 || nz !== 0) &&
|
|
208
|
+
triangleSeparatedOnAxis(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z, nx, ny, nz, hx, hy, hz)) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
const VOXEL_MAX_CELLS = 2_000_000;
|
|
214
|
+
/**
|
|
215
|
+
* Decompose a triangle soup into a compact, deterministic set of model-space AABBs approximating its
|
|
216
|
+
* solid volume — the movement-obstruction counterpart to the raycast BVH. Rasterizes triangles onto a
|
|
217
|
+
* `cellSize` voxel grid, fills each vertical column between its lowest and highest occupied voxel (so a
|
|
218
|
+
* solid becomes solid and a concave opening — an archway — keeps its central gap empty while its pillars
|
|
219
|
+
* and lintel fill in), then greedy-merges occupied voxels into AABBs in a fixed z→y→x order. Pure and
|
|
220
|
+
* allocation-tolerant (runs once per asset at reindex). Caps the output at `maxBoxes`, folding any
|
|
221
|
+
* remainder into one enclosing AABB so obstruction stays conservative. Returns `[]` for degenerate input.
|
|
222
|
+
* @internal
|
|
223
|
+
*/
|
|
224
|
+
export function voxelizeToBoxes(source, cellSize = 0.25, maxBoxes = 32) {
|
|
225
|
+
const positions = source.positions;
|
|
226
|
+
const indices = source.indices;
|
|
227
|
+
if (!(cellSize > 0) || !Number.isFinite(cellSize))
|
|
228
|
+
return [];
|
|
229
|
+
if (!(maxBoxes >= 1))
|
|
230
|
+
return [];
|
|
231
|
+
if (positions.length < 9 || positions.length % 3 !== 0)
|
|
232
|
+
return [];
|
|
233
|
+
if (indices.length < 3 || indices.length % 3 !== 0)
|
|
234
|
+
return [];
|
|
235
|
+
const vertexCount = positions.length / 3;
|
|
236
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
237
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
238
|
+
const referenced = new Uint8Array(vertexCount);
|
|
239
|
+
for (let i = 0; i < indices.length; i += 1) {
|
|
240
|
+
const index = indices[i];
|
|
241
|
+
if (!Number.isInteger(index) || index < 0 || index >= vertexCount)
|
|
242
|
+
continue;
|
|
243
|
+
referenced[index] = 1;
|
|
244
|
+
}
|
|
245
|
+
for (let v = 0; v < vertexCount; v += 1) {
|
|
246
|
+
if (referenced[v] === 0)
|
|
247
|
+
continue;
|
|
248
|
+
const x = positions[v * 3], y = positions[v * 3 + 1], z = positions[v * 3 + 2];
|
|
249
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
|
|
250
|
+
referenced[v] = 0;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (x < minX)
|
|
254
|
+
minX = x;
|
|
255
|
+
if (y < minY)
|
|
256
|
+
minY = y;
|
|
257
|
+
if (z < minZ)
|
|
258
|
+
minZ = z;
|
|
259
|
+
if (x > maxX)
|
|
260
|
+
maxX = x;
|
|
261
|
+
if (y > maxY)
|
|
262
|
+
maxY = y;
|
|
263
|
+
if (z > maxZ)
|
|
264
|
+
maxZ = z;
|
|
265
|
+
}
|
|
266
|
+
if (!Number.isFinite(minX) || !Number.isFinite(maxX))
|
|
267
|
+
return [];
|
|
268
|
+
const spanX = maxX - minX, spanY = maxY - minY, spanZ = maxZ - minZ;
|
|
269
|
+
if (!(spanX > 0) && !(spanY > 0) && !(spanZ > 0))
|
|
270
|
+
return [];
|
|
271
|
+
// Coarsen the grid up until it fits the cell budget, so a huge model can't allocate an enormous grid.
|
|
272
|
+
let cell = cellSize;
|
|
273
|
+
let nx = 1, ny = 1, nz = 1;
|
|
274
|
+
for (;;) {
|
|
275
|
+
nx = Math.max(1, Math.ceil(spanX / cell));
|
|
276
|
+
ny = Math.max(1, Math.ceil(spanY / cell));
|
|
277
|
+
nz = Math.max(1, Math.ceil(spanZ / cell));
|
|
278
|
+
if (nx * ny * nz <= VOXEL_MAX_CELLS)
|
|
279
|
+
break;
|
|
280
|
+
cell *= 2;
|
|
281
|
+
}
|
|
282
|
+
const total = nx * ny * nz;
|
|
283
|
+
const stride = nx * ny;
|
|
284
|
+
const occupancy = new Uint8Array(total);
|
|
285
|
+
// Rasterize: mark every voxel each triangle actually overlaps (true SAT, not a fat AABB), so a triangle
|
|
286
|
+
// spanning an opening never leaks solidity into the gap between the surfaces it connects.
|
|
287
|
+
const half = cell / 2;
|
|
288
|
+
for (let t = 0; t + 2 < indices.length; t += 3) {
|
|
289
|
+
const ia = indices[t], ib = indices[t + 1], ic = indices[t + 2];
|
|
290
|
+
if (referenced[ia] === 0 || referenced[ib] === 0 || referenced[ic] === 0)
|
|
291
|
+
continue;
|
|
292
|
+
const ax = positions[ia * 3], ay = positions[ia * 3 + 1], az = positions[ia * 3 + 2];
|
|
293
|
+
const bx = positions[ib * 3], by = positions[ib * 3 + 1], bz = positions[ib * 3 + 2];
|
|
294
|
+
const cx = positions[ic * 3], cy = positions[ic * 3 + 1], cz = positions[ic * 3 + 2];
|
|
295
|
+
const cellOf = (v, mn, n) => Math.min(n - 1, Math.max(0, Math.floor((v - mn) / cell)));
|
|
296
|
+
const ix0 = cellOf(Math.min(ax, bx, cx), minX, nx);
|
|
297
|
+
const ix1 = cellOf(Math.max(ax, bx, cx), minX, nx);
|
|
298
|
+
const iy0 = cellOf(Math.min(ay, by, cy), minY, ny);
|
|
299
|
+
const iy1 = cellOf(Math.max(ay, by, cy), minY, ny);
|
|
300
|
+
const iz0 = cellOf(Math.min(az, bz, cz), minZ, nz);
|
|
301
|
+
const iz1 = cellOf(Math.max(az, bz, cz), minZ, nz);
|
|
302
|
+
for (let iz = iz0; iz <= iz1; iz += 1) {
|
|
303
|
+
const czc = minZ + (iz + 0.5) * cell;
|
|
304
|
+
for (let iy = iy0; iy <= iy1; iy += 1) {
|
|
305
|
+
const cyc = minY + (iy + 0.5) * cell;
|
|
306
|
+
for (let ix = ix0; ix <= ix1; ix += 1) {
|
|
307
|
+
const idx = ix + iy * nx + iz * stride;
|
|
308
|
+
if (occupancy[idx] === 1)
|
|
309
|
+
continue;
|
|
310
|
+
const cxc = minX + (ix + 0.5) * cell;
|
|
311
|
+
if (triangleOverlapsBox(ax, ay, az, bx, by, bz, cx, cy, cz, cxc, cyc, czc, half, half, half)) {
|
|
312
|
+
occupancy[idx] = 1;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Column fill: solidify each (x,z) column between its lowest and highest occupied voxel. A span becomes
|
|
319
|
+
// a filled interior; an arch keeps its central gap empty (that column only touches the lintel up top).
|
|
320
|
+
for (let iz = 0; iz < nz; iz += 1) {
|
|
321
|
+
for (let ix = 0; ix < nx; ix += 1) {
|
|
322
|
+
let firstY = -1, lastY = -1;
|
|
323
|
+
for (let iy = 0; iy < ny; iy += 1) {
|
|
324
|
+
if (occupancy[ix + iy * nx + iz * stride] === 1) {
|
|
325
|
+
if (firstY < 0)
|
|
326
|
+
firstY = iy;
|
|
327
|
+
lastY = iy;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
for (let iy = firstY; iy <= lastY; iy += 1)
|
|
331
|
+
occupancy[ix + iy * nx + iz * stride] = 1;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Greedy-merge occupied voxels into AABBs in fixed z→y→x order: grow +x along the row, then +y over the
|
|
335
|
+
// whole x-strip, then +z over the whole xy-slab. Deterministic — output order is the scan order.
|
|
336
|
+
const cellToModel = (i, mn, mx) => Math.min(mx, mn + i * cell);
|
|
337
|
+
const claimed = new Uint8Array(total);
|
|
338
|
+
const boxes = [];
|
|
339
|
+
for (let z0 = 0; z0 < nz; z0 += 1) {
|
|
340
|
+
for (let y0 = 0; y0 < ny; y0 += 1) {
|
|
341
|
+
for (let x0 = 0; x0 < nx; x0 += 1) {
|
|
342
|
+
const i0 = x0 + y0 * nx + z0 * stride;
|
|
343
|
+
if (occupancy[i0] === 0 || claimed[i0] === 1)
|
|
344
|
+
continue;
|
|
345
|
+
// At the cap, fold this voxel and every remaining unclaimed occupied voxel into one enclosing AABB.
|
|
346
|
+
if (boxes.length === maxBoxes - 1) {
|
|
347
|
+
let ex0 = nx, ey0 = ny, ez0 = nz, ex1 = -1, ey1 = -1, ez1 = -1;
|
|
348
|
+
for (let z = 0; z < nz; z += 1)
|
|
349
|
+
for (let y = 0; y < ny; y += 1)
|
|
350
|
+
for (let x = 0; x < nx; x += 1) {
|
|
351
|
+
const i = x + y * nx + z * stride;
|
|
352
|
+
if (occupancy[i] === 0 || claimed[i] === 1)
|
|
353
|
+
continue;
|
|
354
|
+
if (x < ex0)
|
|
355
|
+
ex0 = x;
|
|
356
|
+
if (y < ey0)
|
|
357
|
+
ey0 = y;
|
|
358
|
+
if (z < ez0)
|
|
359
|
+
ez0 = z;
|
|
360
|
+
if (x > ex1)
|
|
361
|
+
ex1 = x;
|
|
362
|
+
if (y > ey1)
|
|
363
|
+
ey1 = y;
|
|
364
|
+
if (z > ez1)
|
|
365
|
+
ez1 = z;
|
|
366
|
+
}
|
|
367
|
+
boxes.push({
|
|
368
|
+
min: [cellToModel(ex0, minX, maxX), cellToModel(ey0, minY, maxY), cellToModel(ez0, minZ, maxZ)],
|
|
369
|
+
max: [cellToModel(ex1 + 1, minX, maxX), cellToModel(ey1 + 1, minY, maxY), cellToModel(ez1 + 1, minZ, maxZ)],
|
|
370
|
+
});
|
|
371
|
+
return boxes;
|
|
372
|
+
}
|
|
373
|
+
let x1 = x0;
|
|
374
|
+
while (x1 + 1 < nx) {
|
|
375
|
+
const i = x1 + 1 + y0 * nx + z0 * stride;
|
|
376
|
+
if (occupancy[i] === 0 || claimed[i] === 1)
|
|
377
|
+
break;
|
|
378
|
+
x1 += 1;
|
|
379
|
+
}
|
|
380
|
+
let y1 = y0;
|
|
381
|
+
for (let grow = true; grow && y1 + 1 < ny;) {
|
|
382
|
+
const yy = y1 + 1;
|
|
383
|
+
for (let x = x0; x <= x1; x += 1) {
|
|
384
|
+
const i = x + yy * nx + z0 * stride;
|
|
385
|
+
if (occupancy[i] === 0 || claimed[i] === 1) {
|
|
386
|
+
grow = false;
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (grow)
|
|
391
|
+
y1 = yy;
|
|
392
|
+
}
|
|
393
|
+
let z1 = z0;
|
|
394
|
+
for (let grow = true; grow && z1 + 1 < nz;) {
|
|
395
|
+
const zz = z1 + 1;
|
|
396
|
+
for (let y = y0; grow && y <= y1; y += 1) {
|
|
397
|
+
for (let x = x0; x <= x1; x += 1) {
|
|
398
|
+
const i = x + y * nx + zz * stride;
|
|
399
|
+
if (occupancy[i] === 0 || claimed[i] === 1) {
|
|
400
|
+
grow = false;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (grow)
|
|
406
|
+
z1 = zz;
|
|
407
|
+
}
|
|
408
|
+
for (let z = z0; z <= z1; z += 1)
|
|
409
|
+
for (let y = y0; y <= y1; y += 1)
|
|
410
|
+
for (let x = x0; x <= x1; x += 1)
|
|
411
|
+
claimed[x + y * nx + z * stride] = 1;
|
|
412
|
+
boxes.push({
|
|
413
|
+
min: [cellToModel(x0, minX, maxX), cellToModel(y0, minY, maxY), cellToModel(z0, minZ, maxZ)],
|
|
414
|
+
max: [cellToModel(x1 + 1, minX, maxX), cellToModel(y1 + 1, minY, maxY), cellToModel(z1 + 1, minZ, maxZ)],
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return boxes;
|
|
420
|
+
}
|
|
421
|
+
const LEAF_TRIANGLES = 4;
|
|
422
|
+
function buildBvh(positions, indices) {
|
|
423
|
+
const triangleCount = indices.length / 3;
|
|
424
|
+
const triOrder = new Uint32Array(triangleCount);
|
|
425
|
+
for (let t = 0; t < triangleCount; t += 1)
|
|
426
|
+
triOrder[t] = t;
|
|
427
|
+
const triBounds = new Float32Array(triangleCount * 6);
|
|
428
|
+
const centroids = new Float32Array(triangleCount * 3);
|
|
429
|
+
for (let t = 0; t < triangleCount; t += 1) {
|
|
430
|
+
const a = indices[t * 3] * 3;
|
|
431
|
+
const b = indices[t * 3 + 1] * 3;
|
|
432
|
+
const c = indices[t * 3 + 2] * 3;
|
|
433
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
434
|
+
const pa = positions[a + axis];
|
|
435
|
+
const pb = positions[b + axis];
|
|
436
|
+
const pc = positions[c + axis];
|
|
437
|
+
const lo = Math.min(pa, pb, pc);
|
|
438
|
+
const hi = Math.max(pa, pb, pc);
|
|
439
|
+
triBounds[t * 6 + axis] = lo;
|
|
440
|
+
triBounds[t * 6 + 3 + axis] = hi;
|
|
441
|
+
centroids[t * 3 + axis] = (lo + hi) / 2;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const maxNodes = Math.max(1, 2 * triangleCount - 1);
|
|
445
|
+
const nodeBounds = new Float32Array(maxNodes * 6);
|
|
446
|
+
const nodeStart = new Int32Array(maxNodes);
|
|
447
|
+
const nodeCount = new Int32Array(maxNodes);
|
|
448
|
+
let usedNodes = 0;
|
|
449
|
+
function build(start, count) {
|
|
450
|
+
const node = usedNodes;
|
|
451
|
+
usedNodes += 1;
|
|
452
|
+
let minX = Infinity;
|
|
453
|
+
let minY = Infinity;
|
|
454
|
+
let minZ = Infinity;
|
|
455
|
+
let maxX = -Infinity;
|
|
456
|
+
let maxY = -Infinity;
|
|
457
|
+
let maxZ = -Infinity;
|
|
458
|
+
for (let i = start; i < start + count; i += 1) {
|
|
459
|
+
const t = triOrder[i] * 6;
|
|
460
|
+
if (triBounds[t] < minX)
|
|
461
|
+
minX = triBounds[t];
|
|
462
|
+
if (triBounds[t + 1] < minY)
|
|
463
|
+
minY = triBounds[t + 1];
|
|
464
|
+
if (triBounds[t + 2] < minZ)
|
|
465
|
+
minZ = triBounds[t + 2];
|
|
466
|
+
if (triBounds[t + 3] > maxX)
|
|
467
|
+
maxX = triBounds[t + 3];
|
|
468
|
+
if (triBounds[t + 4] > maxY)
|
|
469
|
+
maxY = triBounds[t + 4];
|
|
470
|
+
if (triBounds[t + 5] > maxZ)
|
|
471
|
+
maxZ = triBounds[t + 5];
|
|
472
|
+
}
|
|
473
|
+
nodeBounds[node * 6] = minX;
|
|
474
|
+
nodeBounds[node * 6 + 1] = minY;
|
|
475
|
+
nodeBounds[node * 6 + 2] = minZ;
|
|
476
|
+
nodeBounds[node * 6 + 3] = maxX;
|
|
477
|
+
nodeBounds[node * 6 + 4] = maxY;
|
|
478
|
+
nodeBounds[node * 6 + 5] = maxZ;
|
|
479
|
+
if (count <= LEAF_TRIANGLES) {
|
|
480
|
+
nodeStart[node] = start;
|
|
481
|
+
nodeCount[node] = count;
|
|
482
|
+
return node;
|
|
483
|
+
}
|
|
484
|
+
const spanX = maxX - minX;
|
|
485
|
+
const spanY = maxY - minY;
|
|
486
|
+
const spanZ = maxZ - minZ;
|
|
487
|
+
const axis = spanX >= spanY && spanX >= spanZ ? 0 : spanY >= spanZ ? 1 : 2;
|
|
488
|
+
triOrder.subarray(start, start + count).sort((a, b) => centroids[a * 3 + axis] - centroids[b * 3 + axis]);
|
|
489
|
+
const mid = start + (count >> 1);
|
|
490
|
+
nodeCount[node] = 0;
|
|
491
|
+
build(start, mid - start);
|
|
492
|
+
nodeStart[node] = build(mid, start + count - mid);
|
|
493
|
+
return node;
|
|
494
|
+
}
|
|
495
|
+
build(0, triangleCount);
|
|
496
|
+
return {
|
|
497
|
+
nodeBounds: nodeBounds.slice(0, usedNodes * 6),
|
|
498
|
+
nodeStart: nodeStart.slice(0, usedNodes),
|
|
499
|
+
nodeCount: nodeCount.slice(0, usedNodes),
|
|
500
|
+
triOrder,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
const preparedCache = new WeakMap();
|
|
504
|
+
/**
|
|
505
|
+
* Decode a stored collision mesh and index it with a BVH. Memoized per data object, so the shell and
|
|
506
|
+
* a headless host preparing the same catalog entry share one build. Returns `null` (also memoized)
|
|
507
|
+
* when the payload is malformed.
|
|
508
|
+
*/
|
|
509
|
+
export function prepareCollisionMesh(data) {
|
|
510
|
+
const cached = preparedCache.get(data);
|
|
511
|
+
if (cached !== undefined)
|
|
512
|
+
return cached;
|
|
513
|
+
const prepared = decodeAndBuild(data);
|
|
514
|
+
preparedCache.set(data, prepared);
|
|
515
|
+
return prepared;
|
|
516
|
+
}
|
|
517
|
+
function decodeAndBuild(data) {
|
|
518
|
+
const { vertexCount, triangleCount } = data;
|
|
519
|
+
if (!Number.isInteger(vertexCount) || vertexCount < 3)
|
|
520
|
+
return null;
|
|
521
|
+
if (!Number.isInteger(triangleCount) || triangleCount < 1)
|
|
522
|
+
return null;
|
|
523
|
+
const positionBytes = base64ToBytes(data.positions);
|
|
524
|
+
const indexBytes = base64ToBytes(data.indices);
|
|
525
|
+
if (positionBytes === null || indexBytes === null)
|
|
526
|
+
return null;
|
|
527
|
+
if (positionBytes.length !== vertexCount * 6)
|
|
528
|
+
return null;
|
|
529
|
+
const wide = vertexCount > 65536;
|
|
530
|
+
if (indexBytes.length !== triangleCount * 3 * (wide ? 4 : 2))
|
|
531
|
+
return null;
|
|
532
|
+
const minX = data.min[0];
|
|
533
|
+
const minY = data.min[1];
|
|
534
|
+
const minZ = data.min[2];
|
|
535
|
+
const spanX = data.max[0] - minX;
|
|
536
|
+
const spanY = data.max[1] - minY;
|
|
537
|
+
const spanZ = data.max[2] - minZ;
|
|
538
|
+
if (!Number.isFinite(minX) || !Number.isFinite(spanX) || spanX < 0 || spanY < 0 || spanZ < 0)
|
|
539
|
+
return null;
|
|
540
|
+
const positionView = new DataView(positionBytes.buffer, positionBytes.byteOffset, positionBytes.byteLength);
|
|
541
|
+
const positions = new Float32Array(vertexCount * 3);
|
|
542
|
+
for (let v = 0; v < vertexCount; v += 1) {
|
|
543
|
+
positions[v * 3] = minX + (positionView.getUint16(v * 6, true) / 65535) * spanX;
|
|
544
|
+
positions[v * 3 + 1] = minY + (positionView.getUint16(v * 6 + 2, true) / 65535) * spanY;
|
|
545
|
+
positions[v * 3 + 2] = minZ + (positionView.getUint16(v * 6 + 4, true) / 65535) * spanZ;
|
|
546
|
+
}
|
|
547
|
+
const indexView = new DataView(indexBytes.buffer, indexBytes.byteOffset, indexBytes.byteLength);
|
|
548
|
+
const indices = new Uint32Array(triangleCount * 3);
|
|
549
|
+
for (let i = 0; i < indices.length; i += 1) {
|
|
550
|
+
const value = wide ? indexView.getUint32(i * 4, true) : indexView.getUint16(i * 2, true);
|
|
551
|
+
if (value >= vertexCount)
|
|
552
|
+
return null;
|
|
553
|
+
indices[i] = value;
|
|
554
|
+
}
|
|
555
|
+
return { positions, indices, ...buildBvh(positions, indices) };
|
|
556
|
+
}
|
|
557
|
+
// Traversal scratch — module-level so the per-ray hot path allocates nothing. Safe because scene
|
|
558
|
+
// raycasts run on a single thread and traversal never re-enters.
|
|
559
|
+
const traversalStack = new Int32Array(64);
|
|
560
|
+
function nodeIntersects(bounds, node, ox, oy, oz, dx, dy, dz, tLimit) {
|
|
561
|
+
let tMin = 0;
|
|
562
|
+
let tMax = tLimit;
|
|
563
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
564
|
+
const origin = axis === 0 ? ox : axis === 1 ? oy : oz;
|
|
565
|
+
const dir = axis === 0 ? dx : axis === 1 ? dy : dz;
|
|
566
|
+
const lo = bounds[node * 6 + axis];
|
|
567
|
+
const hi = bounds[node * 6 + 3 + axis];
|
|
568
|
+
if (Math.abs(dir) < 1e-12) {
|
|
569
|
+
if (origin < lo || origin > hi)
|
|
570
|
+
return false;
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const inv = 1 / dir;
|
|
574
|
+
const t1 = (lo - origin) * inv;
|
|
575
|
+
const t2 = (hi - origin) * inv;
|
|
576
|
+
const near = t1 < t2 ? t1 : t2;
|
|
577
|
+
const far = t1 < t2 ? t2 : t1;
|
|
578
|
+
if (near > tMin)
|
|
579
|
+
tMin = near;
|
|
580
|
+
if (far < tMax)
|
|
581
|
+
tMax = far;
|
|
582
|
+
if (tMin > tMax)
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Nearest triangle hit for a world-space ray against a prepared mesh placed like its rendered model:
|
|
589
|
+
* model space scaled uniformly by `scale`, translated by entity-local `translate`, yaw-rotated by
|
|
590
|
+
* `rotationY`, and moved to `position` — the same composition {@link "scene/colliders".ModelBodySource}
|
|
591
|
+
* fitting applies to the AABB. Allocation-free until the returned hit. `distance` is world distance
|
|
592
|
+
* along `direction` (callers pass `direction` normalized); `normal` is the world-space geometric
|
|
593
|
+
* triangle normal facing the ray.
|
|
594
|
+
*/
|
|
595
|
+
export function raycastCollisionMesh(mesh, origin, direction, maxDistance, position, rotationY, scale, translate) {
|
|
596
|
+
if (!(scale > 0) || !Number.isFinite(scale))
|
|
597
|
+
return null;
|
|
598
|
+
const cos = Math.cos(rotationY);
|
|
599
|
+
const sin = Math.sin(rotationY);
|
|
600
|
+
// World → entity-local (inverse of colliders.worldOffset's yaw), then local → model space.
|
|
601
|
+
const wx = origin[0] - position[0];
|
|
602
|
+
const wy = origin[1] - position[1];
|
|
603
|
+
const wz = origin[2] - position[2];
|
|
604
|
+
const invScale = 1 / scale;
|
|
605
|
+
const ox = (wx * cos - wz * sin - translate[0]) * invScale;
|
|
606
|
+
const oy = (wy - translate[1]) * invScale;
|
|
607
|
+
const oz = (wx * sin + wz * cos - translate[2]) * invScale;
|
|
608
|
+
// Direction is not renormalized, so the parametric t below is world distance directly.
|
|
609
|
+
const dx = (direction[0] * cos - direction[2] * sin) * invScale;
|
|
610
|
+
const dy = direction[1] * invScale;
|
|
611
|
+
const dz = (direction[0] * sin + direction[2] * cos) * invScale;
|
|
612
|
+
const { positions, indices, nodeBounds, nodeStart, nodeCount, triOrder } = mesh;
|
|
613
|
+
let bestT = maxDistance;
|
|
614
|
+
let bestTriangle = -1;
|
|
615
|
+
let stackSize = 0;
|
|
616
|
+
traversalStack[stackSize] = 0;
|
|
617
|
+
stackSize += 1;
|
|
618
|
+
while (stackSize > 0) {
|
|
619
|
+
stackSize -= 1;
|
|
620
|
+
const node = traversalStack[stackSize];
|
|
621
|
+
if (!nodeIntersects(nodeBounds, node, ox, oy, oz, dx, dy, dz, bestT))
|
|
622
|
+
continue;
|
|
623
|
+
const count = nodeCount[node];
|
|
624
|
+
if (count === 0) {
|
|
625
|
+
traversalStack[stackSize] = nodeStart[node];
|
|
626
|
+
traversalStack[stackSize + 1] = node + 1;
|
|
627
|
+
stackSize += 2;
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
const start = nodeStart[node];
|
|
631
|
+
for (let i = start; i < start + count; i += 1) {
|
|
632
|
+
const triangle = triOrder[i];
|
|
633
|
+
const a = indices[triangle * 3] * 3;
|
|
634
|
+
const b = indices[triangle * 3 + 1] * 3;
|
|
635
|
+
const c = indices[triangle * 3 + 2] * 3;
|
|
636
|
+
const ax = positions[a];
|
|
637
|
+
const ay = positions[a + 1];
|
|
638
|
+
const az = positions[a + 2];
|
|
639
|
+
const e1x = positions[b] - ax;
|
|
640
|
+
const e1y = positions[b + 1] - ay;
|
|
641
|
+
const e1z = positions[b + 2] - az;
|
|
642
|
+
const e2x = positions[c] - ax;
|
|
643
|
+
const e2y = positions[c + 1] - ay;
|
|
644
|
+
const e2z = positions[c + 2] - az;
|
|
645
|
+
// Möller–Trumbore, double-sided.
|
|
646
|
+
const px = dy * e2z - dz * e2y;
|
|
647
|
+
const py = dz * e2x - dx * e2z;
|
|
648
|
+
const pz = dx * e2y - dy * e2x;
|
|
649
|
+
const det = e1x * px + e1y * py + e1z * pz;
|
|
650
|
+
if (Math.abs(det) < 1e-12)
|
|
651
|
+
continue;
|
|
652
|
+
const invDet = 1 / det;
|
|
653
|
+
const tx = ox - ax;
|
|
654
|
+
const ty = oy - ay;
|
|
655
|
+
const tz = oz - az;
|
|
656
|
+
const u = (tx * px + ty * py + tz * pz) * invDet;
|
|
657
|
+
if (u < 0 || u > 1)
|
|
658
|
+
continue;
|
|
659
|
+
const qx = ty * e1z - tz * e1y;
|
|
660
|
+
const qy = tz * e1x - tx * e1z;
|
|
661
|
+
const qz = tx * e1y - ty * e1x;
|
|
662
|
+
const v = (dx * qx + dy * qy + dz * qz) * invDet;
|
|
663
|
+
if (v < 0 || u + v > 1)
|
|
664
|
+
continue;
|
|
665
|
+
const t = (e2x * qx + e2y * qy + e2z * qz) * invDet;
|
|
666
|
+
if (t > 1e-6 && t < bestT) {
|
|
667
|
+
bestT = t;
|
|
668
|
+
bestTriangle = triangle;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (bestTriangle < 0)
|
|
673
|
+
return null;
|
|
674
|
+
const a = indices[bestTriangle * 3] * 3;
|
|
675
|
+
const b = indices[bestTriangle * 3 + 1] * 3;
|
|
676
|
+
const c = indices[bestTriangle * 3 + 2] * 3;
|
|
677
|
+
const e1x = positions[b] - positions[a];
|
|
678
|
+
const e1y = positions[b + 1] - positions[a + 1];
|
|
679
|
+
const e1z = positions[b + 2] - positions[a + 2];
|
|
680
|
+
const e2x = positions[c] - positions[a];
|
|
681
|
+
const e2y = positions[c + 1] - positions[a + 1];
|
|
682
|
+
const e2z = positions[c + 2] - positions[a + 2];
|
|
683
|
+
let nx = e1y * e2z - e1z * e2y;
|
|
684
|
+
let ny = e1z * e2x - e1x * e2z;
|
|
685
|
+
let nz = e1x * e2y - e1y * e2x;
|
|
686
|
+
const length = Math.hypot(nx, ny, nz) || 1;
|
|
687
|
+
nx /= length;
|
|
688
|
+
ny /= length;
|
|
689
|
+
nz /= length;
|
|
690
|
+
if (nx * dx + ny * dy + nz * dz > 0) {
|
|
691
|
+
nx = -nx;
|
|
692
|
+
ny = -ny;
|
|
693
|
+
nz = -nz;
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
distance: bestT,
|
|
697
|
+
normal: [nx * cos + nz * sin, ny, -nx * sin + nz * cos],
|
|
698
|
+
};
|
|
699
|
+
}
|