collada 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.travis.yml +0 -5
- data/README.md +53 -1
- data/bin/collada-convert +55 -0
- data/collada.gemspec +19 -15
- data/lib/collada/conversion/mesh.rb +144 -0
- data/lib/collada/conversion/skeleton.rb +84 -0
- data/lib/collada/conversion/tagged_format.rb +172 -0
- data/lib/collada/parser/animation.rb +57 -0
- data/lib/collada/parser/controller.rb +152 -0
- data/lib/collada/parser/geometry.rb +88 -145
- data/lib/collada/parser/library.rb +17 -12
- data/lib/collada/parser/scene.rb +1 -132
- data/lib/collada/parser/support.rb +353 -0
- data/lib/collada/parser/visual_scene.rb +159 -11
- data/lib/collada/transforms.rb +111 -0
- data/lib/collada/version.rb +1 -1
- data/test/animation.dae +165 -0
- data/test/sample.dae +211 -0
- data/test/test_parsing_animation.rb +96 -0
- data/test/test_parsing_geometry.rb +187 -0
- data/test/test_transforms.rb +56 -0
- metadata +70 -20
- data/test/test_parser.rb +0 -105
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MDZkOGNmM2E0ZDBkMjE5MzA1MDBkNWZjZTc4YmNmZWQ2ZDVkOWMxZA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
M2Y0YTUwZTQ1NjU0MThmNjMwMzhkOWYyYTVmZmJiYmE5ODY0MGM4ZQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTY4YzFjNDRmYjhlMDJmYmQ2YjQ1NzgxMDdmZjE4NTVmZjM1Y2FlMjM0ODRh
|
10
|
+
ZDRkODA3ZjUzY2ZhZWNjZTAyNjYxNjk0YWUyNjNkN2E0Y2RlN2Q5MjIzM2Ji
|
11
|
+
NzUyNmQ3YzBlZmRjM2QwOTFkOGI2OWJkZGU4ZTY2Zjk5ODlhOWI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YTBkMTc3ZjZiZWEyN2I4ZTgzNzY0ZDUyZjhkNTY2NDFkMjUyNWU1YzZkYWEy
|
14
|
+
Mzc2NTE0MDNmNDY5ZmNkMzcyZTk3YmI3NGNiYTc0MjkwNjAxM2U5MDRkODE1
|
15
|
+
NmExOTk5YTcxYTMzZjE2ZDkxNzFkMjZlZGRmM2E0MzBlZDBiNGQ=
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
This library provides support for loading and processing data from Collada Digital Asset Exchange files. These files are typically used for sharing geometry and scenes.
|
4
4
|
|
5
|
+
[![Build Status](https://secure.travis-ci.org/ioquatix/collada.png)](http://travis-ci.org/ioquatix/collada)
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Add this line to your application's Gemfile:
|
@@ -18,7 +20,52 @@ Or install it yourself as:
|
|
18
20
|
|
19
21
|
## Usage
|
20
22
|
|
21
|
-
|
23
|
+
This library is designed to be used in scripts which extract data from collada files and output in some other format. As such, you'd typically create your own conversion script which takes the collada file as an argument. Then, use this library to extract relevant geometry and write it out.
|
24
|
+
|
25
|
+
To assist with some basic tasks, the `collada-convert` executable provides a number of basic conversion functions. At present, `collada-convert` is primarily designed to produce [tagged format text][1].
|
26
|
+
|
27
|
+
[1]: https://github.com/ioquatix/tagged-format
|
28
|
+
|
29
|
+
### Example Conversion
|
30
|
+
|
31
|
+
Several example `.dae` files are included and used as part of the unit tests. The `collada-convert` executable can dump the geometries contained within easily:
|
32
|
+
|
33
|
+
$ ./bin/collada-convert tagged-format -v p3n3 ./test/sample.dae
|
34
|
+
Cube-mesh: mesh triangles
|
35
|
+
indices: array index16
|
36
|
+
0 1 2 3 4 5 6 7 8 9 10 11
|
37
|
+
12 13 14 15 16 17 18 19 20 21 22 23
|
38
|
+
end
|
39
|
+
vertices: array vertex-p3n3
|
40
|
+
1.0 1.0 -1.0 0.0 0.0 -1.0
|
41
|
+
1.0 -1.0 -1.0 0.0 0.0 -1.0
|
42
|
+
-1.0 -1.0 -1.0 0.0 0.0 -1.0
|
43
|
+
-1.0 1.0 -1.0 0.0 0.0 -1.0
|
44
|
+
1.0 1.0 1.0 0.0 0.0 1.0
|
45
|
+
-1.0 1.0 1.0 0.0 0.0 1.0
|
46
|
+
-1.0 -1.0 1.0 0.0 0.0 1.0
|
47
|
+
1.0 -1.0 1.0 0.0 0.0 1.0
|
48
|
+
1.0 1.0 -1.0 1.0 -0.0 0.0
|
49
|
+
1.0 1.0 1.0 1.0 -0.0 0.0
|
50
|
+
1.0 -1.0 1.0 1.0 -0.0 0.0
|
51
|
+
1.0 -1.0 -1.0 1.0 -0.0 0.0
|
52
|
+
1.0 -1.0 -1.0 -0.0 -1.0 0.0
|
53
|
+
1.0 -1.0 1.0 -0.0 -1.0 0.0
|
54
|
+
-1.0 -1.0 1.0 -0.0 -1.0 0.0
|
55
|
+
-1.0 -1.0 -1.0 -0.0 -1.0 0.0
|
56
|
+
-1.0 -1.0 -1.0 -1.0 0.0 0.0
|
57
|
+
-1.0 -1.0 1.0 -1.0 0.0 0.0
|
58
|
+
-1.0 1.0 1.0 -1.0 0.0 0.0
|
59
|
+
-1.0 1.0 -1.0 -1.0 0.0 0.0
|
60
|
+
1.0 1.0 1.0 0.0 1.0 0.0
|
61
|
+
1.0 1.0 -1.0 0.0 1.0 0.0
|
62
|
+
-1.0 1.0 -1.0 0.0 1.0 0.0
|
63
|
+
-1.0 1.0 1.0 0.0 1.0 0.0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
top: offset-table
|
67
|
+
Cube: $Cube-mesh
|
68
|
+
end
|
22
69
|
|
23
70
|
## Contributing
|
24
71
|
|
@@ -28,6 +75,11 @@ The main loader is incomplete can can only load geometry data at this point in t
|
|
28
75
|
4. Push to the branch (`git push origin my-new-feature`)
|
29
76
|
5. Create new Pull Request
|
30
77
|
|
78
|
+
### Desired Features
|
79
|
+
|
80
|
+
* Converter: Support .obj files.
|
81
|
+
* Support more of the collada standard.
|
82
|
+
|
31
83
|
## License
|
32
84
|
|
33
85
|
Released under the MIT license.
|
data/bin/collada-convert
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'collada/parser/library'
|
24
|
+
|
25
|
+
require 'collada/conversion/mesh'
|
26
|
+
require 'collada/conversion/skeleton'
|
27
|
+
require 'collada/conversion/tagged_format'
|
28
|
+
|
29
|
+
require 'rainbow'
|
30
|
+
require 'benchmark'
|
31
|
+
require 'trollop'
|
32
|
+
|
33
|
+
module Application
|
34
|
+
TaggedFormat = Collada::Conversion::TaggedFormat
|
35
|
+
|
36
|
+
def self.tagged_format
|
37
|
+
options = Trollop::options do
|
38
|
+
opt :vertex_format, "Vertices format, one of #{TaggedFormat::VERTEX_FORMATS.keys.join(', ')}.", :default => "p3n3"
|
39
|
+
|
40
|
+
opt :nodes, "Dump scene graph data structure", :default => false
|
41
|
+
end
|
42
|
+
|
43
|
+
doc = REXML::Document.new(ARGF)
|
44
|
+
library = Collada::Parser::Library.parse(doc)
|
45
|
+
|
46
|
+
TaggedFormat.new(options, library).dump
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
time = Benchmark.measure do
|
51
|
+
action = ARGV.shift.gsub(/-/, '_').to_sym
|
52
|
+
Application.send(action) if Application.methods.include?(action)
|
53
|
+
end
|
54
|
+
|
55
|
+
$stderr.puts time.format("Elapsed Time: %r").color(:magenta)
|
data/collada.gemspec
CHANGED
@@ -4,20 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'collada/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
gem.name = "collada"
|
8
|
+
gem.version = Collada::VERSION
|
9
|
+
gem.authors = ["Samuel Williams"]
|
10
|
+
gem.email = ["samuel.williams@oriontransfer.co.nz"]
|
11
|
+
gem.description = <<-EOF
|
12
|
+
This library provides support for loading and processing data from Collada
|
13
|
+
Digital Asset Exchange files. These files are typically used for sharing
|
14
|
+
geometry and scenes.
|
15
|
+
EOF
|
16
|
+
gem.summary = %q{A library for loading and manipulating Collada .dae files.}
|
17
|
+
gem.homepage = ""
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
gem.files = `git ls-files`.split($/)
|
20
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
21
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
22
|
+
gem.require_paths = ["lib"]
|
23
|
+
|
24
|
+
gem.add_dependency "rainbow"
|
25
|
+
gem.add_dependency "trollop"
|
26
|
+
gem.add_dependency "libxml-ruby"
|
23
27
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'collada/parser/support'
|
22
|
+
require 'collada/transforms'
|
23
|
+
|
24
|
+
module Collada
|
25
|
+
module Conversion
|
26
|
+
class VertexFormat
|
27
|
+
def initialize(format)
|
28
|
+
@format = format
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(attributes)
|
32
|
+
attributes = Collada::Parser::Attribute.to_hash(attributes) unless Hash === attributes
|
33
|
+
|
34
|
+
@format.collect do |name, components|
|
35
|
+
value = attributes[name]
|
36
|
+
|
37
|
+
raise ArgumentError.new("Invalid vertex format, undefined property #{name} for #{attributes.inspect}!") unless value
|
38
|
+
|
39
|
+
case components
|
40
|
+
when Array
|
41
|
+
components.collect{|key| value[key]}
|
42
|
+
else
|
43
|
+
components.call(value)
|
44
|
+
end
|
45
|
+
end.flatten
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.[] (format)
|
49
|
+
self.new(format)
|
50
|
+
end
|
51
|
+
|
52
|
+
def merge(extra_format)
|
53
|
+
format = @format.dup
|
54
|
+
|
55
|
+
extra_format.each do |key, value|
|
56
|
+
format[key] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
self.class.new(format)
|
60
|
+
end
|
61
|
+
|
62
|
+
def with_vertex_index
|
63
|
+
merge(vertex: [:index])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class WeightFormat
|
68
|
+
def initialize(count)
|
69
|
+
@count = count
|
70
|
+
end
|
71
|
+
|
72
|
+
def call(vertex_weights)
|
73
|
+
# Ensure that we have the correct number:
|
74
|
+
vertex_weights = @count.times.collect do |offset|
|
75
|
+
vertex_weights[offset] || [0, 0.0]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Go from [[bone1, weight1], [bone2, weight2]] => [bone1, bone2, weight1, weight2]
|
79
|
+
return vertex_weights.collect{|(bone,_)| bone}, vertex_weights.collect{|(_,weight)| weight}
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.[] (count)
|
83
|
+
self.new(count)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Vertex
|
88
|
+
# The format provided here is used purely to calculate unique vertices.
|
89
|
+
def initialize(attributes, format)
|
90
|
+
@attributes = Parser::Attribute.to_hash(attributes)
|
91
|
+
@format = format
|
92
|
+
end
|
93
|
+
|
94
|
+
attr :attributes
|
95
|
+
|
96
|
+
def to_a(format = @format)
|
97
|
+
format.call(@attributes)
|
98
|
+
end
|
99
|
+
|
100
|
+
def <=>(other)
|
101
|
+
to_a <=> other.to_a
|
102
|
+
end
|
103
|
+
|
104
|
+
def index
|
105
|
+
@attributes[:vertex][:index]
|
106
|
+
end
|
107
|
+
|
108
|
+
def hash
|
109
|
+
to_a.hash
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Mesh
|
114
|
+
def initialize
|
115
|
+
@indices = []
|
116
|
+
|
117
|
+
# vertex -> index
|
118
|
+
@vertices = {}
|
119
|
+
|
120
|
+
# index -> vertex
|
121
|
+
@indexed = []
|
122
|
+
end
|
123
|
+
|
124
|
+
attr :indices
|
125
|
+
attr :vertices
|
126
|
+
attr :indexed
|
127
|
+
|
128
|
+
def size
|
129
|
+
@indexed.size
|
130
|
+
end
|
131
|
+
|
132
|
+
def << vertex
|
133
|
+
if index = vertices[vertex]
|
134
|
+
@indices << index
|
135
|
+
else
|
136
|
+
@vertices[vertex] = self.size
|
137
|
+
@indices << self.size
|
138
|
+
|
139
|
+
@indexed << vertex
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'collada/parser/support'
|
22
|
+
require 'collada/transforms'
|
23
|
+
|
24
|
+
module Collada
|
25
|
+
module Conversion
|
26
|
+
class Skeleton
|
27
|
+
def initialize(library, scene, node, controller)
|
28
|
+
@library = library
|
29
|
+
@scene = scene
|
30
|
+
@node = node
|
31
|
+
|
32
|
+
@controller = controller
|
33
|
+
|
34
|
+
extract_skeleton(node)
|
35
|
+
end
|
36
|
+
|
37
|
+
attr :library
|
38
|
+
attr :scene
|
39
|
+
attr :node
|
40
|
+
attr :controller
|
41
|
+
|
42
|
+
attr :top
|
43
|
+
attr :bones
|
44
|
+
attr :indexed
|
45
|
+
|
46
|
+
def indexed_weights
|
47
|
+
result = []
|
48
|
+
|
49
|
+
@controller.weights.each do |vertex|
|
50
|
+
output = []
|
51
|
+
|
52
|
+
vertex.each do |weight|
|
53
|
+
weight = Parser::Attribute.merge(weight)
|
54
|
+
|
55
|
+
output << [@indexed[weight[:JOINT]], weight[:WEIGHT]]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Sort bone weights in descending order:
|
59
|
+
result << output.sort{|a,b| b[1] <=> a[1]}
|
60
|
+
end
|
61
|
+
|
62
|
+
return result
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def extract_skeleton(top)
|
68
|
+
@bones = [[0, top]]
|
69
|
+
@indexed = {top.id => 0}
|
70
|
+
|
71
|
+
@scene.traverse(top.children) do |node|
|
72
|
+
next unless node.type == :joint
|
73
|
+
|
74
|
+
bone_index = indexed[node.parents(:joint).first.id]
|
75
|
+
|
76
|
+
@indexed[node.id] = bones.size
|
77
|
+
@bones << [bone_index, node]
|
78
|
+
end
|
79
|
+
|
80
|
+
return bones
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
|
2
|
+
module Collada
|
3
|
+
module Conversion
|
4
|
+
class TaggedFormat
|
5
|
+
VERTEX_FORMATS = {
|
6
|
+
"p3n3" => VertexFormat[position: [:X, :Y, :Z], normal: [:X, :Y, :Z]],
|
7
|
+
"p3n3m2" => VertexFormat[position: [:X, :Y, :Z], normal: [:X, :Y, :Z], texcoord: [:S, :T]],
|
8
|
+
"p3n3m2b2" => VertexFormat[position: [:X, :Y, :Z], normal: [:X, :Y, :Z], texcoord: [:S, :T], bones: WeightFormat[4]],
|
9
|
+
"p3n3m2b4" => VertexFormat[position: [:X, :Y, :Z], normal: [:X, :Y, :Z], texcoord: [:S, :T], bones: WeightFormat[4]],
|
10
|
+
}
|
11
|
+
|
12
|
+
def initialize(options, library, output = nil)
|
13
|
+
@options = options
|
14
|
+
@library = library
|
15
|
+
|
16
|
+
@output_vertex_format = VERTEX_FORMATS[@options[:vertex_format]]
|
17
|
+
|
18
|
+
@output = output || $stdout
|
19
|
+
|
20
|
+
@top = {}
|
21
|
+
@nodes = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def dump_geometry(scene, node, instance, skeleton = nil)
|
25
|
+
vertex_format = @output_vertex_format.with_vertex_index
|
26
|
+
geometry = instance.lookup(@library)
|
27
|
+
|
28
|
+
mesh = Mesh.new
|
29
|
+
|
30
|
+
weights = skeleton.indexed_weights if skeleton
|
31
|
+
|
32
|
+
geometry.mesh.polygons.each do |polygon|
|
33
|
+
polygon.each do |vertex_attributes|
|
34
|
+
vertex = Collada::Conversion::Vertex.new(vertex_attributes, vertex_format)
|
35
|
+
|
36
|
+
vertex.attributes[:bones] = weights[vertex.index] if weights
|
37
|
+
|
38
|
+
mesh << vertex
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@output.puts "#{geometry.id}: mesh triangles"
|
43
|
+
|
44
|
+
@output.puts " indices: array index16"
|
45
|
+
mesh.indices.each_slice(12) do |indices|
|
46
|
+
@output.puts " #{indices.flatten.collect{|i| i.to_s.rjust(5)}.join}"
|
47
|
+
end
|
48
|
+
@output.puts " end"
|
49
|
+
|
50
|
+
@output.puts " vertices: array vertex-#{@options[:vertex_format]}"
|
51
|
+
mesh.vertices.size.times do |index|
|
52
|
+
vertex = mesh.indexed[index]
|
53
|
+
@output.puts " #{vertex.to_a(@output_vertex_format).collect{|v| v.to_s.rjust(12)}.join(' ')}"
|
54
|
+
end
|
55
|
+
@output.puts " end"
|
56
|
+
|
57
|
+
@output.puts "end"
|
58
|
+
|
59
|
+
@top[geometry.name || geometry.id] = geometry.id
|
60
|
+
|
61
|
+
return geometry
|
62
|
+
end
|
63
|
+
|
64
|
+
def dump_controller(scene, node, instance)
|
65
|
+
controller = instance.lookup(@library)
|
66
|
+
if controller.bind_pose_transform != Matrix.identity(4)
|
67
|
+
raise ArgumentError.new("Non-identity bind pose transform is not supported by this exporter!")
|
68
|
+
end
|
69
|
+
|
70
|
+
top_joint = scene[instance.skeleton.id]
|
71
|
+
skeleton = Collada::Conversion::Skeleton.new(@library, scene, top_joint, controller)
|
72
|
+
|
73
|
+
geometry = dump_geometry(scene, node, controller.source, skeleton)
|
74
|
+
|
75
|
+
indexed_weights = skeleton.indexed_weights
|
76
|
+
|
77
|
+
@output.puts "#{controller.id}: skeleton"
|
78
|
+
|
79
|
+
indexed_bones = {}
|
80
|
+
@output.puts " bones: array skeleton-bone"
|
81
|
+
skeleton.bones.each.with_index do |(parent, bone), index|
|
82
|
+
indexed_bones[bone.id] = index
|
83
|
+
@output.puts " #{bone.id} #{parent.to_s.rjust(5)}\t#{bone.transform_matrix.to_a.flatten.join(' ')}"
|
84
|
+
end
|
85
|
+
@output.puts " end"
|
86
|
+
|
87
|
+
# Extract out the animations that transform the bones:
|
88
|
+
channels = {}
|
89
|
+
start_time = 0.0
|
90
|
+
end_time = 0.0
|
91
|
+
@library[:animations].each do |animation|
|
92
|
+
animation.channels.each do |channel|
|
93
|
+
channels[channel.target] = channel
|
94
|
+
|
95
|
+
# Extract out the end time if possible:
|
96
|
+
attributes = Collada::Parser::Attribute.to_hash(channel.source[-1])
|
97
|
+
end_time = attributes[:input][:TIME] || 0.0
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@output.puts " sequences: offset-table"
|
102
|
+
@output.puts " default: skeleton-animation #{start_time} #{end_time}"
|
103
|
+
@output.puts " key-frames: array skeleton-animation-key-frame"
|
104
|
+
skeleton.bones.each do |(parent, bone)|
|
105
|
+
channel = channels["#{bone.id}/transform"]
|
106
|
+
|
107
|
+
next unless channel
|
108
|
+
|
109
|
+
channel.source.count.times do |index|
|
110
|
+
attributes = Collada::Parser::Attribute.merge(channel.source[index])
|
111
|
+
@output.puts " " + [indexed_bones[bone.id], attributes[:INTERPOLATION].downcase, attributes[:TIME], attributes[:TRANSFORM].to_a].flatten.join(' ')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
@output.puts " end"
|
115
|
+
@output.puts " end"
|
116
|
+
@output.puts " end"
|
117
|
+
|
118
|
+
@output.puts "end"
|
119
|
+
|
120
|
+
@top[controller.name || controller.id] = controller.id
|
121
|
+
|
122
|
+
return geometry, skeleton
|
123
|
+
end
|
124
|
+
|
125
|
+
def dump_instances(instances, indent = "")
|
126
|
+
end
|
127
|
+
|
128
|
+
def dump_nodes(nodes, indent = "")
|
129
|
+
nodes.each do |node|
|
130
|
+
next unless node.type == :node
|
131
|
+
|
132
|
+
@output.puts indent + "node #{node.id}"
|
133
|
+
@output.puts indent + " #{node.local_transform_matrix.to_a.flatten.join(' ')}"
|
134
|
+
|
135
|
+
dump_instances(node.instances, indent + "\t")
|
136
|
+
dump_nodes(node.children, indent + "\t")
|
137
|
+
|
138
|
+
@output.puts indent + "end"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def dump
|
143
|
+
@nodes = {}
|
144
|
+
|
145
|
+
@library[:visual_scenes].each do |scene|
|
146
|
+
scene.traverse do |node|
|
147
|
+
$stderr.puts node.inspect.color(:blue)
|
148
|
+
|
149
|
+
node.instances.each do |instance|
|
150
|
+
case instance
|
151
|
+
when Collada::Parser::VisualScene::GeometryInstance
|
152
|
+
geometry = dump_geometry(scene, node, instance)
|
153
|
+
when Collada::Parser::VisualScene::ControllerInstance
|
154
|
+
geometry, skeleton = dump_controller(scene, node, instance)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
if @options[:nodes]
|
160
|
+
dump_nodes(scene.nodes)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
@output.puts "top: offset-table"
|
165
|
+
@top.each do |name, label|
|
166
|
+
@output.puts " #{name}: $#{label}"
|
167
|
+
end
|
168
|
+
@output.puts "end"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|