Created
March 10, 2017 11:10
-
-
Save mieki256/ed7facddbc26c9c90586b2914df8c714 to your computer and use it in GitHub Desktop.
Rubyを使ってWavefront(.obj)形式の3Dモデルデータを読み込んでみる
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!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