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