refactor(render3d): vertex data and draws behind gpu.ludic

Every vertex array, vertex and index buffer, attribute pointer, instance divisor, stream
upload and draw call now goes through gpu_mesh_* / gpu_buffer_* / gpu_draw_*, and no other
file in the package names them. A Mesh records its layout as it is built - which buffer
feeds which attribute at what stride and offset, per vertex or per instance - so a backend
that bakes vertex input into a pipeline can read it back. On OpenGL each call is the GL it
replaces, in the same order: the five fixed viewpoints render bit-identically and the game's
self-tests report exactly what they did before.

scatter_attach takes the mesh rather than its vertex array; a mesh now frees every vertex
buffer it owns (glTF meshes used to keep all but the first); the two helpers nothing called,
mesh_grid_patches and mesh_instance_buffer, are gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-15 10:21:55 +03:00
parent 3fb1b7cd87
commit 43c379aa0f
8 changed files with 271 additions and 267 deletions

View file

@ -123,20 +123,15 @@ function gltf_accessor(idx: int) -> pointer {
function gltf_attrib(m: Mesh, attrs: Val, name: pointer, loc: int) -> bool {
if value_has(attrs, name) == 0 { return false }
let data = gltf_accessor(value_as_int(value_get(attrs, name)))
let b = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, b)
gl_buffer_data(GL_ARRAY_BUFFER, gltf_count * gltf_comps * 4, data, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(loc)
gl_vertex_attrib_pointer(loc, gltf_comps, GL_FLOAT, 0, 0, null)
gpu_mesh_vertices(m, data, gltf_count * gltf_comps * 4, GPU_STATIC)
gpu_mesh_attr(m, loc, gltf_comps, GPU_F32, 0, 0, false)
free(data)
if loc == 0 { m.vbo = b }
return true
}
function gltf_prim(p: Val) -> Prim {
let pr = new Prim
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
let attrs = value_get(p, "attributes")
gltf_attrib(m, attrs, "POSITION", 0)
gltf_attrib(m, attrs, "NORMAL", 1)
@ -144,14 +139,11 @@ function gltf_prim(p: Val) -> Prim {
skin_attribs(m, attrs) # JOINTS_0 / WEIGHTS_0 onto 5 / 6, when the mesh has them
let idx = gltf_accessor(value_as_int(value_get(p, "indices")))
var isz = 4
m.itype = GL_UNSIGNED_INT
if gltf_ctype == 5123 { isz = 2; m.itype = GL_UNSIGNED_SHORT }
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, gltf_count * isz, idx, GL_STATIC_DRAW)
if gltf_ctype == 5123 { isz = 2 }
gpu_mesh_indices(m, idx, gltf_count * isz, isz)
free(idx)
m.count = gltf_count
gl_bind_vertex_array(0)
gpu_mesh_done(m)
pr.mesh = m
# material textures
if gltf_white == 0 { gltf_white = tex_solid(200, 200, 200, 255); gltf_flat = tex_solid(128, 128, 255, 255) }

View file

@ -11,6 +11,7 @@
# - the fixed-function render state: depth test/func/write, blending, face
# culling, colour writes, alpha-to-coverage, depth bias, scissor
# - uniforms: looked up by program and name (gpu_uniform), set by the u_* setters
# - vertex data: meshes, their attribute layouts, instance and stream buffers, draws
#
# Render state is cached. A pipeline API bakes this state into an object picked by
# key; OpenGL gets the same effect by only telling the driver what changed. The
@ -318,3 +319,138 @@ function gpu_caps_probe() -> void {
# Whether the renderer actually draws a feature yet. The Vulkan renderer is being built;
# until a feature lands, choosing it is saved and shown, and says it takes effect later.
function gpu_feature_implemented(f: int) -> bool { return false }
# ---- vertex data --------------------------------------------------------------------
# A Mesh is built through these and records what it is made of - which buffer feeds which
# attribute, at what stride and offset, per vertex or per instance - so a backend that bakes
# vertex input into a pipeline (Vulkan) can read the layout back. On OpenGL each call is the
# GL it replaces, in the same order: a vertex array object per mesh, bound while it is built.
const GPU_F32: int = 1
const GPU_U8: int = 2
const GPU_U16: int = 3
const GPU_STATIC: int = 0
const GPU_DYNAMIC: int = 1
const GPU_STREAM: int = 2
const GPU_MAX_ATTRS: int = 8
const GPU_ATTR_W: int = 7 # per attribute index: buffer, comps, type, stride, offset, normalized, per instance
const GPU_MAX_VBUFS: int = 8
function gpu_gl_type(t: int) -> int {
if t == GPU_U8 { return GL_UNSIGNED_BYTE }
if t == GPU_U16 { return GL_UNSIGNED_SHORT }
return GL_FLOAT
}
function gpu_type_bytes(t: int) -> int {
if t == GPU_U8 { return 1 }
if t == GPU_U16 { return 2 }
return 4
}
function gpu_gl_usage(u: int) -> int {
if u == GPU_DYNAMIC { return GL_DYNAMIC_DRAW }
if u == GPU_STREAM { return GL_STREAM_DRAW }
return GL_STATIC_DRAW
}
# a new mesh, its vertex array bound: the vertex, attribute and index calls below describe it
function gpu_mesh_new() -> Mesh {
let m = new Mesh
m.attrs = words(GPU_MAX_ATTRS * GPU_ATTR_W)
for i in 0 .. GPU_MAX_ATTRS * GPU_ATTR_W { m.attrs[i] = 0 }
m.vbufs = words(GPU_MAX_VBUFS)
m.vao = gl_vao()
return m
}
# a vertex buffer for the mesh being built (data may be null: storage only); returns it
function gpu_mesh_vertices(m: Mesh, data: pointer, nbytes: int, usage: int) -> int {
let b = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, b)
gl_buffer_data(GL_ARRAY_BUFFER, nbytes, data, gpu_gl_usage(usage))
if m.vbo == 0 { m.vbo = b }
if m.n_vbufs < GPU_MAX_VBUFS { m.vbufs[m.n_vbufs] = b; m.n_vbufs += 1 }
m.cur_buf = b
return b
}
function gpu_mesh_record(m: Mesh, index: int, comps: int, type: int, stride: int, offset: int, normalized: bool, inst: bool) -> void {
if index < 0 or index >= GPU_MAX_ATTRS { return }
let o = index * GPU_ATTR_W
var st = stride
if st == 0 { st = comps * gpu_type_bytes(type) }
m.attrs[o] = m.cur_buf; m.attrs[o + 1] = comps; m.attrs[o + 2] = type; m.attrs[o + 3] = st
m.attrs[o + 4] = offset; m.attrs[o + 5] = gpu_b(normalized); m.attrs[o + 6] = gpu_b(inst)
if index + 1 > m.n_attrs { m.n_attrs = index + 1 }
}
# attribute `index` read from the last vertex buffer (stride 0: tightly packed)
function gpu_mesh_attr(m: Mesh, index: int, comps: int, type: int, stride: int, offset: int, normalized: bool) -> void {
gl_enable_vertex_attrib_array(index)
gl_vertex_attrib_pointer(index, comps, gpu_gl_type(type), gpu_b(normalized), stride, gl_ptr(null, offset))
gpu_mesh_record(m, index, comps, type, stride, offset, normalized, false)
}
# the index buffer: 4-byte or 2-byte indices
function gpu_mesh_indices(m: Mesh, data: pointer, nbytes: int, index_bytes: int) -> void {
m.itype = GL_UNSIGNED_INT
if index_bytes == 2 { m.itype = GL_UNSIGNED_SHORT }
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, nbytes, data, GL_STATIC_DRAW)
}
# finished describing: nothing else is bound to it by accident
function gpu_mesh_done(m: Mesh) -> void { gl_bind_vertex_array(0) }
# Per-instance data: `buf` feeds the attributes named next, one element per instance. A mesh
# drawn from different instance buffers (the scatter layers' LOD buckets) is re-pointed here
# before each draw; on Vulkan that is a vertex-buffer binding, not a change of layout.
function gpu_mesh_bind_instances(m: Mesh, buf: int) -> void {
gl_bind_vertex_array(m.vao)
gl_bind_buffer(GL_ARRAY_BUFFER, buf)
m.cur_buf = buf
m.ibuf = buf
}
function gpu_mesh_attr_inst(m: Mesh, index: int, comps: int, type: int, stride: int, offset: int) -> void {
gl_enable_vertex_attrib_array(index)
gl_vertex_attrib_pointer(index, comps, gpu_gl_type(type), 0, stride, gl_ptr(null, offset))
gl_vertex_attrib_divisor(index, 1)
gpu_mesh_record(m, index, comps, type, stride, offset, false, true)
}
# a buffer on its own (instances, a stream): made, filled whole, freed
function gpu_buffer_new() -> int { return gl_buffer() }
function gpu_buffer_upload(buf: int, nbytes: int, data: pointer, usage: int) -> void {
gl_bind_buffer(GL_ARRAY_BUFFER, buf)
gl_buffer_data(GL_ARRAY_BUFFER, nbytes, data, gpu_gl_usage(usage))
}
function gpu_buffer_free(buf: int) -> void {
if buf == 0 { return }
let ids = gpu_tmp()
ids[0] = buf
gl_delete_buffers(1, ids)
}
# drawing
function gpu_mesh_bind(m: Mesh) -> void { gl_bind_vertex_array(m.vao) }
function gpu_mesh_unbind() -> void { gl_bind_vertex_array(0) }
function gpu_draw_mesh(m: Mesh) -> void {
gl_bind_vertex_array(m.vao)
if m.ebo != 0 { gl_draw_elements(m.mode, m.count, m.itype, null) }
else { gl_draw_arrays(m.mode, 0, m.count) }
}
function gpu_draw_mesh_instanced(m: Mesh, n: int) -> void {
gl_bind_vertex_array(m.vao)
if m.ebo != 0 { gl_draw_elements_instanced(m.mode, m.count, m.itype, null, n) }
else { gl_draw_arrays_instanced(m.mode, 0, m.count, n) }
}
# the bound mesh's indices again (a patch mesh drawn once per terrain node)
function gpu_draw_bound_elements(m: Mesh) -> void { gl_draw_elements(m.mode, m.count, m.itype, null) }
# vertices [first, first + count) of the bound mesh, as triangles (the overlay's ranges)
function gpu_draw_range(m: Mesh, first: int, count: int) -> void { gl_draw_arrays(GL_TRIANGLES, first, count) }
function gpu_mesh_free(m: Mesh) -> void {
if m == null { return }
let ids = gpu_tmp()
if m.vbufs != null {
for i in 0 .. m.n_vbufs { ids[0] = m.vbufs[i]; gl_delete_buffers(1, ids) }
m.n_vbufs = 0
} else if m.vbo != 0 { ids[0] = m.vbo; gl_delete_buffers(1, ids) }
m.vbo = 0
if m.ebo != 0 { ids[0] = m.ebo; gl_delete_buffers(1, ids); m.ebo = 0 }
if m.vao != 0 { ids[0] = m.vao; gl_delete_vertex_arrays(1, ids); m.vao = 0 }
}

View file

@ -23,8 +23,7 @@ var grass_dbg: int = 0
# a blade: `rows` rows of 2 vertices (x across, y along, z bend), attribute 2 = uv
function grass_blade_mesh(rows: int) -> Mesh {
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
let v = gl_floats(rows * 2 * 5)
var k = 0
for r in 0 .. rows {
@ -39,11 +38,9 @@ function grass_blade_mesh(rows: int) -> Mesh {
k += 5
}
}
m.vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, m.vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(rows * 2 * 5), v, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(0); gl_vertex_attrib_pointer(0, 3, GL_FLOAT, 0, 20, null)
gl_enable_vertex_attrib_array(2); gl_vertex_attrib_pointer(2, 2, GL_FLOAT, 0, 20, gl_ptr(null, 12))
gpu_mesh_vertices(m, v, gl_bytes_of(rows * 2 * 5), GPU_STATIC)
gpu_mesh_attr(m, 0, 3, GPU_F32, 20, 0, false)
gpu_mesh_attr(m, 2, 2, GPU_F32, 20, 12, false)
free(v)
let nq = rows - 1
let idx = words(nq * 6)
@ -52,12 +49,10 @@ function grass_blade_mesh(rows: int) -> Mesh {
idx[q * 6] = b; idx[q * 6 + 1] = b + 1; idx[q * 6 + 2] = b + 2
idx[q * 6 + 3] = b + 1; idx[q * 6 + 4] = b + 3; idx[q * 6 + 5] = b + 2
}
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, nq * 6 * 4, idx, GL_STATIC_DRAW)
gpu_mesh_indices(m, idx, nq * 6 * 4, 4)
free(idx)
m.count = nq * 6
gl_bind_vertex_array(0)
gpu_mesh_done(m)
return m
}
@ -157,7 +152,7 @@ function grass_draw() -> void {
u_f(gpu_uniform(p, "u_spec_scale"), fl(0.15))
gpu_cull(false)
grass_draws = 0
gl_bind_vertex_array(grass_mesh.vao)
gpu_mesh_bind(grass_mesh)
grass_tiles(16, F_ZERO, fi(300))
grass_tiles(64, fi(300), fi(1200))
grass_tiles(256, fi(1200), grass_radius)

View file

@ -1,34 +1,31 @@
# ============================================================================
# mesh.ludic — vertex data on the GPU: a Mesh record (VAO + buffers + a draw
# call), and the procedural meshes the renderer needs (a grid for the terrain,
# a full-screen triangle, a unit quad for instanced cards).
# mesh.ludic — vertex data on the GPU: a Mesh record (buffers + a draw call), and
# the procedural meshes the renderer needs (a grid for the terrain, a full-screen
# triangle, a unit quad for instanced cards). Built and drawn through gpu.ludic.
# ============================================================================
property Mesh {
vao: int = 0,
vbo: int = 0,
vao: int = 0, # OpenGL's vertex array object (gpu.ludic's, on that backend)
vbo: int = 0, # the first vertex buffer
ebo: int = 0,
count: int = 0, # indices (ebo != 0) or vertices
mode: int = 4, # GL_TRIANGLES
itype: int = 0x1405 # GL_UNSIGNED_INT
itype: int = 0x1405, # GL_UNSIGNED_INT
attrs: words, # the layout, GPU_ATTR_W words per attribute index (gpu.ludic)
n_attrs: int = 0,
vbufs: words, # every vertex buffer the mesh owns, freed with it
n_vbufs: int = 0,
cur_buf: int = 0, # the buffer the next attribute reads (while building)
ibuf: int = 0 # the instance buffer attached last
}
function mesh_draw(m: Mesh) -> void {
gl_bind_vertex_array(m.vao)
if m.ebo != 0 { gl_draw_elements(m.mode, m.count, m.itype, null) }
else { gl_draw_arrays(m.mode, 0, m.count) }
}
function mesh_draw_instanced(m: Mesh, n: int) -> void {
gl_bind_vertex_array(m.vao)
if m.ebo != 0 { gl_draw_elements_instanced(m.mode, m.count, m.itype, null, n) }
else { gl_draw_arrays_instanced(m.mode, 0, m.count, n) }
}
function mesh_draw(m: Mesh) -> void { gpu_draw_mesh(m) }
function mesh_draw_instanced(m: Mesh, n: int) -> void { gpu_draw_mesh_instanced(m, n) }
# A flat n x n vertex grid over [-half, half]^2 in x/z, y = 0. Attribute 0 = (x, z).
# The terrain vertex shader lifts it with the height map.
function mesh_grid(n: int, half: int) -> Mesh {
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
let nv = n * n
let v = gl_floats(nv * 2)
var k = 0
@ -40,11 +37,8 @@ function mesh_grid(n: int, half: int) -> Mesh {
k += 2
}
}
m.vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, m.vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(nv * 2), v, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(0)
gl_vertex_attrib_pointer(0, 2, GL_FLOAT, 0, 8, null)
gpu_mesh_vertices(m, v, gl_bytes_of(nv * 2), GPU_STATIC)
gpu_mesh_attr(m, 0, 2, GPU_F32, 8, 0, false)
free(v)
let ni = (n - 1) * (n - 1) * 6
let idx = words(ni)
@ -57,103 +51,41 @@ function mesh_grid(n: int, half: int) -> Mesh {
k += 6
}
}
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, ni * 4, idx, GL_STATIC_DRAW)
gpu_mesh_indices(m, idx, ni * 4, 4)
free(idx)
m.count = ni
gl_bind_vertex_array(0)
return m
}
# The grid as patches (4 control points per cell) for tessellation shaders.
function mesh_grid_patches(n: int, half: int) -> Mesh {
let m = mesh_grid(n, half)
# rebuild the index buffer as quads
let nq = (n - 1) * (n - 1) * 4
let idx = words(nq)
var k = 0
for j in 0 .. n - 1 {
for i in 0 .. n - 1 {
let a = j * n + i
idx[k] = a; idx[k + 1] = a + 1; idx[k + 2] = a + n + 1; idx[k + 3] = a + n
k += 4
}
}
gl_bind_vertex_array(m.vao)
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, nq * 4, idx, GL_STATIC_DRAW)
gl_bind_vertex_array(0)
free(idx)
m.count = nq
m.mode = GL_PATCHES
gpu_mesh_done(m)
return m
}
# A full-screen triangle with no attributes (the vertex shader uses gl_VertexID).
function mesh_fullscreen() -> Mesh {
let m = new Mesh
m.vao = gl_vao()
gl_bind_vertex_array(0)
let m = gpu_mesh_new()
gpu_mesh_done(m)
m.count = 3
return m
}
# A unit quad in x/y ([-0.5, 0.5] x [0, 1]) with uv, attribute 0 = xy, 1 = uv.
function mesh_card() -> Mesh {
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
let v = gl_floats(16)
gl_put(v, 0, -0.5); gl_put(v, 1, 0.0); gl_put(v, 2, 0.0); gl_put(v, 3, 0.0)
gl_put(v, 4, 0.5); gl_put(v, 5, 0.0); gl_put(v, 6, 1.0); gl_put(v, 7, 0.0)
gl_put(v, 8, 0.5); gl_put(v, 9, 1.0); gl_put(v, 10, 1.0); gl_put(v, 11, 1.0)
gl_put(v, 12, -0.5); gl_put(v, 13, 1.0); gl_put(v, 14, 0.0); gl_put(v, 15, 1.0)
m.vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, m.vbo)
gl_buffer_data(GL_ARRAY_BUFFER, 64, v, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(0)
gl_vertex_attrib_pointer(0, 2, GL_FLOAT, 0, 16, null)
gl_enable_vertex_attrib_array(1)
gl_vertex_attrib_pointer(1, 2, GL_FLOAT, 0, 16, gl_ptr(null, 8))
gpu_mesh_vertices(m, v, 64, GPU_STATIC)
gpu_mesh_attr(m, 0, 2, GPU_F32, 16, 0, false)
gpu_mesh_attr(m, 1, 2, GPU_F32, 16, 8, false)
free(v)
let idx = words(6)
idx[0] = 0; idx[1] = 1; idx[2] = 2; idx[3] = 0; idx[4] = 2; idx[5] = 3
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, 24, idx, GL_STATIC_DRAW)
gpu_mesh_indices(m, idx, 24, 4)
free(idx)
m.count = 6
gl_bind_vertex_array(0)
gpu_mesh_done(m)
return m
}
# Attach a per-instance float buffer (n floats per instance, split into vec4
# attributes from `first_attr`) to a mesh's VAO. Returns the buffer id.
function mesh_instance_buffer(m: Mesh, first_attr: int, floats_per: int, data: pointer, count: int) -> int {
gl_bind_vertex_array(m.vao)
let b = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, b)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(floats_per * count), data, GL_STATIC_DRAW)
var a = 0
var off = 0
while off < floats_per {
var sz = floats_per - off
if sz > 4 { sz = 4 }
gl_enable_vertex_attrib_array(first_attr + a)
gl_vertex_attrib_pointer(first_attr + a, sz, GL_FLOAT, 0, floats_per * 4, gl_ptr(null, off * 4))
gl_vertex_attrib_divisor(first_attr + a, 1)
a += 1
off += 4
}
gl_bind_vertex_array(0)
return b
}
# release a mesh's GL objects (a mesh this package built for something being thrown away)
function mesh_free(m: Mesh) -> void {
if m == null { return }
let ids = gl_scratch()
if m.vbo != 0 { ids[0] = m.vbo; gl_delete_buffers(1, ids); m.vbo = 0 }
if m.ebo != 0 { ids[0] = m.ebo; gl_delete_buffers(1, ids); m.ebo = 0 }
if m.vao != 0 { ids[0] = m.vao; gl_delete_vertex_arrays(1, ids); m.vao = 0 }
}
# release a mesh's GPU objects (a mesh this package built for something being thrown away)
function mesh_free(m: Mesh) -> void { gpu_mesh_free(m) }

View file

@ -12,7 +12,7 @@ const OV_MAX_QUADS: int = 6000
const OV_FLOATS: int = 8 # x, y, u, v, r, g, b, a
var ov_prog: int = 0
var ov_vao: int = 0
var ov_mesh: Mesh = null
var ov_vbo: int = 0
var ov_buf: pointer = null
var ov_n: int = 0
@ -55,15 +55,12 @@ function overlay_init(font_dir: string) -> bool {
r3d_program_log("overlay.vert", "overlay.frag", "")
ov_prog = gl_program("#version 410 core\n" + r3d_shader_file("overlay.vert"), "#version 410 core\n" + r3d_shader_file("overlay.frag"))
if ov_prog == 0 { print("overlay: program failed"); return false }
ov_vao = gl_vao()
gl_bind_vertex_array(ov_vao)
ov_vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, ov_vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(OV_MAX_QUADS * 6 * OV_FLOATS), null, GL_DYNAMIC_DRAW)
gl_enable_vertex_attrib_array(0); gl_vertex_attrib_pointer(0, 2, GL_FLOAT, 0, OV_FLOATS * 4, null)
gl_enable_vertex_attrib_array(1); gl_vertex_attrib_pointer(1, 2, GL_FLOAT, 0, OV_FLOATS * 4, gl_ptr(null, 8))
gl_enable_vertex_attrib_array(2); gl_vertex_attrib_pointer(2, 4, GL_FLOAT, 0, OV_FLOATS * 4, gl_ptr(null, 16))
gl_bind_vertex_array(0)
ov_mesh = gpu_mesh_new()
ov_vbo = gpu_mesh_vertices(ov_mesh, null, gl_bytes_of(OV_MAX_QUADS * 6 * OV_FLOATS), GPU_DYNAMIC)
gpu_mesh_attr(ov_mesh, 0, 2, GPU_F32, OV_FLOATS * 4, 0, false)
gpu_mesh_attr(ov_mesh, 1, 2, GPU_F32, OV_FLOATS * 4, 8, false)
gpu_mesh_attr(ov_mesh, 2, 4, GPU_F32, OV_FLOATS * 4, 16, false)
gpu_mesh_done(ov_mesh)
ov_buf = gl_floats(OV_MAX_QUADS * 6 * OV_FLOATS)
ov_ranges = words(OV_MAX_RANGES * OV_RANGE_W)
ov_white = tex_solid(255, 255, 255, 255)
@ -193,9 +190,8 @@ function ov_flush() -> void {
ov_close_range()
if ov_n == 0 { ov_nr = 0; ov_range_start = 0; return }
gl_use_program(ov_prog)
gl_bind_vertex_array(ov_vao)
gl_bind_buffer(GL_ARRAY_BUFFER, ov_vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(ov_n * 6 * OV_FLOATS), ov_buf, GL_STREAM_DRAW)
gpu_mesh_bind(ov_mesh)
gpu_buffer_upload(ov_vbo, gl_bytes_of(ov_n * 6 * OV_FLOATS), ov_buf, GPU_STREAM)
var last = -1
var clipped = false
for i in 0 .. ov_nr {
@ -212,10 +208,10 @@ function ov_flush() -> void {
clipped = true
gpu_scissor(ov_ranges[o + 3], ov_ranges[o + 4], ov_ranges[o + 5], ov_ranges[o + 6])
} else if clipped { gpu_scissor_off(); clipped = false }
gl_draw_arrays(GL_TRIANGLES, ov_ranges[o + 1] * 6, ov_ranges[o + 2] * 6)
gpu_draw_range(ov_mesh, ov_ranges[o + 1] * 6, ov_ranges[o + 2] * 6)
}
if clipped { gpu_scissor_off() }
gl_bind_vertex_array(0)
gpu_mesh_unbind()
ov_n = 0; ov_nr = 0; ov_range_start = 0
}
function ov_end() -> void {

View file

@ -68,14 +68,20 @@ property Layer {
lvl: words # scratch: the level chosen per gathered instance
}
# the procedural models' one layout: position, normal, uv, interleaved at 32 bytes
function sc_model_layout(m: Mesh) -> void {
gpu_mesh_attr(m, 0, 3, GPU_F32, 32, 0, false)
gpu_mesh_attr(m, 1, 3, GPU_F32, 32, 12, false)
gpu_mesh_attr(m, 2, 2, GPU_F32, 32, 24, false)
}
# Two crossed unit quads (x in [-0.5, 0.5], y in [0, 1]), attribute 0 = pos,
# 1 = the quad's facing normal, 2 = uv. Scaled per layer to the atlas card size.
function model_cross_card() -> Model {
let model = new Model
model.prims = new []Prim
let pr = new Prim
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
let v = gl_floats(8 * 8)
var k = 0
for q in 0 .. 2 {
@ -89,22 +95,16 @@ function model_cross_card() -> Model {
k += 8
}
}
m.vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, m.vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(64), v, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(0); gl_vertex_attrib_pointer(0, 3, GL_FLOAT, 0, 32, null)
gl_enable_vertex_attrib_array(1); gl_vertex_attrib_pointer(1, 3, GL_FLOAT, 0, 32, gl_ptr(null, 12))
gl_enable_vertex_attrib_array(2); gl_vertex_attrib_pointer(2, 2, GL_FLOAT, 0, 32, gl_ptr(null, 24))
gpu_mesh_vertices(m, v, gl_bytes_of(64), GPU_STATIC)
sc_model_layout(m)
free(v)
let idx = words(12)
idx[0] = 0; idx[1] = 1; idx[2] = 2; idx[3] = 0; idx[4] = 2; idx[5] = 3
idx[6] = 4; idx[7] = 5; idx[8] = 6; idx[9] = 4; idx[10] = 6; idx[11] = 7
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, 48, idx, GL_STATIC_DRAW)
gpu_mesh_indices(m, idx, 48, 4)
free(idx)
m.count = 12
gl_bind_vertex_array(0)
gpu_mesh_done(m)
pr.mesh = m
if gltf_white == 0 { gltf_white = tex_solid(200, 200, 200, 255); gltf_flat = tex_solid(128, 128, 255, 255) }
pr.diff = gltf_white; pr.nrm = gltf_flat; pr.arm = gltf_white
@ -127,8 +127,7 @@ function model_lupine() -> Model {
let model = new Model
model.prims = new []Prim
let pr = new Prim
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
# quads: stem x2 + tiers 12 x 2 + 3 leaves = 29 quads
let nq = 29
let v = gl_floats(nq * 4 * 8)
@ -172,19 +171,13 @@ function model_lupine() -> Model {
idx[qi] = b; idx[qi + 1] = b + 1; idx[qi + 2] = b + 2; idx[qi + 3] = b; idx[qi + 4] = b + 2; idx[qi + 5] = b + 3
qi += 6
}
m.vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, m.vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(nq * 4 * 8), v, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(0); gl_vertex_attrib_pointer(0, 3, GL_FLOAT, 0, 32, null)
gl_enable_vertex_attrib_array(1); gl_vertex_attrib_pointer(1, 3, GL_FLOAT, 0, 32, gl_ptr(null, 12))
gl_enable_vertex_attrib_array(2); gl_vertex_attrib_pointer(2, 2, GL_FLOAT, 0, 32, gl_ptr(null, 24))
gpu_mesh_vertices(m, v, gl_bytes_of(nq * 4 * 8), GPU_STATIC)
sc_model_layout(m)
free(v)
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, nq * 6 * 4, idx, GL_STATIC_DRAW)
gpu_mesh_indices(m, idx, nq * 6 * 4, 4)
free(idx)
m.count = nq * 6
gl_bind_vertex_array(0)
gpu_mesh_done(m)
pr.mesh = m
if gltf_white == 0 { gltf_white = tex_solid(200, 200, 200, 255); gltf_flat = tex_solid(128, 128, 255, 255) }
pr.diff = gltf_white; pr.nrm = gltf_flat; pr.arm = gltf_white
@ -199,8 +192,7 @@ function model_lupine_dense() -> Model {
let model = new Model
model.prims = new []Prim
let pr = new Prim
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
let nfl = 220
let nq = 2 + nfl + 5
let v = gl_floats(nq * 4 * 8)
@ -252,19 +244,13 @@ function model_lupine_dense() -> Model {
idx[qi] = b; idx[qi + 1] = b + 1; idx[qi + 2] = b + 2; idx[qi + 3] = b; idx[qi + 4] = b + 2; idx[qi + 5] = b + 3
qi += 6
}
m.vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, m.vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(nq * 4 * 8), v, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(0); gl_vertex_attrib_pointer(0, 3, GL_FLOAT, 0, 32, null)
gl_enable_vertex_attrib_array(1); gl_vertex_attrib_pointer(1, 3, GL_FLOAT, 0, 32, gl_ptr(null, 12))
gl_enable_vertex_attrib_array(2); gl_vertex_attrib_pointer(2, 2, GL_FLOAT, 0, 32, gl_ptr(null, 24))
gpu_mesh_vertices(m, v, gl_bytes_of(nq * 4 * 8), GPU_STATIC)
sc_model_layout(m)
free(v)
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, nq * 6 * 4, idx, GL_STATIC_DRAW)
gpu_mesh_indices(m, idx, nq * 6 * 4, 4)
free(idx)
m.count = nq * 6
gl_bind_vertex_array(0)
gpu_mesh_done(m)
pr.mesh = m
if gltf_white == 0 { gltf_white = tex_solid(200, 200, 200, 255); gltf_flat = tex_solid(128, 128, 255, 255) }
pr.diff = gltf_white; pr.nrm = gltf_flat; pr.arm = gltf_white
@ -278,8 +264,7 @@ function model_blade() -> Model {
let model = new Model
model.prims = new []Prim
let pr = new Prim
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
let rows = 5
let v = gl_floats(rows * 2 * 8)
var k = 0
@ -298,12 +283,8 @@ function model_blade() -> Model {
k += 8
}
}
m.vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, m.vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(rows * 2 * 8), v, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(0); gl_vertex_attrib_pointer(0, 3, GL_FLOAT, 0, 32, null)
gl_enable_vertex_attrib_array(1); gl_vertex_attrib_pointer(1, 3, GL_FLOAT, 0, 32, gl_ptr(null, 12))
gl_enable_vertex_attrib_array(2); gl_vertex_attrib_pointer(2, 2, GL_FLOAT, 0, 32, gl_ptr(null, 24))
gpu_mesh_vertices(m, v, gl_bytes_of(rows * 2 * 8), GPU_STATIC)
sc_model_layout(m)
free(v)
let ni = (rows - 1) * 6
let idx = words(ni)
@ -314,12 +295,10 @@ function model_blade() -> Model {
idx[k + 3] = a + 1; idx[k + 4] = a + 3; idx[k + 5] = a + 2
k += 6
}
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, ni * 4, idx, GL_STATIC_DRAW)
gpu_mesh_indices(m, idx, ni * 4, 4)
free(idx)
m.count = ni
gl_bind_vertex_array(0)
gpu_mesh_done(m)
pr.mesh = m
if gltf_white == 0 { gltf_white = tex_solid(200, 200, 200, 255); gltf_flat = tex_solid(128, 128, 255, 255) }
pr.diff = gltf_white; pr.nrm = gltf_flat; pr.arm = gltf_white
@ -389,24 +368,18 @@ function scatter_init() -> void {
let one = gl_floats(INST_FLOATS)
for i in 0 .. INST_FLOATS { gl_put_bits(one, i, F_ZERO) }
gl_put_bits(one, 3, F_ONE); gl_put_bits(one, 5, F_ONE)
sc_ident_buf = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, sc_ident_buf)
gl_buffer_data(GL_ARRAY_BUFFER, INST_FLOATS * 4, one, GL_STATIC_DRAW)
sc_ident_buf = gpu_buffer_new()
gpu_buffer_upload(sc_ident_buf, INST_FLOATS * 4, one, GPU_STATIC)
free(one)
sc_layers = new []Layer
}
# attach a GL instance buffer to a VAO at attributes 3, 4
function scatter_attach(vao: int, buf: int) -> void {
gl_bind_vertex_array(vao)
gl_bind_buffer(GL_ARRAY_BUFFER, buf)
gl_enable_vertex_attrib_array(3)
gl_vertex_attrib_pointer(3, 4, GL_FLOAT, 0, INST_FLOATS * 4, null)
gl_vertex_attrib_divisor(3, 1)
gl_enable_vertex_attrib_array(4)
gl_vertex_attrib_pointer(4, 4, GL_FLOAT, 0, INST_FLOATS * 4, gl_ptr(null, 16))
gl_vertex_attrib_divisor(4, 1)
gl_bind_vertex_array(0)
# feed a mesh its instances from `buf`: attribute 3 = position + scale, 4 = sin, cos, seed, wind
function scatter_attach(m: Mesh, buf: int) -> void {
gpu_mesh_bind_instances(m, buf)
gpu_mesh_attr_inst(m, 3, 4, GPU_F32, INST_FLOATS * 4, 0)
gpu_mesh_attr_inst(m, 4, 4, GPU_F32, INST_FLOATS * 4, 16)
gpu_mesh_done(m)
}
function layer_new(model: Model, cap: int, foliage: bool, wind: int, near: int, cull: int) -> Layer {
@ -421,11 +394,11 @@ function layer_new(model: Model, cap: int, foliage: bool, wind: int, near: int,
l.inst = words(cap * INST_FLOATS)
l.scratch = words(cap * INST_FLOATS)
l.last_cam = v3_new(fi(100000), F_ZERO, F_ZERO)
l.buf = gl_buffer()
l.imp_buf = gl_buffer()
l.sh_buf = gl_buffer()
l.buf = gpu_buffer_new()
l.imp_buf = gpu_buffer_new()
l.sh_buf = gpu_buffer_new()
l.rough = F_ONE
for i in 0 .. len(model.prims) { scatter_attach(model.prims[i].mesh.vao, l.buf) }
for i in 0 .. len(model.prims) { scatter_attach(model.prims[i].mesh, l.buf) }
push(sc_layers, l)
return l
}
@ -469,7 +442,7 @@ function impostor_bake(model: Model, tiles: int, tw: int, th: int) -> Impostor {
gpu_cull(false)
gpu_blend(false)
# the model's prims temporarily take the identity instance
for i in 0 .. len(model.prims) { scatter_attach(model.prims[i].mesh.vao, sc_ident_buf) }
for i in 0 .. len(model.prims) { scatter_attach(model.prims[i].mesh, sc_ident_buf) }
let view = m4_new(); let proj = m4_new()
let eye = words(3); let at = words(3); let up = v3_new(F_ZERO, F_ONE, F_ZERO)
let cy = f_add(model.ymin, f_mul(model.height, F_HALF))
@ -521,9 +494,9 @@ function layer_set_lods(l: Layer, models: []Model, dists: words) -> void {
l.lod_dist = words(l.n_lods); l.lod_card = words(l.n_lods); l.lod_buf = words(l.n_lods); l.n_lod = words(l.n_lods)
for k in 0 .. l.n_lods {
l.lod_dist[k] = dists[k]; l.lod_card[k] = 0; l.n_lod[k] = 0
l.lod_buf[k] = gl_buffer()
l.lod_buf[k] = gpu_buffer_new()
let m = models[k]
for i in 0 .. len(m.prims) { scatter_attach(m.prims[i].mesh.vao, l.lod_buf[k]) }
for i in 0 .. len(m.prims) { scatter_attach(m.prims[i].mesh, l.lod_buf[k]) }
}
l.model = models[0]
l.near = dists[l.n_lods - 1]
@ -534,7 +507,7 @@ function layer_lod_card(l: Layer, k: int) -> void { l.lod_card[k] = 1 }
function layer_set_impostor(l: Layer, im: Impostor) -> void {
l.imp = im
scatter_attach(sc_card.vao, l.imp_buf)
scatter_attach(sc_card, l.imp_buf)
}
# ---- per frame -----------------------------------------------------------------------
@ -596,8 +569,7 @@ function layer_grid_build(l: Layer, cell: int) -> void {
free(cellof); free(fill)
if l.vis == null { l.vis = words(l.cap * INST_FLOATS) }
l.n_sh = l.count
gl_bind_buffer(GL_ARRAY_BUFFER, l.sh_buf)
gl_buffer_data(GL_ARRAY_BUFFER, l.count * INST_FLOATS * 4, l.inst, GL_STATIC_DRAW)
gpu_buffer_upload(l.sh_buf, l.count * INST_FLOATS * 4, l.inst, GPU_STATIC)
}
# gather the instances of the cells the camera can see (and that are within cull)
@ -671,21 +643,19 @@ function layer_partition_lods(l: Layer, src: words, total: int) -> void {
for k in 0 .. n {
l.n_lod[k] = counts[k]
if counts[k] > 0 {
gl_bind_buffer(GL_ARRAY_BUFFER, l.lod_buf[k])
gl_buffer_data(GL_ARRAY_BUFFER, counts[k] * INST_FLOATS * 4, mem_off(tmp, start[k] * INST_FLOATS * 4), GL_DYNAMIC_DRAW)
gpu_buffer_upload(l.lod_buf[k], counts[k] * INST_FLOATS * 4, mem_off(tmp, start[k] * INST_FLOATS * 4), GPU_DYNAMIC)
}
}
l.n_near = counts[0]
l.n_far = counts[n]
if sc_dbg_lod and total > 1000 { print(`lod partition: total {total} dropped {counts[n + 1]} far {counts[n]} l0 {counts[0]} l1 {counts[1]} l2 {counts[2]} l3 {counts[3]} dist0 {f_fx(l.lod_dist[0])} dist3 {f_fx(l.lod_dist[n - 1])} cull {f_fx(l.cull)} cam {f_fx(cam_pos[0])} {f_fx(cam_pos[2])} first {f_fx(src[0])} {f_fx(src[2])}`) }
if l.n_far > 0 {
gl_bind_buffer(GL_ARRAY_BUFFER, l.imp_buf)
gl_buffer_data(GL_ARRAY_BUFFER, l.n_far * INST_FLOATS * 4, mem_off(tmp, start[n] * INST_FLOATS * 4), GL_DYNAMIC_DRAW)
gpu_buffer_upload(l.imp_buf, l.n_far * INST_FLOATS * 4, mem_off(tmp, start[n] * INST_FLOATS * 4), GPU_DYNAMIC)
}
# casters: the whole (gathered) set from the shadow buffer, unless the impostor casts
if l.gcell == 0 {
l.n_sh = total
if total > 0 { gl_bind_buffer(GL_ARRAY_BUFFER, l.sh_buf); gl_buffer_data(GL_ARRAY_BUFFER, total * INST_FLOATS * 4, src, GL_DYNAMIC_DRAW) }
if total > 0 { gpu_buffer_upload(l.sh_buf, total * INST_FLOATS * 4, src, GPU_DYNAMIC) }
}
free(counts); free(start); free(fill)
}
@ -702,8 +672,7 @@ function layer_update(l: Layer) -> void {
# per-instance loop — one upload, and the same buffer casts its shadows.
if l.streamed and l.imp == null and l.near == 0 and l.n_lods <= 1 {
l.n_near = l.count; l.n_far = 0; l.n_sh = l.count
gl_bind_buffer(GL_ARRAY_BUFFER, l.buf)
gl_buffer_data(GL_ARRAY_BUFFER, l.count * INST_FLOATS * 4, l.inst, GL_DYNAMIC_DRAW)
gpu_buffer_upload(l.buf, l.count * INST_FLOATS * 4, l.inst, GPU_DYNAMIC)
prof_layer_add(gl_now_us() - t_lu, l.count * INST_FLOATS * 4)
return
}
@ -753,15 +722,12 @@ function layer_update(l: Layer) -> void {
if l.gcell == 0 {
l.n_sh = l.count
if l.count > 0 {
gl_bind_buffer(GL_ARRAY_BUFFER, l.sh_buf)
gl_buffer_data(GL_ARRAY_BUFFER, l.count * INST_FLOATS * 4, l.inst, GL_DYNAMIC_DRAW)
gpu_buffer_upload(l.sh_buf, l.count * INST_FLOATS * 4, l.inst, GPU_DYNAMIC)
}
}
gl_bind_buffer(GL_ARRAY_BUFFER, l.buf)
gl_buffer_data(GL_ARRAY_BUFFER, nn * INST_FLOATS * 4, tmp, GL_DYNAMIC_DRAW)
gpu_buffer_upload(l.buf, nn * INST_FLOATS * 4, tmp, GPU_DYNAMIC)
if nf > 0 {
gl_bind_buffer(GL_ARRAY_BUFFER, l.imp_buf)
gl_buffer_data(GL_ARRAY_BUFFER, nf * INST_FLOATS * 4, mem_off(tmp, (far_off - nf * INST_FLOATS) * 4), GL_DYNAMIC_DRAW)
gpu_buffer_upload(l.imp_buf, nf * INST_FLOATS * 4, mem_off(tmp, (far_off - nf * INST_FLOATS) * 4), GPU_DYNAMIC)
}
prof_layer_add(gl_now_us() - t_lu, (nn + nf + l.n_sh) * INST_FLOATS * 4)
}
@ -857,7 +823,7 @@ function layer_draw_model(l: Layer, model: Model, vb: int, cnt: int, card: bool,
gpu_cull(false)
for i in 0 .. len(model.prims) {
let pr = model.prims[i]
scatter_attach(pr.mesh.vao, vb)
scatter_attach(pr.mesh, vb)
if not card {
r3d_bind_2d(p, "u_diff", 0, pr.diff)
if not shadow { r3d_bind_2d(p, "u_nrm", 1, pr.nrm); r3d_bind_2d(p, "u_arm", 2, pr.arm) }
@ -896,7 +862,7 @@ function layer_draw_far(l: Layer, shadow: bool, light_vp: words) -> void {
}
gpu_cull(false)
if not shadow and sc_a2c { gpu_alpha_to_coverage(true) }
scatter_attach(sc_card.vao, l.imp_buf)
scatter_attach(sc_card, l.imp_buf)
mesh_draw_instanced(sc_card, l.n_far)
gpu_alpha_to_coverage(false)
}
@ -918,7 +884,7 @@ function layer_draw_shadow(l: Layer, light_vp: words) -> void {
u_v3(gpu_uniform(p, "u_face_dir"), sun_dir)
u_v3(gpu_uniform(p, "u_cam_pos"), cam_pos)
gpu_cull(false)
scatter_attach(sc_card.vao, l.sh_buf)
scatter_attach(sc_card, l.sh_buf)
mesh_draw_instanced(sc_card, l.n_sh)
}
@ -946,7 +912,7 @@ function layer_draw_depth(l: Layer, model: Model, vb: int, cnt: int) -> void {
gpu_cull(false)
for i in 0 .. len(model.prims) {
let pr = model.prims[i]
scatter_attach(pr.mesh.vao, vb)
scatter_attach(pr.mesh, vb)
r3d_bind_2d(p, "u_diff", 0, pr.diff)
mesh_draw_instanced(pr.mesh, cnt)
}
@ -1065,9 +1031,8 @@ function carpet_bake(layers: []Layer, count: int, tile: int, res: int) -> int {
}
}
}
let buf = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, buf)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(n9 * INST_FLOATS), inst, GL_STATIC_DRAW)
let buf = gpu_buffer_new()
gpu_buffer_upload(buf, gl_bytes_of(n9 * INST_FLOATS), inst, GPU_STATIC)
free(inst)
# straight down: the window is exactly one tile
let view = m4_new(); let proj = m4_new()
@ -1088,7 +1053,7 @@ function carpet_bake(layers: []Layer, count: int, tile: int, res: int) -> int {
r3d_bind_2d(bake, "u_arm", 2, l.atlas.normal)
for i in 0 .. len(l.model.prims) {
let pr = l.model.prims[i]
scatter_attach(pr.mesh.vao, buf)
scatter_attach(pr.mesh, buf)
mesh_draw_instanced(pr.mesh, n9)
}
}
@ -1098,8 +1063,7 @@ function carpet_bake(layers: []Layer, count: int, tile: int, res: int) -> int {
gl_delete_framebuffers(1, ids)
ids[0] = rb
gl_delete_renderbuffers(1, ids)
ids[0] = buf
gl_delete_buffers(1, ids)
gpu_buffer_free(buf)
gl_bind_texture(GL_TEXTURE_2D, tex)
gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
@ -1121,10 +1085,10 @@ function scatter_clear_all() -> void {
let ids = gl_scratch()
for i in 0 .. len(sc_layers) {
let l = sc_layers[i]
if l.buf != 0 { ids[0] = l.buf; gl_delete_buffers(1, ids) }
if l.imp_buf != 0 { ids[0] = l.imp_buf; gl_delete_buffers(1, ids) }
if l.sh_buf != 0 { ids[0] = l.sh_buf; gl_delete_buffers(1, ids) }
if l.lod_buf != null { for k in 0 .. l.n_lods { ids[0] = l.lod_buf[k]; gl_delete_buffers(1, ids) } }
if l.buf != 0 { gpu_buffer_free(l.buf) }
if l.imp_buf != 0 { gpu_buffer_free(l.imp_buf) }
if l.sh_buf != 0 { gpu_buffer_free(l.sh_buf) }
if l.lod_buf != null { for k in 0 .. l.n_lods { gpu_buffer_free(l.lod_buf[k]) } }
if l.inst != null { free(l.inst) }
if l.scratch != null { free(l.scratch) }
if l.tint != null { free(l.tint) }

View file

@ -51,25 +51,19 @@ function skin_attribs(m: Mesh, attrs: Val) -> bool {
if value_has(attrs, "JOINTS_0") == 0 or value_has(attrs, "WEIGHTS_0") == 0 { return false }
let jd = gltf_accessor(value_as_int(value_get(attrs, "JOINTS_0")))
var jsz = 1
var jtype = GL_UNSIGNED_BYTE
if gltf_ctype == 5123 { jsz = 2; jtype = GL_UNSIGNED_SHORT }
let jb = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, jb)
gl_buffer_data(GL_ARRAY_BUFFER, gltf_count * gltf_comps * jsz, jd, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(5)
gl_vertex_attrib_pointer(5, gltf_comps, jtype, 0, 0, null) # integers, read as floats
var jtype = GPU_U8
if gltf_ctype == 5123 { jsz = 2; jtype = GPU_U16 }
gpu_mesh_vertices(m, jd, gltf_count * gltf_comps * jsz, GPU_STATIC)
gpu_mesh_attr(m, 5, gltf_comps, jtype, 0, 0, false) # integers, read as floats
free(jd)
let wd = gltf_accessor(value_as_int(value_get(attrs, "WEIGHTS_0")))
var wsz = 4
var wtype = GL_FLOAT
var norm = 0
if gltf_ctype == 5123 { wsz = 2; wtype = GL_UNSIGNED_SHORT; norm = 1 }
if gltf_ctype == 5121 { wsz = 1; wtype = GL_UNSIGNED_BYTE; norm = 1 }
let wb = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, wb)
gl_buffer_data(GL_ARRAY_BUFFER, gltf_count * gltf_comps * wsz, wd, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(6)
gl_vertex_attrib_pointer(6, gltf_comps, wtype, norm, 0, null)
var wtype = GPU_F32
var norm = false
if gltf_ctype == 5123 { wsz = 2; wtype = GPU_U16; norm = true }
if gltf_ctype == 5121 { wsz = 1; wtype = GPU_U8; norm = true }
gpu_mesh_vertices(m, wd, gltf_count * gltf_comps * wsz, GPU_STATIC)
gpu_mesh_attr(m, 6, gltf_comps, wtype, 0, 0, norm)
free(wd)
return true
}

View file

@ -544,7 +544,7 @@ function terrain_sun_pass(w: int, h: int, depth: int) -> Target {
shadow_bind(p)
sky_bind_lighting(p)
ter_sun_pass = true
gl_bind_vertex_array(cd_mesh.vao)
gpu_mesh_bind(cd_mesh)
cdlod_select(CD_LEVELS - 1, 0, 0)
ter_sun_pass = false
if Os.has_env("R3D_DUMP_SUN") and not ter_sun_dumped and not ter_reflect { ter_sun_dumped = true; tex_dump(t.color, w, h, "build/dbg_sun.ppm") }
@ -587,7 +587,7 @@ function terrain_draw() -> void {
cd_draws = 0
cd_far_draws = 0
cd_near_draws = 0
gl_bind_vertex_array(cd_mesh.vao)
gpu_mesh_bind(cd_mesh)
if ter_wire { gl_polygon_mode(GL_FRONT_AND_BACK, GL_LINE) }
cdlod_select(CD_LEVELS - 1, 0, 0)
if ter_wire { gl_polygon_mode(GL_FRONT_AND_BACK, GL_FILL) }
@ -602,16 +602,13 @@ var ter_printed: bool = false
# shader places, scales and morphs it. Levels are drawn out to cd_range[k] = 48 * 2^k m,
# so cells are 1 m within 48 m, 2 m to 96 m, 4 m to 192 m ... 256 m at the root.
function cdlod_init() -> void {
let m = new Mesh
m.vao = gl_vao()
let m = gpu_mesh_new()
let n = CD_G + 1
let v = gl_floats(n * n * 2)
var k = 0
for j in 0 .. n { for i in 0 .. n { gl_put_bits(v, k, fr(i, CD_G)); gl_put_bits(v, k + 1, fr(j, CD_G)); k += 2 } }
m.vbo = gl_buffer()
gl_bind_buffer(GL_ARRAY_BUFFER, m.vbo)
gl_buffer_data(GL_ARRAY_BUFFER, gl_bytes_of(n * n * 2), v, GL_STATIC_DRAW)
gl_enable_vertex_attrib_array(0); gl_vertex_attrib_pointer(0, 2, GL_FLOAT, 0, 8, null)
gpu_mesh_vertices(m, v, gl_bytes_of(n * n * 2), GPU_STATIC)
gpu_mesh_attr(m, 0, 2, GPU_F32, 8, 0, false)
free(v)
let ni = CD_G * CD_G * 6
let idx = words(ni)
@ -624,12 +621,10 @@ function cdlod_init() -> void {
k += 6
}
}
m.ebo = gl_buffer()
gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo)
gl_buffer_data(GL_ELEMENT_ARRAY_BUFFER, ni * 4, idx, GL_STATIC_DRAW)
gpu_mesh_indices(m, idx, ni * 4, 4)
free(idx)
m.count = ni
gl_bind_vertex_array(0)
gpu_mesh_done(m)
cd_mesh = m
cd_range = words(CD_LEVELS)
var r = fi(48)
@ -713,7 +708,7 @@ function cdlod_draw(level: int, ix: int, iz: int) -> void {
var st0 = F_ZERO
if level > 0 { st0 = cd_range[level - 1] }
u_f2(gpu_uniform(ter_sun_prog, "u_morph"), f_lerp(st0, cd_range[level], fl(0.7)), cd_range[level])
gl_draw_elements(GL_TRIANGLES, cd_mesh.count, GL_UNSIGNED_INT, null)
gpu_draw_bound_elements(cd_mesh)
return
}
let n = CD_LEAVES >> level
@ -735,7 +730,7 @@ function cdlod_draw(level: int, ix: int, iz: int) -> void {
if level > 0 { start = cd_range[level - 1] }
start = f_lerp(start, cd_range[level], fl(0.7))
u_f2(gpu_uniform(p, "u_morph"), start, cd_range[level])
gl_draw_elements(GL_TRIANGLES, cd_mesh.count, GL_UNSIGNED_INT, null)
gpu_draw_bound_elements(cd_mesh)
cd_draws += 1
}