Moved renderering system and model loading into CyberarmEngine, added island_test_map

This commit is contained in:
2020-07-15 21:28:57 -05:00
parent 9264ef6e58
commit 65cfc1a124
29 changed files with 6357 additions and 1874 deletions

View File

@@ -0,0 +1,3 @@
name: "islands_terrain"
model: "islands_terrain.obj"
collision: "mesh"

View File

@@ -0,0 +1,32 @@
# Blender MTL File: 'islands_terrain.blend'
# Material Count: 3
newmtl Ground
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.137348 0.064835 0.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
newmtl Rock
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.388300 0.159443 0.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
newmtl Water
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.003266 0.332269 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 681 B

View File

@@ -6,6 +6,6 @@ on.create do |event|
map.insert_entity("base", "door", event.entity.position + Vector.new(3.3, 0, 6), Vector.new(0, 0, 0))
map.insert_entity("base", "door", event.entity.position + Vector.new(3.3, 0, 6), Vector.new(0, 180, 0))
map.insert_particle_emitter(Vector.new(3.0, 15.379, 0.029), Texture.new(path: ["base", "shared", "particles", "smoke", "smoke.png"]))
map.insert_particle_emitter(Vector.new(5.0, 15.379, 0.029), Texture.new(path: ["base", "shared", "particles", "smoke", "smoke.png"]))
# map.insert_particle_emitter(Vector.new(3.0, 15.379, 0.029), Texture.new(path: ["base", "shared", "particles", "smoke", "smoke.png"]))
# map.insert_particle_emitter(Vector.new(5.0, 15.379, 0.029), Texture.new(path: ["base", "shared", "particles", "smoke", "smoke.png"]))
end

Binary file not shown.

View File

@@ -12,9 +12,11 @@ require "nokogiri"
begin
require_relative "../cyberarm_engine/lib/cyberarm_engine"
require_relative "../cyberarm_engine/lib/cyberarm_engine/opengl"
rescue LoadError => e
pp e
require "cyberarm_engine"
require "cyberarm_engine/opengl"
end
Dir.chdir(File.dirname(__FILE__))

View File

@@ -87,14 +87,5 @@ class IMICFPS
-2
)
end
def gl_error?
e = glGetError()
if e != GL_NO_ERROR
$stderr.puts "OpenGL error detected by handler at: #{caller[0]}"
$stderr.puts " #{gluErrorString(e)} (#{e})\n"
exit if window.config.get(:debug_options, :opengl_error_panic)
end
end
end
end

View File

@@ -1,11 +1,6 @@
class IMICFPS
GAME_ROOT_PATH = File.expand_path("..", File.dirname(__FILE__))
TextureCoordinate = Struct.new(:u, :v, :weight)
Point = Struct.new(:x, :y)
Color = Struct.new(:red, :green, :blue, :alpha)
Face = Struct.new(:vertices, :uvs, :normals, :colors, :material, :smoothing)
# Objects exported from blender using the default or meter object scale will be close to 1 GL unit
MODEL_METER_SCALE = 1.0
@@ -13,4 +8,4 @@ class IMICFPS
EARTH_GRAVITY = 9.8 # m/s
# Moon
MOON_GRAVITY = 1.625 # m/s
end
end

View File

@@ -1,197 +0,0 @@
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, min_view_distance: 0.1, max_view_distance: 155.0)
@position = position
@orientation = orientation
@field_of_view = fov
@min_view_distance = min_view_distance
@max_view_distance = max_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, @max_view_distance)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
glRotatef(@orientation.x, 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.x -= Float(@true_mouse.y - self.mouse_y) / (@mouse_sensitivity * @field_of_view) * 70
@orientation.x = @orientation.x.clamp(-90.0, 90.0)
if @entity
@entity.orientation.y += delta
@entity.orientation.y %= 360.0
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 = 2.5
relative_speed = 1.5 if InputMapper.down?(:sneak)
relative_speed = 10.0 if InputMapper.down?(:sprint)
relative_speed *= window.dt
turn_speed = 50.0 * window.dt
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?(:turn_left)
@orientation.y -= turn_speed
end
if InputMapper.down?(:turn_right)
@orientation.y += turn_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)
@max_view_distance += 1
@max_view_distance = @max_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)
@max_view_distance -= 1
@max_view_distance = @max_view_distance.clamp(1, 1000)
end
end
def aspect_ratio
window.width / window.height.to_f
end
def projection_matrix
Transform.perspective(@field_of_view, aspect_ratio, @min_view_distance, @max_view_distance)
end
def view_matrix
Transform.translate_3d(@position * -1) * Transform.rotate_3d(@orientation)
end
end
end

View File

@@ -1,49 +0,0 @@
class IMICFPS
class Light
DIRECTIONAL = 0
POINT = 1
SPOT = 2
attr_reader :light_id
attr_accessor :type, :ambient, :diffuse, :specular, :position, :direction, :intensity
def initialize(
id:,
type: Light::POINT,
ambient: Vector.new(0.5, 0.5, 0.5),
diffuse: Vector.new(1, 1, 1),
specular: Vector.new(0.2, 0.2, 0.2),
position: Vector.new(0, 0, 0),
direction: Vector.new(0, 0, 0),
intensity: 1
)
@light_id = id
@type = type
@ambient = ambient
@diffuse = diffuse
@specular = specular
@position = position
@direction = direction
@intensity = intensity
end
def draw
glLightfv(@light_id, GL_AMBIENT, convert(@ambient).pack("f*"))
glLightfv(@light_id, GL_DIFFUSE, convert(@diffuse, true).pack("f*"))
glLightfv(@light_id, GL_SPECULAR, convert(@specular, true).pack("f*"))
glLightfv(@light_id, GL_POSITION, convert(@position).pack("f*"))
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

@@ -1,227 +0,0 @@
class IMICFPS
class Model
include CommonMethods
attr_accessor :objects, :materials, :vertices, :uvs, :texures, :normals, :faces, :colors, :bones
attr_accessor :material_file, :current_material, :current_object, :vertex_count, :smoothing
attr_reader :position, :bounding_box, :textured_material, :file_path
attr_reader :positions_buffer_id, :colors_buffer_id, :normals_buffer_id, :uvs_buffer_id, :textures_buffer_id
attr_reader :vertex_array_id
attr_reader :aabb_tree
def initialize(file_path:)
@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)
type = File.basename(file_path).split(".").last.to_sym
parser = Model::Parser.find(type)
unless parser
raise "Unsupported model type '.#{type}', supported models are: #{Model::Parser.supported_formats}"
end
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 window.config.get(:debug_options, :stats)
@has_texture = false
@materials.each do |key, material|
if material.texture_id
@has_texture = true
end
end
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 window.config.get(:debug_options, :stats)
end
window.number_of_vertices+=@vertex_count
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 window.config.get(:debug_options, :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
@positions_buffer_id = nil
buffer = " " * 4
glGenBuffers(1, buffer)
@positions_buffer_id = buffer.unpack('L2').first
@colors_buffer_id = nil
buffer = " " * 4
glGenBuffers(1, buffer)
@colors_buffer_id = buffer.unpack('L2').first
@normals_buffer_id = nil
buffer = " " * 4
glGenBuffers(1, buffer)
@normals_buffer_id = buffer.unpack('L2').first
@uvs_buffer_id = nil
buffer = " " * 4
glGenBuffers(1, buffer)
@uvs_buffer_id = buffer.unpack('L2').first
end
def populate_vertex_buffer
pos = []
colors = []
norms = []
uvs = []
@faces.each do |face|
pos << face.vertices.map { |vert| [vert.x, vert.y, vert.z] }
colors << face.colors.map { |color| [color.red, color.green, color.blue] }
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] }
end
end
glBindBuffer(GL_ARRAY_BUFFER, @positions_buffer_id)
glBufferData(GL_ARRAY_BUFFER, pos.flatten.size * Fiddle::SIZEOF_FLOAT, pos.flatten.pack("f*"), GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, @colors_buffer_id)
glBufferData(GL_ARRAY_BUFFER, colors.flatten.size * Fiddle::SIZEOF_FLOAT, colors.flatten.pack("f*"), GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, @normals_buffer_id)
glBufferData(GL_ARRAY_BUFFER, norms.flatten.size * Fiddle::SIZEOF_FLOAT, norms.flatten.pack("f*"), GL_STATIC_DRAW)
if has_texture?
glBindBuffer(GL_ARRAY_BUFFER, @uvs_buffer_id)
glBufferData(GL_ARRAY_BUFFER, uvs.flatten.size * Fiddle::SIZEOF_FLOAT, uvs.flatten.pack("f*"), GL_STATIC_DRAW)
end
glBindBuffer(GL_ARRAY_BUFFER, 0)
end
def configure_vao
glBindVertexArray(@vertex_array_id)
gl_error?
# index, size, type, normalized, stride, pointer
# vertices (positions)
glBindBuffer(GL_ARRAY_BUFFER, @positions_buffer_id)
gl_error?
# inPosition
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nil)
gl_error?
# colors
glBindBuffer(GL_ARRAY_BUFFER, @colors_buffer_id)
# inColor
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nil)
gl_error?
# normals
glBindBuffer(GL_ARRAY_BUFFER, @normals_buffer_id)
# inNormal
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, nil)
gl_error?
if has_texture?
# uvs
glBindBuffer(GL_ARRAY_BUFFER, @uvs_buffer_id)
# inUV
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, nil)
gl_error?
end
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 *= 1.5
box.max *= 1.5
@aabb_tree.insert(face, box)
end
puts @aabb_tree.inspect if window.config.get(:debug_options, :stats)
end
def has_texture?
@has_texture
end
def release_gl_resources
if @vertex_array_id
end
end
end
end

View File

@@ -1,20 +0,0 @@
class IMICFPS
class Model
class Material
attr_accessor :name, :ambient, :diffuse, :specular
attr_reader :texture_id
def initialize(name)
@name = name
@ambient = Color.new(1, 1, 1, 1)
@diffuse = Color.new(1, 1, 1, 1)
@specular= Color.new(1, 1, 1, 1)
@texture = nil
@texture_id = nil
end
def set_texture(texture_path)
@texture_id = Texture.new(path: texture_path).id
end
end
end
end

View File

@@ -1,128 +0,0 @@
class IMICFPS
class Model
class ModelObject
attr_reader :id, :name, :vertices, :uvs, :normals, :materials, :bounding_box, :debug_color
attr_accessor :faces, :scale
def initialize(id, name)
@id = id
@name = name
@vertices = []
@uvs = []
@normals = []
@faces = []
@materials = []
@bounding_box = BoundingBox.new
@debug_color = Color.new(1.0,1.0,1.0)
@scale = 1.0
# Faces array packs everything:
# vertex = index[0]
# uv = index[1]
# normal = index[2]
# material = index[3]
end
def has_texture?
@materials.find { |mat| mat.texture_id } ? true : false
end
def reflatten
@vertices_list = nil
@uvs_list = nil
@normals_list = nil
flattened_vertices
flattened_uvs
flattened_normals
end
def flattened_vertices
unless @vertices_list
@debug_color = @faces.first.material.diffuse
list = []
@faces.each do |face|
face.vertices.each do |v|
next unless v
list << v.x*@scale
list << v.y*@scale
list << v.z*@scale
list << v.weight
end
end
@vertices_list_size = list.size
@vertices_list = list.pack("f*")
end
return @vertices_list
end
def flattened_vertices_size
@vertices_list_size
end
def flattened_uvs
unless @uvs_list
list = []
@faces.each do |face|
face.uvs.each do |v|
next unless v
list << v.x
list << v.y
list << v.z
end
end
@uvs_list_size = list.size
@uvs_list = list.pack("f*")
end
return @uvs_list
end
def flattened_normals
unless @normals_list
list = []
@faces.each do |face|
face.normals.each do |n|
next unless n
list << n.x
list << n.y
list << n.z
end
end
@normals_list_size = list.size
@normals_list = list.pack("f*")
end
return @normals_list
end
def flattened_materials
unless @materials_list
list = []
@faces.each do |face|
material = face.material
next unless material
face.vertices.each do # Add material to each vertex
list << material.diffuse.red
list << material.diffuse.green
list << material.diffuse.blue
# list << material.alpha
end
end
@materials_list_size = list.size
@materials_list = list.pack("f*")
end
return @materials_list
end
end
end
end

View File

@@ -1,70 +0,0 @@
class IMICFPS
class Model
class Parser
@@parsers = []
def self.handles
raise NotImplementedError, "Model::Parser#handles must return an array of file extensions that this parser supports"
end
def self.inherited(parser)
@@parsers << parser
end
def self.find(file_type)
found_parser = @@parsers.find do |parser|
parser.handles.include?(file_type)
end
return found_parser
end
def self.supported_formats
@@parsers.map { |parser| parser.handles }.flatten.map { |s| ".#{s}" }.join(", ")
end
def initialize(model)
@model = model
end
def parse
end
def set_object(id: nil, name: nil)
_model = nil
if id
_model = @model.objects.find { |o| o.id == id }
elsif name
_model = @model.objects.find { |o| o.name == name }
else
raise "Must provide either an id: or name:"
end
if _model
@model.current_object = _model
else
raise "Couldn't find ModelObject!"
end
end
def change_object(id, name)
@model.objects << Model::ModelObject.new(id, name)
@model.current_object = @model.objects.last
end
def set_material(name)
@model.current_material = name
@model.current_object.materials << current_material
end
def add_material(name, material)
@model.materials[name] = material
end
def current_material
@model.materials[@model.current_material]
end
end
end
end

View File

@@ -1,135 +0,0 @@
class IMICFPS
class ColladaParser < Model::Parser
def self.handles
[:dae]
end
def parse
@collada = Nokogiri::XML(File.read(@model.file_path))
@collada.css("library_materials material").each do |material|
parse_material(material)
end
@collada.css("library_geometries geometry").each do |geometry|
parse_geometry(geometry)
end
@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_material(material)
name = material.attributes["id"].value
effect_id = material.at_css("instance_effect").attributes["url"].value
mat = Model::Material.new(name)
effect = @collada.at_css("[id=\"#{effect_id.sub('#', '')}\"]")
emission = effect.at_css("emission color")
diffuse = effect.at_css("diffuse color").children.first.to_s.split(" ").map { |c| Float(c) }
mat.diffuse = Color.new(*diffuse[0..2])
add_material(name, mat)
end
def parse_geometry(geometry)
geometry_id = geometry.attributes["id"].value
geometry_name = geometry.attributes["name"].value
change_object(geometry_id, geometry_name)
mesh = geometry.at_css("mesh")
get_positions(geometry_id, mesh)
get_normals(geometry_id, mesh)
get_texture_coordinates(geometry_id, mesh)
project_node(geometry_name)
build_faces(geometry_id, mesh)
end
def get_positions(id, mesh)
positions = mesh.at_css("[id=\"#{id}-positions\"]")
array = positions.at_css("[id=\"#{id}-positions-array\"]")
stride = Integer(positions.at_css("[source=\"##{id}-positions-array\"]").attributes["stride"].value)
list = array.children.first.to_s.split(" ").map{ |f| Float(f) }.each_slice(stride).each do |slice|
position = Vector.new(*slice)
@model.current_object.vertices << position
@model.vertices << position
end
end
def get_normals(id, mesh)
normals = mesh.at_css("[id=\"#{id}-normals\"]")
array = normals.at_css("[id=\"#{id}-normals-array\"]")
stride = Integer(normals.at_css("[source=\"##{id}-normals-array\"]").attributes["stride"].value)
list = array.children.first.to_s.split(" ").map{ |f| Float(f) }.each_slice(stride).each do |slice|
normal = Vector.new(*slice)
@model.current_object.normals << normal
@model.normals << normal
end
end
def get_texture_coordinates(id, mesh)
end
def project_node(name)
@collada.css("library_visual_scenes visual_scene node").each do |node|
if node.attributes["name"].value == name
transform = Transform.new( node.at_css("matrix").children.first.to_s.split(" ").map { |f| Float(f) } )
@model.current_object.vertices.each do |vert|
v = vert.multiply_transform(transform)
vert.x, vert.y, vert.z, vert.w = v.x, v.y, v.z, v.w
end
break
end
end
end
def build_faces(id, mesh)
material_name = mesh.at_css("triangles").attributes["material"].value
set_material(material_name)
positions_index = []
normals_index = []
uvs_index = []
mesh.at_css("triangles p").children.first.to_s.split(" ").map { |i| Integer(i) }.each_slice(3).each do |slice|
positions_index << slice[0]
normals_index << slice[1]
uvs_index << slice[2]
end
norm_index = 0
positions_index.each_slice(3) do |slice|
face = Face.new
face.vertices = []
face.uvs = []
face.normals = []
face.colors = []
face.material = current_material
face.smoothing= @model.smoothing
slice.each do |index|
face.vertices << @model.vertices[index]
# face.uvs << @model.uvs[index]
face.normals << @model.normals[normals_index[norm_index]]
face.colors << current_material.diffuse
norm_index += 1
end
@model.current_object.faces << face
@model.faces << face
end
end
end
end

View File

@@ -1,157 +0,0 @@
class IMICFPS
class WavefrontParser < Model::Parser
def self.handles
[:obj]
end
def parse
lines = 0
list = File.read(@model.file_path).split("\n")
list.each do |line|
lines+=1
line = line.strip
array = line.split(' ')
case array[0]
when 'mtllib'
@model.material_file = array[1]
parse_mtllib
when 'usemtl'
set_material(array[1])
when 'o'
change_object(nil, 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
face = Face.new
face.vertices = []
face.uvs = []
face.normals = []
face.colors = []
face.material = current_material
face.smoothing= @model.smoothing
mat = face.material.diffuse
color = mat
verts.each_with_index do |v, index|
if uvs.first != ""
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 << @model.vertices[Integer(v)-1]
face.uvs << nil
face.normals << @model.normals[Integer(norms[index])-1]
face.colors << color
end
end
@model.current_object.faces << face
@model.faces << face
end
end
puts "Total Lines: #{lines}" if $window.config.get(:debug_options, :stats)
@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(@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 = Model::Material.new(array.last)
@model.current_material = array.last
@model.materials[array.last] = material
when 'Ns' # Specular Exponent
when 'Ka' # Ambient color
@model.materials[@model.current_material].ambient = Color.new(Float(array[1]), Float(array[2]), Float(array[3]))
when 'Kd' # Diffuse color
@model.materials[@model.current_material].diffuse = Color.new(Float(array[1]), Float(array[2]), Float(array[3]))
when 'Ks' # Specular color
@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
texture = File.basename(array[1])
texture_path = "#{File.expand_path("../../", @model.file_path)}/textures/#{texture}"
@model.materials[@model.current_material].set_texture(texture_path)
end
end
end
def set_smoothing(value)
if value == "1"
@model.smoothing = true
else
@model.smoothing = false
end
end
def add_vertex(array)
@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]))
elsif array.size == 4
vert = Vector.new(Float(array[1]), Float(array[2]), Float(array[3]), 1.0)
else
raise
end
@model.current_object.vertices << vert
@model.vertices << vert
end
def add_normal(array)
vert = nil
if array.size == 5
vert = Vector.new(Float(array[1]), Float(array[2]), Float(array[3]), Float(array[4]))
elsif array.size == 4
vert = Vector.new(Float(array[1]), Float(array[2]), Float(array[3]), 1.0)
else
raise
end
@model.current_object.normals << vert
@model.normals << vert
end
def add_texture_coordinate(array)
texture = nil
if array.size == 4
texture = Vector.new(Float(array[1]), 1-Float(array[2]), Float(array[3]))
elsif array.size == 3
texture = Vector.new(Float(array[1]), 1-Float(array[2]), 1.0)
else
raise
end
@model.uvs << texture
@model.current_object.uvs << texture
end
end
end

View File

@@ -1,36 +0,0 @@
class IMICFPS
module ModelCache
CACHE = {}
def self.find_or_cache(manifest:)
model_file = manifest.file_path + "/model/#{manifest.model}"
type = File.basename(model_file).split(".").last.to_sym
if model = load_model_from_cache(type, model_file)
return model
else
model = IMICFPS::Model.new(file_path: model_file)
cache_model(type, model_file, model)
return model
end
end
def self.load_model_from_cache(type, model_file)
if CACHE[type].is_a?(Hash)
if CACHE[type][model_file]
puts "Used cached model for: #{model_file.split('/').last}" if $window.config.get(:debug_options, :stats)
return CACHE[type][model_file]
end
end
return false
end
def self.cache_model(type, model_file, model)
CACHE[type] = {} unless CACHE[type].is_a?(Hash)
CACHE[type][model_file] = model
end
end
end

View File

@@ -1,248 +0,0 @@
class IMICFPS
class BoundingBoxRenderer
attr_reader :bounding_boxes, :vertex_count
def initialize
@bounding_boxes = {}
@vertex_count = 0
end
def render(entities)
entities.each do |entity|
create_bounding_box(entity,color = nil)
draw_bounding_boxes
end
(@bounding_boxes.keys - entities.map { |e| e.object_id }).each do |key|
@bounding_boxes.delete(key)
end
end
def create_bounding_box(entity, color = nil)
color ||= entity.debug_color
entity_id = entity.object_id
if @bounding_boxes[entity_id]
if @bounding_boxes[entity_id][:color] != color
@bounding_boxes[entity_id][:colors] = mesh_colors(color).pack("f*")
@bounding_boxes[entity_id][:color] = color
return
else
return
end
end
@bounding_boxes[entity_id] = {
entity: entity,
color: color,
objects: []
}
box = entity.normalize_bounding_box
normals = mesh_normals
colors = mesh_colors(color)
vertices = mesh_vertices(box)
@vertex_count+=vertices.size
@bounding_boxes[entity_id][:vertices_size] = vertices.size
@bounding_boxes[entity_id][:vertices] = vertices.pack("f*")
@bounding_boxes[entity_id][:normals] = normals.pack("f*")
@bounding_boxes[entity_id][:colors] = colors.pack("f*")
entity.model.objects.each do |mesh|
data = {}
box = mesh.bounding_box.normalize(entity)
normals = mesh_normals
colors = mesh_colors(mesh.debug_color)
vertices = mesh_vertices(box)
@vertex_count+=vertices.size
data[:vertices_size] = vertices.size
data[:vertices] = vertices.pack("f*")
data[:normals] = normals.pack("f*")
data[:colors] = colors.pack("f*")
@bounding_boxes[entity_id][:objects] << data
end
end
def mesh_normals
[
0,1,0,
0,1,0,
0,1,0,
0,1,0,
0,1,0,
0,1,0,
0,-1,0,
0,-1,0,
0,-1,0,
0,-1,0,
0,-1,0,
0,-1,0,
0,0,1,
0,0,1,
0,0,1,
0,0,1,
0,0,1,
0,0,1,
1,0,0,
1,0,0,
1,0,0,
1,0,0,
1,0,0,
1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0,
-1,0,0
]
end
def mesh_colors(color)
[
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue,
color.red, color.green, color.blue
]
end
def mesh_vertices(box)
[
box.min.x, box.max.y, box.max.z,
box.min.x, box.max.y, box.min.z,
box.max.x, box.max.y, box.min.z,
box.min.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.min.z,
box.max.x, box.min.y, box.min.z,
box.max.x, box.min.y, box.max.z,
box.min.x, box.min.y, box.max.z,
box.max.x, box.min.y, box.min.z,
box.min.x, box.min.y, box.min.z,
box.min.x, box.min.y, box.max.z,
box.min.x, box.max.y, box.max.z,
box.min.x, box.max.y, box.min.z,
box.min.x, box.min.y, box.min.z,
box.min.x, box.min.y, box.max.z,
box.min.x, box.min.y, box.min.z,
box.min.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.min.z,
box.max.x, box.min.y, box.min.z,
box.max.x, box.min.y, box.max.z,
box.max.x, box.min.y, box.min.z,
box.max.x, box.max.y, box.max.z,
box.min.x, box.max.y, box.max.z,
box.max.x, box.max.y, box.max.z,
box.max.x, box.min.y, box.max.z,
box.min.x, box.max.y, box.max.z,
box.max.x, box.min.y, box.max.z,
box.min.x, box.min.y, box.max.z,
box.max.x, box.min.y, box.min.z,
box.min.x, box.min.y, box.min.z,
box.min.x, box.max.y, box.min.z,
box.max.x, box.min.y, box.min.z,
box.min.x, box.max.y, box.min.z,
box.max.x, box.max.y, box.min.z
]
end
def draw_bounding_boxes
@bounding_boxes.each do |key, bounding_box|
glPushMatrix
glTranslatef(
bounding_box[:entity].position.x,
bounding_box[:entity].position.y,
bounding_box[:entity].position.z
)
draw_bounding_box(bounding_box)
@bounding_boxes[key][:objects].each {|o| draw_bounding_box(o)}
glPopMatrix
end
end
def draw_bounding_box(bounding_box)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
glVertexPointer(3, GL_FLOAT, 0, bounding_box[:vertices])
glColorPointer(3, GL_FLOAT, 0, bounding_box[:colors])
glNormalPointer(GL_FLOAT, 0, bounding_box[:normals])
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
glDisable(GL_LIGHTING)
glDrawArrays(GL_TRIANGLES, 0, bounding_box[:vertices_size] / 3)
glEnable(GL_LIGHTING)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
end
end
end

View File

@@ -1,170 +0,0 @@
class IMICFPS
class GBuffer
include CommonMethods
attr_reader :screen_vbo, :vertices, :uvs
def initialize
@framebuffer = nil
@buffers = [:position, :diffuse, :normal, :texcoord]
@textures = {}
@screen_vbo = nil
@ready = false
@vertices = [
-1.0, -1.0, 0,
1.0, -1.0, 0,
-1.0, 1.0, 0,
-1.0, 1.0, 0,
1.0, -1.0, 0,
1.0, 1.0, 0,
].freeze
@uvs = [
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1
].freeze
create_framebuffer
create_screen_vbo
end
def width
window.width
end
def height
window.height
end
def create_framebuffer
buffer = ' ' * 4
glGenFramebuffers(1, buffer)
@framebuffer = buffer.unpack('L2').first
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, @framebuffer)
create_textures
status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
if status != GL_FRAMEBUFFER_COMPLETE
message = ""
case status
when GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
message = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"
when GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
message = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"
when GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
message = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"
when GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
message = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"
when GL_FRAMEBUFFER_UNSUPPORTED
message = "GL_FRAMEBUFFER_UNSUPPORTED"
else
message = "Unknown error!"
end
puts "Incomplete framebuffer: #{status}\nError: #{message}"
else
@ready = true
end
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
end
def create_textures
@buffers.size.times do |i|
buffer = ' ' * 4
glGenTextures(1, buffer)
texture_id = buffer.unpack('L2').first
@textures[@buffers[i]] = texture_id
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, nil)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, texture_id, 0)
end
buffer = ' ' * 4
glGenTextures(1, buffer)
texture_id = buffer.unpack('L2').first
@textures[:depth] = texture_id
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nil)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture_id, 0)
draw_buffers = [ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 ]
glDrawBuffers(draw_buffers.size, draw_buffers.pack("I*"))
end
def create_screen_vbo
buffer = ' ' * 4
glGenVertexArrays(1, buffer)
@screen_vbo = buffer.unpack('L2').first
buffer = " " * 4
glGenBuffers(1, buffer)
@positions_buffer_id = buffer.unpack('L2').first
buffer = " " * 4
glGenBuffers(1, buffer)
@uvs_buffer_id = buffer.unpack('L2').first
glBindVertexArray(@screen_vbo)
glBindBuffer(GL_ARRAY_BUFFER, @positions_buffer_id)
glBufferData(GL_ARRAY_BUFFER, @vertices.size * Fiddle::SIZEOF_FLOAT, @vertices.pack("f*"), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nil)
glBindBuffer(GL_ARRAY_BUFFER, @uvs_buffer_id)
glBufferData(GL_ARRAY_BUFFER, @uvs.size * Fiddle::SIZEOF_FLOAT, @uvs.pack("f*"), GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nil)
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glBindVertexArray(0)
end
def bind_for_writing
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, @framebuffer)
end
def bind_for_reading
glBindFramebuffer(GL_READ_FRAMEBUFFER, @framebuffer)
end
def set_read_buffer(buffer)
glReadBuffer(GL_COLOR_ATTACHMENT0 + @textures.keys.index(buffer))
end
def set_read_buffer_depth
glReadBuffer(GL_DEPTH_ATTACHMENT)
end
def unbind_framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0)
end
def texture(type)
@textures[type]
end
def clean_up
glDeleteFramebuffers(1, [@framebuffer].pack("L"))
glDeleteTextures(@textures.values.size, @textures.values.pack("L*"))
glDeleteBuffers(2, [@positions_buffer_id, @uvs_buffer_id].pack("L*"))
glDeleteVertexArrays(1, [@screen_vbo].pack("L"))
gl_error?
end
end
end

View File

@@ -1,285 +0,0 @@
class IMICFPS
class OpenGLRenderer
include CommonMethods
@@immediate_mode_warning = false
def initialize
@g_buffer = GBuffer.new
end
def canvas_size_changed
@g_buffer.unbind_framebuffer
@g_buffer.clean_up
@g_buffer = GBuffer.new
end
def render(camera, lights, entities)
if window.config.get(:debug_options, :use_shaders) && Shader.available?("g_buffer") && Shader.available?("lighting")
@g_buffer.bind_for_writing
gl_error?
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
Shader.use("g_buffer") do |shader|
gl_error?
entities.each do |entity|
next unless entity.visible && entity.renderable
shader.uniform_transform("projection", camera.projection_matrix)
shader.uniform_transform("view", camera.view_matrix)
shader.uniform_transform("model", entity.model_matrix)
shader.uniform_vec3("cameraPosition", camera.position)
gl_error?
draw_model(entity.model, shader)
entity.draw
end
end
@g_buffer.unbind_framebuffer
gl_error?
@g_buffer.bind_for_reading
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
lighting(lights)
gl_error?
post_processing
gl_error?
# render_framebuffer
gl_error?
@g_buffer.unbind_framebuffer
gl_error?
else
puts "Shaders are disabled or failed to compile, using immediate mode for rendering..." unless @@immediate_mode_warning
@@immediate_mode_warning = true
gl_error?
lights.each(&:draw)
camera.draw
glEnable(GL_NORMALIZE)
entities.each do |entity|
next unless entity.visible && entity.renderable
glPushMatrix
glTranslatef(entity.position.x, entity.position.y, entity.position.z)
glScalef(entity.scale.x, entity.scale.y, entity.scale.z)
glRotatef(entity.orientation.x, 1.0, 0, 0)
glRotatef(entity.orientation.y, 0, 1.0, 0)
glRotatef(entity.orientation.z, 0, 0, 1.0)
gl_error?
draw_mesh(entity.model)
entity.draw
glPopMatrix
end
end
gl_error?
end
def copy_g_buffer_to_screen
@g_buffer.set_read_buffer(:position)
glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
0, 0, @g_buffer.width / 2, @g_buffer.height / 2,
GL_COLOR_BUFFER_BIT, GL_LINEAR)
@g_buffer.set_read_buffer(:diffuse)
glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
0, @g_buffer.height / 2, @g_buffer.width / 2, @g_buffer.height,
GL_COLOR_BUFFER_BIT, GL_LINEAR)
@g_buffer.set_read_buffer(:normal)
glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
@g_buffer.width / 2, @g_buffer.height / 2, @g_buffer.width, @g_buffer.height,
GL_COLOR_BUFFER_BIT, GL_LINEAR)
@g_buffer.set_read_buffer(:texcoord)
glBlitFramebuffer(0, 0, @g_buffer.width, @g_buffer.height,
@g_buffer.width / 2, 0, @g_buffer.width, @g_buffer.height / 2,
GL_COLOR_BUFFER_BIT, GL_LINEAR)
end
def lighting(lights)
Shader.use("lighting") do |shader|
glBindVertexArray(@g_buffer.screen_vbo)
glDisable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:diffuse))
shader.uniform_integer("diffuse", 0)
glActiveTexture(GL_TEXTURE1)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:position))
shader.uniform_integer("position", 1)
glActiveTexture(GL_TEXTURE2)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:texcoord))
shader.uniform_integer("texcoord", 2)
glActiveTexture(GL_TEXTURE3)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:normal))
shader.uniform_integer("normal", 3)
glActiveTexture(GL_TEXTURE4)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:depth))
shader.uniform_integer("depth", 4)
lights.each_with_index do |light, i|
shader.uniform_integer("light[0].type", light.type);
shader.uniform_vec3("light[0].direction", light.direction)
shader.uniform_vec3("light[0].position", light.position)
shader.uniform_vec3("light[0].diffuse", light.diffuse)
shader.uniform_vec3("light[0].ambient", light.ambient)
shader.uniform_vec3("light[0].specular", light.specular)
glDrawArrays(GL_TRIANGLES, 0, @g_buffer.vertices.size)
end
glBindVertexArray(0)
end
end
def post_processing
end
def render_framebuffer
if Shader.available?("lighting")
Shader.use("lighting") do |shader|
glBindVertexArray(@g_buffer.screen_vbo)
glDisable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, @g_buffer.texture(:diffuse))
shader.uniform_integer("diffuse_texture", 0)
glDrawArrays(GL_TRIANGLES, 0, @g_buffer.vertices.size)
glBindVertexArray(0)
end
end
end
def draw_model(model, shader)
glBindVertexArray(model.vertex_array_id)
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glEnableVertexAttribArray(2)
if model.has_texture?
glEnableVertexAttribArray(3)
glEnableVertexAttribArray(4)
end
if window.config.get(:debug_options, :wireframe)
glLineWidth(2)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
Shader.active_shader.uniform_boolean("disableLighting", true)
glDrawArrays(GL_TRIANGLES, 0, model.faces.count * 3)
window.number_of_vertices += model.faces.count * 3
Shader.active_shader.uniform_boolean("disableLighting", false)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
glLineWidth(1)
end
offset = 0
model.objects.each do |object|
shader.uniform_boolean("hasTexture", object.has_texture?)
if object.has_texture?
glBindTexture(GL_TEXTURE_2D, object.materials.find { |mat| mat.texture_id }.texture_id)
else
glBindTexture(GL_TEXTURE_2D, 0)
end
glDrawArrays(GL_TRIANGLES, offset, object.faces.count * 3)
offset += object.faces.count * 3
end
window.number_of_vertices += model.faces.count * 3
if model.has_texture?
glDisableVertexAttribArray(4)
glDisableVertexAttribArray(3)
glBindTexture(GL_TEXTURE_2D, 0)
end
glDisableVertexAttribArray(2)
glDisableVertexAttribArray(1)
glDisableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
end
def draw_mesh(model)
model.objects.each_with_index do |o, i|
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
glShadeModel(GL_FLAT) unless o.faces.first[4]
glShadeModel(GL_SMOOTH) if o.faces.first[4]
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
if o.has_texture?
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, o.materials.find { |mat| mat.texture_id }.texture_id)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glTexCoordPointer(3, GL_FLOAT, 0, o.flattened_uvs)
end
glVertexPointer(4, GL_FLOAT, 0, o.flattened_vertices)
glColorPointer(3, GL_FLOAT, 0, o.flattened_materials)
glNormalPointer(GL_FLOAT, 0, o.flattened_normals)
if window.config.get(:debug_options, :wireframe) # This is kinda expensive
glDisable(GL_LIGHTING)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
glPolygonOffset(2, 0.5)
glLineWidth(3)
glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size/4)
window.number_of_vertices+=model.vertices.size
glLineWidth(1)
glPolygonOffset(0, 0)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
glEnable(GL_LIGHTING)
glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size/4)
window.number_of_vertices+=model.vertices.size
else
glDrawArrays(GL_TRIANGLES, 0, o.flattened_vertices_size/4)
window.number_of_vertices+=model.vertices.size
end
# glBindBuffer(GL_ARRAY_BUFFER, 0)
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
if o.has_texture?
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
glDisable(GL_TEXTURE_2D)
end
glDisable(GL_COLOR_MATERIAL)
end
end
end
end

View File

@@ -1,39 +0,0 @@
class IMICFPS
class Renderer
include CommonMethods
attr_reader :opengl_renderer, :bounding_box_renderer
def initialize
@bounding_box_renderer = BoundingBoxRenderer.new
@opengl_renderer = OpenGLRenderer.new
end
def preload_default_shaders
shaders = ["g_buffer", "lighting"]
shaders.each do |shader|
Shader.new(
name: shader,
includes_dir: "shaders/include",
vertex: "shaders/vertex/#{shader}.glsl",
fragment: "shaders/fragment/#{shader}.glsl"
)
end
end
def draw(camera, lights, entities)
glViewport(0, 0, window.width, window.height)
glEnable(GL_DEPTH_TEST)
@opengl_renderer.render(camera, lights, entities)
@bounding_box_renderer.render(entities) if window.config.get(:debug_options, :boundingboxes)
end
def canvas_size_changed
@opengl_renderer.canvas_size_changed
end
def finalize # cleanup
end
end
end

View File

@@ -3,7 +3,7 @@ class IMICFPS
attr_reader :camera, :entities, :lights
def initialize
@camera = Camera.new(position: Vector.new)
@camera = PerspectiveCamera.new(position: Vector.new, aspect_ratio: $window.aspect_ratio )
@entities = []
@lights = []

View File

@@ -7,8 +7,7 @@ class IMICFPS
@map.setup
@player = @map.find_entity_by(name: "character")
@camera = Camera.new(position: @player.position.clone)
@camera.attach_to(@player)
@camera = PerspectiveCamera.new( position: @player.position.clone, aspect_ratio: window.aspect_ratio )
@director = Networking::Director.new
@director.load_map(map_parser: @options[:map_parser])
@@ -42,7 +41,6 @@ class IMICFPS
control_player
@hud.update
@camera.update
@connection.update
@director.tick(window.dt)
@map.update
@@ -66,9 +64,9 @@ OpenGL Shader Language Version: #{glGetString(GL_SHADING_LANGUAGE_VERSION)}
Camera Pitch: #{@camera.orientation.x.round(2)} Yaw: #{@camera.orientation.y.round(2)} Roll #{@camera.orientation.z.round(2)}
Camera X: #{@camera.position.x.round(2)} Y: #{@camera.position.y.round(2)} Z: #{@camera.position.z.round(2)}
Camera Field Of View: #{@camera.field_of_view}
Camera Mouse Sesitivity: #{@camera.mouse_sensitivity}
Camera Mouse Sesitivity: nil
#{if @camera.entity then "Actor X: #{@camera.entity.position.x.round(2)} Y: #{@camera.entity.position.y.round(2)} Z: #{@camera.entity.position.z.round(2)}";end}
Player X: #{@player.position.x.round(2)} Y: #{@player.position.y.round(2)} Z: #{@player.position.z.round(2)}"
eos
end
@@ -108,8 +106,6 @@ eos
@map.entities.each do |entity|
entity.button_up(id) if defined?(entity.button_up)
end
@camera.button_up(id)
end
end
end

View File

@@ -1,86 +0,0 @@
class IMICFPS
class Texture
DEFAULT_TEXTURE = "#{IMICFPS.assets_path}/base/shared/textures/default.png"
include CommonMethods
CACHE = {}
def self.release_textures
CACHE.values.each do |id|
glDeleteTextures(id)
end
end
def self.from_cache(path, retro)
return CACHE.dig("#{path}?retro=#{retro}")
end
attr_reader :id
def initialize(path: nil, image: nil, retro: false)
raise "keyword :path or :image must be provided!" if path.nil? && image.nil?
@retro = retro
if path.is_a?(Array)
if path.size > 1
# Try loading from models textures folder
path = "#{GAME_ROOT_PATH}/assets/#{path.join("/")}"
else
path = path.first
end
end
# Try searching shared textures folder
if path && !File.exist?(path)
path = "#{IMICFPS.assets_path}/base/shared/textures/#{path.split("/").last}"
end
@path = path
if @path
unless File.exist?(@path)
warn "Missing texture at: #{@path}" if window.config.get(:debug_options, :stats)
@retro = true # override retro setting
@path = DEFAULT_TEXTURE
end
if texture = Texture.from_cache(@path, @retro)
@id = texture.id
puts "Using cached texture: #{@path}" if window.config.get(:debug_options, :stats)
return
end
puts "Allocating texture for: #{@path}" if window.config.get(:debug_options, :stats)
image = load_image(@path)
@id = create_from_image(image)
else
puts "Allocating texture for: #{image}" if window.config.get(:debug_options, :stats)
@id = create_from_image(image)
end
end
def load_image(path)
CACHE["#{path}?retro=#{@retro}"] = self
Gosu::Image.new(path, retro: @retro)
end
def create_from_image(image)
array_of_pixels = image.to_blob
tex_names_buf = ' ' * 4
glGenTextures(1, tex_names_buf)
texture_id = tex_names_buf.unpack('L2').first
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, array_of_pixels)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) if @retro
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) unless @retro
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
glGenerateMipmap(GL_TEXTURE_2D)
gl_error?
return texture_id
end
end
end

View File

@@ -9,7 +9,7 @@ class IMICFPS
# and refactor Game to use it.
Publisher.new
@map = Map.new( map_parser: @options[:map_parser] )
@camera = Camera.new( position: Vector.new )
@camera = PerspectiveCamera.new( position: Vector.new, aspect_ratio: window.aspect_ratio )
@crosshair = Crosshair.new
@map.setup

View File

@@ -24,7 +24,7 @@ class IMICFPS
Commands::Command.setup
@renderer = Renderer.new
@renderer.preload_default_shaders
preload_default_shaders
@scene = TurnTableScene.new
@overlay = Overlay.new
@@ -39,6 +39,18 @@ class IMICFPS
@delta_time = Gosu.milliseconds
end
def preload_default_shaders
shaders = ["g_buffer", "lighting"]
shaders.each do |shader|
Shader.new(
name: shader,
includes_dir: "shaders/include",
vertex: "shaders/vertex/#{shader}.glsl",
fragment: "shaders/fragment/#{shader}.glsl"
)
end
end
def needs_cursor?
false
end

217
maps/islands_test_map.json Normal file
View File

@@ -0,0 +1,217 @@
{
"metadata": {
"name":"Islands Testing Map",
"gamemode":"C&C",
"authors":["Cyberarm"],
"datetime":"2020-05-12 21:15:03 UTC",
"thumbnail":"",
"description":"A map for testing the map specification."
},
"terrain":{
"package":"base",
"name":"islands_terrain",
"water_level":null
},
"skydome":{
"package":"base",
"name":"skydome",
"scale":0.08
},
"lights":[
{
"type":"directional",
"intensity": 1,
"position": {
"x":30,
"y":10,
"z":30
},
"diffuse": {
"red":1,
"green":1,
"blue":1
},
"ambient": {
"red":0.5,
"green":0.5,
"blue":0.5
},
"specular": {
"red":0.2,
"green":0.2,
"blue":0.2
}
},
{
"type":"directional",
"intensity": 1,
"position": {
"x":0,
"y":100,
"z":0
},
"diffuse": {
"red":1,
"green":0.5,
"blue":0.1
},
"ambient": {
"red":0.5,
"green":0.5,
"blue":0.5
},
"specular": {
"red":0.2,
"green":0.2,
"blue":0.2
}
}
],
"entities":[
{
"package":"base",
"name":"tree",
"position": {
"x": -129.563,
"y": 0,
"z": 34.9507
},
"orientation": {
"x": 0,
"y": 37.0,
"z": 0
},
"scale": 1.0,
"scripts": []
},
{
"package":"base",
"name":"tree",
"position": {
"x": 11.0938,
"y": 0,
"z": 102.047
},
"orientation": {
"x": 0,
"y": 37.0,
"z": 0
},
"scale": 1.0,
"scripts": []
},
{
"package":"base",
"name":"tree",
"position": {
"x":-21.2121,
"y":0.06,
"z":6.06061
},
"orientation": {
"x": 0,
"y": 64.0,
"z": 0
},
"scale": 1.0,
"scripts": []
},
{
"package":"base",
"name":"tree",
"position": {
"x":-13.3683,
"y":0,
"z":109.036
},
"orientation": {
"x": 0,
"y": 144.0,
"z": 0
},
"scale": 1.0,
"scripts": []
},
{
"package":"base",
"name":"tree",
"position": {
"x":63.3727,
"y":0,
"z":45.6301
},
"orientation": {
"x": 0,
"y": 100.0,
"z": 0
},
"scale": 1.0,
"scripts": []
},
{
"package":"base",
"name":"tree",
"position": {
"x":74.4436,
"y":0,
"z":49.1526
},
"orientation": {
"x": 0,
"y": 21.0,
"z": 0
},
"scale": 1.0,
"scripts": []
},
{
"package":"base",
"name":"war_factory",
"position": {
"x":39.3939,
"y":0.499657,
"z":9.69697
},
"orientation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": 1,
"scripts": []
}
],
"spawnpoints": [
{
"team":0,
"position": {
"x": -104.133,
"y": 0,
"z": -0.374469
},
"orientation": {
"x": 0,
"y": 180,
"z": 0
}
},
{
"team":1,
"position": {
"x": 183.27,
"y": 0,
"z": -26.4139
},
"orientation": {
"x": 0,
"y": 0,
"z": 0
}
}
]
}

162
svg/faction_two_ghort.svg Normal file
View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="faction_two_ghort.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg86"
version="1.1"
viewBox="0 0 33.866666 33.866668"
height="128"
width="128">
<defs
id="defs80" />
<sodipodi:namedview
inkscape:snap-bbox-midpoints="true"
inkscape:window-maximized="1"
inkscape:window-y="0"
inkscape:window-x="0"
inkscape:window-height="1010"
inkscape:window-width="1920"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox="true"
inkscape:snap-nodes="true"
inkscape:guide-bbox="true"
showguides="true"
inkscape:showpageshadow="false"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer3"
inkscape:document-units="px"
inkscape:cy="73.014627"
inkscape:cx="28.305817"
inkscape:zoom="4"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base">
<inkscape:grid
empspacing="8"
id="grid88"
type="xygrid" />
<sodipodi:guide
inkscape:color="rgb(0,0,255)"
inkscape:locked="false"
inkscape:label=""
id="guide94"
orientation="0,1"
position="16.933333,16.933333" />
<sodipodi:guide
id="guide99"
orientation="0,-1"
position="16.933333,29.633334" />
<sodipodi:guide
id="guide101"
orientation="0,-1"
position="16.933333,4.2333335" />
<sodipodi:guide
id="guide103"
orientation="1,0"
position="6.3499999,14.816667" />
<sodipodi:guide
id="guide105"
orientation="1,0"
position="27.516666,14.816667" />
<sodipodi:guide
id="guide107"
orientation="0,-1"
position="16.933333,10.583334" />
<sodipodi:guide
id="guide109"
orientation="0,-1"
position="16.933333,25.400001" />
</sodipodi:namedview>
<metadata
id="metadata83">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
sodipodi:insensitive="true"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
d="M 31.59803,25.400001 16.933334,33.866667 2.2686381,25.4 2.2686382,8.4666679 16.933334,1.7798752e-6 31.59803,8.4666681 Z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="1.0471976"
sodipodi:arg1="0.52359878"
sodipodi:r2="14.664696"
sodipodi:r1="16.933332"
sodipodi:cy="16.933334"
sodipodi:cx="16.933334"
sodipodi:sides="6"
id="path96"
style="fill:#f9f06b;stroke-width:6.35;stroke-linecap:round;paint-order:stroke markers fill"
sodipodi:type="star" />
</g>
<g
inkscape:label="Layer 2"
id="layer3"
inkscape:groupmode="layer">
<g
style="stroke:none"
id="g1018-5">
<path
sodipodi:nodetypes="ccccc"
transform="scale(0.26458334)"
d="M 64,32 31.999999,41.999999 32,72 63.999999,63.999999 Z"
style="fill:#8ff0a4;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path128-6" />
<path
sodipodi:nodetypes="ccccc"
transform="scale(0.26458334)"
d="m 64,32 -10e-7,31.999999 L 96,72 l -10e-7,-30.000001 z"
style="fill:#33d17a;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path113-2" />
</g>
<ellipse
ry="1.5875005"
rx="3.1750009"
cy="16.404167"
cx="16.933334"
id="path1010"
style="fill:#813d9c;stroke-width:0.264584;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal" />
<path
transform="scale(0.26458334)"
d="M 64 16 L 64 32 L 96 42 L 96 72 L 64 64 L 64 112 L 104 88 L 104 32 L 64 16 z "
style="fill:#a51d2d;stroke:#ffffff;stroke-width:0.99999998;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
id="path111" />
<path
transform="scale(0.26458334)"
d="M 64 16 L 24 32 L 24 88 L 64 112 L 64 64 L 32 72 L 32 42 L 64 32 L 64 16 z "
style="fill:#ed333b;stroke:#ffffff;stroke-width:0.999999px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
id="path122" />
<path
id="path1012"
d="M 25.400001,21.166667 19.05,25.400001"
style="fill:none;stroke:#ffffff;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path1014"
d="M 14.816667,25.400001 8.4666669,21.166667"
style="fill:none;stroke:#ffffff;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB