Renamed objects/ to game_objects/ removed redundant entities, added Map loader and test map, made LoadingState use Map for entity asset loading.

This commit is contained in:
2019-09-25 10:09:05 -05:00
parent 42191729ae
commit ee844f256f
18 changed files with 246 additions and 82 deletions

171
lib/game_objects/camera.rb Normal file
View File

@@ -0,0 +1,171 @@
class IMICFPS
class Camera
include CommonMethods
attr_accessor :field_of_view, :mouse_sensitivity
attr_reader :entity, :position, :orientation, :mouse_captured
def initialize(position:, orientation: Vector.new(0, 0, 0), fov: 70.0, view_distance: 155.0)
@position = position
@orientation = orientation
@field_of_view = fov
@view_distance = view_distance
@constant_pitch = 20.0
@entity = nil
@distance = 4
@origin_distance = @distance
self.mouse_x, self.mouse_y = window.width / 2, window.height / 2
@true_mouse = Point.new(window.width / 2, window.height / 2)
@mouse_sensitivity = 20.0 # Less is faster, more is slower
@mouse_captured = true
@mouse_checked = 0
end
def attach_to(entity)
raise "Not an Entity!" unless entity.is_a?(Entity)
@entity = entity
@entity.attach_camera(self)
end
def detach
@entity.detach_camera
@entity = nil
end
def distance_from_object
@distance
end
def horizontal_distance_from_object
distance_from_object * Math.cos(@constant_pitch)
end
def vertical_distance_from_object
distance_from_object * Math.sin(@constant_pitch)
end
def position_camera
if defined?(@entity.first_person_view)
if @entity.first_person_view
@distance = 0
else
@distance = @origin_distance
end
end
x_offset = horizontal_distance_from_object * Math.sin(@entity.orientation.y.degrees_to_radians)
z_offset = horizontal_distance_from_object * Math.cos(@entity.orientation.y.degrees_to_radians)
@position.x = @entity.position.x - x_offset
@position.y = @entity.position.y + 2
@position.z = @entity.position.z - z_offset
@orientation.y = 180 - @entity.orientation.y
end
def draw
#glMatrixMode(matrix) indicates that following [matrix] is going to get used
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, @view_distance)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
glRotatef(@orientation.z, 1, 0, 0)
glRotatef(@orientation.y, 0, 1, 0)
glTranslatef(-@position.x, -@position.y, -@position.z)
glMatrixMode(GL_MODELVIEW) # The modelview matrix is where object information is stored.
glLoadIdentity
end
def update
if @mouse_captured
delta = Float(@true_mouse.x - self.mouse_x) / (@mouse_sensitivity * @field_of_view) * 70
@orientation.y -= delta
@orientation.y %= 360.0
@orientation.z -= Float(@true_mouse.y - self.mouse_y) / (@mouse_sensitivity * @field_of_view) * 70
@orientation.z = @orientation.z.clamp(-90.0, 90.0)
if @entity
@entity.orientation.y += delta
@entity.orientation.y %= 360
position_camera
else
free_move
end
self.mouse_x = window.width / 2 if self.mouse_x <= 1 || window.mouse_x >= window.width-1
self.mouse_y = window.height / 2 if self.mouse_y <= 1 || window.mouse_y >= window.height-1
@true_mouse.x, @true_mouse.y = self.mouse_x, self.mouse_y
end
end
def looking_at
ray = Ray.new(@position, @orientation.direction * -1)
window.current_state.collision_manager.search(ray)
end
def free_move
relative_y_rotation = (@orientation.y + 180)
relative_speed = 0.25
if InputMapper.down?( :forward)
@position.z+=Math.cos(relative_y_rotation * Math::PI / 180)*relative_speed
@position.x-=Math.sin(relative_y_rotation * Math::PI / 180)*relative_speed
end
if InputMapper.down?(:backward)
@position.z-=Math.cos(relative_y_rotation * Math::PI / 180)*relative_speed
@position.x+=Math.sin(relative_y_rotation * Math::PI / 180)*relative_speed
end
if InputMapper.down?(:strife_left)
@position.z+=Math.sin(relative_y_rotation * Math::PI / 180)*relative_speed
@position.x+=Math.cos(relative_y_rotation * Math::PI / 180)*relative_speed
end
if InputMapper.down?(:strife_right)
@position.z-=Math.sin(relative_y_rotation * Math::PI / 180)*relative_speed
@position.x-=Math.cos(relative_y_rotation * Math::PI / 180)*relative_speed
end
if InputMapper.down?(:ascend)
@position.y+=relative_speed
end
if InputMapper.down?(:descend)
@position.y-=relative_speed
end
end
def button_up(id)
if InputMapper.is?(:release_mouse, id)
@mouse_captured = false
window.needs_cursor = true
elsif InputMapper.is?(:capture_mouse, id)
@mouse_captured = true
window.needs_cursor = false
elsif InputMapper.is?(:increase_mouse_sensitivity, id)
@mouse_sensitivity+=1
@mouse_sensitivity = @mouse_sensitivity.clamp(1.0, 100.0)
elsif InputMapper.is?(:decrease_mouse_sensitivity, id)
@mouse_sensitivity-=1
@mouse_sensitivity = @mouse_sensitivity.clamp(1.0, 100.0)
elsif InputMapper.is?(:reset_mouse_sensitivity, id)
@mouse_sensitivity = 20.0
elsif InputMapper.is?(:increase_view_distance, id)
# @field_of_view += 1
# @field_of_view = @field_of_view.clamp(1, 100)
@view_distance += 1
@view_distance = @view_distance.clamp(1, 1000)
elsif InputMapper.is?(:decrease_view_distance, id)
# @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
end

View File

@@ -0,0 +1,174 @@
require "etc"
class IMICFPS
class Player < Entity
attr_accessor :speed
attr_reader :name, :bound_model, :first_person_view
def setup
bind_model("base", "biped")
@collision = :dynamic
@speed = 2.5 # meter's per second
@running_speed = 5.0 # meter's per second
@turn_speed = 50.0
@old_speed = @speed
@mass = 72 # kg
@first_person_view = true
@visible = false
@drag = 0.6
@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
array_of_pixels = @name_image.to_blob
tex_names_buf = ' ' * 8
glGenTextures(1, tex_names_buf)
@name_texture_id = tex_names_buf.unpack('L2').first
glBindTexture(GL_TEXTURE_2D, @name_texture_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, @name_image.width, @name_image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, array_of_pixels)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glGenerateMipmap(GL_TEXTURE_2D)
end
def draw_nameplate
_width = (@name_image.width / @devisor) / 2
_height = (@name_image.height / @devisor)
_y = 2#normalize_bounding_box(model.bounding_box).max_y+0.05
glPushMatrix
glRotatef(180, 0, 1, 0)
glDisable(GL_LIGHTING)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_TEXTURE_2D)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND)
glBindTexture(GL_TEXTURE_2D, @name_texture_id)
glBegin(GL_TRIANGLES)
glColor3f(1.0,1.0,1.0)
# TOP LEFT
glTexCoord2f(0, 0)
glVertex3f(0-_width,_y+_height,0)
# TOP RIGHT
glTexCoord2f(1, 0)
glVertex3f(0+_width, _y+_height,0)
# BOTTOM LEFT
glTexCoord2f(0, 1)
glVertex3f(0-_width,_y,0)
# BOTTOM LEFT
glTexCoord2f(0, 1)
glVertex3f(0-_width,_y,0)
# BOTTOM RIGHT
glTexCoord2f(1, 1)
glVertex3f(0+_width, _y,0)
# TOP RIGHT
glTexCoord2f(1, 0)
glVertex3f(0+_width,_y+_height,0)
glEnd
# glDisable(GL_BLEND)
glDisable(GL_TEXTURE_2D)
glEnable(GL_LIGHTING)
glPopMatrix
end
def draw
if !@first_person_view
super
draw_nameplate
end
end
def update
# Do not handle movement if mouse is not captured
return if @camera && !@camera.mouse_captured
if @_time_in_air
air_time = (Gosu.milliseconds - @_time_in_air) / 1000.0
@velocity.y -= IMICFPS::GRAVITY * air_time * delta_time
end
super
end
def relative_speed
InputMapper.down?(:sprint) ? @running_speed : @speed
end
def relative_y_rotation
@orientation.y * -1
end
def forward
@velocity.z += Math.cos(relative_y_rotation * Math::PI / 180) * relative_speed
@velocity.x -= Math.sin(relative_y_rotation * Math::PI / 180) * relative_speed
end
def backward
@velocity.z -= Math.cos(relative_y_rotation * Math::PI / 180) * relative_speed
@velocity.x += Math.sin(relative_y_rotation * Math::PI / 180) * relative_speed
end
def strife_left
@velocity.z += Math.sin(relative_y_rotation * Math::PI / 180) * relative_speed
@velocity.x += Math.cos(relative_y_rotation * Math::PI / 180) * relative_speed
end
def strife_right
@velocity.z -= Math.sin(relative_y_rotation * Math::PI / 180) * relative_speed
@velocity.x -= Math.cos(relative_y_rotation * Math::PI / 180) * relative_speed
end
def turn_left
puts "CALLED"
@orientation.y += @turn_speed * delta_time
end
def turn_right
@orientation.y -= @turn_speed * delta_time
end
def jump
if InputMapper.down?(:jump) && !@jumping
@jumping = true
@_time_in_air = Gosu.milliseconds
elsif !@jumping
@falling = true
@_time_in_air ||= Gosu.milliseconds # FIXME
else
if @jumping && @velocity.y <= 0
@falling = false
@jumping = false
end
end
if @jumping && !@falling
if InputMapper.down?(:jump)
@velocity.y = 1.5
@falling = true
end
end
end
def toggle_first_person_view
@first_person_view = !@first_person_view
@visible = !@first_person_view
end
def turn_180
@orientation.y = @orientation.y + 180
@orientation.y %= 360
end
end
end

View File

@@ -0,0 +1,19 @@
class IMICFPS
class Skydome < Entity
def setup
@collision = :none
end
def draw
glDisable(GL_LIGHTING)
super
glEnable(GL_LIGHTING)
end
def update
@orientation.y += 0.01
@orientation.y %= 360
super
end
end
end

View File

@@ -0,0 +1,4 @@
class IMICFPS
class Terrain < Entity
end
end

119
lib/game_objects/entity.rb Normal file
View File

@@ -0,0 +1,119 @@
class IMICFPS
# A game object is any renderable thing
class Entity
include CommonMethods
attr_accessor :scale, :visible, :renderable, :backface_culling
attr_accessor :position, :orientation, :velocity
attr_reader :name, :debug_color, :bounding_box, :collision, :physics, :mass, :drag, :camera
def initialize(map_entity: nil, spawnpoint: nil, backface_culling: true, auto_manage: true)
@position = map_entity ? map_entity.position : spawnpoint.position
@orientation = map_entity ? map_entity.orientation : spawnpoint.position
@scale = map_entity ? map_entity.scale : 1.0
@backface_culling = backface_culling
@bound_model = map_entity ? bind_model(map_entity.package, map_entity.model) : nil
@visible = true
@renderable = true
@velocity = Vector.new(0, 0, 0)
@drag = 1.0
@debug_color = Color.new(0.0, 1.0, 0.0)
@collidable = [:static, :dynamic]
# :dynamic => moves in response,
# :static => does not move ever,
# :none => no collision check, entities can pass through
@collision = :static
@physics = false # Entity affected by gravity and what not
@mass = 100 # kg
@delta_time = Gosu.milliseconds
@last_position = Vector.new(@position.x, @position.y, @position.z)
setup
if @bound_model
@bound_model.model.entity = self
@bound_model.model.objects.each { |o| o.scale = self.scale }
@normalized_bounding_box = normalize_bounding_box_with_offset
normalize_bounding_box
end
@camera = nil
return self
end
def collidable?
@collidable.include?(@collision)
end
def bind_model(package, name)
model = ModelLoader.new(manifest_file: IMICFPS.assets_path + "/#{package}/#{name}/manifest.yaml", entity: @dummy_entity)
raise "model isn't a model!" unless model.is_a?(ModelLoader)
@bound_model = model
@bound_model.model.entity = self
@bound_model.model.objects.each { |o| o.scale = self.scale }
@bounding_box = normalize_bounding_box_with_offset
return model
end
def model
@bound_model.model if @bound_model
end
def unbind_model
@bound_model = nil
end
def attach_camera(camera)
@camera = camera
end
def detach_camera
@camera = nil
end
def setup
end
# Not advisable to put OpenGL code here, instead put it in Renderer.
def draw
end
def update
model.update
@delta_time = Gosu.milliseconds
unless at_same_position?
@bounding_box = normalize_bounding_box_with_offset if model
end
end
def debug_color=(color)
@debug_color = color
end
def at_same_position?
@position == @last_position
end
def normalize_bounding_box_with_offset
@bound_model.model.bounding_box.normalize_with_offset(self)
end
def normalize_bounding_box
@bound_model.model.bounding_box.normalize(self)
end
end
end

62
lib/game_objects/light.rb Normal file
View File

@@ -0,0 +1,62 @@
class IMICFPS
class Light
attr_reader :ambient, :diffuse, :specular, :position, :light_id
attr_accessor :x, :y, :z, :intensity
def initialize(x:,y:,z:, game_state:,
ambient: Vector.new(0.5, 0.5, 0.5, 1),
diffuse: Vector.new(1, 0.5, 0, 1), specular: Vector.new(0.2, 0.2, 0.2, 1),
position: Vector.new(x, y, z, 0), intensity: 1)
@x,@y,@z = x,y,z
@game_state = game_state
@intensity = intensity
self.ambient = ambient
self.diffuse = diffuse
self.specular = specular
self.position = position
@light_id = available_light
@game_state.add_light(self)
end
def available_light
raise "Using to many lights, #{@game_state.light_count}/#{LightManager::MAX_LIGHTS}" if @game_state.light_count > LightManager::MAX_LIGHTS
puts "OpenGL::GL_LIGHT#{@game_state.light_count}" if $debug.get(:stats)
@light_id = Object.const_get "OpenGL::GL_LIGHT#{@game_state.light_count}"
end
def ambient=(color)
@ambient = convert(color).pack("f*")
end
def diffuse=(color)
@diffuse = convert(color, true).pack("f*")
end
def specular=(color)
@specular = convert(color, true).pack("f*")
end
def position=(vertex)
@position = convert(vertex).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, @position)
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1)
glEnable(GL_LIGHTING)
glEnable(@light_id)
end
def convert(struct, apply_intensity = false)
if apply_intensity
return struct.to_a.compact.map{|i| i*@intensity}
else
return struct.to_a.compact
end
end
end
end

View File

@@ -0,0 +1,56 @@
class IMICFPS
class ModelLoader
def self.supported_models
["Wavefront OBJ"]
end
CACHE = {}
attr_reader :model, :name, :debug_color
def initialize(manifest_file:, entity: nil)
@manifest = YAML.load(File.read(manifest_file))
# pp @manifest
@file_path = File.expand_path("./../model/", manifest_file) + "/#{@manifest["model"]}"
@name = @manifest["name"]
@type = File.basename(@file_path).split(".").last.to_sym
@debug_color = Color.new(0.0, 1.0, 0.0)
@model = nil
@supported_models = ["OBJ"]
unless load_model_from_cache
case @type
when :obj
@model = Wavefront::Model.new(file_path: @file_path, entity: entity)
else
raise "Unsupported model type, supported models are: #{@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]#.dup # Don't know why, but adding .dup improves performance with Sponza (1 fps -> 20 fps)
puts "Used cached model for: #{@file_path.split('/').last}" if $debug.get(:stats)
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