md2 1.0.0
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/.document +5 -0
- data/.gitignore +26 -0
- data/LICENSE +20 -0
- data/README.rdoc +174 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/bin/md2_to_json +33 -0
- data/ext/md2/extconf.rb +3 -0
- data/ext/md2/md2.c +95 -0
- data/lib/md2.rb +97 -0
- data/lib/md2/command.rb +63 -0
- data/lib/md2/errors.rb +13 -0
- data/lib/md2/frame.rb +61 -0
- data/lib/md2/header.rb +37 -0
- data/lib/md2/normals.rb +81 -0
- data/lib/md2/triangle.rb +9 -0
- data/lib/md2/vertex.rb +33 -0
- data/md2.gemspec +117 -0
- data/spec/lib/md2/frame_spec.rb +13 -0
- data/spec/lib/md2/header_spec.rb +21 -0
- data/spec/lib/md2_spec.rb +66 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/support/crafty/Crafty.BMP +0 -0
- data/spec/support/crafty/Crafty.pcx +0 -0
- data/spec/support/crafty/Crafty.txt +113 -0
- data/spec/support/crafty/Weapon.md2 +0 -0
- data/spec/support/crafty/Weapon.pcx +0 -0
- data/spec/support/crafty/crafty.md2 +0 -0
- data/spec/support/laalaa/laalaa.md2 +0 -0
- data/spec/support/laalaa/laalaa24.BMP +0 -0
- data/spec/support/laalaa/laalaa8.BMP +0 -0
- data/spec/support/ogro/Ogro.txt +132 -0
- data/spec/support/ogro/Ogrobase.pcx +0 -0
- data/spec/support/ogro/Weapon.md2 +0 -0
- data/spec/support/ogro/Weapon.pcx +0 -0
- data/spec/support/ogro/ogro.md2 +0 -0
- data/spec/support/ogro/ogro.png +0 -0
- data/spec/support/pilot/CC_attribution_licence.txt +68 -0
- data/spec/support/pilot/GNU_licence.txt +340 -0
- data/spec/support/pilot/Readme.txt +30 -0
- data/spec/support/pilot/body.md2 +0 -0
- data/spec/support/pilot/body.obj +9331 -0
- data/spec/support/pilot/head.md2 +0 -0
- data/spec/support/pilot/head.obj +6145 -0
- data/spec/support/pilot/pilot.md2 +0 -0
- data/spec/support/pilot/pilot_body.3ds +0 -0
- data/spec/support/pilot/pilot_body.jpg +0 -0
- data/spec/support/pilot/pilot_body.obj +2264 -0
- data/spec/support/pilot/pilot_head.3ds +0 -0
- data/spec/support/pilot/pilot_head.jpg +0 -0
- data/spec/support/pilot/pilot_head.obj +1970 -0
- data/spec/support/pknight/pknight.BMP +0 -0
- data/spec/support/pknight/pknight.PCX +0 -0
- data/spec/support/pknight/pknight.md2 +0 -0
- data/spec/support/pknight/pknight_weapon.PCX +0 -0
- data/spec/support/pknight/pknight_weapon.bmp +0 -0
- data/spec/support/pknight/pknight_weapon.md2 +0 -0
- data/spec/support/sodf8/Abarlith.pcx +0 -0
- data/spec/support/sodf8/SFOD8.txt +108 -0
- data/spec/support/sodf8/Weapon.PCX +0 -0
- data/spec/support/sodf8/Weapon.md2 +0 -0
- data/spec/support/sodf8/sodf8.md2 +0 -0
- metadata +167 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
rdoc
|
19
|
+
pkg
|
20
|
+
|
21
|
+
## PROJECT::SPECIFIC
|
22
|
+
.idea
|
23
|
+
*.bundle
|
24
|
+
*.o
|
25
|
+
*.out
|
26
|
+
Makefile
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Colin MacKenzie IV
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
= md2
|
2
|
+
|
3
|
+
A Ruby library for loading MD2 (Quake II) 3D model files. It doesn't actually render them; that part is up to you.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
gem install md2
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
require 'md2'
|
12
|
+
|
13
|
+
md2 = MD2.new("/path/to/md2")
|
14
|
+
md2.frames.each_with_index do |frame, index|
|
15
|
+
puts frame.name
|
16
|
+
puts frame.vertices.length
|
17
|
+
puts frame.normal_indices
|
18
|
+
end
|
19
|
+
puts md2.triangles.length
|
20
|
+
puts md2.texcoords.length
|
21
|
+
# and so on.
|
22
|
+
|
23
|
+
See the MD2 class documentation for more, or scroll down for an example of how to render the model.
|
24
|
+
|
25
|
+
== Converting to JSON
|
26
|
+
|
27
|
+
The MD2 library works great with the 'json' gem. Just load an MD2 and then call #to_json:
|
28
|
+
|
29
|
+
require 'md2'
|
30
|
+
require 'json'
|
31
|
+
|
32
|
+
md2 = MD2.new("/path/to/md2")
|
33
|
+
json = md2.to_json
|
34
|
+
|
35
|
+
I think this will be pretty cool as WebGL and friends begin to take off. I've also added a generator for this. You can
|
36
|
+
convert an MD2 file into its JSON counterpart at the command line with:
|
37
|
+
|
38
|
+
$ md2_to_json /path/to/md2 >/path/to/json
|
39
|
+
|
40
|
+
The generator just prints the JSON to stdout (which made it easier for me to test), so you have to redirect the output
|
41
|
+
into a file if you want to capture it.
|
42
|
+
|
43
|
+
The ">" in the command above is used to redirect stdout. Check Wikipedia for
|
44
|
+
Redirection (computing) for more details if you've never seen this before:
|
45
|
+
http://en.wikipedia.org/wiki/Redirection_(computing)
|
46
|
+
|
47
|
+
=== About the JSON output
|
48
|
+
|
49
|
+
First, here's the general structure of a JSON string created by this library:
|
50
|
+
|
51
|
+
{
|
52
|
+
"header": {
|
53
|
+
"skin_width":256, "skin_height":256, "skin_count":0, "frame_count":199,
|
54
|
+
(other header data that was previously used for loading the model)
|
55
|
+
},
|
56
|
+
"frames": {[
|
57
|
+
{"name" : "Frame1",
|
58
|
+
"translation" :[ (XYZ float array) ],
|
59
|
+
"scale" :[ (XYZ float array) ],
|
60
|
+
"vertices" :[ (array of vertex data: these are INTEGERS! - see below) ],
|
61
|
+
"normal_indices":[array of normal indices]
|
62
|
+
},
|
63
|
+
...
|
64
|
+
]},
|
65
|
+
"triangles":[
|
66
|
+
{"vertex_indices" :[ (XYZ int array) ],
|
67
|
+
"texcoord_indices":[ (ST int array) ]},
|
68
|
+
...
|
69
|
+
],
|
70
|
+
"texcoords":[
|
71
|
+
[ (ST float array) ],
|
72
|
+
[ (ST float array) ],
|
73
|
+
[ (ST float array) ],
|
74
|
+
...
|
75
|
+
],
|
76
|
+
"skins": [ (string array of filenames) ],
|
77
|
+
"gl_commands": [
|
78
|
+
{ "texture_s": float,
|
79
|
+
"texture_t": float,
|
80
|
+
"vertex_index": int },
|
81
|
+
...
|
82
|
+
]
|
83
|
+
}
|
84
|
+
|
85
|
+
Now that you can see the general flow of the JSON, here's a bit more info:
|
86
|
+
|
87
|
+
The header info is generally not useful to you and consists mostly of information that was read from the MD2 file when
|
88
|
+
it was initially loaded.
|
89
|
+
|
90
|
+
Frames are one potential gotcha: the vertex information stored in the frame is an integer between 0 and 255. This
|
91
|
+
integer is useless on its own; you need to expand the vertex data from it by first multiplying by scale, and then
|
92
|
+
adding translation. The vertex count is divisible by 3 because -- you guessed it -- every first vertex is X, every
|
93
|
+
second is Y and every third is Z. The reason for all of this? Because when I tried to precalculate it all for you,
|
94
|
+
the JSON result was 4 times bigger! A 250KB MD2 file resulted in over 4MB of JSON. By letting you do this on your
|
95
|
+
end, I'm saving you a ton of bandwidth. If you go another step and gzip compress the current result, it actually ends
|
96
|
+
up smaller than the original MD2 file!
|
97
|
+
|
98
|
+
* Note that the vertex data is already unpacked for you if you're using just the Ruby library; the vertex information
|
99
|
+
is only packaged this way in JSON, to reduce bandwidth requirements.
|
100
|
+
|
101
|
+
So here's a code snippet of how to extract the vertex information in JavaScript. Copy and paste to your heart's content!
|
102
|
+
/* assume we have a JSON object called 'model' */
|
103
|
+
for (var frame_index = 0; frame_index < model.frames.length; frame_index++)
|
104
|
+
{
|
105
|
+
var vertices = [];
|
106
|
+
var frame = model.frames[frame_index];
|
107
|
+
for (var vert_index = 0; vert_index < frame.vertices.length; vert_index += 3)
|
108
|
+
for (var k = 0; k < 3; k++)
|
109
|
+
vertices[vert_index+k] = (frame.vertices[vert_index+k] * frame.scale[k]) + frame.translation[k];
|
110
|
+
/* we have the unpacked vertex data; may as well just replace the now-useless packed data */
|
111
|
+
frame.vertices = vertices;
|
112
|
+
}
|
113
|
+
|
114
|
+
Nothing else in the JSON output is packed in this way. The texture coordinate indices point to the "texcoords"
|
115
|
+
array, and the normal indices point to the precomputed MD2 normals. If you need to implement those in JavaScript,
|
116
|
+
take a look at the MD2::Normals constant for the data.
|
117
|
+
|
118
|
+
Triangles are made up of 3 vertices (each with an X, Y, Z coordinate) and 3 texture coordinates (each with an S, T
|
119
|
+
coordinate); therefore, they consist of 3 vertex indices (which reference the frame vertices you just unpacked) and
|
120
|
+
3 texture coordinate indices.
|
121
|
+
|
122
|
+
The texcoords array consists of a number of nested arrays. Each nested array has 2 elements, the S and T value for
|
123
|
+
that texture coordinate. The nesting is designed to make it easier for you to access via coordinate indices (see
|
124
|
+
Triangles, above).
|
125
|
+
|
126
|
+
Skins are simply a list of filenames (Strings) referenced within the MD2. Do what you will with them.
|
127
|
+
|
128
|
+
The final component, gl_commands, is discussed below. The code is in Ruby, but the approach is the same.
|
129
|
+
|
130
|
+
== Note on Rendering
|
131
|
+
|
132
|
+
The preferred way to render an MD2 file is to make use of its GL Commands, which produce an optimized approach to
|
133
|
+
rendering by switching between triangle strips and triangle fans (as opposed to just rendering the entire model with
|
134
|
+
only triangles). This library parses the command information into an intuitive structure so you don't have to deal
|
135
|
+
with the nuances of the format. Instead, here's how you would go about rendering an MD2 using its GL commands:
|
136
|
+
|
137
|
+
frame = md2.frames[current_frame]
|
138
|
+
|
139
|
+
md2.gl_commands.each do |command|
|
140
|
+
case command.type
|
141
|
+
when :triangle_strip then glBegin(GL_TRIANGLE_STRIP)
|
142
|
+
when :triangle_fan then glBegin(GL_TRIANGLE_FAN)
|
143
|
+
end
|
144
|
+
|
145
|
+
command.segments.each do |segment|
|
146
|
+
index = segment.vertex_index
|
147
|
+
|
148
|
+
glTexCoord2f(segment.texture_s, segment.texture_t)
|
149
|
+
glNormal3f(frame.normals[index].x, frame.normals[index].y, frame.normals[index].z)
|
150
|
+
glVertex3f(frame.vertices[index].x, frame.vertices[index].y, frame.vertices[index].z)
|
151
|
+
end
|
152
|
+
|
153
|
+
glEnd
|
154
|
+
end
|
155
|
+
|
156
|
+
== Limitations
|
157
|
+
|
158
|
+
Modifying and saving the MD2 file is not currently supported, although this may be implemented sometime in the future.
|
159
|
+
|
160
|
+
== Note on Patches/Pull Requests
|
161
|
+
|
162
|
+
* Fork the project.
|
163
|
+
* Make your feature addition or bug fix.
|
164
|
+
* Add tests for it. This is important so I don't break it in a
|
165
|
+
future version unintentionally.
|
166
|
+
* Commit, do not mess with rakefile, version, or history.
|
167
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
168
|
+
* Send me a pull request. Bonus points for topic branches.
|
169
|
+
|
170
|
+
== Copyright
|
171
|
+
|
172
|
+
Copyright (c) 2010 Colin MacKenzie IV. See LICENSE for details.
|
173
|
+
|
174
|
+
http://thoughtsincomputation.com
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "md2"
|
8
|
+
gem.summary = %Q{A Ruby library for loading MD2 3D model files.}
|
9
|
+
gem.description = %Q{A Ruby library for loading MD2 3D model files.}
|
10
|
+
gem.email = "sinisterchipmunk@gmail.com"
|
11
|
+
gem.homepage = "http://thoughtsincomputation.com"
|
12
|
+
gem.authors = ["Colin MacKenzie IV"]
|
13
|
+
gem.add_dependency "sizes", ">= 1.0"
|
14
|
+
gem.add_dependency "activesupport", ">= 2.3.5"
|
15
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
Jeweler::GemcutterTasks.new
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'spec/rake/spectask'
|
25
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
28
|
+
end
|
29
|
+
|
30
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
31
|
+
spec.libs << 'lib' << 'spec'
|
32
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
33
|
+
spec.rcov = true
|
34
|
+
end
|
35
|
+
|
36
|
+
task :spec => :check_dependencies
|
37
|
+
|
38
|
+
task :default => :spec
|
39
|
+
rescue LoadError
|
40
|
+
puts "It seems you don't have rspec 1.x. You won't be able to run the test suite."
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "md2 #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/bin/md2_to_json
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
def banner
|
4
|
+
puts "Usage:"
|
5
|
+
puts "\truby md2_to_json.rb path/to/input.md2"
|
6
|
+
puts
|
7
|
+
end
|
8
|
+
|
9
|
+
unless ARGV.length == 1
|
10
|
+
banner
|
11
|
+
puts "Output is printed to stdout. To capture json output, redirect"
|
12
|
+
puts "to file:"
|
13
|
+
puts
|
14
|
+
puts "\truby md2_to_json.rb path/to/input.md2 >path/to/output.md2"
|
15
|
+
puts
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
|
19
|
+
if File.file?(filename = ARGV.first)
|
20
|
+
require 'rubygems'
|
21
|
+
gem 'json'
|
22
|
+
|
23
|
+
require File.join(File.dirname(__FILE__), '../lib/md2')
|
24
|
+
require 'json'
|
25
|
+
md2 = MD2.new(filename)
|
26
|
+
puts md2.to_json
|
27
|
+
else
|
28
|
+
puts
|
29
|
+
puts "!! File '#{filename}' does not exist !!"
|
30
|
+
puts
|
31
|
+
banner
|
32
|
+
exit
|
33
|
+
end
|
data/ext/md2/extconf.rb
ADDED
data/ext/md2/md2.c
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
static VALUE read_gl_commands_ext(VALUE self, VALUE gl_command_count, VALUE gl_command_offset, VALUE gl_command_data);
|
4
|
+
static VALUE construct_command(VALUE args);
|
5
|
+
static VALUE cMD2 = Qnil;
|
6
|
+
|
7
|
+
void Init_md2()
|
8
|
+
{
|
9
|
+
cMD2 = rb_define_class("MD2", rb_cObject);
|
10
|
+
rb_define_method(cMD2, "read_gl_commands_ext", read_gl_commands_ext, 3);
|
11
|
+
}
|
12
|
+
|
13
|
+
/*
|
14
|
+
Accepts a command count, command offset and filename. The file is opened and the data is read;
|
15
|
+
an argument error is raised if the file could not be opened or if the exact command count could
|
16
|
+
not be read from the file. Otherwise, the data is deserialized according to the MD2::Command
|
17
|
+
documentation and then it is returned as an array of MD2::Command instances.
|
18
|
+
|
19
|
+
See the documentation for MD2::Command for the exact algoritm; I couldn't figure out how to
|
20
|
+
implement it using pure Ruby.
|
21
|
+
*/
|
22
|
+
static VALUE read_gl_commands_ext(VALUE self, VALUE gl_command_count, VALUE gl_command_offset, VALUE file_path)
|
23
|
+
{
|
24
|
+
char *cpath = StringValueCStr(file_path);
|
25
|
+
FILE *inf = fopen(cpath, "r");
|
26
|
+
if (!inf) rb_raise(rb_eArgError, "Could not open file %s to read GL command data!", cpath);
|
27
|
+
|
28
|
+
unsigned int command_count = FIX2INT(gl_command_count);
|
29
|
+
int command_offset = FIX2INT(gl_command_offset);
|
30
|
+
int *cdata = (int *)malloc(sizeof(int) * command_count);
|
31
|
+
|
32
|
+
fseek(inf, command_offset, SEEK_SET);
|
33
|
+
if (fread(cdata, sizeof(int), command_count, inf) != command_count)
|
34
|
+
{
|
35
|
+
fclose(inf);
|
36
|
+
free(cdata);
|
37
|
+
rb_raise(rb_eArgError, "Could not read %d GL commands from file", command_count);
|
38
|
+
}
|
39
|
+
fclose(inf);
|
40
|
+
|
41
|
+
VALUE ary = rb_ary_new();
|
42
|
+
int i = 0, error;
|
43
|
+
int val;
|
44
|
+
while ((val = cdata[i++]) != 0)
|
45
|
+
{
|
46
|
+
int count;
|
47
|
+
if (val > 0) count = val;
|
48
|
+
else count = -val;
|
49
|
+
|
50
|
+
VALUE sym;
|
51
|
+
const char *symcstr;
|
52
|
+
if (val > 0) sym = ID2SYM(rb_intern("triangle_strip"));
|
53
|
+
else sym = ID2SYM(rb_intern("triangle_fan"));
|
54
|
+
|
55
|
+
// construct the packet which will contain the args to be sent to MD2::Command
|
56
|
+
VALUE packet = rb_ary_new();
|
57
|
+
// add the render type to the packet
|
58
|
+
rb_ary_push(packet, sym);
|
59
|
+
|
60
|
+
while (count--)
|
61
|
+
{
|
62
|
+
float s = *(float *)&cdata[i++];
|
63
|
+
float t = *(float *)&cdata[i++];
|
64
|
+
int index = cdata[i++];
|
65
|
+
|
66
|
+
// add s, t, index to the packet
|
67
|
+
rb_ary_push(packet, rb_float_new(s));
|
68
|
+
rb_ary_push(packet, rb_float_new(t));
|
69
|
+
rb_ary_push(packet, INT2FIX(index));
|
70
|
+
}
|
71
|
+
|
72
|
+
// construct the MD2::Command from the packet data. If error,
|
73
|
+
// intercept it and then break so that we can free cdata properly.
|
74
|
+
VALUE command = rb_protect(construct_command, packet, &error);
|
75
|
+
if (error) break;
|
76
|
+
|
77
|
+
// store the command in the resultant array
|
78
|
+
rb_ary_push(ary, command);
|
79
|
+
}
|
80
|
+
|
81
|
+
free(cdata);
|
82
|
+
|
83
|
+
// now that cdata is taken care of, re-raise the error if there was one
|
84
|
+
if (error)
|
85
|
+
rb_exc_raise(rb_gv_get("$!"));
|
86
|
+
|
87
|
+
return ary;
|
88
|
+
}
|
89
|
+
|
90
|
+
static VALUE construct_command(VALUE args)
|
91
|
+
{
|
92
|
+
VALUE cMD2Command = rb_const_get(cMD2, rb_intern("Command"));
|
93
|
+
VALUE command = rb_funcall2(cMD2Command, rb_intern("new"), 1, &args);
|
94
|
+
return command;
|
95
|
+
}
|
data/lib/md2.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
require File.join(File.dirname(__FILE__), "../ext/md2/md2")
|
3
|
+
require 'sizes'
|
4
|
+
require 'active_support/core_ext'
|
5
|
+
|
6
|
+
class MD2
|
7
|
+
include Sizes
|
8
|
+
|
9
|
+
autoload :Header, "md2/header"
|
10
|
+
autoload :Errors, "md2/errors"
|
11
|
+
autoload :Frame, "md2/frame"
|
12
|
+
autoload :Vertex, "md2/vertex"
|
13
|
+
autoload :Triangle, "md2/triangle"
|
14
|
+
autoload :Command, "md2/command"
|
15
|
+
autoload :Normals, "md2/normals"
|
16
|
+
|
17
|
+
attr_reader :header
|
18
|
+
attr_reader :base_path, :frames, :triangles, :skins, :texcoords, :gl_commands
|
19
|
+
|
20
|
+
delegate :skin_width, :skin_height, :frame_size, :skin_count, :vertex_count,
|
21
|
+
:texture_coord_count, :triangle_count, :gl_command_count, :frame_count,
|
22
|
+
:skin_name_offset, :texture_coord_offset, :triangle_offset, :frame_data_offset,
|
23
|
+
:gl_command_offset, :eof_offset, :to => :header
|
24
|
+
|
25
|
+
def initialize(path)
|
26
|
+
load(path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_json
|
30
|
+
raise "Can't convert to json unless you've already activated the 'json' gem!" if !defined?(JSON)
|
31
|
+
|
32
|
+
{
|
33
|
+
:header => @header,
|
34
|
+
:frames => @frames.collect { |f| f.reduce },
|
35
|
+
:triangles => @triangles,
|
36
|
+
:texcoords => @texcoords,
|
37
|
+
:skins => @skins,
|
38
|
+
:gl_commands => @gl_commands,
|
39
|
+
:base_path => @base_path
|
40
|
+
}.to_json
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def load(path)
|
45
|
+
@base_path = File.dirname(path)
|
46
|
+
File.open(path, "rb") do |file|
|
47
|
+
read_header(file)
|
48
|
+
read_frames(file)
|
49
|
+
read_triangles(file)
|
50
|
+
read_skins(file)
|
51
|
+
read_texcoords(file)
|
52
|
+
end
|
53
|
+
@gl_commands = read_gl_commands_ext(gl_command_count, gl_command_offset, path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def read_header(file)
|
57
|
+
@header = MD2::Header.new(file)
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_frames(file)
|
61
|
+
@frames = []
|
62
|
+
read_data(file, frame_data_offset, frame_count, frame_size) do |chunk|
|
63
|
+
@frames << MD2::Frame.new(chunk)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def read_triangles(file)
|
68
|
+
@triangles = []
|
69
|
+
read_data(file, triangle_offset, triangle_count, 6 * sizeof(:short)) do |chunk|
|
70
|
+
@triangles << MD2::Triangle.new(chunk)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def read_skins(file)
|
75
|
+
@skins = []
|
76
|
+
read_data(file, skin_name_offset, skin_count, sizeof(:char)*64) do |chunk|
|
77
|
+
@skins << chunk.strip
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def read_texcoords(file)
|
82
|
+
@texcoords = []
|
83
|
+
read_data(file, texture_coord_offset, texture_coord_count, sizeof(:short)*2) do |chunk|
|
84
|
+
@texcoords << chunk.unpack("s2")
|
85
|
+
@texcoords.last[0] = @texcoords.last[0] / skin_width.to_f
|
86
|
+
@texcoords.last[1] = @texcoords.last[1] / skin_height.to_f
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def read_data(file, offset, count, chunk_size)
|
91
|
+
file.sysseek(offset)
|
92
|
+
data = file.sysread(count * chunk_size)
|
93
|
+
count.times do |num|
|
94
|
+
yield data[(num*chunk_size)...(num*chunk_size+chunk_size)]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|