$npx -y skills add thedivergentai/GD-Agentic-Skills --skill godot-3d-world-buildingExpert patterns for 3D level design using GridMap with MeshLibrary, CSG constructive solid geometry, WorldEnvironment setup, ProceduralSkyMaterial, and volumetric fog. Use when building 3D levels, modular tilesets, BSP-style geometry, or environmental effects. Trigger keywords: G
| 1 | # 3D World Building |
| 2 | |
| 3 | Expert guidance for level design with GridMaps, CSG, and environmental setup. |
| 4 | |
| 5 | ## NEVER Do |
| 6 | |
| 7 | - **NEVER forget to bake GridMap navigation** — GridMaps don't auto-generate navigation meshes. Use EditorPlugin or manual NavigationRegion3D. |
| 8 | - **NEVER use CSG for final game geometry** — CSG is for prototyping. Convert to static meshes for performance (use "Bake CSG Mesh" in editor). |
| 9 | - **NEVER scale GridMap cell size after placing tiles** — Changing `cell_size` doesn't update existing tiles, causing misalignment. Set it once at the start. |
| 10 | - **NEVER use MeshLibrary without collision shapes** — Items without collision spawn visual-only geometry that players fall through. |
| 11 | - **NEVER enable volumetric fog without DirectionalLight3D** — Volumetric fog requires at least one light to scatter. No lights = no visible fog. |
| 12 | - **NEVER animate CSG nodes during gameplay** — Moving a CSG node within another forces the CPU to recalculate the boolean geometry, causing significant performance drops. |
| 13 | - **NEVER place generic logic nodes in a GridMap** — GridMap is highly optimized only for meshes, navigation, and collision. It is not a general-purpose system for placing arbitrary node structures on a grid. |
| 14 | - **NEVER use non-manifold meshes in CSG** — If you import a custom mesh for CSGMesh3D, it must be manifold (closed, no self-intersections, no interior faces, no negative volume). Non-manifold meshes will break the CSG algorithm and are completely unsupported. |
| 15 | |
| 16 | --- |
| 17 | |
| 18 | ## Godot 4.7: 3D Editor Workflow |
| 19 | |
| 20 | - **Path3D** supports snap-to-colliders for path point placement on geometry. |
| 21 | - **3D vertex snapping** with vertex/origin base setting (editor B key workflow). |
| 22 | - `EditorSceneFormatImporter` uses **ImportFlags** enum for import constants. |
| 23 | |
| 24 | ## Available Scripts |
| 25 | |
| 26 | > **MANDATORY**: Read the appropriate script before implementing the corresponding pattern. |
| 27 | |
| 28 | ### [collision_gen.gd](scripts/collision_gen.gd) |
| 29 | Automatic collision shape generation from meshes. Use when importing models without collision or for procedural geometry. |
| 30 | |
| 31 | ### [gridmap_runtime_builder.gd](scripts/gridmap_runtime_builder.gd) |
| 32 | Runtime GridMap tile placement with batch operations and auto-navigation baking. |
| 33 | |
| 34 | ### [csg_bake_tool.gd](scripts/csg_bake_tool.gd) |
| 35 | EditorScript to bake CSG geometry to static meshes with proper materials and collision. Use when finalizing level prototypes. |
| 36 | |
| 37 | ### [safe_csg_baking.gd](scripts/safe_csg_baking.gd) |
| 38 | Expert technique for safe CSG baking. Awaits the end of the frame before extracting baked meshes to avoid empty data. |
| 39 | |
| 40 | ### [lod_manager.gd](scripts/lod_manager.gd) |
| 41 | Level-of-detail switching based on camera distance. Manages mesh swapping and visibility for large outdoor scenes. |
| 42 | |
| 43 | ### [occlusion_setup.gd](scripts/occlusion_setup.gd) |
| 44 | OccluderInstance3D configuration for manual occlusion culling. Use for indoor levels with many rooms. |
| 45 | |
| 46 | --- |
| 47 | |
| 48 | ## GridMap Fundamentals |
| 49 | |
| 50 | ### Setup Workflow |
| 51 | |
| 52 | ```gdscript |
| 53 | # 1. Create MeshLibrary resource (editor) |
| 54 | # Scene → New Inherits Scene → Create Grid-aligned meshes |
| 55 | # Scene → Convert To → MeshLibrary... |
| 56 | |
| 57 | # 2. Assign to GridMap |
| 58 | extends GridMap |
| 59 | |
| 60 | func _ready() -> void: |
| 61 | mesh_library = load("res://tilesets/dungeon_library.tres") |
| 62 | cell_size = Vector3(2, 2, 2) # Must match library cell size |
| 63 | ``` |
| 64 | |
| 65 | ### Cell Manipulation |
| 66 | |
| 67 | ```gdscript |
| 68 | # gridmap_builder.gd |
| 69 | extends GridMap |
| 70 | |
| 71 | # Place cell |
| 72 | func place_tile(grid_pos: Vector3i, tile_index: int) -> void: |
| 73 | set_cell_item(grid_pos, tile_index) |
| 74 | |
| 75 | # Get cell |
| 76 | func get_tile(grid_pos: Vector3i) -> int: |
| 77 | return get_cell_item(grid_pos) # Returns index or INVALID_CELL_ITEM (-1) |
| 78 | |
| 79 | # Remove cell |
| 80 | func remove_tile(grid_pos: Vector3i) -> void: |
| 81 | set_cell_item(grid_pos, INVALID_CELL_ITEM) |
| 82 | |
| 83 | # Rotate cell (0-23, see GridMap.ROTATION_* constants) |
| 84 | func place_rotated(grid_pos: Vector3i, tile_index: int, orientation: int) -> void: |
| 85 | set_cell_item(grid_pos, tile_index, orientation) |
| 86 | ``` |
| 87 | |
| 88 | ### Coordinate Conversion |
| 89 | |
| 90 | ```gdscript |
| 91 | # World position ↔ Grid coordinates |
| 92 | func _input(event: InputEvent) -> void: |
| 93 | if event is InputEventMouseButton and event.pressed: |
| 94 | var camera := get_viewport().get_camera_3d() |
| 95 | var from := camera.project_ray_origin(event.position) |
| 96 | var to := from + camera.project_ray_normal(event.position) * 1000 |
| 97 | |
| 98 | var space := get_world_3d().direct_space_state |
| 99 | var query := PhysicsRayQueryParameters3D.create(from, to) |
| 100 | var result := space.intersect_ray(qu |