Added a large portion of I-MIC-FPS's opengl rendering and model loading systems

This commit is contained in:
2020-07-15 21:19:31 -05:00
parent d7dbcf8511
commit 041cfcccaa
22 changed files with 1724 additions and 3 deletions

View File

@@ -0,0 +1,248 @@
module CyberarmEngine
class BoundingBoxRenderer
attr_reader :bounding_boxes, :vertex_count
def initialize
@bounding_boxes = {}
@vertex_count = 0
end
def render(entities)
entities.each do |entity|
create_bounding_box(entity,color = nil)
draw_bounding_boxes
end
(@bounding_boxes.keys - entities.map { |e| e.object_id }).each do |key|
@bounding_boxes.delete(key)
end
end
def create_bounding_box(entity, color = nil)
color ||= entity.debug_color
entity_id = entity.object_id
if @bounding_boxes[entity_id]
if @bounding_boxes[entity_id][:color] != color
@bounding_boxes[entity_id][:colors] = mesh_colors(color).pack("f*")
@bounding_boxes[entity_id][:color] = color
return
else
return
end
end
@bounding_boxes[entity_id] = {
entity: entity,
color: color,
objects: []
}
box = entity.normalize_bounding_box
normals = mesh_normals
colors = mesh_colors(color)
vertices = mesh_vertices(box)
@vertex_count+=vertices.size
@bounding_boxes[entity_id][:vertices_size] = vertices.size
@bounding_boxes[entity_id][:vertices] = vertices.pack("f*")
@bounding_boxes[entity_id][:normals] = normals.pack("f*")
@bounding_boxes[entity_id][:colors] = colors.pack("f*")
entity.model.objects.each do |mesh|
data = {}
box = mesh.bounding_box.normalize(entity)
normals = mesh_normals
colors = mesh_colors(mesh.debug_color)
vertices = mesh_vertices(box)
@vertex_count+=vertices.size
data[:vertices_size] = vertices.size
data[:vertices] = vertices.pack("f*")
data[:normals] = normals.pack("f*")
data[:colors] = colors.pack("f*")
@bounding_boxes[entity_id][:objects] << data
end
end
def mesh_normals
[
0,1,0,
0,1,0,
0,1,0,
0,1,0,
0,1,0,
0,1,0,
0,-1,0,
0,-1,0,
0,-1,0,
0,-1,0,
0,-1,0,
0,-1,0,
0,0,1,
0,0,1,
0,0,1,
0,0,1,
0,0,1,
0,0,1,
1,0,0,
1,0,0,
1,0,0,
1,0,0,
1,0,0,
1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0
]
end
def mesh_colors(color)
[
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue
]
end
def mesh_vertices(box)
[
box.min.x, box.max.y, box.max.z,
box.min.x, box.max.y, box.min.z,
box.max.x, box.max.y, box.min.z,
box.min.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.min.z,
box.max.x, box.min.y, box.min.z,
box.max.x, box.min.y, box.max.z,
box.min.x, box.min.y, box.max.z,
box.max.x, box.min.y, box.min.z,
box.min.x, box.min.y, box.min.z,
box.min.x, box.min.y, box.max.z,
box.min.x, box.max.y, box.max.z,
box.min.x, box.max.y, box.min.z,
box.min.x, box.min.y, box.min.z,
box.min.x, box.min.y, box.max.z,
box.min.x, box.min.y, box.min.z,
box.min.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.min.z,
box.max.x, box.min.y, box.min.z,
box.max.x, box.min.y, box.max.z,
box.max.x, box.min.y, box.min.z,
box.max.x, box.max.y, box.max.z,
box.min.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.max.z,
box.max.x, box.min.y, box.max.z,
box.min.x, box.max.y, box.max.z,
box.max.x, box.min.y, box.max.z,
box.min.x, box.min.y, box.max.z,
box.max.x, box.min.y, box.min.z,
box.min.x, box.min.y, box.min.z,
box.min.x, box.max.y, box.min.z,
box.max.x, box.min.y, box.min.z,
box.min.x, box.max.y, box.min.z,
box.max.x, box.max.y, box.min.z
]
end
def draw_bounding_boxes
@bounding_boxes.each do |key, bounding_box|
glPushMatrix
glTranslatef(
bounding_box[:entity].position.x,
bounding_box[:entity].position.y,
bounding_box[:entity].position.z
)
draw_bounding_box(bounding_box)
@bounding_boxes[key][:objects].each {|o| draw_bounding_box(o)}
glPopMatrix
end
end
def draw_bounding_box(bounding_box)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
glVertexPointer(3, GL_FLOAT, 0, bounding_box[:vertices])
glColorPointer(3, GL_FLOAT, 0, bounding_box[:colors])
glNormalPointer(GL_FLOAT, 0, bounding_box[:normals])
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
glDisable(GL_LIGHTING)
glDrawArrays(GL_TRIANGLES, 0, bounding_box[:vertices_size] / 3)
glEnable(GL_LIGHTING)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
end
end
end

View File

@@ -0,0 +1,162 @@
module CyberarmEngine
class GBuffer
attr_reader :screen_vbo, :vertices, :uvs
def initialize(width:, height:)
@width, @height = width, height
@framebuffer = nil
@buffers = [:position, :diffuse, :normal, :texcoord]
@textures = {}
@screen_vbo = nil
@ready = false
@vertices = [
-1.0, -1.0, 0,
1.0, -1.0, 0,
-1.0, 1.0, 0,
-1.0, 1.0, 0,
1.0, -1.0, 0,
1.0, 1.0, 0,
].freeze
@uvs = [
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1
].freeze
create_framebuffer
create_screen_vbo
end
def create_framebuffer
buffer = ' ' * 4
glGenFramebuffers(1, buffer)
@framebuffer = buffer.unpack('L2').first
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, @framebuffer)
create_textures
status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
if status != GL_FRAMEBUFFER_COMPLETE
message = ""
case status
when GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
message = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"
when GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
message = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"
when GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
message = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"
when GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
message = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"
when GL_FRAMEBUFFER_UNSUPPORTED
message = "GL_FRAMEBUFFER_UNSUPPORTED"
else
message = "Unknown error!"
end
puts "Incomplete framebuffer: #{status}\nError: #{message}"
else
@ready = true
end
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
end
def create_textures
@buffers.size.times do |i|
buffer = ' ' * 4
glGenTextures(1, buffer)
texture_id = buffer.unpack('L2').first
@textures[@buffers[i]] = texture_id
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, @width, @height, 0, GL_RGBA, GL_FLOAT, nil)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, texture_id, 0)
end
buffer = ' ' * 4
glGenTextures(1, buffer)
texture_id = buffer.unpack('L2').first
@textures[:depth] = texture_id
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, @width, @height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nil)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture_id, 0)
draw_buffers = [ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 ]
glDrawBuffers(draw_buffers.size, draw_buffers.pack("I*"))
end
def create_screen_vbo
buffer = ' ' * 4
glGenVertexArrays(1, buffer)
@screen_vbo = buffer.unpack('L2').first
buffer = " " * 4
glGenBuffers(1, buffer)
@positions_buffer_id = buffer.unpack('L2').first
buffer = " " * 4
glGenBuffers(1, buffer)
@uvs_buffer_id = buffer.unpack('L2').first
glBindVertexArray(@screen_vbo)
glBindBuffer(GL_ARRAY_BUFFER, @positions_buffer_id)
glBufferData(GL_ARRAY_BUFFER, @vertices.size * Fiddle::SIZEOF_FLOAT, @vertices.pack("f*"), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nil)
glBindBuffer(GL_ARRAY_BUFFER, @uvs_buffer_id)
glBufferData(GL_ARRAY_BUFFER, @uvs.size * Fiddle::SIZEOF_FLOAT, @uvs.pack("f*"), GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nil)
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glBindVertexArray(0)
end
def bind_for_writing
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, @framebuffer)
end
def bind_for_reading
glBindFramebuffer(GL_READ_FRAMEBUFFER, @framebuffer)
end
def set_read_buffer(buffer)
glReadBuffer(GL_COLOR_ATTACHMENT0 + @textures.keys.index(buffer))
end
def set_read_buffer_depth
glReadBuffer(GL_DEPTH_ATTACHMENT)
end
def unbind_framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0)
end
def texture(type)
@textures[type]
end
def clean_up
glDeleteFramebuffers(1, [@framebuffer].pack("L"))
glDeleteTextures(@textures.values.size, @textures.values.pack("L*"))
glDeleteBuffers(2, [@positions_buffer_id, @uvs_buffer_id].pack("L*"))
glDeleteVertexArrays(1, [@screen_vbo].pack("L"))
gl_error?
end
end
end

View File

@@ -0,0 +1,285 @@
module CyberarmEngine
class OpenGLRenderer
@@immediate_mode_warning = false
attr_accessor :show_wireframe
def initialize(width:, height:, show_wireframe: false)
@width, @height = width, height
@show_wireframe = show_wireframe
@g_buffer = GBuffer.new(width: @width, height: @height)
end
def canvas_size_changed
@g_buffer.unbind_framebuffer
@g_buffer.clean_up
@g_buffer = GBuffer.new(width: @width, height: @height)
end
def render(camera, lights, entities)
glViewport(0, 0, @width, @height)
glEnable(GL_DEPTH_TEST)
if Shader.available?("g_buffer") && Shader.available?("lighting")
@g_buffer.bind_for_writing
gl_error?
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
Shader.use("g_buffer") do |shader|
gl_error?
entities.each do |entity|
next unless entity.visible && entity.renderable
shader.uniform_transform("projection", camera.projection_matrix)
shader.uniform_transform("view", camera.view_matrix)
shader.uniform_transform("model", entity.model_matrix)
shader.uniform_vec3("cameraPosition", camera.position)
gl_error?
draw_model(entity.model, shader)
entity.draw
end
end
@g_buffer.unbind_framebuffer
gl_error?
@g_buffer.bind_for_reading
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
lighting(lights)
gl_error?
post_processing
gl_error?
# render_framebuffer
gl_error?
@g_buffer.unbind_framebuffer
gl_error?
else
puts "Shaders are disabled or failed to compile, using immediate mode for rendering..." unless @@immediate_mode_warning
@@immediate_mode_warning = true
gl_error?
lights.each(&:draw)
camera.draw
glEnable(GL_NORMALIZE)
entities.each do |entity|
next unless entity.visible && entity.renderable
glPushMatrix
glTranslatef(entity.position.x, entity.position.y, entity.position.z)
glScalef(entity.scale.x, entity.scale.y, entity.scale.z)
glRotatef(entity.orientation.x, 1.0, 0, 0)
glRotatef(entity.orientation.y, 0, 1.0, 0)
glRotatef(entity.orientation.z, 0, 0, 1.0)
gl_error?
draw_mesh(entity.model)
entity.draw
glPopMatrix
end
end
gl_error?
end
def copy_g_buffer_to_screen
@g_buffer.set_read_buffer(:position)
glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
0, 0, @g_buffer.width / 2, @g_buffer.height / 2,
GL_COLOR_BUFFER_BIT, GL_LINEAR)
@g_buffer.set_read_buffer(:diffuse)
glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
0, @g_buffer.height / 2, @g_buffer.width / 2, @g_buffer.height,
GL_COLOR_BUFFER_BIT, GL_LINEAR)
@g_buffer.set_read_buffer(:normal)
glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
@g_buffer.width / 2, @g_buffer.height / 2, @g_buffer.width, @g_buffer.height,
GL_COLOR_BUFFER_BIT, GL_LINEAR)
@g_buffer.set_read_buffer(:texcoord)
glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
@g_buffer.width / 2, 0, @g_buffer.width, @g_buffer.height / 2,
GL_COLOR_BUFFER_BIT, GL_LINEAR)
end
def lighting(lights)
Shader.use("lighting") do |shader|
glBindVertexArray(@g_buffer.screen_vbo)
glDisable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:diffuse))
shader.uniform_integer("diffuse", 0)
glActiveTexture(GL_TEXTURE1)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:position))
shader.uniform_integer("position", 1)
glActiveTexture(GL_TEXTURE2)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:texcoord))
shader.uniform_integer("texcoord", 2)
glActiveTexture(GL_TEXTURE3)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:normal))
shader.uniform_integer("normal", 3)
glActiveTexture(GL_TEXTURE4)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:depth))
shader.uniform_integer("depth", 4)
lights.each_with_index do |light, i|
shader.uniform_integer("light[0].type", light.type);
shader.uniform_vec3("light[0].direction", light.direction)
shader.uniform_vec3("light[0].position", light.position)
shader.uniform_vec3("light[0].diffuse", light.diffuse)
shader.uniform_vec3("light[0].ambient", light.ambient)
shader.uniform_vec3("light[0].specular", light.specular)
glDrawArrays(GL_TRIANGLES, 0, @g_buffer.vertices.size)
end
glBindVertexArray(0)
end
end
def post_processing
end
def render_framebuffer
if Shader.available?("lighting")
Shader.use("lighting") do |shader|
glBindVertexArray(@g_buffer.screen_vbo)
glDisable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:diffuse))
shader.uniform_integer("diffuse_texture", 0)
glDrawArrays(GL_TRIANGLES, 0, @g_buffer.vertices.size)
glBindVertexArray(0)
end
end
end
def draw_model(model, shader)
glBindVertexArray(model.vertex_array_id)
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glEnableVertexAttribArray(2)
if model.has_texture?
glEnableVertexAttribArray(3)
glEnableVertexAttribArray(4)
end
if @show_wireframe
glLineWidth(2)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
Shader.active_shader.uniform_boolean("disableLighting", true)
glDrawArrays(GL_TRIANGLES, 0, model.faces.count * 3)
Shader.active_shader.uniform_boolean("disableLighting", false)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
glLineWidth(1)
end
offset = 0
model.objects.each do |object|
shader.uniform_boolean("hasTexture", object.has_texture?)
if object.has_texture?
glBindTexture(GL_TEXTURE_2D, object.materials.find { |mat| mat.texture_id }.texture_id)
else
glBindTexture(GL_TEXTURE_2D, 0)
end
glDrawArrays(GL_TRIANGLES, offset, object.faces.count * 3)
offset += object.faces.count * 3
end
if model.has_texture?
glDisableVertexAttribArray(4)
glDisableVertexAttribArray(3)
glBindTexture(GL_TEXTURE_2D, 0)
end
glDisableVertexAttribArray(2)
glDisableVertexAttribArray(1)
glDisableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
end
def draw_mesh(model)
model.objects.each_with_index do |o, i|
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
glShadeModel(GL_FLAT) unless o.faces.first[4]
glShadeModel(GL_SMOOTH) if o.faces.first[4]
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
if o.has_texture?
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, o.materials.find { |mat| mat.texture_id }.texture_id)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glTexCoordPointer(3, GL_FLOAT, 0, o.flattened_uvs)
end
glVertexPointer(4, GL_FLOAT, 0, o.flattened_vertices)
glColorPointer(3, GL_FLOAT, 0, o.flattened_materials)
glNormalPointer(GL_FLOAT, 0, o.flattened_normals)
if @show_wireframe # This is kinda expensive
glDisable(GL_LIGHTING)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
glPolygonOffset(2, 0.5)
glLineWidth(3)
glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size/4)
glLineWidth(1)
glPolygonOffset(0, 0)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
glEnable(GL_LIGHTING)
glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size/4)
else
glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size/4)
end
# glBindBuffer(GL_ARRAY_BUFFER, 0)
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
if o.has_texture?
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
glDisable(GL_TEXTURE_2D)
end
glDisable(GL_COLOR_MATERIAL)
end
end
end
end

View File

@@ -0,0 +1,22 @@
module CyberarmEngine
class Renderer
attr_reader :opengl_renderer, :bounding_box_renderer
def initialize
@bounding_box_renderer = BoundingBoxRenderer.new
@opengl_renderer = OpenGLRenderer.new(width: $window.width, height: $window.height)
end
def draw(camera, lights, entities)
@opengl_renderer.render(camera, lights, entities)
@bounding_box_renderer.render(entities) if @show_bounding_boxes
end
def canvas_size_changed
@opengl_renderer.canvas_size_changed
end
def finalize # cleanup
end
end
end