Voxel functionality extension
This commit is contained in:
parent
3e6b6a5847
commit
992ea781cb
14 changed files with 3045 additions and 17 deletions
250
tests/demo/screens/voxel_navigation_demo.py
Normal file
250
tests/demo/screens/voxel_navigation_demo.py
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
#!/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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue