bugfix and added to immediate rendering capability
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 6 Jul 2021 03:26:44 +0000 (22:26 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 6 Jul 2021 03:26:44 +0000 (22:26 -0500)
bin/onyx
docs/plan
docs/todo
modules/immediate_mode/canvas.onyx [new file with mode: 0644]
modules/immediate_mode/immediate_renderer.onyx
modules/immediate_mode/module.onyx
modules/immediate_mode/texture.onyx [new file with mode: 0644]
modules/ui/ui.onyx
modules/webgl2/webgl2.js
modules/webgl2/webgl2.onyx
src/onyxutils.c

index efef128ab1ec0ceca6109f640c765b04e2ded42f..4248b85a435d768c33cdd750450cbac28a68f8c7 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index 081487fd32806411cbfac92f35e769f82240c192..116ef9e70ee8df788c3256cc406c94210fcf28b4 100644 (file)
--- a/docs/plan
+++ b/docs/plan
@@ -34,7 +34,7 @@ HOW:
         I have a working compiler with many features, but here are some additional features
         I would like to consider adding in the future.
 
-        [ ] Put type info in data section so it is runtime accessible
+        [X] Put type info in data section so it is runtime accessible
             - type name
             - size
             - alignment
index cdbeaf3b03567f1a0c22f3f130145f49f52d5939..f7eab465ebb8b650f7a152fa329ff488d2aaf7a8 100644 (file)
--- a/docs/todo
+++ b/docs/todo
@@ -11,7 +11,7 @@ Command Line Interface:
 
     [X] Remove old code from CLI logic
     [X] Add statistic printing
-    [ ] Fix documentation generation (broken since compiler architecture change) 
+    [X] Fix documentation generation (broken since compiler architecture change) 
     [ ] Add automated running component
         - Store to temporary file (OS independent)
         - Detect / choose WASM backend
diff --git a/modules/immediate_mode/canvas.onyx b/modules/immediate_mode/canvas.onyx
new file mode 100644 (file)
index 0000000..6e9d88a
--- /dev/null
@@ -0,0 +1,39 @@
+package immediate_mode
+
+use package core
+
+// A render target that can be used instead of the default
+Canvas :: struct {
+    color_texture        : Texture;
+    depth_stencil_buffer : gl.GLRenderbuffer;
+    framebuffer          : gl.GLFramebuffer;
+
+    width, height: i32;
+}
+
+create_canvas :: (width: i32, height: i32) -> Canvas {
+
+    color_texture := create_texture(width, height, gl.RGBA8);
+
+    depth_stencil_buffer := gl.createRenderbuffer();
+    gl.bindRenderbuffer(gl.RENDERBUFFER, depth_stencil_buffer);
+    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, width, height);
+    gl.bindRenderbuffer(gl.RENDERBUFFER, -1);
+
+    framebuffer := gl.createFramebuffer();
+    gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color_texture.texture, 0);
+    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depth_stencil_buffer);
+    gl.bindFramebuffer(gl.FRAMEBUFFER, -1);
+
+    assert(gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE, "Framebuffer not complete!");
+
+    return .{
+        color_texture = color_texture,
+        depth_stencil_buffer = depth_stencil_buffer,
+        framebuffer = framebuffer,
+        width = width,
+        height = height,
+    };
+}
+
index 302f6482d1d40db62030588c1615ebc1cc7424cb..1f750c503855683660e12a1d0165f4d28697c775 100644 (file)
@@ -1,7 +1,7 @@
 package immediate_mode
 
 use package core
-#private_file gl :: package gl
+use package core.intrinsics.onyx { __initialize }
 
 Vector2 :: struct {
     x, y: f32;
@@ -12,6 +12,11 @@ Color4 :: struct {
     a: f32 = 1;
 }
 
+Immediate_Mode :: enum {
+    Triangles;
+    Lines;
+}
+
 Immediate_Vertex :: struct {
     position   : Vector2;
     color      : Color4;
@@ -19,7 +24,6 @@ Immediate_Vertex :: struct {
 }
 
 Immediate_Renderer :: struct {
-    // Will point to either the simple_shader or the textured_shader.
     active_shader : ^Shader;
 
     simple_shader, textured_shader : Shader;
@@ -28,7 +32,8 @@ Immediate_Renderer :: struct {
     // '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.
+    // expected to be a multiple of 3 or 2, given that triangles or lines are
+    // being rendered.
     verticies : [] Immediate_Vertex;
     vertex_count : u32;
 
@@ -37,11 +42,18 @@ Immediate_Renderer :: struct {
     vertex_array  : gl.GLVertexArrayObject;
     vertex_buffer : gl.GLBuffer;
 
-    // Needs to be a multiple of 3!!
-    Default_Max_Verticies :: 1023;
+    // Needs to be a multiple of 6!!
+    Default_Max_Verticies :: 1020;
+
+    mode := Immediate_Mode.Triangles;
+
+    canvas: ^Canvas = null;
+
+    window_width, window_height: i32;
 
     make :: (max_verticies := Default_Max_Verticies) -> Immediate_Renderer {
         ir : Immediate_Renderer;
+        __initialize(^ir);
         init(^ir, max_verticies);
 
         return ir;
@@ -120,9 +132,15 @@ Immediate_Renderer :: struct {
         gl.bufferSubData(gl.ARRAY_BUFFER, 0, .{ count = vertex_count * sizeof Immediate_Vertex, data = ~~verticies.data });
         gl.bindBuffer(gl.ARRAY_BUFFER, -1);
 
+        gl_mode : gl.GLenum;
+        switch mode {
+            case .Triangles do gl_mode = gl.TRIANGLES;
+            case .Lines     do gl_mode = gl.LINES;
+        }
+
         gl.useProgram(active_shader.program);
         gl.bindVertexArray(vertex_array);
-        gl.drawArrays(gl.TRIANGLES, 0, vertex_count);
+        gl.drawArrays(gl_mode, 0, vertex_count);
         gl.bindVertexArray(-1);
 
         vertex_count = 0;
@@ -267,6 +285,45 @@ Immediate_Renderer :: struct {
             gl.disable(gl.SCISSOR_TEST);
         }
     }
+
+    set_mode :: (use ir: ^Immediate_Renderer, new_mode: Immediate_Mode) {
+        if mode != new_mode {
+            ir->flush();
+            mode = new_mode;
+        }
+    }
+
+    use_canvas :: (use ir: ^Immediate_Renderer, new_canvas: ^Canvas) {
+        if canvas == new_canvas do return;
+        ir->flush();
+
+        canvas = new_canvas;
+        if canvas != null {
+            gl.bindFramebuffer(gl.FRAMEBUFFER, canvas.framebuffer);
+            gl.viewport(0, 0, canvas.width, canvas.height);
+
+            @Note // This flips the projection because generally with using framebuffers as textures,
+            // the bottom left, (0, 0) in WebGL world, you want to be the top left when you render it.
+            ir->use_ortho_projection(0, ~~canvas.width, ~~canvas.height, 0);
+
+        } else {
+            gl.bindFramebuffer(gl.FRAMEBUFFER, -1);
+            gl.viewport(0, 0, window_width, window_height);
+            ir->use_ortho_projection(0, ~~window_width, 0, ~~window_height);
+        }
+    }
+
+    set_window_size :: (use ir: ^Immediate_Renderer, width: i32, height: i32) {
+        window_width = width;
+        window_height = height;
+
+        // If there is no active canvas, immediately update the values.
+        // Maybe this should flush the current buffer first?
+        if canvas == null {
+            gl.viewport(0, 0, window_width, window_height);
+            ir->use_ortho_projection(0, ~~window_width, 0, ~~window_height);
+        }
+    }
 }
 
 
@@ -280,7 +337,7 @@ Immediate_Renderer :: struct {
 immediate_renderer : Immediate_Renderer;
 
 immediate_renderer_init :: () {
-    Immediate_Renderer.init(^immediate_renderer);
+    immediate_renderer = Immediate_Renderer.make();
 }
 
 immediate_renderer_free :: () {
@@ -306,7 +363,20 @@ circle :: (center: Vector2, radius: f32, color: Color4 = .{1,1,1}, segments := 1
 
 flush :: () do immediate_renderer->flush();
 
-set_texture :: (texture_id: i32 = -1) do immediate_renderer->set_texture(texture_id);
+set_texture :: #match {
+    (texture_id: i32 = -1) { immediate_renderer->set_texture(texture_id); },
+    (texture: ^Texture, texture_index := 0) {
+        immediate_renderer->set_texture(texture_index);
+
+        if texture_index >= 0 {
+            gl.activeTexture(gl.TEXTURE0 + texture_index);
+            gl.bindTexture(gl.TEXTURE_2D, texture.texture);
+
+        } else {
+            gl.bindTexture(gl.TEXTURE_2D, -1);
+        }
+    }
+}
 
 use_ortho_projection :: (left: f32, right: f32, top: f32, bottom: f32) {
     immediate_renderer->use_ortho_projection(left, right, top, bottom);
@@ -322,4 +392,16 @@ scissor :: (x: f32, y: f32, w: f32, h: f32) {
 
 scissor_disable :: () {
     immediate_renderer->scissor_disable();
-}
\ No newline at end of file
+}
+
+set_mode :: (mode: Immediate_Mode) {
+    immediate_renderer->set_mode(mode);
+}
+
+use_canvas :: (canvas: ^Canvas) {
+    immediate_renderer->use_canvas(canvas);
+}
+
+set_window_size :: (width: i32, height: i32) {
+    immediate_renderer->set_window_size(width, height);
+}
index fc992e234dee0258cc3aa7ecb57355b8b6e847a6..3c72307f5e79dd14bcb05a2d33c12d0b161c1ce3 100644 (file)
@@ -3,3 +3,7 @@ package immediate_mode
 
 #load "./immediate_renderer"
 #load "./gl_utils"
+#load "./canvas"
+#load "./texture"
+
+#private gl :: package gl
diff --git a/modules/immediate_mode/texture.onyx b/modules/immediate_mode/texture.onyx
new file mode 100644 (file)
index 0000000..9982e13
--- /dev/null
@@ -0,0 +1,49 @@
+package immediate_mode
+
+use package core
+
+Texture :: struct {
+    texture : gl.GLTexture;
+
+    width, height: i32;
+}
+
+create_texture :: (width: i32, height: i32, format: gl.GLenum) -> Texture {
+    texture := gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D, texture);
+    gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height);
+
+    // Setting some reasonable defaults for the texture parameters
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+    gl.bindTexture(gl.TEXTURE_2D, -1);
+
+    return .{
+        texture = texture,
+        width = width,
+        height = height,
+    };
+}
+
+load_texture :: (width: i32, height: i32, data: [] u8, format: gl.GLenum, internal_type := gl.UNSIGNED_BYTE) -> Texture {
+    texture := gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D, texture);
+    gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, internal_type, data);
+
+    // Setting some reasonable defaults for the texture parameters
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+    gl.bindTexture(gl.TEXTURE_2D, -1);
+
+    return .{
+        texture = texture,
+        width = width,
+        height = height,
+    };
+}
index 39f67518e802add718fcdde0976706575600fcfd..aef9b0fe762aea5d08fe5c0e3cb32a24e37dc717 100644 (file)
@@ -5,7 +5,7 @@ use package core
 @Cleanup // Move these to the theme?
 // Or create a cache of fonts and put pointers/string in the themes?
 #private font : bmfont.BMFont;
-#private font_texture : gl.GLTexture;
+#private font_texture : gfx.Texture;
 
 @Temporary
 DEFAULT_TEXT_SIZE :: 1.0f
@@ -161,8 +161,7 @@ is_hot_item :: (id: UI_Id) -> bool {
 
 @FontSizing // Currently, `size` is just a multipler for the baked font size. This should be changed to be height in pixels, or 'em's.
 draw_text_raw :: (text: str, x: f32, y: f32, size := DEFAULT_TEXT_SIZE, color := gfx.Color4.{1,1,1}) {
-    gl.activeTexture(gl.TEXTURE0);
-    gl.bindTexture(gl.TEXTURE_2D, font_texture);
+    gfx.set_texture(^font_texture);
     gfx.use_alpha_shader(0);
 
     for glyph: bmfont.get_character_positions(^font, size, text, x, y) {
@@ -173,7 +172,7 @@ draw_text_raw :: (text: str, x: f32, y: f32, size := DEFAULT_TEXT_SIZE, color :=
     }
     
     gfx.flush();
-    gl.bindTexture(gl.TEXTURE_2D, -1);
+    gfx.set_texture();
 }
 
 draw_rect :: #match {
@@ -286,14 +285,7 @@ get_text_width :: (text: str, size := DEFAULT_TEXT_SIZE) -> f32 {
 
     tex_width, tex_height := font.common.scale_width, font.common.scale_height;
 
-    font_texture = gl.createTexture();
-    gl.bindTexture(gl.TEXTURE_2D, font_texture);
-    gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, tex_width, tex_height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, texture_data);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-    gl.bindTexture(gl.TEXTURE_2D, -1);
+    font_texture = gfx.load_texture(tex_width, tex_height, texture_data, gl.ALPHA);
 }
 
 
index bd5339936379059d152f471dea94bb1e7f0dce32..df8936d2cddac644a318831807574d45c2bbe742 100644 (file)
@@ -38,10 +38,34 @@ window.ONYX_MODULES.push({
             gl.bindBuffer(target, buffers[buffer]);
         }
     },
-    bindFramebuffer(target, framebuffer) { gl.bindFramebuffer(target, framebuffers[framebuffer]); },
-    bindRenderbuffer(target, renderbuffer) { gl.bindRenderbuffer(target, renderbuffers[renderbuffer]); },
-    bindTexture(target, texture) { gl.bindTexture(target, textures[texture]); },
-    bindVertexArray(vertexArray) { gl.bindVertexArray(vertexArrays[vertexArray]); },
+    bindFramebuffer(target, framebuffer) {
+        if (framebuffer == -1) {
+            gl.bindFramebuffer(target, null);
+        } else {
+            gl.bindFramebuffer(target, framebuffers[framebuffer]);
+        }
+    },
+    bindRenderbuffer(target, renderbuffer) {
+        if (renderbuffer == -1) {
+            gl.bindRenderbuffer(target, null);
+        } else {
+            gl.bindRenderbuffer(target, renderbuffers[renderbuffer]);
+        }
+    },
+    bindTexture(target, texture) {
+        if (texture == -1) {
+            gl.bindTexture(target, null);
+        } else {
+            gl.bindTexture(target, textures[texture]);
+        }
+    },
+    bindVertexArray(vertexArray) {
+        if (vertexArray == -1) {
+            gl.bindVertexArray(null);
+        } else {
+            gl.bindVertexArray(vertexArrays[vertexArray]); 
+        }
+    },
 
     blendColor(red, green, blue, alpha) { gl.blendColor(red, green, blue, alpha); },
     blendEquation(mode) { gl.blendEquation(mode); },
@@ -67,7 +91,7 @@ window.ONYX_MODULES.push({
         canvas.width = width;
         canvas.height = height;
     },
-    checkFrameBufferStatus(target) { return gl.checkFrameBufferStatus(target); },
+    checkFramebufferStatus(target) { return gl.checkFramebufferStatus(target); },
     clear(bit) { gl.clear(bit); },
     clearColor(r, g, b, a) { gl.clearColor(r, g, b, a); },
     clearDepth(depth) { gl.clearDepth(depth); },
@@ -83,8 +107,8 @@ window.ONYX_MODULES.push({
         gl.compressedSubTexImage2D(target, level, internalformat, xoff, yoff, width, height, format, pixels);
     },
     copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size) { gl.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); },
-    copyTexImage2D(target, level, internalforamt, x, y, width, height, border) {
-        gl.copyTexImage2D(target, level, internalforamt, x, y, width, height, border);
+    copyTexImage2D(target, level, internalformat, x, y, width, height, border) {
+        gl.copyTexImage2D(target, level, internalformat, x, y, width, height, border);
     },
     copyTexSubImage2D(target, level, xoff, yoff, x, y, width, height) {
         gl.copyTexSubImage2D(target, level, xoff, yoff, x, y, width, height);
@@ -229,8 +253,9 @@ window.ONYX_MODULES.push({
         gl.readPixels(x, y, width, height, format, type, pixeldata);
     },
     readBuffer(src) { gl.readBuffer(src); },
-    renderbufferStorageMultisample(target, samples, internalforamt, width, height) {
-        gl.renderbufferStorageMultisample(target, samples, internalforamt, width, height);
+    renderbufferStorage(target, internalformat, width, height) { gl.renderbufferStorage(target, internalformat, width, height); },
+    renderbufferStorageMultisample(target, samples, internalformat, width, height) {
+        gl.renderbufferStorageMultisample(target, samples, internalformat, width, height);
     },
     sampleCoverage(value, invert) { gl.sampleCoverage(value, invert); },
     scissor(x, y, width, height) { gl.scissor(x, y, width, height); },
@@ -248,12 +273,13 @@ window.ONYX_MODULES.push({
     stencilMaskSeparate(face, mask) { gl.stencilMaskSeparate(face, mask); },
     stencilOp(fail, zfail, mask) { gl.stencilOp(fail, zfail, mask); },
     stencilOpSeparate(face, fail, zfail, zpass) { gl.stencilOpSeparate(face, fail, zfail, zpass); },
-    texImage2D(target, level, internalforamt, width, height, border, format, type, pixels, pixelslen) {
+    texImage2D(target, level, internalformat, width, height, border, format, type, pixels, pixelslen) {
         const data = new Uint8Array(window.ONYX_MEMORY.buffer, pixels, pixelslen);
-        gl.texImage2D(target, level, internalforamt, width, height, border, format, type, data);
+        gl.texImage2D(target, level, internalformat, width, height, border, format, type, data);
     },
     texParameterf(target, pname, param) { gl.texParameterf(target, pname, param); },
     texParameteri(target, pname, param) { gl.texParameteri(target, pname, param); },
+    texStorage2D(target, levels, internalformat, width, height) { gl.texStorage2D(target, levels, internalformat, width, height); },
     texSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels, pixelslen) {
         const data = new Uint8Array(window.ONYX_MEMORY.buffer, pixels, pixelslen);
         gl.texSubImage2D(target, level, xoff, yoff, width, height, format, type, data);
index 0b20cfb2b566b30bca47d5fb3c51c363140150a0..24141a39b7a33c09c45754be05ebf5563d2b3ed0 100644 (file)
@@ -747,7 +747,7 @@ bufferDataNoData               :: (target: GLenum, size: GLsizei, usage: GLenum)
 bufferData                     :: #match { bufferDataWithData, bufferDataNoData }
 bufferSubData                  :: (target: GLenum, offset: GLsizei, data: [] void) -> void #foreign "gl" "bufferSubData" ---
 canvasSize                     :: (width: GLsizei, height: GLsizei) -> void #foreign "gl" "canvasSize" ---
-checkFrameBufferStatus         :: (target: GLenum) -> GLenum #foreign "gl" "checkFrameBufferStatus" ---
+checkFramebufferStatus         :: (target: GLenum) -> GLenum #foreign "gl" "checkFramebufferStatus" ---
 clear                          :: (mask: GLbitfield) -> void #foreign "gl" "clear" ---
 clearColor                     :: (red: GLclampf, green: GLclampf, blue: GLclampf, alpha: GLclampf) -> void #foreign "gl" "clearColor" ---
 clearDepth                     :: (depth: GLclampf) -> void #foreign "gl" "clearDepth" ---
@@ -819,7 +819,8 @@ printProgramInfoLog            :: (program: GLProgram) -> void #foreign "gl" "pr
 printShaderInfoLog             :: (shader: GLShader) -> void #foreign "gl" "printShaderInfoLog" ---
 readBuffer                     :: (src: GLenum) -> void #foreign "gl" "readBuffer" ---
 readPixels                     :: (x: GLint, y: GLint, width: GLsizei, height: GLsizei, format: GLenum, type: GLenum, pixels: str) -> void #foreign "gl" "readPixels" ---
-renderbufferStorageMultisample :: (target: GLenum, samples: GLsizei, internalforamt: GLenum, width: GLsizei, height: GLsizei) -> void #foreign "gl" "renderbufferStorageMultisample" ---
+renderbufferStorage            :: (target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei) -> void #foreign "gl" "renderbufferStorage" ---
+renderbufferStorageMultisample :: (target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) -> void #foreign "gl" "renderbufferStorageMultisample" ---
 sampleCoverage                 :: (value: GLclampf, invert: GLboolean) -> void #foreign "gl" "sampleCoverage" ---
 scissor                        :: (x: GLint, y: GLint, width: GLsizei, height: GLsizei) -> void #foreign "gl" "scissor" ---
 setSize                        :: (width: GLint, y: GLint) -> void #foreign "gl" "setSize" ---
@@ -833,6 +834,7 @@ stencilOpSeparate              :: (face: GLenum, fail: GLenum, zfail: GLenum, zp
 texImage2D                     :: (target: GLenum, level: GLint, internalFormat: GLenum, width: GLsizei, height: GLsizei, border: GLint, format: GLenum, type: GLenum, pixels: str) -> void #foreign "gl" "texImage2D" ---
 texParameterf                  :: (target: GLenum, pname: GLenum, param: GLfloat) -> void #foreign "gl" "texParameterf" ---
 texParameteri                  :: (target: GLenum, pname: GLenum, param: GLint) -> void #foreign "gl" "texParameteri" ---
+texStorage2D                   :: (target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) -> void #foreign "gl" "texStorage2D" ---
 texSubImage2D                  :: (target: GLenum, level: GLint, xoff: GLint, yoff: GLint, width: GLsizei, height: GLsizei, format: GLenum, type: GLenum, pixels: str) -> void #foreign "gl" "texSubImage2D" ---
 uniform1f                      :: (loc: GLUniformLocation, x: GLfloat) -> void #foreign "gl" "uniform1f" ---
 uniform1i                      :: (loc: GLUniformLocation, x: GLint) -> void #foreign "gl" "uniform1i" ---
index 5864662f73998c03b540701a939a11df1b0fdfc5..c4a8ef567d490bd80ea29dad8d29d8189ec4b16e 100644 (file)
@@ -154,23 +154,27 @@ AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn) {
 }
 
 AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol) {
-    // CLEANUP: So many checks for null....
-    if (!node) return NULL;
-
-    if (node->kind == Ast_Kind_Type_Raw_Alias)
-        node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type;
+    b32 used_pointer = 0;
 
-    if (!node) return NULL;
+    while (1) {
+        if (!node) return NULL;
 
-    if (node->kind == Ast_Kind_Type_Alias)
-        node = (AstNode *) ((AstTypeAlias *) node)->to;
+        switch (node->kind) {
+            case Ast_Kind_Type_Raw_Alias: node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; break;
+            case Ast_Kind_Type_Alias:     node = (AstNode *) ((AstTypeAlias *) node)->to; break;
+            case Ast_Kind_Pointer_Type: {
+                if (used_pointer) goto all_types_peeled_off;
+                used_pointer = 1;
 
-    if (!node) return NULL;
+                node = (AstNode *) ((AstPointerType *) node)->elem;
+                break;
+            }
 
-    // A single pointer can be dereferenced to lookup symbols in struct.
-    if (node->kind == Ast_Kind_Pointer_Type)
-        node = (AstNode *) ((AstPointerType *) node)->elem;
+            default: goto all_types_peeled_off;
+        }
+    }
 
+all_types_peeled_off:
     if (!node) return NULL;
 
     switch (node->kind) {