md2 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|