diff --git a/i-mic-fps.rb b/i-mic-fps.rb index 626dd46..9bd1532 100644 --- a/i-mic-fps.rb +++ b/i-mic-fps.rb @@ -24,9 +24,13 @@ require_relative "lib/common_methods" require_relative "lib/managers/object_manager" require_relative "lib/managers/light_manager" +require_relative "lib/objects/game_object" require_relative "lib/objects/light" require_relative "lib/objects/camera" -require_relative "lib/objects/model" +require_relative "lib/objects/player" +require_relative "lib/objects/tree" +require_relative "lib/objects/skydome" +require_relative "lib/objects/model_loader" require_relative "lib/objects/terrain" require_relative "lib/wavefront/model" diff --git a/lib/objects/camera.rb b/lib/objects/camera.rb index 69efce8..86c3222 100644 --- a/lib/objects/camera.rb +++ b/lib/objects/camera.rb @@ -6,28 +6,17 @@ class IMICFPS attr_accessor :x,:y,:z, :field_of_view, :vertical_angle, :horizontal_angle, :mouse_sensitivity attr_reader :bound_model - def initialize(x: 0, y: 0, z: 0, fov: 70.0) + def initialize(x: 0, y: 0, z: 0, fov: 70.0, distance: 100.0) @x,@y,@z = x,y,z @vertical_angle = 0.0 @horizontal_angle = 0.0 @field_of_view = fov + @view_distance = distance - @speed = 0.05 - @old_speed = @speed self.mouse_x, self.mouse_y = Gosu.screen_width/2, Gosu.screen_height/2 @true_mouse = Point.new(Gosu.screen_width/2, Gosu.screen_height/2) @true_mouse_checked = 0 @mouse_sensitivity = 20.0 - - @bound_model = nil - end - - def bind_model(model) - @bound_model = model - end - - def unbind - @bound_model = nil end def draw @@ -35,7 +24,7 @@ class IMICFPS glMatrixMode(GL_PROJECTION) # The projection matrix is responsible for adding perspective to our scene. glLoadIdentity # Resets current modelview matrix # Calculates aspect ratio of the window. Gets perspective view. 45 is degree viewing angle, (0.1, 100) are ranges how deep can we draw into the screen - gluPerspective(@field_of_view, $window.width / $window.height, 0.1, 1000.0) + gluPerspective(@field_of_view, $window.width / $window.height, 0.1, @view_distance) glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) glRotatef(@vertical_angle,1,0,0) glRotatef(@horizontal_angle,0,1,0) @@ -43,8 +32,6 @@ class IMICFPS glMatrixMode(GL_MODELVIEW) # The modelview matrix is where object information is stored. glLoadIdentity - - glEnable(GL_DEPTH_TEST) end def update @@ -62,51 +49,6 @@ class IMICFPS self.mouse_x, self.mouse_y = Gosu.screen_width/2.0, Gosu.screen_height/2.0 @true_mouse_checked = 0 if (button_down?(Gosu::KbLeftAlt) && (button_down?(Gosu::KbEnter) || button_down?(Gosu::KbReturn))) @true_mouse_checked = 0 if (button_down?(Gosu::KbRightAlt) && (button_down?(Gosu::KbEnter) || button_down?(Gosu::KbReturn))) - - relative_speed = @speed - if button_down?(Gosu::KbLeftControl) - relative_speed = (@speed*10.0)*(delta_time/60.0) - else - relative_speed = @speed*(delta_time/60.0) - end - - if button_down?(Gosu::KbUp) || button_down?(Gosu::KbW) - @z+=Math.cos(@horizontal_angle * Math::PI / 180)*relative_speed - @x-=Math.sin(@horizontal_angle * Math::PI / 180)*relative_speed - end - if button_down?(Gosu::KbDown) || button_down?(Gosu::KbS) - @z-=Math.cos(@horizontal_angle * Math::PI / 180)*relative_speed - @x+=Math.sin(@horizontal_angle * Math::PI / 180)*relative_speed - end - if button_down?(Gosu::KbA) - @z+=Math.sin(@horizontal_angle * Math::PI / 180)*relative_speed - @x+=Math.cos(@horizontal_angle * Math::PI / 180)*relative_speed - end - if button_down?(Gosu::KbD) - @z-=Math.sin(@horizontal_angle * Math::PI / 180)*relative_speed - @x-=Math.cos(@horizontal_angle * Math::PI / 180)*relative_speed - end - - if button_down?(Gosu::KbLeft) - @horizontal_angle-=relative_speed*100 - end - if button_down?(Gosu::KbRight) - @horizontal_angle+=relative_speed*100 - end - - @y+=relative_speed if button_down?(Gosu::KbC) || button_down?(Gosu::KbLeftShift) - @y-=relative_speed if button_down?(Gosu::KbSpace) - - if @bound_model - distance = 2.0 - x_offset = distance * Math.cos(@bound_model.y_rotation) - z_offset = distance * Math.sin(@bound_model.y_rotation) - @bound_model.x = @x*-1+x_offset - @bound_model.y = @y*-1-2 - @bound_model.z = @z*-1-z_offset - - @bound_model.y_rotation = (@horizontal_angle*-1)+180 - end end def button_up(id) @@ -120,11 +62,15 @@ class IMICFPS when Gosu::KB_NUMPAD_MULTIPLY @mouse_sensitivity = 20.0 when Gosu::MsWheelUp - @field_of_view += 1 - @field_of_view = @field_of_view.clamp(1, 100) + # @field_of_view += 1 + # @field_of_view = @field_of_view.clamp(1, 100) + @view_distance += 1 + @view_distance = @view_distance.clamp(1, 1000) when Gosu::MsWheelDown - @field_of_view -= 1 - @field_of_view = @field_of_view.clamp(1, 100) + # @field_of_view -= 1 + # @field_of_view = @field_of_view.clamp(1, 100) + @view_distance -= 1 + @view_distance = @view_distance.clamp(1, 1000) end end end diff --git a/lib/objects/model.rb b/lib/objects/game_object.rb similarity index 59% rename from lib/objects/model.rb rename to lib/objects/game_object.rb index 0b16776..93678aa 100644 --- a/lib/objects/model.rb +++ b/lib/objects/game_object.rb @@ -1,51 +1,42 @@ class IMICFPS - class Model - def self.supported_models - ["Wavefront OBJ"] - end - CACHE = {} + # A game object is any renderable thing + class GameObject include OpenGL - include GLU - + include CommonMethods attr_accessor :x, :y, :z, :scale - attr_accessor :visible, :renderable + attr_accessor :visible, :renderable, :backface_culling attr_accessor :x_rotation, :y_rotation, :z_rotation attr_reader :model, :name, :debug_color - - def initialize(type:, file_path:, x: 0, y: 0, z: 0, scale: MODEL_METER_SCALE, backface_culling: true, auto_manage: true) - @type = type - @file_path = file_path + def initialize(x: 0, y: 0, z: 0, bound_model: nil, scale: MODEL_METER_SCALE, backface_culling: true, auto_manage: true) @x,@y,@z,@scale = x,y,z,scale + @bound_model = bound_model @backface_culling = backface_culling @visible = true @renderable = true @x_rotation,@y_rotation,@z_rotation = 0,0,0 - @name = file_path.split("/").last.split(".").first - @debug_color = Color.new(rand(0.0..1.0), rand(0.0..1.0), rand(0.0..1.0)) - - @model = nil - - unless load_model_from_cache - case type - when :obj - @model = Wavefront::Model.new(file_path: @file_path, x: x, y: y, z: z, scale: scale) - else - raise "Unsupported model type, supported models are: #{Model.supported_models.join(', ')}" - end - - cache_model - end - + @debug_color = Color.new(0.0, 1.0, 0.0) ObjectManager.add_object(self) if auto_manage - setup return self end + def bind_model(model) + raise "model isn't a model!" unless model.is_a?(ModelLoader) + @bound_model = model + end + + def model + @bound_model.model if @bound_model + end + + def unbind_model + @bound_model = nil + end + def setup end @@ -55,16 +46,16 @@ class IMICFPS glEnable(GL_NORMALIZE) glPushMatrix # Render bounding boxes before transformation is applied - render_bounding_box(@model.bounding_box) if $debug - # @model.objects.each {|o| render_bounding_box(o.bounding_box, o.debug_color)} if $debug + render_bounding_box(model.bounding_box) if $debug + model.objects.each {|o| render_bounding_box(o.bounding_box, o.debug_color)} if $debug - # glTranslatef(@x, @y, @z) - # glRotatef(@x_rotation,1.0, 0, 0) - # glRotatef(@y_rotation,0, 1.0, 0) - # glRotatef(@z_rotation,0, 0, 1.0) + glTranslatef(@x, @y, @z) + glRotatef(@x_rotation,1.0, 0, 0) + glRotatef(@y_rotation,0, 1.0, 0) + glRotatef(@z_rotation,0, 0, 1.0) handleGlError - @model.draw(@x, @y, @z, @scale, @backface_culling) + model.draw(@x, @y, @z, @scale, @backface_culling) handleGlError glPopMatrix @@ -72,89 +63,23 @@ class IMICFPS end def update - ObjectManager.objects.each do |b| - next if b.name == self.name - raise if b.name == self.name - - if self.intersect(self.model.bounding_box, b.model.bounding_box) - self.y_rotation+=0.02 - - puts "#{b.name} is touching #{self.name}" - a_box = normalize_bounding_box(self.model.bounding_box).to_a.map {|q| q.round(2)} - puts "(#{self.name}): (#{a_box[0..2].join(',')}) and (#{a_box[3..5].join(',')})" - - b_box = normalize_bounding_box(b.model.bounding_box).to_a.map {|q| q.round(2)} - puts "(#{b.name}): (#{b_box[0..2].join(',')}) and (#{b_box[3..5].join(',')})" - else - # puts "!=! No Collision" - end - end end - - def load_model_from_cache - found = false - if CACHE[@type].is_a?(Hash) - if CACHE[@type][@file_path] - @model = CACHE[@type][@file_path] - puts "Used cached model for: #{@file_path.split('/').last}" - found = true - end - end - - return found - end - - def cache_model - CACHE[@type] = {} unless CACHE[@type].is_a?(Hash) - CACHE[@type][@file_path] = @model - end - # Do two Axis Aligned Bounding Boxes intersect? def intersect(a, b) a = normalize_bounding_box(a) b = normalize_bounding_box(b) puts "bounding boxes match!" if a == b - # p to_abs(a),to_abs(b) - # exit - # puts "MAX_X" - # return false if a.max_x <= b.min_x - # puts "MIN_X" - # return false if a.min_x >= b.max_x - # - # puts "MAX_Y" - # return false if a.max_y <= b.min_y - # puts "MIN_Y" - # return false if a.min_y >= b.max_y - # - # puts "MAX_Z" - # return false if a.max_z <= b.min_z - # puts "MIN_Z" - # return false if a.min_z >= b.max_z - # puts "END" - # return true # if (((a.min_x <= b.min_x && b.max_x <= a.max_x) || (b.min_x <= a.min_x && a.min_x <= b.max_x)) && # ((a.min_y <= b.min_y && b.max_y <= a.max_y) || (b.min_y <= a.min_y && a.min_y <= b.max_y)) && # ((a.min_z <= b.min_z && b.max_z <= a.max_z) || (b.min_z <= a.min_z && a.min_z <= b.max_z))) if (a.max_x >= b.max_x && a.min_x <= b.max_x) && (a.max_y >= b.min_y && a.min_y <= b.max_y) && (a.max_z >= b.min_z && a.min_z <= b.max_z) return true - # elsif (b.max_x >= a.max_x && b.min_x <= a.max_x) && (b.max_y >= a.min_y && b.min_y <= a.max_y) && (b.max_z >= a.min_z && b.min_z <= a.max_z) - # return true + else + return false end end - def to_abs(box) - temp = BoundingBox.new - temp.min_x = box.min_x.abs - temp.min_y = box.min_y.abs - temp.min_z = box.min_z.abs - - temp.max_x = box.max_x.abs - temp.max_y = box.max_y.abs - temp.max_z = box.max_z.abs - return temp - end - def normalize_bounding_box(box) temp = BoundingBox.new temp.min_x = box.min_x.to_f*scale+x diff --git a/lib/objects/model_loader.rb b/lib/objects/model_loader.rb new file mode 100644 index 0000000..d7feedc --- /dev/null +++ b/lib/objects/model_loader.rb @@ -0,0 +1,52 @@ +class IMICFPS + class ModelLoader + def self.supported_models + ["Wavefront OBJ"] + end + + CACHE = {} + + attr_reader :model, :name, :debug_color + + def initialize(type:, file_path:, game_object:) + @type = type + @file_path = file_path + @name = file_path.split("/").last.split(".").first + @debug_color = Color.new(0.0, 1.0, 0.0) + + @model = nil + + unless load_model_from_cache + case type + when :obj + @model = Wavefront::Model.new(file_path: @file_path, game_object: game_object) + else + raise "Unsupported model type, supported models are: #{Model.supported_models.join(', ')}" + end + + cache_model + end + + + return self + end + + def load_model_from_cache + found = false + if CACHE[@type].is_a?(Hash) + if CACHE[@type][@file_path] + @model = CACHE[@type][@file_path] + puts "Used cached model for: #{@file_path.split('/').last}" + found = true + end + end + + return found + end + + def cache_model + CACHE[@type] = {} unless CACHE[@type].is_a?(Hash) + CACHE[@type][@file_path] = @model + end + end +end diff --git a/lib/objects/player.rb b/lib/objects/player.rb new file mode 100644 index 0000000..3b53c51 --- /dev/null +++ b/lib/objects/player.rb @@ -0,0 +1,60 @@ +class IMICFPS + class Player < GameObject + + attr_accessor :speed + attr_reader :name, :bound_model + def setup + bind_model(ModelLoader.new(type: :obj, file_path: "objects/biped.obj", game_object: self)) + + @speed = 0.05 + @old_speed = @speed + end + + def update + super + + relative_speed = @speed + if button_down?(Gosu::KbLeftControl) + relative_speed = (@speed*10.0)*(delta_time/60.0) + else + relative_speed = @speed*(delta_time/60.0) + end + + if button_down?(Gosu::KbUp) || button_down?(Gosu::KbW) + @z+=Math.cos(@y_rotation * Math::PI / 180)*relative_speed + @x-=Math.sin(@y_rotation * Math::PI / 180)*relative_speed + end + if button_down?(Gosu::KbDown) || button_down?(Gosu::KbS) + @z-=Math.cos(@y_rotation * Math::PI / 180)*relative_speed + @x+=Math.sin(@y_rotation * Math::PI / 180)*relative_speed + end + if button_down?(Gosu::KbA) + @z+=Math.sin(@y_rotation * Math::PI / 180)*relative_speed + @x+=Math.cos(@y_rotation * Math::PI / 180)*relative_speed + end + if button_down?(Gosu::KbD) + @z-=Math.sin(@y_rotation * Math::PI / 180)*relative_speed + @x-=Math.cos(@y_rotation * Math::PI / 180)*relative_speed + end + + if button_down?(Gosu::KbLeft) + @y_rotation-=relative_speed*100 + end + if button_down?(Gosu::KbRight) + @y_rotation+=relative_speed*100 + end + + @y-=relative_speed if button_down?(Gosu::KbC) || button_down?(Gosu::KbLeftShift) + @y+=relative_speed if button_down?(Gosu::KbSpace) + + # distance = 2.0 + # x_offset = distance * Math.cos(@bound_model.y_rotation) + # z_offset = distance * Math.sin(@bound_model.y_rotation) + model.x = @x + model.y = @y + model.z = @z + + # model.y_rotation = (@y_rotation) + end + end +end diff --git a/lib/objects/skydome.rb b/lib/objects/skydome.rb new file mode 100644 index 0000000..5c5c4f1 --- /dev/null +++ b/lib/objects/skydome.rb @@ -0,0 +1,9 @@ +class IMICFPS + class Skydome < GameObject + def setup + bind_model(ModelLoader.new(type: :obj, file_path: "objects/skydome.obj", game_object: self)) + p model.class + # raise "Skydome scale: #{self.scale}" unless self.scale == 1 + end + end +end diff --git a/lib/objects/tree.rb b/lib/objects/tree.rb new file mode 100644 index 0000000..59473cf --- /dev/null +++ b/lib/objects/tree.rb @@ -0,0 +1,7 @@ +class IMICFPS + class Tree < GameObject + def setup + bind_model(ModelLoader.new(type: :obj, file_path: "objects/tree.obj", game_object: self)) + end + end +end diff --git a/lib/wavefront/model.rb b/lib/wavefront/model.rb index 1182cb7..6ca90fc 100644 --- a/lib/wavefront/model.rb +++ b/lib/wavefront/model.rb @@ -14,9 +14,10 @@ class IMICFPS attr_accessor :x, :y, :z, :scale attr_reader :bounding_box - def initialize(file_path:, x: 0, y: 0, z: 0, scale: MODEL_METER_SCALE) - @x, @y, @z = x, y, z - @scale = scale + def initialize(file_path:, game_object:) + @game_object = game_object + @x, @y, @z = game_object.x, game_object.y, game_object.z + @scale = game_object.scale @file_path = file_path @file = File.open(file_path, 'r') @material_file = nil @@ -34,16 +35,12 @@ class IMICFPS @bounding_box = BoundingBox.new(0,0,0, 0,0,0) start_time = Time.now parse - # point = rand(1.0..10.0) - # @bounding_box = BoundingBox.new(point.to_f/2, point.to_f/2, point.to_f/2, point, point, point) - # puts "!!!!!!!!!!!!!!" - # puts @bounding_box puts "#{@file_path.split('/').last} took #{(Time.now-start_time).round(2)} seconds to parse" face_count = 0 @objects.each {|o| face_count+=o.faces.size} @objects.each_with_index do |o, i| - puts "Model::Object Name: #{o.name} Faces: #{o.faces.size}, array size divided by 3: #{o.faces.size.to_f/3.0}" + puts " Model::Object Name: #{o.name} Faces: #{o.faces.size}" end $window.number_of_faces+=face_count @model_has_texture = false @@ -56,6 +53,7 @@ class IMICFPS end def draw(x,y,z, scale, back_face_culling) + @x,@y,@z,@scale,@back_face_culling = x,y,z, scale, back_face_culling @objects.each_with_index do |o, i| glEnable(GL_CULL_FACE) if back_face_culling glEnable(GL_COLOR_MATERIAL) diff --git a/lib/wavefront/object.rb b/lib/wavefront/object.rb index b7f544d..635ce35 100644 --- a/lib/wavefront/object.rb +++ b/lib/wavefront/object.rb @@ -12,7 +12,7 @@ class IMICFPS @normals = [] @faces = [] @bounding_box = BoundingBox.new(0,0,0, 0,0,0) - @debug_color = Color.new(1.0,0.0,0.0) + @debug_color = Color.new(1.0,1.0,1.0) @x,@y,@z = 0,0,0 # Faces array packs everything: @@ -25,7 +25,7 @@ class IMICFPS def at_same_position? if @x == @parent.x if @x == @parent.x - if @x == @parent.x + if @z == @parent.z true end end @@ -41,9 +41,9 @@ class IMICFPS @faces.each do |face| [face[0]].each do |v| next unless v - list << v.x*@parent.scale+@parent.x - list << v.y*@parent.scale+@parent.y - list << v.z*@parent.scale+@parent.z + list << v.x*@parent.scale#+@parent.x + list << v.y*@parent.scale#+@parent.y + list << v.z*@parent.scale#+@parent.z list << v.weight end end diff --git a/lib/window.rb b/lib/window.rb index e60dd8d..c33d3a4 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -18,17 +18,17 @@ class IMICFPS @delta_time = Gosu.milliseconds @number_of_faces = 0 - @draw_skydome = false - @skydome = Model.new(type: :obj, file_path: "objects/skydome.obj", x: 0, y: 0,z: 0, scale: 0.8, backface_culling: false, auto_manage: false) - @actor = Model.new(type: :obj, file_path: "objects/biped.obj", x: 1, y: 0, z: 8) - Model.new(type: :obj, file_path: "objects/tree.obj", x: 10, y: 0, z: 10) + @draw_skydome = true + @skydome = Skydome.new(scale: 0.1, backface_culling: false, auto_manage: false) + Tree.new(x: 1, y: 0, z: -5) + p ObjectManager.objects.map {|o| o.name} # Model.new(type: :obj, file_path: "objects/tree.obj", z: -5) # Model.new(type: :obj, file_path: "objects/tree.obj", x: -2, z: -6) # Model.new(type: :obj, file_path: "objects/sponza.obj", scale: 1, y: -0.2) # @terrain = Terrain.new(size: 20) @camera = Camera.new(x: 0, y: -2, z: 1) - # @camera.bind_model(@actor) + @player = Player.new(x: 1, y: 0, z: -10) @crosshair_size = 10 @crosshair_thickness = 3 @@ -56,7 +56,9 @@ class IMICFPS light.draw end @camera.draw - @skydome.draw if @skydome.renderable + @skydome.draw# if @skydome.renderable + glEnable(GL_DEPTH_TEST) + ObjectManager.objects.each do |object| object.draw if object.visible && object.renderable end @@ -111,10 +113,10 @@ class IMICFPS case id when Gosu::KbZ @draw_skydome = !@draw_skydome - @skydome.renderable = @draw_skydome when Gosu::KbBacktick $debug = !$debug end + @skydome.renderable = @draw_skydome end def needs_cursor?