]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - model_brush.c
fix for drawing explosions and sky twice as much as necessary (!), no longer does...
[xonotic/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23
24 // note: model_shared.c sets up r_notexture, and r_surf_notexture
25
26 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
27
28 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
29 cvar_t halflifebsp = {0, "halflifebsp", "0"};
30 cvar_t r_novis = {0, "r_novis", "0"};
31 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
32 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
33 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
34 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
35
36 #define NUM_DETAILTEXTURES 1
37 static rtexture_t *detailtextures[NUM_DETAILTEXTURES];
38 static rtexturepool_t *detailtexturepool;
39
40 /*
41 ===============
42 Mod_BrushInit
43 ===============
44 */
45 void Mod_BrushInit (void)
46 {
47 //      Cvar_RegisterVariable(&r_subdivide_size);
48         Cvar_RegisterVariable(&halflifebsp);
49         Cvar_RegisterVariable(&r_novis);
50         Cvar_RegisterVariable(&r_miplightmaps);
51         Cvar_RegisterVariable(&r_lightmaprgba);
52         Cvar_RegisterVariable(&r_nosurftextures);
53         Cvar_RegisterVariable(&r_sortsurfaces);
54         memset(mod_novis, 0xff, sizeof(mod_novis));
55 }
56
57 void Mod_BrushStartup (void)
58 {
59         int i, x, y, light;
60         float vc[3], vx[3], vy[3], vn[3], lightdir[3];
61 #define DETAILRESOLUTION 256
62         qbyte data[DETAILRESOLUTION][DETAILRESOLUTION][4], noise[DETAILRESOLUTION][DETAILRESOLUTION];
63         detailtexturepool = R_AllocTexturePool();
64         lightdir[0] = 0.5;
65         lightdir[1] = 1;
66         lightdir[2] = -0.25;
67         VectorNormalize(lightdir);
68         for (i = 0;i < NUM_DETAILTEXTURES;i++)
69         {
70                 fractalnoise(&noise[0][0], DETAILRESOLUTION, DETAILRESOLUTION >> 4);
71                 for (y = 0;y < DETAILRESOLUTION;y++)
72                 {
73                         for (x = 0;x < DETAILRESOLUTION;x++)
74                         {
75                                 vc[0] = x;
76                                 vc[1] = y;
77                                 vc[2] = noise[y][x] * (1.0f / 32.0f);
78                                 vx[0] = x + 1;
79                                 vx[1] = y;
80                                 vx[2] = noise[y][(x + 1) % DETAILRESOLUTION] * (1.0f / 32.0f);
81                                 vy[0] = x;
82                                 vy[1] = y + 1;
83                                 vy[2] = noise[(y + 1) % DETAILRESOLUTION][x] * (1.0f / 32.0f);
84                                 VectorSubtract(vx, vc, vx);
85                                 VectorSubtract(vy, vc, vy);
86                                 CrossProduct(vx, vy, vn);
87                                 VectorNormalize(vn);
88                                 light = 128 - DotProduct(vn, lightdir) * 128;
89                                 light = bound(0, light, 255);
90                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = light;
91                                 data[y][x][3] = 255;
92                         }
93                 }
94                 detailtextures[i] = R_LoadTexture2D(detailtexturepool, va("detailtexture%i", i), DETAILRESOLUTION, DETAILRESOLUTION, &data[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_PRECACHE, NULL);
95         }
96 }
97
98 void Mod_BrushShutdown (void)
99 {
100         int i;
101         for (i = 0;i < NUM_DETAILTEXTURES;i++)
102                 R_FreeTexture(detailtextures[i]);
103         R_FreeTexturePool(&detailtexturepool);
104 }
105
106 /*
107 ===============
108 Mod_PointInLeaf
109 ===============
110 */
111 mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model)
112 {
113         mnode_t *node;
114
115         if (model == NULL)
116                 return NULL;
117
118         Mod_CheckLoaded(model);
119
120         // LordHavoc: modified to start at first clip node,
121         // in other words: first node of the (sub)model
122         node = model->nodes + model->hulls[0].firstclipnode;
123         while (node->contents == 0)
124                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
125
126         return (mleaf_t *)node;
127 }
128
129 int Mod_PointContents (const vec3_t p, model_t *model)
130 {
131         mnode_t *node;
132
133         if (model == NULL)
134                 return CONTENTS_EMPTY;
135
136         Mod_CheckLoaded(model);
137
138         // LordHavoc: modified to start at first clip node,
139         // in other words: first node of the (sub)model
140         node = model->nodes + model->hulls[0].firstclipnode;
141         while (node->contents == 0)
142                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
143
144         return ((mleaf_t *)node)->contents;
145 }
146
147 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
148 {
149         if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
150         pos[0]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
151         pos[0]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
152         pos[0]-=1;
153         pos[1]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
154         pos[1]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
155         pos[1]-=1;
156         pos[2]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
157         pos[2]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
158         pos[2]-=1;
159 }
160
161
162 /*
163 ===================
164 Mod_DecompressVis
165 ===================
166 */
167 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
168 {
169         static qbyte decompressed[MAX_MAP_LEAFS/8];
170         int c;
171         qbyte *out;
172         int row;
173
174         row = (model->numleafs+7)>>3;
175         out = decompressed;
176
177         do
178         {
179                 if (*in)
180                 {
181                         *out++ = *in++;
182                         continue;
183                 }
184
185                 c = in[1];
186                 in += 2;
187                 while (c)
188                 {
189                         *out++ = 0;
190                         c--;
191                 }
192         } while (out - decompressed < row);
193
194         return decompressed;
195 }
196
197 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
198 {
199         if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
200                 return mod_novis;
201         return Mod_DecompressVis (leaf->compressed_vis, model);
202 }
203
204 /*
205 =================
206 Mod_LoadTextures
207 =================
208 */
209 static void Mod_LoadTextures (lump_t *l)
210 {
211         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
212         miptex_t *dmiptex;
213         texture_t *tx, *tx2, *anims[10], *altanims[10];
214         dmiptexlump_t *m;
215         qbyte *data, *mtdata;
216         char name[256];
217         qbyte *basepixels, *bumppixels, *nmappixels, *glosspixels, *glowpixels, *maskpixels;
218         int basepixels_width, basepixels_height, bumppixels_width, bumppixels_height;
219         int nmappixels_width, nmappixels_height, glosspixels_width, glosspixels_height;
220         int glowpixels_width, glowpixels_height, maskpixels_width, maskpixels_height;
221         rtexture_t *detailtexture;
222
223         loadmodel->textures = NULL;
224
225         if (!l->filelen)
226                 return;
227
228         m = (dmiptexlump_t *)(mod_base + l->fileofs);
229
230         m->nummiptex = LittleLong (m->nummiptex);
231
232         // add two slots for notexture walls and notexture liquids
233         loadmodel->numtextures = m->nummiptex + 2;
234         loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(texture_t));
235
236         // fill out all slots with notexture
237         for (i = 0, tx = loadmodel->textures;i < loadmodel->numtextures;i++, tx++)
238         {
239                 tx->width = 16;
240                 tx->height = 16;
241                 tx->texture = r_notexture;
242                 tx->shader = &Cshader_wall_lightmap;
243                 if (i == loadmodel->numtextures - 1)
244                 {
245                         tx->flags = SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
246                         tx->shader = &Cshader_water;
247                 }
248         }
249
250         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
251         dofs = m->dataofs;
252         // LordHavoc: mostly rewritten map texture loader
253         for (i = 0;i < m->nummiptex;i++)
254         {
255                 dofs[i] = LittleLong(dofs[i]);
256                 if (dofs[i] == -1 || r_nosurftextures.integer)
257                         continue;
258                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
259
260                 // make sure name is no more than 15 characters
261                 for (j = 0;dmiptex->name[j] && j < 15;j++)
262                         name[j] = dmiptex->name[j];
263                 name[j] = 0;
264
265                 mtwidth = LittleLong (dmiptex->width);
266                 mtheight = LittleLong (dmiptex->height);
267                 mtdata = NULL;
268                 j = LittleLong (dmiptex->offsets[0]);
269                 if (j)
270                 {
271                         // texture included
272                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
273                         {
274                                 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
275                                 continue;
276                         }
277                         mtdata = (qbyte *)dmiptex + j;
278                 }
279
280                 if ((mtwidth & 15) || (mtheight & 15))
281                         Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
282
283                 // LordHavoc: force all names to lowercase
284                 for (j = 0;name[j];j++)
285                         if (name[j] >= 'A' && name[j] <= 'Z')
286                                 name[j] += 'a' - 'A';
287
288                 tx = loadmodel->textures + i;
289                 strcpy(tx->name, name);
290                 tx->width = mtwidth;
291                 tx->height = mtheight;
292
293                 if (!tx->name[0])
294                 {
295                         sprintf(tx->name, "unnamed%i", i);
296                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
297                 }
298
299                 basepixels = NULL;basepixels_width = 0;basepixels_height = 0;
300                 bumppixels = NULL;bumppixels_width = 0;bumppixels_height = 0;
301                 nmappixels = NULL;nmappixels_width = 0;nmappixels_height = 0;
302                 glosspixels = NULL;glosspixels_width = 0;glosspixels_height = 0;
303                 glowpixels = NULL;glowpixels_width = 0;glowpixels_height = 0;
304                 maskpixels = NULL;maskpixels_width = 0;maskpixels_height = 0;
305                 detailtexture = NULL;
306
307                 // LordHavoc: HL sky textures are entirely different than quake
308                 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
309                 {
310                         if (loadmodel->isworldmodel)
311                         {
312                                 data = loadimagepixels(tx->name, false, 0, 0);
313                                 if (data)
314                                 {
315                                         if (image_width == 256 && image_height == 128)
316                                         {
317                                                 R_InitSky (data, 4);
318                                                 Mem_Free(data);
319                                         }
320                                         else
321                                         {
322                                                 Mem_Free(data);
323                                                 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
324                                                 if (mtdata != NULL)
325                                                         R_InitSky (mtdata, 1);
326                                         }
327                                 }
328                                 else if (mtdata != NULL)
329                                         R_InitSky (mtdata, 1);
330                         }
331                 }
332                 else
333                 {
334                         if ((basepixels = loadimagepixels(tx->name, false, 0, 0)) != NULL)
335                         {
336                                 basepixels_width = image_width;
337                                 basepixels_height = image_height;
338                         }
339                         // _luma is supported for tenebrae compatibility
340                         // (I think it's a very stupid name, but oh well)
341                         if ((glowpixels = loadimagepixels(va("%s_glow", tx->name), false, 0, 0)) != NULL
342                          || (glowpixels = loadimagepixels(va("%s_luma", tx->name), false, 0, 0)) != NULL)
343                         {
344                                 glowpixels_width = image_width;
345                                 glowpixels_height = image_height;
346                         }
347                         if ((bumppixels = loadimagepixels(va("%s_bump", tx->name), false, 0, 0)) != NULL)
348                         {
349                                 bumppixels_width = image_width;
350                                 bumppixels_height = image_height;
351                         }
352                         if ((glosspixels = loadimagepixels(va("%s_gloss", tx->name), false, 0, 0)) != NULL)
353                         {
354                                 glosspixels_width = image_width;
355                                 glosspixels_height = image_height;
356                         }
357                         if (!basepixels)
358                         {
359                                 if (loadmodel->ishlbsp)
360                                 {
361                                         // internal texture overrides wad
362                                         if (mtdata && (basepixels = W_ConvertWAD3Texture(dmiptex)) != NULL)
363                                         {
364                                                 basepixels_width = image_width;
365                                                 basepixels_height = image_height;
366                                         }
367                                         else if ((basepixels = W_GetTexture(tx->name)) != NULL)
368                                         {
369                                                 // get the size from the wad texture
370                                                 tx->width = basepixels_width = image_width;
371                                                 tx->height = basepixels_height = image_height;
372                                         }
373                                 }
374                                 else
375                                 {
376                                         if (mtdata) // texture included
377                                         {
378                                                 if (r_fullbrights.integer && tx->name[0] != '*')
379                                                 {
380                                                         basepixels_width = tx->width;
381                                                         basepixels_height = tx->height;
382                                                         basepixels = Mem_Alloc(loadmodel->mempool, basepixels_width * basepixels_height * 4);
383                                                         Image_Copy8bitRGBA(mtdata, basepixels, basepixels_width * basepixels_height, palette_nofullbrights);
384                                                         if (!glowpixels)
385                                                         {
386                                                                 for (j = 0;j < tx->width*tx->height;j++)
387                                                                         if (((qbyte *)&palette_onlyfullbrights[mtdata[j]])[3] > 0) // fullbright
388                                                                                 break;
389                                                                 if (j < tx->width * tx->height)
390                                                                 {
391                                                                         glowpixels_width = tx->width;
392                                                                         glowpixels_height = tx->height;
393                                                                         glowpixels = Mem_Alloc(loadmodel->mempool, glowpixels_width * glowpixels_height * 4);
394                                                                         Image_Copy8bitRGBA(mtdata, glowpixels, glowpixels_width * glowpixels_height, palette_onlyfullbrights);
395                                                                 }
396                                                         }
397                                                 }
398                                                 else
399                                                 {
400                                                         basepixels_width = tx->width;
401                                                         basepixels_height = tx->height;
402                                                         basepixels = Mem_Alloc(loadmodel->mempool, basepixels_width * basepixels_height * 4);
403                                                         Image_Copy8bitRGBA(mtdata, basepixels, tx->width * tx->height, palette_complete);
404                                                 }
405                                         }
406                                 }
407                         }
408                 }
409
410                 if (basepixels)
411                 {
412                         for (j = 3;j < basepixels_width * basepixels_height * 4;j += 4)
413                                 if (basepixels[j] < 255)
414                                         break;
415                         if (j < basepixels_width * basepixels_height * 4)
416                         {
417                                 maskpixels = Mem_Alloc(loadmodel->mempool, basepixels_width * basepixels_height * 4);
418                                 maskpixels_width = basepixels_width;
419                                 maskpixels_height = basepixels_height;
420                                 for (j = 0;j < basepixels_width * basepixels_height * 4;j += 4)
421                                 {
422                                         maskpixels[j+0] = 255;
423                                         maskpixels[j+1] = 255;
424                                         maskpixels[j+2] = 255;
425                                         maskpixels[j+3] = basepixels[j+3];
426                                 }
427                         }
428
429                         if (!bumppixels)
430                         {
431                                 bumppixels = Mem_Alloc(loadmodel->mempool, basepixels_width * basepixels_height * 4);
432                                 bumppixels_width = basepixels_width;
433                                 bumppixels_height = basepixels_height;
434                                 memcpy(bumppixels, basepixels, bumppixels_width * bumppixels_height * 4);
435                         }
436
437                         if (!nmappixels && bumppixels)
438                         {
439                                 nmappixels = Mem_Alloc(loadmodel->mempool, bumppixels_width * bumppixels_height * 4);
440                                 nmappixels_width = bumppixels_width;
441                                 nmappixels_height = bumppixels_height;
442                                 Image_HeightmapToNormalmap(bumppixels, nmappixels, nmappixels_width, nmappixels_height, false, 1);
443                         }
444                 }
445
446                 if (!detailtexture)
447                         detailtexture = detailtextures[i % NUM_DETAILTEXTURES];
448
449                 if (basepixels)
450                 {
451                         tx->texture = R_LoadTexture2D (loadmodel->texturepool, tx->name, basepixels_width, basepixels_height, basepixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
452                         if (nmappixels)
453                                 tx->nmaptexture = R_LoadTexture2D (loadmodel->texturepool, va("%s_nmap", tx->name), basepixels_width, basepixels_height, nmappixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
454                         if (glosspixels)
455                                 tx->glosstexture = R_LoadTexture2D (loadmodel->texturepool, va("%s_gloss", tx->name), glosspixels_width, glosspixels_height, glosspixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
456                         if (glowpixels)
457                                 tx->glowtexture = R_LoadTexture2D (loadmodel->texturepool, va("%s_glow", tx->name), glowpixels_width, glowpixels_height, glowpixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
458                         if (maskpixels)
459                                 tx->fogtexture = R_LoadTexture2D (loadmodel->texturepool, va("%s_mask", tx->name), maskpixels_width, maskpixels_height, maskpixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
460                         tx->detailtexture = detailtexture;
461                 }
462                 else
463                 {
464                         // no texture found
465                         tx->width = 16;
466                         tx->height = 16;
467                         tx->texture = r_notexture;
468                         tx->nmaptexture = NULL;
469                         tx->glosstexture = NULL;
470                         tx->glowtexture = NULL;
471                         tx->fogtexture = NULL;
472                         tx->detailtexture = NULL;
473                 }
474
475                 if (basepixels)
476                         Mem_Free(basepixels);
477                 if (bumppixels)
478                         Mem_Free(bumppixels);
479                 if (nmappixels)
480                         Mem_Free(nmappixels);
481                 if (glosspixels)
482                         Mem_Free(glosspixels);
483                 if (glowpixels)
484                         Mem_Free(glowpixels);
485                 if (maskpixels)
486                         Mem_Free(maskpixels);
487
488                 if (tx->name[0] == '*')
489                 {
490                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
491                         // LordHavoc: some turbulent textures should be fullbright and solid
492                         if (!strncmp(tx->name,"*lava",5)
493                          || !strncmp(tx->name,"*teleport",9)
494                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
495                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
496                         else
497                                 tx->flags |= SURF_WATERALPHA;
498                         tx->shader = &Cshader_water;
499                 }
500                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
501                 {
502                         tx->flags |= SURF_DRAWSKY;
503                         tx->shader = &Cshader_sky;
504                 }
505                 else
506                 {
507                         tx->flags |= SURF_LIGHTMAP;
508                         if (!tx->fogtexture)
509                                 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
510                         tx->shader = &Cshader_wall_lightmap;
511                 }
512
513                 // start out with no animation
514                 tx->currentframe[0] = tx;
515                 tx->currentframe[1] = tx;
516         }
517
518         // sequence the animations
519         for (i = 0;i < m->nummiptex;i++)
520         {
521                 tx = loadmodel->textures + i;
522                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
523                         continue;
524                 if (tx->anim_total[0] || tx->anim_total[1])
525                         continue;       // already sequenced
526
527                 // find the number of frames in the animation
528                 memset (anims, 0, sizeof(anims));
529                 memset (altanims, 0, sizeof(altanims));
530
531                 for (j = i;j < m->nummiptex;j++)
532                 {
533                         tx2 = loadmodel->textures + j;
534                         if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
535                                 continue;
536
537                         num = tx2->name[1];
538                         if (num >= '0' && num <= '9')
539                                 anims[num - '0'] = tx2;
540                         else if (num >= 'a' && num <= 'j')
541                                 altanims[num - 'a'] = tx2;
542                         else
543                                 Con_Printf ("Bad animating texture %s\n", tx->name);
544                 }
545
546                 max = altmax = 0;
547                 for (j = 0;j < 10;j++)
548                 {
549                         if (anims[j])
550                                 max = j + 1;
551                         if (altanims[j])
552                                 altmax = j + 1;
553                 }
554                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
555
556                 incomplete = false;
557                 for (j = 0;j < max;j++)
558                 {
559                         if (!anims[j])
560                         {
561                                 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
562                                 incomplete = true;
563                         }
564                 }
565                 for (j = 0;j < altmax;j++)
566                 {
567                         if (!altanims[j])
568                         {
569                                 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
570                                 incomplete = true;
571                         }
572                 }
573                 if (incomplete)
574                         continue;
575
576                 if (altmax < 1)
577                 {
578                         // if there is no alternate animation, duplicate the primary
579                         // animation into the alternate
580                         altmax = max;
581                         for (k = 0;k < 10;k++)
582                                 altanims[k] = anims[k];
583                 }
584
585                 // link together the primary animation
586                 for (j = 0;j < max;j++)
587                 {
588                         tx2 = anims[j];
589                         tx2->animated = true;
590                         tx2->anim_total[0] = max;
591                         tx2->anim_total[1] = altmax;
592                         for (k = 0;k < 10;k++)
593                         {
594                                 tx2->anim_frames[0][k] = anims[k];
595                                 tx2->anim_frames[1][k] = altanims[k];
596                         }
597                 }
598
599                 // if there really is an alternate anim...
600                 if (anims[0] != altanims[0])
601                 {
602                         // link together the alternate animation
603                         for (j = 0;j < altmax;j++)
604                         {
605                                 tx2 = altanims[j];
606                                 tx2->animated = true;
607                                 // the primary/alternate are reversed here
608                                 tx2->anim_total[0] = altmax;
609                                 tx2->anim_total[1] = max;
610                                 for (k = 0;k < 10;k++)
611                                 {
612                                         tx2->anim_frames[0][k] = altanims[k];
613                                         tx2->anim_frames[1][k] = anims[k];
614                                 }
615                         }
616                 }
617         }
618 }
619
620 /*
621 =================
622 Mod_LoadLighting
623 =================
624 */
625 static void Mod_LoadLighting (lump_t *l)
626 {
627         int i;
628         qbyte *in, *out, *data, d;
629         char litfilename[1024];
630         loadmodel->lightdata = NULL;
631         if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
632         {
633                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
634                 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
635         }
636         else // LordHavoc: bsp version 29 (normal white lighting)
637         {
638                 // LordHavoc: hope is not lost yet, check for a .lit file to load
639                 strcpy(litfilename, loadmodel->name);
640                 COM_StripExtension(litfilename, litfilename);
641                 strcat(litfilename, ".lit");
642                 data = (qbyte*) COM_LoadFile (litfilename, false);
643                 if (data)
644                 {
645                         if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
646                         {
647                                 i = LittleLong(((int *)data)[1]);
648                                 if (i == 1)
649                                 {
650                                         Con_DPrintf("%s loaded", litfilename);
651                                         loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
652                                         memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
653                                         Mem_Free(data);
654                                         return;
655                                 }
656                                 else
657                                 {
658                                         Con_Printf("Unknown .lit file version (%d)\n", i);
659                                         Mem_Free(data);
660                                 }
661                         }
662                         else
663                         {
664                                 if (loadsize == 8)
665                                         Con_Printf("Empty .lit file, ignoring\n");
666                                 else
667                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
668                                 Mem_Free(data);
669                         }
670                 }
671                 // LordHavoc: oh well, expand the white lighting data
672                 if (!l->filelen)
673                         return;
674                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
675                 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
676                 out = loadmodel->lightdata;
677                 memcpy (in, mod_base + l->fileofs, l->filelen);
678                 for (i = 0;i < l->filelen;i++)
679                 {
680                         d = *in++;
681                         *out++ = d;
682                         *out++ = d;
683                         *out++ = d;
684                 }
685         }
686 }
687
688 void Mod_LoadLightList(void)
689 {
690         int a, n, numlights;
691         char lightsfilename[1024], *s, *t, *lightsstring;
692         mlight_t *e;
693
694         strcpy(lightsfilename, loadmodel->name);
695         COM_StripExtension(lightsfilename, lightsfilename);
696         strcat(lightsfilename, ".lights");
697         s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
698         if (s)
699         {
700                 numlights = 0;
701                 while (*s)
702                 {
703                         while (*s && *s != '\n')
704                                 s++;
705                         if (!*s)
706                         {
707                                 Mem_Free(lightsstring);
708                                 Host_Error("lights file must end with a newline\n");
709                         }
710                         s++;
711                         numlights++;
712                 }
713                 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
714                 s = lightsstring;
715                 n = 0;
716                 while (*s && n < numlights)
717                 {
718                         t = s;
719                         while (*s && *s != '\n')
720                                 s++;
721                         if (!*s)
722                         {
723                                 Mem_Free(lightsstring);
724                                 Host_Error("misparsed lights file!\n");
725                         }
726                         e = loadmodel->lights + n;
727                         *s = 0;
728                         a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
729                         *s = '\n';
730                         if (a != 14)
731                         {
732                                 Mem_Free(lightsstring);
733                                 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
734                         }
735                         s++;
736                         n++;
737                 }
738                 if (*s)
739                 {
740                         Mem_Free(lightsstring);
741                         Host_Error("misparsed lights file!\n");
742                 }
743                 loadmodel->numlights = numlights;
744                 Mem_Free(lightsstring);
745         }
746 }
747
748 static int castshadowcount = 0;
749 void Mod_ProcessLightList(void)
750 {
751         int j, k, l, *mark, lnum;
752         mlight_t *e;
753         msurface_t *surf;
754         float dist;
755         mleaf_t *leaf;
756         qbyte *pvs;
757         vec3_t temp;
758         float *v, radius2;
759         for (lnum = 0, e = loadmodel->lights;lnum < loadmodel->numlights;lnum++, e++)
760         {
761                 e->cullradius2 = DotProduct(e->light, e->light) / (e->falloff * e->falloff * 8192.0f * 8192.0f * 2.0f * 2.0f);// + 4096.0f;
762                 if (e->cullradius2 > 4096.0f * 4096.0f)
763                         e->cullradius2 = 4096.0f * 4096.0f;
764                 e->cullradius = e->lightradius = sqrt(e->cullradius2);
765                 leaf = Mod_PointInLeaf(e->origin, loadmodel);
766                 if (leaf->compressed_vis)
767                         pvs = Mod_DecompressVis (leaf->compressed_vis, loadmodel);
768                 else
769                         pvs = mod_novis;
770                 for (j = 0;j < loadmodel->numsurfaces;j++)
771                         loadmodel->surfacevisframes[j] = -1;
772                 for (j = 0, leaf = loadmodel->leafs + 1;j < loadmodel->numleafs - 1;j++, leaf++)
773                 {
774                         if (pvs[j >> 3] & (1 << (j & 7)))
775                         {
776                                 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
777                                 {
778                                         surf = loadmodel->surfaces + *mark;
779                                         if (surf->number != *mark)
780                                                 Con_Printf("%d != %d\n", surf->number, *mark);
781                                         dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
782                                         if (surf->flags & SURF_PLANEBACK)
783                                                 dist = -dist;
784                                         if (dist > 0 && dist < e->cullradius)
785                                         {
786                                                 VectorSubtract(e->origin, surf->poly_center, temp);
787                                                 if (DotProduct(temp, temp) - surf->poly_radius2 < e->cullradius2)
788                                                         loadmodel->surfacevisframes[*mark] = -2;
789                                         }
790                                 }
791                         }
792                 }
793                 // build list of light receiving surfaces
794                 e->numsurfaces = 0;
795                 for (j = 0;j < loadmodel->numsurfaces;j++)
796                         if (loadmodel->surfacevisframes[j] == -2)
797                                 e->numsurfaces++;
798                 e->surfaces = NULL;
799                 if (e->numsurfaces > 0)
800                 {
801                         e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
802                         e->numsurfaces = 0;
803                         for (j = 0;j < loadmodel->numsurfaces;j++)
804                                 if (loadmodel->surfacevisframes[j] == -2)
805                                         e->surfaces[e->numsurfaces++] = loadmodel->surfaces + j;
806                 }
807                 // find bounding box and sphere of lit surfaces
808                 // (these will be used for creating a shape to clip the light)
809                 radius2 = 0;
810                 for (j = 0;j < e->numsurfaces;j++)
811                 {
812                         surf = e->surfaces[j];
813                         if (j == 0)
814                         {
815                                 VectorCopy(surf->poly_verts, e->mins);
816                                 VectorCopy(surf->poly_verts, e->maxs);
817                         }
818                         for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
819                         {
820                                 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
821                                 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
822                                 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
823                                 VectorSubtract(v, e->origin, temp);
824                                 dist = DotProduct(temp, temp);
825                                 if (radius2 < dist)
826                                         radius2 = dist;
827                         }
828                 }
829                 if (e->cullradius2 > radius2)
830                 {
831                         e->cullradius2 = radius2;
832                         e->cullradius = sqrt(e->cullradius2);
833                 }
834                 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
835                 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
836                 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
837                 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
838                 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
839                 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
840                 // clip shadow volumes against eachother to remove unnecessary
841                 // polygons (and sections of polygons)
842                 {
843                         //vec3_t polymins, polymaxs;
844                         int maxverts = 4;
845                         float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
846                         float f, *v0, *v1, projectdistance;
847
848                         e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool);
849 #if 0
850                         {
851                         vec3_t outermins, outermaxs, innermins, innermaxs;
852                         innermins[0] = e->mins[0] - 1;
853                         innermins[1] = e->mins[1] - 1;
854                         innermins[2] = e->mins[2] - 1;
855                         innermaxs[0] = e->maxs[0] + 1;
856                         innermaxs[1] = e->maxs[1] + 1;
857                         innermaxs[2] = e->maxs[2] + 1;
858                         outermins[0] = loadmodel->normalmins[0] - 1;
859                         outermins[1] = loadmodel->normalmins[1] - 1;
860                         outermins[2] = loadmodel->normalmins[2] - 1;
861                         outermaxs[0] = loadmodel->normalmaxs[0] + 1;
862                         outermaxs[1] = loadmodel->normalmaxs[1] + 1;
863                         outermaxs[2] = loadmodel->normalmaxs[2] + 1;
864                         // add bounding box around the whole shadow volume set,
865                         // facing inward to limit light area, with an outer bounding box
866                         // facing outward (this is needed by the shadow rendering method)
867                         // X major
868                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
869                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
870                         verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
871                         verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
872                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
873                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
874                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
875                         verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
876                         verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
877                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
878                         // X minor
879                         verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
880                         verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
881                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
882                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
883                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
884                         verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
885                         verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
886                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
887                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
888                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
889                         // Y major
890                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
891                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
892                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
893                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
894                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
895                         verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
896                         verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
897                         verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
898                         verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
899                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
900                         // Y minor
901                         verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
902                         verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
903                         verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
904                         verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
905                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
906                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
907                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
908                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
909                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
910                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
911                         // Z major
912                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
913                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
914                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
915                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
916                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
917                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
918                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
919                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
920                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
921                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
922                         // Z minor
923                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
924                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
925                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
926                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
927                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
928                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
929                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
930                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
931                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
932                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
933                         }
934 #endif
935                         castshadowcount++;
936                         for (j = 0;j < e->numsurfaces;j++)
937                         {
938                                 surf = e->surfaces[j];
939                                 if (surf->flags & SURF_SHADOWCAST)
940                                         surf->castshadow = castshadowcount;
941                         }
942                         for (j = 0;j < e->numsurfaces;j++)
943                         {
944                                 surf = e->surfaces[j];
945                                 if (surf->castshadow != castshadowcount)
946                                         continue;
947                                 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
948                                 if (surf->flags & SURF_PLANEBACK)
949                                         f = -f;
950                                 projectdistance = e->lightradius;
951                                 if (maxverts < surf->poly_numverts)
952                                 {
953                                         maxverts = surf->poly_numverts;
954                                         if (verts)
955                                                 Mem_Free(verts);
956                                         verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
957                                 }
958                                 // copy the original polygon, for the front cap of the volume
959                                 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
960                                         VectorCopy(v0, v1);
961                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
962                                 // project the original polygon, reversed, for the back cap of the volume
963                                 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
964                                 {
965                                         VectorSubtract(v0, e->origin, temp);
966                                         VectorNormalize(temp);
967                                         VectorMA(v0, projectdistance, temp, v1);
968                                 }
969                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
970                                 // project the shadow volume sides
971                                 for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
972                                 {
973                                         if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
974                                         {
975                                                 VectorCopy(v1, &verts[0]);
976                                                 VectorCopy(v0, &verts[3]);
977                                                 VectorCopy(v0, &verts[6]);
978                                                 VectorCopy(v1, &verts[9]);
979                                                 VectorSubtract(&verts[6], e->origin, temp);
980                                                 VectorNormalize(temp);
981                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
982                                                 VectorSubtract(&verts[9], e->origin, temp);
983                                                 VectorNormalize(temp);
984                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
985                                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
986                                         }
987                                 }
988                         }
989                         // build the triangle mesh
990                         e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
991                         {
992                                 shadowmesh_t *mesh;
993                                 l = 0;
994                                 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
995                                         l += mesh->numtriangles;
996                                 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
997                         }
998                 }
999         }
1000 }
1001
1002
1003 /*
1004 =================
1005 Mod_LoadVisibility
1006 =================
1007 */
1008 static void Mod_LoadVisibility (lump_t *l)
1009 {
1010         loadmodel->visdata = NULL;
1011         if (!l->filelen)
1012                 return;
1013         loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1014         memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
1015 }
1016
1017 // used only for HalfLife maps
1018 void Mod_ParseWadsFromEntityLump(const char *data)
1019 {
1020         char key[128], value[4096];
1021         char wadname[128];
1022         int i, j, k;
1023         if (!data)
1024                 return;
1025         if (!COM_ParseToken(&data))
1026                 return; // error
1027         if (com_token[0] != '{')
1028                 return; // error
1029         while (1)
1030         {
1031                 if (!COM_ParseToken(&data))
1032                         return; // error
1033                 if (com_token[0] == '}')
1034                         break; // end of worldspawn
1035                 if (com_token[0] == '_')
1036                         strcpy(key, com_token + 1);
1037                 else
1038                         strcpy(key, com_token);
1039                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1040                         key[strlen(key)-1] = 0;
1041                 if (!COM_ParseToken(&data))
1042                         return; // error
1043                 strcpy(value, com_token);
1044                 if (!strcmp("wad", key)) // for HalfLife maps
1045                 {
1046                         if (loadmodel->ishlbsp)
1047                         {
1048                                 j = 0;
1049                                 for (i = 0;i < 4096;i++)
1050                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1051                                                 break;
1052                                 if (value[i])
1053                                 {
1054                                         for (;i < 4096;i++)
1055                                         {
1056                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1057                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1058                                                         j = i+1;
1059                                                 else if (value[i] == ';' || value[i] == 0)
1060                                                 {
1061                                                         k = value[i];
1062                                                         value[i] = 0;
1063                                                         strcpy(wadname, "textures/");
1064                                                         strcat(wadname, &value[j]);
1065                                                         W_LoadTextureWadFile (wadname, false);
1066                                                         j = i+1;
1067                                                         if (!k)
1068                                                                 break;
1069                                                 }
1070                                         }
1071                                 }
1072                         }
1073                 }
1074         }
1075 }
1076
1077 /*
1078 =================
1079 Mod_LoadEntities
1080 =================
1081 */
1082 static void Mod_LoadEntities (lump_t *l)
1083 {
1084         loadmodel->entities = NULL;
1085         if (!l->filelen)
1086                 return;
1087         loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1088         memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
1089         if (loadmodel->ishlbsp)
1090                 Mod_ParseWadsFromEntityLump(loadmodel->entities);
1091 }
1092
1093
1094 /*
1095 =================
1096 Mod_LoadVertexes
1097 =================
1098 */
1099 static void Mod_LoadVertexes (lump_t *l)
1100 {
1101         dvertex_t       *in;
1102         mvertex_t       *out;
1103         int                     i, count;
1104
1105         in = (void *)(mod_base + l->fileofs);
1106         if (l->filelen % sizeof(*in))
1107                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1108         count = l->filelen / sizeof(*in);
1109         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1110
1111         loadmodel->vertexes = out;
1112         loadmodel->numvertexes = count;
1113
1114         for ( i=0 ; i<count ; i++, in++, out++)
1115         {
1116                 out->position[0] = LittleFloat (in->point[0]);
1117                 out->position[1] = LittleFloat (in->point[1]);
1118                 out->position[2] = LittleFloat (in->point[2]);
1119         }
1120 }
1121
1122 /*
1123 =================
1124 Mod_LoadSubmodels
1125 =================
1126 */
1127 static void Mod_LoadSubmodels (lump_t *l)
1128 {
1129         dmodel_t        *in;
1130         dmodel_t        *out;
1131         int                     i, j, count;
1132
1133         in = (void *)(mod_base + l->fileofs);
1134         if (l->filelen % sizeof(*in))
1135                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1136         count = l->filelen / sizeof(*in);
1137         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1138
1139         loadmodel->submodels = out;
1140         loadmodel->numsubmodels = count;
1141
1142         for ( i=0 ; i<count ; i++, in++, out++)
1143         {
1144                 for (j=0 ; j<3 ; j++)
1145                 {
1146                         // spread the mins / maxs by a pixel
1147                         out->mins[j] = LittleFloat (in->mins[j]) - 1;
1148                         out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
1149                         out->origin[j] = LittleFloat (in->origin[j]);
1150                 }
1151                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1152                         out->headnode[j] = LittleLong (in->headnode[j]);
1153                 out->visleafs = LittleLong (in->visleafs);
1154                 out->firstface = LittleLong (in->firstface);
1155                 out->numfaces = LittleLong (in->numfaces);
1156         }
1157 }
1158
1159 /*
1160 =================
1161 Mod_LoadEdges
1162 =================
1163 */
1164 static void Mod_LoadEdges (lump_t *l)
1165 {
1166         dedge_t *in;
1167         medge_t *out;
1168         int     i, count;
1169
1170         in = (void *)(mod_base + l->fileofs);
1171         if (l->filelen % sizeof(*in))
1172                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1173         count = l->filelen / sizeof(*in);
1174         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1175
1176         loadmodel->edges = out;
1177         loadmodel->numedges = count;
1178
1179         for ( i=0 ; i<count ; i++, in++, out++)
1180         {
1181                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1182                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1183         }
1184 }
1185
1186 /*
1187 =================
1188 Mod_LoadTexinfo
1189 =================
1190 */
1191 static void Mod_LoadTexinfo (lump_t *l)
1192 {
1193         texinfo_t *in;
1194         mtexinfo_t *out;
1195         int i, j, k, count, miptex;
1196
1197         in = (void *)(mod_base + l->fileofs);
1198         if (l->filelen % sizeof(*in))
1199                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1200         count = l->filelen / sizeof(*in);
1201         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1202
1203         loadmodel->texinfo = out;
1204         loadmodel->numtexinfo = count;
1205
1206         for (i = 0;i < count;i++, in++, out++)
1207         {
1208                 for (k = 0;k < 2;k++)
1209                         for (j = 0;j < 4;j++)
1210                                 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
1211
1212                 miptex = LittleLong (in->miptex);
1213                 out->flags = LittleLong (in->flags);
1214
1215                 out->texture = NULL;
1216                 if (loadmodel->textures)
1217                 {
1218                         if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
1219                                 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
1220                         else
1221                                 out->texture = loadmodel->textures + miptex;
1222                 }
1223                 if (out->flags & TEX_SPECIAL)
1224                 {
1225                         // if texture chosen is NULL or the shader needs a lightmap,
1226                         // force to notexture water shader
1227                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1228                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 1);
1229                 }
1230                 else
1231                 {
1232                         // if texture chosen is NULL, force to notexture
1233                         if (out->texture == NULL)
1234                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 2);
1235                 }
1236         }
1237 }
1238
1239 /*
1240 ================
1241 CalcSurfaceExtents
1242
1243 Fills in s->texturemins[] and s->extents[]
1244 ================
1245 */
1246 static void CalcSurfaceExtents (msurface_t *s)
1247 {
1248         float   mins[2], maxs[2], val;
1249         int             i,j, e;
1250         mvertex_t       *v;
1251         mtexinfo_t      *tex;
1252         int             bmins[2], bmaxs[2];
1253
1254         mins[0] = mins[1] = 999999999;
1255         maxs[0] = maxs[1] = -999999999;
1256
1257         tex = s->texinfo;
1258
1259         for (i=0 ; i<s->numedges ; i++)
1260         {
1261                 e = loadmodel->surfedges[s->firstedge+i];
1262                 if (e >= 0)
1263                         v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
1264                 else
1265                         v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
1266
1267                 for (j=0 ; j<2 ; j++)
1268                 {
1269                         val = v->position[0] * tex->vecs[j][0] +
1270                                 v->position[1] * tex->vecs[j][1] +
1271                                 v->position[2] * tex->vecs[j][2] +
1272                                 tex->vecs[j][3];
1273                         if (val < mins[j])
1274                                 mins[j] = val;
1275                         if (val > maxs[j])
1276                                 maxs[j] = val;
1277                 }
1278         }
1279
1280         for (i=0 ; i<2 ; i++)
1281         {
1282                 bmins[i] = floor(mins[i]/16);
1283                 bmaxs[i] = ceil(maxs[i]/16);
1284
1285                 s->texturemins[i] = bmins[i] * 16;
1286                 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
1287         }
1288 }
1289
1290
1291 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
1292 {
1293         int             i, j;
1294         float   *v;
1295
1296         mins[0] = mins[1] = mins[2] = 9999;
1297         maxs[0] = maxs[1] = maxs[2] = -9999;
1298         v = verts;
1299         for (i = 0;i < numverts;i++)
1300         {
1301                 for (j = 0;j < 3;j++, v++)
1302                 {
1303                         if (*v < mins[j])
1304                                 mins[j] = *v;
1305                         if (*v > maxs[j])
1306                                 maxs[j] = *v;
1307                 }
1308         }
1309 }
1310
1311 #if 0
1312 #define MAX_SUBDIVPOLYTRIANGLES 4096
1313 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
1314
1315 static int subdivpolyverts, subdivpolytriangles;
1316 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1317 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1318
1319 static int subdivpolylookupvert(vec3_t v)
1320 {
1321         int i;
1322         for (i = 0;i < subdivpolyverts;i++)
1323                 if (subdivpolyvert[i][0] == v[0]
1324                  && subdivpolyvert[i][1] == v[1]
1325                  && subdivpolyvert[i][2] == v[2])
1326                         return i;
1327         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1328                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1329         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1330         return subdivpolyverts++;
1331 }
1332
1333 static void SubdividePolygon (int numverts, float *verts)
1334 {
1335         int             i, i1, i2, i3, f, b, c, p;
1336         vec3_t  mins, maxs, front[256], back[256];
1337         float   m, *pv, *cv, dist[256], frac;
1338
1339         if (numverts > 250)
1340                 Host_Error ("SubdividePolygon: ran out of verts in buffer");
1341
1342         BoundPoly (numverts, verts, mins, maxs);
1343
1344         for (i = 0;i < 3;i++)
1345         {
1346                 m = (mins[i] + maxs[i]) * 0.5;
1347                 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
1348                 if (maxs[i] - m < 8)
1349                         continue;
1350                 if (m - mins[i] < 8)
1351                         continue;
1352
1353                 // cut it
1354                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1355                         dist[c] = cv[i] - m;
1356
1357                 f = b = 0;
1358                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1359                 {
1360                         if (dist[p] >= 0)
1361                         {
1362                                 VectorCopy (pv, front[f]);
1363                                 f++;
1364                         }
1365                         if (dist[p] <= 0)
1366                         {
1367                                 VectorCopy (pv, back[b]);
1368                                 b++;
1369                         }
1370                         if (dist[p] == 0 || dist[c] == 0)
1371                                 continue;
1372                         if ( (dist[p] > 0) != (dist[c] > 0) )
1373                         {
1374                                 // clip point
1375                                 frac = dist[p] / (dist[p] - dist[c]);
1376                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1377                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1378                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1379                                 f++;
1380                                 b++;
1381                         }
1382                 }
1383
1384                 SubdividePolygon (f, front[0]);
1385                 SubdividePolygon (b, back[0]);
1386                 return;
1387         }
1388
1389         i1 = subdivpolylookupvert(verts);
1390         i2 = subdivpolylookupvert(verts + 3);
1391         for (i = 2;i < numverts;i++)
1392         {
1393                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1394                 {
1395                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1396                         return;
1397                 }
1398
1399                 i3 = subdivpolylookupvert(verts + i * 3);
1400                 subdivpolyindex[subdivpolytriangles][0] = i1;
1401                 subdivpolyindex[subdivpolytriangles][1] = i2;
1402                 subdivpolyindex[subdivpolytriangles][2] = i3;
1403                 i2 = i3;
1404                 subdivpolytriangles++;
1405         }
1406 }
1407
1408 /*
1409 ================
1410 Mod_GenerateWarpMesh
1411
1412 Breaks a polygon up along axial 64 unit
1413 boundaries so that turbulent and sky warps
1414 can be done reasonably.
1415 ================
1416 */
1417 void Mod_GenerateWarpMesh (msurface_t *surf)
1418 {
1419         int i, j;
1420         surfvertex_t *v;
1421         surfmesh_t *mesh;
1422
1423         subdivpolytriangles = 0;
1424         subdivpolyverts = 0;
1425         SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1426         if (subdivpolytriangles < 1)
1427                 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1428
1429         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1430         mesh->numverts = subdivpolyverts;
1431         mesh->numtriangles = subdivpolytriangles;
1432         mesh->vertex = (surfvertex_t *)(mesh + 1);
1433         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1434         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1435
1436         for (i = 0;i < mesh->numtriangles;i++)
1437                 for (j = 0;j < 3;j++)
1438                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1439
1440         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1441         {
1442                 VectorCopy(subdivpolyvert[i], v->v);
1443                 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1444                 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1445         }
1446 }
1447 #endif
1448
1449 surfmesh_t *Mod_AllocSurfMesh(int numverts, int numtriangles)
1450 {
1451         surfmesh_t *mesh;
1452         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (4 + 4 + 4 + 4 + 4 + 4 + 4 + 1) * sizeof(float));
1453         mesh->numverts = numverts;
1454         mesh->numtriangles = numtriangles;
1455         mesh->verts = (float *)(mesh + 1);
1456         mesh->str = mesh->verts + mesh->numverts * 4;
1457         mesh->uvw = mesh->str + mesh->numverts * 4;
1458         mesh->abc = mesh->uvw + mesh->numverts * 4;
1459         mesh->svectors = (float *)(mesh->abc + mesh->numverts * 4);
1460         mesh->tvectors = mesh->svectors + mesh->numverts * 4;
1461         mesh->normals = mesh->tvectors + mesh->numverts * 4;
1462         mesh->lightmapoffsets = (int *)(mesh->normals + mesh->numverts * 4);
1463         mesh->index = mesh->lightmapoffsets + mesh->numverts;
1464         mesh->triangleneighbors = mesh->index + mesh->numtriangles * 3;
1465         return mesh;
1466 }
1467
1468 void Mod_GenerateWallMesh (msurface_t *surf, int vertexonly)
1469 {
1470         int i, iu, iv, *index, smax, tmax;
1471         float *in, s, t, u, v, ubase, vbase, uscale, vscale, normal[3];
1472         surfmesh_t *mesh;
1473
1474         smax = surf->extents[0] >> 4;
1475         tmax = surf->extents[1] >> 4;
1476
1477         if (vertexonly)
1478         {
1479                 surf->lightmaptexturestride = 0;
1480                 surf->lightmaptexture = NULL;
1481                 uscale = 0;
1482                 vscale = 0;
1483                 ubase = 0;
1484                 vbase = 0;
1485         }
1486         else
1487         {
1488                 surf->flags |= SURF_LIGHTMAP;
1489                 if (r_miplightmaps.integer)
1490                 {
1491                         surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1492                         surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE, NULL);
1493                 }
1494                 else
1495                 {
1496                         surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1497                         surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE, NULL);
1498                 }
1499                 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1500                 uscale = (uscale - ubase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1501                 vscale = (vscale - vbase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1502         }
1503
1504         surf->mesh = mesh = Mod_AllocSurfMesh(surf->poly_numverts, surf->poly_numverts - 2);
1505
1506         index = mesh->index;
1507         for (i = 0;i < mesh->numtriangles;i++)
1508         {
1509                 *index++ = 0;
1510                 *index++ = i + 1;
1511                 *index++ = i + 2;
1512         }
1513         Mod_BuildTriangleNeighbors(mesh->triangleneighbors, mesh->index, mesh->numtriangles);
1514
1515         VectorCopy(surf->plane->normal, normal);
1516         if (surf->flags & SURF_PLANEBACK)
1517                 VectorNegate(normal, normal);
1518         for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1519         {
1520                 s = DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1521                 t = DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1522                 u = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1523                 v = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1524                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1525                 iu = (int) u;
1526                 iv = (int) v;
1527                 iu = bound(0, iu, smax);
1528                 iv = bound(0, iv, tmax);
1529                 u = u * uscale + ubase;
1530                 v = v * vscale + vbase;
1531
1532                 mesh->verts[i * 4 + 0] = in[0];
1533                 mesh->verts[i * 4 + 1] = in[1];
1534                 mesh->verts[i * 4 + 2] = in[2];
1535                 mesh->str[i * 4 + 0] = s / surf->texinfo->texture->width;
1536                 mesh->str[i * 4 + 1] = t / surf->texinfo->texture->height;
1537                 mesh->uvw[i * 4 + 0] = u;
1538                 mesh->uvw[i * 4 + 1] = v;
1539                 mesh->abc[i * 4 + 0] = s * (1.0f / 16.0f);
1540                 mesh->abc[i * 4 + 1] = t * (1.0f / 16.0f);
1541                 mesh->lightmapoffsets[i] = ((iv * (smax+1) + iu) * 3);
1542         }
1543         Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->verts, mesh->str, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals);
1544 }
1545
1546 void Mod_GenerateVertexMesh (msurface_t *surf)
1547 {
1548         int i, *index;
1549         float *in, s, t, normal[3];
1550         surfmesh_t *mesh;
1551
1552         surf->lightmaptexturestride = 0;
1553         surf->lightmaptexture = NULL;
1554
1555         surf->mesh = mesh = Mod_AllocSurfMesh(surf->poly_numverts, surf->poly_numverts - 2);
1556
1557         index = mesh->index;
1558         for (i = 0;i < mesh->numtriangles;i++)
1559         {
1560                 *index++ = 0;
1561                 *index++ = i + 1;
1562                 *index++ = i + 2;
1563         }
1564         Mod_BuildTriangleNeighbors(mesh->triangleneighbors, mesh->index, mesh->numtriangles);
1565
1566         VectorCopy(surf->plane->normal, normal);
1567         if (surf->flags & SURF_PLANEBACK)
1568                 VectorNegate(normal, normal);
1569         for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1570         {
1571                 s = (DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
1572                 t = (DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
1573                 mesh->verts[i * 4 + 0] = in[0];
1574                 mesh->verts[i * 4 + 1] = in[1];
1575                 mesh->verts[i * 4 + 2] = in[2];
1576                 mesh->str[i * 4 + 0] = s / surf->texinfo->texture->width;
1577                 mesh->str[i * 4 + 1] = t / surf->texinfo->texture->height;
1578                 mesh->uvw[i * 4 + 0] = 0;
1579                 mesh->uvw[i * 4 + 1] = 0;
1580                 mesh->abc[i * 4 + 0] = s * (1.0f / 16.0f);
1581                 mesh->abc[i * 4 + 1] = t * (1.0f / 16.0f);
1582         }
1583         Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->verts, mesh->str, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals);
1584 }
1585
1586 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1587 {
1588         int i, lindex;
1589         float *vec, *vert, mins[3], maxs[3], temp[3], dist;
1590
1591         // convert edges back to a normal polygon
1592         surf->poly_numverts = surf->numedges;
1593         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1594         for (i = 0;i < surf->numedges;i++)
1595         {
1596                 lindex = loadmodel->surfedges[surf->firstedge + i];
1597                 if (lindex > 0)
1598                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1599                 else
1600                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1601                 VectorCopy (vec, vert);
1602                 vert += 3;
1603         }
1604         vert = surf->poly_verts;
1605         VectorCopy(vert, mins);
1606         VectorCopy(vert, maxs);
1607         vert += 3;
1608         for (i = 1;i < surf->poly_numverts;i++)
1609         {
1610                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1611                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1612                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1613                 vert += 3;
1614         }
1615         VectorCopy(mins, surf->poly_mins);
1616         VectorCopy(maxs, surf->poly_maxs);
1617         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1618         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1619         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1620         surf->poly_radius2 = 0;
1621         vert = surf->poly_verts;
1622         for (i = 0;i < surf->poly_numverts;i++)
1623         {
1624                 VectorSubtract(vert, surf->poly_center, temp);
1625                 dist = DotProduct(temp, temp);
1626                 if (surf->poly_radius2 < dist)
1627                         surf->poly_radius2 = dist;
1628                 vert += 3;
1629         }
1630         surf->poly_radius = sqrt(surf->poly_radius2);
1631 }
1632
1633 /*
1634 =================
1635 Mod_LoadFaces
1636 =================
1637 */
1638 static void Mod_LoadFaces (lump_t *l)
1639 {
1640         dface_t *in;
1641         msurface_t      *out;
1642         int i, count, surfnum, planenum, ssize, tsize;
1643
1644         in = (void *)(mod_base + l->fileofs);
1645         if (l->filelen % sizeof(*in))
1646                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1647         count = l->filelen / sizeof(*in);
1648         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1649
1650         loadmodel->surfaces = out;
1651         loadmodel->numsurfaces = count;
1652         loadmodel->surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1653         loadmodel->surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1654         loadmodel->pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1655
1656         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1657         {
1658                 out->number = surfnum;
1659                 // FIXME: validate edges, texinfo, etc?
1660                 out->firstedge = LittleLong(in->firstedge);
1661                 out->numedges = LittleShort(in->numedges);
1662                 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1663                         Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1664
1665                 i = LittleShort (in->texinfo);
1666                 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1667                         Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1668                 out->texinfo = loadmodel->texinfo + i;
1669                 out->flags = out->texinfo->texture->flags;
1670
1671                 planenum = LittleShort(in->planenum);
1672                 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1673                         Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1674
1675                 if (LittleShort(in->side))
1676                         out->flags |= SURF_PLANEBACK;
1677
1678                 out->plane = loadmodel->planes + planenum;
1679
1680                 // clear lightmap (filled in later)
1681                 out->lightmaptexture = NULL;
1682
1683                 // force lightmap upload on first time seeing the surface
1684                 out->cached_dlight = true;
1685
1686                 CalcSurfaceExtents (out);
1687
1688                 ssize = (out->extents[0] >> 4) + 1;
1689                 tsize = (out->extents[1] >> 4) + 1;
1690
1691                 // lighting info
1692                 for (i = 0;i < MAXLIGHTMAPS;i++)
1693                         out->styles[i] = in->styles[i];
1694                 i = LittleLong(in->lightofs);
1695                 if (i == -1)
1696                         out->samples = NULL;
1697                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1698                         out->samples = loadmodel->lightdata + i;
1699                 else // LordHavoc: white lighting (bsp version 29)
1700                         out->samples = loadmodel->lightdata + (i * 3);
1701
1702                 Mod_GenerateSurfacePolygon(out);
1703                 if (out->texinfo->texture->shader == &Cshader_wall_lightmap)
1704                 {
1705                         if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1706                                 Host_Error ("Bad surface extents");
1707                         Mod_GenerateWallMesh (out, false);
1708                         // stainmap for permanent marks on walls
1709                         out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1710                         // clear to white
1711                         memset(out->stainsamples, 255, ssize * tsize * 3);
1712                 }
1713                 else
1714                         Mod_GenerateVertexMesh (out);
1715         }
1716 }
1717
1718 /*
1719 =================
1720 Mod_SetParent
1721 =================
1722 */
1723 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1724 {
1725         node->parent = parent;
1726         if (node->contents < 0)
1727                 return;
1728         Mod_SetParent (node->children[0], node);
1729         Mod_SetParent (node->children[1], node);
1730 }
1731
1732 /*
1733 =================
1734 Mod_LoadNodes
1735 =================
1736 */
1737 static void Mod_LoadNodes (lump_t *l)
1738 {
1739         int                     i, j, count, p;
1740         dnode_t         *in;
1741         mnode_t         *out;
1742
1743         in = (void *)(mod_base + l->fileofs);
1744         if (l->filelen % sizeof(*in))
1745                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1746         count = l->filelen / sizeof(*in);
1747         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1748
1749         loadmodel->nodes = out;
1750         loadmodel->numnodes = count;
1751
1752         for ( i=0 ; i<count ; i++, in++, out++)
1753         {
1754                 for (j=0 ; j<3 ; j++)
1755                 {
1756                         out->mins[j] = LittleShort (in->mins[j]);
1757                         out->maxs[j] = LittleShort (in->maxs[j]);
1758                 }
1759
1760                 p = LittleLong(in->planenum);
1761                 out->plane = loadmodel->planes + p;
1762
1763                 out->firstsurface = LittleShort (in->firstface);
1764                 out->numsurfaces = LittleShort (in->numfaces);
1765
1766                 for (j=0 ; j<2 ; j++)
1767                 {
1768                         p = LittleShort (in->children[j]);
1769                         if (p >= 0)
1770                                 out->children[j] = loadmodel->nodes + p;
1771                         else
1772                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1773                 }
1774         }
1775
1776         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1777 }
1778
1779 /*
1780 =================
1781 Mod_LoadLeafs
1782 =================
1783 */
1784 static void Mod_LoadLeafs (lump_t *l)
1785 {
1786         dleaf_t         *in;
1787         mleaf_t         *out;
1788         int                     i, j, count, p;
1789
1790         in = (void *)(mod_base + l->fileofs);
1791         if (l->filelen % sizeof(*in))
1792                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1793         count = l->filelen / sizeof(*in);
1794         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1795
1796         loadmodel->leafs = out;
1797         loadmodel->numleafs = count;
1798
1799         for ( i=0 ; i<count ; i++, in++, out++)
1800         {
1801                 for (j=0 ; j<3 ; j++)
1802                 {
1803                         out->mins[j] = LittleShort (in->mins[j]);
1804                         out->maxs[j] = LittleShort (in->maxs[j]);
1805                 }
1806
1807                 p = LittleLong(in->contents);
1808                 out->contents = p;
1809
1810                 out->firstmarksurface = loadmodel->marksurfaces +
1811                         LittleShort(in->firstmarksurface);
1812                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1813
1814                 p = LittleLong(in->visofs);
1815                 if (p == -1)
1816                         out->compressed_vis = NULL;
1817                 else
1818                         out->compressed_vis = loadmodel->visdata + p;
1819
1820                 for (j=0 ; j<4 ; j++)
1821                         out->ambient_sound_level[j] = in->ambient_level[j];
1822
1823                 // FIXME: Insert caustics here
1824         }
1825 }
1826
1827 /*
1828 =================
1829 Mod_LoadClipnodes
1830 =================
1831 */
1832 static void Mod_LoadClipnodes (lump_t *l)
1833 {
1834         dclipnode_t *in, *out;
1835         int                     i, count;
1836         hull_t          *hull;
1837
1838         in = (void *)(mod_base + l->fileofs);
1839         if (l->filelen % sizeof(*in))
1840                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1841         count = l->filelen / sizeof(*in);
1842         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1843
1844         loadmodel->clipnodes = out;
1845         loadmodel->numclipnodes = count;
1846
1847         if (loadmodel->ishlbsp)
1848         {
1849                 hull = &loadmodel->hulls[1];
1850                 hull->clipnodes = out;
1851                 hull->firstclipnode = 0;
1852                 hull->lastclipnode = count-1;
1853                 hull->planes = loadmodel->planes;
1854                 hull->clip_mins[0] = -16;
1855                 hull->clip_mins[1] = -16;
1856                 hull->clip_mins[2] = -36;
1857                 hull->clip_maxs[0] = 16;
1858                 hull->clip_maxs[1] = 16;
1859                 hull->clip_maxs[2] = 36;
1860                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1861
1862                 hull = &loadmodel->hulls[2];
1863                 hull->clipnodes = out;
1864                 hull->firstclipnode = 0;
1865                 hull->lastclipnode = count-1;
1866                 hull->planes = loadmodel->planes;
1867                 hull->clip_mins[0] = -32;
1868                 hull->clip_mins[1] = -32;
1869                 hull->clip_mins[2] = -32;
1870                 hull->clip_maxs[0] = 32;
1871                 hull->clip_maxs[1] = 32;
1872                 hull->clip_maxs[2] = 32;
1873                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1874
1875                 hull = &loadmodel->hulls[3];
1876                 hull->clipnodes = out;
1877                 hull->firstclipnode = 0;
1878                 hull->lastclipnode = count-1;
1879                 hull->planes = loadmodel->planes;
1880                 hull->clip_mins[0] = -16;
1881                 hull->clip_mins[1] = -16;
1882                 hull->clip_mins[2] = -18;
1883                 hull->clip_maxs[0] = 16;
1884                 hull->clip_maxs[1] = 16;
1885                 hull->clip_maxs[2] = 18;
1886                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1887         }
1888         else
1889         {
1890                 hull = &loadmodel->hulls[1];
1891                 hull->clipnodes = out;
1892                 hull->firstclipnode = 0;
1893                 hull->lastclipnode = count-1;
1894                 hull->planes = loadmodel->planes;
1895                 hull->clip_mins[0] = -16;
1896                 hull->clip_mins[1] = -16;
1897                 hull->clip_mins[2] = -24;
1898                 hull->clip_maxs[0] = 16;
1899                 hull->clip_maxs[1] = 16;
1900                 hull->clip_maxs[2] = 32;
1901                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1902
1903                 hull = &loadmodel->hulls[2];
1904                 hull->clipnodes = out;
1905                 hull->firstclipnode = 0;
1906                 hull->lastclipnode = count-1;
1907                 hull->planes = loadmodel->planes;
1908                 hull->clip_mins[0] = -32;
1909                 hull->clip_mins[1] = -32;
1910                 hull->clip_mins[2] = -24;
1911                 hull->clip_maxs[0] = 32;
1912                 hull->clip_maxs[1] = 32;
1913                 hull->clip_maxs[2] = 64;
1914                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1915         }
1916
1917         for (i=0 ; i<count ; i++, out++, in++)
1918         {
1919                 out->planenum = LittleLong(in->planenum);
1920                 out->children[0] = LittleShort(in->children[0]);
1921                 out->children[1] = LittleShort(in->children[1]);
1922                 if (out->children[0] >= count || out->children[1] >= count)
1923                         Host_Error("Corrupt clipping hull (out of range child)\n");
1924         }
1925 }
1926
1927 /*
1928 =================
1929 Mod_MakeHull0
1930
1931 Duplicate the drawing hull structure as a clipping hull
1932 =================
1933 */
1934 static void Mod_MakeHull0 (void)
1935 {
1936         mnode_t         *in;
1937         dclipnode_t *out;
1938         int                     i;
1939         hull_t          *hull;
1940
1941         hull = &loadmodel->hulls[0];
1942
1943         in = loadmodel->nodes;
1944         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1945
1946         hull->clipnodes = out;
1947         hull->firstclipnode = 0;
1948         hull->lastclipnode = loadmodel->numnodes - 1;
1949         hull->planes = loadmodel->planes;
1950
1951         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1952         {
1953                 out->planenum = in->plane - loadmodel->planes;
1954                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1955                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1956         }
1957 }
1958
1959 /*
1960 =================
1961 Mod_LoadMarksurfaces
1962 =================
1963 */
1964 static void Mod_LoadMarksurfaces (lump_t *l)
1965 {
1966         int i, j;
1967         short *in;
1968
1969         in = (void *)(mod_base + l->fileofs);
1970         if (l->filelen % sizeof(*in))
1971                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1972         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1973         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(int));
1974
1975         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1976         {
1977                 j = (unsigned) LittleShort(in[i]);
1978                 if (j >= loadmodel->numsurfaces)
1979                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1980                 loadmodel->marksurfaces[i] = j;
1981         }
1982 }
1983
1984 /*
1985 =================
1986 Mod_LoadSurfedges
1987 =================
1988 */
1989 static void Mod_LoadSurfedges (lump_t *l)
1990 {
1991         int             i;
1992         int             *in;
1993
1994         in = (void *)(mod_base + l->fileofs);
1995         if (l->filelen % sizeof(*in))
1996                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1997         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1998         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1999
2000         for (i = 0;i < loadmodel->numsurfedges;i++)
2001                 loadmodel->surfedges[i] = LittleLong (in[i]);
2002 }
2003
2004
2005 /*
2006 =================
2007 Mod_LoadPlanes
2008 =================
2009 */
2010 static void Mod_LoadPlanes (lump_t *l)
2011 {
2012         int                     i;
2013         mplane_t        *out;
2014         dplane_t        *in;
2015
2016         in = (void *)(mod_base + l->fileofs);
2017         if (l->filelen % sizeof(*in))
2018                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
2019
2020         loadmodel->numplanes = l->filelen / sizeof(*in);
2021         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
2022
2023         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
2024         {
2025                 out->normal[0] = LittleFloat (in->normal[0]);
2026                 out->normal[1] = LittleFloat (in->normal[1]);
2027                 out->normal[2] = LittleFloat (in->normal[2]);
2028                 out->dist = LittleFloat (in->dist);
2029
2030                 PlaneClassify(out);
2031         }
2032 }
2033
2034 #define MAX_POINTS_ON_WINDING 64
2035
2036 typedef struct
2037 {
2038         int numpoints;
2039         int padding;
2040         double points[8][3]; // variable sized
2041 }
2042 winding_t;
2043
2044 /*
2045 ==================
2046 NewWinding
2047 ==================
2048 */
2049 static winding_t *NewWinding (int points)
2050 {
2051         winding_t *w;
2052         int size;
2053
2054         if (points > MAX_POINTS_ON_WINDING)
2055                 Sys_Error("NewWinding: too many points\n");
2056
2057         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2058         w = Mem_Alloc(loadmodel->mempool, size);
2059         memset (w, 0, size);
2060
2061         return w;
2062 }
2063
2064 static void FreeWinding (winding_t *w)
2065 {
2066         Mem_Free(w);
2067 }
2068
2069 /*
2070 =================
2071 BaseWindingForPlane
2072 =================
2073 */
2074 static winding_t *BaseWindingForPlane (mplane_t *p)
2075 {
2076         double org[3], vright[3], vup[3], normal[3];
2077         winding_t *w;
2078
2079         VectorCopy(p->normal, normal);
2080         VectorVectorsDouble(normal, vright, vup);
2081
2082         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
2083         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
2084
2085         // project a really big axis aligned box onto the plane
2086         w = NewWinding (4);
2087
2088         VectorScale (p->normal, p->dist, org);
2089
2090         VectorSubtract (org, vright, w->points[0]);
2091         VectorAdd (w->points[0], vup, w->points[0]);
2092
2093         VectorAdd (org, vright, w->points[1]);
2094         VectorAdd (w->points[1], vup, w->points[1]);
2095
2096         VectorAdd (org, vright, w->points[2]);
2097         VectorSubtract (w->points[2], vup, w->points[2]);
2098
2099         VectorSubtract (org, vright, w->points[3]);
2100         VectorSubtract (w->points[3], vup, w->points[3]);
2101
2102         w->numpoints = 4;
2103
2104         return w;
2105 }
2106
2107 /*
2108 ==================
2109 ClipWinding
2110
2111 Clips the winding to the plane, returning the new winding on the positive side
2112 Frees the input winding.
2113 If keepon is true, an exactly on-plane winding will be saved, otherwise
2114 it will be clipped away.
2115 ==================
2116 */
2117 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
2118 {
2119         double  dists[MAX_POINTS_ON_WINDING + 1];
2120         int             sides[MAX_POINTS_ON_WINDING + 1];
2121         int             counts[3];
2122         double  dot;
2123         int             i, j;
2124         double  *p1, *p2;
2125         double  mid[3];
2126         winding_t       *neww;
2127         int             maxpts;
2128
2129         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2130
2131         // determine sides for each point
2132         for (i = 0;i < in->numpoints;i++)
2133         {
2134                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
2135                 if (dot > ON_EPSILON)
2136                         sides[i] = SIDE_FRONT;
2137                 else if (dot < -ON_EPSILON)
2138                         sides[i] = SIDE_BACK;
2139                 else
2140                         sides[i] = SIDE_ON;
2141                 counts[sides[i]]++;
2142         }
2143         sides[i] = sides[0];
2144         dists[i] = dists[0];
2145
2146         if (keepon && !counts[0] && !counts[1])
2147                 return in;
2148
2149         if (!counts[0])
2150         {
2151                 FreeWinding (in);
2152                 return NULL;
2153         }
2154         if (!counts[1])
2155                 return in;
2156
2157         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2158         if (maxpts > MAX_POINTS_ON_WINDING)
2159                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2160
2161         neww = NewWinding (maxpts);
2162
2163         for (i = 0;i < in->numpoints;i++)
2164         {
2165                 if (neww->numpoints >= maxpts)
2166                         Sys_Error ("ClipWinding: points exceeded estimate");
2167
2168                 p1 = in->points[i];
2169
2170                 if (sides[i] == SIDE_ON)
2171                 {
2172                         VectorCopy (p1, neww->points[neww->numpoints]);
2173                         neww->numpoints++;
2174                         continue;
2175                 }
2176
2177                 if (sides[i] == SIDE_FRONT)
2178                 {
2179                         VectorCopy (p1, neww->points[neww->numpoints]);
2180                         neww->numpoints++;
2181                 }
2182
2183                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2184                         continue;
2185
2186                 // generate a split point
2187                 p2 = in->points[(i+1)%in->numpoints];
2188
2189                 dot = dists[i] / (dists[i]-dists[i+1]);
2190                 for (j = 0;j < 3;j++)
2191                 {       // avoid round off error when possible
2192                         if (split->normal[j] == 1)
2193                                 mid[j] = split->dist;
2194                         else if (split->normal[j] == -1)
2195                                 mid[j] = -split->dist;
2196                         else
2197                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2198                 }
2199
2200                 VectorCopy (mid, neww->points[neww->numpoints]);
2201                 neww->numpoints++;
2202         }
2203
2204         // free the original winding
2205         FreeWinding (in);
2206
2207         return neww;
2208 }
2209
2210
2211 /*
2212 ==================
2213 DivideWinding
2214
2215 Divides a winding by a plane, producing one or two windings.  The
2216 original winding is not damaged or freed.  If only on one side, the
2217 returned winding will be the input winding.  If on both sides, two
2218 new windings will be created.
2219 ==================
2220 */
2221 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2222 {
2223         double  dists[MAX_POINTS_ON_WINDING + 1];
2224         int             sides[MAX_POINTS_ON_WINDING + 1];
2225         int             counts[3];
2226         double  dot;
2227         int             i, j;
2228         double  *p1, *p2;
2229         double  mid[3];
2230         winding_t       *f, *b;
2231         int             maxpts;
2232
2233         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2234
2235         // determine sides for each point
2236         for (i = 0;i < in->numpoints;i++)
2237         {
2238                 dot = DotProduct (in->points[i], split->normal);
2239                 dot -= split->dist;
2240                 dists[i] = dot;
2241                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2242                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2243                 else sides[i] = SIDE_ON;
2244                 counts[sides[i]]++;
2245         }
2246         sides[i] = sides[0];
2247         dists[i] = dists[0];
2248
2249         *front = *back = NULL;
2250
2251         if (!counts[0])
2252         {
2253                 *back = in;
2254                 return;
2255         }
2256         if (!counts[1])
2257         {
2258                 *front = in;
2259                 return;
2260         }
2261
2262         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2263
2264         if (maxpts > MAX_POINTS_ON_WINDING)
2265                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2266
2267         *front = f = NewWinding (maxpts);
2268         *back = b = NewWinding (maxpts);
2269
2270         for (i = 0;i < in->numpoints;i++)
2271         {
2272                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2273                         Sys_Error ("DivideWinding: points exceeded estimate");
2274
2275                 p1 = in->points[i];
2276
2277                 if (sides[i] == SIDE_ON)
2278                 {
2279                         VectorCopy (p1, f->points[f->numpoints]);
2280                         f->numpoints++;
2281                         VectorCopy (p1, b->points[b->numpoints]);
2282                         b->numpoints++;
2283                         continue;
2284                 }
2285
2286                 if (sides[i] == SIDE_FRONT)
2287                 {
2288                         VectorCopy (p1, f->points[f->numpoints]);
2289                         f->numpoints++;
2290                 }
2291                 else if (sides[i] == SIDE_BACK)
2292                 {
2293                         VectorCopy (p1, b->points[b->numpoints]);
2294                         b->numpoints++;
2295                 }
2296
2297                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2298                         continue;
2299
2300                 // generate a split point
2301                 p2 = in->points[(i+1)%in->numpoints];
2302
2303                 dot = dists[i] / (dists[i]-dists[i+1]);
2304                 for (j = 0;j < 3;j++)
2305                 {       // avoid round off error when possible
2306                         if (split->normal[j] == 1)
2307                                 mid[j] = split->dist;
2308                         else if (split->normal[j] == -1)
2309                                 mid[j] = -split->dist;
2310                         else
2311                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2312                 }
2313
2314                 VectorCopy (mid, f->points[f->numpoints]);
2315                 f->numpoints++;
2316                 VectorCopy (mid, b->points[b->numpoints]);
2317                 b->numpoints++;
2318         }
2319 }
2320
2321 typedef struct portal_s
2322 {
2323         mplane_t plane;
2324         mnode_t *nodes[2];              // [0] = front side of plane
2325         struct portal_s *next[2];
2326         winding_t *winding;
2327         struct portal_s *chain; // all portals are linked into a list
2328 }
2329 portal_t;
2330
2331 static portal_t *portalchain;
2332
2333 /*
2334 ===========
2335 AllocPortal
2336 ===========
2337 */
2338 static portal_t *AllocPortal (void)
2339 {
2340         portal_t *p;
2341         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2342         p->chain = portalchain;
2343         portalchain = p;
2344         return p;
2345 }
2346
2347 static void FreePortal(portal_t *p)
2348 {
2349         Mem_Free(p);
2350 }
2351
2352 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2353 {
2354         // calculate children first
2355         if (node->children[0]->contents >= 0)
2356                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2357         if (node->children[1]->contents >= 0)
2358                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2359
2360         // make combined bounding box from children
2361         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2362         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2363         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2364         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2365         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2366         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2367 }
2368
2369 static void Mod_FinalizePortals(void)
2370 {
2371         int i, j, numportals, numpoints;
2372         portal_t *p, *pnext;
2373         mportal_t *portal;
2374         mvertex_t *point;
2375         mleaf_t *leaf, *endleaf;
2376         winding_t *w;
2377
2378         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2379         leaf = loadmodel->leafs;
2380         endleaf = leaf + loadmodel->numleafs;
2381         for (;leaf < endleaf;leaf++)
2382         {
2383                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2384                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2385         }
2386         p = portalchain;
2387         while(p)
2388         {
2389                 if (p->winding)
2390                 {
2391                         for (i = 0;i < 2;i++)
2392                         {
2393                                 leaf = (mleaf_t *)p->nodes[i];
2394                                 w = p->winding;
2395                                 for (j = 0;j < w->numpoints;j++)
2396                                 {
2397                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2398                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2399                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2400                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2401                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2402                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2403                                 }
2404                         }
2405                 }
2406                 p = p->chain;
2407         }
2408
2409         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2410
2411         // tally up portal and point counts
2412         p = portalchain;
2413         numportals = 0;
2414         numpoints = 0;
2415         while(p)
2416         {
2417                 // note: this check must match the one below or it will usually corrupt memory
2418                 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2419                 if (p->winding && p->nodes[0] != p->nodes[1]
2420                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2421                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2422                 {
2423                         numportals += 2;
2424                         numpoints += p->winding->numpoints * 2;
2425                 }
2426                 p = p->chain;
2427         }
2428         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2429         loadmodel->numportals = numportals;
2430         loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2431         loadmodel->numportalpoints = numpoints;
2432         // clear all leaf portal chains
2433         for (i = 0;i < loadmodel->numleafs;i++)
2434                 loadmodel->leafs[i].portals = NULL;
2435         // process all portals in the global portal chain, while freeing them
2436         portal = loadmodel->portals;
2437         point = loadmodel->portalpoints;
2438         p = portalchain;
2439         portalchain = NULL;
2440         while (p)
2441         {
2442                 pnext = p->chain;
2443
2444                 if (p->winding)
2445                 {
2446                         // note: this check must match the one above or it will usually corrupt memory
2447                         // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2448                         if (p->nodes[0] != p->nodes[1]
2449                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2450                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2451                         {
2452                                 // first make the back to front portal (forward portal)
2453                                 portal->points = point;
2454                                 portal->numpoints = p->winding->numpoints;
2455                                 portal->plane.dist = p->plane.dist;
2456                                 VectorCopy(p->plane.normal, portal->plane.normal);
2457                                 portal->here = (mleaf_t *)p->nodes[1];
2458                                 portal->past = (mleaf_t *)p->nodes[0];
2459                                 // copy points
2460                                 for (j = 0;j < portal->numpoints;j++)
2461                                 {
2462                                         VectorCopy(p->winding->points[j], point->position);
2463                                         point++;
2464                                 }
2465                                 PlaneClassify(&portal->plane);
2466
2467                                 // link into leaf's portal chain
2468                                 portal->next = portal->here->portals;
2469                                 portal->here->portals = portal;
2470
2471                                 // advance to next portal
2472                                 portal++;
2473
2474                                 // then make the front to back portal (backward portal)
2475                                 portal->points = point;
2476                                 portal->numpoints = p->winding->numpoints;
2477                                 portal->plane.dist = -p->plane.dist;
2478                                 VectorNegate(p->plane.normal, portal->plane.normal);
2479                                 portal->here = (mleaf_t *)p->nodes[0];
2480                                 portal->past = (mleaf_t *)p->nodes[1];
2481                                 // copy points
2482                                 for (j = portal->numpoints - 1;j >= 0;j--)
2483                                 {
2484                                         VectorCopy(p->winding->points[j], point->position);
2485                                         point++;
2486                                 }
2487                                 PlaneClassify(&portal->plane);
2488
2489                                 // link into leaf's portal chain
2490                                 portal->next = portal->here->portals;
2491                                 portal->here->portals = portal;
2492
2493                                 // advance to next portal
2494                                 portal++;
2495                         }
2496                         FreeWinding(p->winding);
2497                 }
2498                 FreePortal(p);
2499                 p = pnext;
2500         }
2501 }
2502
2503 /*
2504 =============
2505 AddPortalToNodes
2506 =============
2507 */
2508 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2509 {
2510         if (!front)
2511                 Host_Error ("AddPortalToNodes: NULL front node");
2512         if (!back)
2513                 Host_Error ("AddPortalToNodes: NULL back node");
2514         if (p->nodes[0] || p->nodes[1])
2515                 Host_Error ("AddPortalToNodes: already included");
2516         // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
2517
2518         p->nodes[0] = front;
2519         p->next[0] = (portal_t *)front->portals;
2520         front->portals = (mportal_t *)p;
2521
2522         p->nodes[1] = back;
2523         p->next[1] = (portal_t *)back->portals;
2524         back->portals = (mportal_t *)p;
2525 }
2526
2527 /*
2528 =============
2529 RemovePortalFromNode
2530 =============
2531 */
2532 static void RemovePortalFromNodes(portal_t *portal)
2533 {
2534         int i;
2535         mnode_t *node;
2536         void **portalpointer;
2537         portal_t *t;
2538         for (i = 0;i < 2;i++)
2539         {
2540                 node = portal->nodes[i];
2541
2542                 portalpointer = (void **) &node->portals;
2543                 while (1)
2544                 {
2545                         t = *portalpointer;
2546                         if (!t)
2547                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2548
2549                         if (t == portal)
2550                         {
2551                                 if (portal->nodes[0] == node)
2552                                 {
2553                                         *portalpointer = portal->next[0];
2554                                         portal->nodes[0] = NULL;
2555                                 }
2556                                 else if (portal->nodes[1] == node)
2557                                 {
2558                                         *portalpointer = portal->next[1];
2559                                         portal->nodes[1] = NULL;
2560                                 }
2561                                 else
2562                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2563                                 break;
2564                         }
2565
2566                         if (t->nodes[0] == node)
2567                                 portalpointer = (void **) &t->next[0];
2568                         else if (t->nodes[1] == node)
2569                                 portalpointer = (void **) &t->next[1];
2570                         else
2571                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2572                 }
2573         }
2574 }
2575
2576 static void Mod_RecursiveNodePortals (mnode_t *node)
2577 {
2578         int side;
2579         mnode_t *front, *back, *other_node;
2580         mplane_t clipplane, *plane;
2581         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2582         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2583
2584         // if a leaf, we're done
2585         if (node->contents)
2586                 return;
2587
2588         plane = node->plane;
2589
2590         front = node->children[0];
2591         back = node->children[1];
2592         if (front == back)
2593                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2594
2595         // create the new portal by generating a polygon for the node plane,
2596         // and clipping it by all of the other portals (which came from nodes above this one)
2597         nodeportal = AllocPortal ();
2598         nodeportal->plane = *node->plane;
2599
2600         nodeportalwinding = BaseWindingForPlane (node->plane);
2601         side = 0;       // shut up compiler warning
2602         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2603         {
2604                 clipplane = portal->plane;
2605                 if (portal->nodes[0] == portal->nodes[1])
2606                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2607                 if (portal->nodes[0] == node)
2608                         side = 0;
2609                 else if (portal->nodes[1] == node)
2610                 {
2611                         clipplane.dist = -clipplane.dist;
2612                         VectorNegate (clipplane.normal, clipplane.normal);
2613                         side = 1;
2614                 }
2615                 else
2616                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2617
2618                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2619                 if (!nodeportalwinding)
2620                 {
2621                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2622                         break;
2623                 }
2624         }
2625
2626         if (nodeportalwinding)
2627         {
2628                 // if the plane was not clipped on all sides, there was an error
2629                 nodeportal->winding = nodeportalwinding;
2630                 AddPortalToNodes (nodeportal, front, back);
2631         }
2632
2633         // split the portals of this node along this node's plane and assign them to the children of this node
2634         // (migrating the portals downward through the tree)
2635         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2636         {
2637                 if (portal->nodes[0] == portal->nodes[1])
2638                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2639                 if (portal->nodes[0] == node)
2640                         side = 0;
2641                 else if (portal->nodes[1] == node)
2642                         side = 1;
2643                 else
2644                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2645                 nextportal = portal->next[side];
2646
2647                 other_node = portal->nodes[!side];
2648                 RemovePortalFromNodes (portal);
2649
2650                 // cut the portal into two portals, one on each side of the node plane
2651                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2652
2653                 if (!frontwinding)
2654                 {
2655                         if (side == 0)
2656                                 AddPortalToNodes (portal, back, other_node);
2657                         else
2658                                 AddPortalToNodes (portal, other_node, back);
2659                         continue;
2660                 }
2661                 if (!backwinding)
2662                 {
2663                         if (side == 0)
2664                                 AddPortalToNodes (portal, front, other_node);
2665                         else
2666                                 AddPortalToNodes (portal, other_node, front);
2667                         continue;
2668                 }
2669
2670                 // the winding is split
2671                 splitportal = AllocPortal ();
2672                 temp = splitportal->chain;
2673                 *splitportal = *portal;
2674                 splitportal->chain = temp;
2675                 splitportal->winding = backwinding;
2676                 FreeWinding (portal->winding);
2677                 portal->winding = frontwinding;
2678
2679                 if (side == 0)
2680                 {
2681                         AddPortalToNodes (portal, front, other_node);
2682                         AddPortalToNodes (splitportal, back, other_node);
2683                 }
2684                 else
2685                 {
2686                         AddPortalToNodes (portal, other_node, front);
2687                         AddPortalToNodes (splitportal, other_node, back);
2688                 }
2689         }
2690
2691         Mod_RecursiveNodePortals(front);
2692         Mod_RecursiveNodePortals(back);
2693 }
2694
2695
2696 static void Mod_MakePortals(void)
2697 {
2698         portalchain = NULL;
2699         Mod_RecursiveNodePortals (loadmodel->nodes);
2700         Mod_FinalizePortals();
2701 }
2702
2703 static void Mod_BuildSurfaceNeighbors (msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2704 {
2705         int surfnum, vertnum, snum, vnum;
2706         msurface_t *surf, *s;
2707         float *v0, *v1, *v2, *v3;
2708         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2709         {
2710                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2711                 for (vertnum = 0;vertnum < surf->poly_numverts;vertnum++)
2712                 {
2713                         v0 = surf->poly_verts + ((vertnum + 1) % surf->poly_numverts) * 3;
2714                         v1 = surf->poly_verts + vertnum * 3;
2715                         surf->neighborsurfaces[vertnum] = NULL;
2716                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2717                         {
2718                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2719                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2720                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2721                                  || s == surf)
2722                                         continue;
2723                                 for (vnum = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum < s->poly_numverts;vnum++, v2 = v3, v3 += 3)
2724                                 {
2725                                         if (v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2726                                         {
2727                                                 surf->neighborsurfaces[vertnum] = s;
2728                                                 break;
2729                                         }
2730                                 }
2731                                 if (vnum < s->poly_numverts)
2732                                         break;
2733                         }
2734                 }
2735         }
2736 }
2737
2738 /*
2739 =================
2740 Mod_LoadBrushModel
2741 =================
2742 */
2743 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2744 extern void R_Model_Brush_Draw(entity_render_t *ent);
2745 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2746 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor);
2747 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2748 {
2749         int                     i, j;
2750         dheader_t       *header;
2751         dmodel_t        *bm;
2752         mempool_t       *mainmempool;
2753         char            *loadname;
2754         model_t         *originalloadmodel;
2755
2756         mod->type = mod_brush;
2757
2758         header = (dheader_t *)buffer;
2759
2760         i = LittleLong (header->version);
2761         if (i != BSPVERSION && i != 30)
2762                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2763         mod->ishlbsp = i == 30;
2764         if (loadmodel->isworldmodel)
2765         {
2766                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2767                 // until we get a texture for it...
2768                 R_ResetQuakeSky();
2769         }
2770
2771 // swap all the lumps
2772         mod_base = (qbyte *)header;
2773
2774         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2775                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2776
2777 // load into heap
2778
2779         // store which lightmap format to use
2780         mod->lightmaprgba = r_lightmaprgba.integer;
2781
2782         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2783         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2784         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2785         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2786         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2787         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2788         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2789         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2790         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2791         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2792         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2793         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2794         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2795         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2796         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2797
2798         Mod_MakeHull0 ();
2799         Mod_MakePortals();
2800
2801         mod->numframes = 2;             // regular and alternate animation
2802
2803         mainmempool = mod->mempool;
2804         loadname = mod->name;
2805
2806         Mod_LoadLightList ();
2807         originalloadmodel = loadmodel;
2808
2809 //
2810 // set up the submodels (FIXME: this is confusing)
2811 //
2812         for (i = 0;i < mod->numsubmodels;i++)
2813         {
2814                 int k, l;
2815                 float dist, modelyawradius, modelradius, *vec;
2816                 msurface_t *surf;
2817
2818                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2819                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2820                 modelyawradius = 0;
2821                 modelradius = 0;
2822
2823                 bm = &mod->submodels[i];
2824
2825                 mod->hulls[0].firstclipnode = bm->headnode[0];
2826                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2827                 {
2828                         mod->hulls[j].firstclipnode = bm->headnode[j];
2829                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2830                 }
2831
2832                 mod->firstmodelsurface = bm->firstface;
2833                 mod->nummodelsurfaces = bm->numfaces;
2834
2835                 // this gets altered below if sky is used
2836                 mod->DrawSky = NULL;
2837                 mod->Draw = R_Model_Brush_Draw;
2838                 mod->DrawFakeShadow = NULL;
2839                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2840                 mod->DrawLight = R_Model_Brush_DrawLight;
2841                 mod->texturesurfacechains = Mem_Alloc(originalloadmodel->mempool, mod->numtextures * sizeof(msurface_t *));
2842                 if (mod->nummodelsurfaces)
2843                 {
2844                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2845                         for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2846                         {
2847                                 // we only need to have a drawsky function if it is used (usually only on world model)
2848                                 if (surf->texinfo->texture->shader == &Cshader_sky)
2849                                         mod->DrawSky = R_Model_Brush_DrawSky;
2850                                 // link into texture chain
2851                                 surf->texturechain = mod->texturesurfacechains[surf->texinfo->texture - mod->textures];
2852                                 mod->texturesurfacechains[surf->texinfo->texture - mod->textures] = surf;
2853                                 // calculate bounding shapes
2854                                 for (k = 0;k < surf->numedges;k++)
2855                                 {
2856                                         l = mod->surfedges[k + surf->firstedge];
2857                                         if (l > 0)
2858                                                 vec = mod->vertexes[mod->edges[l].v[0]].position;
2859                                         else
2860                                                 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2861                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2862                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2863                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2864                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2865                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2866                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2867                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
2868                                         if (modelyawradius < dist)
2869                                                 modelyawradius = dist;
2870                                         dist += vec[2]*vec[2];
2871                                         if (modelradius < dist)
2872                                                 modelradius = dist;
2873                                 }
2874                         }
2875                         modelyawradius = sqrt(modelyawradius);
2876                         modelradius = sqrt(modelradius);
2877                         mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2878                         mod->yawmins[2] = mod->normalmins[2];
2879                         mod->yawmaxs[2] = mod->normalmaxs[2];
2880                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2881                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2882                         mod->radius = modelradius;
2883                         mod->radius2 = modelradius * modelradius;
2884                         // LordHavoc: build triangle meshs for entire model's geometry
2885                         // (only used for shadow volumes)
2886                         mod->shadowmesh = Mod_ShadowMesh_Begin(originalloadmodel->mempool);
2887                         for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2888                                 if (surf->flags & SURF_SHADOWCAST)
2889                                         Mod_ShadowMesh_AddPolygon(originalloadmodel->mempool, mod->shadowmesh, surf->poly_numverts, surf->poly_verts);
2890                         mod->shadowmesh = Mod_ShadowMesh_Finish(originalloadmodel->mempool, mod->shadowmesh);
2891                         Mod_ShadowMesh_CalcBBox(mod->shadowmesh, mod->shadowmesh_mins, mod->shadowmesh_maxs, mod->shadowmesh_center, &mod->shadowmesh_radius);
2892                 }
2893                 else
2894                 {
2895                         // LordHavoc: empty submodel (lacrima.bsp has such a glitch)
2896                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2897                         VectorClear(mod->normalmins);
2898                         VectorClear(mod->normalmaxs);
2899                         VectorClear(mod->yawmins);
2900                         VectorClear(mod->yawmaxs);
2901                         VectorClear(mod->rotatedmins);
2902                         VectorClear(mod->rotatedmaxs);
2903                         mod->radius = 0;
2904                         mod->radius2 = 0;
2905                         mod->shadowmesh = NULL;
2906                 }
2907                 Mod_BuildSurfaceNeighbors(mod->surfaces + mod->firstmodelsurface, mod->nummodelsurfaces, originalloadmodel->mempool);
2908
2909                 mod->numleafs = bm->visleafs;
2910
2911                 // LordHavoc: only register submodels if it is the world
2912                 // (prevents bsp models from replacing world submodels)
2913                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2914                 {
2915                         char    name[10];
2916                         // duplicate the basic information
2917                         sprintf (name, "*%i", i+1);
2918                         loadmodel = Mod_FindName (name);
2919                         *loadmodel = *mod;
2920                         strcpy (loadmodel->name, name);
2921                         // textures and memory belong to the main model
2922                         loadmodel->texturepool = NULL;
2923                         loadmodel->mempool = NULL;
2924                         mod = loadmodel;
2925                 }
2926         }
2927
2928         loadmodel = originalloadmodel;
2929         Mod_ProcessLightList ();
2930 }
2931