Skip to content

Instantly share code, notes, and snippets.

@mieki256
Created March 10, 2017 11:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mieki256/ed7facddbc26c9c90586b2914df8c714 to your computer and use it in GitHub Desktop.
Save mieki256/ed7facddbc26c9c90586b2914df8c714 to your computer and use it in GitHub Desktop.
Rubyを使ってWavefront(.obj)形式の3Dモデルデータを読み込んでみる
#!ruby -Ku
# -*- mode: ruby; coding: utf-8 -*-
# Last updated: <2017/03/10 14:53:55 +0900>
#
# wavefront(.obj) read and parse
#
# use : Ruby 2.2.6 p396 mingw32
# License : CC0 / Public Domain
require "pp"
# wavefront(.obj) read and parse class
class WaveFrontObj
# @return [String] .obj file path
attr_reader :objpath
# @return [String] Directory where .obj is stored
attr_reader :objdir
# @return [String] .mtl filename (material file)
attr_reader :material_filename
# @return [Array<Array>] vertex (x, y, z, w)
attr_reader :vertexs
# @return [Array<Array>] uv (u, v, w)
attr_reader :uvs
# @return [Array<Array>] normal (x, y, z)
attr_reader :normals
# @return [Array<Array>] vp ( u or u, v or u, v, w)
attr_reader :vps
# @return [Hash<Array>] face
attr_reader :faces
# @return [Hash] material
attr_reader :mtls
# @return [Array] textures
attr_reader :texs
def initialize(objpath)
@objpath = File.expand_path(objpath)
@objdir = File.dirname(@objpath)
@mtlname = ""
@vertexs = []
@uvs = []
@normals = []
@vps = []
@faces = {}
@mtls = {}
@texs = []
read_geometry(@objpath)
@mtlpath = File.join(@objdir, @mtlname)
read_material(@mtlpath)
@texs = get_texture_list
end
def read_geometry(objpath)
dbg = false
mat = "none"
smooth = false
objgroup = ""
File.open(objpath) do |file|
file.each_line do |l|
next if l =~ /^#/
next if l =~ /^$/
s = l.split(" ")
case s[0]
when "mtllib"
@mtlname = s[1]
puts ".mtl = #{@mtlname}" if dbg
when "o"
objgroup = s[1]
puts "objgroup #{objgroup}" if dbg
when "v"
# vertex
if s.size == 4
x, y, z, w = s[1].to_f, s[2].to_f, s[3].to_f, 1.0
@vertexs.push([x, y, z, w])
elsif s.size == 5
x, y, z, w = s[1].to_f, s[2].to_f, s[3].to_f, s[4].to_f
@vertexs.push([x, y, z, w])
end
when "vt"
# texture u v
if s.size == 3
u, v, w = s[1].to_f, s[2].to_f, 0.0
@uvs.push([u, v, w])
elsif s.size == 4
u, v, w = s[1].to_f, s[2].to_f, s[3].to_f
@uvs.push([u, v, w])
end
when "vn"
# normal
x, y, z = s[1].to_f, s[2].to_f, s[3].to_f
@normals.push([x, y, z])
when "vp"
if s.size == 2
u = s[1].to_f
@vps.push([u])
elsif s.size == 3
u, v = s[1].to_f, s[2].to_f
@vps.push([u, v])
elsif s.size == 4
u, v, w = s[1].to_f, s[2].to_f, s[3].to_f
@vps.push([u, v, w])
end
when "usemtl"
# use material
mat = s[1]
puts "material #{mat}" if dbg
unless @faces.key?(mat)
@faces[mat] = []
end
when "s"
# Smooth
if s[1] == "off"
smooth = false
else
smooth = true
end
puts "smooth #{smooth}" if dbg
when "f"
# face
finfo = []
s.each do |n|
if n =~ %r|^(\d+)/(\d+)/(\d+)$|
vi, vti, vni = ($1.to_i - 1), ($2.to_i - 1), ($3.to_i - 1)
finfo.push([vi, vti, vni])
elsif n =~ %r|^(\d+)//(\d+)$|
vi, vti, vni = ($1.to_i - 1), nil, ($2.to_i - 1)
finfo.push([vi, vti, vni])
elsif n =~ %r|^(\d+)/(\d+)$|
vi, vti, vni = ($1.to_i - 1), ($2.to_i - 1), nil
finfo.push([vi, vti, vni])
elsif n =~ %r|^(\d+)$|
vi, vti, vni = ($1.to_i - 1), nil, nil
finfo.push([vi, vti, vni])
end
end
unless finfo.empty?
dt = { :mat => mat, :smooth => smooth, :vertexs => finfo }
@faces[mat].push(dt)
end
end
end
end
end
def read_material(mtlpath)
mtlname = ""
File.open(mtlpath) do |f|
f.each_line do |l|
next if l=~ /^#/
next if l=~ /^$/
s = l.split(" ")
case s[0]
when "newmtl"
mtlname = s[1]
@mtls[mtlname] = {}
when "Ka"
# Ambient
r, g, b = s[1].to_f, s[2].to_f, s[3].to_f
@mtls[mtlname][:ambient] = [r, g, b, 1.0]
when "Kd"
# Diffuse
r, g, b = s[1].to_f, s[2].to_f, s[3].to_f
@mtls[mtlname][:diffuse] = [r, g, b, 1.0]
when "Ks"
# Specular
r, g, b = s[1].to_f, s[2].to_f, s[3].to_f
@mtls[mtlname][:specular] = [r, g, b, 1.0]
when "Ns"
# Shininess
@mtls[mtlname][:shininess] = s[1].to_f
when "Ke"
# Emission ?
r, g, b = s[1].to_f, s[2].to_f, s[3].to_f
@mtls[mtlname][:emission] = [r, g, b, 1.0]
when "Ni"
# Optical density
@mtls[mtlname][:optical_density] = s[1].to_f
when "d"
# Dissolve
@mtls[mtlname][:dissolve] = s[1].to_f
when "illum"
# Lighting model
@mtls[mtlname][:illum] = s[1].to_i
when "map_Ka"
# Ambient texture
@mtls[mtlname][:ambient_tex] = s[1]
when "map_Kd"
# Diffuse texture
@mtls[mtlname][:diffuse_tex] = s[1]
when "map_Ks"
# Specular texture
@mtls[mtlname][:specular_tex] = s[1]
when "map_Ns"
# Specular highlight texture
@mtls[mtlname][:specular_high_tex] = s[1]
when "map_d"
# Dissolve texture
@mtls[mtlname][:dissolve_tex] = s[1]
when "map_bump"
# Bump mapping texture
@mtls[mtlname][:map_bump_tex] = s[1]
when "bump"
# Bump mapping texture
@mtls[mtlname][:bump_tex] = s[1]
when "disp"
# Displacement texture
@mtls[mtlname][:displacement_tex] = s[1]
when "decal"
# Stencil decal texture
@mtls[mtlname][:decal_tex] = s[1]
end
end
end
end
# get texture list
# @return [Array<String>] texture name
def get_texture_list
lst = {}
@mtls.each_value do |mat|
[
:ambient_tex,
:diffuse_tex,
:specular_tex,
:specular_high_tex,
:dissolve_tex,
:map_bump_tex,
:bump_tex,
:displacement_tex,
:decal_tex
].each do |k|
lst[mat[k]] = 1 if mat.key?(k)
end
end
return lst.keys
end
def dump_info_size
puts "vertex = #{@vertexs.size}"
puts "uv = #{@uvs.size}"
puts "normal = #{@normals.size}"
cnt = 0
@faces.each { |k, v| cnt += v.size }
puts "face = #{cnt}"
puts "material = #{@faces.size}"
end
def dump_faces
@faces.each do |key, value|
puts "material : #{key}"
value.each_with_index do |f, i|
puts "face #{i} : "
pp f
end
end
end
def dump_mtl
@mtls.each do |key, value|
puts "#{key} :"
pp value
end
end
def dump_texs
@texs.each { |tn| puts tn }
end
end
if $0 == __FILE__
# o = WaveFrontObj.new("wavefront/airplane.obj")
# o = WaveFrontObj.new("wavefront/airplane_metaseq.obj")
o = WaveFrontObj.new("wavefront/suzanne.obj")
# o = WaveFrontObj.new("wavefront/robo_01.obj")
if false
o.dump_info_size
o.dump_faces
o.dump_mtl
o.dump_texs
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment