diff --git a/i-mic-fps.rb b/i-mic-fps.rb index 734790f..d8b3e16 100644 --- a/i-mic-fps.rb +++ b/i-mic-fps.rb @@ -37,8 +37,6 @@ else raise RuntimeError, "Unsupported platform." end -BoundingBox = Struct.new(:min_x, :min_y, :min_z, :max_x, :max_y, :max_z) - if RUBY_VERSION < "2.5.0" puts "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-" puts "|NOTICE| Ruby is #{RUBY_VERSION} not 2.5.0+..............................|Notice|" @@ -63,6 +61,7 @@ $debug = ARGV.join.include?("--debug") ? true : false require_relative "lib/common_methods" require_relative "lib/math/vector" +require_relative "lib/math/bounding_box" require_relative "lib/trees/aabb_tree" require_relative "lib/managers/input_mapper" diff --git a/lib/managers/collision_manager.rb b/lib/managers/collision_manager.rb index b948ca7..9ecbb30 100644 --- a/lib/managers/collision_manager.rb +++ b/lib/managers/collision_manager.rb @@ -9,6 +9,11 @@ class IMICFPS @aabb_tree.add(entity.normalized_bounding_box, entity) end + def update + lazy_check_collisions + @aabb_tree.update + end + def remove(entity) @aabb_tree.remove(entity) end @@ -19,8 +24,10 @@ class IMICFPS @game_state.entities.each do |other| next if entity == other next if entity.is_a?(Terrain) || other.is_a?(Terrain) + next unless entity.collidable? + next unless other.collidable? - if entity.intersect(other) + if entity.normalized_bounding_box.intersect(other.normalized_bounding_box) entity.debug_color = Color.new(1.0,0.0,0.0) other.debug_color = Color.new(1.0,0.0,0.0) @@ -33,10 +40,5 @@ class IMICFPS end end end - - def update - lazy_check_collisions - # @aabb_tree.rebuild - end end end diff --git a/lib/math/bounding_box.rb b/lib/math/bounding_box.rb new file mode 100644 index 0000000..27e4d42 --- /dev/null +++ b/lib/math/bounding_box.rb @@ -0,0 +1,100 @@ +class IMICFPS + class BoundingBox + attr_accessor :min_x, :min_y, :min_z, :max_x, :max_y, :max_z + + def initialize(min_x = 0.0, min_y = 0.0, min_z = 0.0, max_x = 0.0, max_y = 0.0, max_z = 0.0) + @min_x = min_x + @min_y = min_y + @min_z = min_z + + @max_x = max_x + @max_y = max_y + @max_z = max_z + end + + def ==(other) + min_x == other.min_x && + min_y == other.min_y && + min_z == other.min_z && + max_x == other.max_x && + max_y == other.max_y && + max_z == other.max_z + end + + # returns a new bounding box that includes both bounding boxes + def union(other) + temp = BoundingBox.new + temp.min_x = [min_x, other.min_x].min + temp.min_y = [min_y, other.min_y].min + temp.min_z = [min_z, other.min_z].min + + temp.max_x = [max_x, other.max_x].max + temp.max_y = [max_y, other.max_y].max + temp.max_z = [max_z, other.max_z].max + + return temp + end + + # returns boolean + def intersect(other) + (min_x <= other.max_x && max_x >= other.min_x) && + (min_y <= other.max_y && max_y >= other.min_y) && + (min_z <= other.max_z && max_z >= other.min_z) + end + + def difference(other) + temp = BoundingBox.new + temp.min_x = min_x - other.min_x + temp.min_y = min_y - other.min_y + temp.min_z = min_z - other.min_z + + temp.max_x = max_x - other.max_x + temp.max_y = max_y - other.max_y + temp.max_z = max_z - other.max_z + + return temp + end + + def volume + width * height * depth + end + + def width + @max_x - @min_x + end + + def height + @max_y - @min_y + end + + def depth + @max_z - @min_z + end + + def normalize(entity) + temp = BoundingBox.new + temp.min_x = min_x.to_f * entity.scale + temp.min_y = min_y.to_f * entity.scale + temp.min_z = min_z.to_f * entity.scale + + temp.max_x = max_x.to_f * entity.scale + temp.max_y = max_y.to_f * entity.scale + temp.max_z = max_z.to_f * entity.scale + + return temp + end + + def normalize_with_offset(entity) + temp = BoundingBox.new + temp.min_x = min_x.to_f * entity.scale + entity.position.x + temp.min_y = min_y.to_f * entity.scale + entity.position.y + temp.min_z = min_z.to_f * entity.scale + entity.position.z + + temp.max_x = max_x.to_f * entity.scale + entity.position.x + temp.max_y = max_y.to_f * entity.scale + entity.position.y + temp.max_z = max_z.to_f * entity.scale + entity.position.z + + return temp + end + end +end \ No newline at end of file diff --git a/lib/objects/entities/terrain.rb b/lib/objects/entities/terrain.rb index 63abbaa..d8404b4 100644 --- a/lib/objects/entities/terrain.rb +++ b/lib/objects/entities/terrain.rb @@ -1,7 +1,7 @@ class IMICFPS class Terrain < Entity def setup - bind_model("base", "randomish_terrain") + bind_model("base", "randomish_terrain") end end end \ No newline at end of file diff --git a/lib/objects/entity.rb b/lib/objects/entity.rb index cdbd5a8..f7901dc 100644 --- a/lib/objects/entity.rb +++ b/lib/objects/entity.rb @@ -6,10 +6,9 @@ class IMICFPS include OpenGL include GLU include CommonMethods - attr_accessor :scale - attr_accessor :visible, :renderable, :backface_culling + attr_accessor :scale, :visible, :renderable, :backface_culling attr_reader :position, :rotation, :velocity - attr_reader :model, :name, :debug_color, :width, :height, :depth, :last_x, :last_y, :last_z, :normalized_bounding_box + attr_reader :model, :name, :debug_color, :normalized_bounding_box def initialize(x: 0, y: 0, z: 0, bound_model: nil, scale: MODEL_METER_SCALE, backface_culling: true, auto_manage: true, manifest_file: nil) @position = Vector.new(x, y, z) @scale = scale @@ -27,7 +26,6 @@ class IMICFPS @physics = false @mass = 100 # kg - @width, @height, @depth = 0,0,0 @delta_time = Gosu.milliseconds @last_position = Vector.new(@position.x, @position.y, @position.z) @@ -39,9 +37,6 @@ class IMICFPS @normalized_bounding_box = normalize_bounding_box_with_offset box = normalize_bounding_box - @width = box.max_x-box.min_x - @height = box.max_y-box.min_y - @depth = box.max_z-box.min_z end return self @@ -61,9 +56,6 @@ class IMICFPS @normalized_bounding_box = normalize_bounding_box_with_offset box = normalize_bounding_box - @width = box.max_x-box.min_x - @height = box.max_y-box.min_y - @depth = box.max_z-box.min_z end def model @@ -89,8 +81,6 @@ class IMICFPS unless at_same_position? @normalized_bounding_box = normalize_bounding_box_with_offset if model end - - @last_x, @last_y, @last_z = @x, @y, @z end def debug_color=(color) @@ -101,53 +91,16 @@ class IMICFPS @position == @last_position end - # Do two Axis Aligned Bounding Boxes intersect? - def intersect(other) - me = normalized_bounding_box - other = other.normalized_bounding_box - - # puts "bounding boxes match!" if a == b - if (me.min_x <= other.max_x && me.max_x >= other.min_x) && - (me.min_y <= other.max_y && me.max_y >= other.min_y) && - (me.min_z <= other.max_z && me.max_z >= other.min_z) - return true - else - return false - end - end - def distance(vertex, other) return Math.sqrt((vertex.x-other.x)**2 + (vertex.y-other.y)**2 + (vertex.z-other.z)**2) end - def normalize_bounding_box - box = @bound_model.model.bounding_box - - temp = BoundingBox.new - temp.min_x = box.min_x.to_f*scale - temp.min_y = box.min_y.to_f*scale - temp.min_z = box.min_z.to_f*scale - - temp.max_x = box.max_x.to_f*scale - temp.max_y = box.max_y.to_f*scale - temp.max_z = box.max_z.to_f*scale - - return temp + def normalize_bounding_box_with_offset + @bound_model.model.bounding_box.normalize_with_offset(self) end - def normalize_bounding_box_with_offset - box = @bound_model.model.bounding_box - - temp = BoundingBox.new - temp.min_x = box.min_x.to_f*scale+@position.x - temp.min_y = box.min_y.to_f*scale+@position.y - temp.min_z = box.min_z.to_f*scale+@position.z - - temp.max_x = box.max_x.to_f*scale+@position.x - temp.max_y = box.max_y.to_f*scale+@position.y - temp.max_z = box.max_z.to_f*scale+@position.z - - return temp + def normalize_bounding_box + @bound_model.model.bounding_box.normalize(self) end def handleGlError diff --git a/lib/trees/aabb_tree.rb b/lib/trees/aabb_tree.rb index 0f83bf2..ef907b8 100644 --- a/lib/trees/aabb_tree.rb +++ b/lib/trees/aabb_tree.rb @@ -10,13 +10,20 @@ class IMICFPS raise "Object can't be nil!" unless object if @root - @root.insert_subtree(bounding_box, object) + @root.insert_subtree(bounding_box.dup, object) else @root = AABBNode.new(parent: nil, object: object, bounding_box: BoundingBox.new(0,0,0, 0,0,0)) end end - def update(object) + def update + @objects.each do |object, node| + unless object.bounding_box == node.bounding_box + puts "#{object.class} mutated!" + remove(node) + add(object) + end + end end # Returns a list of all collided objects inside Bounding Box @@ -26,17 +33,23 @@ class IMICFPS end def remove(object) + @root.remove_subtree(@objects[object]) + @objects[object] = nil end class AABBNode + attr_accessor :bounding_box, :parent, :object, :a, :b def initialize(parent:, object:, bounding_box:) @parent = parent @object = object @bounding_box = bounding_box + + @a = nil + @b = nil end def insert_subtree(bounding_box, object) - p "#{bounding_box} -> #{object.class}" + # p "#{bounding_box} -> #{object.class}" end def remove_subtree(node)