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:
parent
3fb1b7cd87
commit
43c379aa0f
8 changed files with 271 additions and 267 deletions
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue