wavefront 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +95 -0
- data/Rakefile +1 -0
- data/lib/group.rb +45 -0
- data/lib/smoothing_group.rb +26 -0
- data/lib/triangle.rb +10 -0
- data/lib/vec3.rb +15 -0
- data/lib/vertex.rb +10 -0
- data/lib/wavefront.rb +9 -0
- data/lib/wavefront/version.rb +3 -0
- data/lib/wavefront_file.rb +45 -0
- data/lib/wavefront_object.rb +145 -0
- data/wavefront.gemspec +19 -0
- metadata +60 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Misha Conway
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Wavefront
|
2
|
+
|
3
|
+
This is a simple gem for importing and exporting wavefront object files. As it is in it's initial version there are
|
4
|
+
some unsupported features that will be coming in future updates. Still, I hope this initial version suffices for
|
5
|
+
your most basic wavefront obj needs
|
6
|
+
|
7
|
+
Here are some of the things it supports importing and exporting:
|
8
|
+
-objects
|
9
|
+
-groups
|
10
|
+
-smoothing groups
|
11
|
+
-vertices, normals, and texture coordinates
|
12
|
+
|
13
|
+
Some additional operations supported are:
|
14
|
+
-computing a simple vertex buffer from the wavefront model
|
15
|
+
|
16
|
+
Some features that are coming in the future
|
17
|
+
-ability to compute a vertex/index buffer pair since these will be more optimal than a single giant vertex buffer
|
18
|
+
-ability to scale, transform, rotate, and unitize the obj
|
19
|
+
-materials (this is a feature I am currently working on, but is not available in the current version)
|
20
|
+
-ability to support more than one object. Even though wavefront spec says a file should have only one object,
|
21
|
+
there are some 3d modeling tools that export anomalous .obj files and I plan to support them even though
|
22
|
+
they are out of spec.
|
23
|
+
-parameter space vertices (these are an exotic rarely used feature in the obj format but I plan to support them)
|
24
|
+
-higher-order surfaces using several different kinds of interpolation, such as Taylor and B-splines
|
25
|
+
(again this is an exotic rarely used feauture in the obj format)
|
26
|
+
#############################################################################################################
|
27
|
+
#############################################################################################################
|
28
|
+
Note: -Let me know if there are any features I should include that are missing from this list.
|
29
|
+
-Also please let me know about any bugs or crashes you encounter. My email is MishaAConway@gmail.com.
|
30
|
+
If you attach a wavefront model that fails to parse with this gem, I will see what I can do to
|
31
|
+
update the code.
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
## Installation
|
39
|
+
|
40
|
+
Add this line to your application's Gemfile:
|
41
|
+
|
42
|
+
gem 'wavefront'
|
43
|
+
|
44
|
+
And then execute:
|
45
|
+
|
46
|
+
$ bundle
|
47
|
+
|
48
|
+
Or install it yourself as:
|
49
|
+
|
50
|
+
$ gem install wavefront
|
51
|
+
|
52
|
+
## Usage
|
53
|
+
|
54
|
+
So here is a little primer. A wavefront object basically contains a single object. This single object contains
|
55
|
+
several groups. Each of these groups can contain sub smoothing groups. Both groups and smoothing groups have a
|
56
|
+
list of triangles. Each triangle contains three vertices while each vertex contains a position, normal, and
|
57
|
+
texture coordinate. I've tried to make it so that the parsed WavefrontFile instance represents this hierarchy
|
58
|
+
as much as possible. Now with primer aside, let's go over some sample values we can extract from the
|
59
|
+
WavefrontFile instance.
|
60
|
+
|
61
|
+
w = Wavefront::File.new "my_wavefront_model" (import file, including .obj extension is optional)
|
62
|
+
w.export "my_exported_wavefront_model" (export file, including .obj extension is optional)
|
63
|
+
|
64
|
+
vertex_buffer = w.compute_vertex_buffer (compute an array of vertices from file)
|
65
|
+
|
66
|
+
w.object (inspect object itself)
|
67
|
+
group = w.object.groups.first (grab the first group)
|
68
|
+
group.name (see what it's name is)
|
69
|
+
smoothing_group = group.smoothing_groups.first (grab the first smoothing group)
|
70
|
+
smoothing_group.name (see what it's name is)
|
71
|
+
triangles = smoothing_group.triangles (grab triangles from this smoothing group)
|
72
|
+
triangle = triangles.first (grab the first triangle)
|
73
|
+
vertices = triangle.vertices (get the 3 vertices in this triangle)
|
74
|
+
vertex = vertices.first (get the first vertex)
|
75
|
+
vertex.position (look at position)
|
76
|
+
vertex.normal (look at normal)
|
77
|
+
vertex.tex (look at texture coordinate)
|
78
|
+
|
79
|
+
|
80
|
+
This is just the surface of what you can do to explore the parsed wavefront file. When I get more time, I'll try
|
81
|
+
to make these usage examples better.
|
82
|
+
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it
|
87
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
89
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
5. Create new Pull Request
|
91
|
+
|
92
|
+
or.....
|
93
|
+
|
94
|
+
Let me know about any issues and send me any wavefront obj models that crash the app so that I can update the gem
|
95
|
+
to support them.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/group.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Wavefront
|
2
|
+
class Group
|
3
|
+
attr_reader :name, :triangles, :material, :smoothing_groups
|
4
|
+
|
5
|
+
def initialize n
|
6
|
+
@name = n
|
7
|
+
@triangles = []
|
8
|
+
@smoothing_groups = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_smoothing_group smoothing_group_name
|
12
|
+
if smoothing_group_name.nil? || 'off' == smoothing_group_name.to_s || '0' == smoothing_group_name.to_s
|
13
|
+
@current_smoothing_group = nil
|
14
|
+
else
|
15
|
+
@current_smoothing_group = smoothing_groups.find{ |sg| sg.name.to_s == smoothing_group_name.to_s }
|
16
|
+
if @current_smoothing_group.nil?
|
17
|
+
@current_smoothing_group = SmoothingGroup.new smoothing_group_name
|
18
|
+
smoothing_groups << @current_smoothing_group
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def merge_smoothing_groups!
|
24
|
+
set_smoothing_group nil
|
25
|
+
smoothing_groups.each { |smoothing_group| triangles += smoothing_group.triangles }
|
26
|
+
smoothing_groups.clear
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_triangle triangle
|
30
|
+
if @current_smoothing_group
|
31
|
+
@current_smoothing_group.add_triangle triangle
|
32
|
+
else
|
33
|
+
@triangles << triangle
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def num_faces
|
38
|
+
triangles.size + smoothing_groups.map(&:num_faces).inject(:+).to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
def num_vertices
|
42
|
+
triangles.size * 3 + smoothing_groups.map(&:num_vertices).inject(:+).to_i
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Wavefront
|
2
|
+
class SmoothingGroup
|
3
|
+
attr_reader :name, :triangles
|
4
|
+
|
5
|
+
def initialize _name
|
6
|
+
@name = _name
|
7
|
+
@triangles = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_triangle triangle
|
11
|
+
@triangles << triangle
|
12
|
+
end
|
13
|
+
|
14
|
+
def num_faces
|
15
|
+
triangles.size
|
16
|
+
end
|
17
|
+
|
18
|
+
def num_vertices
|
19
|
+
triangles.size * 3
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"Smoothing Group\n\tNum Faces: #{num_faces}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/triangle.rb
ADDED
data/lib/vec3.rb
ADDED
data/lib/vertex.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module Wavefront
|
2
|
+
class Vertex
|
3
|
+
attr_reader :position, :tex, :normal, :position_index, :texture_index, :normal_index
|
4
|
+
|
5
|
+
def initialize p, uv, n, p_index, t_index, n_index
|
6
|
+
@position, @uv, @normal = p, uv, n
|
7
|
+
@position_index, @texture_index, @normal_index = p_index, t_index, n_index
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/lib/wavefront.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Wavefront
|
2
|
+
class File
|
3
|
+
attr_reader :file_path, :objects
|
4
|
+
|
5
|
+
def initialize file_path
|
6
|
+
@file_path = file_path
|
7
|
+
unless /\.obj$/.match @file_path
|
8
|
+
@file_path += ".obj"
|
9
|
+
end
|
10
|
+
|
11
|
+
@objects = []
|
12
|
+
|
13
|
+
file = ::File.new @file_path, 'r'
|
14
|
+
while line = file.gets
|
15
|
+
components = line.split
|
16
|
+
type = components.shift
|
17
|
+
if 'o' == type
|
18
|
+
name = components.first
|
19
|
+
objects << Wavefront::Object.new(name, file)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#no object was found so let's create one, rewind back to file, and try parsing again
|
24
|
+
if objects.size.zero?
|
25
|
+
file.rewind
|
26
|
+
objects << Wavefront::Object.new("default", file)
|
27
|
+
end
|
28
|
+
|
29
|
+
file.close
|
30
|
+
end
|
31
|
+
|
32
|
+
def export out_path
|
33
|
+
raise "no objects to export!" if objects.size.zero?
|
34
|
+
objects.first.export out_path
|
35
|
+
end
|
36
|
+
|
37
|
+
def compute_vertex_buffer
|
38
|
+
object.compute_vertex_buffer
|
39
|
+
end
|
40
|
+
|
41
|
+
def object
|
42
|
+
objects.first
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Wavefront
|
2
|
+
class Object
|
3
|
+
attr_reader :file, :vertices, :texture_coordinates, :normals, :faces, :groups, :name
|
4
|
+
|
5
|
+
def initialize name, file
|
6
|
+
@name = name
|
7
|
+
@file = file
|
8
|
+
@vertices, @texture_coordinates, @normals, @faces, @groups = [], [], [], [], []
|
9
|
+
parse!
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
s = "Object\n\tName: #{name}\n\tNum Vertices: #{vertices.size}\n\tNum Faces: #{num_faces}"
|
14
|
+
unless groups.size.zero?
|
15
|
+
s += "\n"
|
16
|
+
groups.each do |group|
|
17
|
+
s += "\tGroup #{group.name}\n\t\tNum Vertices: #{group.num_vertices}\n\t\tNum Faces: #{group.num_faces}"
|
18
|
+
group.smoothing_groups.each do |smoothing_group|
|
19
|
+
s += "\n\t\t\tSmoothing Group #{smoothing_group.name}\n\t\t\t\tNum Vertices: #{smoothing_group.num_vertices}\n\t\t\t\tNum Faces: #{smoothing_group.num_faces}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
s
|
24
|
+
end
|
25
|
+
|
26
|
+
def num_faces
|
27
|
+
@num_faces = 0
|
28
|
+
groups.each do |g|
|
29
|
+
@num_faces += g.num_faces
|
30
|
+
end
|
31
|
+
@num_faces
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
def export file_name
|
38
|
+
unless /\.obj$/.match file_name
|
39
|
+
file_name += ".obj"
|
40
|
+
end
|
41
|
+
|
42
|
+
::File.delete file_name if ::File.exist? file_name
|
43
|
+
open file_name, 'a' do |f|
|
44
|
+
f.puts "# Exported from Wavefront Ruby Gem Version #{Wavefront::VERSION}"
|
45
|
+
f.puts "o #{name}"
|
46
|
+
f.puts "##{vertices.size} vertices, #{num_faces} faces"
|
47
|
+
vertices.each { |v| f.puts "v #{v}" }
|
48
|
+
texture_coordinates.each { |t| f.puts "vt #{t}" }
|
49
|
+
normals.each { |n| f.puts "vn #{n}" }
|
50
|
+
groups.each do |group|
|
51
|
+
f.puts "g ##{group.name}"
|
52
|
+
group.triangles.each do |t|
|
53
|
+
f.puts 'f ' + t.vertices.map { |v| [v.position_index, v.texture_index, v.normal_index].join '/' }.join(' ')
|
54
|
+
end
|
55
|
+
group.smoothing_groups.each do |smoothing_group|
|
56
|
+
f.puts "s #{smoothing_group.name}"
|
57
|
+
smoothing_group.triangles.each do |t|
|
58
|
+
f.puts 'f ' + t.vertices.map { |v| [v.position_index, v.texture_index, v.normal_index].join '/' }.join(' ')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def compute_vertex_buffer
|
66
|
+
vertex_buffer = []
|
67
|
+
groups.each do |group|
|
68
|
+
group.triangles.each { |t| vertex_buffer << t.vertices }
|
69
|
+
group.smoothing_groups.each do |smoothing_group|
|
70
|
+
smoothing_group.triangles.each { |t| vertex_buffer << t.vertices }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
vertex_buffer.flatten
|
74
|
+
end
|
75
|
+
|
76
|
+
def compute_vertex_buffer_and_index_buffer
|
77
|
+
raise "not yet implemented in version #{Wavefront::VERSION}! will be coming in future versions!"
|
78
|
+
vertex_buffer, index_buffer = [], []
|
79
|
+
|
80
|
+
|
81
|
+
{:vertex_buffer => vertex_buffer, :index_buffer => index_buffer}
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def parse!
|
86
|
+
while line = file.gets
|
87
|
+
components = line.split
|
88
|
+
type = components.shift
|
89
|
+
case type
|
90
|
+
when 'v'
|
91
|
+
vertices << Vec3.new(*components.map(&:to_f))
|
92
|
+
when 'vt'
|
93
|
+
texture_coordinates << Vec3.new(*components.map(&:to_f))
|
94
|
+
when 'vn'
|
95
|
+
normals << Vec3.new(*components.map(&:to_f))
|
96
|
+
when 'vp'
|
97
|
+
#TODO: handle these later
|
98
|
+
when 'f'
|
99
|
+
triangles = []
|
100
|
+
if components.size > 4
|
101
|
+
raise "Sorry this version of the gem does not support polygons with more than 4 points. Updates to this gem will fix this issue."
|
102
|
+
end
|
103
|
+
if components.size == 4
|
104
|
+
triangles << triangle_from_face_components(components[0, 3])
|
105
|
+
triangles << triangle_from_face_components([components[0], components[2], components[3]])
|
106
|
+
elsif components.size == 3
|
107
|
+
triangles << triangle_from_face_components(components)
|
108
|
+
else
|
109
|
+
raise "current version of gem cannot parse triangles with #{components.size} verts!"
|
110
|
+
end
|
111
|
+
triangles.each { |triangle| @current_group.add_triangle triangle }
|
112
|
+
when 'g'
|
113
|
+
name = components.first
|
114
|
+
@current_group = Group.new name
|
115
|
+
groups << @current_group
|
116
|
+
|
117
|
+
when 's'
|
118
|
+
@current_group.set_smoothing_group components.first
|
119
|
+
when 'o'
|
120
|
+
raise "Wavefront Version #{Wavefront::VERSION} does not support obj files with more than one object. If you encounter such an obj that fails to load, please attach and email to mishaAconway@gmail.com so that I can update the gem to support the file."
|
121
|
+
#file.seek -line.size, IO::SEEK_CUR
|
122
|
+
#return
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def triangle_from_face_components face_components
|
128
|
+
triangle_vertices = []
|
129
|
+
face_components.each do |vertex_str|
|
130
|
+
vertex_str_components = vertex_str.split('/').map { |index| index.size > 0 ? index.to_i : nil }
|
131
|
+
position_index = vertex_str_components[0]
|
132
|
+
tex_index = vertex_str_components[1]
|
133
|
+
|
134
|
+
normal_index = vertex_str_components[2]
|
135
|
+
|
136
|
+
position = vertices[position_index]
|
137
|
+
tex_coordinate = tex_index ? texture_coordinates[tex_index] : nil
|
138
|
+
normal = normals[normal_index]
|
139
|
+
|
140
|
+
triangle_vertices << Vertex.new(position, tex_coordinate, normal, position_index, tex_index, normal_index)
|
141
|
+
end
|
142
|
+
Triangle.new triangle_vertices
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/wavefront.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'wavefront/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "wavefront"
|
8
|
+
gem.version = Wavefront::VERSION
|
9
|
+
gem.authors = ["Misha Conway"]
|
10
|
+
gem.email = ["MishaAConway@gmail.com"]
|
11
|
+
gem.description = %q{Wavefront parser and exporter}
|
12
|
+
gem.summary = %q{Wavefront parser and exporter}
|
13
|
+
gem.homepage = "https://github.com/MishaConway/wavefront-ruby"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wavefront
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Misha Conway
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-07 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Wavefront parser and exporter
|
15
|
+
email:
|
16
|
+
- MishaAConway@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- Gemfile
|
23
|
+
- LICENSE.txt
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
26
|
+
- lib/group.rb
|
27
|
+
- lib/smoothing_group.rb
|
28
|
+
- lib/triangle.rb
|
29
|
+
- lib/vec3.rb
|
30
|
+
- lib/vertex.rb
|
31
|
+
- lib/wavefront.rb
|
32
|
+
- lib/wavefront/version.rb
|
33
|
+
- lib/wavefront_file.rb
|
34
|
+
- lib/wavefront_object.rb
|
35
|
+
- wavefront.gemspec
|
36
|
+
homepage: https://github.com/MishaConway/wavefront-ruby
|
37
|
+
licenses: []
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
none: false
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
none: false
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.8.24
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: Wavefront parser and exporter
|
60
|
+
test_files: []
|