diff options
Diffstat (limited to 'src/r_patch.c')
-rw-r--r-- | src/r_patch.c | 786 |
1 files changed, 786 insertions, 0 deletions
diff --git a/src/r_patch.c b/src/r_patch.c new file mode 100644 index 0000000..e466fb2 --- /dev/null +++ b/src/r_patch.c | |||
@@ -0,0 +1,786 @@ | |||
1 | /* Emacs style mode select -*- C++ -*- | ||
2 | *----------------------------------------------------------------------------- | ||
3 | * | ||
4 | * | ||
5 | * PrBoom: a Doom port merged with LxDoom and LSDLDoom | ||
6 | * based on BOOM, a modified and improved DOOM engine | ||
7 | * Copyright (C) 1999 by | ||
8 | * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman | ||
9 | * Copyright (C) 1999-2002 by | ||
10 | * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze | ||
11 | * Copyright 2005, 2006 by | ||
12 | * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version 2 | ||
17 | * of the License, or (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||
27 | * 02111-1307, USA. | ||
28 | * | ||
29 | *-----------------------------------------------------------------------------*/ | ||
30 | |||
31 | #include "z_zone.h" | ||
32 | #include "doomstat.h" | ||
33 | #include "w_wad.h" | ||
34 | #include "r_main.h" | ||
35 | #include "r_sky.h" | ||
36 | #include "r_bsp.h" | ||
37 | #include "r_things.h" | ||
38 | #include "p_tick.h" | ||
39 | #include "i_system.h" | ||
40 | #include "r_draw.h" | ||
41 | #include "lprintf.h" | ||
42 | #include "r_patch.h" | ||
43 | #include <assert.h> | ||
44 | |||
45 | // posts are runs of non masked source pixels | ||
46 | typedef struct | ||
47 | { | ||
48 | byte topdelta; // -1 is the last post in a column | ||
49 | byte length; // length data bytes follows | ||
50 | } post_t; | ||
51 | |||
52 | // column_t is a list of 0 or more post_t, (byte)-1 terminated | ||
53 | typedef post_t column_t; | ||
54 | |||
55 | // | ||
56 | // Patches. | ||
57 | // A patch holds one or more columns. | ||
58 | // Patches are used for sprites and all masked pictures, | ||
59 | // and we compose textures from the TEXTURE1/2 lists | ||
60 | // of patches. | ||
61 | // | ||
62 | |||
63 | typedef struct | ||
64 | { | ||
65 | short width, height; // bounding box size | ||
66 | short leftoffset; // pixels to the left of origin | ||
67 | short topoffset; // pixels below the origin | ||
68 | int columnofs[8]; // only [width] used | ||
69 | } patch_t; | ||
70 | |||
71 | //--------------------------------------------------------------------------- | ||
72 | // Re-engineered patch support | ||
73 | //--------------------------------------------------------------------------- | ||
74 | static rpatch_t *patches = 0; | ||
75 | |||
76 | static rpatch_t *texture_composites = 0; | ||
77 | |||
78 | //--------------------------------------------------------------------------- | ||
79 | void R_InitPatches(void) { | ||
80 | if (!patches) | ||
81 | { | ||
82 | patches = (rpatch_t*)malloc(numlumps * sizeof(rpatch_t)); | ||
83 | // clear out new patches to signal they're uninitialized | ||
84 | memset(patches, 0, sizeof(rpatch_t)*numlumps); | ||
85 | } | ||
86 | if (!texture_composites) | ||
87 | { | ||
88 | texture_composites = (rpatch_t*)malloc(numtextures * sizeof(rpatch_t)); | ||
89 | // clear out new patches to signal they're uninitialized | ||
90 | memset(texture_composites, 0, sizeof(rpatch_t)*numtextures); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | //--------------------------------------------------------------------------- | ||
95 | void R_FlushAllPatches(void) { | ||
96 | int i; | ||
97 | |||
98 | if (patches) | ||
99 | { | ||
100 | for (i=0; i < numlumps; i++) | ||
101 | if (patches[i].locks > 0) | ||
102 | I_Error("R_FlushAllPatches: patch number %i still locked",i); | ||
103 | free(patches); | ||
104 | patches = NULL; | ||
105 | } | ||
106 | if (texture_composites) | ||
107 | { | ||
108 | for (i=0; i<numtextures; i++) | ||
109 | if (texture_composites[i].data) | ||
110 | free(texture_composites[i].data); | ||
111 | free(texture_composites); | ||
112 | texture_composites = NULL; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | //--------------------------------------------------------------------------- | ||
117 | int R_NumPatchWidth(int lump) | ||
118 | { | ||
119 | const rpatch_t *patch = R_CachePatchNum(lump); | ||
120 | int width = patch->width; | ||
121 | R_UnlockPatchNum(lump); | ||
122 | return width; | ||
123 | } | ||
124 | |||
125 | //--------------------------------------------------------------------------- | ||
126 | int R_NumPatchHeight(int lump) | ||
127 | { | ||
128 | const rpatch_t *patch = R_CachePatchNum(lump); | ||
129 | int height = patch->height; | ||
130 | R_UnlockPatchNum(lump); | ||
131 | return height; | ||
132 | } | ||
133 | |||
134 | //--------------------------------------------------------------------------- | ||
135 | static int getPatchIsNotTileable(const patch_t *patch) { | ||
136 | int x=0, numPosts, lastColumnDelta = 0; | ||
137 | const column_t *column; | ||
138 | int cornerCount = 0; | ||
139 | int hasAHole = 0; | ||
140 | |||
141 | for (x=0; x<SHORT(patch->width); x++) { | ||
142 | column = (const column_t *)((const byte *)patch + LONG(patch->columnofs[x])); | ||
143 | if (!x) lastColumnDelta = column->topdelta; | ||
144 | else if (lastColumnDelta != column->topdelta) hasAHole = 1; | ||
145 | |||
146 | numPosts = 0; | ||
147 | while (column->topdelta != 0xff) { | ||
148 | // check to see if a corner pixel filled | ||
149 | if (x == 0 && column->topdelta == 0) cornerCount++; | ||
150 | else if (x == 0 && column->topdelta + column->length >= SHORT(patch->height)) cornerCount++; | ||
151 | else if (x == SHORT(patch->width)-1 && column->topdelta == 0) cornerCount++; | ||
152 | else if (x == SHORT(patch->width)-1 && column->topdelta + column->length >= SHORT(patch->height)) cornerCount++; | ||
153 | |||
154 | if (numPosts++) hasAHole = 1; | ||
155 | column = (const column_t *)((const byte *)column + column->length + 4); | ||
156 | } | ||
157 | } | ||
158 | |||
159 | if (cornerCount == 4) return 0; | ||
160 | return hasAHole; | ||
161 | } | ||
162 | |||
163 | //--------------------------------------------------------------------------- | ||
164 | static int getIsSolidAtSpot(const column_t *column, int spot) { | ||
165 | if (!column) return 0; | ||
166 | while (column->topdelta != 0xff) { | ||
167 | if (spot < column->topdelta) return 0; | ||
168 | if ((spot >= column->topdelta) && (spot <= column->topdelta + column->length)) return 1; | ||
169 | column = (const column_t*)((const byte*)column + 3 + column->length + 1); | ||
170 | } | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | //--------------------------------------------------------------------------- | ||
175 | // Used to determine whether a column edge (top or bottom) should slope | ||
176 | // up or down for smoothed masked edges - POPE | ||
177 | //--------------------------------------------------------------------------- | ||
178 | static int getColumnEdgeSlope(const column_t *prevcolumn, const column_t *nextcolumn, int spot) { | ||
179 | int holeToLeft = !getIsSolidAtSpot(prevcolumn, spot); | ||
180 | int holeToRight = !getIsSolidAtSpot(nextcolumn, spot); | ||
181 | |||
182 | if (holeToLeft && !holeToRight) return 1; | ||
183 | if (!holeToLeft && holeToRight) return -1; | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | //--------------------------------------------------------------------------- | ||
188 | static void createPatch(int id) { | ||
189 | rpatch_t *patch; | ||
190 | const int patchNum = id; | ||
191 | const patch_t *oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); | ||
192 | const column_t *oldColumn, *oldPrevColumn, *oldNextColumn; | ||
193 | int x, y; | ||
194 | int pixelDataSize; | ||
195 | int columnsDataSize; | ||
196 | int postsDataSize; | ||
197 | int dataSize; | ||
198 | int *numPostsInColumn; | ||
199 | int numPostsTotal; | ||
200 | const unsigned char *oldColumnPixelData; | ||
201 | int numPostsUsedSoFar; | ||
202 | int edgeSlope; | ||
203 | |||
204 | #ifdef RANGECHECK | ||
205 | if (id >= numlumps) | ||
206 | I_Error("createPatch: %i >= numlumps", id); | ||
207 | #endif | ||
208 | |||
209 | patch = &patches[id]; | ||
210 | // proff - 2003-02-16 What about endianess? | ||
211 | patch->width = SHORT(oldPatch->width); | ||
212 | patch->widthmask = 0; | ||
213 | patch->height = SHORT(oldPatch->height); | ||
214 | patch->leftoffset = SHORT(oldPatch->leftoffset); | ||
215 | patch->topoffset = SHORT(oldPatch->topoffset); | ||
216 | patch->isNotTileable = getPatchIsNotTileable(oldPatch); | ||
217 | |||
218 | // work out how much memory we need to allocate for this patch's data | ||
219 | pixelDataSize = (patch->width * patch->height + 4) & ~3; | ||
220 | columnsDataSize = sizeof(rcolumn_t) * patch->width; | ||
221 | |||
222 | // count the number of posts in each column | ||
223 | numPostsInColumn = (int*)malloc(sizeof(int) * patch->width); | ||
224 | numPostsTotal = 0; | ||
225 | |||
226 | for (x=0; x<patch->width; x++) { | ||
227 | oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); | ||
228 | numPostsInColumn[x] = 0; | ||
229 | while (oldColumn->topdelta != 0xff) { | ||
230 | numPostsInColumn[x]++; | ||
231 | numPostsTotal++; | ||
232 | oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | postsDataSize = numPostsTotal * sizeof(rpost_t); | ||
237 | |||
238 | // allocate our data chunk | ||
239 | dataSize = pixelDataSize + columnsDataSize + postsDataSize; | ||
240 | patch->data = (unsigned char*)Z_Malloc(dataSize, PU_CACHE, (void **)&patch->data); | ||
241 | memset(patch->data, 0, dataSize); | ||
242 | |||
243 | // set out pixel, column, and post pointers into our data array | ||
244 | patch->pixels = patch->data; | ||
245 | patch->columns = (rcolumn_t*)((unsigned char*)patch->pixels + pixelDataSize); | ||
246 | patch->posts = (rpost_t*)((unsigned char*)patch->columns + columnsDataSize); | ||
247 | |||
248 | // sanity check that we've got all the memory allocated we need | ||
249 | assert((((byte*)patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)patch->data) == dataSize); | ||
250 | |||
251 | memset(patch->pixels, 0xff, (patch->width*patch->height)); | ||
252 | |||
253 | // fill in the pixels, posts, and columns | ||
254 | numPostsUsedSoFar = 0; | ||
255 | for (x=0; x<patch->width; x++) { | ||
256 | |||
257 | oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); | ||
258 | |||
259 | if (patch->isNotTileable) { | ||
260 | // non-tiling | ||
261 | if (x == 0) oldPrevColumn = 0; | ||
262 | else oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x-1])); | ||
263 | if (x == patch->width-1) oldNextColumn = 0; | ||
264 | else oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x+1])); | ||
265 | } | ||
266 | else { | ||
267 | // tiling | ||
268 | int prevColumnIndex = x-1; | ||
269 | int nextColumnIndex = x+1; | ||
270 | while (prevColumnIndex < 0) prevColumnIndex += patch->width; | ||
271 | while (nextColumnIndex >= patch->width) nextColumnIndex -= patch->width; | ||
272 | oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[prevColumnIndex])); | ||
273 | oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[nextColumnIndex])); | ||
274 | } | ||
275 | |||
276 | // setup the column's data | ||
277 | patch->columns[x].pixels = patch->pixels + (x*patch->height) + 0; | ||
278 | patch->columns[x].numPosts = numPostsInColumn[x]; | ||
279 | patch->columns[x].posts = patch->posts + numPostsUsedSoFar; | ||
280 | |||
281 | while (oldColumn->topdelta != 0xff) { | ||
282 | // set up the post's data | ||
283 | patch->posts[numPostsUsedSoFar].topdelta = oldColumn->topdelta; | ||
284 | patch->posts[numPostsUsedSoFar].length = oldColumn->length; | ||
285 | patch->posts[numPostsUsedSoFar].slope = 0; | ||
286 | |||
287 | edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta); | ||
288 | if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_UP; | ||
289 | else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_DOWN; | ||
290 | |||
291 | edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta+oldColumn->length); | ||
292 | if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_UP; | ||
293 | else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_DOWN; | ||
294 | |||
295 | // fill in the post's pixels | ||
296 | oldColumnPixelData = (const byte *)oldColumn + 3; | ||
297 | for (y=0; y<oldColumn->length; y++) { | ||
298 | patch->pixels[x * patch->height + oldColumn->topdelta + y] = oldColumnPixelData[y]; | ||
299 | } | ||
300 | |||
301 | oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); | ||
302 | numPostsUsedSoFar++; | ||
303 | } | ||
304 | } | ||
305 | |||
306 | if (1 || patch->isNotTileable) { | ||
307 | const rcolumn_t *column, *prevColumn; | ||
308 | |||
309 | // copy the patch image down and to the right where there are | ||
310 | // holes to eliminate the black halo from bilinear filtering | ||
311 | for (x=0; x<patch->width; x++) { | ||
312 | //oldColumn = (const column_t *)((const byte *)oldPatch + oldPatch->columnofs[x]); | ||
313 | |||
314 | column = R_GetPatchColumnClamped(patch, x); | ||
315 | prevColumn = R_GetPatchColumnClamped(patch, x-1); | ||
316 | |||
317 | if (column->pixels[0] == 0xff) { | ||
318 | // force the first pixel (which is a hole), to use | ||
319 | // the color from the next solid spot in the column | ||
320 | for (y=0; y<patch->height; y++) { | ||
321 | if (column->pixels[y] != 0xff) { | ||
322 | column->pixels[0] = column->pixels[y]; | ||
323 | break; | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | |||
328 | // copy from above or to the left | ||
329 | for (y=1; y<patch->height; y++) { | ||
330 | //if (getIsSolidAtSpot(oldColumn, y)) continue; | ||
331 | if (column->pixels[y] != 0xff) continue; | ||
332 | |||
333 | // this pixel is a hole | ||
334 | |||
335 | if (x && prevColumn->pixels[y-1] != 0xff) { | ||
336 | // copy the color from the left | ||
337 | column->pixels[y] = prevColumn->pixels[y]; | ||
338 | } | ||
339 | else { | ||
340 | // copy the color from above | ||
341 | column->pixels[y] = column->pixels[y-1]; | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | |||
346 | // verify that the patch truly is non-rectangular since | ||
347 | // this determines tiling later on | ||
348 | } | ||
349 | |||
350 | W_UnlockLumpNum(patchNum); | ||
351 | free(numPostsInColumn); | ||
352 | } | ||
353 | |||
354 | typedef struct { | ||
355 | unsigned short patches; | ||
356 | unsigned short posts; | ||
357 | unsigned short posts_used; | ||
358 | } count_t; | ||
359 | |||
360 | static void switchPosts(rpost_t *post1, rpost_t *post2) { | ||
361 | rpost_t dummy; | ||
362 | |||
363 | dummy.topdelta = post1->topdelta; | ||
364 | dummy.length = post1->length; | ||
365 | dummy.slope = post1->slope; | ||
366 | post1->topdelta = post2->topdelta; | ||
367 | post1->length = post2->length; | ||
368 | post1->slope = post2->slope; | ||
369 | post2->topdelta = dummy.topdelta; | ||
370 | post2->length = dummy.length; | ||
371 | post2->slope = dummy.slope; | ||
372 | } | ||
373 | |||
374 | static void removePostFromColumn(rcolumn_t *column, int post) { | ||
375 | int i; | ||
376 | #ifdef RANGECHECK | ||
377 | if (post >= column->numPosts) | ||
378 | I_Error("removePostFromColumn: invalid post index"); | ||
379 | #endif | ||
380 | if (post < column->numPosts) | ||
381 | for (i=post; i<(column->numPosts-1); i++) { | ||
382 | rpost_t *post1 = &column->posts[i]; | ||
383 | rpost_t *post2 = &column->posts[i+1]; | ||
384 | post1->topdelta = post2->topdelta; | ||
385 | post1->length = post2->length; | ||
386 | post1->slope = post2->slope; | ||
387 | } | ||
388 | column->numPosts--; | ||
389 | } | ||
390 | |||
391 | //--------------------------------------------------------------------------- | ||
392 | static void createTextureCompositePatch(int id) { | ||
393 | rpatch_t *composite_patch; | ||
394 | texture_t *texture; | ||
395 | texpatch_t *texpatch; | ||
396 | int patchNum; | ||
397 | const patch_t *oldPatch; | ||
398 | const column_t *oldColumn, *oldPrevColumn, *oldNextColumn; | ||
399 | int i, x, y; | ||
400 | int oy, count; | ||
401 | int pixelDataSize; | ||
402 | int columnsDataSize; | ||
403 | int postsDataSize; | ||
404 | int dataSize; | ||
405 | int numPostsTotal; | ||
406 | const unsigned char *oldColumnPixelData; | ||
407 | int numPostsUsedSoFar; | ||
408 | int edgeSlope; | ||
409 | count_t *countsInColumn; | ||
410 | |||
411 | #ifdef RANGECHECK | ||
412 | if (id >= numtextures) | ||
413 | I_Error("createTextureCompositePatch: %i >= numtextures", id); | ||
414 | #endif | ||
415 | |||
416 | composite_patch = &texture_composites[id]; | ||
417 | |||
418 | texture = textures[id]; | ||
419 | |||
420 | composite_patch->width = texture->width; | ||
421 | composite_patch->height = texture->height; | ||
422 | composite_patch->widthmask = texture->widthmask; | ||
423 | composite_patch->leftoffset = 0; | ||
424 | composite_patch->topoffset = 0; | ||
425 | composite_patch->isNotTileable = 0; | ||
426 | |||
427 | // work out how much memory we need to allocate for this patch's data | ||
428 | pixelDataSize = (composite_patch->width * composite_patch->height + 4) & ~3; | ||
429 | columnsDataSize = sizeof(rcolumn_t) * composite_patch->width; | ||
430 | |||
431 | // count the number of posts in each column | ||
432 | countsInColumn = (count_t *)calloc(sizeof(count_t), composite_patch->width); | ||
433 | numPostsTotal = 0; | ||
434 | |||
435 | for (i=0; i<texture->patchcount; i++) { | ||
436 | texpatch = &texture->patches[i]; | ||
437 | patchNum = texpatch->patch; | ||
438 | oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); | ||
439 | |||
440 | for (x=0; x<SHORT(oldPatch->width); x++) { | ||
441 | int tx = texpatch->originx + x; | ||
442 | |||
443 | if (tx < 0) | ||
444 | continue; | ||
445 | if (tx >= composite_patch->width) | ||
446 | break; | ||
447 | |||
448 | countsInColumn[tx].patches++; | ||
449 | |||
450 | oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); | ||
451 | while (oldColumn->topdelta != 0xff) { | ||
452 | countsInColumn[tx].posts++; | ||
453 | numPostsTotal++; | ||
454 | oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); | ||
455 | } | ||
456 | } | ||
457 | |||
458 | W_UnlockLumpNum(patchNum); | ||
459 | } | ||
460 | |||
461 | postsDataSize = numPostsTotal * sizeof(rpost_t); | ||
462 | |||
463 | // allocate our data chunk | ||
464 | dataSize = pixelDataSize + columnsDataSize + postsDataSize; | ||
465 | composite_patch->data = (unsigned char*)Z_Malloc(dataSize, PU_STATIC, (void **)&composite_patch->data); | ||
466 | memset(composite_patch->data, 0, dataSize); | ||
467 | |||
468 | // set out pixel, column, and post pointers into our data array | ||
469 | composite_patch->pixels = composite_patch->data; | ||
470 | composite_patch->columns = (rcolumn_t*)((unsigned char*)composite_patch->pixels + pixelDataSize); | ||
471 | composite_patch->posts = (rpost_t*)((unsigned char*)composite_patch->columns + columnsDataSize); | ||
472 | |||
473 | // sanity check that we've got all the memory allocated we need | ||
474 | assert((((byte*)composite_patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)composite_patch->data) == dataSize); | ||
475 | |||
476 | memset(composite_patch->pixels, 0xff, (composite_patch->width*composite_patch->height)); | ||
477 | |||
478 | numPostsUsedSoFar = 0; | ||
479 | |||
480 | for (x=0; x<texture->width; x++) { | ||
481 | // setup the column's data | ||
482 | composite_patch->columns[x].pixels = composite_patch->pixels + (x*composite_patch->height); | ||
483 | composite_patch->columns[x].numPosts = countsInColumn[x].posts; | ||
484 | composite_patch->columns[x].posts = composite_patch->posts + numPostsUsedSoFar; | ||
485 | numPostsUsedSoFar += countsInColumn[x].posts; | ||
486 | } | ||
487 | |||
488 | // fill in the pixels, posts, and columns | ||
489 | for (i=0; i<texture->patchcount; i++) { | ||
490 | texpatch = &texture->patches[i]; | ||
491 | patchNum = texpatch->patch; | ||
492 | oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); | ||
493 | |||
494 | for (x=0; x<SHORT(oldPatch->width); x++) { | ||
495 | int tx = texpatch->originx + x; | ||
496 | |||
497 | if (tx < 0) | ||
498 | continue; | ||
499 | if (tx >= composite_patch->width) | ||
500 | break; | ||
501 | |||
502 | oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); | ||
503 | |||
504 | { | ||
505 | // tiling | ||
506 | int prevColumnIndex = x-1; | ||
507 | int nextColumnIndex = x+1; | ||
508 | while (prevColumnIndex < 0) prevColumnIndex += SHORT(oldPatch->width); | ||
509 | while (nextColumnIndex >= SHORT(oldPatch->width)) nextColumnIndex -= SHORT(oldPatch->width); | ||
510 | oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[prevColumnIndex])); | ||
511 | oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[nextColumnIndex])); | ||
512 | } | ||
513 | |||
514 | while (oldColumn->topdelta != 0xff) { | ||
515 | rpost_t *post = &composite_patch->columns[tx].posts[countsInColumn[tx].posts_used]; | ||
516 | oldColumnPixelData = (const byte *)oldColumn + 3; | ||
517 | oy = texpatch->originy; | ||
518 | count = oldColumn->length; | ||
519 | // the original renderer had several bugs which we reproduce here | ||
520 | if (countsInColumn[tx].patches > 1) { | ||
521 | // when there are multiple patches, then we need to handle the | ||
522 | // column differently | ||
523 | if (i == 0) { | ||
524 | // draw first patch at original position, it will be partly | ||
525 | // overdrawn below | ||
526 | for (y=0; y<count; y++) { | ||
527 | int ty = oy + oldColumn->topdelta + y; | ||
528 | if (ty < 0) | ||
529 | continue; | ||
530 | if (ty >= composite_patch->height) | ||
531 | break; | ||
532 | composite_patch->pixels[tx * composite_patch->height + ty] = oldColumnPixelData[y]; | ||
533 | } | ||
534 | } | ||
535 | // do the buggy clipping | ||
536 | if ((oy + oldColumn->topdelta) < 0) { | ||
537 | count += oy; | ||
538 | oy = 0; | ||
539 | } | ||
540 | } else { | ||
541 | // with a single patch only negative y origins are wrong | ||
542 | oy = 0; | ||
543 | } | ||
544 | // set up the post's data | ||
545 | post->topdelta = oldColumn->topdelta + oy; | ||
546 | post->length = count; | ||
547 | if ((post->topdelta + post->length) > composite_patch->height) { | ||
548 | if (post->topdelta > composite_patch->height) | ||
549 | post->length = 0; | ||
550 | else | ||
551 | post->length = composite_patch->height - post->topdelta; | ||
552 | } | ||
553 | if (post->topdelta < 0) { | ||
554 | if ((post->topdelta + post->length) <= 0) | ||
555 | post->length = 0; | ||
556 | else | ||
557 | post->length -= post->topdelta; | ||
558 | post->topdelta = 0; | ||
559 | } | ||
560 | post->slope = 0; | ||
561 | |||
562 | edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta); | ||
563 | if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_TOP_UP; | ||
564 | else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_TOP_DOWN; | ||
565 | |||
566 | edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta+count); | ||
567 | if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_BOT_UP; | ||
568 | else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_BOT_DOWN; | ||
569 | |||
570 | // fill in the post's pixels | ||
571 | for (y=0; y<count; y++) { | ||
572 | int ty = oy + oldColumn->topdelta + y; | ||
573 | if (ty < 0) | ||
574 | continue; | ||
575 | if (ty >= composite_patch->height) | ||
576 | break; | ||
577 | composite_patch->pixels[tx * composite_patch->height + ty] = oldColumnPixelData[y]; | ||
578 | } | ||
579 | |||
580 | oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); | ||
581 | countsInColumn[tx].posts_used++; | ||
582 | assert(countsInColumn[tx].posts_used <= countsInColumn[tx].posts); | ||
583 | } | ||
584 | } | ||
585 | |||
586 | W_UnlockLumpNum(patchNum); | ||
587 | } | ||
588 | |||
589 | for (x=0; x<texture->width; x++) { | ||
590 | rcolumn_t *column; | ||
591 | |||
592 | if (countsInColumn[x].patches <= 1) | ||
593 | continue; | ||
594 | |||
595 | // cleanup posts on multipatch columns | ||
596 | column = &composite_patch->columns[x]; | ||
597 | |||
598 | i = 0; | ||
599 | while (i<(column->numPosts-1)) { | ||
600 | rpost_t *post1 = &column->posts[i]; | ||
601 | rpost_t *post2 = &column->posts[i+1]; | ||
602 | int length; | ||
603 | |||
604 | if ((post2->topdelta - post1->topdelta) < 0) | ||
605 | switchPosts(post1, post2); | ||
606 | |||
607 | if ((post1->topdelta + post1->length) >= post2->topdelta) { | ||
608 | length = (post1->length + post2->length) - ((post1->topdelta + post1->length) - post2->topdelta); | ||
609 | if (post1->length < length) { | ||
610 | post1->slope = post2->slope; | ||
611 | post1->length = length; | ||
612 | } | ||
613 | removePostFromColumn(column, i+1); | ||
614 | i = 0; | ||
615 | continue; | ||
616 | } | ||
617 | i++; | ||
618 | } | ||
619 | } | ||
620 | |||
621 | if (1 || composite_patch->isNotTileable) { | ||
622 | const rcolumn_t *column, *prevColumn; | ||
623 | |||
624 | // copy the patch image down and to the right where there are | ||
625 | // holes to eliminate the black halo from bilinear filtering | ||
626 | for (x=0; x<composite_patch->width; x++) { | ||
627 | //oldColumn = (const column_t *)((const byte *)oldPatch + oldPatch->columnofs[x]); | ||
628 | |||
629 | column = R_GetPatchColumnClamped(composite_patch, x); | ||
630 | prevColumn = R_GetPatchColumnClamped(composite_patch, x-1); | ||
631 | |||
632 | if (column->pixels[0] == 0xff) { | ||
633 | // force the first pixel (which is a hole), to use | ||
634 | // the color from the next solid spot in the column | ||
635 | for (y=0; y<composite_patch->height; y++) { | ||
636 | if (column->pixels[y] != 0xff) { | ||
637 | column->pixels[0] = column->pixels[y]; | ||
638 | break; | ||
639 | } | ||
640 | } | ||
641 | } | ||
642 | |||
643 | // copy from above or to the left | ||
644 | for (y=1; y<composite_patch->height; y++) { | ||
645 | //if (getIsSolidAtSpot(oldColumn, y)) continue; | ||
646 | if (column->pixels[y] != 0xff) continue; | ||
647 | |||
648 | // this pixel is a hole | ||
649 | |||
650 | if (x && prevColumn->pixels[y-1] != 0xff) { | ||
651 | // copy the color from the left | ||
652 | column->pixels[y] = prevColumn->pixels[y]; | ||
653 | } | ||
654 | else { | ||
655 | // copy the color from above | ||
656 | column->pixels[y] = column->pixels[y-1]; | ||
657 | } | ||
658 | } | ||
659 | } | ||
660 | |||
661 | // verify that the patch truly is non-rectangular since | ||
662 | // this determines tiling later on | ||
663 | } | ||
664 | |||
665 | free(countsInColumn); | ||
666 | } | ||
667 | |||
668 | //--------------------------------------------------------------------------- | ||
669 | const rpatch_t *R_CachePatchNum(int id) { | ||
670 | const int locks = 1; | ||
671 | |||
672 | if (!patches) | ||
673 | I_Error("R_CachePatchNum: Patches not initialized"); | ||
674 | |||
675 | #ifdef RANGECHECK | ||
676 | if (id >= numlumps) | ||
677 | I_Error("createPatch: %i >= numlumps", id); | ||
678 | #endif | ||
679 | |||
680 | if (!patches[id].data) | ||
681 | createPatch(id); | ||
682 | |||
683 | /* cph - if wasn't locked but now is, tell z_zone to hold it */ | ||
684 | if (!patches[id].locks && locks) { | ||
685 | Z_ChangeTag(patches[id].data,PU_STATIC); | ||
686 | #ifdef TIMEDIAG | ||
687 | patches[id].locktic = gametic; | ||
688 | #endif | ||
689 | } | ||
690 | patches[id].locks += locks; | ||
691 | |||
692 | #ifdef SIMPLECHECKS | ||
693 | if (!((patches[id].locks+1) & 0xf)) | ||
694 | lprintf(LO_DEBUG, "R_CachePatchNum: High lock on %8s (%d)\n", | ||
695 | lumpinfo[id].name, patches[id].locks); | ||
696 | #endif | ||
697 | |||
698 | return &patches[id]; | ||
699 | } | ||
700 | |||
701 | void R_UnlockPatchNum(int id) | ||
702 | { | ||
703 | const int unlocks = 1; | ||
704 | #ifdef SIMPLECHECKS | ||
705 | if ((signed short)patches[id].locks < unlocks) | ||
706 | lprintf(LO_DEBUG, "R_UnlockPatchNum: Excess unlocks on %8s (%d-%d)\n", | ||
707 | lumpinfo[id].name, patches[id].locks, unlocks); | ||
708 | #endif | ||
709 | patches[id].locks -= unlocks; | ||
710 | /* cph - Note: must only tell z_zone to make purgeable if currently locked, | ||
711 | * else it might already have been purged | ||
712 | */ | ||
713 | if (unlocks && !patches[id].locks) | ||
714 | Z_ChangeTag(patches[id].data, PU_CACHE); | ||
715 | } | ||
716 | |||
717 | //--------------------------------------------------------------------------- | ||
718 | const rpatch_t *R_CacheTextureCompositePatchNum(int id) { | ||
719 | const int locks = 1; | ||
720 | |||
721 | if (!texture_composites) | ||
722 | I_Error("R_CacheTextureCompositePatchNum: Composite patches not initialized"); | ||
723 | |||
724 | #ifdef RANGECHECK | ||
725 | if (id >= numtextures) | ||
726 | I_Error("createTextureCompositePatch: %i >= numtextures", id); | ||
727 | #endif | ||
728 | |||
729 | if (!texture_composites[id].data) | ||
730 | createTextureCompositePatch(id); | ||
731 | |||
732 | /* cph - if wasn't locked but now is, tell z_zone to hold it */ | ||
733 | if (!texture_composites[id].locks && locks) { | ||
734 | Z_ChangeTag(texture_composites[id].data,PU_STATIC); | ||
735 | #ifdef TIMEDIAG | ||
736 | texture_composites[id].locktic = gametic; | ||
737 | #endif | ||
738 | } | ||
739 | texture_composites[id].locks += locks; | ||
740 | |||
741 | #ifdef SIMPLECHECKS | ||
742 | if (!((texture_composites[id].locks+1) & 0xf)) | ||
743 | lprintf(LO_DEBUG, "R_CacheTextureCompositePatchNum: High lock on %8s (%d)\n", | ||
744 | textures[id]->name, texture_composites[id].locks); | ||
745 | #endif | ||
746 | |||
747 | return &texture_composites[id]; | ||
748 | |||
749 | } | ||
750 | |||
751 | void R_UnlockTextureCompositePatchNum(int id) | ||
752 | { | ||
753 | const int unlocks = 1; | ||
754 | #ifdef SIMPLECHECKS | ||
755 | if ((signed short)texture_composites[id].locks < unlocks) | ||
756 | lprintf(LO_DEBUG, "R_UnlockTextureCompositePatchNum: Excess unlocks on %8s (%d-%d)\n", | ||
757 | textures[id]->name, texture_composites[id].locks, unlocks); | ||
758 | #endif | ||
759 | texture_composites[id].locks -= unlocks; | ||
760 | /* cph - Note: must only tell z_zone to make purgeable if currently locked, | ||
761 | * else it might already have been purged | ||
762 | */ | ||
763 | if (unlocks && !texture_composites[id].locks) | ||
764 | Z_ChangeTag(texture_composites[id].data, PU_CACHE); | ||
765 | } | ||
766 | |||
767 | //--------------------------------------------------------------------------- | ||
768 | const rcolumn_t *R_GetPatchColumnWrapped(const rpatch_t *patch, int columnIndex) { | ||
769 | while (columnIndex < 0) columnIndex += patch->width; | ||
770 | columnIndex %= patch->width; | ||
771 | return &patch->columns[columnIndex]; | ||
772 | } | ||
773 | |||
774 | //--------------------------------------------------------------------------- | ||
775 | const rcolumn_t *R_GetPatchColumnClamped(const rpatch_t *patch, int columnIndex) { | ||
776 | if (columnIndex < 0) columnIndex = 0; | ||
777 | if (columnIndex >= patch->width) columnIndex = patch->width-1; | ||
778 | return &patch->columns[columnIndex]; | ||
779 | } | ||
780 | |||
781 | //--------------------------------------------------------------------------- | ||
782 | const rcolumn_t *R_GetPatchColumn(const rpatch_t *patch, int columnIndex) { | ||
783 | if (patch->isNotTileable) return R_GetPatchColumnClamped(patch, columnIndex); | ||
784 | else return R_GetPatchColumnWrapped(patch, columnIndex); | ||
785 | } | ||
786 | |||