-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAnimation.cpp
440 lines (339 loc) · 11.1 KB
/
Animation.cpp
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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
/////////////////////////////////////////////////////////////////////////////
//
// $Header: /WinArena/Animation.cpp 9 12/31/06 10:45a Lee $
//
// File: Animation.cpp
//
//
// This file contains several utility classes for storing textures and
// animation sequences (each animation being composed of several individual
// textures). All of the internal logic required to load and decompress
// image data is hidden under here (most of it resides in ParseBSA.cpp).
//
/////////////////////////////////////////////////////////////////////////////
#include "WinArena.h"
#include "Animation.h"
#include "ParseBSA.h"
#include "TGA.h"
/////////////////////////////////////////////////////////////////////////////
//
// constructor
//
CwaTexture::CwaTexture(void)
: m_Width(0),
m_Height(0),
m_OffsetX(0),
m_OffsetY(0),
m_Pitch(0),
m_pTexels(NULL),
m_pPalette(NULL)
{
}
/////////////////////////////////////////////////////////////////////////////
//
// destructor
//
CwaTexture::~CwaTexture(void)
{
Free();
}
/////////////////////////////////////////////////////////////////////////////
//
// Allocate()
//
// Allocates a new texture buffer. This defaults to making the image have
// one extra pixel along the right and bottom edges, so that special cases
// aren't needed to clamp or wrap textures properly.
//
void CwaTexture::Allocate(DWORD width, DWORD height, bool expand)
{
Free();
m_Width = width;
m_Height = height;
m_Pitch = width;
if (expand) {
m_Pitch += 1;
}
m_pTexels = new BYTE[m_Pitch * (m_Height + (expand ? 1 : 0))];
}
/////////////////////////////////////////////////////////////////////////////
//
// Free()
//
void CwaTexture::Free(void)
{
m_Width = 0;
m_Height = 0;
m_Pitch = 0;
SafeDeleteArray(m_pTexels);
SafeDeleteArray(m_pPalette);
}
/////////////////////////////////////////////////////////////////////////////
//
// LoadIMG()
//
void CwaTexture::LoadIMG(char filename[], bool clamp)
{
PROFILE(PFID_Load_IMG);
Free();
DWORD width, height;
bool hasHeader;
g_pArenaLoader->CheckIMG(filename, width, height, m_OffsetX, m_OffsetY, hasHeader);
Allocate(width, height);
m_pPalette = g_pArenaLoader->LoadIMG(filename, width, height, m_Pitch, hasHeader, m_pTexels);
Clamp(clamp);
}
/////////////////////////////////////////////////////////////////////////////
//
// LoadWallTexture()
//
void CwaTexture::LoadWallTexture(char filename[], DWORD index, bool clamp)
{
PROFILE(PFID_Load_WallTexture);
Free();
DWORD resID = g_pArenaLoader->LookUpResource(filename);
if (DWORD(-1) != resID) {
Allocate(64, 64, false);
g_pArenaLoader->LoadWallTexture(resID, m_pTexels, (index * 64 * 64));
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Clamp()
//
// Assuming that the texture was allocated with extra pixels, this will set
// up the texture to properly wrap or clamp for sampling purposes.
//
// This removes the need for special cases when rendering. If sampling a
// pixel near the end of the line (or the bottom of the texture), sampling
// logic needs extra <if> conditions to handle different special cases,
// which slows rendering time. Presetting the texture like this will
// eliminate the need for that extra logic, making rendering faster.
//
void CwaTexture::Clamp(bool clamp)
{
// First loop over each line of the frame, adding an extra pixel at the
// end of the line.
for (DWORD y = 0; y < m_Height; ++y) {
BYTE *pLine = m_pTexels + (y * m_Pitch);
// Clamp by replicating the last pixel on the line.
if (clamp) {
pLine[m_Width] = pLine[m_Width-1];
}
// Wrap by copying the first pixel to the end of the line.
else {
pLine[m_Width] = pLine[0];
}
}
// Then do something similar by adding an extra line below the texture,
// either duplicating the first line of the image (to wrap) or the last
// valid line (to clamp).
BYTE *pSrc = clamp ? (m_pTexels + ((m_Height-1) * m_Pitch)) : m_pTexels;
BYTE *pDst = m_pTexels + (m_Height * m_Pitch);
for (DWORD x = 0; x <= m_Width; ++x) {
pDst[x] = pSrc[x];
}
}
/////////////////////////////////////////////////////////////////////////////
//
// SaveTexture()
//
// Takes whatever the current texture is (as set by SetTexture(), or the
// default checkerboard texture if no texture has been provided), and writes
// it off to a 32-bit TGA file.
//
// Compression would be nice, to reduce file size. Plus most muggles won't
// have a clue how to view a TGA file, so making it a GIF would be useful.
// But that requires adding libraries to the project, which I don't want to
// do -- keeping binaries out of the project eliminates the possibility that
// someone will embed trojans or other malware in those binaries.
//
void CwaTexture::SaveTexture(char filename[])
{
TGAFileHeader header;
memset(&header, 0, sizeof(header));
header.ImageType = TGAImageType_ColorMapped;
header.ColorMapType = 1;
header.ColorMapSpec.Length = 256;
header.ColorMapSpec.EntrySize = 24;
header.ImageSpec.ImageDescriptor = TGATopToBottom;
header.ImageSpec.ImageWidth = WORD(m_Width);
header.ImageSpec.ImageHeight = WORD(m_Height);
header.ImageSpec.PixelDepth = 8;
TGAFooter footer;
footer.devDirOffset = 0;
footer.extAreaOffset = 0;
strcpy(footer.signature, TGASignature);
BYTE palette[3*256];
g_pArenaLoader->GetPaletteBlueFirst(Palette_Default, palette);
FILE *pFile = fopen(filename, "wb");
if (NULL != pFile) {
fwrite(&header, sizeof(header), 1, pFile);
fwrite(palette, 1, 3 * 256, pFile);
for (DWORD y = 0; y < m_Height; ++y) {
fwrite(m_pTexels + (y * m_Pitch), 1, m_Width, pFile);
}
fwrite(&footer, sizeof(footer), 1, pFile);
fclose(pFile);
}
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
// constructor
//
CwaAnimation::CwaAnimation(void)
: m_Width(0),
m_Height(0),
m_FrameCount(0),
m_pFrames(NULL)
{
}
/////////////////////////////////////////////////////////////////////////////
//
// destructor
//
CwaAnimation::~CwaAnimation(void)
{
SafeDeleteArray(m_pFrames);
}
/////////////////////////////////////////////////////////////////////////////
//
// LoadAnimation()
//
// Loads animation data from a CFA or DFA file. These animations are used
// in similar ways, but are encoded differenly, so different code paths are
// needed to extract the animation data into a useable format.
//
bool CwaAnimation::LoadAnimation(char filename[])
{
PROFILE(PFID_Load_Animation);
SafeDeleteArray(m_pFrames);
m_Width = 0;
m_Height = 0;
m_FrameCount = 0;
DWORD resID = g_pArenaLoader->LookUpResource(filename);
if (DWORD(-1) == resID) {
return false;
}
char *extension = strstr(filename, ".");
if (0 == strcmp(extension, ".CFA")) {
g_pArenaLoader->CheckCFA(resID, m_Width, m_Height, m_FrameCount);
if ((m_Width == 0) || (m_Height == 0) || (m_FrameCount == 0)) {
return false;
}
m_pFrames = new CwaTexture[m_FrameCount];
BYTE **ppFrames = reinterpret_cast<BYTE**>(alloca(m_FrameCount * sizeof(BYTE*)));
for (DWORD frameNum = 0; frameNum < m_FrameCount; ++frameNum) {
m_pFrames[frameNum].Allocate(m_Width, m_Height);
ppFrames[frameNum] = m_pFrames[frameNum].m_pTexels;
}
g_pArenaLoader->LoadCFA(resID, m_Width, m_Height, m_Width + 1, m_FrameCount, ppFrames);
for (DWORD frameNum = 0; frameNum < m_FrameCount; ++frameNum) {
m_pFrames[frameNum].Clamp(true);
}
}
else if (0 == strcmp(extension, ".DFA")) {
g_pArenaLoader->CheckDFA(resID, m_Width, m_Height, m_FrameCount);
if ((m_Width == 0) || (m_Height == 0) || (m_FrameCount == 0)) {
return false;
}
m_pFrames = new CwaTexture[m_FrameCount];
for (DWORD frameNum = 0; frameNum < m_FrameCount; ++frameNum) {
m_pFrames[frameNum].Allocate(m_Width, m_Height);
g_pArenaLoader->LoadDFA(resID, m_Width, m_Height, m_Width + 1, frameNum, m_pFrames[frameNum].m_pTexels);
m_pFrames[frameNum].Clamp(true);
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
//
// LoadCIF()
//
// Loads a CIF (Compressed Image File?) image into the animation. This
// will contain a sequence of frames. Most CIFs fall into one of three
// categories: character faces, equipment icons, and weapon animations.
//
bool CwaAnimation::LoadCIF(char filename[], DWORD frameCount)
{
PROFILE(PFID_Load_CIF);
SafeDeleteArray(m_pFrames);
m_FrameCount = frameCount;
m_pFrames = new CwaTexture[m_FrameCount];
for (DWORD frameNum = 0; frameNum < frameCount; ++frameNum) {
DWORD width, height, offsetX, offsetY;
g_pArenaLoader->CheckCIF(filename, frameNum, width, height, offsetX, offsetY);
m_pFrames[frameNum].Allocate(width, height);
// Record the offsets. These are essential for weapon animations.
m_pFrames[frameNum].m_OffsetX = offsetX;
m_pFrames[frameNum].m_OffsetY = offsetY;
if (0 == frameNum) {
m_Width = width;
m_Height = height;
}
g_pArenaLoader->LoadCIF(filename, width, height, frameNum, m_pFrames[frameNum].m_pTexels);
}
return true;
}
/////////////////////////////////////////////////////////////////////////////
//
// LoadWater()
//
// The water and lava animations are identical, both in how they are stored
// and rendered. They are found in RCI files, contain 5 frames of 320x100
// image data, uncompressed, without any header information.
//
void CwaAnimation::LoadWater(bool lava)
{
PROFILE(PFID_Load_Water);
SafeDeleteArray(m_pFrames);
m_Width = 320;
m_Height = 100;
m_FrameCount = 5;
m_pFrames = new CwaTexture[m_FrameCount];
for (DWORD frameNum = 0; frameNum < m_FrameCount; ++frameNum) {
m_pFrames[frameNum].Allocate(m_Width-1, m_Height);
g_pArenaLoader->LoadWater(lava ? "LAVAANI.RCI" : "WATERANI.RCI", frameNum, m_pFrames[frameNum].m_pTexels);
}
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
// constructor
//
CwaCreature::CwaCreature(void)
{
}
/////////////////////////////////////////////////////////////////////////////
//
// destructor
//
CwaCreature::~CwaCreature(void)
{
}
/////////////////////////////////////////////////////////////////////////////
//
// LoadAnimation()
//
// Given the base name of a creature animation, all 6 animation sequences
// will be loaded. For example, a "RAT" animation is stored in the files,
// "RAT1.CFA", "RAT2.CFA", ... "RAT6.CFA."
//
// Each animation sequence shows the creature from a different point of view.
// RAT1.CFA shows a rat facing the player (and also includes an attack
// animation, unlike the other sequences), while RAT6.CFA shows the rat
// facing directly away from the player.
//
void CwaCreature::LoadAnimation(char basename[])
{
char filename[64];
for (DWORD i = 0; i < 6; ++i) {
sprintf(filename, "%s%d.CFA", basename, i + 1);
m_Animation[i].LoadAnimation(filename);
}
}