#!/usr/bin/env python3 """Visual Demo: Milestone 12 - VoxelGrid Navigation Projection Demonstrates projection of 3D voxel terrain to 2D navigation grid for pathfinding. Shows: 1. Voxel dungeon with multiple levels 2. Navigation grid projection (walkable/unwalkable areas) 3. A* pathfinding through the projected terrain 4. FOV computation from voxel transparency """ import mcrfpy import sys import math def create_demo_scene(): """Create the navigation projection demo scene""" scene = mcrfpy.Scene("voxel_nav_demo") # ========================================================================= # Create a small dungeon-style voxel grid # ========================================================================= vg = mcrfpy.VoxelGrid((16, 8, 16), cell_size=1.0) # Add materials floor_mat = vg.add_material("floor", (100, 80, 60)) # Brown floor wall_mat = vg.add_material("wall", (80, 80, 90), transparent=False) # Gray walls pillar_mat = vg.add_material("pillar", (60, 60, 70), transparent=False) # Dark pillars glass_mat = vg.add_material("glass", (150, 200, 255), transparent=True) # Transparent glass water_mat = vg.add_material("water", (50, 100, 200), transparent=True, path_cost=3.0) # Slow water # Create floor vg.fill_box((0, 0, 0), (15, 0, 15), floor_mat) # Create outer walls vg.fill_box((0, 1, 0), (15, 4, 0), wall_mat) # North wall vg.fill_box((0, 1, 15), (15, 4, 15), wall_mat) # South wall vg.fill_box((0, 1, 0), (0, 4, 15), wall_mat) # West wall vg.fill_box((15, 1, 0), (15, 4, 15), wall_mat) # East wall # Interior walls creating rooms vg.fill_box((5, 1, 0), (5, 4, 10), wall_mat) # Vertical wall vg.fill_box((10, 1, 5), (15, 4, 5), wall_mat) # Horizontal wall # Doorways (carve holes) vg.fill_box((5, 1, 3), (5, 2, 4), 0) # Door in vertical wall vg.fill_box((12, 1, 5), (13, 2, 5), 0) # Door in horizontal wall # Central pillars vg.fill_box((8, 1, 8), (8, 4, 8), pillar_mat) vg.fill_box((8, 1, 12), (8, 4, 12), pillar_mat) # Water pool in one corner (slow movement) vg.fill_box((1, 0, 11), (3, 0, 14), water_mat) # Glass window vg.fill_box((10, 2, 5), (11, 3, 5), glass_mat) # Raised platform in one area (height variation) vg.fill_box((12, 1, 8), (14, 1, 13), floor_mat) # Platform at y=1 # ========================================================================= # Create Viewport3D with navigation grid # ========================================================================= viewport = mcrfpy.Viewport3D(pos=(10, 10), size=(600, 400)) viewport.set_grid_size(16, 16) viewport.cell_size = 1.0 # Configure camera for top-down view viewport.camera_pos = (8, 15, 20) viewport.camera_target = (8, 0, 8) # Add voxel layer viewport.add_voxel_layer(vg, z_index=0) # Project voxels to navigation grid with headroom=2 (entity needs 2 voxels height) viewport.project_voxel_to_nav(vg, headroom=2) # ========================================================================= # Info panel # ========================================================================= info_frame = mcrfpy.Frame(pos=(620, 10), size=(250, 400)) info_frame.fill_color = mcrfpy.Color(30, 30, 40, 220) info_frame.outline_color = mcrfpy.Color(100, 100, 120) info_frame.outline = 2.0 title = mcrfpy.Caption(text="Nav Projection Demo", pos=(10, 10)) title.fill_color = mcrfpy.Color(255, 255, 100) desc = mcrfpy.Caption(text="Voxels projected to\n2D nav grid", pos=(10, 35)) desc.fill_color = mcrfpy.Color(200, 200, 200) info1 = mcrfpy.Caption(text="Grid: 16x16 cells", pos=(10, 75)) info1.fill_color = mcrfpy.Color(150, 200, 255) info2 = mcrfpy.Caption(text="Headroom: 2 voxels", pos=(10, 95)) info2.fill_color = mcrfpy.Color(150, 200, 255) # Count walkable cells walkable_count = 0 for x in range(16): for z in range(16): cell = viewport.at(x, z) if cell.walkable: walkable_count += 1 info3 = mcrfpy.Caption(text=f"Walkable: {walkable_count}/256", pos=(10, 115)) info3.fill_color = mcrfpy.Color(100, 255, 100) # Find path example path = viewport.find_path((1, 1), (13, 13)) info4 = mcrfpy.Caption(text=f"Path length: {len(path)}", pos=(10, 135)) info4.fill_color = mcrfpy.Color(255, 200, 100) # FOV example fov = viewport.compute_fov((8, 8), 10) info5 = mcrfpy.Caption(text=f"FOV cells: {len(fov)}", pos=(10, 155)) info5.fill_color = mcrfpy.Color(200, 150, 255) # Legend legend_title = mcrfpy.Caption(text="Materials:", pos=(10, 185)) legend_title.fill_color = mcrfpy.Color(255, 255, 255) leg1 = mcrfpy.Caption(text=" Floor (walkable)", pos=(10, 205)) leg1.fill_color = mcrfpy.Color(100, 80, 60) leg2 = mcrfpy.Caption(text=" Wall (blocking)", pos=(10, 225)) leg2.fill_color = mcrfpy.Color(80, 80, 90) leg3 = mcrfpy.Caption(text=" Water (slow)", pos=(10, 245)) leg3.fill_color = mcrfpy.Color(50, 100, 200) leg4 = mcrfpy.Caption(text=" Glass (see-through)", pos=(10, 265)) leg4.fill_color = mcrfpy.Color(150, 200, 255) controls = mcrfpy.Caption(text="[Space] Recompute FOV\n[P] Show path\n[Q] Quit", pos=(10, 300)) controls.fill_color = mcrfpy.Color(150, 150, 150) info_frame.children.extend([ title, desc, info1, info2, info3, info4, info5, legend_title, leg1, leg2, leg3, leg4, controls ]) # ========================================================================= # Status bar # ========================================================================= status_frame = mcrfpy.Frame(pos=(10, 420), size=(860, 50)) status_frame.fill_color = mcrfpy.Color(20, 20, 30, 220) status_frame.outline_color = mcrfpy.Color(80, 80, 100) status_frame.outline = 1.0 status_text = mcrfpy.Caption( text="Milestone 12: VoxelGrid Navigation Projection - Project 3D voxels to 2D pathfinding grid", pos=(10, 15) ) status_text.fill_color = mcrfpy.Color(180, 180, 200) status_frame.children.append(status_text) # ========================================================================= # Add elements to scene # ========================================================================= scene.children.extend([viewport, info_frame, status_frame]) # Store references for interaction (using module-level globals) global demo_viewport, demo_voxelgrid, demo_path, demo_fov_origin demo_viewport = viewport demo_voxelgrid = vg demo_path = path demo_fov_origin = (8, 8) # ========================================================================= # Keyboard handler # ========================================================================= def on_key(key, state): global demo_fov_origin if state != mcrfpy.InputState.PRESSED: return if key == mcrfpy.Key.Q or key == mcrfpy.Key.ESCAPE: # Exit sys.exit(0) elif key == mcrfpy.Key.SPACE: # Recompute FOV from different origin ox, oz = demo_fov_origin ox = (ox + 3) % 14 + 1 oz = (oz + 5) % 14 + 1 demo_fov_origin = (ox, oz) fov = demo_viewport.compute_fov((ox, oz), 8) info5.text = f"FOV from ({ox},{oz}): {len(fov)}" elif key == mcrfpy.Key.P: # Show path info print(f"Path from (1,1) to (13,13): {len(demo_path)} steps") for i, (px, pz) in enumerate(demo_path[:10]): cell = demo_viewport.at(px, pz) print(f" Step {i}: ({px},{pz}) h={cell.height:.1f} cost={cell.cost:.1f}") if len(demo_path) > 10: print(f" ... and {len(demo_path) - 10} more steps") scene.on_key = on_key return scene def main(): """Main entry point""" print("=== Milestone 12: VoxelGrid Navigation Projection Demo ===") print() print("This demo shows how 3D voxel terrain is projected to a 2D") print("navigation grid for pathfinding and FOV calculations.") print() print("The projection scans each column from top to bottom, finding") print("the topmost walkable floor with adequate headroom.") print() scene = create_demo_scene() mcrfpy.current_scene = scene # Print nav grid summary grid_w, grid_d = demo_viewport.grid_size print("Navigation grid summary:") print(f" Grid size: {grid_w}x{grid_d}") # Count by walkability and transparency walkable = 0 blocking = 0 transparent = 0 for x in range(grid_w): for z in range(grid_d): cell = demo_viewport.at(x, z) if cell.walkable: walkable += 1 else: blocking += 1 if cell.transparent: transparent += 1 print(f" Walkable cells: {walkable}") print(f" Blocking cells: {blocking}") print(f" Transparent cells: {transparent}") print() if __name__ == "__main__": main() sys.exit(0)