feat(gl): OpenGL 4.1 and the ludic.render3d renderer
`Gl.*` binds the whole OpenGL 4.1 core API — every entry point of the platform gl3.h with every GL_* constant, generated by `ludic-dev glgen` with per-call ABI thunks. Windowed builds get an NSOpenGLContext on the existing window at Retina resolution; headless builds render into an offscreen CGL context, so a program that uses Gl.* renders and screenshots identically under the test harness. It links gl.ll, the thunks and OpenGL.framework only when used; every other build stays byte-identical. packages/ludic.render3d is a physically based renderer written on that surface: HDRI image-based lighting, GPU-generated terrain with scanned PBR materials, CDLOD, cascaded shadows, glTF with skinning, instanced vegetation with impostors, procedural grass, water, SSAO, and an HDR pipeline with bloom, auto-exposure and ACES. It also carries this session's work on it: the terrain at half its cost (10.3 -> 5.4 ms of frame), the streaming hitch that got worse the longer you played, a resize that emptied the world, and the packaging that lets a game use the renderer from its own repository — `ludic assets`, the material manifest shipping with the package, and shader lookup falling back to the install root. See changes/ for each, with its numbers. The camping game that drove all of it has moved out to its own repository, Maroon Lake; examples/rendering/smooth.ludic stays as the renderer's example here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
470971bf70
commit
f25289db20
90 changed files with 35316 additions and 19853 deletions
37
docs/language/gl/_section.md
Normal file
37
docs/language/gl/_section.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
id: gl
|
||||
title: Gl
|
||||
order: 60
|
||||
---
|
||||
|
||||
OpenGL for Ludic. <code>Gl.*</code> binds the <strong>whole OpenGL 4.1 core API</strong>: every <code>gl*</code> entry point of the platform <code>gl3.h</code> is a method named by its snake case (<code>glBindBuffer</code> → <code>Gl.bind_buffer</code>, <code>glTexImage2D</code> → <code>Gl.tex_image2d</code>, <code>glDrawElementsInstanced</code> → <code>Gl.draw_elements_instanced</code>), with every <code>GL_*</code> constant available as written. Parameters keep the header's names as labels (<code>glVertexAttribPointer</code>'s <code>pointer</code> is called <code>offset</code>, <code>program</code> is <code>prog</code>); <code>GLfloat</code>/<code>GLdouble</code> parameters take <code>fixed</code>, pointer parameters take the raw <code>bytes</code>/<code>words</code> buffers Ludic already has, and 64-bit sizes take <code>long</code> (an <code>int</code> widens). The binding is generated by <code>ludic-dev glgen</code> from the header, with one ABI thunk per entry point.
|
||||
|
||||
A context comes from <code>Gl.open(width, height, title)</code>: windowed, an <code>NSOpenGLContext</code> on the game's window at the display's backing resolution (2× on Retina — <code>Gl.width()</code>/<code>Gl.height()</code> are the drawable's pixels); headless, an offscreen context with a framebuffer standing in for the screen (<code>Gl.screen_fbo()</code>), so the same program renders and screenshots under the test harness. <code>Gl.swap()</code> presents, <code>Gl.screenshot(path)</code> writes the screen as a PPM, <code>Gl.check(tag)</code> prints any pending error.
|
||||
|
||||
The glue: <code>Gl.program(vs, fs)</code> / <code>Gl.program5(vs, tcs, tes, gs, fs)</code> compile and link (logs on failure), <code>Gl.uniform(prog, name)</code>, <code>Gl.vao()</code>, <code>Gl.buffer()</code>, <code>Gl.texture()</code>, <code>Gl.framebuffer()</code> allocate, and float data is filled from Q16.16 with <code>Gl.floats(n)</code> / <code>Gl.put(buf, i, v)</code> / <code>Gl.bytes_of(n)</code> or from IEEE bits with <code>Gl.put_bits</code>; <code>Gl.f32(v)</code> and <code>Gl.fixed(bits)</code> convert single values. The bare <code>f_add</code>/<code>f_mul</code>/… helpers do IEEE single-precision arithmetic on those bit patterns for programs that want real floats on the CPU.
|
||||
|
||||
Using <code>Gl.*</code> links <code>gl.ll</code>, the thunks and <code>OpenGL.framework</code>; a program that does not is byte-identical to before. The <code>ludic.render3d</code> package is a physically based 3D renderer written on this surface (see <code>examples/rendering/smooth.ludic</code> here, and <a href="https://git.workshopsoft.io/workshopsoft/maroon-lake">Maroon Lake</a> for a game built on it).
|
||||
|
||||
```ludic
|
||||
program Triangle {
|
||||
property Marker { on: int = 1 }
|
||||
model Anchor { Marker }
|
||||
var prog: int = 0
|
||||
var vao: int = 0
|
||||
handler Boot phase Start {
|
||||
spawn Anchor {}
|
||||
if not Gl.open(width: 640, height: 360, title: "GL") { quit() }
|
||||
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")
|
||||
vao = Gl.vao()
|
||||
}
|
||||
handler Draw 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: prog)
|
||||
Gl.bind_vertex_array(array: vao)
|
||||
Gl.draw_arrays(mode: GL_TRIANGLES, first: 0, count: 3)
|
||||
Gl.swap()
|
||||
}
|
||||
}
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue