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

Property definitions

gamnit $ SpriteContext :: draw
	# 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?
		var update_everything = false
		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
				update_everything = true
				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 or update_everything then
			app.perf_clock_sprites.lapse

			if update_everything then
				for sprite in sprites.items do if sprite != null then
					update_sprite(sprite)
				end
			else
				for sprite in sprites_to_update do update_sprite(sprite)
			end

			sprites_to_update.clear
			last_sprite_to_update = null

			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
		assert glGetError == gl_NO_ERROR

		var animation = animation_texture
		if animation != null then
			glActiveTexture gl_TEXTURE1
			glBindTexture(gl_TEXTURE_2D, animation.gl_texture)
			app.simple_2d_program.animation_texture.uniform 1
		end
		assert glGetError == gl_NO_ERROR

		# Configure attributes, in order:
		# vec4 translation, vec4 color, float scale, vec4 coord, vec2 tex_coord, vec4 rotation_row*,
		# a_fps, a_n_frames, a_coord, a_tex_coord, a_tex_diff, a_start, a_loops

		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
		assert glGetError == gl_NO_ERROR

		size = 4
		glEnableVertexAttribArray p.color.location
		glVertexAttribPointeri(p.color.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		size = 1
		glEnableVertexAttribArray p.scale.location
		glVertexAttribPointeri(p.scale.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		size = 4
		glEnableVertexAttribArray p.coord.location
		glVertexAttribPointeri(p.coord.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_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
		assert glGetError == gl_NO_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
			assert glGetError == gl_NO_ERROR
		end

		size = 1
		glEnableVertexAttribArray p.animation_fps.location
		glVertexAttribPointeri(p.animation_fps.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		size = 1
		glEnableVertexAttribArray p.animation_n_frames.location
		glVertexAttribPointeri(p.animation_n_frames.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		size = 2
		glEnableVertexAttribArray p.animation_coord.location
		glVertexAttribPointeri(p.animation_coord.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		size = 2
		glEnableVertexAttribArray p.animation_tex_coord.location
		glVertexAttribPointeri(p.animation_tex_coord.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		size = 2
		glEnableVertexAttribArray p.animation_tex_diff.location
		glVertexAttribPointeri(p.animation_tex_diff.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		size = 1
		glEnableVertexAttribArray p.animation_start.location
		glVertexAttribPointeri(p.animation_start.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		size = 1
		glEnableVertexAttribArray p.animation_loops.location
		glVertexAttribPointeri(p.animation_loops.location, size, gl_FLOAT, false, bytes_per_vertex, offset)
		offset += size * sizeof_gl_float
		assert glGetError == gl_NO_ERROR

		# 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)
			assert glGetError == gl_NO_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)
			assert glGetError == gl_NO_ERROR
		end

		glBindBuffer(gl_ARRAY_BUFFER, 0)
		glBindBuffer(gl_ELEMENT_ARRAY_BUFFER, 0)
		assert glGetError == gl_NO_ERROR
	end
lib/gamnit/flat/flat_core.nit:1292,2--1477,4