diff --git a/i-mic-fps.rb b/i-mic-fps.rb index 4f5d311..8d3b31c 100644 --- a/i-mic-fps.rb +++ b/i-mic-fps.rb @@ -84,9 +84,11 @@ require_relative "lib/game_objects/entities/skydome" require_relative "lib/game_objects/entities/terrain" require_relative "lib/model" -require_relative "lib/wavefront/parser" -require_relative "lib/wavefront/object" -require_relative "lib/wavefront/material" +require_relative "lib/model/parser" +require_relative "lib/model/model_object" +require_relative "lib/model/material" + +require_relative "lib/model/parsers/wavefront_parser" require_relative "lib/map_parser" require_relative "lib/manifest" diff --git a/lib/game_objects/model_loader.rb b/lib/game_objects/model_loader.rb index 432b65c..05eacdd 100644 --- a/lib/game_objects/model_loader.rb +++ b/lib/game_objects/model_loader.rb @@ -21,7 +21,7 @@ class IMICFPS unless load_model_from_cache case @type when :obj - @model = IMICFPS::Model.new(file_path: @model_file, parser: Wavefront::Parser) + @model = IMICFPS::Model.new(file_path: @model_file) else raise "Unsupported model type, supported models are: #{@supported_models.join(', ')}" end diff --git a/lib/model.rb b/lib/model.rb index 890ca90..69c8722 100644 --- a/lib/model.rb +++ b/lib/model.rb @@ -10,14 +10,16 @@ class IMICFPS attr_reader :vertex_array_id attr_reader :aabb_tree - def initialize(file_path:, entity: nil, parser:) + def initialize(file_path:, entity: nil) + @file_path = file_path @entity = entity update if @entity - @file_path = file_path + @material_file = nil @current_object = nil @current_material=nil @vertex_count = 0 + @objects = [] @materials= {} @vertices = [] @@ -31,7 +33,7 @@ class IMICFPS @bounding_box = BoundingBox.new start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - parse(parser) + parse( Model::Parser.find(File.basename(file_path).split(".").last.to_sym) ) 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) diff --git a/lib/wavefront/material.rb b/lib/model/material.rb similarity index 98% rename from lib/wavefront/material.rb rename to lib/model/material.rb index df7e4f1..f09e875 100644 --- a/lib/wavefront/material.rb +++ b/lib/model/material.rb @@ -1,5 +1,5 @@ class IMICFPS - class Wavefront + class Model class Material attr_accessor :name, :ambient, :diffuse, :specular attr_reader :texture_id diff --git a/lib/wavefront/object.rb b/lib/model/model_object.rb similarity index 98% rename from lib/wavefront/object.rb rename to lib/model/model_object.rb index cf60fd4..22f6d77 100644 --- a/lib/wavefront/object.rb +++ b/lib/model/model_object.rb @@ -1,6 +1,6 @@ class IMICFPS - class Wavefront - class Object + class Model + class ModelObject attr_reader :name, :vertices, :textures, :normals, :bounding_box, :debug_color attr_accessor :faces, :scale diff --git a/lib/model/parser.rb b/lib/model/parser.rb new file mode 100644 index 0000000..f81a3f8 --- /dev/null +++ b/lib/model/parser.rb @@ -0,0 +1,30 @@ +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 initialize(model) + @model = model + end + + def parse + end + end + end +end \ No newline at end of file diff --git a/lib/model/parsers/wavefront_parser.rb b/lib/model/parsers/wavefront_parser.rb new file mode 100644 index 0000000..4ddd76e --- /dev/null +++ b/lib/model/parsers/wavefront_parser.rb @@ -0,0 +1,180 @@ +class IMICFPS + class WavefrontParser < Model::Parser + def self.handles + [:obj] + end + + def initialize(model) + @model = model + 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(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 = material + face.smoothing= @model.smoothing + + mat = face.material.diffuse + color = Vector.new(mat.red, mat.green, mat.blue) + + 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 change_object(name) + @model.objects << Model::ModelObject.new(name) + @model.current_object = @model.objects.last + end + + def set_smoothing(value) + if value == "1" + @model.smoothing = true + else + @model.smoothing = false + end + end + + def set_material(name) + @model.current_material = name + end + + def material + @model.materials[@model.current_material] + end + + def faces_count + count = 0 + @model.objects.each {|o| count+=o.faces.count} + return count + 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.current_object.textures << texture + @model.uvs << texture + end + end +end diff --git a/lib/wavefront/parser.rb b/lib/wavefront/parser.rb deleted file mode 100644 index e89897d..0000000 --- a/lib/wavefront/parser.rb +++ /dev/null @@ -1,178 +0,0 @@ -class IMICFPS - class Wavefront - class Parser - def initialize(model) - @model = model - 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(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 = material - face.smoothing= @model.smoothing - - mat = face.material.diffuse - color = Vector.new(mat.red, mat.green, mat.blue) - - 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 = 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 change_object(name) - @model.objects << Object.new(name) - @model.current_object = @model.objects.last - end - - def set_smoothing(value) - if value == "1" - @model.smoothing = true - else - @model.smoothing = false - end - end - - def set_material(name) - @model.current_material = name - end - - def material - @model.materials[@model.current_material] - end - - def faces_count - count = 0 - @model.objects.each {|o| count+=o.faces.count} - return count - 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.current_object.textures << texture - @model.uvs << texture - end - end - end -end