ludic/docs/language/gl/_section.md

3.6 KiB
Raw Permalink Blame History

id title order
gl Gl 60

OpenGL for Ludic. Gl. binds the whole OpenGL 4.1 core API: every gl entry point of the platform gl3.h is a method named by its snake case (glBindBuffer → Gl.bind_buffer, glTexImage2D → Gl.tex_image2d, glDrawElementsInstanced → Gl.draw_elements_instanced), with every GL_* constant available as written. Parameters keep the header's names as labels (glVertexAttribPointer's pointer is called offset, program is prog); GLfloat/GLdouble parameters take fixed, pointer parameters take the raw bytes/words buffers Ludic already has, and 64-bit sizes take long (an int widens). The binding is generated by ludic-dev glgen from the header, with one ABI thunk per entry point.

A context comes from Gl.open(width, height, title): windowed, an NSOpenGLContext on the game's window at the display's backing resolution (2× on Retina — Gl.width()/Gl.height() are the drawable's pixels); headless, an offscreen context with a framebuffer standing in for the screen (Gl.screen_fbo()), so the same program renders and screenshots under the test harness. Gl.swap() presents, Gl.screenshot(path) writes the screen as a PPM, Gl.check(tag) prints any pending error.

The glue: Gl.program(vs, fs) / Gl.program5(vs, tcs, tes, gs, fs) compile and link (logs on failure), Gl.uniform(prog, name), Gl.vao(), Gl.buffer(), Gl.texture(), Gl.framebuffer() allocate, and float data is filled from Q16.16 with Gl.floats(n) / Gl.put(buf, i, v) / Gl.bytes_of(n) or from IEEE bits with Gl.put_bits; Gl.f32(v) and Gl.fixed(bits) convert single values. The bare f_add/f_mul/… helpers do IEEE single-precision arithmetic on those bit patterns for programs that want real floats on the CPU.

Using Gl.* links gl.ll, the thunks and OpenGL.framework; a program that does not is byte-identical to before. The ludic.render3d package is a physically based 3D renderer written on this surface (see examples/rendering/smooth.ludic here, and Maroon Lake for a game built on it).

program Triangle {
  property Marker { on: int = 1 }
  model Anchor { Marker }
  state TriangleState {
    prog: int = 0
    vao:  int = 0
  }
  handler Boot(triangle_st: mut TriangleState) phase Start {
    spawn Anchor {}
    if not Gl.open(width: 640, height: 360, title: "GL") { quit() }
    triangle_st.prog = Gl.program(vs: "#version 410 core\nvoid main(){ gl_Position = vec4(float(gl_VertexID == 1) * 2.0 - 0.5, float(gl_VertexID == 2) * 2.0 - 0.5, 0.0, 1.0); }\n",
                      fs: "#version 410 core\nout vec4 o; void main(){ o = vec4(1.0, 0.5, 0.2, 1.0); }\n")
    triangle_st.vao = Gl.vao()
  }
  handler Draw(triangle_st: TriangleState) phase Render {
    Gl.clear_color(red: 0.1, green: 0.1, blue: 0.15, alpha: 1.0)
    Gl.clear(mask: GL_COLOR_BUFFER_BIT)
    Gl.use_program(prog: triangle_st.prog)
    Gl.bind_vertex_array(array: triangle_st.vao)
    Gl.draw_arrays(mode: GL_TRIANGLES, first: 0, count: 3)
    Gl.swap()
  }
}