1 //----------------------------------------------------------------------------
2 // EDGE Rendering Data Handling Code
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2009 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // Based on the DOOM source code, released by Id Software under the
20 // following copyright:
21 //
22 // Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 //
26 // -ACB- 1998/09/09 Reformatted File Layout.
27 // -KM- 1998/09/27 Colourmaps can be dynamically changed.
28 // -ES- 2000/02/12 Moved most of this module to w_texture.c.
29
30 #include "i_defs.h"
31
32 #include "e_search.h"
33 #include "dm_state.h"
34 #include "dm_defs.h"
35 #include "m_argv.h"
36 #include "m_misc.h"
37 #include "p_local.h"
38 #include "r_image.h"
39 #include "r_sky.h"
40 #include "w_flat.h"
41 #include "w_model.h"
42 #include "w_sprite.h"
43 #include "w_wad.h"
44 #include "w_texture.h"
45 #include "z_zone.h"
46
47
48 cvar_c r_precache_tex;
49 cvar_c r_precache_sprite;
50 cvar_c r_precache_model;
51
52
53 //
54 // R_AddFlatAnim
55 //
56 // Here are the rules for flats, they get a bit hairy, but are the
57 // simplest thing which achieves expected behaviour:
58 //
59 // 1. When two flats in different wads have the same name, the flat
60 // in the _later_ wad overrides the flat in the earlier wad. This
61 // allows pwads to replace iwad flats -- as is usual. For general
62 // use of flats (e.g. in levels) their order is not an issue.
63 //
64 // 2. The flat animation sequence is determined by the _earliest_ wad
65 // which contains _both_ the start and the end flat. The sequence
66 // contained in that wad becomes the animation sequence (the list
67 // of flat names). These names are then looked up normally, so
68 // flats in newer wads will get used if their name matches one in
69 // the sequence.
70 //
71 // -AJA- 2001/01/28: reworked flat animations.
72 //
R_AddFlatAnim(animdef_c * anim)73 void R_AddFlatAnim(animdef_c *anim)
74 {
75 if (anim->pics.GetSize() == 0) // old way
76 {
77 int start = W_CheckNumForName(anim->startname);
78 int end = W_CheckNumForName(anim->endname);
79
80 int file;
81 int s_offset, e_offset;
82
83 int i;
84
85 if (start == -1 || end == -1)
86 {
87 // sequence not valid. Maybe it is the DOOM 1 IWAD.
88 return;
89 }
90
91 file = W_FindFlatSequence(anim->startname, anim->endname,
92 &s_offset, &e_offset);
93
94 if (file < 0)
95 {
96 I_Warning("Missing flat animation: %s-%s not in any wad.\n",
97 (const char*)anim->startname, (const char*)anim->endname);
98 return;
99 }
100
101 epi::u32array_c& lumps = W_GetListLumps(file, LMPLST_Flats);
102 int total = lumps.GetSize();
103
104 SYS_ASSERT(s_offset <= e_offset);
105 SYS_ASSERT(e_offset < total);
106
107 // determine animation sequence
108 total = e_offset - s_offset + 1;
109
110 const image_c **flats = new const image_c* [total];
111
112 // lookup each flat
113 for (i=0; i < total; i++)
114 {
115 const char *name = W_GetLumpName(lumps[s_offset + i]);
116
117 // Note we use W_ImageFromFlat() here. It might seem like a good
118 // optimisation to use the lump number directly, but we can't do
119 // that -- the lump list does NOT take overriding flats (in newer
120 // pwads) into account.
121
122 flats[i] = W_ImageLookup(name, INS_Flat, ILF_Null|ILF_Exact|ILF_NoNew);
123 }
124
125 W_AnimateImageSet(flats, total, anim->speed);
126 delete[] flats;
127 }
128
129 // -AJA- 2004/10/27: new SEQUENCE command for anims
130
131 int total = anim->pics.GetSize();
132
133 if (total == 1)
134 return;
135
136 const image_c **flats = new const image_c* [total];
137
138 for (int i = 0; i < total; i++)
139 {
140 flats[i] = W_ImageLookup(anim->pics[i], INS_Flat, ILF_Null|ILF_Exact);
141 }
142
143 W_AnimateImageSet(flats, total, anim->speed);
144 delete[] flats;
145 }
146
147 //
148 // R_AddTextureAnim
149 //
150 // Here are the rules for textures:
151 //
152 // 1. The TEXTURE1/2 lumps require a PNAMES lump to complete their
153 // meaning. Some wads have the TEXTURE1/2 lump(s) but lack a
154 // PNAMES lump -- in this case the next oldest PNAMES lump is used
155 // (e.g. the one in the IWAD).
156 //
157 // 2. When two textures in different wads have the same name, the
158 // texture in the _later_ wad overrides the one in the earlier wad,
159 // as is usual. For general use of textures (e.g. in levels),
160 // their ordering is not an issue.
161 //
162 // 3. The texture animation sequence is determined by the _latest_ wad
163 // whose TEXTURE1/2 lump contains _both_ the start and the end
164 // texture. The sequence within that lump becomes the animation
165 // sequence (the list of texture names). These names are then
166 // looked up normally, so textures in newer wads can get used if
167 // their name matches one in the sequence.
168 //
169 // -AJA- 2001/06/17: reworked texture animations.
170 //
R_AddTextureAnim(animdef_c * anim)171 void R_AddTextureAnim(animdef_c *anim)
172 {
173 if (anim->pics.GetSize() == 0) // old way
174 {
175 int set, s_offset, e_offset;
176
177 set = W_FindTextureSequence(anim->startname, anim->endname,
178 &s_offset, &e_offset);
179
180 if (set < 0)
181 {
182 // sequence not valid. Maybe it is the DOOM 1 IWAD.
183 return;
184 }
185
186 SYS_ASSERT(s_offset <= e_offset);
187
188 int total = e_offset - s_offset + 1;
189 const image_c **texs = new const image_c* [total];
190
191 // lookup each texture
192 for (int i = 0; i < total; i++)
193 {
194 const char *name = W_TextureNameInSet(set, s_offset + i);
195 texs[i] = W_ImageLookup(name, INS_Texture, ILF_Null|ILF_Exact|ILF_NoNew);
196 }
197
198 W_AnimateImageSet(texs, total, anim->speed);
199 delete[] texs;
200
201 return;
202 }
203
204 // -AJA- 2004/10/27: new SEQUENCE command for anims
205
206 int total = anim->pics.GetSize();
207
208 if (total == 1)
209 return;
210
211 const image_c **texs = new const image_c* [total];
212
213 for (int i = 0; i < total; i++)
214 {
215 texs[i] = W_ImageLookup(anim->pics[i], INS_Texture, ILF_Null|ILF_Exact);
216 }
217
218 W_AnimateImageSet(texs, total, anim->speed);
219 delete[] texs;
220 }
221
222 //
223 // R_AddGraphicAnim
224 //
R_AddGraphicAnim(animdef_c * anim)225 void R_AddGraphicAnim(animdef_c *anim)
226 {
227 int total = anim->pics.GetSize();
228
229 SYS_ASSERT(total != 0);
230
231 if (total == 1)
232 return;
233
234 const image_c **users = new const image_c* [total];
235
236 for (int i = 0; i < total; i++)
237 {
238 users[i] = W_ImageLookup(anim->pics[i], INS_Graphic, ILF_Null|ILF_Exact);
239 }
240
241 W_AnimateImageSet(users, total, anim->speed);
242 delete[] users;
243 }
244
245 //
246 // W_InitFlats
247 //
W_InitFlats(void)248 void W_InitFlats(void)
249 {
250 int max_file = W_GetNumFiles();
251 int j, file;
252
253 int *F_lumps = NULL;
254 int numflats = 0;
255
256 I_Printf("W_InitFlats...\n");
257
258 // iterate over each file, creating our big array of flats
259
260 for (file=0; file < max_file; file++)
261 {
262 epi::u32array_c& lumps = W_GetListLumps(file, LMPLST_Flats);
263 int lumpnum = lumps.GetSize();
264
265 if (lumpnum == 0)
266 continue;
267
268 Z_Resize(F_lumps, int, numflats + lumpnum);
269
270 for (j=0; j < lumpnum; j++, numflats++)
271 F_lumps[numflats] = lumps[j];
272 }
273
274 if (numflats == 0)
275 I_Error("No flats found ! Make sure the chosen IWAD is valid.\n");
276
277 // now sort the flats, primarily by increasing name, secondarily by
278 // increasing lump number (a measure of newness).
279
280 #define CMP(a, b) \
281 (strcmp(W_GetLumpName(a), W_GetLumpName(b)) < 0 || \
282 (strcmp(W_GetLumpName(a), W_GetLumpName(b)) == 0 && a < b))
283 QSORT(int, F_lumps, numflats, CUTOFF);
284 #undef CMP
285
286 // remove duplicate names. We rely on the fact that newer lumps
287 // have greater lump values than older ones. Because the QSORT took
288 // newness into account, only the last entry in a run of identically
289 // named flats needs to be kept.
290
291 for (j=1; j < numflats; j++)
292 {
293 int a = F_lumps[j - 1];
294 int b = F_lumps[j];
295
296 if (strcmp(W_GetLumpName(a), W_GetLumpName(b)) == 0)
297 F_lumps[j - 1] = -1;
298 }
299
300 #if 0 // DEBUGGING
301 for (j=0; j < numflats; j++)
302 {
303 L_WriteDebug("FLAT #%d: lump=%d name=[%s]\n", j,
304 F_lumps[j], W_GetLumpName(F_lumps[j]));
305 }
306 #endif
307
308 W_ImageCreateFlats(F_lumps, numflats);
309 Z_Free(F_lumps);
310 }
311
312 //
313 // W_InitPicAnims
314 //
W_InitPicAnims(void)315 void W_InitPicAnims(void)
316 {
317 epi::array_iterator_c it;
318 animdef_c *A;
319
320 // loop through animdefs, and add relevant anims.
321 // Note: reverse order, give priority to newer anims.
322 for (it = animdefs.GetTailIterator(); it.IsValid(); it--)
323 {
324 A = ITERATOR_TO_TYPE(it, animdef_c*);
325
326 SYS_ASSERT(A);
327
328 switch (A->type)
329 {
330 case animdef_c::A_Texture:
331 R_AddTextureAnim(A);
332 break;
333
334 case animdef_c::A_Flat:
335 R_AddFlatAnim(A);
336 break;
337
338 case animdef_c::A_Graphic:
339 R_AddGraphicAnim(A);
340 break;
341 }
342 }
343 }
344
345
W_PrecacheTextures(void)346 void W_PrecacheTextures(void)
347 {
348 int i;
349
350 // maximum possible images
351 int max_image = 1 + 3 * numsides + 2 * numsectors;
352 int count = 0;
353
354 const image_c ** images = new const image_c* [max_image];
355
356 // Sky texture is always present.
357 images[count++] = sky_image;
358
359 // add in sidedefs
360 for (i=0; i < numsides; i++)
361 {
362 if (sides[i].top.image)
363 images[count++] = sides[i].top.image;
364
365 if (sides[i].middle.image)
366 images[count++] = sides[i].middle.image;
367
368 if (sides[i].bottom.image)
369 images[count++] = sides[i].bottom.image;
370 }
371
372 SYS_ASSERT(count <= max_image);
373
374 // add in planes
375 for (i=0; i < numsectors; i++)
376 {
377 if (sectors[i].floor.image)
378 images[count++] = sectors[i].floor.image;
379
380 if (sectors[i].ceil.image)
381 images[count++] = sectors[i].ceil.image;
382 }
383
384 SYS_ASSERT(count <= max_image);
385
386 // Sort the images, so we can ignore the duplicates
387
388 #define CMP(a, b) (a < b)
389 QSORT(const image_c *, images, count, CUTOFF);
390 #undef CMP
391
392 for (i=0; i < count; i++)
393 {
394 SYS_ASSERT(images[i]);
395
396 if (i+1 < count && images[i] == images[i + 1])
397 continue;
398
399 if (images[i] == skyflatimage)
400 continue;
401
402 W_ImagePreCache(images[i]);
403 }
404
405 delete[] images;
406 }
407
408
409 //
410 // W_PrecacheLevel
411 //
412 // Preloads all relevant graphics for the level.
413 //
414 // -AJA- 2001/06/18: Reworked for image system.
415 //
W_PrecacheLevel(void)416 void W_PrecacheLevel(void)
417 {
418 if (r_precache_sprite.d)
419 W_PrecacheSprites();
420
421 if (r_precache_tex.d)
422 W_PrecacheTextures();
423
424 if (r_precache_model.d)
425 W_PrecacheModels();
426
427 RGL_PreCacheSky();
428 }
429
430 //--- editor settings ---
431 // vi:ts=4:sw=4:noexpandtab
432