From a46f3deff96ad87cbbaf9a5e513ed3d89ae15f60 Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Mon, 19 Mar 2018 14:35:13 -0500 Subject: [PATCH] Moved OBJ parser out of Model, added axis-aligned bounding boxes, added --debug, bounding boxes can be rendered. --- i-mic-fps.rb | 3 + lib/object.rb | 134 -------------------------- lib/objects/light.rb | 10 +- lib/wavefront/model.rb | 202 ++++++++++++++-------------------------- lib/wavefront/object.rb | 3 +- lib/wavefront/parser.rb | 176 ++++++++++++++++++++++++++++++++++ lib/window.rb | 14 +-- 7 files changed, 261 insertions(+), 281 deletions(-) delete mode 100644 lib/object.rb create mode 100644 lib/wavefront/parser.rb diff --git a/i-mic-fps.rb b/i-mic-fps.rb index 6598c40..0a4d437 100644 --- a/i-mic-fps.rb +++ b/i-mic-fps.rb @@ -16,7 +16,10 @@ else raise RuntimeError, "Unsupported platform." end +BoundingBox = Struct.new(:min_x, :min_y, :min_z, :max_x, :max_y, :max_z) + require_relative "lib/objects/light" +require_relative "lib/wavefront/parser" require_relative "lib/wavefront/model" require_relative "lib/wavefront/object" require_relative "lib/wavefront/material" diff --git a/lib/object.rb b/lib/object.rb deleted file mode 100644 index 3d6309a..0000000 --- a/lib/object.rb +++ /dev/null @@ -1,134 +0,0 @@ -class IMICFPS - class Object - include GL - include GLU - TextureCoordinate = Struct.new(:u, :v, :weight) - Vertex = Struct.new(:x, :y, :z, :weight) - Color = Struct.new(:red, :green, :blue) - - attr_accessor :vertexes, :texures, :normals, :faces - - def initialize(object = "objects/cube.obj") - @object_path = object - @file = File.open(object, 'r') - @material_file = nil - @vertexes = [] - @textures = [] - @normals = [] - @faces = [] - parse - @color = Color.new(1.0,1.0,1.0) - end - - def parse - @file.each_line do |line| - line = line.strip - - array = line.split(' ') - case array[0] - when 'mtllib' - @material_file = array[1] - puts @material_file - parse_mtllib - when 'usemtl' - # PI*(r*r) - set_material(array[1]) - when 'o' - change_object(array[1]) - when 'v' - add_vertex(array) - when 'vt' - add_texture_coordinate(array) - - when 'vn' - add_normal(array) - - when 'f' - vert = [] - norm = [] - array[1..3].each do |f| - vert << f.split("/")[0] - norm << f.split("/")[2] - end - - vert.each_with_index do |v, index| - face = [@vertexes[Integer(v)-1], @normals[Integer(norm[index])-1]] - @faces << face - end - end - end - end - - def parse_mtllib - file = File.open(@object_path.sub(File.basename(@object_path), '')+@material_file, 'r') - file.readlines.each do |line| - array = line.strip.split(' ') - p array - case array.first - when 'newmtl' - when 'Ns' # Specular Exponent - when 'Ka' # Ambient - when 'Kd' # Diffuse - when 'Ks' # Specular - when 'Ke' # Emissive - when 'Ni' # Unknown (Blender Specific?) - when 'd' # Dissolved (Transparency) - when 'illum' # Illumination model - end - end - end - - def add_vertex(array) - vert = nil - if array.size == 5 - vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), Float(array[4])) - elsif array.size == 4 - vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), 1.0) - end - @vertexes << vert - end - - def add_normal(array) - vert = nil - if array.size == 5 - vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), Float(array[4])) - elsif array.size == 4 - vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), 1.0) - end - @normals << vert - end - - def add_texture_coordinate(array) - texture = nil - if array.size == 4 - texture = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3])) - elsif array.size == 3 - texture = Vertex.new(Float(array[1]), Float(array[2]), 0.0) - end - @textures << texture - end - - def draw - glEnable(GL_CULL_FACE) - glEnable(GL_COLOR_MATERIAL) - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE) - glBegin(GL_TRIANGLES) # begin drawing model - self.faces.each do |vert| - vertex = vert[0] - normal = vert[1] - - glColor3f(@color.red, @color.green, @color.blue) - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, [@color.red, @color.green, @color.blue, 1.0]) - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, [@color.red, @color.green, @color.blue, 1.0]) - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, [1,1,1,1]) - glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, [10.0]) - - glNormal3f(normal.x, normal.y, normal.z) - glVertex3f(vertex.x, vertex.y, vertex.z) - end - glEnd - glDisable(GL_CULL_FACE) - glDisable(GL_COLOR_MATERIAL) - end - end -end diff --git a/lib/objects/light.rb b/lib/objects/light.rb index c891031..cae73cf 100644 --- a/lib/objects/light.rb +++ b/lib/objects/light.rb @@ -2,7 +2,7 @@ class IMICFPS class Light include OpenGL MAX_LIGHTS = GL_MAX_LIGHTS-1 - attr_accessor :x, :y, :z, :ambient, :diffuse, :specular + attr_reader :x, :y, :z, :ambient, :diffuse, :specular, :position def self.number_of_lights @number_of_lights ||= 0 end @@ -16,7 +16,7 @@ class IMICFPS @ambient = [0.0, 0.0, 0.0, 1].pack("f*") @diffuse = [1, 0.5, 0, 1].pack("f*") @specular = [0.0, 0.0, 0.0, 1].pack("f*") - @postion = [0, 0, 0, 0].pack("f*") + @position = [0, 0, 0, 0].pack("f*") @light_id = available_light end @@ -39,15 +39,15 @@ class IMICFPS @specular = array.pack("f*") end - def postion=(array) - @postion = array.pack("f*") + def position=(array) + @position = array.pack("f*") end def draw glLightfv(@light_id, GL_AMBIENT, @ambient) glLightfv(@light_id, GL_DIFFUSE, @diffuse) glLightfv(@light_id, GL_SPECULAR, @specular) - glLightfv(@light_id, GL_POSITION, @postion) + glLightfv(@light_id, GL_POSITION, @position) glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1) glEnable(GL_LIGHTING) glEnable(@light_id) diff --git a/lib/wavefront/model.rb b/lib/wavefront/model.rb index 8ae4d38..f7bbe76 100644 --- a/lib/wavefront/model.rb +++ b/lib/wavefront/model.rb @@ -3,9 +3,8 @@ class IMICFPS class Model include OpenGL include GLU - TextureCoordinate = Struct.new(:u, :v, :weight) - Vertex = Struct.new(:x, :y, :z, :weight) - Color = Struct.new(:red, :green, :blue, :alpha) + + include Parser attr_accessor :objects, :materials, :vertexes, :texures, :normals, :faces attr_accessor :x, :y, :z @@ -25,7 +24,14 @@ class IMICFPS @normals = [] @faces = [] @smoothing= 0 + + @bounding_box = BoundingBox.new(0.0,0.0,0.0, 0.0,0.0,0.0) + @debug_color = Color.new(rand(0.0..1.0), rand(0.0..1.0), rand(0.0..1.0)) + start_time = Time.now parse + puts "#{object.split('/').last} took #{(Time.now-start_time).round(2)} seconds to parse" + p @bounding_box + face_count = 0 @objects.each {|o| face_count+=o.faces.size} @objects.each_with_index do |o, i| @@ -91,153 +97,81 @@ class IMICFPS end glDisable(GL_CULL_FACE) if back_face_culling glDisable(GL_COLOR_MATERIAL) + render_bounding_box(o.bounding_box) if ARGV.join("--debug") end + render_bounding_box(@bounding_box) if ARGV.join("--debug") glPopMatrix + + $window.number_of_faces+=self.faces.size end - def parse - lines = 0 - @file.each_line do |line| - lines+=1 - line = line.strip + def render_bounding_box(bounding_box, color = @debug_color) + # TODO: Minimize number of calls in here + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + glBegin(GL_TRIANGLES) + # TOP + glNormal3f(0,1,0) + glColor3f(color.red, color.green, color.blue) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.max_z) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.min_z) + glVertex3f(bounding_box.max_x, bounding_box.max_y, bounding_box.min_z) - array = line.split(' ') - case array[0] - when 'mtllib' - @material_file = array[1] - parse_mtllib - when 'usemtl' - set_material(array[1]) - when 'o' - change_object(array[1]) - when 's' - set_smoothing(array[1]) - when 'v' - add_vertex(array) - when 'vt' - add_texture_coordinate(array) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.max_z) + glVertex3f(bounding_box.max_x, bounding_box.max_y, bounding_box.max_z) + glVertex3f(bounding_box.max_x, bounding_box.max_y, bounding_box.min_z) - when 'vn' - add_normal(array) + # BOTTOM + glNormal3f(0,-1,0) + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.min_z) + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.max_z) + glVertex3f(bounding_box.min_x, bounding_box.min_y, bounding_box.max_z) - when 'f' - verts = [] - uvs = [] - norms = [] - array[1..3].each do |f| - verts << f.split("/")[0] - uvs << f.split("/")[1] - norms << f.split("/")[2] - end + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.min_z) + glVertex3f(bounding_box.min_x, bounding_box.min_y, bounding_box.min_z) + glVertex3f(bounding_box.min_x, bounding_box.min_y, bounding_box.max_z) - verts.each_with_index do |v, index| - if uvs.first != "" - face = [@vertices[Integer(v)-1], @uvs[Integer(uvs[index])-1], @normals[Integer(norms[index])-1], material, @smoothing] - else - face = [@vertices[Integer(v)-1], nil, @normals[Integer(norms[index])-1], material, @smoothing] - end - @current_object.faces << face - @faces << face - end - end - end + # RIGHT SIDE + glNormal3f(0,0,1) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.max_z) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.min_z) + glVertex3f(bounding_box.min_x, bounding_box.min_y, bounding_box.min_z) - puts "Total Lines: #{lines}" - end + glVertex3f(bounding_box.min_x, bounding_box.min_y, bounding_box.max_z) + glVertex3f(bounding_box.min_x, bounding_box.min_y, bounding_box.min_z) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.max_z) - def parse_mtllib - file = File.open(@object_path.sub(File.basename(@object_path), '')+@material_file, 'r') - file.readlines.each do |line| - array = line.strip.split(' ') - # puts array.join - case array.first - when 'newmtl' - material = Material.new(array.last) - @current_material = array.last - @materials[array.last] = material - when 'Ns' # Specular Exponent - when 'Ka' # Ambient - @materials[@current_material].ambient = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) - when 'Kd' # Diffuse - @materials[@current_material].diffuse = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) - when 'Ks' # Specular - @materials[@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' - @materials[@current_material].set_texture(array[1]) - end - end - end + # LEFT SIDE + glNormal3f(1,0,0) + glVertex3f(bounding_box.max_x, bounding_box.max_y, bounding_box.max_z) + glVertex3f(bounding_box.max_x, bounding_box.max_y, bounding_box.min_z) + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.min_z) - def change_object(name) - @objects << Object.new(name) - @current_object = @objects.last - end + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.max_z) + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.min_z) + glVertex3f(bounding_box.max_x, bounding_box.max_y, bounding_box.max_z) - def set_smoothing(value) - if value == "1" - @smoothing = true - else - @smoothing = false - end - end + # FRONT + glNormal3f(-1,0,0) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.max_z) + glVertex3f(bounding_box.max_x, bounding_box.max_y, bounding_box.max_z) + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.max_z) - def set_material(name) - @current_material = name - end + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.max_z) + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.max_z) + glVertex3f(bounding_box.min_x, bounding_box.min_y, bounding_box.max_z) - def material - @materials[@current_material] - end + # BACK + glNormal3f(-1,0,0) + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.min_z) + glVertex3f(bounding_box.min_x, bounding_box.min_y, bounding_box.min_z) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.min_z) - def faces_count - count = 0 - @objects.each {|o| count+=o.faces.count} - return count - end - - def add_vertex(array) - @vertex_count+=1 - vert = nil - if array.size == 5 - vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), Float(array[4])) - elsif array.size == 4 - vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), 1.0) - else - raise - end - @current_object.vertices << vert - @vertices << vert - end - - def add_normal(array) - vert = nil - if array.size == 5 - vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), Float(array[4])) - elsif array.size == 4 - vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), 1.0) - else - raise - end - @current_object.normals << vert - @normals << vert - end - - def add_texture_coordinate(array) - texture = nil - if array.size == 4 - texture = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3])) - elsif array.size == 3 - texture = Vertex.new(Float(array[1]), Float(array[2]), 0.0) - else - raise - end - @current_object.textures << texture - @uvs << texture + glVertex3f(bounding_box.max_x, bounding_box.min_y, bounding_box.min_z) + glVertex3f(bounding_box.min_x, bounding_box.max_y, bounding_box.min_z) + glVertex3f(bounding_box.max_x, bounding_box.max_y, bounding_box.min_z) + glEnd + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) end end end diff --git a/lib/wavefront/object.rb b/lib/wavefront/object.rb index 204bcbc..4f8c40e 100644 --- a/lib/wavefront/object.rb +++ b/lib/wavefront/object.rb @@ -1,7 +1,7 @@ class IMICFPS class Wavefront class Object - attr_reader :name, :vertices, :textures, :normals + attr_reader :name, :vertices, :textures, :normals, :bounding_box attr_accessor :faces def initialize(name) @@ -10,6 +10,7 @@ class IMICFPS @textures = [] @normals = [] @faces = [] + @bounding_box = BoundingBox.new(nil,nil,nil, nil,nil,nil) # Faces array packs everything: # vertex = index[0] diff --git a/lib/wavefront/parser.rb b/lib/wavefront/parser.rb new file mode 100644 index 0000000..4acc71c --- /dev/null +++ b/lib/wavefront/parser.rb @@ -0,0 +1,176 @@ +class IMICFPS + class Wavefront + module Parser + TextureCoordinate = Struct.new(:u, :v, :weight) + Vertex = Struct.new(:x, :y, :z, :weight) + Color = Struct.new(:red, :green, :blue, :alpha) + + def parse + lines = 0 + @file.each_line do |line| + lines+=1 + line = line.strip + + array = line.split(' ') + case array[0] + when 'mtllib' + @material_file = array[1] + parse_mtllib + when 'usemtl' + set_material(array[1]) + when 'o' + change_object(array[1]) + when 's' + set_smoothing(array[1]) + when 'v' + add_vertex(array) + when 'vt' + add_texture_coordinate(array) + + when 'vn' + add_normal(array) + + when 'f' + verts = [] + uvs = [] + norms = [] + array[1..3].each do |f| + verts << f.split("/")[0] + uvs << f.split("/")[1] + norms << f.split("/")[2] + end + + verts.each_with_index do |v, index| + if uvs.first != "" + face = [@vertices[Integer(v)-1], @uvs[Integer(uvs[index])-1], @normals[Integer(norms[index])-1], material, @smoothing] + else + face = [@vertices[Integer(v)-1], nil, @normals[Integer(norms[index])-1], material, @smoothing] + end + @current_object.faces << face + @faces << face + end + end + end + + puts "Total Lines: #{lines}" + end + + def parse_mtllib + file = File.open(@object_path.sub(File.basename(@object_path), '')+@material_file, 'r') + file.readlines.each do |line| + array = line.strip.split(' ') + # puts array.join + case array.first + when 'newmtl' + material = Material.new(array.last) + @current_material = array.last + @materials[array.last] = material + when 'Ns' # Specular Exponent + when 'Ka' # Ambient + @materials[@current_material].ambient = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) + when 'Kd' # Diffuse + @materials[@current_material].diffuse = Color.new(Float(array[1]), Float(array[2]), Float(array[3])) + when 'Ks' # Specular + @materials[@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' + @materials[@current_material].set_texture(array[1]) + end + end + end + + def change_object(name) + @objects << Object.new(name) + @current_object = @objects.last + end + + def set_smoothing(value) + if value == "1" + @smoothing = true + else + @smoothing = false + end + end + + def set_material(name) + @current_material = name + end + + def material + @materials[@current_material] + end + + def faces_count + count = 0 + @objects.each {|o| count+=o.faces.count} + return count + end + + def add_vertex(array) + @vertex_count+=1 + vert = nil + if array.size == 5 + vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), Float(array[4])) + elsif array.size == 4 + vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), 1.0) + else + raise + end + recalculate_bounding_box(vert, @bounding_box) + recalculate_bounding_box(vert, @current_object.bounding_box) + @current_object.vertices << vert + @vertices << vert + end + + def add_normal(array) + vert = nil + if array.size == 5 + vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), Float(array[4])) + elsif array.size == 4 + vert = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3]), 1.0) + else + raise + end + @current_object.normals << vert + @normals << vert + end + + def add_texture_coordinate(array) + texture = nil + if array.size == 4 + texture = Vertex.new(Float(array[1]), Float(array[2]), Float(array[3])) + elsif array.size == 3 + texture = Vertex.new(Float(array[1]), Float(array[2]), 0.0) + else + raise + end + @current_object.textures << texture + @uvs << texture + end + + def recalculate_bounding_box(vertex, bounding_box) + # Set a default relative to the model + unless bounding_box.min_x.is_a?(Float) + 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 + + 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 + end +end diff --git a/lib/window.rb b/lib/window.rb index 2a42295..85184bd 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -16,9 +16,9 @@ class IMICFPS @draw_skydome = true @skydome = Wavefront::Model.new("objects/skydome.obj") @model = Wavefront::Model.new("objects/biped.obj") - @scene = Wavefront::Model.new("objects/cube.obj") + # @scene = Wavefront::Model.new("objects/cube.obj") @tree = Wavefront::Model.new("objects/tree.obj") - # @mega_model = Wavefront::Model.new("objects/sponza.obj") + @mega_model = Wavefront::Model.new("objects/sponza.obj") @camera = Wavefront::Model::Vertex.new(0,-1,0) @camera_target = Wavefront::Model::Vertex.new(0,-1,0) @@ -38,13 +38,13 @@ class IMICFPS @ambient_light = [0.5, 0.5, 0.5, 1] @diffuse_light = [1, 0.5, 0, 1] @specular_light = [0.2, 0.2, 0.2, 1] - @light_postion = [1, 1, 1, 0] + @light_position = [3, 6, 6, 0] @camera_light = Light.new(0,0,0) @camera_light.ambient = @ambient_light @camera_light.diffuse = @diffuse_light @camera_light.specular = @specular_light - @camera_light.specular = @specular_light + @camera_light.position = @light_position end def draw @@ -79,8 +79,8 @@ class IMICFPS # gluLookAt(@camera.x,@camera.y,@camera.z, @angle_x,@angle_y,0, 0,1,0) color = [@c1, @c2, @c3] - @skydome.draw(0,0,0, 0.004, false) if @draw_skydome - @scene.draw(0,0,0, 1) + @skydome.draw(0,0,0, 1, false) if @draw_skydome + # @scene.draw(0,0,0, 1) @model.draw(1, 0, 0) @tree.draw(5, 0, 0) @tree.draw(5, 0, 3) @@ -115,7 +115,7 @@ class IMICFPS self.mouse_x, self.mouse_y = Gosu.screen_width/2, Gosu.screen_height/2 @light_postion = [@camera.x, @camera.y, @camera.z, 0] - @camera_light.postion = @light_postion + # @camera_light.position = @light_position # @light_postion = [0.0, 10, 0, 0] relative_speed = @speed*(delta_time/60.0)