-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMD2Loader.cpp
More file actions
291 lines (246 loc) · 7.97 KB
/
Copy pathMD2Loader.cpp
File metadata and controls
291 lines (246 loc) · 7.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#include "MD2Loader.h"
#include "Texture.h"
// File reading
#include <iostream>
#include <fstream>
using namespace std;
// BYTE added in case Windows.h is not included.
typedef unsigned char BYTE;
// Magic number for MD2 files "IDP2" or 844121161
const int MD2_IDENT = (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I');
// MS2 version
const int MD2_VERSION = 8;
struct Md2Header
{
int indent; // The magic number used to identify the file.
int version; // The file version number (must be 8).
int skinWidth; // The width in pixels of our image.
int skinHeight; // The height in pixels of our image.
int frameSize; // The size in bytes the frames are.
int numSkins; // The number of skins associated with the model.
int numVertices; // The number of vertices.
int numTexCoords; // The number of texture coordinates.
int numTriangles; // The number of faces (polygons).
int numGlCommands; // The number of gl commands.
int numFrames; // The number of animated frames.
int offsetSkins; // The offset in the file for the skin data.
int offsetTexCoords; // The offset in the file for the texture data.
int offsetTriangles; // The offset in the file for the face data.
int offsetFrames; // The offset in the file for the frames data.
int offsetGlCommands; // The offset in the file for the gl commands data.
int offsetEnd; // The end of the file offset.
};
struct Md2Triangle
{
short vertexIndex[3]; // Vertex indices of the triangle
short uvIndex[3]; // Texture coordinate indices
};
struct Md2Vertex
{
BYTE v[3]; // Compressed vertex (x, y, z) coordinates
BYTE lightNormalIndex; // Index to a normal vector for the lighting
};
// Texture co-ordinates
struct Md2TextureCoord
{
short textureCoord[2];
};
struct Md2Frame
{
float scale[3]; // Scale values
float translate[3]; // Translation vector
char name[16]; // Frame name
Md2Vertex verts[1]; // First vertex of this frame
};
struct PcxHeader
{
BYTE ID;
BYTE Version;
BYTE Encoding;
BYTE BitsPerPixel;
short XMin;
short YMin;
short XMax;
short YMax;
short HRes;
short VRes;
BYTE ClrMap[16 * 3];
BYTE Reserved;
BYTE NumPlanes;
short BytesPerLine;
short Pal;
BYTE Filler[58];
};
MD2Loader::MD2Loader()
{
}
MD2Loader::~MD2Loader()
{
}
bool LoadPCX(const char* textureFilename, Texture& texture, const Md2Header* md2Header)
{
std::ifstream file;
BYTE* paletteIndices = texture.GetPaletteIndices();
COLORREF* palette = texture.GetPalette();
// Try to open file
file.open(textureFilename, std::ios::in | std::ios::binary);
if (file.fail())
{
return false;
}
// Read PCX header
PcxHeader header;
file.read(reinterpret_cast<char*>(&header), sizeof(PcxHeader));
// Verify that this is a valid PCX file
// We only handle those with 256 colour palette
if ((header.Version != 5) || (header.BitsPerPixel != 8) ||
(header.Encoding != 1) || (header.NumPlanes != 1) ||
(md2Header && (header.BytesPerLine != md2Header->skinWidth)))
{
// This is not valid supported PCX
file.close();
return false;
}
// Check dimensions
int xSize = header.XMax - header.XMin + 1;
int ySize = header.YMax - header.YMin + 1;
int size = xSize * ySize;
// Check that this matches our MD2 expected texture
// Note. valid size is <= because uses RLE (so potentially smaller)
if (md2Header && (size > (md2Header->skinHeight * md2Header->skinWidth)))
{
// Doesn't match expected MD2 skin size
file.close();
return false;
}
// Reading file data
BYTE processByte, colourByte;
int count = 0;
while (count < size)
{
file.read(reinterpret_cast<char*>(&processByte), 1);
// Run length encoding - test if byte is an RLE byte
if ((processByte & 192) == 192)
{
// Extract number of times repeated byte
processByte &= 63;
file.read(reinterpret_cast<char*>(&colourByte), 1);
for (int index = 0; index < processByte; ++index)
{
// repeatedly write colour
paletteIndices[count] = colourByte;
++count;
}
}
else
{
// Byte is the colour
paletteIndices[count] = processByte;
++count;
}
}
bool returnValue = false;
// read palette data...
file.seekg(-769, std::ios::end); // This offset from end of file
file.read(reinterpret_cast<char*>(&processByte), 1);
if (processByte == 12)
{
BYTE rawPalette[768];
file.read(reinterpret_cast<char*>(&rawPalette), 768);
// Build palette
for (int palIndex = 0; palIndex < 256; ++palIndex)
{
palette[palIndex] = RGB(rawPalette[palIndex * 3],
rawPalette[(palIndex * 3) + 1],
rawPalette[(palIndex * 3) + 2]);
}
returnValue = true;
}
file.close();
return returnValue;
}
// Load model from file.
bool MD2Loader::LoadModel(const char* md2Filename, const char* textureFilename, Model& model, AddPolygon addPolygon, AddVertex addVertex, AddTextureUV addTextureUV)
{
ifstream file;
Md2Header header;
bool bHasTexture = false;
// Try to open MD2 file
file.open(md2Filename, ios::in | ios::binary);
if (file.fail())
{
return false;
}
// Read file header
file.read(reinterpret_cast<char*>(&header), sizeof(Md2Header));
// Verify that this is a MD2 file (check for the magic number and version number)
if ((header.indent != MD2_IDENT) && (header.version != MD2_VERSION))
{
// This is not a MD2 model
file.close();
return false;
}
// Allocate the memory we need
Md2Triangle* triangles = new Md2Triangle[header.numTriangles];
// We are only interested in the first frame
BYTE* frameBuffer = new BYTE[header.frameSize];
Md2Frame* frame = reinterpret_cast<Md2Frame*>(frameBuffer);
Md2TextureCoord* textureCoords = new Md2TextureCoord[header.numTexCoords];
// Read polygon data...
file.seekg(header.offsetTriangles, ios::beg);
file.read(reinterpret_cast<char*>(triangles), sizeof(Md2Triangle)* header.numTriangles);
// Read frame data...
file.seekg(header.offsetFrames, ios::beg);
file.read(reinterpret_cast<char*>(frame), header.frameSize);
// Read texture coordinate data
file.seekg(header.offsetTexCoords, std::ios::beg);
file.read(reinterpret_cast<char*>(textureCoords), sizeof(Md2TextureCoord)* header.numTexCoords);
// Close the file
file.close();
// Attempt to load any texture
if (textureFilename)
{
model.GetTexture().SetTextureSize(header.skinWidth, header.skinHeight);
bHasTexture = LoadPCX(textureFilename, model.GetTexture(), &header);
}
// Polygon array initialization
for (int i = 0; i < header.numTriangles; ++i)
{
// Call supplied member function to add a new polygon to the list
std::invoke(addPolygon, model,
triangles[i].vertexIndex[0], triangles[i].vertexIndex[1], triangles[i].vertexIndex[2],
triangles[i].uvIndex[0], triangles[i].uvIndex[1], triangles[i].uvIndex[2]);
}
// Vertex array initialization
for (int i = 0; i < header.numVertices; ++i)
{
// The following are the expressions needed to access each of the co-ordinates.
//
// X co-ordinate: frame->verts[i].v[0] * frame->scale[0] + frame->translate[0]
// Y co-ordinate: frame->verts[i].v[2] * frame->scale[2] + frame->translate[2]
// Z co-ordinate: frame->verts[i].v[1] * frame->scale[1] + frame->translate[1]
//
// NOTE: We have to swap Y and Z over because Z is up in MD2 and we have Y as up-axis
std::invoke(addVertex, model,
static_cast<float>((frame->verts[i].v[0] * frame->scale[0]) + frame->translate[0]),
static_cast<float>((frame->verts[i].v[2] * frame->scale[2]) + frame->translate[2]),
static_cast<float>((frame->verts[i].v[1] * frame->scale[1]) + frame->translate[1]));
}
// Texture coordinates initialisation
if (bHasTexture)
{
for (int i = 0; i < header.numTexCoords; i++)
{
std::invoke(addTextureUV, model, textureCoords[i].textureCoord[0], textureCoords[i].textureCoord[1]);
}
}
// Free dynamically allocated memory
delete[] triangles; // NOTE: this is 'array' delete. Must be sure to use this
triangles = 0;
delete[] frameBuffer;
frameBuffer = 0;
frame = 0;
delete[] textureCoords;
textureCoords = 0;
return true;
}