summer-engine 3.1.0 → 3.2.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +27 -5
- package/.codex-plugin/plugin.json +28 -6
- package/.cursor-plugin/plugin.json +27 -5
- package/.factory-plugin/plugin.json +27 -5
- package/.opencode/plugins/summer.js +1 -1
- package/CHANGELOG.md +102 -0
- package/README.md +18 -3
- package/assets/audit/scene_audit.gd +4136 -0
- package/assets/seeing/seeing_probe.gd +1873 -0
- package/dist/cli/commands/skills.js +20 -2
- package/dist/core/api-client.d.ts +9 -1
- package/dist/core/api-client.js +21 -4
- package/dist/core/capabilities/agent-playbook.js +46 -0
- package/dist/core/capabilities/audit/accept.d.ts +55 -0
- package/dist/core/capabilities/audit/accept.js +147 -0
- package/dist/core/capabilities/audit/args.d.ts +95 -0
- package/dist/core/capabilities/audit/args.js +89 -0
- package/dist/core/capabilities/audit/audit.d.ts +56 -0
- package/dist/core/capabilities/audit/audit.js +287 -0
- package/dist/core/capabilities/audit/frame.d.ts +14 -0
- package/dist/core/capabilities/audit/frame.js +35 -0
- package/dist/core/capabilities/audit/gaps.d.ts +96 -0
- package/dist/core/capabilities/audit/gaps.js +257 -0
- package/dist/core/capabilities/audit/judge.d.ts +162 -0
- package/dist/core/capabilities/audit/judge.js +1134 -0
- package/dist/core/capabilities/audit/math.d.ts +110 -0
- package/dist/core/capabilities/audit/math.js +263 -0
- package/dist/core/capabilities/audit/summary.d.ts +74 -0
- package/dist/core/capabilities/audit/summary.js +212 -0
- package/dist/core/capabilities/board.d.ts +31 -0
- package/dist/core/capabilities/board.js +43 -0
- package/dist/core/capabilities/camera-view.d.ts +4 -2
- package/dist/core/capabilities/camera-view.js +6 -3
- package/dist/core/capabilities/connect-signal.d.ts +85 -0
- package/dist/core/capabilities/connect-signal.js +285 -0
- package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
- package/dist/core/capabilities/engine-fallbacks.js +5 -0
- package/dist/core/capabilities/engine-ops.d.ts +5 -1
- package/dist/core/capabilities/engine-ops.js +0 -3
- package/dist/core/capabilities/engine-receipt.d.ts +12 -0
- package/dist/core/capabilities/engine-receipt.js +26 -0
- package/dist/core/capabilities/file-read.d.ts +64 -0
- package/dist/core/capabilities/file-read.js +325 -0
- package/dist/core/capabilities/game-task-plan.js +4 -2
- package/dist/core/capabilities/grep.d.ts +60 -0
- package/dist/core/capabilities/grep.js +190 -0
- package/dist/core/capabilities/inspect-node.d.ts +56 -0
- package/dist/core/capabilities/inspect-node.js +225 -0
- package/dist/core/capabilities/inspect-resource.d.ts +61 -0
- package/dist/core/capabilities/inspect-resource.js +376 -0
- package/dist/core/capabilities/math3d.d.ts +28 -0
- package/dist/core/capabilities/math3d.js +88 -0
- package/dist/core/capabilities/motion-generation.d.ts +62 -0
- package/dist/core/capabilities/motion-generation.js +138 -0
- package/dist/core/capabilities/placement-batch.d.ts +102 -0
- package/dist/core/capabilities/placement-batch.js +483 -0
- package/dist/core/capabilities/placement-math.d.ts +70 -0
- package/dist/core/capabilities/placement-math.js +209 -0
- package/dist/core/capabilities/placement-script.d.ts +42 -0
- package/dist/core/capabilities/placement-script.js +1096 -0
- package/dist/core/capabilities/placement.d.ts +485 -0
- package/dist/core/capabilities/placement.js +1731 -0
- package/dist/core/capabilities/project-context.d.ts +33 -2
- package/dist/core/capabilities/project-context.js +87 -4
- package/dist/core/capabilities/replace-node.d.ts +91 -0
- package/dist/core/capabilities/replace-node.js +423 -0
- package/dist/core/capabilities/runtime-control.d.ts +18 -2
- package/dist/core/capabilities/runtime-control.js +67 -2
- package/dist/core/capabilities/scene-batch.d.ts +63 -0
- package/dist/core/capabilities/scene-batch.js +394 -0
- package/dist/core/capabilities/scene-readback.d.ts +36 -0
- package/dist/core/capabilities/scene-readback.js +54 -0
- package/dist/core/capabilities/seeing/args.d.ts +348 -0
- package/dist/core/capabilities/seeing/args.js +178 -0
- package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
- package/dist/core/capabilities/seeing/candidates.js +282 -0
- package/dist/core/capabilities/seeing/math.d.ts +134 -0
- package/dist/core/capabilities/seeing/math.js +269 -0
- package/dist/core/capabilities/seeing/probe.d.ts +62 -0
- package/dist/core/capabilities/seeing/probe.js +188 -0
- package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
- package/dist/core/capabilities/seeing/scoring.js +639 -0
- package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
- package/dist/core/capabilities/seeing/seeing.js +1341 -0
- package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
- package/dist/core/capabilities/seeing/shot-store.js +204 -0
- package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
- package/dist/core/capabilities/surface-snap-mesh.js +425 -0
- package/dist/core/capabilities/surface-snap.d.ts +77 -0
- package/dist/core/capabilities/surface-snap.js +461 -0
- package/dist/core/capabilities/tool-dispatch.js +231 -72
- package/dist/core/capabilities/tscn.d.ts +85 -0
- package/dist/core/capabilities/tscn.js +391 -0
- package/dist/core/capabilities/world-snapshot.d.ts +58 -0
- package/dist/core/capabilities/world-snapshot.js +150 -0
- package/dist/core/capability-skew.d.ts +5 -5
- package/dist/core/capability-skew.js +14 -6
- package/dist/core/headless/worker-engine-client.d.ts +1 -1
- package/dist/core/headless/worker-engine-client.js +2 -2
- package/dist/core/library-read.d.ts +37 -5
- package/dist/core/library-read.js +264 -33
- package/dist/core/library-search.d.ts +2 -2
- package/dist/installer/agent-config.js +5 -3
- package/dist/installer/agent-table.d.ts +16 -0
- package/dist/installer/agent-table.js +52 -17
- package/dist/mcp/server.js +9 -3
- package/dist/mcp/tools/asset-tools.js +21 -3
- package/dist/mcp/tools/audit-tools.d.ts +23 -0
- package/dist/mcp/tools/audit-tools.js +60 -0
- package/dist/mcp/tools/debug-tools.js +3 -1
- package/dist/mcp/tools/file-tools.js +17 -4
- package/dist/mcp/tools/generate-tools.js +74 -66
- package/dist/mcp/tools/library-tools.js +2 -0
- package/dist/mcp/tools/perception-tools.js +6 -19
- package/dist/mcp/tools/placement-tools.d.ts +24 -0
- package/dist/mcp/tools/placement-tools.js +91 -0
- package/dist/mcp/tools/project-tools.js +18 -15
- package/dist/mcp/tools/scene-tools.js +71 -83
- package/dist/mcp/tools/seeing-tools.d.ts +24 -0
- package/dist/mcp/tools/seeing-tools.js +64 -0
- package/dist/mcp/tools/spatial-tools.js +17 -9
- package/dist/mcp/tools/visual-tools.js +59 -7
- package/gemini-extension.json +26 -4
- package/integrations/amp/README.md +1 -1
- package/integrations/crush/README.md +1 -1
- package/integrations/grok-build/README.md +1 -1
- package/integrations/hermes/README.md +1 -1
- package/integrations/kimi-code/README.md +1 -1
- package/integrations/mistral-vibe/README.md +1 -1
- package/integrations/opencode/README.md +1 -1
- package/integrations/rovo-dev/README.md +1 -1
- package/integrations/vscode-copilot/README.md +1 -1
- package/integrations/warp/README.md +1 -1
- package/integrations/windsurf/README.md +1 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/references/summer-folder/resource.yaml +1 -1
- package/library/references/summer-folder/summer-folder.md +1 -1
- package/library/skills/3d-lighting/SKILL.md +1 -1
- package/library/skills/README.md +36 -14
- package/library/skills/adaptive-music/SKILL.md +1 -1
- package/library/skills/agent-playtesting/SKILL.md +1 -1
- package/library/skills/ai-characters-in-games/SKILL.md +220 -0
- package/library/skills/ai-characters-in-games/resource.yaml +30 -0
- package/library/skills/ambient-bed/SKILL.md +1 -1
- package/library/skills/animated-loop/SKILL.md +1 -1
- package/library/skills/animation-tree/SKILL.md +1 -1
- package/library/skills/art-direction/SKILL.md +1 -1
- package/library/skills/asset-strategy/SKILL.md +2 -1
- package/library/skills/asset-strategy/resource.yaml +2 -1
- package/library/skills/audio-direction/SKILL.md +1 -1
- package/library/skills/auto-fire-targeting/SKILL.md +1 -1
- package/library/skills/brainstorm-game/SKILL.md +1 -1
- package/library/skills/brainstorming/SKILL.md +1 -1
- package/library/skills/browse-templates/SKILL.md +3 -3
- package/library/skills/browse-templates/resource.yaml +1 -1
- package/library/skills/camera-rigs/SKILL.md +128 -0
- package/library/skills/camera-rigs/resource.yaml +29 -0
- package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
- package/library/skills/character-animation-wiring/SKILL.md +1 -1
- package/library/skills/character-model/SKILL.md +1 -1
- package/library/skills/character-movement/SKILL.md +326 -0
- package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
- package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
- package/library/skills/character-movement/references/movement-3d.md +136 -0
- package/library/skills/character-movement/references/platformer-feel.md +83 -0
- package/library/skills/character-movement/resource.yaml +40 -0
- package/library/skills/character-portrait/SKILL.md +1 -1
- package/library/skills/cinematic-cutscene/SKILL.md +1 -1
- package/library/skills/combat-basics/SKILL.md +159 -0
- package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
- package/library/skills/combat-basics/resource.yaml +34 -0
- package/library/skills/concept-art/SKILL.md +1 -1
- package/library/skills/create-asset-sheet/SKILL.md +1 -1
- package/library/skills/debug/SKILL.md +1 -1
- package/library/skills/debugging-game-feel/SKILL.md +1 -1
- package/library/skills/design-level/SKILL.md +1 -1
- package/library/skills/design-mechanic/SKILL.md +1 -1
- package/library/skills/design-npc/SKILL.md +1 -1
- package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
- package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
- package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
- package/library/skills/environment-kit/SKILL.md +1 -1
- package/library/skills/export-and-ship/SKILL.md +1 -1
- package/library/skills/fabricating-assets/SKILL.md +1 -1
- package/library/skills/facial-and-lipsync/SKILL.md +1 -1
- package/library/skills/fps-controller/SKILL.md +9 -7
- package/library/skills/fps-controller/resource.yaml +4 -1
- package/library/skills/game-feel/SKILL.md +1 -1
- package/library/skills/gameskill/SKILL.md +1 -1
- package/library/skills/gdscript-patterns/SKILL.md +1 -1
- package/library/skills/generate-motion/SKILL.md +9 -9
- package/library/skills/generate-motion/resource.yaml +2 -1
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
- package/library/skills/headless-scripting/SKILL.md +1 -1
- package/library/skills/in-game-purchases/SKILL.md +254 -0
- package/library/skills/in-game-purchases/resource.yaml +29 -0
- package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
- package/library/skills/investigating-bugs/SKILL.md +1 -1
- package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
- package/library/skills/make-game/SKILL.md +25 -3
- package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
- package/library/skills/make-game/references/2d-platformer.md +117 -0
- package/library/skills/make-game/references/2d-rpg-content.md +234 -0
- package/library/skills/make-game/references/2d-rpg.md +241 -0
- package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
- package/library/skills/make-game/references/2d-survivors.md +237 -0
- package/library/skills/make-game/references/3d-fps-systems.md +216 -0
- package/library/skills/make-game/references/3d-fps.md +177 -0
- package/library/skills/make-game/references/3d-horror-systems.md +186 -0
- package/library/skills/make-game/references/3d-horror.md +201 -0
- package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
- package/library/skills/make-game/references/3d-third-person.md +215 -0
- package/library/skills/make-game/resource.yaml +6 -1
- package/library/skills/multiplayer/SKILL.md +150 -0
- package/library/skills/multiplayer/resource.yaml +33 -0
- package/library/skills/multiplayer-movement/SKILL.md +510 -0
- package/library/skills/multiplayer-movement/files.md +501 -0
- package/library/skills/multiplayer-movement/resource.yaml +30 -0
- package/library/skills/multiplayer-project/SKILL.md +521 -0
- package/library/skills/multiplayer-project/files.md +448 -0
- package/library/skills/multiplayer-project/resource.yaml +36 -0
- package/library/skills/multiplayer-publish/SKILL.md +229 -0
- package/library/skills/multiplayer-publish/export-errors.md +89 -0
- package/library/skills/multiplayer-publish/resource.yaml +28 -0
- package/library/skills/multiplayer-state/SKILL.md +438 -0
- package/library/skills/multiplayer-state/files.md +592 -0
- package/library/skills/multiplayer-state/resource.yaml +35 -0
- package/library/skills/multiplayer-testing/SKILL.md +236 -0
- package/library/skills/multiplayer-testing/resource.yaml +39 -0
- package/library/skills/music-track/SKILL.md +1 -1
- package/library/skills/navigate-summer/SKILL.md +1 -1
- package/library/skills/new-project/SKILL.md +1 -1
- package/library/skills/organic-model/SKILL.md +1 -1
- package/library/skills/pixel-art/SKILL.md +1 -1
- package/library/skills/play/SKILL.md +1 -1
- package/library/skills/playtesting-a-feature/SKILL.md +1 -1
- package/library/skills/procedural-animation/SKILL.md +2 -2
- package/library/skills/procedural-animation/resource.yaml +1 -1
- package/library/skills/prop-model/SKILL.md +1 -1
- package/library/skills/psx-retro-rendering/SKILL.md +1 -1
- package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
- package/library/skills/remote-deploy/SKILL.md +1 -1
- package/library/skills/retarget/SKILL.md +1 -1
- package/library/skills/running-in-the-cloud/SKILL.md +1 -1
- package/library/skills/save-load/SKILL.md +156 -0
- package/library/skills/save-load/resource.yaml +27 -0
- package/library/skills/scene-composition/SKILL.md +1 -1
- package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
- package/library/skills/scene-scripting/SKILL.md +1 -1
- package/library/skills/scene-to-level/SKILL.md +1 -1
- package/library/skills/skill-create/SKILL.md +1 -1
- package/library/skills/skill-improve/SKILL.md +1 -1
- package/library/skills/skill-test/SKILL.md +1 -1
- package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
- package/library/skills/skybox-panorama/SKILL.md +1 -1
- package/library/skills/sound-effect/SKILL.md +1 -1
- package/library/skills/spatial-placement/SKILL.md +134 -3
- package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
- package/library/skills/spatial-placement/resource.yaml +16 -2
- package/library/skills/sprite-sheet/SKILL.md +1 -1
- package/library/skills/summer-analytics/SKILL.md +147 -0
- package/library/skills/summer-analytics/resource.yaml +28 -0
- package/library/skills/summer-friends/SKILL.md +226 -0
- package/library/skills/summer-friends/resource.yaml +28 -0
- package/library/skills/summer-leaderboards/SKILL.md +231 -0
- package/library/skills/summer-leaderboards/resource.yaml +30 -0
- package/library/skills/summer-match-results/SKILL.md +286 -0
- package/library/skills/summer-match-results/resource.yaml +32 -0
- package/library/skills/summer-matchmaking/SKILL.md +301 -0
- package/library/skills/summer-matchmaking/resource.yaml +32 -0
- package/library/skills/summer-parties/SKILL.md +253 -0
- package/library/skills/summer-parties/resource.yaml +30 -0
- package/library/skills/summer-player-data/SKILL.md +322 -0
- package/library/skills/summer-player-data/resource.yaml +30 -0
- package/library/skills/summer-store/SKILL.md +271 -0
- package/library/skills/summer-store/resource.yaml +29 -0
- package/library/skills/summer-world-chat/SKILL.md +195 -0
- package/library/skills/summer-world-chat/resource.yaml +30 -0
- package/library/skills/summer-world-saves/SKILL.md +284 -0
- package/library/skills/summer-world-saves/resource.yaml +29 -0
- package/library/skills/text-to-motion/SKILL.md +99 -0
- package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
- package/library/skills/text-to-motion/resource.yaml +41 -0
- package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
- package/library/skills/tileable-texture/SKILL.md +1 -1
- package/library/skills/trailer-shot/SKILL.md +1 -1
- package/library/skills/tune-performance/SKILL.md +1 -1
- package/library/skills/ui-basics/SKILL.md +10 -1
- package/library/skills/ui-graphics/SKILL.md +1 -1
- package/library/skills/use-widget-asset/SKILL.md +1 -1
- package/library/skills/using-summer/SKILL.md +2 -2
- package/library/skills/using-summer/resource.yaml +2 -2
- package/library/skills/vehicle-model/SKILL.md +1 -1
- package/library/skills/verification-before-completion/SKILL.md +1 -1
- package/library/skills/verifying-scenes/SKILL.md +76 -2
- package/library/skills/verifying-scenes/resource.yaml +13 -2
- package/library/skills/vfx-dissolve/SKILL.md +1 -1
- package/library/skills/vfx-fire/SKILL.md +1 -1
- package/library/skills/vfx-hit-spark/SKILL.md +1 -1
- package/library/skills/vfx-lightning/SKILL.md +1 -1
- package/library/skills/vfx-magic-glow/SKILL.md +1 -1
- package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
- package/library/skills/vfx-smoke/SKILL.md +1 -1
- package/library/skills/vfx-water-ripple/SKILL.md +1 -1
- package/library/skills/voice-line/SKILL.md +1 -1
- package/library/skills/web-and-touch-input/SKILL.md +38 -0
- package/library/skills/web-and-touch-input/resource.yaml +28 -0
- package/library/skills/web-compatibility-look/SKILL.md +62 -0
- package/library/skills/web-compatibility-look/resource.yaml +30 -0
- package/library/skills/world-building-3d/SKILL.md +8 -1
- package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
- package/library/skills/world-building-3d/resource.yaml +2 -1
- package/library/skills/writing-plans/SKILL.md +1 -1
- package/library/skills/writing-skills/SKILL.md +1 -1
- package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
- package/library/tools/attach-to-surface/resource.yaml +142 -0
- package/library/tools/batch/resource.yaml +9 -3
- package/library/tools/connect-ports/resource.yaml +120 -0
- package/library/tools/connect-signal/resource.yaml +3 -2
- package/library/tools/debug-views/resource.yaml +94 -0
- package/library/tools/frame-nodes/resource.yaml +104 -0
- package/library/tools/frame-shot/resource.yaml +134 -0
- package/library/tools/generate-motion/resource.yaml +44 -9
- package/library/tools/get-board/resource.yaml +50 -0
- package/library/tools/get-project-context/resource.yaml +13 -2
- package/library/tools/grep/resource.yaml +77 -0
- package/library/tools/inspect-asset/resource.yaml +71 -0
- package/library/tools/inspect-node/resource.yaml +9 -2
- package/library/tools/inspect-resource/resource.yaml +16 -5
- package/library/tools/instantiate-scene/resource.yaml +27 -2
- package/library/tools/measure/resource.yaml +96 -0
- package/library/tools/place-adjacent/resource.yaml +113 -0
- package/library/tools/play/resource.yaml +15 -2
- package/library/tools/raycast/resource.yaml +101 -0
- package/library/tools/read-file/resource.yaml +36 -2
- package/library/tools/read-library/resource.yaml +2 -2
- package/library/tools/repeat-along/resource.yaml +129 -0
- package/library/tools/replace-node/resource.yaml +11 -5
- package/library/tools/scene-audit/resource.yaml +144 -0
- package/library/tools/screenshot/resource.yaml +13 -2
- package/library/tools/search-assets/resource.yaml +18 -0
- package/library/tools/shot-sheet/resource.yaml +110 -0
- package/library/tools/snap-to-surface/resource.yaml +4 -1
- package/library/tools/world-snapshot/resource.yaml +24 -3
- package/library/tools/zoom/resource.yaml +105 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +17 -3
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +26 -4
- package/registry/generated/index.json +2154 -469
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +27 -5
- package/registry/generated/plugin.codex.json +28 -6
- package/registry/generated/plugin.cursor.json +27 -5
- package/registry/generated/plugin.factory.json +27 -5
- package/registry/generated/skills-index.md +36 -14
- package/registry/generated/skills-registry.json +421 -124
- package/registry/generated/templates-registry.json +2 -2
- package/library/skills/host-authoritative-state/SKILL.md +0 -332
- package/library/skills/host-authoritative-state/resource.yaml +0 -28
- package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
- package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
- package/library/skills/setup-multiplayer/SKILL.md +0 -336
- package/library/skills/setup-multiplayer/resource.yaml +0 -28
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* surface-snap-mesh — the visible-mesh measurement behind
|
|
3
|
+
* `summer_snap_to_surface` when colliders cannot answer.
|
|
4
|
+
*
|
|
5
|
+
* The engine's SnapToSurface sweeps the subject's colliders against body
|
|
6
|
+
* colliders. When the subject or the support has no collider it falls back to
|
|
7
|
+
* visual_aabb: the subject's visible AABB swept against the AABB of every
|
|
8
|
+
* other visible geometry in the scene. One large AABB anywhere around the
|
|
9
|
+
* subject (a tree, a building block, a full-screen post quad whose mesh
|
|
10
|
+
* carries a huge custom AABB) then counts as an overlap that no back-off
|
|
11
|
+
* clears: collider-less ferns on a ground PlaneMesh all failed with
|
|
12
|
+
* overlap_recovery_exceeded, "starts in contact with or inside (unnamed)".
|
|
13
|
+
*
|
|
14
|
+
* This probe measures the same move from triangles instead. It is one
|
|
15
|
+
* read-only GDScript sent through RunSceneScript (undo "none", no
|
|
16
|
+
* checkpoint), with its arguments as base64 data, never source:
|
|
17
|
+
*
|
|
18
|
+
* - the subject's visible mesh triangles in scene space; its vertices are
|
|
19
|
+
* cast along the direction, each from the subject's top plane (the
|
|
20
|
+
* subject's extreme against the direction) above it, onto the
|
|
21
|
+
* triangles of every other visible mesh whose bounds meet the swept
|
|
22
|
+
* column. A surface between that plane and the vertex means the subject
|
|
23
|
+
* is sunk into it (negative travel);
|
|
24
|
+
* - the support's vertices inside the subject's footprint are cast back
|
|
25
|
+
* against the subject's triangles, for support peaks between subject
|
|
26
|
+
* vertices;
|
|
27
|
+
* - travel = the smallest of those distances; the new local position is
|
|
28
|
+
* the current one plus (travel - gap) along the direction, mapped
|
|
29
|
+
* through the parent's transform.
|
|
30
|
+
*
|
|
31
|
+
* Meshes whose shader writes POSITION (screen-space quads) and non-mesh
|
|
32
|
+
* geometry (CSG, MultiMesh, sprites) are skipped and counted. It reads the
|
|
33
|
+
* scene only; the mutation is an ordinary SetProp + SaveScene sent by
|
|
34
|
+
* surface-snap.ts.
|
|
35
|
+
*/
|
|
36
|
+
import { missingEngineOpResult } from "../capability-skew.js";
|
|
37
|
+
import { asRecord } from "../util/json.js";
|
|
38
|
+
import { extractOpError } from "./engine-receipt.js";
|
|
39
|
+
import { encodeScriptArgs } from "./placement-script.js";
|
|
40
|
+
import { buildRunSceneScriptOp } from "./scene-script.js";
|
|
41
|
+
export const SNAP_MESH_ARGS_TOKEN = "__SUMMER_SNAP_MESH_ARGS__";
|
|
42
|
+
/** Measure the visible-mesh snap from the subject's current pose. */
|
|
43
|
+
export async function measureMeshSnap(client, args) {
|
|
44
|
+
if (missingEngineOpResult(client, "RunSceneScript", "")) {
|
|
45
|
+
return { ok: false, failure_reason: "engine_lacks_op", error: "this engine build has no RunSceneScript, so the visible-mesh measurement cannot run" };
|
|
46
|
+
}
|
|
47
|
+
const { op, timeoutMs } = buildRunSceneScriptOp({
|
|
48
|
+
source: buildSnapMeshScript({
|
|
49
|
+
scene_path: args.scenePath,
|
|
50
|
+
subject: args.subjectPath,
|
|
51
|
+
direction: args.direction,
|
|
52
|
+
max_distance: args.maxDistance,
|
|
53
|
+
gap: args.gap,
|
|
54
|
+
}),
|
|
55
|
+
max_seconds: 30,
|
|
56
|
+
checkpoint: false,
|
|
57
|
+
undo: "none",
|
|
58
|
+
});
|
|
59
|
+
let receipt;
|
|
60
|
+
try {
|
|
61
|
+
receipt = await client.executeIdentityBoundOps([op], undefined, timeoutMs);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
return { ok: false, failure_reason: "mesh_probe_failed", error: `the visible-mesh probe failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
65
|
+
}
|
|
66
|
+
const results = asRecord(receipt)?.results;
|
|
67
|
+
const entry = asRecord(Array.isArray(results) ? results.find((r) => asRecord(r)?.op === "RunSceneScript") ?? results[0] : undefined);
|
|
68
|
+
const envelopeError = extractOpError(receipt);
|
|
69
|
+
if (!entry || entry.ok === false || envelopeError) {
|
|
70
|
+
return { ok: false, failure_reason: "mesh_probe_failed", error: `the visible-mesh probe (RunSceneScript) failed: ${String(entry?.error ?? envelopeError ?? "no result")}` };
|
|
71
|
+
}
|
|
72
|
+
const result = asRecord(entry.result);
|
|
73
|
+
if (!result)
|
|
74
|
+
return { ok: false, failure_reason: "mesh_probe_failed", error: "the visible-mesh probe ran but returned no result" };
|
|
75
|
+
if (result.ok !== true) {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
failure_reason: typeof result.failure_reason === "string" ? result.failure_reason : "mesh_probe_failed",
|
|
79
|
+
error: typeof result.error === "string" ? result.error : "the visible-mesh probe reported a failure",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
export function buildSnapMeshScript(args) {
|
|
85
|
+
return SNAP_MESH_PROBE_SOURCE.split(SNAP_MESH_ARGS_TOKEN).join(`"${encodeScriptArgs(args)}"`);
|
|
86
|
+
}
|
|
87
|
+
/** The probe source with its placeholder, for tests. */
|
|
88
|
+
export function snapMeshProbeTemplate() {
|
|
89
|
+
return SNAP_MESH_PROBE_SOURCE;
|
|
90
|
+
}
|
|
91
|
+
// Tabs are significant: GDScript indentation. String.raw keeps backslashes
|
|
92
|
+
// literal; the source never contains a template placeholder.
|
|
93
|
+
const SNAP_MESH_PROBE_SOURCE = String.raw `@tool
|
|
94
|
+
extends RefCounted
|
|
95
|
+
|
|
96
|
+
const ARGS_B64 = __SUMMER_SNAP_MESH_ARGS__
|
|
97
|
+
const MAX_NODES = 60000
|
|
98
|
+
const MAX_SUPPORT_TRIS = 400000
|
|
99
|
+
const MAX_SUBJECT_SAMPLES = 6000
|
|
100
|
+
const MAX_REVERSE_VERTS = 150000
|
|
101
|
+
const EPS = 0.0001
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
func run(_ctx):
|
|
105
|
+
var args = JSON.parse_string(Marshalls.base64_to_utf8(ARGS_B64))
|
|
106
|
+
if typeof(args) != TYPE_DICTIONARY:
|
|
107
|
+
return _fail("bad_args", "snap probe arguments did not parse")
|
|
108
|
+
var scene = _open_scene(String(args.get("scene_path", "")))
|
|
109
|
+
if scene.has("fail"):
|
|
110
|
+
return scene["fail"]
|
|
111
|
+
var root = scene["root"]
|
|
112
|
+
var subject = _node(root, String(args.get("subject", "")))
|
|
113
|
+
if subject == null:
|
|
114
|
+
return _fail("node_not_found", "subject not found: " + String(args.get("subject", "")))
|
|
115
|
+
if not (subject is Node3D) or subject == root:
|
|
116
|
+
return _fail("not_snappable", "the subject must be a Node3D below the scene root")
|
|
117
|
+
var dv = _vec(args.get("direction", [0, -1, 0]))
|
|
118
|
+
if dv.length() < 0.000001:
|
|
119
|
+
return _fail("bad_args", "direction must be non-zero")
|
|
120
|
+
var d = dv.normalized()
|
|
121
|
+
var max_distance = float(args.get("max_distance", 20.0))
|
|
122
|
+
var gap = float(args.get("gap", 0.0))
|
|
123
|
+
var skipped = {"screen_space": 0, "not_mesh": 0}
|
|
124
|
+
var sub_faces = PackedVector3Array()
|
|
125
|
+
for it in _mesh_items(subject, root, null, skipped):
|
|
126
|
+
sub_faces.append_array(it[1] * it[0].mesh.get_faces())
|
|
127
|
+
if sub_faces.size() < 3:
|
|
128
|
+
return _fail("no_subject_mesh", "the subject has no visible mesh triangles to measure")
|
|
129
|
+
# The subject's unique vertices, its extent along d and its bounds.
|
|
130
|
+
var uniq = {}
|
|
131
|
+
var verts = []
|
|
132
|
+
var lo = INF
|
|
133
|
+
var hi = -INF
|
|
134
|
+
var bb = AABB(sub_faces[0], Vector3.ZERO)
|
|
135
|
+
for p in sub_faces:
|
|
136
|
+
var key = Vector3i(roundi(p.x * 10000.0), roundi(p.y * 10000.0), roundi(p.z * 10000.0))
|
|
137
|
+
if uniq.has(key):
|
|
138
|
+
continue
|
|
139
|
+
uniq[key] = true
|
|
140
|
+
verts.append(p)
|
|
141
|
+
var s = p.dot(d)
|
|
142
|
+
lo = minf(lo, s)
|
|
143
|
+
hi = maxf(hi, s)
|
|
144
|
+
bb = bb.expand(p)
|
|
145
|
+
var u = d.cross(Vector3.UP if absf(d.y) < 0.9 else Vector3.RIGHT).normalized()
|
|
146
|
+
var v = d.cross(u).normalized()
|
|
147
|
+
var umin = INF
|
|
148
|
+
var umax = -INF
|
|
149
|
+
var vmin = INF
|
|
150
|
+
var vmax = -INF
|
|
151
|
+
for p in verts:
|
|
152
|
+
umin = minf(umin, p.dot(u))
|
|
153
|
+
umax = maxf(umax, p.dot(u))
|
|
154
|
+
vmin = minf(vmin, p.dot(v))
|
|
155
|
+
vmax = maxf(vmax, p.dot(v))
|
|
156
|
+
# Visible meshes outside the subject whose bounds meet the swept column.
|
|
157
|
+
var column = bb.merge(AABB(bb.position + d * max_distance, bb.size)).grow(0.001)
|
|
158
|
+
var sup_faces = PackedVector3Array()
|
|
159
|
+
var ranges = []
|
|
160
|
+
var tris = 0
|
|
161
|
+
for it in _mesh_items(root, root, subject, skipped):
|
|
162
|
+
var wb = it[1] * it[0].get_aabb()
|
|
163
|
+
if not column.intersects(wb.grow(0.001)):
|
|
164
|
+
continue
|
|
165
|
+
var faces = it[1] * it[0].mesh.get_faces()
|
|
166
|
+
if faces.size() < 3:
|
|
167
|
+
continue
|
|
168
|
+
tris += floori(faces.size() / 3.0)
|
|
169
|
+
if tris > MAX_SUPPORT_TRIS:
|
|
170
|
+
return _fail("mesh_budget_exceeded", "the meshes around the subject have more than " + str(MAX_SUPPORT_TRIS) + " triangles; the visible-mesh measurement stops rather than miss a surface")
|
|
171
|
+
sup_faces.append_array(faces)
|
|
172
|
+
ranges.append([tris, it[0]])
|
|
173
|
+
var stats = {"samples": 0, "subject_vertices": verts.size(), "support_meshes": ranges.size(), "support_triangles": tris, "extent": hi - lo, "skipped": skipped, "subject_colliders": _count_colliders(subject)}
|
|
174
|
+
if tris == 0:
|
|
175
|
+
return _done(stats, false)
|
|
176
|
+
var tm = TriangleMesh.new()
|
|
177
|
+
if not tm.create_from_faces(sup_faces):
|
|
178
|
+
return _fail("mesh_build_failed", "could not build a triangle tree from the support meshes")
|
|
179
|
+
# Subject samples: every vertex up to the cap, the leading band first.
|
|
180
|
+
var samples = verts
|
|
181
|
+
if verts.size() > MAX_SUBJECT_SAMPLES:
|
|
182
|
+
samples = []
|
|
183
|
+
var edge = hi - maxf(0.01, (hi - lo) * 0.1)
|
|
184
|
+
var lead = []
|
|
185
|
+
var rest = []
|
|
186
|
+
for p in verts:
|
|
187
|
+
if p.dot(d) >= edge:
|
|
188
|
+
lead.append(p)
|
|
189
|
+
else:
|
|
190
|
+
rest.append(p)
|
|
191
|
+
var half = floori(MAX_SUBJECT_SAMPLES * 0.5)
|
|
192
|
+
var lstep = maxi(1, ceili(lead.size() / float(half)))
|
|
193
|
+
for i in range(0, lead.size(), lstep):
|
|
194
|
+
samples.append(lead[i])
|
|
195
|
+
var room = maxi(1, MAX_SUBJECT_SAMPLES - samples.size())
|
|
196
|
+
var rstep = maxi(1, ceili(rest.size() / float(room)))
|
|
197
|
+
for i in range(0, rest.size(), rstep):
|
|
198
|
+
samples.append(rest[i])
|
|
199
|
+
stats["samples"] = samples.size()
|
|
200
|
+
var best = INF
|
|
201
|
+
var best_hit = {}
|
|
202
|
+
# Subject vertices cast along d, each from the subject's top plane above it.
|
|
203
|
+
for p in samples:
|
|
204
|
+
var back = p.dot(d) - lo + EPS
|
|
205
|
+
var begin = p - d * back
|
|
206
|
+
var hit = tm.intersect_ray(begin, d)
|
|
207
|
+
if hit.is_empty():
|
|
208
|
+
continue
|
|
209
|
+
var travel = (hit["position"] - begin).dot(d) - back
|
|
210
|
+
if travel < best:
|
|
211
|
+
best = travel
|
|
212
|
+
best_hit = {"point": hit["position"], "normal": hit["normal"], "face": int(hit["face_index"]), "from": "subject_vertex"}
|
|
213
|
+
# Support vertices inside the footprint cast back onto the subject.
|
|
214
|
+
var stm = TriangleMesh.new()
|
|
215
|
+
if stm.create_from_faces(sub_faces):
|
|
216
|
+
var checked = 0
|
|
217
|
+
for i in range(sup_faces.size()):
|
|
218
|
+
var q = sup_faces[i]
|
|
219
|
+
var sq = q.dot(d)
|
|
220
|
+
if sq < lo or sq > hi + max_distance:
|
|
221
|
+
continue
|
|
222
|
+
var qu = q.dot(u)
|
|
223
|
+
var qv = q.dot(v)
|
|
224
|
+
if qu < umin or qu > umax or qv < vmin or qv > vmax:
|
|
225
|
+
continue
|
|
226
|
+
checked += 1
|
|
227
|
+
if checked > MAX_REVERSE_VERTS:
|
|
228
|
+
stats["reverse_partial"] = true
|
|
229
|
+
break
|
|
230
|
+
var down = maxf(0.0, hi - sq) + EPS
|
|
231
|
+
var begin = q + d * down
|
|
232
|
+
var hit = stm.intersect_ray(begin, -d)
|
|
233
|
+
if hit.is_empty():
|
|
234
|
+
continue
|
|
235
|
+
var travel = (begin - hit["position"]).dot(d) - down
|
|
236
|
+
if travel < best:
|
|
237
|
+
best = travel
|
|
238
|
+
best_hit = {"point": q, "normal": -hit["normal"], "face": floori(i / 3.0), "from": "support_vertex"}
|
|
239
|
+
if best_hit.is_empty() or best > max_distance:
|
|
240
|
+
return _done(stats, false)
|
|
241
|
+
var shift = best - gap
|
|
242
|
+
var world_delta = d * shift
|
|
243
|
+
var parent_basis = Basis.IDENTITY
|
|
244
|
+
if not subject.top_level and subject.get_parent() != null:
|
|
245
|
+
parent_basis = _xf(subject.get_parent(), root).basis
|
|
246
|
+
if absf(parent_basis.determinant()) < 0.0000001:
|
|
247
|
+
return _fail("singular_parent", "the subject's parent transform is singular")
|
|
248
|
+
var after = subject.position + parent_basis.inverse() * world_delta
|
|
249
|
+
var support = _support_of(ranges, int(best_hit["face"]))
|
|
250
|
+
var origin = _xf(subject, root).origin
|
|
251
|
+
stats["travel"] = best
|
|
252
|
+
stats["shift"] = shift
|
|
253
|
+
stats["support"] = _rel(root, support) if support != null else ""
|
|
254
|
+
stats["support_has_collider"] = _has_collider(support, root)
|
|
255
|
+
stats["hit_point"] = _raw3(best_hit["point"])
|
|
256
|
+
stats["hit_normal"] = _raw3(Vector3(best_hit["normal"]).normalized())
|
|
257
|
+
stats["contact_from"] = best_hit["from"]
|
|
258
|
+
stats["position"] = var_to_str(subject.position)
|
|
259
|
+
stats["position_after"] = var_to_str(after)
|
|
260
|
+
stats["origin_before"] = _raw3(origin)
|
|
261
|
+
stats["origin_after"] = _raw3(origin + world_delta)
|
|
262
|
+
return _done(stats, true)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
func _done(stats, found):
|
|
266
|
+
var out = {"ok": true, "found": found}
|
|
267
|
+
for k in stats:
|
|
268
|
+
out[k] = stats[k]
|
|
269
|
+
return out
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
func _fail(reason, message):
|
|
273
|
+
return {"ok": false, "failure_reason": reason, "error": message}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
func _vec(a):
|
|
277
|
+
return Vector3(float(a[0]), float(a[1]), float(a[2]))
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
func _raw3(v):
|
|
281
|
+
return [v.x, v.y, v.z]
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
func _open_scene(scene_path):
|
|
285
|
+
var wanted = scene_path.simplify_path()
|
|
286
|
+
for r in EditorInterface.get_open_scene_roots():
|
|
287
|
+
if r != null and String(r.scene_file_path).simplify_path() == wanted:
|
|
288
|
+
return {"root": r}
|
|
289
|
+
return {"fail": _fail("scene_not_open", "Scene " + wanted + " is not open in the editor")}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
func _node(root, path):
|
|
293
|
+
var p = String(path).strip_edges()
|
|
294
|
+
if p == "" or p == "." or p == "./":
|
|
295
|
+
return root
|
|
296
|
+
if p.begins_with("./"):
|
|
297
|
+
p = p.substr(2)
|
|
298
|
+
var n = root.get_node_or_null(NodePath(p))
|
|
299
|
+
if n == null and p.begins_with(String(root.name) + "/"):
|
|
300
|
+
n = root.get_node_or_null(NodePath(p.substr(String(root.name).length() + 1)))
|
|
301
|
+
return n
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
func _rel(root, n):
|
|
305
|
+
if n == root:
|
|
306
|
+
return "."
|
|
307
|
+
return String(root.get_path_to(n))
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# Scene-space transform: Node3D.transform accumulated up to and including root.
|
|
311
|
+
func _xf(node, root):
|
|
312
|
+
var t = Transform3D.IDENTITY
|
|
313
|
+
var cur = node
|
|
314
|
+
while cur != null:
|
|
315
|
+
if cur is Node3D:
|
|
316
|
+
t = cur.transform * t
|
|
317
|
+
if cur.top_level and cur != root:
|
|
318
|
+
break
|
|
319
|
+
if cur == root:
|
|
320
|
+
break
|
|
321
|
+
cur = cur.get_parent()
|
|
322
|
+
return t
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
func _visible(node, root):
|
|
326
|
+
var cur = node
|
|
327
|
+
while cur != null:
|
|
328
|
+
if cur is Node3D and not cur.visible:
|
|
329
|
+
return false
|
|
330
|
+
if cur == root:
|
|
331
|
+
break
|
|
332
|
+
cur = cur.get_parent()
|
|
333
|
+
return true
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# Visible MeshInstance3D nodes under start (inclusive) with their scene-space
|
|
337
|
+
# transform; exclude is a subtree root to skip.
|
|
338
|
+
func _mesh_items(start, root, exclude, skipped):
|
|
339
|
+
var base_xf = Transform3D.IDENTITY
|
|
340
|
+
var base_vis = true
|
|
341
|
+
if start != root and start.get_parent() != null:
|
|
342
|
+
base_xf = _xf(start.get_parent(), root)
|
|
343
|
+
base_vis = _visible(start.get_parent(), root)
|
|
344
|
+
var out = []
|
|
345
|
+
var stack = [[start, base_xf, base_vis]]
|
|
346
|
+
var visited = 0
|
|
347
|
+
while stack.size() > 0:
|
|
348
|
+
visited += 1
|
|
349
|
+
if visited > MAX_NODES:
|
|
350
|
+
skipped["node_budget"] = true
|
|
351
|
+
break
|
|
352
|
+
var item = stack.pop_back()
|
|
353
|
+
var n = item[0]
|
|
354
|
+
if exclude != null and n == exclude:
|
|
355
|
+
continue
|
|
356
|
+
var t = item[1]
|
|
357
|
+
var vis = item[2]
|
|
358
|
+
if n is Node3D:
|
|
359
|
+
if n.top_level and n != root:
|
|
360
|
+
t = n.transform
|
|
361
|
+
else:
|
|
362
|
+
t = t * n.transform
|
|
363
|
+
vis = vis and n.visible
|
|
364
|
+
if vis and n is GeometryInstance3D:
|
|
365
|
+
if n is MeshInstance3D:
|
|
366
|
+
if n.mesh != null:
|
|
367
|
+
if _screen_space(n):
|
|
368
|
+
skipped["screen_space"] += 1
|
|
369
|
+
else:
|
|
370
|
+
out.append([n, t])
|
|
371
|
+
else:
|
|
372
|
+
skipped["not_mesh"] += 1
|
|
373
|
+
for c in n.get_children():
|
|
374
|
+
stack.append([c, t, vis])
|
|
375
|
+
return out
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
# A mesh whose shader writes POSITION is drawn in screen space (a post quad):
|
|
379
|
+
# its triangles are not where the scene shows anything.
|
|
380
|
+
func _screen_space(mi):
|
|
381
|
+
var mats = []
|
|
382
|
+
if mi.material_override != null:
|
|
383
|
+
mats.append(mi.material_override)
|
|
384
|
+
for s in range(mi.mesh.get_surface_count()):
|
|
385
|
+
var m = mi.get_active_material(s)
|
|
386
|
+
if m != null:
|
|
387
|
+
mats.append(m)
|
|
388
|
+
for m in mats:
|
|
389
|
+
if m is ShaderMaterial and m.shader != null:
|
|
390
|
+
var code = String(m.shader.code)
|
|
391
|
+
if code.contains("POSITION") and code.contains("vertex"):
|
|
392
|
+
return true
|
|
393
|
+
return false
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
func _support_of(ranges, face):
|
|
397
|
+
for r in ranges:
|
|
398
|
+
if face < int(r[0]):
|
|
399
|
+
return r[1]
|
|
400
|
+
return null
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
func _count_colliders(node):
|
|
404
|
+
var count = 0
|
|
405
|
+
var stack = [node]
|
|
406
|
+
while stack.size() > 0:
|
|
407
|
+
var n = stack.pop_back()
|
|
408
|
+
if (n is CollisionShape3D or n is CollisionPolygon3D) and not n.disabled and n.get_parent() is CollisionObject3D:
|
|
409
|
+
count += 1
|
|
410
|
+
for c in n.get_children():
|
|
411
|
+
stack.append(c)
|
|
412
|
+
return count
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
# The support mesh sits in a collision body, or carries one below it.
|
|
416
|
+
func _has_collider(node, root):
|
|
417
|
+
if node == null:
|
|
418
|
+
return false
|
|
419
|
+
var cur = node
|
|
420
|
+
while cur != null and cur != root:
|
|
421
|
+
if cur is CollisionObject3D:
|
|
422
|
+
return true
|
|
423
|
+
cur = cur.get_parent()
|
|
424
|
+
return _count_colliders(node) > 0
|
|
425
|
+
`;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* surface-snap — ONE implementation of `summer_snap_to_surface` for both faces
|
|
3
|
+
* (src/mcp/tools/spatial-tools.ts and tool-dispatch.ts), around the engine's
|
|
4
|
+
* SnapToSurface op (modules/1summer_engine/editor/ops/surface_snap_ops.cpp).
|
|
5
|
+
*
|
|
6
|
+
* Two gaps in the engine op, closed here with ops main already ships:
|
|
7
|
+
*
|
|
8
|
+
* 1. Its two recovery failures say nothing about WHY. gap_exceeds_hit_travel
|
|
9
|
+
* and overlap_recovery_exceeded name neither the node in the way nor
|
|
10
|
+
* whether the subject started out overlapping. After either failure a
|
|
11
|
+
* read-only Starcast3D at the subject's current pose names the contacts,
|
|
12
|
+
* the start-overlap state and what is below, and the failure gains a
|
|
13
|
+
* concrete next_step.
|
|
14
|
+
*
|
|
15
|
+
* 2. It cannot lift a prop SUNK into its support. When the start pose
|
|
16
|
+
* overlaps, the engine backs the subject off against the cast direction,
|
|
17
|
+
* sweeps, and measures hitTravel from the ORIGINAL pose — negative for a
|
|
18
|
+
* sunk prop — then refuses any gap above it ("would move opposite the
|
|
19
|
+
* verified sweep"), although the backed-off sweep did verify that space
|
|
20
|
+
* (_solve_physics compares p_gap with hit_travel instead of with the swept
|
|
21
|
+
* distance from the backed-off start). So on gap_exceeds_hit_travel with a
|
|
22
|
+
* start overlap this module lifts the subject against the cast direction
|
|
23
|
+
* by the overlap depth plus a margin (exact local position from the saved
|
|
24
|
+
* scene), snaps again from there, and keeps the result only when it
|
|
25
|
+
* settles back on a node the subject was sunk into. Otherwise the original
|
|
26
|
+
* position is restored and the failure is reported.
|
|
27
|
+
*
|
|
28
|
+
* 3. Without a collider on the subject or the support it falls back to
|
|
29
|
+
* visual_aabb: AABBs swept against AABBs. One big AABB around the subject
|
|
30
|
+
* (a tree, a building block, a post quad with a huge custom AABB) is an
|
|
31
|
+
* overlap no back-off clears, so collider-less props on a PlaneMesh failed
|
|
32
|
+
* with overlap_recovery_exceeded, and a success can rest on an AABB above
|
|
33
|
+
* the real surface. Whenever the engine answers with visual_aabb evidence
|
|
34
|
+
* (overlap_recovery_exceeded, gap_exceeds_hit_travel, surface_not_found,
|
|
35
|
+
* or a seat), a read-only probe measures the move from visible triangles
|
|
36
|
+
* instead (surface-snap-mesh.ts), one SetProp places the subject, a second
|
|
37
|
+
* read verifies the gap and SaveScene saves it: evidence visual_mesh.
|
|
38
|
+
*/
|
|
39
|
+
import { type CapabilityAdvertisingClient } from "../capability-skew.js";
|
|
40
|
+
import { type JsonRecord } from "../util/json.js";
|
|
41
|
+
import { type Vec3 } from "./math3d.js";
|
|
42
|
+
/** Clearance added above the support before the second sweep. */
|
|
43
|
+
export declare const SNAP_LIFT_MARGIN = 0.02;
|
|
44
|
+
/** Never lift further than this automatically (a deeper overlap is a placement mistake to report). */
|
|
45
|
+
export declare const SNAP_MAX_AUTO_LIFT = 0.5;
|
|
46
|
+
export interface SnapToSurfaceArgs {
|
|
47
|
+
scenePath: string;
|
|
48
|
+
subjectPath: string;
|
|
49
|
+
direction: Vec3;
|
|
50
|
+
maxDistance: number;
|
|
51
|
+
gap: number;
|
|
52
|
+
alignUp: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface SnapClient extends CapabilityAdvertisingClient {
|
|
55
|
+
executeIdentityBoundOps(ops: Record<string, unknown>[], options?: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
56
|
+
readProjectFile(path: string, maxBytes?: number): Promise<unknown>;
|
|
57
|
+
}
|
|
58
|
+
/** A visible-mesh gap this close to the requested one needs no move. */
|
|
59
|
+
export declare const MESH_SNAP_TOLERANCE = 0.001;
|
|
60
|
+
/** The verify read must find the requested gap within this, or the move is undone. */
|
|
61
|
+
export declare const MESH_SNAP_VERIFY_TOLERANCE = 0.005;
|
|
62
|
+
export declare function buildSnapToSurfaceOp(args: SnapToSurfaceArgs): JsonRecord;
|
|
63
|
+
export interface SnapDiagnosis {
|
|
64
|
+
available: boolean;
|
|
65
|
+
/** true / false from the starcast contact state; null when it could not run. */
|
|
66
|
+
startOverlap: boolean | null;
|
|
67
|
+
contacts: string[];
|
|
68
|
+
below?: {
|
|
69
|
+
status?: unknown;
|
|
70
|
+
object?: unknown;
|
|
71
|
+
distance?: unknown;
|
|
72
|
+
};
|
|
73
|
+
/** World AABB size of the subject. */
|
|
74
|
+
size?: Vec3;
|
|
75
|
+
note?: string;
|
|
76
|
+
}
|
|
77
|
+
export declare function snapToSurface(client: SnapClient, args: SnapToSurfaceArgs): Promise<unknown>;
|