Просмотр исходного кода

Minimal example is more minimal

Karl Zylinski 2 месяцев назад
Родитель
Сommit
8e157f0b5a

+ 1 - 1
.sublime/karl2d.sublime-project

@@ -18,7 +18,7 @@
 				},
 				},
 				{
 				{
 					"name": "minimal (web)",
 					"name": "minimal (web)",
-					"shell_cmd": "odin run build_web_example -- minimal",
+					"shell_cmd": "odin run build_web_example -- minimal_web",
 					"working_dir": "$project_path/../examples"
 					"working_dir": "$project_path/../examples"
 				},
 				},
 				{
 				{

+ 1 - 1
examples/build_web_example/build_web_example.odin

@@ -63,7 +63,7 @@ main :: proc() {
 			web_dir,
 			web_dir,
 			fmt.tprintf("-out:%v", wasm_out_path),
 			fmt.tprintf("-out:%v", wasm_out_path),
 			"-target:js_wasm32",
 			"-target:js_wasm32",
-			"-o:speed",
+			"-debug",
 			"-vet",
 			"-vet",
 			"-strict-style",
 			"-strict-style",
 		},
 		},

+ 1 - 1
examples/build_web_example/web_entry_templates/web_entry_template.odin

@@ -18,5 +18,5 @@ main :: proc() {
 @export
 @export
 step :: proc(dt: f64) -> bool {
 step :: proc(dt: f64) -> bool {
 	context = ctx
 	context = ctx
-	return ex.step(f32(dt))
+	return ex.step()
 }
 }

+ 30 - 64
examples/minimal/minimal.odin

@@ -1,83 +1,49 @@
+// A small program that draws some shapes, some texts and a texture.
+//
+// There is a web version of this example in `../minimal_web` -- Many other examples work on web out
+// of the box. But the web support requires slight changes to the structure of the program. Here we
+// want to keep things as simple as possible, which is why the `minimal` example has a separate web
+// version.
 package karl2d_minimal_example
 package karl2d_minimal_example
 
 
 import k2 "../.."
 import k2 "../.."
-import "core:mem"
 import "core:log"
 import "core:log"
-import "core:fmt"
 import "core:math"
 import "core:math"
-import "core:time"
-
-_ :: fmt
-_ :: mem
+import "core:fmt"
 
 
-tex: k2.Texture
+main :: proc() {
+	context.logger = log.create_console_logger()
 
 
-init :: proc() {
 	k2.init(1080, 1080, "Karl2D Minimal Program")
 	k2.init(1080, 1080, "Karl2D Minimal Program")
-	tex = k2.load_texture_from_bytes(#load("sixten.jpg"))
-}
-
-t: f32
+	tex := k2.load_texture_from_file("sixten.jpg")
 
 
-step :: proc(dt: f32) -> bool {
-	k2.process_events()
-	k2.clear(k2.BLUE)
-
-	t += dt
-
-	pos := math.sin(t*10)*10
-	rot := t*50
-	k2.draw_texture_ex(tex, {0, 0, f32(tex.width), f32(tex.height)}, {pos + 400, 450, 900, 500}, {450, 250}, rot)
+	for !k2.shutdown_wanted() {
+		k2.new_frame()
+		k2.process_events()
+		k2.clear(k2.BLUE)
 
 
-	k2.draw_rect({10, 10, 60, 60}, k2.GREEN)
-	k2.draw_rect({20, 20, 40, 40}, k2.BLACK)
-	k2.draw_circle({120, 40}, 30, k2.BLACK)
-	k2.draw_circle({120, 40}, 20, k2.GREEN)
-	k2.draw_text("Hellöpe!", {10, 100}, 64, k2.WHITE)
+		t := k2.get_time()
 
 
-	if k2.key_went_down(.R) {
-		k2.set_window_flags({.Resizable})
-	}
+		pos_x := f32(math.sin(t*10)*10)
+		rot := f32(t*50)
+		k2.draw_texture_ex(tex, {0, 0, f32(tex.width), f32(tex.height)}, {pos_x + 400, 450, 900, 500}, {450, 250}, rot)
 
 
-	if k2.key_went_down(.N) {
-		k2.set_window_flags({})
-	}
-	
-
-	k2.present()
-	free_all(context.temp_allocator)
+		k2.draw_rect({10, 10, 60, 60}, k2.GREEN)
+		k2.draw_rect({20, 20, 40, 40}, k2.BLACK)
+		k2.draw_circle({120, 40}, 30, k2.BLACK)
+		k2.draw_circle({120, 40}, 20, k2.GREEN)
+		
+		k2.draw_text("Hellöpe!", {10, 100}, 48, k2.WHITE)
 
 
-	res := true
+		msg1 := fmt.tprintf("Time since start: %.3f s", t)
+		msg2 := fmt.tprintf("Last frame time: %.5f s", k2.get_frame_time())
+		k2.draw_text(msg1, {10, 148}, 48, k2.WHITE)
+		k2.draw_text(msg2, {10, 196}, 48, k2.WHITE)
 
 
-	if k2.key_went_down(.Escape) {
-		res = false
+		k2.present()
+		free_all(context.temp_allocator)
 	}
 	}
 
 
-	return res
-}
-
-shutdown :: proc() {
 	k2.destroy_texture(tex)
 	k2.destroy_texture(tex)
 	k2.shutdown()
 	k2.shutdown()
 }
 }
-
-main :: proc() {
-	context.logger = log.create_console_logger()
-
-	init()
-
-	prev_time := time.now()
-
-	for !k2.shutdown_wanted() {
-		now := time.now()
-		since := time.diff(prev_time, now)
-		dt := f32(time.duration_seconds(since))
-		prev_time = now
-		
-		if !step(dt) {
-			break
-		}
-	}
-
-	shutdown()
-}

+ 74 - 0
examples/minimal_web/minimal_web.odin

@@ -0,0 +1,74 @@
+// A small program that draws some shapes, some texts and a texture.
+//
+// This is the same as `../minimal`, but adapted to work on web. Compile the web version by being in
+// the examples folder (the one above this one) and run:
+//
+//    odin run build_web_example -- minimal_web
+//
+// The built web application will be in minimal_web/web/build
+package karl2d_minimal_example_web
+
+import k2 "../.."
+import "core:log"
+import "core:fmt"
+import "core:math"
+
+_ :: fmt
+
+tex: k2.Texture
+
+init :: proc() {
+	k2.init(1080, 1080, "Karl2D Minimal Program")
+
+	// Note that we #load the texture: This bakes it into the program's data. WASM has no filesystem
+	// so in order to bundle textures with your game, you need to store them somewhere it can fetch
+	// them.
+	tex = k2.load_texture_from_bytes(#load("../minimal/sixten.jpg"))
+}
+
+step :: proc() -> bool {
+	k2.new_frame()
+	k2.process_events()
+	k2.clear(k2.BLUE)
+
+	t := k2.get_time()
+
+	pos_x := f32(math.sin(t*10)*10)
+	rot := f32(t*50)
+	k2.draw_texture_ex(tex, {0, 0, f32(tex.width), f32(tex.height)}, {pos_x + 400, 450, 900, 500}, {450, 250}, rot)
+
+	k2.draw_rect({10, 10, 60, 60}, k2.GREEN)
+	k2.draw_rect({20, 20, 40, 40}, k2.BLACK)
+	k2.draw_circle({120, 40}, 30, k2.BLACK)
+	k2.draw_circle({120, 40}, 20, k2.GREEN)
+	
+	k2.draw_text("Hellöpe!", {10, 100}, 48, k2.WHITE)
+
+	msg1 := fmt.tprintf("Time since start: %.3f s", t)
+	msg2 := fmt.tprintf("Last frame time: %.5f s", k2.get_frame_time())
+	k2.draw_text(msg1, {10, 148}, 48, k2.WHITE)
+	k2.draw_text(msg2, {10, 196}, 48, k2.WHITE)
+
+	k2.present()
+	free_all(context.temp_allocator)
+
+	return !k2.shutdown_wanted()
+}
+
+shutdown :: proc() {
+	k2.destroy_texture(tex)
+	k2.shutdown()
+}
+
+// This is not run by the web version, but it makes this program also work on non-web!
+main :: proc() {
+	context.logger = log.create_console_logger()
+	init()
+
+	run := true
+	for run {
+		run = step() 
+	}
+
+	shutdown()
+}

+ 16 - 4
karl2d.doc.odin

@@ -28,17 +28,16 @@ shutdown :: proc()
 // cleared instead.
 // cleared instead.
 clear :: proc(color: Color)
 clear :: proc(color: Color)
 
 
+new_frame :: proc()
+
 // "Flips the backbuffer": Call at end of frame to make everything you've drawn appear on the screen.
 // "Flips the backbuffer": Call at end of frame to make everything you've drawn appear on the screen.
 //
 //
 // When you draw using for example `draw_texture`, then that stuff is drawn to an invisible texture
 // When you draw using for example `draw_texture`, then that stuff is drawn to an invisible texture
 // called a "backbuffer". This makes sure that we don't see half-drawn frames. So when you are happy
 // called a "backbuffer". This makes sure that we don't see half-drawn frames. So when you are happy
 // with a frame and want to show it to the player, use this procedure.
 // with a frame and want to show it to the player, use this procedure.
 //
 //
-// Also, this procedure clears the frame_allocator that Karl2D uses for internal allocations that
-// have the lifetime of a single frame.
-//
 // WebGL note: WebGL does the backbuffer flipping automatically. But you should still call this to
 // WebGL note: WebGL does the backbuffer flipping automatically. But you should still call this to
-// make Karl2D clear its frame allocator.
+// make sure that all rendering has been sent off to the GPU.
 present :: proc()
 present :: proc()
 
 
 // Call at start or end of frame to process all events that have arrived to the window. This
 // Call at start or end of frame to process all events that have arrived to the window. This
@@ -47,6 +46,10 @@ present :: proc()
 // WARNING: Not calling this will make your program impossible to interact with.
 // WARNING: Not calling this will make your program impossible to interact with.
 process_events :: proc()
 process_events :: proc()
 
 
+get_frame_time :: proc() -> f32
+
+get_time :: proc() -> f64
+
 // Gets the width of the drawing area within the window. The returned number is not scaled by any
 // Gets the width of the drawing area within the window. The returned number is not scaled by any
 // monitor DPI scaling. You do that manually using the number returned by `get_window_scale()`.
 // monitor DPI scaling. You do that manually using the number returned by `get_window_scale()`.
 get_screen_width :: proc() -> int
 get_screen_width :: proc() -> int
@@ -546,6 +549,15 @@ State :: struct {
 	vertex_buffer_cpu: []u8,
 	vertex_buffer_cpu: []u8,
 	vertex_buffer_cpu_used: int,
 	vertex_buffer_cpu_used: int,
 	default_shader: Shader,
 	default_shader: Shader,
+
+	// Time when the first call to `new_frame` happened
+	start_time: time.Time,
+	prev_frame_time: time.Time,
+
+	// "dt"
+	frame_time: f32,
+
+	time: f64
 }
 }
 
 
 // Support for up to 255 mouse buttons. Cast an int to type `Mouse_Button` to use things outside the
 // Support for up to 255 mouse buttons. Cast an int to type `Mouse_Button` to use things outside the

+ 40 - 7
karl2d.odin

@@ -11,6 +11,7 @@ import "core:slice"
 import "core:strings"
 import "core:strings"
 import "core:reflect"
 import "core:reflect"
 import "core:os"
 import "core:os"
+import "core:time"
 
 
 import fs "vendor:fontstash"
 import fs "vendor:fontstash"
 
 
@@ -153,21 +154,36 @@ clear :: proc(color: Color) {
 	s.depth = s.depth_start
 	s.depth = s.depth_start
 }
 }
 
 
+new_frame :: proc() {
+	free_all(s.frame_allocator)
+
+	now := time.now()
+
+	if s.prev_frame_time != {} {
+		since := time.diff(s.prev_frame_time, now)
+		s.frame_time = f32(time.duration_seconds(since))
+	}
+
+	s.prev_frame_time = now
+
+	if s.start_time == {} {
+		s.start_time = time.now()
+	}
+
+	s.time = time.duration_seconds(time.since(s.start_time))
+}
+
 // "Flips the backbuffer": Call at end of frame to make everything you've drawn appear on the screen.
 // "Flips the backbuffer": Call at end of frame to make everything you've drawn appear on the screen.
 //
 //
 // When you draw using for example `draw_texture`, then that stuff is drawn to an invisible texture
 // When you draw using for example `draw_texture`, then that stuff is drawn to an invisible texture
 // called a "backbuffer". This makes sure that we don't see half-drawn frames. So when you are happy
 // called a "backbuffer". This makes sure that we don't see half-drawn frames. So when you are happy
 // with a frame and want to show it to the player, use this procedure.
 // with a frame and want to show it to the player, use this procedure.
 //
 //
-// Also, this procedure clears the frame_allocator that Karl2D uses for internal allocations that
-// have the lifetime of a single frame.
-//
 // WebGL note: WebGL does the backbuffer flipping automatically. But you should still call this to
 // WebGL note: WebGL does the backbuffer flipping automatically. But you should still call this to
-// make Karl2D clear its frame allocator.
+// make sure that all rendering has been sent off to the GPU.
 present :: proc() {
 present :: proc() {
 	draw_current_batch()
 	draw_current_batch()
 	rb.present()
 	rb.present()
-	free_all(s.frame_allocator)
 }
 }
 
 
 // Call at start or end of frame to process all events that have arrived to the window. This
 // Call at start or end of frame to process all events that have arrived to the window. This
@@ -238,6 +254,14 @@ process_events :: proc() {
 	win.clear_events()
 	win.clear_events()
 }
 }
 
 
+get_frame_time :: proc() -> f32 {
+	return s.frame_time
+}
+
+get_time :: proc() -> f64 {
+	return s.time
+}
+
 // Gets the width of the drawing area within the window. The returned number is not scaled by any
 // Gets the width of the drawing area within the window. The returned number is not scaled by any
 // monitor DPI scaling. You do that manually using the number returned by `get_window_scale()`.
 // monitor DPI scaling. You do that manually using the number returned by `get_window_scale()`.
 get_screen_width :: proc() -> int {
 get_screen_width :: proc() -> int {
@@ -300,12 +324,12 @@ set_window_flags :: proc(flags: Window_Flags) {
 // so the maximum number of vertices that can be drawn in each batch is
 // so the maximum number of vertices that can be drawn in each batch is
 // VERTEX_BUFFER_MAX / shader.vertex_size
 // VERTEX_BUFFER_MAX / shader.vertex_size
 draw_current_batch :: proc() {
 draw_current_batch :: proc() {
-	_update_font(s.batch_font)
-
 	if s.vertex_buffer_cpu_used == 0 {
 	if s.vertex_buffer_cpu_used == 0 {
 		return
 		return
 	}
 	}
 
 
+	_update_font(s.batch_font)
+
 	shader := s.batch_shader
 	shader := s.batch_shader
 
 
 	mvp := s.proj_matrix * s.view_matrix
 	mvp := s.proj_matrix * s.view_matrix
@@ -1565,6 +1589,15 @@ State :: struct {
 	vertex_buffer_cpu: []u8,
 	vertex_buffer_cpu: []u8,
 	vertex_buffer_cpu_used: int,
 	vertex_buffer_cpu_used: int,
 	default_shader: Shader,
 	default_shader: Shader,
+
+	// Time when the first call to `new_frame` happened
+	start_time: time.Time,
+	prev_frame_time: time.Time,
+
+	// "dt"
+	frame_time: f32,
+
+	time: f64,
 }
 }
 
 
 // Support for up to 255 mouse buttons. Cast an int to type `Mouse_Button` to use things outside the
 // Support for up to 255 mouse buttons. Cast an int to type `Mouse_Button` to use things outside the