mirror of
https://github.com/cyberarm/i-mic-fps.git
synced 2025-12-13 06:42:35 +00:00
Moved renderering system and model loading into CyberarmEngine, added island_test_map
This commit is contained in:
3
assets/base/islands_terrain/manifest.yaml
Normal file
3
assets/base/islands_terrain/manifest.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
name: "islands_terrain"
|
||||
model: "islands_terrain.obj"
|
||||
collision: "mesh"
|
||||
32
assets/base/islands_terrain/model/islands_terrain.mtl
Normal file
32
assets/base/islands_terrain/model/islands_terrain.mtl
Normal 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
|
||||
5920
assets/base/islands_terrain/model/islands_terrain.obj
Normal file
5920
assets/base/islands_terrain/model/islands_terrain.obj
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 681 B |
@@ -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
|
||||
BIN
blends/islands_terrain.blend
Normal file
BIN
blends/islands_terrain.blend
Normal file
Binary file not shown.
@@ -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__))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
227
lib/model.rb
227
lib/model.rb
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 = []
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
217
maps/islands_test_map.json
Normal 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
162
svg/faction_two_ghort.svg
Normal 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 |
Reference in New Issue
Block a user