+ var sprite_index = sprites.index_of(sprite)
+ if sprite_index == -1 then return
+
+ # Vertices data
+
+ var data = local_data_buffer
+ var o = 0
+ for v in [0..4[ do
+ # vec4 translation
+ data[o+ 0] = sprite.center.x
+ data[o+ 1] = sprite.center.y
+ data[o+ 2] = sprite.center.z
+ data[o+ 3] = 0.0
+
+ # vec4 color
+ data[o+ 4] = sprite.tint[0]
+ data[o+ 5] = sprite.tint[1]
+ data[o+ 6] = sprite.tint[2]
+ data[o+ 7] = sprite.tint[3]
+
+ # float scale
+ data[o+ 8] = sprite.scale
+
+ # vec4 coord
+ data[o+ 9] = sprite.texture.vertices[v*3+0]
+ data[o+10] = sprite.texture.vertices[v*3+1]
+ data[o+11] = sprite.texture.vertices[v*3+2]
+ data[o+12] = 0.0
+
+ # vec2 tex_coord
+ var texture = texture
+ if texture != null then
+ var tc = if sprite.invert_x then
+ sprite.texture.texture_coords_invert_x
+ else sprite.texture.texture_coords
+ data[o+13] = tc[v*2+0]
+ data[o+14] = tc[v*2+1]
+ end
+
+ # mat4 rotation
+ var rot
+ if sprite.rotation == 0.0 then
+ # Cache the matrix at no rotation
+ rot = once new Matrix.identity(4)
+ else
+ rot = new Matrix.rotation(sprite.rotation, 0.0, 0.0, 1.0)
+ end
+ data.fill_from_matrix(rot, o+15)
+
+ o += float_per_vertex
+ end
+
+ glBindBuffer(gl_ARRAY_BUFFER, buffer_array)
+ glBufferSubData(gl_ARRAY_BUFFER, sprite_index*bytes_per_sprite, bytes_per_sprite, data.native_array)
+
+ var gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ # Element / indices
+ #
+ # 0--1
+ # | /|
+ # |/ |
+ # 2--3
+
+ var indices = local_indices_buffer
+ var io = sprite_index*4
+ indices[0] = io+0
+ indices[1] = io+2
+ indices[2] = io+1
+ indices[3] = io+1
+ indices[4] = io+2
+ indices[5] = io+3
+
+ glBindBuffer(gl_ELEMENT_ARRAY_BUFFER, buffer_element)
+ glBufferSubData(gl_ELEMENT_ARRAY_BUFFER, sprite_index*6*2, 6*2, indices.native_array)
+
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ end
+
+ # Draw all `sprites`
+ #
+ # Call `resize` and `update_sprite` as needed before actual draw operation.
+ #
+ # Require: `app.simple_2d_program` and `mvp` must be bound on the GPU
+ fun draw
+ do
+ if buffer_array == -1 then prepare
+
+ assert buffer_array > 0 and buffer_element > 0 else
+ print_error "Internal error: {self} was destroyed"
+ end
+
+ # Setup
+ glBindBuffer(gl_ARRAY_BUFFER, buffer_array)
+ glBindBuffer(gl_ELEMENT_ARRAY_BUFFER, buffer_element)
+
+ # Resize GPU buffers?
+ if sprites.capacity > buffer_capacity then
+ # Try to defragment first
+ var moved = sprites.defragment
+
+ if sprites.capacity > buffer_capacity then
+ # Defragmentation wasn't enough, grow
+ resize
+
+ # We must update everything
+ for s in sprites.items do if s != null then sprites_to_update.add s
+ else
+ # Just update the moved sprites
+ for s in moved do sprites_to_update.add s
+ end
+ else if sprites.available.not_empty then
+ # Defragment a bit anyway
+ # TODO defrag only when there's time left on a frame
+ var moved = sprites.defragment(1)
+ for s in moved do sprites_to_update.add s
+ end
+
+ # Update GPU sprites data
+ if sprites_to_update.not_empty then
+ app.perf_clock_sprites.lapse
+
+ for sprite in sprites_to_update do update_sprite(sprite)
+ sprites_to_update.clear
+
+ sys.perfs["gamnit flat gpu update"].add app.perf_clock_sprites.lapse
+ end
+
+ # Update uniforms specific to this context
+ var texture = texture
+ app.simple_2d_program.use_texture.uniform texture != null
+ if texture != null then
+ glActiveTexture gl_TEXTURE0
+ glBindTexture(gl_TEXTURE_2D, texture.gl_texture)
+ app.simple_2d_program.texture.uniform 0
+ end
+ var gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ # Configure attributes, in order:
+ # vec4 translation, vec4 color, float scale, vec4 coord, vec2 tex_coord, vec4 rotation_row*
+ var offset = 0
+ var p = app.simple_2d_program
+ var sizeof_gl_float = 4 # sizeof(GL_FLOAT)
+
+ var size = 4 # Number of floats
+ glEnableVertexAttribArray p.translation.location
+ glVertexAttribPointeri(p.translation.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
+ offset += size * sizeof_gl_float
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ size = 4
+ glEnableVertexAttribArray p.color.location
+ glVertexAttribPointeri(p.color.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
+ offset += size * sizeof_gl_float
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ size = 1
+ glEnableVertexAttribArray p.scale.location
+ glVertexAttribPointeri(p.scale.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
+ offset += size * sizeof_gl_float
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ size = 4
+ glEnableVertexAttribArray p.coord.location
+ glVertexAttribPointeri(p.coord.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
+ offset += size * sizeof_gl_float
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ size = 2
+ glEnableVertexAttribArray p.tex_coord.location
+ glVertexAttribPointeri(p.tex_coord.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
+ offset += size * sizeof_gl_float
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+
+ size = 4
+ for r in [p.rotation_row0, p.rotation_row1, p.rotation_row2, p.rotation_row3] do
+ if r.is_active then
+ glEnableVertexAttribArray r.location
+ glVertexAttribPointeri(r.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
+ end
+ offset += size * sizeof_gl_float
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ end
+
+ # Actual draw
+ for s in sprites.starts, e in sprites.ends do
+ var l = e-s
+ glDrawElementsi(gl_TRIANGLES, l*indices_per_sprite, gl_UNSIGNED_SHORT, 2*s*indices_per_sprite)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ end
+
+ # Take down
+ for attr in [p.translation, p.color, p.scale, p.coord, p.tex_coord,
+ p.rotation_row0, p.rotation_row1, p.rotation_row2, p.rotation_row3: Attribute] do
+ if not attr.is_active then continue
+ glDisableVertexAttribArray(attr.location)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error
+ end
+
+ glBindBuffer(gl_ARRAY_BUFFER, 0)
+ glBindBuffer(gl_ELEMENT_ARRAY_BUFFER, 0)
+ gl_error = glGetError
+ assert gl_error == gl_NO_ERROR else print_error gl_error