-*.sublime-workspace
\ No newline at end of file
+*.sublime-workspace
+*.wasm
--- /dev/null
+#!/bin/sh
+
+onyx -r js -V --use-post-mvp-features test/basic.onyx -o imgui.wasm
\ No newline at end of file
--- /dev/null
+// This is a simple buffered system to receive events from the webbrowser
+// in a buffer that you can poll and consume all of the events. It is not
+// direclty used by the immediate mode graphics, but it makes standing up
+// a simple application much easier.
+
+package imgui.events
+
+#private_file Num_Buffered_Events :: 16
+
+// NOTE: These need to match exactly what is in the corresponding javascript
+DomEventKind :: enum {
+ None :: 0x00;
+
+ MouseDown :: 0x01;
+ MouseUp :: 0x02;
+ MouseMove :: 0x03;
+ MouseWheel :: 0x07;
+
+ KeyDown :: 0x04;
+ KeyUp :: 0x05;
+
+ Resize :: 0x06;
+}
+
+DomEvent :: struct {
+ kind : DomEventKind;
+ timestamp : u32;
+}
+
+KeyboardEvent :: struct {
+ use event : DomEvent;
+
+ keycode : u32;
+}
+
+MouseButton :: enum {
+ Left :: 0x00;
+ Middle :: 0x01;
+ Right :: 0x02;
+
+ WheelUp :: 0x03;
+ WheelDown :: 0x04;
+}
+
+MouseEvent :: struct {
+ use event : DomEvent;
+
+ pos_x : u32;
+ pos_y : u32;
+ button : MouseButton;
+}
+
+ResizeEvent :: struct {
+ use event : DomEvent;
+
+ width : u32;
+ height : u32;
+}
+
+Event :: struct #union {
+ use dom : DomEvent;
+
+ keyboard : KeyboardEvent;
+ mouse : MouseEvent;
+ resize : ResizeEvent;
+}
+
+clear_event :: (ev: ^Event) {
+ ev.kind = DomEventKind.None;
+ ev.timestamp = 0;
+}
+
+init :: () {
+ event_storage.event_count = 0;
+ event_storage.max_events = Num_Buffered_Events;
+
+ for ^ev: event_storage.event_buffer do clear_event(ev);
+
+ event_setup(^event_storage, sizeof Event);
+}
+
+poll :: (ev: ^Event) -> bool {
+ if event_storage.event_count == 0 do return false;
+
+ *ev = event_storage.event_buffer[0];
+ for i: 0 .. Num_Buffered_Events - 2 {
+ event_storage.event_buffer[i] = event_storage.event_buffer[i + 1];
+ }
+
+ event_storage.event_count -= 1;
+
+ return true;
+}
+
+/* Private members */
+
+#private_file EventStorage :: struct {
+ event_count : u32;
+ max_events : u32;
+ event_buffer : [Num_Buffered_Events] Event;
+}
+
+#private_file event_storage : EventStorage;
+#private_file event_setup :: (event_storage: ^EventStorage, event_size: u32) -> void #foreign "event" "setup" ---
--- /dev/null
+package imgui.gl
+
+use package core
+use package gl as gl
+
+// This Shader represents an OpenGL program, not a shader. The name
+// is confusing but conceptually for most people, shaders are a bundled
+// version of the vertex and fragment shader.
+Shader :: struct {
+ program : gl.GLProgram;
+
+ position_loc : gl.GLint;
+ color_loc : gl.GLint;
+ texture_loc : gl.GLint;
+
+ view_matrix_loc : gl.GLUniformLocation;
+ world_matrix_loc : gl.GLUniformLocation;
+
+ make_from_source :: (vertex_source: str, fragment_source: str) -> Shader {
+ shader: Shader;
+ init_from_source(^shader, vertex_source, fragment_source);
+ return shader;
+ }
+
+ init_from_source :: (use shader: ^Shader, vertex_source: str, fragment_source: str) {
+ // @Robustness: Errors in compiling the shaders are ignored right now.
+ vertex_shader, _ := compile_shader(vertex_source, gl.VERTEX_SHADER);
+ fragment_shader, _ := compile_shader(fragment_source, gl.FRAGMENT_SHADER);
+
+ // @Robustness: Errors in linkning the program are ignored right now.
+ shader_program, _ := link_program(vertex_shader, fragment_shader);
+ program = shader_program;
+
+ position_loc = gl.getAttribLocation(program, "a_position");
+ color_loc = gl.getAttribLocation(program, "a_color");
+ texture_loc = gl.getAttribLocation(program, "a_texture");
+
+ view_matrix_loc = gl.getUniformLocation(program, "u_view");
+ world_matrix_loc = gl.getUniformLocation(program, "u_world");
+
+ compile_shader :: (source: str, shader_type: gl.GLenum) -> (gl.GLShader, bool) {
+ shader := gl.createShader(shader_type);
+ gl.shaderSource(shader, source);
+ gl.compileShader(shader);
+
+ success := true;
+ if gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0 {
+ printf("Error compiling shader.");
+ gl.printShaderInfoLog(shader);
+ success = false;
+ }
+
+ return shader, success;
+ }
+
+ link_program :: (vertex_shader: gl.GLShader, fragment_shader: gl.GLShader) -> (gl.GLProgram, bool) {
+ program := gl.createProgram();
+ gl.attachShader(program, vertex_shader);
+ gl.attachShader(program, fragment_shader);
+ gl.linkProgram(program);
+
+ success := true;
+ if gl.getProgramParameter(program, gl.LINK_STATUS) == 0 {
+ printf("Error linking program.");
+ gl.printProgramInfoLog(program);
+ success = false;
+ }
+
+ return program, success;
+ }
+ }
+}
+
package imgui
-
+#load "lib/immediate_renderer"
--- /dev/null
+package imgui
+
+#load "core/js/webgl"
+#load "lib/gl/gl_utils"
+
+use package core
+use package imgui.gl
+use package gl as gl
+
+Vector2 :: struct {
+ x, y: f32;
+}
+
+Color4 :: struct {
+ r, g, b, a: f32;
+}
+
+Immediate_Vertex :: struct {
+ position : Vector2;
+ color : Color4;
+ texture : Vector2;
+
+ // @Feature: At some point, it would be nice to bind multiple textures
+ // and then use a texture_id field to choose which texture to use for this
+ // vertex. Granted, this should probably be per triangle, not per vertex. But
+ // for now, I will just use a single texture.
+ // texture_id : i32;
+}
+
+
+Immediate_Renderer :: struct {
+ shader : Shader;
+
+ // 'verticies' contains the vertex data and the maximum number of verticies
+ // that can be rendered at a given time. 'vertex_count' is used to store how
+ // many verticies will be rendered in the next draw call. 'vertex_count' is
+ // expected to be a multiple of 3, given that triangles are being rendered.
+ verticies : [] Immediate_Vertex;
+ vertex_count : u32;
+
+ clear_color : Color4;
+
+ // Needs to be a multiple of 3!!
+ Default_Max_Verticies :: 1023;
+
+ make :: (max_verticies := Default_Max_Verticies) -> Immediate_Renderer {
+ ir : Immediate_Renderer;
+ init(^ir, max_verticies);
+
+ return ir;
+ }
+
+ init :: (use ir: ^Immediate_Renderer, max_verticies := Default_Max_Verticies) {
+ vertex_source := #file_contents "lib/shaders/immediate_vertex.glsl";
+ fragment_source := #file_contents "lib/shaders/immediate_fragment.glsl";
+ shader = Shader.make_from_source(vertex_source, fragment_source);
+
+ verticies = memory.make_slice(Immediate_Vertex, max_verticies);
+ }
+
+ push_vertex :: proc {
+ // If a color is not provided, the previous color is used.
+ (use ir: ^Immediate_Renderer, position: Vector2) {
+ push_vertex(ir, position, color = verticies[vertex_count - 1].color);
+ },
+
+ (use ir: ^Immediate_Renderer, position: Vector2, color: Color4) {
+ vertex_ptr := ^verticies[vertex_count];
+ defer vertex_count += 1;
+
+ vertex_ptr.position = position;
+ vertex_ptr.color = color;
+ },
+ }
+
+ flush :: (use ir: ^Immediate_Renderer) {
+ gl.useProgram(shader.program);
+ gl.drawArrays(gl.TRIANGLES, 0, vertex_count);
+
+ vertex_count = 0;
+ }
+}
+
+
+
+#private
+immediate_renderer : Immediate_Renderer;
+
+immediate_renderer_init :: () {
+ immediate_renderer = Immediate_Renderer.make();
+}
+
+immediate_vertex :: proc {
+ (position: Vector2) { immediate_renderer->push_vertex(position); },
+ (position: Vector2, color: Color4) { immediate_renderer->push_vertex(position, color); },
+}
+
+immediate_flush :: () do immediate_renderer->flush();
--- /dev/null
+#version 300 es
+
+precision mediump float;
+
+uniform sampler2D u_texture;
+
+in vec4 v_color;
+in vec2 v_texture;
+
+out vec4 fragColor;
+
+void main() {
+ fragColor = v_color + vec4(v_texture, 0, 0) * 0;
+}
\ No newline at end of file
--- /dev/null
+#version 300 es
+
+layout(location = 0) in vec2 a_vertex;
+layout(location = 1) in vec4 a_color;
+layout(location = 2) in vec2 a_texture;
+
+uniform mat4 u_view;
+uniform mat4 u_world;
+
+out vec4 v_color;
+out vec4 v_texture;
+
+void main() {
+ gl_Position = u_view * u_world * vec4(a_vertex, 0, 1);
+
+ v_color = a_color;
+ v_texture = v_texture;
+}
\ No newline at end of file
[
{
"path": "."
+ },
+ {
+ "path": "C:\\dev\\onyx\\core"
}
]
}
+// Things still needed for an initial version:
+// - HTML page that load the correct files. (ugh...)
+// - Eventing system to talk to JS. This will not be directly used by the immediate
+// mode renderer, because that defeats the whole point of the "immediate mode".
+// However, it is some thing that should be provided, given that every application
+// will need it or something very similar.
+// - Event loop in this file.
+
+
#load "core/std"
#load "lib/imgui"
+#load "lib/events"
use package core
use package imgui as imgui
+use package imgui.events as events
+
+poll_events :: () {
+ use events.DomEventKind;
+
+ event: events.Event;
+ while events.poll(^event) do switch event.kind {
+ case MouseDown {
+ println("The mouse was pressed!");
+ printf("Specifically, the %i button.\n", event.mouse.button);
+ }
+ }
+}
+update :: () {
+}
+draw :: () {
+}
+
+loop :: () {
+ poll_events();
+
+ //update();
+ //draw();
+}
main :: (args: [] cstr) {
println("Hey! We got here!");
-}
\ No newline at end of file
+
+ events.init();
+
+ imgui.immediate_renderer_init();
+}