From f7936b38bc852156c257c524e5cfb7abb1300aa9 Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Thu, 26 Sep 2019 14:29:42 -0500 Subject: [PATCH] Ripped IMICFPS::Wavefront::Model to IMICFPS::Model, more refactoring needed. --- i-mic-fps.rb | 5 +- lib/game_objects/entities/player.rb | 2 +- lib/game_objects/model_loader.rb | 2 +- lib/model.rb | 240 ++++++++++++++++++++++++++++ lib/wavefront/model.rb | 221 ------------------------- lib/wavefront/parser.rb | 98 +++++------- 6 files changed, 285 insertions(+), 283 deletions(-) create mode 100644 lib/model.rb delete mode 100644 lib/wavefront/model.rb diff --git a/i-mic-fps.rb b/i-mic-fps.rb index 4f5f1eb..8376c04 100644 --- a/i-mic-fps.rb +++ b/i-mic-fps.rb @@ -76,7 +76,10 @@ require_relative "lib/game_objects/entities/player" require_relative "lib/game_objects/entities/skydome" require_relative "lib/game_objects/entities/terrain" -require_relative "lib/wavefront/model" +require_relative "lib/model" +require_relative "lib/wavefront/parser" +require_relative "lib/wavefront/object" +require_relative "lib/wavefront/material" require_relative "lib/map" require_relative "lib/manifest" diff --git a/lib/game_objects/entities/player.rb b/lib/game_objects/entities/player.rb index 589ff5e..b612fff 100644 --- a/lib/game_objects/entities/player.rb +++ b/lib/game_objects/entities/player.rb @@ -23,7 +23,7 @@ class IMICFPS @devisor = 500.0 @name_image = Gosu::Image.from_text("#{Etc.getlogin}", 100, font: "Consolas", align: :center) # @name_image.save("temp.png") - @name_tex = @name_image.gl_tex_info + # @name_tex = @name_image.gl_tex_info array_of_pixels = @name_image.to_blob tex_names_buf = ' ' * 8 diff --git a/lib/game_objects/model_loader.rb b/lib/game_objects/model_loader.rb index 939ce8b..6336f9a 100644 --- a/lib/game_objects/model_loader.rb +++ b/lib/game_objects/model_loader.rb @@ -21,7 +21,7 @@ class IMICFPS unless load_model_from_cache case @type when :obj - @model = Wavefront::Model.new(file_path: @model_file, entity: entity) + @model = IMICFPS::Model.new(file_path: @model_file, entity: entity, parser: Wavefront::Parser) else raise "Unsupported model type, supported models are: #{@supported_models.join(', ')}" end diff --git a/lib/model.rb b/lib/model.rb new file mode 100644 index 0000000..af17e24 --- /dev/null +++ b/lib/model.rb @@ -0,0 +1,240 @@ +class IMICFPS + class Model + include CommonMethods + + attr_accessor :objects, :materials, :vertices, :uvs, :texures, :normals, :faces, :colors, :bones + attr_accessor :scale, :entity, :material_file, :current_material, :current_object, :vertex_count, :smoothing + attr_reader :position, :bounding_box, :textured_material, :file_path + + attr_reader :vertices_buffer_id + attr_reader :vertices_buffer_data + attr_reader :vertices_buffer_size + attr_reader :vertex_array_id + attr_reader :aabb_tree + + def initialize(file_path:, entity: nil, parser:) + @entity = entity + update if @entity + @file_path = file_path + @material_file = nil + @current_object = nil + @current_material=nil + @vertex_count = 0 + @objects = [] + @materials= {} + @vertices = [] + @colors = [] + @uvs = [] + @normals = [] + @faces = [] + @bones = [] + @smoothing= 0 + + @bounding_box = BoundingBox.new + start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) + + parse(parser) + + puts "#{@file_path.split('/').last} took #{((Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)-start_time)/1000.0).round(2)} seconds to parse" if $debug.get(:stats) + + # allocate_gl_objects + # populate_vertex_buffer + # configure_vao + + @objects.each {|o| @vertex_count+=o.vertices.size} + @objects.each_with_index do |o, i| + puts " Model::Object Name: #{o.name}, Vertices: #{o.vertices.size}" if $debug.get(:stats) + end + window.number_of_vertices+=@vertex_count + @has_texture = false + @materials.each do |key, material| + if material.texture_id + @has_texture = true + @textured_material = key + end + end + + start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) + build_collision_tree + puts " Building mesh collision tree took #{((Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)-start_time)/1000.0).round(2)} seconds" if $debug.get(:stats) + end + + def parse(parser) + parser.new(self).parse + end + + def calculate_bounding_box(vertices, bounding_box) + unless bounding_box.min.x.is_a?(Float) + vertex = vertices.last + bounding_box.min.x = vertex.x + bounding_box.min.y = vertex.y + bounding_box.min.z = vertex.z + + bounding_box.max.x = vertex.x + bounding_box.max.y = vertex.y + bounding_box.max.z = vertex.z + end + + vertices.each do |vertex| + bounding_box.min.x = vertex.x if vertex.x <= bounding_box.min.x + bounding_box.min.y = vertex.y if vertex.y <= bounding_box.min.y + bounding_box.min.z = vertex.z if vertex.z <= bounding_box.min.z + + bounding_box.max.x = vertex.x if vertex.x >= bounding_box.max.x + bounding_box.max.y = vertex.y if vertex.y >= bounding_box.max.y + bounding_box.max.z = vertex.z if vertex.z >= bounding_box.max.z + end + end + + def allocate_gl_objects + # Allocate arrays for future use + @vertex_array_id = nil + buffer = " " * 4 + + glGenVertexArrays(1, buffer) + @vertex_array_id = buffer.unpack('L2').first + + # Allocate buffers for future use + @vertices_buffer_id = nil + buffer = " " * 4 + + glGenBuffers(1, buffer) + @vertices_buffer_id = buffer.unpack('L2').first + end + + def populate_vertex_buffer + @vertices_buffer_size = 0 + @vertices_buffer_data = [] + + verts = [] + colors = [] + norms = [] + uvs = [] + tex_ids = [] + + @faces.each do |face| + verts << face.vertices.map { |vert| [vert.x, vert.y, vert.z] } + colors << face.colors.map { |vert| [vert.x, vert.y, vert.z] } + norms << face.normals.map { |vert| [vert.x, vert.y, vert.z, vert.weight] } + + if @has_texture + uvs << face.uvs.map { |vert| [vert.x, vert.y, vert.z] } + tex_ids << face.material.texture_id ? face.material.texture_id.to_f : -1.0 + end + end + + verts.each_with_index do |vert, i| + @vertices_buffer_data << vert + @vertices_buffer_data << colors[i] + @vertices_buffer_data << norms[i] + + # if @has_texture + # @vertices_buffer_data << uvs[i] if uvs.size > 0 + # @vertices_buffer_data << tex_ids[i] if tex_ids.size > 0 + # end + end + + data_size = 0 + data_size += Fiddle::SIZEOF_FLOAT * 3 * verts.size + data_size += Fiddle::SIZEOF_FLOAT * 3 * colors.size + data_size += Fiddle::SIZEOF_FLOAT * 4 * norms.size + + if @has_texture + data_size += Fiddle::SIZEOF_FLOAT * 3 * uvs.size + data_size += Fiddle::SIZEOF_FLOAT * 1 * tex_ids.size + end + + @vertices_buffer_size = data_size + + data = @vertices_buffer_data.flatten + + glBindBuffer(GL_ARRAY_BUFFER, @vertices_buffer_id) + glBufferData(GL_ARRAY_BUFFER, @vertices_buffer_size, data.pack("f*"), GL_STATIC_DRAW) + glBindBuffer(GL_ARRAY_BUFFER, 0) + end + + def configure_vao + glBindBuffer(GL_ARRAY_BUFFER, @vertices_buffer_id) + glBindVertexArray(@vertex_array_id) + + glEnableVertexAttribArray(0) + glEnableVertexAttribArray(1) + glEnableVertexAttribArray(2) + glEnableVertexAttribArray(3) + glEnableVertexAttribArray(4) + + program = Shader.get("default").program + + stride = 0 + position_stride = Fiddle::SIZEOF_FLOAT * 3 + color_stride = Fiddle::SIZEOF_FLOAT * 3 + normal_stride = Fiddle::SIZEOF_FLOAT * 4 + uv_stride = Fiddle::SIZEOF_FLOAT * 3 + texture_id_stride = Fiddle::SIZEOF_FLOAT + + if @has_texture + stride = position_stride + color_stride + normal_stride + uv_stride + texture_id_stride + else + stride = position_stride + color_stride + normal_stride + end + + # index, size, type, normalized, stride, pointer + # vertices (positions) + glVertexAttribPointer(glGetAttribLocation(program, "inPosition"), 3, GL_FLOAT, GL_FALSE, stride, nil) + handleGlError + # colors + glVertexAttribPointer(glGetAttribLocation(program, "inColor"), 3, GL_FLOAT, GL_FALSE, stride + position_stride, nil) + handleGlError + # normals + glVertexAttribPointer(glGetAttribLocation(program, "inNormal"), 4, GL_FLOAT, GL_FALSE, stride + position_stride + color_stride, nil) + handleGlError + # uvs + glVertexAttribPointer(glGetAttribLocation(program, "inUV"), 3, GL_FLOAT, GL_FALSE, stride + position_stride + color_stride + normal_stride, nil) + handleGlError + # texture ids + glVertexAttribPointer(glGetAttribLocation(program, "inTextureID"), 1, GL_FLOAT, GL_FALSE, stride + position_stride + color_stride + normal_stride + uv_stride, nil) + handleGlError + + glDisableVertexAttribArray(4) + glDisableVertexAttribArray(3) + glDisableVertexAttribArray(2) + glDisableVertexAttribArray(1) + glDisableVertexAttribArray(0) + + glBindBuffer(GL_ARRAY_BUFFER, 0) + glBindVertexArray(0) + end + + def build_collision_tree + @aabb_tree = AABBTree.new + @faces.each do |face| + box = BoundingBox.new + box.min = face.vertices.first.dup + box.max = face.vertices.first.dup + + face.vertices.each do |vertex| + if vertex.sum < box.min.sum + box.min = vertex.dup + elsif vertex.sum > box.max.sum + box.max = vertex.dup + end + end + + # FIXME: Handle negatives + box.min -= Vector.new(-0.1, -0.1, -0.1) + box.max += Vector.new( 0.1, 0.1, 0.1) + @aabb_tree.insert(face, box) + end + puts @aabb_tree.inspect if $debug.get(:stats) + end + + def update + @position = @entity.position + @scale = @entity.scale + end + + def has_texture? + @has_texture + end + end +end diff --git a/lib/wavefront/model.rb b/lib/wavefront/model.rb deleted file mode 100644 index 938c99d..0000000 --- a/lib/wavefront/model.rb +++ /dev/null @@ -1,221 +0,0 @@ -require_relative "parser" -require_relative "object" -require_relative "material" - -class IMICFPS - class Wavefront - class Model - include CommonMethods - - include Parser - - attr_accessor :objects, :materials, :vertices, :texures, :normals, :faces, :colors - attr_accessor :scale, :entity - attr_reader :position, :bounding_box, :textured_material - - attr_reader :vertices_buffer_id - attr_reader :vertices_buffer_data - attr_reader :vertices_buffer_size - attr_reader :vertex_array_id - attr_reader :aabb_tree - - def initialize(file_path:, entity: nil) - @entity = entity - update if @entity - @file_path = file_path - @file = File.open(file_path, 'r') - @material_file = nil - @current_object = nil - @current_material=nil - @vertex_count = 0 - @objects = [] - @materials= {} - @vertices = [] - @colors = [] - @uvs = [] - @normals = [] - @faces = [] - @smoothing= 0 - - @bounding_box = BoundingBox.new - start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - - parse - - puts "#{@file_path.split('/').last} took #{((Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)-start_time)/1000.0).round(2)} seconds to parse" if $debug.get(:stats) - - # allocate_gl_objects - # populate_vertex_buffer - # configure_vao - - @objects.each {|o| @vertex_count+=o.vertices.size} - @objects.each_with_index do |o, i| - puts " Model::Object Name: #{o.name}, Vertices: #{o.vertices.size}" if $debug.get(:stats) - end - window.number_of_vertices+=@vertex_count - @has_texture = false - @materials.each do |key, material| - if material.texture_id - @has_texture = true - @textured_material = key - end - end - - start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - build_collision_tree - puts " Building mesh collision tree took #{((Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)-start_time)/1000.0).round(2)} seconds" if $debug.get(:stats) - end - - def allocate_gl_objects - # Allocate arrays for future use - @vertex_array_id = nil - buffer = " " * 4 - - glGenVertexArrays(1, buffer) - @vertex_array_id = buffer.unpack('L2').first - - # Allocate buffers for future use - @vertices_buffer_id = nil - buffer = " " * 4 - - glGenBuffers(1, buffer) - @vertices_buffer_id = buffer.unpack('L2').first - end - - def populate_vertex_buffer - @vertices_buffer_size = 0 - @vertices_buffer_data = [] - - verts = [] - colors = [] - norms = [] - uvs = [] - tex_ids = [] - - @faces.each do |face| - verts << face.vertices.map { |vert| [vert.x, vert.y, vert.z] } - colors << face.colors.map { |vert| [vert.x, vert.y, vert.z] } - norms << face.normals.map { |vert| [vert.x, vert.y, vert.z, vert.weight] } - - if @has_texture - uvs << face.uvs.map { |vert| [vert.x, vert.y, vert.z] } - tex_ids << face.material.texture_id ? face.material.texture_id.to_f : -1.0 - end - end - - verts.each_with_index do |vert, i| - @vertices_buffer_data << vert - @vertices_buffer_data << colors[i] - @vertices_buffer_data << norms[i] - - # if @has_texture - # @vertices_buffer_data << uvs[i] if uvs.size > 0 - # @vertices_buffer_data << tex_ids[i] if tex_ids.size > 0 - # end - end - - data_size = 0 - data_size += Fiddle::SIZEOF_FLOAT * 3 * verts.size - data_size += Fiddle::SIZEOF_FLOAT * 3 * colors.size - data_size += Fiddle::SIZEOF_FLOAT * 4 * norms.size - - if @has_texture - data_size += Fiddle::SIZEOF_FLOAT * 3 * uvs.size - data_size += Fiddle::SIZEOF_FLOAT * 1 * tex_ids.size - end - - @vertices_buffer_size = data_size - - data = @vertices_buffer_data.flatten - - glBindBuffer(GL_ARRAY_BUFFER, @vertices_buffer_id) - glBufferData(GL_ARRAY_BUFFER, @vertices_buffer_size, data.pack("f*"), GL_STATIC_DRAW) - glBindBuffer(GL_ARRAY_BUFFER, 0) - end - - def configure_vao - glBindBuffer(GL_ARRAY_BUFFER, @vertices_buffer_id) - glBindVertexArray(@vertex_array_id) - - glEnableVertexAttribArray(0) - glEnableVertexAttribArray(1) - glEnableVertexAttribArray(2) - glEnableVertexAttribArray(3) - glEnableVertexAttribArray(4) - - program = Shader.get("default").program - - stride = 0 - position_stride = Fiddle::SIZEOF_FLOAT * 3 - color_stride = Fiddle::SIZEOF_FLOAT * 3 - normal_stride = Fiddle::SIZEOF_FLOAT * 4 - uv_stride = Fiddle::SIZEOF_FLOAT * 3 - texture_id_stride = Fiddle::SIZEOF_FLOAT - - if @has_texture - stride = position_stride + color_stride + normal_stride + uv_stride + texture_id_stride - else - stride = position_stride + color_stride + normal_stride - end - - # index, size, type, normalized, stride, pointer - # vertices (positions) - glVertexAttribPointer(glGetAttribLocation(program, "inPosition"), 3, GL_FLOAT, GL_FALSE, stride, nil) - handleGlError - # colors - glVertexAttribPointer(glGetAttribLocation(program, "inColor"), 3, GL_FLOAT, GL_FALSE, stride + position_stride, nil) - handleGlError - # normals - glVertexAttribPointer(glGetAttribLocation(program, "inNormal"), 4, GL_FLOAT, GL_FALSE, stride + position_stride + color_stride, nil) - handleGlError - # uvs - glVertexAttribPointer(glGetAttribLocation(program, "inUV"), 3, GL_FLOAT, GL_FALSE, stride + position_stride + color_stride + normal_stride, nil) - handleGlError - # texture ids - glVertexAttribPointer(glGetAttribLocation(program, "inTextureID"), 1, GL_FLOAT, GL_FALSE, stride + position_stride + color_stride + normal_stride + uv_stride, nil) - handleGlError - - glDisableVertexAttribArray(4) - glDisableVertexAttribArray(3) - glDisableVertexAttribArray(2) - glDisableVertexAttribArray(1) - glDisableVertexAttribArray(0) - - glBindBuffer(GL_ARRAY_BUFFER, 0) - glBindVertexArray(0) - end - - def build_collision_tree - @aabb_tree = AABBTree.new - @faces.each do |face| - box = BoundingBox.new - box.min = face.vertices.first.dup - box.max = face.vertices.first.dup - - face.vertices.each do |vertex| - if vertex.sum < box.min.sum - box.min = vertex.dup - elsif vertex.sum > box.max.sum - box.max = vertex.dup - end - end - - # FIXME: Handle negatives - box.min -= Vector.new(-0.1, -0.1, -0.1) - box.max += Vector.new( 0.1, 0.1, 0.1) - @aabb_tree.insert(face, box) - end - puts @aabb_tree.inspect if $debug.get(:stats) - end - - def update - @position = @entity.position - @scale = @entity.scale - end - - def has_texture? - @has_texture - end - end - end -end diff --git a/lib/wavefront/parser.rb b/lib/wavefront/parser.rb index da288e6..c307282 100644 --- a/lib/wavefront/parser.rb +++ b/lib/wavefront/parser.rb @@ -1,10 +1,13 @@ class IMICFPS class Wavefront - module Parser + class Parser + def initialize(model) + @model = model + end + def parse lines = 0 - list = @file.read.split("\n") - # @file.each_line do |line| + list = File.read(@model.file_path).split("\n") list.each do |line| lines+=1 line = line.strip @@ -12,7 +15,7 @@ class IMICFPS array = line.split(' ') case array[0] when 'mtllib' - @material_file = array[1] + @model.material_file = array[1] parse_mtllib when 'usemtl' set_material(array[1]) @@ -44,7 +47,7 @@ class IMICFPS face.normals = [] face.colors = [] face.material = material - face.smoothing= @smoothing + face.smoothing= @model.smoothing mat = face.material.diffuse color = Vector.new(mat.red, mat.green, mat.blue) @@ -52,85 +55,85 @@ class IMICFPS verts.each_with_index do |v, index| if uvs.first != "" - face.vertices << @vertices[Integer(v)-1] - face.uvs << @uvs[Integer(uvs[index])-1] - face.normals << @normals[Integer(norms[index])-1] + face.vertices << @model.vertices[Integer(v)-1] + face.uvs << @model.uvs[Integer(uvs[index])-1] + face.normals << @model.normals[Integer(norms[index])-1] face.colors << color else - face.vertices << @vertices[Integer(v)-1] + face.vertices << @model.vertices[Integer(v)-1] face.uvs << nil - face.normals << @normals[Integer(norms[index])-1] + face.normals << @model.normals[Integer(norms[index])-1] face.colors << color end end - @current_object.faces << face - @faces << face + @model.current_object.faces << face + @model.faces << face end end puts "Total Lines: #{lines}" if $debug.get(:stats) - calculate_bounding_box(@vertices, @bounding_box) - @objects.each do |o| - calculate_bounding_box(o.vertices, o.bounding_box) + @model.calculate_bounding_box(@model.vertices, @model.bounding_box) + @model.objects.each do |o| + @model.calculate_bounding_box(o.vertices, o.bounding_box) end end def parse_mtllib - file = File.open(@file_path.sub(File.basename(@file_path), '')+@material_file, 'r') + file = File.open(@model.file_path.sub(File.basename(@model.file_path), '')+@model.material_file, 'r') file.readlines.each do |line| array = line.strip.split(' ') case array.first when 'newmtl' material = Material.new(array.last) - @current_material = array.last - @materials[array.last] = material + @model.current_material = array.last + @model.materials[array.last] = material when 'Ns' # Specular Exponent when 'Ka' # Ambient color - @materials[@current_material].ambient = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) + @model.materials[@model.current_material].ambient = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) when 'Kd' # Diffuse color - @materials[@current_material].diffuse = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) + @model.materials[@model.current_material].diffuse = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) when 'Ks' # Specular color - @materials[@current_material].specular = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) + @model.materials[@model.current_material].specular = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) when 'Ke' # Emissive when 'Ni' # Unknown (Blender Specific?) when 'd' # Dissolved (Transparency) when 'illum' # Illumination model when 'map_Kd' # Diffuse texture - @materials[@current_material].set_texture(array[1]) + @model.materials[@model.current_material].set_texture(array[1]) end end end def change_object(name) - @objects << Object.new(name) - @current_object = @objects.last + @model.objects << Object.new(name) + @model.current_object = @model.objects.last end def set_smoothing(value) if value == "1" - @smoothing = true + @model.smoothing = true else - @smoothing = false + @model.smoothing = false end end def set_material(name) - @current_material = name + @model.current_material = name end def material - @materials[@current_material] + @model.materials[@model.current_material] end def faces_count count = 0 - @objects.each {|o| count+=o.faces.count} + @model.objects.each {|o| count+=o.faces.count} return count end def add_vertex(array) - @vertex_count+=1 + @model.vertex_count+=1 vert = nil if array.size == 5 vert = Vector.new(Float(array[1]), Float(array[2]), Float(array[3]), Float(array[4])) @@ -139,8 +142,8 @@ class IMICFPS else raise end - @current_object.vertices << vert - @vertices << vert + @model.current_object.vertices << vert + @model.vertices << vert end def add_normal(array) @@ -152,8 +155,8 @@ class IMICFPS else raise end - @current_object.normals << vert - @normals << vert + @model.current_object.normals << vert + @model.normals << vert end def add_texture_coordinate(array) @@ -165,31 +168,8 @@ class IMICFPS else raise end - @current_object.textures << texture - @uvs << texture - end - - def calculate_bounding_box(vertices, bounding_box) - unless bounding_box.min.x.is_a?(Float) - vertex = vertices.last - bounding_box.min.x = vertex.x - bounding_box.min.y = vertex.y - bounding_box.min.z = vertex.z - - bounding_box.max.x = vertex.x - bounding_box.max.y = vertex.y - bounding_box.max.z = vertex.z - end - - vertices.each do |vertex| - bounding_box.min.x = vertex.x if vertex.x <= bounding_box.min.x - bounding_box.min.y = vertex.y if vertex.y <= bounding_box.min.y - bounding_box.min.z = vertex.z if vertex.z <= bounding_box.min.z - - bounding_box.max.x = vertex.x if vertex.x >= bounding_box.max.x - bounding_box.max.y = vertex.y if vertex.y >= bounding_box.max.y - bounding_box.max.z = vertex.z if vertex.z >= bounding_box.max.z - end + @model.current_object.textures << texture + @model.uvs << texture end end end