136 lines
4.0 KiB
Python
136 lines
4.0 KiB
Python
import pyray as RL
|
|
from pyray import (Rectangle as Rect, Vector2 as Vec2, Vector3 as Vec3, Camera3D, BoundingBox)
|
|
import math
|
|
import pdb
|
|
import random
|
|
from ctypes import Structure, c_float
|
|
from typing import Optional, Tuple, List
|
|
from dataclasses import dataclass, field
|
|
|
|
def dump(struct):
|
|
s = f"{RL.ffi.typeof(struct)}: (".replace('<ctype ', '').replace('>', '')
|
|
for field in dir(struct):
|
|
data = struct.__getattribute__(field)
|
|
if str(data).startswith("<cdata"):
|
|
data = dump(data)
|
|
s += f"{field}:{data} "
|
|
s += ")"
|
|
return s
|
|
|
|
screen_width = 1024
|
|
screen_height = 768
|
|
grid_slices = 100
|
|
grid_spacing = 0.2
|
|
vertex_radius = 0.12
|
|
|
|
@dataclass
|
|
class World:
|
|
cam: Camera3D
|
|
floor_bb: BoundingBox
|
|
rotate_cam: bool = True
|
|
frame_count: int = 0
|
|
vertices: List[Vec3] = field(default_factory=list)
|
|
dragging_vert: Vec3 = None
|
|
convex_hull_points: List[Vec3] = field(default_factory=list)
|
|
|
|
def init() -> World:
|
|
cam = Camera3D(Vec3(0, 10, 10), Vec3(0, 0, 0), Vec3(0, 1, 0), 45, RL.CAMERA_PERSPECTIVE)
|
|
half_grid = grid_slices * grid_spacing * 0.5
|
|
floor_bb = BoundingBox(Vec3(-half_grid, -0.01, -half_grid), Vec3(half_grid, 0.01, half_grid))
|
|
return World(cam, floor_bb)
|
|
|
|
def player_input(w: World):
|
|
if RL.is_key_pressed(RL.KEY_SPACE):
|
|
w.rotate_cam = not w.rotate_cam
|
|
if RL.is_mouse_button_pressed(0):
|
|
ray = RL.get_mouse_ray(RL.get_mouse_position(), w.cam)
|
|
# First try to collide with existing points
|
|
for v in w.vertices:
|
|
collision = RL.get_ray_collision_sphere(ray, v, vertex_radius)
|
|
if collision.hit:
|
|
w.dragging_vert = v
|
|
found_collision = True
|
|
return
|
|
|
|
w.dragging_vert = None
|
|
collision = RL.get_ray_collision_box(ray, w.floor_bb)
|
|
if collision.hit:
|
|
p = collision.point
|
|
w.vertices.append(Vec3(p.x, 0, p.z))
|
|
if RL.is_mouse_button_down(0):
|
|
if w.dragging_vert is not None:
|
|
ray = RL.get_mouse_ray(RL.get_mouse_position(), w.cam)
|
|
collision = RL.get_ray_collision_box(ray, w.floor_bb)
|
|
if collision.hit:
|
|
w.dragging_vert.x = collision.point.x
|
|
w.dragging_vert.z = collision.point.z
|
|
if RL.is_mouse_button_released(0):
|
|
w.dragging_vert = None
|
|
|
|
def update(w: World):
|
|
if len(w.vertices) <= 2:
|
|
return
|
|
w.convex_hull_points.clear()
|
|
pending = sorted(w.vertices, key=lambda v: v.x)
|
|
w.convex_hull_points.append(pending[0])
|
|
idx = 0
|
|
# TODO:
|
|
while True:
|
|
v1 = w.convex_hull_points[-1]
|
|
left_most = pending[(idx + 1) % len(pending)]
|
|
idx += 1
|
|
if v1 == left_most:
|
|
continue
|
|
for v2 in pending:
|
|
x1 = v1.x - left_most.x
|
|
x2 = v1.x - v2.x
|
|
y1 = v1.z - left_most.z
|
|
y2 = v1.z - v2.z
|
|
|
|
sign = y2*x1 - y1*x2
|
|
if v1 == v2:
|
|
continue
|
|
if sign > 0:
|
|
left_most = v2
|
|
if left_most == w.convex_hull_points[0]:
|
|
break
|
|
w.convex_hull_points.append(left_most)
|
|
|
|
def draw_3d(w: World):
|
|
RL.draw_grid(grid_slices, grid_spacing)
|
|
RL.draw_bounding_box(w.floor_bb, RL.GREEN)
|
|
for vert in w.vertices:
|
|
RL.draw_sphere(vert, vertex_radius, RL.GREEN)
|
|
if len(w.convex_hull_points) > 2:
|
|
for i,p in enumerate(w.convex_hull_points[:-1]):
|
|
RL.draw_line_3d(p, w.convex_hull_points[i+1], RL.GREEN)
|
|
RL.draw_line_3d(w.convex_hull_points[-1], w.convex_hull_points[0], RL.GREEN)
|
|
# exit(0)
|
|
|
|
def draw_2d(w: World):
|
|
RL.draw_fps(10, 10)
|
|
|
|
RL.init_window(screen_width, screen_height, "Starter");
|
|
RL.set_target_fps(60)
|
|
|
|
w = init()
|
|
while not RL.window_should_close():
|
|
player_input(w)
|
|
update(w)
|
|
|
|
# Drawing
|
|
if w.rotate_cam:
|
|
RL.update_camera(w.cam, RL.CameraMode.CAMERA_ORBITAL)
|
|
RL.begin_drawing()
|
|
RL.clear_background(RL.WHITE)
|
|
|
|
RL.begin_mode_3d(w.cam)
|
|
draw_3d(w)
|
|
RL.end_mode_3d()
|
|
|
|
draw_2d(w)
|
|
|
|
RL.end_drawing()
|
|
w.frame_count += 1
|
|
|