Skip to content

Commit

Permalink
Add demo
Browse files Browse the repository at this point in the history
  • Loading branch information
CharlesAverill committed Oct 11, 2023
1 parent a3637d1 commit 36cdd18
Show file tree
Hide file tree
Showing 12 changed files with 873 additions and 81 deletions.
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ test: fmt
testf: fmt
opam exec -- dune runtest -f

OBJ ?= objs/teapot.obj
OBJS ?= objs/teapot.obj
N ?= 1

run: build
opam exec -- dune exec -- zenith -obj $(OBJ)
opam exec -- dune exec -- zenith $(OBJS) -n $(N)

raw_run: build
clear
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ZENITH
Zen Engine for Navigating wIreframes In Three-dimensional Holographic space

![teapot](media/success2/teapot.gif)
![demo](media/demo.gif)

## Usage

Expand Down
26 changes: 18 additions & 8 deletions bin/argparse.ml
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
open Zenith.Mesh
open Zenith.Objloader

type arguments = { obj_mesh : mesh }
type arguments = { obj_meshes : mesh list }

let copies l n =
if n <= 0 then []
else List.concat (List.map (fun x -> List.init n (fun _ -> x)) l)

let parse_arguments () =
let obj_file = ref "" in
let obj = ref cube in
let obj_files = ref [] in
let objs = ref [ cube ] in
let num_each = ref 1 in

let speclist =
[ ("-obj", Arg.String (fun s -> obj_file := s), "An .OBJ file to render") ]
[
( "-n",
Arg.Int (fun n -> num_each := n),
"How many of each object to render" );
]
in
let usage_msg = "Usage: zenith [OBJ]+" in

let usage_msg = "Usage: zenith [OBJ]" in
Arg.parse speclist (fun n -> obj_files := n :: !obj_files) usage_msg;

Arg.parse speclist (fun _ -> ()) usage_msg;
if !obj_files != [] then objs := List.map load_obj !obj_files;

if !obj_file != "" then obj := load_obj !obj_file;
if !num_each != 1 then objs := copies !objs !num_each;

{ obj_mesh = !obj }
{ obj_meshes = !objs }
113 changes: 113 additions & 0 deletions bin/driver.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
open Graphics
open Logging
open Zenith.Config
open Math.Vector
open Zenith.Mesh
open Zenith.Renderer

let clear_window color =
let fg = foreground in
set_color color;
fill_rect 0 0 (size_x ()) (size_y ());
set_color fg

let evenly_spaced_positions meshes =
let n = List.length meshes in
let radius = if n > 1 then 1. +. largest_distance meshes else 0. in
let delta_theta = 2.0 *. Float.pi /. float_of_int n in

let rec generate_positions theta count acc =
if count <= 0 then acc
else
let x = radius *. Float.cos theta in
let y = radius *. Float.sin theta in
generate_positions (theta +. delta_theta) (count - 1) (v3 x y 0. :: acc)
in

generate_positions 0.0 n []

let to_draw = ref []
let break_mainloop = ref false
let euler = ref zvec
let translation = ref zvec

let update_euler n d =
let eulerx, eulery, eulerz =
match !euler with { x; y; z; w = __ } -> (x, y, z)
in
match n with
| 0 -> euler := { x = eulerx +. d; y = eulery; z = eulerz; w = 0. }
| 1 -> euler := { x = eulerx; y = eulery +. d; z = eulerz; w = 0. }
| 2 -> euler := { x = eulerx; y = eulery; z = eulerz +. d; w = 0. }
| _ ->
fatal rc_MatchError
("Tried to update euler angle " ^ string_of_int n
^ ", but expected [0:2]")

let update_translation n d =
let tx, ty, tz = match !translation with { x; y; z; w = __ } -> (x, y, z) in
match n with
| 0 -> translation := { x = tx +. d; y = ty; z = tz; w = 0. }
| 1 -> translation := { x = tx; y = ty +. d; z = tz; w = 0. }
| 2 -> translation := { x = tx; y = ty; z = tz +. d; w = 0. }
| _ ->
fatal rc_MatchError
("Tried to update translation " ^ string_of_int n
^ ", but expected [0:2]")

let input_dispatch key =
match key with
(* X Translation *)
| 'a' -> update_translation 0 (-.translation_incr)
| 'd' -> update_translation 0 translation_incr
(* Y Translation *)
| 'w' -> update_translation 1 translation_incr
| 's' -> update_translation 1 (-.translation_incr)
(*Z Translation *)
| 'q' -> update_translation 2 (-.translation_incr)
| 'e' -> update_translation 2 translation_incr
(* Pitch *)
| 'i' -> update_euler 0 (-.rotation_incr)
| 'k' -> update_euler 0 rotation_incr
(* Yaw *)
| 'j' -> update_euler 1 (-.rotation_incr)
| 'l' -> update_euler 1 rotation_incr
(* Roll *)
| 'u' -> update_euler 2 (-.rotation_incr)
| 'o' -> update_euler 2 rotation_incr
(* Reset scene *)
| 'r' ->
euler := zvec;
translation := zvec
(* Escape *)
| x when x = char_of_int 27 -> break_mainloop := true
| _ ->
print_int (int_of_char key);
print_endline ""

let draw_scene () =
display_mode false;
clear_window black;
(* Draw each mesh at their offset *)
(* print_endline (string_of_int (List.length !to_draw - 1)); *)
for i = 0 to List.length !to_draw - 1 do
let mesh, offset = List.nth !to_draw i in
draw_mesh mesh !euler (v3_add !translation offset)
done;
let input = wait_next_event [ Poll ] in
synchronize ();
display_mode true;
if input.keypressed then input_dispatch (read_key ())

let rec main_loop () =
draw_scene ();
if !break_mainloop then () else main_loop ()

let start meshes =
(* Evenly-space meshes *)
_log Log_Debug (string_of_float (largest_distance meshes));
let mesh_positions = evenly_spaced_positions meshes in
to_draw := List.combine meshes mesh_positions;
auto_synchronize false;
open_graph (" " ^ string_of_int viewportw ^ "x" ^ string_of_int viewporth);
main_loop ()
63 changes: 3 additions & 60 deletions bin/main.ml
Original file line number Diff line number Diff line change
@@ -1,63 +1,6 @@
open Graphics
open Logging
open Zenith.Config
open Zenith.Mesh
open Zenith.Renderer

let clear_window color =
let fg = foreground in
set_color color;
fill_rect 0 0 (size_x ()) (size_y ());
set_color fg

let to_draw = ref cube
let break_mainloop = ref false
let euler = ref (0., 0., 0.)

let update_euler n d =
let eulerx, eulery, eulerz = !euler in
match n with
| 0 -> euler := (eulerx +. d, eulery, eulerz)
| 1 -> euler := (eulerx, eulery +. d, eulerz)
| 2 -> euler := (eulerx, eulery, eulerz +. d)
| _ ->
fatal rc_MatchError
("Tried to update euler angle " ^ string_of_int n
^ ", but expected [0:2]")

let input_dispatch key =
match key with
(* Pitch *)
| 'w' -> update_euler 0 (-.rotation_incr)
| 's' -> update_euler 0 rotation_incr
(* Yaw *)
| 'a' -> update_euler 1 (-.rotation_incr)
| 'd' -> update_euler 1 rotation_incr
(* Roll *)
| 'q' -> update_euler 2 (-.rotation_incr)
| 'e' -> update_euler 2 rotation_incr
(* Reset scene *)
| 'r' -> euler := (0., 0., 0.)
(* Escape *)
| x when x = char_of_int 27 -> break_mainloop := true
| _ ->
print_int (int_of_char key);
print_endline ""

let draw_scene () =
clear_window black;
draw_mesh !to_draw !euler;
let input = wait_next_event [ Poll ] in
synchronize ();
if input.keypressed then input_dispatch (read_key ())

let rec main_loop () =
draw_scene ();
if !break_mainloop then () else main_loop ()
open Argparse
open Driver

let () =
let args = Argparse.parse_arguments () in
to_draw := args.obj_mesh;
open_graph (" " ^ string_of_int viewportw ^ "x" ^ string_of_int viewporth);
auto_synchronize false;
main_loop ()
start args.obj_meshes
7 changes: 4 additions & 3 deletions lib/config.ml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
open Math.Vector
open Graphics

let viewportw, viewporth = (700, 700)
let viewpoint = v3 0. 0. 10.
let viewportw, viewporth = (1000, 1000)
let viewpoint = v3 0. 0. 30.
let fov = 45.0
let aspect_ratio = 1.
let z_near = 0.1
let z_far = 50.
let rotation_incr = 7.5
let translation_incr = 0.5
let draw_verts = false
let vert_radius = 5
let vert_radius = 3
let vert_color = red
let edge_color = green
9 changes: 9 additions & 0 deletions lib/math/vector.ml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,14 @@ let vec_unit vec =
let vec_dot a b = (a.x *. b.x) +. (a.y *. b.y) +. (a.z *. b.z) +. (a.w *. b.w)
let vec_div v n = { x = v.x /. n; y = v.y /. n; z = v.y /. n; w = v.w }

let v3_add v1 v2 =
{ x = v1.x +. v2.x; y = v1.y +. v2.y; z = v1.z +. v2.z; w = v1.w }

let v3_sub v1 v2 =
{ x = v1.x -. v2.x; y = v1.y -. v2.y; z = v1.z -. v2.z; w = v1.w }

let min_vec a b = v3 (min a.x b.x) (min a.y b.y) (min a.z b.z)
let max_vec a b = v3 (max a.x b.x) (max a.y b.y) (max a.z b.z)

let string_of_vec vec =
Printf.sprintf "<%f, %f, %f, %f>" vec.x vec.y vec.z vec.w
27 changes: 27 additions & 0 deletions lib/mesh.ml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,30 @@ let cube =
let pyramid =
( [ v3 0. 0. 0.; v3 1. 0. 0.; v3 1. 0. 1.; v3 0. 0. 1.; v3 0.5 1. 0.5 ],
[ (0, 1); (1, 2); (2, 3); (3, 0); (0, 4); (1, 4); (2, 4); (3, 4) ] )

let filter_duplicates meshes =
let rec aux acc seen = function
| [] -> List.rev acc
| mesh :: rest ->
if List.exists (fun m -> m = mesh) seen then aux acc seen rest
else aux (mesh :: acc) (mesh :: seen) rest
in
aux [] [] meshes

let largest_distance (meshes : mesh list) =
let max_distance =
List.fold_left
(fun acc (verts, _) ->
List.fold_left
(fun acc_vert_i vert_i ->
List.fold_left
(fun acc_vert_j vert_j ->
let dist = v3_sub vert_i vert_j in
let max_dist = max (max dist.x dist.y) dist.z in
max acc_vert_j max_dist)
acc_vert_i verts)
acc verts)
0.0 (filter_duplicates meshes)
in

max_distance
23 changes: 16 additions & 7 deletions lib/renderer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,21 @@ let view_matrix eye_pos =
dim = (4, 4);
}

let model_matrix euler_rot =
let model_matrix euler_rot translation_vec =
let radx, rady, radz =
match euler_rot with
| rotx, roty, rotz -> (deg_to_rad rotx, deg_to_rad roty, deg_to_rad rotz)
| { x = rotx; y = roty; z = rotz; w = _ } ->
(deg_to_rad rotx, deg_to_rad roty, deg_to_rad rotz)
and tx, ty, tz =
match translation_vec with
| { x = tx; y = ty; z = tz; w = _ } -> (tx, ty, tz)
in
let translation =
{
arr = [| 1.; 0.; 0.; tx; 0.; 1.; 0.; ty; 0.; 0.; 1.; tz; 0.; 0.; 0.; 1. |];
dim = (4, 4);
}
in
let translation = id_matrix 4 4 in
let rotation_x, rotation_y, rotation_z =
( {
arr =
Expand Down Expand Up @@ -173,14 +182,14 @@ let projection_matrix fov aspect_ratio z_near z_far =
dim = (4, 4);
}

let project_mesh mesh euler_rot =
let project_mesh mesh euler_rot translation =
let mvp =
matmul
(matmul
(projection_matrix Config.fov Config.aspect_ratio Config.z_near
Config.z_far)
(view_matrix Config.viewpoint))
(model_matrix euler_rot)
(model_matrix euler_rot translation)
in
let f1, f2 =
( (Config.z_far -. Config.z_near) /. 2.,
Expand All @@ -200,10 +209,10 @@ let project_mesh mesh euler_rot =
w = x.w;
})

let draw_mesh mesh euler_rot =
let draw_mesh mesh euler_rot translation =
let c = foreground in
set_color Config.edge_color;
let verts, edges = project_mesh mesh euler_rot in
let verts, edges = project_mesh mesh euler_rot translation in
let _ =
List.fold_left
(fun visited_verts edge ->
Expand Down
Binary file added media/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions objs/star_destroyer.mtl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Blender MTL File: 'None'
# Material Count: 0
Loading

0 comments on commit 36cdd18

Please sign in to comment.