2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // models.c -- model loading and caching
22 // models are the only shared resource between a client and server running
23 // on the same machine.
30 cvar_t r_enableshadowvolumes = {CVAR_SAVE, "r_enableshadowvolumes", "1", "Enables use of Stencil Shadow Volume shadowing methods, saves some memory if turned off"};
31 cvar_t r_mipskins = {CVAR_SAVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
32 cvar_t r_mipnormalmaps = {CVAR_SAVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
33 cvar_t mod_generatelightmaps_unitspersample = {CVAR_SAVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
34 cvar_t mod_generatelightmaps_borderpixels = {CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
35 cvar_t mod_generatelightmaps_texturesize = {CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
36 cvar_t mod_generatelightmaps_lightmapsamples = {CVAR_SAVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
37 cvar_t mod_generatelightmaps_vertexsamples = {CVAR_SAVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
38 cvar_t mod_generatelightmaps_gridsamples = {CVAR_SAVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
39 cvar_t mod_generatelightmaps_lightmapradius = {CVAR_SAVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
40 cvar_t mod_generatelightmaps_vertexradius = {CVAR_SAVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
41 cvar_t mod_generatelightmaps_gridradius = {CVAR_SAVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
43 dp_model_t *loadmodel;
45 static mempool_t *mod_mempool;
46 static memexpandablearray_t models;
48 static mempool_t* q3shaders_mem;
49 typedef struct q3shader_hash_entry_s
51 q3shaderinfo_t shader;
52 struct q3shader_hash_entry_s* chain;
53 } q3shader_hash_entry_t;
54 #define Q3SHADER_HASH_SIZE 1021
55 typedef struct q3shader_data_s
57 memexpandablearray_t hash_entries;
58 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
59 memexpandablearray_t char_ptrs;
61 static q3shader_data_t* q3shader_data;
63 static void mod_start(void)
66 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
69 SCR_PushLoadingScreen(false, "Loading models", 1.0);
71 for (i = 0;i < nummodels;i++)
72 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
75 for (i = 0;i < nummodels;i++)
76 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
79 SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
80 Mod_LoadModel(mod, true, false);
81 SCR_PopLoadingScreen(false);
83 SCR_PopLoadingScreen(false);
86 static void mod_shutdown(void)
89 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
92 for (i = 0;i < nummodels;i++)
93 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
97 Mod_Skeletal_FreeBuffers();
100 static void mod_newmap(void)
103 int i, j, k, l, surfacenum, ssize, tsize;
104 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
107 for (i = 0;i < nummodels;i++)
109 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
111 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
113 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
114 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
115 if (mod->data_textures[j].shaderpasses[l])
116 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
117 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
119 if (mod->brush.solidskyskinframe)
120 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
121 if (mod->brush.alphaskyskinframe)
122 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
126 if (!cl_stainmaps_clearonload.integer)
129 for (i = 0;i < nummodels;i++)
131 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
133 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
135 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
137 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
138 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
139 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
140 mod->brushq1.lightmapupdateflags[surfacenum] = true;
152 static void Mod_Print(void);
153 static void Mod_Precache (void);
154 static void Mod_Decompile_f(void);
155 static void Mod_GenerateLightmaps_f(void);
158 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
159 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(dp_model_t), 16);
165 Cvar_RegisterVariable(&r_enableshadowvolumes);
166 Cvar_RegisterVariable(&r_mipskins);
167 Cvar_RegisterVariable(&r_mipnormalmaps);
168 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
169 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
170 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
172 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
173 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
174 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
175 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
176 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
177 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
179 Cmd_AddCommand ("modellist", Mod_Print, "prints a list of loaded models");
180 Cmd_AddCommand ("modelprecache", Mod_Precache, "load a model");
181 Cmd_AddCommand ("modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
182 Cmd_AddCommand ("mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
185 void Mod_RenderInit(void)
187 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
190 void Mod_UnloadModel (dp_model_t *mod)
192 char name[MAX_QPATH];
194 dp_model_t *parentmodel;
196 if (developer_loading.integer)
197 Con_Printf("unloading model %s\n", mod->name);
199 strlcpy(name, mod->name, sizeof(name));
200 parentmodel = mod->brush.parentmodel;
204 if (mod->surfmesh.data_element3i_indexbuffer)
205 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
206 mod->surfmesh.data_element3i_indexbuffer = NULL;
207 if (mod->surfmesh.data_element3s_indexbuffer)
208 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
209 mod->surfmesh.data_element3s_indexbuffer = NULL;
210 if (mod->surfmesh.vbo_vertexbuffer)
211 R_Mesh_DestroyMeshBuffer(mod->surfmesh.vbo_vertexbuffer);
212 mod->surfmesh.vbo_vertexbuffer = NULL;
214 // free textures/memory attached to the model
215 R_FreeTexturePool(&mod->texturepool);
216 Mem_FreePool(&mod->mempool);
217 // clear the struct to make it available
218 memset(mod, 0, sizeof(dp_model_t));
219 // restore the fields we want to preserve
220 strlcpy(mod->name, name, sizeof(mod->name));
221 mod->brush.parentmodel = parentmodel;
226 static void R_Model_Null_Draw(entity_render_t *ent)
232 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass);
234 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
249 // REQUIRED: fetch start
250 COM_ParseToken_Simple(&bufptr, true, false, true);
252 break; // end of file
253 if (!strcmp(com_token, "\n"))
254 continue; // empty line
255 start = atoi(com_token);
257 // REQUIRED: fetch length
258 COM_ParseToken_Simple(&bufptr, true, false, true);
259 if (!bufptr || !strcmp(com_token, "\n"))
261 Con_Printf("framegroups file: missing number of frames\n");
264 len = atoi(com_token);
266 // OPTIONAL args start
267 COM_ParseToken_Simple(&bufptr, true, false, true);
269 // OPTIONAL: fetch fps
271 if (bufptr && strcmp(com_token, "\n"))
273 fps = atof(com_token);
274 COM_ParseToken_Simple(&bufptr, true, false, true);
277 // OPTIONAL: fetch loopflag
279 if (bufptr && strcmp(com_token, "\n"))
281 loop = (atoi(com_token) != 0);
282 COM_ParseToken_Simple(&bufptr, true, false, true);
285 // OPTIONAL: fetch name
287 if (bufptr && strcmp(com_token, "\n"))
289 strlcpy(name, com_token, sizeof(name));
290 COM_ParseToken_Simple(&bufptr, true, false, true);
293 // OPTIONAL: remaining unsupported tokens (eat them)
294 while (bufptr && strcmp(com_token, "\n"))
295 COM_ParseToken_Simple(&bufptr, true, false, true);
297 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
300 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
307 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass)
309 dp_model_t *mod = (dp_model_t *) pass;
310 animscene_t *anim = &mod->animscenes[i];
312 strlcpy(anim->name, name, sizeof(anim[i].name));
314 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
315 anim->firstframe = bound(0, start, mod->num_poses - 1);
316 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
317 anim->framerate = max(1, fps);
319 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
322 static void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
327 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
330 Con_Printf("no scene found in framegroups file, aborting\n");
333 mod->numframes = cnt;
336 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
337 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
340 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
343 static void Mod_FindPotentialDeforms(dp_model_t *mod)
347 mod->wantnormals = false;
348 mod->wanttangents = false;
349 for (i = 0;i < mod->num_textures;i++)
351 texture = mod->data_textures + i;
352 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
353 mod->wantnormals = true;
354 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
355 mod->wantnormals = true;
356 for (j = 0;j < Q3MAXDEFORMS;j++)
358 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
360 mod->wanttangents = true;
361 mod->wantnormals = true;
364 if (texture->deforms[j].deform != Q3DEFORM_NONE)
365 mod->wantnormals = true;
377 dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
382 fs_offset_t filesize = 0;
387 if (mod->name[0] == '*') // submodel
390 if (!strcmp(mod->name, "null"))
395 if (mod->loaded || mod->mempool)
396 Mod_UnloadModel(mod);
398 if (developer_loading.integer)
399 Con_Printf("loading model %s\n", mod->name);
402 mod->crc = (unsigned int)-1;
405 VectorClear(mod->normalmins);
406 VectorClear(mod->normalmaxs);
407 VectorClear(mod->yawmins);
408 VectorClear(mod->yawmaxs);
409 VectorClear(mod->rotatedmins);
410 VectorClear(mod->rotatedmaxs);
412 mod->modeldatatypestring = "null";
413 mod->type = mod_null;
414 mod->Draw = R_Model_Null_Draw;
418 // no fatal errors occurred, so this model is ready to use.
427 // even if the model is loaded it still may need reloading...
429 // if it is not loaded or checkdisk is true we need to calculate the crc
430 if (!mod->loaded || checkdisk)
432 if (checkdisk && mod->loaded)
433 Con_DPrintf("checking model %s\n", mod->name);
434 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
437 crc = CRC_Block((unsigned char *)buf, filesize);
438 // we need to reload the model if the crc does not match
444 // if the model is already loaded and checks passed, just return
452 if (developer_loading.integer)
453 Con_Printf("loading model %s\n", mod->name);
455 SCR_PushLoadingScreen(true, mod->name, 1);
457 // LordHavoc: unload the existing model in this slot (if there is one)
458 if (mod->loaded || mod->mempool)
459 Mod_UnloadModel(mod);
464 // errors can prevent the corresponding mod->loaded = true;
467 // default lightmap scale
468 mod->lightmapscale = 1;
470 // default model radius and bounding box (mainly for missing models)
472 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
473 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
474 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
475 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
476 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
477 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
481 // load q3 shaders for the first time, or after a level change
487 char *bufend = (char *)buf + filesize;
489 // all models use memory, so allocate a memory pool
490 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
492 num = LittleLong(*((int *)buf));
493 // call the apropriate loader
495 if (!strcasecmp(FS_FileExtension(mod->name), "obj")) Mod_OBJ_Load(mod, buf, bufend);
496 else if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
497 else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend);
498 else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend);
499 else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend);
500 else if (!memcmp(buf, "IDS2", 4)) Mod_IDS2_Load(mod, buf, bufend);
501 else if (!memcmp(buf, "IBSP", 4)) Mod_IBSP_Load(mod, buf, bufend);
502 else if (!memcmp(buf, "ZYMOTICMODEL", 12)) Mod_ZYMOTICMODEL_Load(mod, buf, bufend);
503 else if (!memcmp(buf, "DARKPLACESMODEL", 16)) Mod_DARKPLACESMODEL_Load(mod, buf, bufend);
504 else if (!memcmp(buf, "ACTRHEAD", 8)) Mod_PSKMODEL_Load(mod, buf, bufend);
505 else if (!memcmp(buf, "INTERQUAKEMODEL", 16)) Mod_INTERQUAKEMODEL_Load(mod, buf, bufend);
506 else if (strlen(mod->name) >= 4 && !strcmp(mod->name + strlen(mod->name) - 4, ".map")) Mod_MAP_Load(mod, buf, bufend);
507 else if (num == BSPVERSION || num == 30 || !memcmp(buf, "BSP2", 4) || !memcmp(buf, "2PSB", 4)) Mod_Q1BSP_Load(mod, buf, bufend);
508 else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
511 Mod_FindPotentialDeforms(mod);
513 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
516 Mod_FrameGroupify(mod, (const char *)buf);
524 // LordHavoc: Sys_Error was *ANNOYING*
525 Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
528 // no fatal errors occurred, so this model is ready to use.
531 SCR_PopLoadingScreen(false);
536 void Mod_ClearUsed(void)
539 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
541 for (i = 0;i < nummodels;i++)
542 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
546 void Mod_PurgeUnused(void)
549 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
551 for (i = 0;i < nummodels;i++)
553 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
555 Mod_UnloadModel(mod);
556 Mem_ExpandableArray_FreeRecord(&models, mod);
567 dp_model_t *Mod_FindName(const char *name, const char *parentname)
576 // if we're not dedicatd, the renderer calls will crash without video
579 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
582 Host_Error ("Mod_ForName: empty name");
584 // search the currently loaded models
585 for (i = 0;i < nummodels;i++)
587 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name) && ((!mod->brush.parentmodel && !parentname[0]) || (mod->brush.parentmodel && parentname[0] && !strcmp(mod->brush.parentmodel->name, parentname))))
594 // no match found, create a new one
595 mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
596 strlcpy(mod->name, name, sizeof(mod->name));
598 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
600 mod->brush.parentmodel = NULL;
610 Loads in a model for the given name
613 dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
616 model = Mod_FindName(name, parentname);
617 if (!model->loaded || checkdisk)
618 Mod_LoadModel(model, crash, checkdisk);
626 Reloads all models if they have changed
629 void Mod_Reload(void)
632 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
635 SCR_PushLoadingScreen(false, "Reloading models", 1.0);
637 for (i = 0;i < nummodels;i++)
638 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
640 for (i = 0;i < nummodels;i++)
641 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
643 SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
644 Mod_LoadModel(mod, true, true);
645 SCR_PopLoadingScreen(false);
647 SCR_PopLoadingScreen(false);
650 unsigned char *mod_base;
653 //=============================================================================
660 static void Mod_Print(void)
663 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
666 Con_Print("Loaded models:\n");
667 for (i = 0;i < nummodels;i++)
669 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
671 if (mod->brush.numsubmodels)
672 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
674 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
684 static void Mod_Precache(void)
687 Mod_ForName(Cmd_Argv(1), false, true, Cmd_Argv(1)[0] == '*' ? cl.model_name[1] : NULL);
689 Con_Print("usage: modelprecache <filename>\n");
692 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
696 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
697 memset(used, 0, numvertices);
698 for (i = 0;i < numelements;i++)
699 used[elements[i]] = 1;
700 for (i = 0, count = 0;i < numvertices;i++)
701 remapvertices[i] = used[i] ? count++ : -1;
707 // fast way, using an edge hash
708 #define TRIANGLEEDGEHASH 8192
709 void Mod_BuildTriangleNeighbors(int *neighbors, const int *elements, int numtriangles)
711 int i, j, p, e1, e2, *n, hashindex, count, match;
713 typedef struct edgehashentry_s
715 struct edgehashentry_s *next;
720 static edgehashentry_t **edgehash;
721 edgehashentry_t *edgehashentries, *hash;
724 edgehash = (edgehashentry_t **)Mem_Alloc(tempmempool, TRIANGLEEDGEHASH * sizeof(*edgehash));
725 // if there are too many triangles for the stack array, allocate larger buffer
726 edgehashentries = (edgehashentry_t *)Mem_Alloc(tempmempool, numtriangles * 3 * sizeof(edgehashentry_t));
727 // find neighboring triangles
728 for (i = 0, e = elements, n = neighbors;i < numtriangles;i++, e += 3, n += 3)
730 for (j = 0, p = 2;j < 3;p = j, j++)
734 // this hash index works for both forward and backward edges
735 hashindex = (unsigned int)(e1 + e2) % TRIANGLEEDGEHASH;
736 hash = edgehashentries + i * 3 + j;
737 hash->next = edgehash[hashindex];
738 edgehash[hashindex] = hash;
740 hash->element[0] = e1;
741 hash->element[1] = e2;
744 for (i = 0, e = elements, n = neighbors;i < numtriangles;i++, e += 3, n += 3)
746 for (j = 0, p = 2;j < 3;p = j, j++)
750 // this hash index works for both forward and backward edges
751 hashindex = (unsigned int)(e1 + e2) % TRIANGLEEDGEHASH;
754 for (hash = edgehash[hashindex];hash;hash = hash->next)
756 if (hash->element[0] == e2 && hash->element[1] == e1)
758 if (hash->triangle != i)
759 match = hash->triangle;
762 else if ((hash->element[0] == e1 && hash->element[1] == e2))
765 // detect edges shared by three triangles and make them seams
771 // also send a keepalive here (this can take a while too!)
772 CL_KeepaliveMessage(false);
774 // free the allocated buffer
775 Mem_Free(edgehashentries);
779 // very slow but simple way
780 static int Mod_FindTriangleWithEdge(const int *elements, int numtriangles, int start, int end, int ignore)
785 for (i = 0;i < numtriangles;i++, elements += 3)
787 if ((elements[0] == start && elements[1] == end)
788 || (elements[1] == start && elements[2] == end)
789 || (elements[2] == start && elements[0] == end))
795 else if ((elements[1] == start && elements[0] == end)
796 || (elements[2] == start && elements[1] == end)
797 || (elements[0] == start && elements[2] == end))
800 // detect edges shared by three triangles and make them seams
806 void Mod_BuildTriangleNeighbors(int *neighbors, const int *elements, int numtriangles)
810 for (i = 0, e = elements, n = neighbors;i < numtriangles;i++, e += 3, n += 3)
812 n[0] = Mod_FindTriangleWithEdge(elements, numtriangles, e[1], e[0], i);
813 n[1] = Mod_FindTriangleWithEdge(elements, numtriangles, e[2], e[1], i);
814 n[2] = Mod_FindTriangleWithEdge(elements, numtriangles, e[0], e[2], i);
819 qboolean Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
821 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
823 int invalidintcount = 0, invalidintexample = 0;
824 int invalidshortcount = 0, invalidshortexample = 0;
825 int invalidmismatchcount = 0, invalidmismatchexample = 0;
828 for (i = 0; i < numelements; i++)
830 if (element3i[i] < first || element3i[i] > last)
833 invalidintexample = i;
839 for (i = 0; i < numelements; i++)
841 if (element3s[i] < first || element3s[i] > last)
844 invalidintexample = i;
848 if (element3i && element3s)
850 for (i = 0; i < numelements; i++)
852 if (element3s[i] != element3i[i])
854 invalidmismatchcount++;
855 invalidmismatchexample = i;
859 if (invalidintcount || invalidshortcount || invalidmismatchcount)
861 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, element3i, element3s, filename, fileline);
862 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
863 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
864 Con_Printf(", %i elements mismatch between element3i and element3s (example: element3s[%i] is %i and element3i[i] is %i)", invalidmismatchcount, invalidmismatchexample, element3i ? element3i[invalidmismatchexample] : 0, invalidmismatchexample, element3s ? element3s[invalidmismatchexample] : 0);
865 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
867 // edit the elements to make them safer, as the driver will crash otherwise
869 for (i = 0; i < numelements; i++)
870 if (element3i[i] < first || element3i[i] > last)
871 element3i[i] = first;
873 for (i = 0; i < numelements; i++)
874 if (element3s[i] < first || element3s[i] > last)
875 element3s[i] = first;
876 if (element3i && element3s)
877 for (i = 0; i < numelements; i++)
878 if (element3s[i] != element3i[i])
879 element3s[i] = element3i[i];
886 // warning: this is an expensive function!
887 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
894 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
895 // process each vertex of each triangle and accumulate the results
896 // use area-averaging, to make triangles with a big area have a bigger
897 // weighting on the vertex normal than triangles with a small area
898 // to do so, just add the 'normals' together (the bigger the area
899 // the greater the length of the normal is
901 for (i = 0; i < numtriangles; i++, element += 3)
904 vertex3f + element[0] * 3,
905 vertex3f + element[1] * 3,
906 vertex3f + element[2] * 3,
911 VectorNormalize(areaNormal);
913 for (j = 0;j < 3;j++)
915 vectorNormal = normal3f + element[j] * 3;
916 vectorNormal[0] += areaNormal[0];
917 vectorNormal[1] += areaNormal[1];
918 vectorNormal[2] += areaNormal[2];
921 // and just normalize the accumulated vertex normal in the end
922 vectorNormal = normal3f + 3 * firstvertex;
923 for (i = 0; i < numvertices; i++, vectorNormal += 3)
924 VectorNormalize(vectorNormal);
928 static void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, const float *tc0, const float *tc1, const float *tc2, float *svector3f, float *tvector3f, float *normal3f)
930 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
931 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
932 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
934 // 6 multiply, 9 subtract
935 VectorSubtract(v1, v0, v10);
936 VectorSubtract(v2, v0, v20);
937 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
938 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
939 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
940 // 12 multiply, 10 subtract
941 tc10[1] = tc1[1] - tc0[1];
942 tc20[1] = tc2[1] - tc0[1];
943 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
944 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
945 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
946 tc10[0] = tc1[0] - tc0[0];
947 tc20[0] = tc2[0] - tc0[0];
948 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
949 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
950 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
951 // 12 multiply, 4 add, 6 subtract
952 f = DotProduct(svector3f, normal3f);
953 svector3f[0] -= f * normal3f[0];
954 svector3f[1] -= f * normal3f[1];
955 svector3f[2] -= f * normal3f[2];
956 f = DotProduct(tvector3f, normal3f);
957 tvector3f[0] -= f * normal3f[0];
958 tvector3f[1] -= f * normal3f[1];
959 tvector3f[2] -= f * normal3f[2];
960 // if texture is mapped the wrong way (counterclockwise), the tangents
961 // have to be flipped, this is detected by calculating a normal from the
962 // two tangents, and seeing if it is opposite the surface normal
963 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
964 CrossProduct(tvector3f, svector3f, tangentcross);
965 if (DotProduct(tangentcross, normal3f) < 0)
967 VectorNegate(svector3f, svector3f);
968 VectorNegate(tvector3f, tvector3f);
973 // warning: this is a very expensive function!
974 void Mod_BuildTextureVectorsFromNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const float *texcoord2f, const float *normal3f, const int *elements, float *svector3f, float *tvector3f, qboolean areaweighting)
977 float sdir[3], tdir[3], normal[3], *svec, *tvec;
978 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
979 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
982 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
983 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
984 // process each vertex of each triangle and accumulate the results
985 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
987 v0 = vertex3f + e[0] * 3;
988 v1 = vertex3f + e[1] * 3;
989 v2 = vertex3f + e[2] * 3;
990 tc0 = texcoord2f + e[0] * 2;
991 tc1 = texcoord2f + e[1] * 2;
992 tc2 = texcoord2f + e[2] * 2;
994 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
995 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
997 // calculate the edge directions and surface normal
998 // 6 multiply, 9 subtract
999 VectorSubtract(v1, v0, v10);
1000 VectorSubtract(v2, v0, v20);
1001 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
1002 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
1003 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
1005 // calculate the tangents
1006 // 12 multiply, 10 subtract
1007 tc10[1] = tc1[1] - tc0[1];
1008 tc20[1] = tc2[1] - tc0[1];
1009 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
1010 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
1011 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
1012 tc10[0] = tc1[0] - tc0[0];
1013 tc20[0] = tc2[0] - tc0[0];
1014 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
1015 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
1016 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
1018 // if texture is mapped the wrong way (counterclockwise), the tangents
1019 // have to be flipped, this is detected by calculating a normal from the
1020 // two tangents, and seeing if it is opposite the surface normal
1021 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
1022 CrossProduct(tdir, sdir, tangentcross);
1023 if (DotProduct(tangentcross, normal) < 0)
1025 VectorNegate(sdir, sdir);
1026 VectorNegate(tdir, tdir);
1031 VectorNormalize(sdir);
1032 VectorNormalize(tdir);
1034 for (i = 0;i < 3;i++)
1036 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
1037 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
1040 // make the tangents completely perpendicular to the surface normal, and
1041 // then normalize them
1042 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
1043 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
1045 f = -DotProduct(svec, n);
1046 VectorMA(svec, f, n, svec);
1047 VectorNormalize(svec);
1048 f = -DotProduct(tvec, n);
1049 VectorMA(tvec, f, n, tvec);
1050 VectorNormalize(tvec);
1054 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors, qboolean neighbors)
1056 unsigned char *data;
1057 data = (unsigned char *)Mem_Alloc(mempool, numvertices * (3 + 3 + 3 + 3 + 2 + 2 + (vertexcolors ? 4 : 0)) * sizeof(float) + numvertices * (lightmapoffsets ? 1 : 0) * sizeof(int) + numtriangles * (3 + (neighbors ? 3 : 0)) * sizeof(int) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0));
1058 loadmodel->surfmesh.num_vertices = numvertices;
1059 loadmodel->surfmesh.num_triangles = numtriangles;
1060 if (loadmodel->surfmesh.num_vertices)
1062 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1063 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1064 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1065 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1066 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
1067 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
1069 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
1070 if (lightmapoffsets)
1071 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
1073 if (loadmodel->surfmesh.num_triangles)
1075 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
1077 loadmodel->surfmesh.data_neighbor3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
1078 if (loadmodel->surfmesh.num_vertices <= 65536)
1079 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
1083 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, int light, int neighbors, int expandable)
1085 shadowmesh_t *newmesh;
1086 unsigned char *data;
1088 size = sizeof(shadowmesh_t);
1089 size += maxverts * sizeof(float[3]);
1091 size += maxverts * sizeof(float[11]);
1092 size += maxtriangles * sizeof(int[3]);
1093 if (maxverts <= 65536)
1094 size += maxtriangles * sizeof(unsigned short[3]);
1096 size += maxtriangles * sizeof(int[3]);
1098 size += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *) + maxverts * sizeof(shadowmeshvertexhash_t);
1099 data = (unsigned char *)Mem_Alloc(mempool, size);
1100 newmesh = (shadowmesh_t *)data;data += sizeof(*newmesh);
1101 newmesh->map_diffuse = map_diffuse;
1102 newmesh->map_specular = map_specular;
1103 newmesh->map_normal = map_normal;
1104 newmesh->maxverts = maxverts;
1105 newmesh->maxtriangles = maxtriangles;
1106 newmesh->numverts = 0;
1107 newmesh->numtriangles = 0;
1108 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1109 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1111 newmesh->vertex3f = (float *)data;data += maxverts * sizeof(float[3]);
1114 newmesh->svector3f = (float *)data;data += maxverts * sizeof(float[3]);
1115 newmesh->tvector3f = (float *)data;data += maxverts * sizeof(float[3]);
1116 newmesh->normal3f = (float *)data;data += maxverts * sizeof(float[3]);
1117 newmesh->texcoord2f = (float *)data;data += maxverts * sizeof(float[2]);
1119 newmesh->element3i = (int *)data;data += maxtriangles * sizeof(int[3]);
1122 newmesh->neighbor3i = (int *)data;data += maxtriangles * sizeof(int[3]);
1126 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)data;data += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *);
1127 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)data;data += maxverts * sizeof(shadowmeshvertexhash_t);
1129 if (maxverts <= 65536)
1130 newmesh->element3s = (unsigned short *)data;data += maxtriangles * sizeof(unsigned short[3]);
1134 shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh, int light, int neighbors)
1136 shadowmesh_t *newmesh;
1137 newmesh = Mod_ShadowMesh_Alloc(mempool, oldmesh->numverts, oldmesh->numtriangles, oldmesh->map_diffuse, oldmesh->map_specular, oldmesh->map_normal, light, neighbors, false);
1138 newmesh->numverts = oldmesh->numverts;
1139 newmesh->numtriangles = oldmesh->numtriangles;
1140 memcpy(newmesh->sideoffsets, oldmesh->sideoffsets, sizeof(oldmesh->sideoffsets));
1141 memcpy(newmesh->sidetotals, oldmesh->sidetotals, sizeof(oldmesh->sidetotals));
1143 memcpy(newmesh->vertex3f, oldmesh->vertex3f, oldmesh->numverts * sizeof(float[3]));
1144 if (newmesh->svector3f && oldmesh->svector3f)
1146 memcpy(newmesh->svector3f, oldmesh->svector3f, oldmesh->numverts * sizeof(float[3]));
1147 memcpy(newmesh->tvector3f, oldmesh->tvector3f, oldmesh->numverts * sizeof(float[3]));
1148 memcpy(newmesh->normal3f, oldmesh->normal3f, oldmesh->numverts * sizeof(float[3]));
1149 memcpy(newmesh->texcoord2f, oldmesh->texcoord2f, oldmesh->numverts * sizeof(float[2]));
1151 memcpy(newmesh->element3i, oldmesh->element3i, oldmesh->numtriangles * sizeof(int[3]));
1152 if (newmesh->neighbor3i && oldmesh->neighbor3i)
1153 memcpy(newmesh->neighbor3i, oldmesh->neighbor3i, oldmesh->numtriangles * sizeof(int[3]));
1157 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, float *vertex14f)
1159 int hashindex, vnum;
1160 shadowmeshvertexhash_t *hash;
1161 // this uses prime numbers intentionally
1162 hashindex = (unsigned int) (vertex14f[0] * 2003 + vertex14f[1] * 4001 + vertex14f[2] * 7919) % SHADOWMESHVERTEXHASH;
1163 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1165 vnum = (hash - mesh->vertexhashentries);
1166 if ((mesh->vertex3f == NULL || (mesh->vertex3f[vnum * 3 + 0] == vertex14f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex14f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex14f[2]))
1167 && (mesh->svector3f == NULL || (mesh->svector3f[vnum * 3 + 0] == vertex14f[3] && mesh->svector3f[vnum * 3 + 1] == vertex14f[4] && mesh->svector3f[vnum * 3 + 2] == vertex14f[5]))
1168 && (mesh->tvector3f == NULL || (mesh->tvector3f[vnum * 3 + 0] == vertex14f[6] && mesh->tvector3f[vnum * 3 + 1] == vertex14f[7] && mesh->tvector3f[vnum * 3 + 2] == vertex14f[8]))
1169 && (mesh->normal3f == NULL || (mesh->normal3f[vnum * 3 + 0] == vertex14f[9] && mesh->normal3f[vnum * 3 + 1] == vertex14f[10] && mesh->normal3f[vnum * 3 + 2] == vertex14f[11]))
1170 && (mesh->texcoord2f == NULL || (mesh->texcoord2f[vnum * 2 + 0] == vertex14f[12] && mesh->texcoord2f[vnum * 2 + 1] == vertex14f[13])))
1171 return hash - mesh->vertexhashentries;
1173 vnum = mesh->numverts++;
1174 hash = mesh->vertexhashentries + vnum;
1175 hash->next = mesh->vertexhashtable[hashindex];
1176 mesh->vertexhashtable[hashindex] = hash;
1177 if (mesh->vertex3f) {mesh->vertex3f[vnum * 3 + 0] = vertex14f[0];mesh->vertex3f[vnum * 3 + 1] = vertex14f[1];mesh->vertex3f[vnum * 3 + 2] = vertex14f[2];}
1178 if (mesh->svector3f) {mesh->svector3f[vnum * 3 + 0] = vertex14f[3];mesh->svector3f[vnum * 3 + 1] = vertex14f[4];mesh->svector3f[vnum * 3 + 2] = vertex14f[5];}
1179 if (mesh->tvector3f) {mesh->tvector3f[vnum * 3 + 0] = vertex14f[6];mesh->tvector3f[vnum * 3 + 1] = vertex14f[7];mesh->tvector3f[vnum * 3 + 2] = vertex14f[8];}
1180 if (mesh->normal3f) {mesh->normal3f[vnum * 3 + 0] = vertex14f[9];mesh->normal3f[vnum * 3 + 1] = vertex14f[10];mesh->normal3f[vnum * 3 + 2] = vertex14f[11];}
1181 if (mesh->texcoord2f) {mesh->texcoord2f[vnum * 2 + 0] = vertex14f[12];mesh->texcoord2f[vnum * 2 + 1] = vertex14f[13];}
1185 void Mod_ShadowMesh_AddTriangle(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, float *vertex14f)
1187 if (mesh->numtriangles == 0)
1189 // set the properties on this empty mesh to be more favorable...
1190 // (note: this case only occurs for the first triangle added to a new mesh chain)
1191 mesh->map_diffuse = map_diffuse;
1192 mesh->map_specular = map_specular;
1193 mesh->map_normal = map_normal;
1195 while (mesh->map_diffuse != map_diffuse || mesh->map_specular != map_specular || mesh->map_normal != map_normal || mesh->numverts + 3 > mesh->maxverts || mesh->numtriangles + 1 > mesh->maxtriangles)
1197 if (mesh->next == NULL)
1198 mesh->next = Mod_ShadowMesh_Alloc(mempool, max(mesh->maxverts, 300), max(mesh->maxtriangles, 100), map_diffuse, map_specular, map_normal, mesh->svector3f != NULL, mesh->neighbor3i != NULL, true);
1201 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 0);
1202 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 1);
1203 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 2);
1204 mesh->numtriangles++;
1207 void Mod_ShadowMesh_AddMesh(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, int numtris, const int *element3i)
1210 float vbuf[3*14], *v;
1211 memset(vbuf, 0, sizeof(vbuf));
1212 for (i = 0;i < numtris;i++)
1214 for (j = 0, v = vbuf;j < 3;j++, v += 14)
1219 v[0] = vertex3f[e * 3 + 0];
1220 v[1] = vertex3f[e * 3 + 1];
1221 v[2] = vertex3f[e * 3 + 2];
1225 v[3] = svector3f[e * 3 + 0];
1226 v[4] = svector3f[e * 3 + 1];
1227 v[5] = svector3f[e * 3 + 2];
1231 v[6] = tvector3f[e * 3 + 0];
1232 v[7] = tvector3f[e * 3 + 1];
1233 v[8] = tvector3f[e * 3 + 2];
1237 v[9] = normal3f[e * 3 + 0];
1238 v[10] = normal3f[e * 3 + 1];
1239 v[11] = normal3f[e * 3 + 2];
1243 v[12] = texcoord2f[e * 2 + 0];
1244 v[13] = texcoord2f[e * 2 + 1];
1247 Mod_ShadowMesh_AddTriangle(mempool, mesh, map_diffuse, map_specular, map_normal, vbuf);
1250 // the triangle calculation can take a while, so let's do a keepalive here
1251 CL_KeepaliveMessage(false);
1254 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, int light, int neighbors, int expandable)
1256 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1257 CL_KeepaliveMessage(false);
1259 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles, map_diffuse, map_specular, map_normal, light, neighbors, expandable);
1262 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh, mempool_t *mempool)
1264 if (!mesh->numverts)
1267 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1268 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1270 // build r_vertexmesh_t array
1271 // (compressed interleaved array for D3D)
1272 if (!mesh->vertexmesh && mesh->texcoord2f && vid.useinterleavedarrays)
1275 int numvertices = mesh->numverts;
1276 r_vertexmesh_t *vertexmesh;
1277 mesh->vertexmesh = vertexmesh = (r_vertexmesh_t*)Mem_Alloc(mempool, numvertices * sizeof(*mesh->vertexmesh));
1278 for (vertexindex = 0;vertexindex < numvertices;vertexindex++, vertexmesh++)
1280 VectorCopy(mesh->vertex3f + 3*vertexindex, vertexmesh->vertex3f);
1281 VectorScale(mesh->svector3f + 3*vertexindex, 1.0f, vertexmesh->svector3f);
1282 VectorScale(mesh->tvector3f + 3*vertexindex, 1.0f, vertexmesh->tvector3f);
1283 VectorScale(mesh->normal3f + 3*vertexindex, 1.0f, vertexmesh->normal3f);
1284 Vector2Copy(mesh->texcoord2f + 2*vertexindex, vertexmesh->texcoordtexture2f);
1288 // upload short indices as a buffer
1289 if (mesh->element3s && !mesh->element3s_indexbuffer)
1290 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1292 // upload int indices as a buffer
1293 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1294 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1296 // vertex buffer is several arrays and we put them in the same buffer
1298 // is this wise? the texcoordtexture2f array is used with dynamic
1299 // vertex/svector/tvector/normal when rendering animated models, on the
1300 // other hand animated models don't use a lot of vertices anyway...
1301 if (!mesh->vbo_vertexbuffer && !vid.useinterleavedarrays)
1306 mesh->vbooffset_vertexmesh = size;if (mesh->vertexmesh ) size += mesh->numverts * sizeof(r_vertexmesh_t);
1307 mesh->vbooffset_vertex3f = size;if (mesh->vertex3f ) size += mesh->numverts * sizeof(float[3]);
1308 mesh->vbooffset_svector3f = size;if (mesh->svector3f ) size += mesh->numverts * sizeof(float[3]);
1309 mesh->vbooffset_tvector3f = size;if (mesh->tvector3f ) size += mesh->numverts * sizeof(float[3]);
1310 mesh->vbooffset_normal3f = size;if (mesh->normal3f ) size += mesh->numverts * sizeof(float[3]);
1311 mesh->vbooffset_texcoord2f = size;if (mesh->texcoord2f ) size += mesh->numverts * sizeof(float[2]);
1312 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
1313 if (mesh->vertexmesh ) memcpy(mem + mesh->vbooffset_vertexmesh , mesh->vertexmesh , mesh->numverts * sizeof(r_vertexmesh_t));
1314 if (mesh->vertex3f ) memcpy(mem + mesh->vbooffset_vertex3f , mesh->vertex3f , mesh->numverts * sizeof(float[3]));
1315 if (mesh->svector3f ) memcpy(mem + mesh->vbooffset_svector3f , mesh->svector3f , mesh->numverts * sizeof(float[3]));
1316 if (mesh->tvector3f ) memcpy(mem + mesh->vbooffset_tvector3f , mesh->tvector3f , mesh->numverts * sizeof(float[3]));
1317 if (mesh->normal3f ) memcpy(mem + mesh->vbooffset_normal3f , mesh->normal3f , mesh->numverts * sizeof(float[3]));
1318 if (mesh->texcoord2f ) memcpy(mem + mesh->vbooffset_texcoord2f , mesh->texcoord2f , mesh->numverts * sizeof(float[2]));
1319 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, "shadowmesh", false, false, false, false);
1324 shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, qboolean light, qboolean neighbors, qboolean createvbo)
1326 shadowmesh_t *mesh, *newmesh, *nextmesh;
1327 // reallocate meshs to conserve space
1328 for (mesh = firstmesh, firstmesh = NULL;mesh;mesh = nextmesh)
1330 nextmesh = mesh->next;
1331 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1333 newmesh = Mod_ShadowMesh_ReAlloc(mempool, mesh, light, neighbors);
1334 newmesh->next = firstmesh;
1335 firstmesh = newmesh;
1336 if (newmesh->element3s)
1339 for (i = 0;i < newmesh->numtriangles*3;i++)
1340 newmesh->element3s[i] = newmesh->element3i[i];
1343 Mod_ShadowMesh_CreateVBOs(newmesh, mempool);
1348 // this can take a while, so let's do a keepalive here
1349 CL_KeepaliveMessage(false);
1354 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *firstmesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1358 vec3_t nmins, nmaxs, ncenter, temp;
1359 float nradius2, dist2, *v;
1363 for (mesh = firstmesh;mesh;mesh = mesh->next)
1365 if (mesh == firstmesh)
1367 VectorCopy(mesh->vertex3f, nmins);
1368 VectorCopy(mesh->vertex3f, nmaxs);
1370 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1372 if (nmins[0] > v[0]) nmins[0] = v[0];if (nmaxs[0] < v[0]) nmaxs[0] = v[0];
1373 if (nmins[1] > v[1]) nmins[1] = v[1];if (nmaxs[1] < v[1]) nmaxs[1] = v[1];
1374 if (nmins[2] > v[2]) nmins[2] = v[2];if (nmaxs[2] < v[2]) nmaxs[2] = v[2];
1377 // calculate center and radius
1378 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1379 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1380 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1382 for (mesh = firstmesh;mesh;mesh = mesh->next)
1384 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1386 VectorSubtract(v, ncenter, temp);
1387 dist2 = DotProduct(temp, temp);
1388 if (nradius2 < dist2)
1394 VectorCopy(nmins, mins);
1396 VectorCopy(nmaxs, maxs);
1398 VectorCopy(ncenter, center);
1400 *radius = sqrt(nradius2);
1403 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1405 shadowmesh_t *nextmesh;
1406 for (;mesh;mesh = nextmesh)
1408 if (mesh->element3i_indexbuffer)
1409 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1410 if (mesh->element3s_indexbuffer)
1411 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1412 if (mesh->vbo_vertexbuffer)
1413 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1414 nextmesh = mesh->next;
1419 void Mod_CreateCollisionMesh(dp_model_t *mod)
1421 int k, numcollisionmeshtriangles;
1422 qboolean usesinglecollisionmesh = false;
1423 const msurface_t *surface = NULL;
1425 mempool_t *mempool = mod->mempool;
1426 if (!mempool && mod->brush.parentmodel)
1427 mempool = mod->brush.parentmodel->mempool;
1428 // make a single combined collision mesh for physics engine use
1429 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1430 numcollisionmeshtriangles = 0;
1431 for (k = 0;k < mod->nummodelsurfaces;k++)
1433 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1434 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1436 usesinglecollisionmesh = true;
1437 numcollisionmeshtriangles = surface->num_triangles;
1440 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1442 numcollisionmeshtriangles += surface->num_triangles;
1444 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles, NULL, NULL, NULL, false, false, true);
1445 if (usesinglecollisionmesh)
1446 Mod_ShadowMesh_AddMesh(mempool, mod->brush.collisionmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1449 for (k = 0;k < mod->nummodelsurfaces;k++)
1451 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1452 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1454 Mod_ShadowMesh_AddMesh(mempool, mod->brush.collisionmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1457 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mempool, mod->brush.collisionmesh, false, false, false);
1461 static void Mod_GetTerrainVertex3fTexCoord2fFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
1466 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1467 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1470 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1471 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1472 texcoord2f[0] = tc[0];
1473 texcoord2f[1] = tc[1];
1476 static void Mod_GetTerrainVertexFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *svector3f, float *tvector3f, float *normal3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
1478 float vup[3], vdown[3], vleft[3], vright[3];
1479 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1480 float sv[3], tv[3], nl[3];
1481 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1482 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1483 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1484 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1485 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1486 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1487 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1488 VectorAdd(svector3f, sv, svector3f);
1489 VectorAdd(tvector3f, tv, tvector3f);
1490 VectorAdd(normal3f, nl, normal3f);
1491 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1492 VectorAdd(svector3f, sv, svector3f);
1493 VectorAdd(tvector3f, tv, tvector3f);
1494 VectorAdd(normal3f, nl, normal3f);
1495 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1496 VectorAdd(svector3f, sv, svector3f);
1497 VectorAdd(tvector3f, tv, tvector3f);
1498 VectorAdd(normal3f, nl, normal3f);
1501 static void Mod_ConstructTerrainPatchFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int x1, int y1, int width, int height, int *element3i, int *neighbor3i, float *vertex3f, float *svector3f, float *tvector3f, float *normal3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
1503 int x, y, ix, iy, *e;
1505 for (y = 0;y < height;y++)
1507 for (x = 0;x < width;x++)
1509 e[0] = (y + 1) * (width + 1) + (x + 0);
1510 e[1] = (y + 0) * (width + 1) + (x + 0);
1511 e[2] = (y + 1) * (width + 1) + (x + 1);
1512 e[3] = (y + 0) * (width + 1) + (x + 0);
1513 e[4] = (y + 0) * (width + 1) + (x + 1);
1514 e[5] = (y + 1) * (width + 1) + (x + 1);
1518 Mod_BuildTriangleNeighbors(neighbor3i, element3i, width*height*2);
1519 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1520 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1521 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1526 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1530 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1531 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1532 float viewvector[3];
1533 unsigned int firstvertex;
1536 if (chunkwidth < 2 || chunkheight < 2)
1538 VectorSet(mins, model->terrain.mins[0] + x * stepsize * model->terrain.scale[0], model->terrain.mins[1] + y * stepsize * model->terrain.scale[1], model->terrain.mins[2]);
1539 VectorSet(maxs, model->terrain.mins[0] + (x+1) * stepsize * model->terrain.scale[0], model->terrain.mins[1] + (y+1) * stepsize * model->terrain.scale[1], model->terrain.maxs[2]);
1540 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1541 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1542 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1543 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1545 // too close for this stepsize, emit as 4 chunks instead
1547 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1548 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1549 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1550 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1553 // emit the geometry at stepsize into our vertex buffer / index buffer
1554 // we add two columns and two rows for skirt
1555 outwidth = chunkwidth+2;
1556 outheight = chunkheight+2;
1557 outwidth2 = outwidth-1;
1558 outheight2 = outheight-1;
1559 outwidth3 = outwidth+1;
1560 outheight3 = outheight+1;
1561 firstvertex = numvertices;
1562 e = model->terrain.element3i + numtriangles;
1563 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1564 v = model->terrain.vertex3f + numvertices;
1565 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1566 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1567 for (ty = 0;ty < outheight;ty++)
1569 for (tx = 0;tx < outwidth;tx++)
1571 *e++ = firstvertex + (ty )*outwidth3+(tx );
1572 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1573 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1574 *e++ = firstvertex + (ty )*outwidth3+(tx );
1575 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1576 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1579 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1580 for (ty = 0;ty <= outheight;ty++)
1582 skirtrow = ty == 0 || ty == outheight;
1583 ry = y+bound(1, ty, outheight)*stepsize;
1584 for (tx = 0;tx <= outwidth;tx++)
1586 skirt = skirtrow || tx == 0 || tx == outwidth;
1587 rx = x+bound(1, tx, outwidth)*stepsize;
1590 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1594 // TODO: emit skirt vertices
1597 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1599 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1600 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1601 Mod_Terrain_BuildChunk(model,
1605 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1608 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1610 offset = bound(0, s[4] - '0', 9);
1611 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1616 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1617 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1618 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1619 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1620 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1621 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1622 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1623 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1624 return offset | Q3WAVEFUNC_NONE;
1627 void Mod_FreeQ3Shaders(void)
1629 Mem_FreePool(&q3shaders_mem);
1632 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
1634 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1635 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1636 q3shader_hash_entry_t* lastEntry = NULL;
1639 if (strcasecmp (entry->shader.name, shader->name) == 0)
1642 if(shader->dpshaderkill)
1644 // killed shader is a redeclarion? we can safely ignore it
1647 else if(entry->shader.dpshaderkill)
1649 // replace the old shader!
1650 // this will skip the entry allocating part
1651 // below and just replace the shader
1656 unsigned char *start, *end, *start2;
1657 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1658 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1659 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1660 if(memcmp(start, start2, end - start))
1661 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1663 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1668 entry = entry->chain;
1670 while (entry != NULL);
1673 if (lastEntry->shader.name[0] != 0)
1676 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1677 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1679 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1680 lastEntry->chain = newEntry;
1681 newEntry->chain = NULL;
1682 lastEntry = newEntry;
1684 /* else: head of chain, in hash entry array */
1687 memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
1690 extern cvar_t mod_noshader_default_offsetmapping;
1691 extern cvar_t mod_q3shader_default_offsetmapping;
1692 extern cvar_t mod_q3shader_default_offsetmapping_scale;
1693 extern cvar_t mod_q3shader_default_offsetmapping_bias;
1694 extern cvar_t mod_q3shader_default_polygonoffset;
1695 extern cvar_t mod_q3shader_default_polygonfactor;
1696 extern cvar_t mod_q3shader_force_addalpha;
1697 extern cvar_t mod_q3shader_force_terrain_alphaflag;
1698 void Mod_LoadQ3Shaders(void)
1705 q3shaderinfo_t shader;
1706 q3shaderinfo_layer_t *layer;
1708 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1709 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1710 unsigned long custsurfaceflags[256];
1711 int numcustsurfaceflags;
1712 qboolean dpshaderkill;
1714 Mod_FreeQ3Shaders();
1716 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1717 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1718 sizeof (q3shader_data_t));
1719 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1720 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1721 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1722 q3shaders_mem, sizeof (char**), 256);
1724 // parse custinfoparms.txt
1725 numcustsurfaceflags = 0;
1726 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1728 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1729 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1732 while (COM_ParseToken_QuakeC(&text, false))
1733 if (!strcasecmp(com_token, "}"))
1735 // custom surfaceflags section
1736 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1737 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1740 while(COM_ParseToken_QuakeC(&text, false))
1742 if (!strcasecmp(com_token, "}"))
1744 // register surfaceflag
1745 if (numcustsurfaceflags >= 256)
1747 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1751 j = (int)strlen(com_token)+1;
1752 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1753 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1755 if (COM_ParseToken_QuakeC(&text, false))
1756 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1758 custsurfaceflags[numcustsurfaceflags] = 0;
1759 numcustsurfaceflags++;
1767 search = FS_Search("scripts/*.shader", true, false);
1770 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1772 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1775 while (COM_ParseToken_QuakeC(&text, false))
1777 memset (&shader, 0, sizeof(shader));
1779 shader.surfaceparms = 0;
1780 shader.surfaceflags = 0;
1781 shader.textureflags = 0;
1782 shader.numlayers = 0;
1783 shader.lighting = false;
1784 shader.vertexalpha = false;
1785 shader.textureblendalpha = false;
1786 shader.skyboxname[0] = 0;
1787 shader.deforms[0].deform = Q3DEFORM_NONE;
1788 shader.dpnortlight = false;
1789 shader.dpshadow = false;
1790 shader.dpnoshadow = false;
1791 shader.dpmeshcollisions = false;
1792 shader.dpshaderkill = false;
1793 shader.dpreflectcube[0] = 0;
1794 shader.reflectmin = 0;
1795 shader.reflectmax = 1;
1796 shader.refractfactor = 1;
1797 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1798 shader.reflectfactor = 1;
1799 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1800 shader.r_water_wateralpha = 1;
1801 shader.r_water_waterscroll[0] = 0;
1802 shader.r_water_waterscroll[1] = 0;
1803 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1804 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1805 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1806 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1807 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1808 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1809 shader.specularscalemod = 1;
1810 shader.specularpowermod = 1;
1811 shader.rtlightambient = 0;
1812 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1813 // JUST GREP FOR "specularscalemod = 1".
1815 strlcpy(shader.name, com_token, sizeof(shader.name));
1816 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1818 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1821 while (COM_ParseToken_QuakeC(&text, false))
1823 if (!strcasecmp(com_token, "}"))
1825 if (!strcasecmp(com_token, "{"))
1827 static q3shaderinfo_layer_t dummy;
1828 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1830 layer = shader.layers + shader.numlayers++;
1834 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1835 memset(&dummy, 0, sizeof(dummy));
1838 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1839 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1840 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1841 layer->blendfunc[0] = GL_ONE;
1842 layer->blendfunc[1] = GL_ZERO;
1843 while (COM_ParseToken_QuakeC(&text, false))
1845 if (!strcasecmp(com_token, "}"))
1847 if (!strcasecmp(com_token, "\n"))
1850 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1852 if (j < TEXTURE_MAXFRAMES + 4)
1854 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1855 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1856 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1858 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1859 numparameters = j + 1;
1861 if (!COM_ParseToken_QuakeC(&text, true))
1864 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1865 // parameter[j][0] = 0;
1866 if (developer_insane.integer)
1868 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1869 for (j = 0;j < numparameters;j++)
1870 Con_DPrintf(" %s", parameter[j]);
1873 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1875 if (numparameters == 2)
1877 if (!strcasecmp(parameter[1], "add"))
1879 layer->blendfunc[0] = GL_ONE;
1880 layer->blendfunc[1] = GL_ONE;
1882 else if (!strcasecmp(parameter[1], "addalpha"))
1884 layer->blendfunc[0] = GL_SRC_ALPHA;
1885 layer->blendfunc[1] = GL_ONE;
1887 else if (!strcasecmp(parameter[1], "filter"))
1889 layer->blendfunc[0] = GL_DST_COLOR;
1890 layer->blendfunc[1] = GL_ZERO;
1892 else if (!strcasecmp(parameter[1], "blend"))
1894 layer->blendfunc[0] = GL_SRC_ALPHA;
1895 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1898 else if (numparameters == 3)
1901 for (k = 0;k < 2;k++)
1903 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1904 layer->blendfunc[k] = GL_ONE;
1905 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1906 layer->blendfunc[k] = GL_ZERO;
1907 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1908 layer->blendfunc[k] = GL_SRC_COLOR;
1909 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1910 layer->blendfunc[k] = GL_SRC_ALPHA;
1911 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1912 layer->blendfunc[k] = GL_DST_COLOR;
1913 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1914 layer->blendfunc[k] = GL_DST_ALPHA;
1915 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1916 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1917 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1918 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1919 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1920 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1921 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1922 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1924 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1928 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1929 layer->alphatest = true;
1930 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1932 if (!strcasecmp(parameter[0], "clampmap"))
1933 layer->clampmap = true;
1934 layer->numframes = 1;
1935 layer->framerate = 1;
1936 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1937 &q3shader_data->char_ptrs);
1938 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1939 if (!strcasecmp(parameter[1], "$lightmap"))
1940 shader.lighting = true;
1942 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1945 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1946 layer->framerate = atof(parameter[1]);
1947 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1948 for (i = 0;i < layer->numframes;i++)
1949 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1951 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1954 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1955 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1956 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1957 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1958 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1959 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1960 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1961 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1962 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1963 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1964 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1965 else if (!strcasecmp(parameter[1], "wave"))
1967 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1968 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1969 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1970 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1972 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1974 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1977 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1978 layer->alphagen.parms[i] = atof(parameter[i+2]);
1979 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1980 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1981 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1982 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1983 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1984 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1985 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1986 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1987 else if (!strcasecmp(parameter[1], "wave"))
1989 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1990 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1991 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1992 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1994 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1996 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1999 // observed values: tcgen environment
2000 // no other values have been observed in real shaders
2001 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
2002 layer->tcgen.parms[i] = atof(parameter[i+2]);
2003 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
2004 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
2005 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
2006 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
2007 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
2008 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
2010 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
2017 // tcmod stretch sin # # # #
2018 // tcmod stretch triangle # # # #
2019 // tcmod transform # # # # # #
2020 // tcmod turb # # # #
2021 // tcmod turb sin # # # # (this is bogus)
2022 // no other values have been observed in real shaders
2023 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
2024 if (!layer->tcmods[tcmodindex].tcmod)
2026 if (tcmodindex < Q3MAXTCMODS)
2028 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
2029 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
2030 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
2031 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
2032 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
2033 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
2034 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
2035 else if (!strcasecmp(parameter[1], "stretch"))
2037 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
2038 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
2039 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
2040 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
2042 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
2043 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
2044 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
2047 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
2049 // break out a level if it was a closing brace (not using the character here to not confuse vim)
2050 if (!strcasecmp(com_token, "}"))
2053 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2054 shader.lighting = true;
2055 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
2057 if (layer == shader.layers + 0)
2059 // vertex controlled transparency
2060 shader.vertexalpha = true;
2064 // multilayer terrain shader or similar
2065 shader.textureblendalpha = true;
2066 if (mod_q3shader_force_terrain_alphaflag.integer)
2067 shader.layers[0].dptexflags |= TEXF_ALPHA;
2071 if(mod_q3shader_force_addalpha.integer)
2073 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
2074 // this cvar brings back this behaviour
2075 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
2076 layer->blendfunc[0] = GL_SRC_ALPHA;
2079 layer->dptexflags = 0;
2080 if (layer->alphatest)
2081 layer->dptexflags |= TEXF_ALPHA;
2082 switch(layer->blendfunc[0])
2085 case GL_ONE_MINUS_SRC_ALPHA:
2086 layer->dptexflags |= TEXF_ALPHA;
2089 switch(layer->blendfunc[1])
2092 case GL_ONE_MINUS_SRC_ALPHA:
2093 layer->dptexflags |= TEXF_ALPHA;
2096 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
2097 layer->dptexflags |= TEXF_MIPMAP;
2098 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
2099 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
2100 if (layer->clampmap)
2101 layer->dptexflags |= TEXF_CLAMP;
2105 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
2107 if (j < TEXTURE_MAXFRAMES + 4)
2109 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
2110 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
2111 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
2113 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
2114 numparameters = j + 1;
2116 if (!COM_ParseToken_QuakeC(&text, true))
2119 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
2120 // parameter[j][0] = 0;
2121 if (fileindex == 0 && !strcasecmp(com_token, "}"))
2123 if (developer_insane.integer)
2125 Con_DPrintf("%s: ", shader.name);
2126 for (j = 0;j < numparameters;j++)
2127 Con_DPrintf(" %s", parameter[j]);
2130 if (numparameters < 1)
2132 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
2134 if (!strcasecmp(parameter[1], "alphashadow"))
2135 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
2136 else if (!strcasecmp(parameter[1], "areaportal"))
2137 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
2138 else if (!strcasecmp(parameter[1], "botclip"))
2139 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
2140 else if (!strcasecmp(parameter[1], "clusterportal"))
2141 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
2142 else if (!strcasecmp(parameter[1], "detail"))
2143 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
2144 else if (!strcasecmp(parameter[1], "donotenter"))
2145 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
2146 else if (!strcasecmp(parameter[1], "dust"))
2147 shader.surfaceparms |= Q3SURFACEPARM_DUST;
2148 else if (!strcasecmp(parameter[1], "hint"))
2149 shader.surfaceparms |= Q3SURFACEPARM_HINT;
2150 else if (!strcasecmp(parameter[1], "fog"))
2151 shader.surfaceparms |= Q3SURFACEPARM_FOG;
2152 else if (!strcasecmp(parameter[1], "lava"))
2153 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
2154 else if (!strcasecmp(parameter[1], "lightfilter"))
2155 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
2156 else if (!strcasecmp(parameter[1], "lightgrid"))
2157 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
2158 else if (!strcasecmp(parameter[1], "metalsteps"))
2159 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
2160 else if (!strcasecmp(parameter[1], "nodamage"))
2161 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
2162 else if (!strcasecmp(parameter[1], "nodlight"))
2163 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
2164 else if (!strcasecmp(parameter[1], "nodraw"))
2165 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
2166 else if (!strcasecmp(parameter[1], "nodrop"))
2167 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
2168 else if (!strcasecmp(parameter[1], "noimpact"))
2169 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
2170 else if (!strcasecmp(parameter[1], "nolightmap"))
2171 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
2172 else if (!strcasecmp(parameter[1], "nomarks"))
2173 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
2174 else if (!strcasecmp(parameter[1], "nomipmaps"))
2175 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2176 else if (!strcasecmp(parameter[1], "nonsolid"))
2177 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
2178 else if (!strcasecmp(parameter[1], "origin"))
2179 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
2180 else if (!strcasecmp(parameter[1], "playerclip"))
2181 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
2182 else if (!strcasecmp(parameter[1], "sky"))
2183 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2184 else if (!strcasecmp(parameter[1], "slick"))
2185 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
2186 else if (!strcasecmp(parameter[1], "slime"))
2187 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
2188 else if (!strcasecmp(parameter[1], "structural"))
2189 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
2190 else if (!strcasecmp(parameter[1], "trans"))
2191 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
2192 else if (!strcasecmp(parameter[1], "water"))
2193 shader.surfaceparms |= Q3SURFACEPARM_WATER;
2194 else if (!strcasecmp(parameter[1], "pointlight"))
2195 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
2196 else if (!strcasecmp(parameter[1], "antiportal"))
2197 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
2198 else if (!strcasecmp(parameter[1], "skip"))
2199 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
2202 // try custom surfaceparms
2203 for (j = 0; j < numcustsurfaceflags; j++)
2205 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
2207 shader.surfaceflags |= custsurfaceflags[j];
2212 if (j == numcustsurfaceflags)
2213 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
2216 else if (!strcasecmp(parameter[0], "dpshadow"))
2217 shader.dpshadow = true;
2218 else if (!strcasecmp(parameter[0], "dpnoshadow"))
2219 shader.dpnoshadow = true;
2220 else if (!strcasecmp(parameter[0], "dpnortlight"))
2221 shader.dpnortlight = true;
2222 else if (!strcasecmp(parameter[0], "dpreflectcube"))
2223 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
2224 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
2225 shader.dpmeshcollisions = true;
2226 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
2227 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
2229 if (Cvar_VariableValue(parameter[1]) == 0.0f)
2230 shader.dpshaderkill = dpshaderkill;
2232 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
2233 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
2235 const char *op = NULL;
2236 if (numparameters >= 3)
2240 if (Cvar_VariableValue(parameter[1]) != 0.0f)
2241 shader.dpshaderkill = dpshaderkill;
2243 else if (numparameters >= 4 && !strcmp(op, "=="))
2245 if (Cvar_VariableValue(parameter[1]) == atof(parameter[3]))
2246 shader.dpshaderkill = dpshaderkill;
2248 else if (numparameters >= 4 && !strcmp(op, "!="))
2250 if (Cvar_VariableValue(parameter[1]) != atof(parameter[3]))
2251 shader.dpshaderkill = dpshaderkill;
2253 else if (numparameters >= 4 && !strcmp(op, ">"))
2255 if (Cvar_VariableValue(parameter[1]) > atof(parameter[3]))
2256 shader.dpshaderkill = dpshaderkill;
2258 else if (numparameters >= 4 && !strcmp(op, "<"))
2260 if (Cvar_VariableValue(parameter[1]) < atof(parameter[3]))
2261 shader.dpshaderkill = dpshaderkill;
2263 else if (numparameters >= 4 && !strcmp(op, ">="))
2265 if (Cvar_VariableValue(parameter[1]) >= atof(parameter[3]))
2266 shader.dpshaderkill = dpshaderkill;
2268 else if (numparameters >= 4 && !strcmp(op, "<="))
2270 if (Cvar_VariableValue(parameter[1]) <= atof(parameter[3]))
2271 shader.dpshaderkill = dpshaderkill;
2275 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2278 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2280 // some q3 skies don't have the sky parm set
2281 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2282 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2284 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2286 // some q3 skies don't have the sky parm set
2287 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2288 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2289 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2291 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2293 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2294 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2296 else if (!strcasecmp(parameter[0], "nomipmaps"))
2297 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2298 else if (!strcasecmp(parameter[0], "nopicmip"))
2299 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2300 else if (!strcasecmp(parameter[0], "polygonoffset"))
2301 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2302 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2304 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2305 if(numparameters >= 2)
2307 shader.biaspolygonfactor = atof(parameter[1]);
2308 if(numparameters >= 3)
2309 shader.biaspolygonoffset = atof(parameter[2]);
2311 shader.biaspolygonoffset = 0;
2314 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2316 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2317 if (!strcasecmp(parameter[1], "sky"))
2318 shader.transparentsort = TRANSPARENTSORT_SKY;
2319 else if (!strcasecmp(parameter[1], "distance"))
2320 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2321 else if (!strcasecmp(parameter[1], "hud"))
2322 shader.transparentsort = TRANSPARENTSORT_HUD;
2324 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2326 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2328 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2329 shader.refractfactor = atof(parameter[1]);
2330 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2332 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2334 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2335 shader.reflectfactor = atof(parameter[1]);
2336 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2338 else if (!strcasecmp(parameter[0], "dpcamera"))
2340 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2342 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2344 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2345 shader.reflectmin = atof(parameter[1]);
2346 shader.reflectmax = atof(parameter[2]);
2347 shader.refractfactor = atof(parameter[3]);
2348 shader.reflectfactor = atof(parameter[4]);
2349 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2350 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2351 shader.r_water_wateralpha = atof(parameter[11]);
2353 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2355 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2356 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2358 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2360 shader.specularscalemod = atof(parameter[1]);
2362 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2364 shader.specularpowermod = atof(parameter[1]);
2366 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2368 shader.rtlightambient = atof(parameter[1]);
2370 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2372 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2373 shader.offsetmapping = OFFSETMAPPING_OFF;
2374 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2375 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2376 else if (!strcasecmp(parameter[1], "linear"))
2377 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2378 else if (!strcasecmp(parameter[1], "relief"))
2379 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2380 if (numparameters >= 3)
2381 shader.offsetscale = atof(parameter[2]);
2382 if (numparameters >= 5)
2384 if(!strcasecmp(parameter[3], "bias"))
2385 shader.offsetbias = atof(parameter[4]);
2386 else if(!strcasecmp(parameter[3], "match"))
2387 shader.offsetbias = 1.0f - atof(parameter[4]);
2388 else if(!strcasecmp(parameter[3], "match8"))
2389 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2390 else if(!strcasecmp(parameter[3], "match16"))
2391 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2394 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2397 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2398 if (!shader.deforms[deformindex].deform)
2400 if (deformindex < Q3MAXDEFORMS)
2402 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2403 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2404 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2405 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2406 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2407 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2408 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2409 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2410 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2411 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2412 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2413 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2414 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2415 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2416 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2417 else if (!strcasecmp(parameter[1], "wave" ))
2419 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2420 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2421 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2422 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2424 else if (!strcasecmp(parameter[1], "move" ))
2426 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2427 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2428 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2429 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2434 // hide this shader if a cvar said it should be killed
2435 if (shader.dpshaderkill)
2436 shader.numlayers = 0;
2437 // fix up multiple reflection types
2438 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2439 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2441 Q3Shader_AddToHash (&shader);
2445 FS_FreeSearch(search);
2446 // free custinfoparm values
2447 for (j = 0; j < numcustsurfaceflags; j++)
2448 Mem_Free(custsurfaceparmnames[j]);
2451 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
2453 unsigned short hash;
2454 q3shader_hash_entry_t* entry;
2456 Mod_LoadQ3Shaders();
2457 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2458 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2459 while (entry != NULL)
2461 if (strcasecmp (entry->shader.name, name) == 0)
2462 return &entry->shader;
2463 entry = entry->chain;
2468 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2470 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2471 shaderpass->framerate = 0.0f;
2472 shaderpass->numframes = 1;
2473 shaderpass->blendfunc[0] = GL_ONE;
2474 shaderpass->blendfunc[1] = GL_ZERO;
2475 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2476 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2477 shaderpass->alphatest = false;
2478 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2479 shaderpass->skinframes[0] = skinframe;
2483 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2486 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2487 shaderpass->alphatest = layer->alphatest != 0;
2488 shaderpass->framerate = layer->framerate;
2489 shaderpass->numframes = layer->numframes;
2490 shaderpass->blendfunc[0] = layer->blendfunc[0];
2491 shaderpass->blendfunc[1] = layer->blendfunc[1];
2492 shaderpass->rgbgen = layer->rgbgen;
2493 shaderpass->alphagen = layer->alphagen;
2494 shaderpass->tcgen = layer->tcgen;
2495 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2496 shaderpass->tcmods[j] = layer->tcmods[j];
2497 for (j = 0; j < layer->numframes; j++)
2498 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2502 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2504 int texflagsmask, texflagsor;
2505 qboolean success = true;
2506 q3shaderinfo_t *shader;
2509 strlcpy(texture->name, name, sizeof(texture->name));
2510 texture->basealpha = 1.0f;
2511 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2513 // allow disabling of picmip or compression by defaulttexflags
2515 if(!(defaulttexflags & TEXF_PICMIP))
2516 texflagsmask &= ~TEXF_PICMIP;
2517 if(!(defaulttexflags & TEXF_COMPRESS))
2518 texflagsmask &= ~TEXF_COMPRESS;
2520 if(defaulttexflags & TEXF_ISWORLD)
2521 texflagsor |= TEXF_ISWORLD;
2522 if(defaulttexflags & TEXF_ISSPRITE)
2523 texflagsor |= TEXF_ISSPRITE;
2524 // unless later loaded from the shader
2525 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2526 texture->offsetscale = 1;
2527 texture->offsetbias = 0;
2528 texture->specularscalemod = 1;
2529 texture->specularpowermod = 1;
2530 texture->rtlightambient = 0;
2531 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2532 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2533 // JUST GREP FOR "specularscalemod = 1".
2537 if (developer_loading.integer)
2538 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2540 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2542 texture->basematerialflags = MATERIALFLAG_SKY;
2543 if (shader->skyboxname[0] && loadmodel)
2545 // quake3 seems to append a _ to the skybox name, so this must do so as well
2546 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2549 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2550 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2552 texture->basematerialflags = MATERIALFLAG_WALL;
2554 if (shader->layers[0].alphatest)
2555 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2556 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2557 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2558 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2560 texture->biaspolygonoffset += shader->biaspolygonoffset;
2561 texture->biaspolygonfactor += shader->biaspolygonfactor;
2563 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2564 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2565 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2566 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2567 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2568 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2569 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2570 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2571 texture->customblendfunc[0] = GL_ONE;
2572 texture->customblendfunc[1] = GL_ZERO;
2573 texture->transparentsort = shader->transparentsort;
2574 if (shader->numlayers > 0)
2576 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2577 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2579 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2580 * additive GL_ONE GL_ONE
2581 additive weird GL_ONE GL_SRC_ALPHA
2582 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2583 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2584 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2585 brighten GL_DST_COLOR GL_ONE
2586 brighten GL_ONE GL_SRC_COLOR
2587 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2588 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2589 * modulate GL_DST_COLOR GL_ZERO
2590 * modulate GL_ZERO GL_SRC_COLOR
2591 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2592 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2593 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2594 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2595 * no blend GL_ONE GL_ZERO
2596 nothing GL_ZERO GL_ONE
2598 // if not opaque, figure out what blendfunc to use
2599 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2601 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2602 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2603 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2604 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2605 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2606 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2608 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2611 if (!shader->lighting)
2612 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2614 // here be dragons: convert quake3 shaders to material
2615 if (shader->numlayers > 0)
2618 int terrainbackgroundlayer = -1;
2619 int lightmaplayer = -1;
2620 int alphagenspecularlayer = -1;
2621 int rgbgenvertexlayer = -1;
2622 int rgbgendiffuselayer = -1;
2623 int materiallayer = -1;
2624 int endofprelayers = 0;
2625 int firstpostlayer = 0;
2626 int shaderpassindex = 0;
2627 for (i = 0; i < shader->numlayers; i++)
2629 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2631 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2632 rgbgenvertexlayer = i;
2633 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2634 rgbgendiffuselayer = i;
2635 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2636 alphagenspecularlayer = i;
2638 if (shader->numlayers >= 2
2639 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2640 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2641 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2642 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2644 // terrain blend or certain other effects involving alphatest over a regular layer
2645 terrainbackgroundlayer = 0;
2647 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2648 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2650 else if (lightmaplayer == 0)
2652 // ordinary texture but with $lightmap before diffuse
2654 firstpostlayer = lightmaplayer + 2;
2656 else if (lightmaplayer >= 1)
2658 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2659 endofprelayers = lightmaplayer - 1;
2660 materiallayer = lightmaplayer - 1;
2661 firstpostlayer = lightmaplayer + 1;
2663 else if (rgbgenvertexlayer >= 0)
2665 // map models with baked lighting
2666 materiallayer = rgbgenvertexlayer;
2667 endofprelayers = rgbgenvertexlayer;
2668 firstpostlayer = rgbgenvertexlayer + 1;
2669 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2670 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2671 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR;
2673 else if (rgbgendiffuselayer >= 0)
2675 // entity models with dynamic lighting
2676 materiallayer = rgbgendiffuselayer;
2677 endofprelayers = rgbgendiffuselayer;
2678 firstpostlayer = rgbgendiffuselayer + 1;
2679 // player models often have specular as a pass after diffuse - we don't currently make use of that specular texture (would need to meld it into the skinframe)...
2680 if (alphagenspecularlayer >= 0)
2681 firstpostlayer = alphagenspecularlayer + 1;
2685 // special effects shaders - treat first as primary layer and do everything else as post
2690 // convert the main material layer
2691 // FIXME: if alphagenspecularlayer is used, we should pass a specular texture name to R_SkinFrame_LoadExternal and have it load that texture instead of the assumed name for _gloss texture
2692 if (materiallayer >= 0)
2693 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2694 // convert the terrain background blend layer (if any)
2695 if (terrainbackgroundlayer >= 0)
2696 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2697 // convert the prepass layers (if any)
2698 texture->startpreshaderpass = shaderpassindex;
2699 for (i = 0; i < endofprelayers; i++)
2700 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2701 texture->endpreshaderpass = shaderpassindex;
2702 texture->startpostshaderpass = shaderpassindex;
2703 // convert the postpass layers (if any)
2704 for (i = firstpostlayer; i < shader->numlayers; i++)
2705 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2706 texture->startpostshaderpass = shaderpassindex;
2709 if (shader->dpshadow)
2710 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2711 if (shader->dpnoshadow)
2712 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2713 if (shader->dpnortlight)
2714 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2715 if (shader->vertexalpha)
2716 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2717 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2718 texture->reflectmin = shader->reflectmin;
2719 texture->reflectmax = shader->reflectmax;
2720 texture->refractfactor = shader->refractfactor;
2721 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2722 texture->reflectfactor = shader->reflectfactor;
2723 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2724 texture->r_water_wateralpha = shader->r_water_wateralpha;
2725 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2726 texture->offsetmapping = shader->offsetmapping;
2727 texture->offsetscale = shader->offsetscale;
2728 texture->offsetbias = shader->offsetbias;
2729 texture->specularscalemod = shader->specularscalemod;
2730 texture->specularpowermod = shader->specularpowermod;
2731 texture->rtlightambient = shader->rtlightambient;
2732 if (shader->dpreflectcube[0])
2733 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2735 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2736 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2737 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2738 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2739 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2740 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2741 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2742 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2743 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2745 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2746 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2747 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2748 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2749 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2750 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2751 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2752 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2753 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2754 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2755 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2756 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2757 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2758 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2759 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2760 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2761 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2762 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2763 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2764 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2765 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2766 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2767 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2768 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2769 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2770 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2771 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2772 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2773 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2774 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2775 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2776 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2778 texture->surfaceflags = shader->surfaceflags;
2779 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2780 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2781 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2782 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2783 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2784 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2785 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2786 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2787 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2788 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2789 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2790 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2791 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2792 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2793 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2794 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2795 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2796 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2797 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2798 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2799 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2800 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2801 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2802 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2803 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2804 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2805 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2806 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2807 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2808 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2809 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2810 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2812 if (shader->dpmeshcollisions)
2813 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2814 if (shader->dpshaderkill && developer_extra.integer)
2815 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2817 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2819 if (developer_extra.integer)
2820 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2821 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2823 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2825 if (developer_extra.integer)
2826 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2827 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2828 texture->supercontents = SUPERCONTENTS_SOLID;
2832 if (developer_extra.integer)
2833 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2834 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2836 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2837 texture->supercontents = SUPERCONTENTS_SOLID;
2839 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2841 texture->basematerialflags = MATERIALFLAG_SKY;
2842 texture->supercontents = SUPERCONTENTS_SKY;
2846 texture->basematerialflags = defaultmaterialflags;
2847 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2849 if(cls.state == ca_dedicated)
2851 texture->materialshaderpass = NULL;
2856 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2859 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2860 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2861 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2862 if (texture->q2contents)
2863 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2867 if (!success && warnmissing)
2868 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2871 // init the animation variables
2872 texture->currentframe = texture;
2873 texture->currentmaterialflags = texture->basematerialflags;
2874 if (!texture->materialshaderpass)
2875 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2876 if (!texture->materialshaderpass->skinframes[0])
2877 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2878 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2879 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2883 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2885 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2886 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2888 strlcpy(texture->name, name, sizeof(texture->name));
2889 texture->basealpha = 1.0f;
2890 texture->basematerialflags = materialflags;
2891 texture->supercontents = supercontents;
2893 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2894 texture->offsetscale = 1;
2895 texture->offsetbias = 0;
2896 texture->specularscalemod = 1;
2897 texture->specularpowermod = 1;
2898 texture->rtlightambient = 0;
2899 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2900 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2901 // JUST GREP FOR "specularscalemod = 1".
2903 if (developer_extra.integer)
2904 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2906 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2908 // init the animation variables
2909 texture->currentmaterialflags = texture->basematerialflags;
2910 texture->currentframe = texture;
2911 texture->currentskinframe = skinframe;
2912 texture->backgroundcurrentskinframe = NULL;
2915 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2918 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2920 if (texture->shaderpasses[i])
2923 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2924 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2925 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2926 Mem_Free(texture->shaderpasses[i]);
2927 texture->shaderpasses[i] = NULL;
2930 texture->materialshaderpass = NULL;
2931 texture->currentskinframe = NULL;
2932 texture->backgroundcurrentskinframe = NULL;
2935 skinfile_t *Mod_LoadSkinFiles(void)
2937 int i, words, line, wordsoverflow;
2940 skinfile_t *skinfile = NULL, *first = NULL;
2941 skinfileitem_t *skinfileitem;
2942 char word[10][MAX_QPATH];
2947 U_bodyBox,models/players/Legoman/BikerA2.tga
2948 U_RArm,models/players/Legoman/BikerA1.tga
2949 U_LArm,models/players/Legoman/BikerA1.tga
2950 U_armor,common/nodraw
2951 U_sword,common/nodraw
2952 U_shield,common/nodraw
2953 U_homb,common/nodraw
2954 U_backpack,common/nodraw
2955 U_colcha,common/nodraw
2960 memset(word, 0, sizeof(word));
2961 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2963 // If it's the first file we parse
2964 if (skinfile == NULL)
2966 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2971 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2972 skinfile = skinfile->next;
2974 skinfile->next = NULL;
2976 for(line = 0;;line++)
2979 if (!COM_ParseToken_QuakeC(&data, true))
2981 if (!strcmp(com_token, "\n"))
2984 wordsoverflow = false;
2988 strlcpy(word[words++], com_token, sizeof (word[0]));
2990 wordsoverflow = true;
2992 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2995 Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: line with too many statements, skipping\n", loadmodel->name, i, line);
2998 // words is always >= 1
2999 if (!strcmp(word[0], "replace"))
3003 if (developer_loading.integer)
3004 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
3005 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
3006 skinfileitem->next = skinfile->items;
3007 skinfile->items = skinfileitem;
3008 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
3009 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
3012 Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: wrong number of parameters to command \"%s\", see documentation in DP_GFX_SKINFILES extension in dpextensions.qc\n", loadmodel->name, i, line, word[0]);
3014 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
3016 // tag name, like "tag_weapon,"
3017 // not used for anything (not even in Quake3)
3019 else if (words >= 2 && !strcmp(word[1], ","))
3021 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
3022 if (developer_loading.integer)
3023 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
3024 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
3025 skinfileitem->next = skinfile->items;
3026 skinfile->items = skinfileitem;
3027 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
3028 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
3031 Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: does not look like tag or mesh specification, or replace command, see documentation in DP_GFX_SKINFILES extension in dpextensions.qc\n", loadmodel->name, i, line);
3036 loadmodel->numskins = i;
3040 void Mod_FreeSkinFiles(skinfile_t *skinfile)
3043 skinfileitem_t *skinfileitem, *nextitem;
3044 for (;skinfile;skinfile = next)
3046 next = skinfile->next;
3047 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
3049 nextitem = skinfileitem->next;
3050 Mem_Free(skinfileitem);
3056 int Mod_CountSkinFiles(skinfile_t *skinfile)
3059 for (i = 0;skinfile;skinfile = skinfile->next, i++);
3063 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
3066 double isnap = 1.0 / snap;
3067 for (i = 0;i < numvertices*numcomponents;i++)
3068 vertices[i] = floor(vertices[i]*isnap)*snap;
3071 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
3073 int i, outtriangles;
3074 float edgedir1[3], edgedir2[3], temp[3];
3075 // a degenerate triangle is one with no width (thickness, surface area)
3076 // these are characterized by having all 3 points colinear (along a line)
3077 // or having two points identical
3078 // the simplest check is to calculate the triangle's area
3079 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
3081 // calculate first edge
3082 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
3083 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
3084 CrossProduct(edgedir1, edgedir2, temp);
3085 if (VectorLength2(temp) < 0.001f)
3086 continue; // degenerate triangle (no area)
3087 // valid triangle (has area)
3088 VectorCopy(inelement3i, outelement3i);
3092 return outtriangles;
3095 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
3098 int firstvertex, lastvertex;
3099 if (numelements > 0 && elements)
3101 firstvertex = lastvertex = elements[0];
3102 for (i = 1;i < numelements;i++)
3105 firstvertex = min(firstvertex, e);
3106 lastvertex = max(lastvertex, e);
3110 firstvertex = lastvertex = 0;
3111 if (firstvertexpointer)
3112 *firstvertexpointer = firstvertex;
3113 if (lastvertexpointer)
3114 *lastvertexpointer = lastvertex;
3117 void Mod_MakeSortedSurfaces(dp_model_t *mod)
3119 // make an optimal set of texture-sorted batches to draw...
3121 int *firstsurfacefortexture;
3122 int *numsurfacesfortexture;
3123 if (!mod->sortedmodelsurfaces)
3124 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
3125 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
3126 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
3127 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
3128 for (j = 0;j < mod->nummodelsurfaces;j++)
3130 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
3131 t = (int)(surface->texture - mod->data_textures);
3132 numsurfacesfortexture[t]++;
3135 for (t = 0;t < mod->num_textures;t++)
3137 firstsurfacefortexture[t] = j;
3138 j += numsurfacesfortexture[t];
3140 for (j = 0;j < mod->nummodelsurfaces;j++)
3142 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
3143 t = (int)(surface->texture - mod->data_textures);
3144 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
3146 Mem_Free(firstsurfacefortexture);
3147 Mem_Free(numsurfacesfortexture);
3150 void Mod_BuildVBOs(void)
3152 if (!loadmodel->surfmesh.num_vertices)
3155 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
3158 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
3160 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
3162 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
3163 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
3168 // build r_vertexmesh_t array
3169 // (compressed interleaved array for D3D)
3170 if (!loadmodel->surfmesh.data_vertexmesh && vid.useinterleavedarrays)
3173 int numvertices = loadmodel->surfmesh.num_vertices;
3174 r_vertexmesh_t *vertexmesh;
3175 loadmodel->surfmesh.data_vertexmesh = vertexmesh = (r_vertexmesh_t*)Mem_Alloc(loadmodel->mempool, numvertices * sizeof(r_vertexmesh_t));
3176 for (vertexindex = 0;vertexindex < numvertices;vertexindex++, vertexmesh++)
3178 VectorCopy(loadmodel->surfmesh.data_vertex3f + 3*vertexindex, vertexmesh->vertex3f);
3179 VectorScale(loadmodel->surfmesh.data_svector3f + 3*vertexindex, 1.0f, vertexmesh->svector3f);
3180 VectorScale(loadmodel->surfmesh.data_tvector3f + 3*vertexindex, 1.0f, vertexmesh->tvector3f);
3181 VectorScale(loadmodel->surfmesh.data_normal3f + 3*vertexindex, 1.0f, vertexmesh->normal3f);
3182 if (loadmodel->surfmesh.data_lightmapcolor4f)
3183 Vector4Copy(loadmodel->surfmesh.data_lightmapcolor4f + 4*vertexindex, vertexmesh->color4f);
3184 Vector2Copy(loadmodel->surfmesh.data_texcoordtexture2f + 2*vertexindex, vertexmesh->texcoordtexture2f);
3185 if (loadmodel->surfmesh.data_texcoordlightmap2f)
3186 Vector2Scale(loadmodel->surfmesh.data_texcoordlightmap2f + 2*vertexindex, 1.0f, vertexmesh->texcoordlightmap2f);
3187 if (loadmodel->surfmesh.data_skeletalindex4ub)
3188 Vector4Copy(loadmodel->surfmesh.data_skeletalindex4ub + 4*vertexindex, vertexmesh->skeletalindex4ub);
3189 if (loadmodel->surfmesh.data_skeletalweight4ub)
3190 Vector4Copy(loadmodel->surfmesh.data_skeletalweight4ub + 4*vertexindex, vertexmesh->skeletalweight4ub);
3194 // upload short indices as a buffer
3195 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
3196 loadmodel->surfmesh.data_element3s_indexbuffer = R_Mesh_CreateMeshBuffer(loadmodel->surfmesh.data_element3s, loadmodel->surfmesh.num_triangles * sizeof(short[3]), loadmodel->name, true, false, false, true);
3198 // upload int indices as a buffer
3199 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
3200 loadmodel->surfmesh.data_element3i_indexbuffer = R_Mesh_CreateMeshBuffer(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles * sizeof(int[3]), loadmodel->name, true, false, false, false);
3202 // only build a vbo if one has not already been created (this is important for brush models which load specially)
3203 // vertex buffer is several arrays and we put them in the same buffer
3205 // is this wise? the texcoordtexture2f array is used with dynamic
3206 // vertex/svector/tvector/normal when rendering animated models, on the
3207 // other hand animated models don't use a lot of vertices anyway...
3208 if (!loadmodel->surfmesh.vbo_vertexbuffer && !vid.useinterleavedarrays)
3213 loadmodel->surfmesh.vbooffset_vertexmesh = size;if (loadmodel->surfmesh.data_vertexmesh ) size += loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t);
3214 loadmodel->surfmesh.vbooffset_vertex3f = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3215 loadmodel->surfmesh.vbooffset_svector3f = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3216 loadmodel->surfmesh.vbooffset_tvector3f = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3217 loadmodel->surfmesh.vbooffset_normal3f = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3218 loadmodel->surfmesh.vbooffset_texcoordtexture2f = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3219 loadmodel->surfmesh.vbooffset_texcoordlightmap2f = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3220 loadmodel->surfmesh.vbooffset_lightmapcolor4f = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
3221 loadmodel->surfmesh.vbooffset_skeletalindex4ub = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3222 loadmodel->surfmesh.vbooffset_skeletalweight4ub = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3223 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
3224 if (loadmodel->surfmesh.data_vertexmesh ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertexmesh , loadmodel->surfmesh.data_vertexmesh , loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t));
3225 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertex3f , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3226 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_svector3f , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3227 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_tvector3f , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3228 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_normal3f , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3229 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordtexture2f , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3230 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordlightmap2f, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3231 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.vbooffset_lightmapcolor4f , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
3232 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalindex4ub , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3233 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalweight4ub , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3234 loadmodel->surfmesh.vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3239 extern cvar_t mod_obj_orientation;
3240 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3242 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3244 const char *texname;
3246 const float *v, *vn, *vt;
3248 size_t outbufferpos = 0;
3249 size_t outbuffermax = 0x100000;
3250 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3251 const msurface_t *surface;
3252 const int maxtextures = 256;
3253 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3254 dp_model_t *submodel;
3256 // construct the mtllib file
3257 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3260 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3263 countvertices += surface->num_vertices;
3264 countfaces += surface->num_triangles;
3265 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3266 for (textureindex = 0;textureindex < counttextures;textureindex++)
3267 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3269 if (textureindex < counttextures)
3270 continue; // already wrote this material entry
3271 if (textureindex >= maxtextures)
3272 continue; // just a precaution
3273 textureindex = counttextures++;
3274 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3275 if (outbufferpos >= outbuffermax >> 1)
3278 oldbuffer = outbuffer;
3279 outbuffer = (char *) Z_Malloc(outbuffermax);
3280 memcpy(outbuffer, oldbuffer, outbufferpos);
3283 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "newmtl %s\nNs 96.078431\nKa 0 0 0\nKd 0.64 0.64 0.64\nKs 0.5 0.5 0.5\nNi 1\nd 1\nillum 2\nmap_Kd %s%s\n\n", texname, texname, strstr(texname, ".tga") ? "" : ".tga");
3288 // write the mtllib file
3289 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3291 // construct the obj file
3293 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# model exported from %s by darkplaces engine\n# %i vertices, %i faces, %i surfaces\nmtllib %s\n", originalfilename, countvertices, countfaces, countsurfaces, mtlfilename);
3297 for (vertexindex = 0, v = model->surfmesh.data_vertex3f, vn = model->surfmesh.data_normal3f, vt = model->surfmesh.data_texcoordtexture2f;vertexindex < model->surfmesh.num_vertices;vertexindex++, v += 3, vn += 3, vt += 2)
3299 if (outbufferpos >= outbuffermax >> 1)
3302 oldbuffer = outbuffer;
3303 outbuffer = (char *) Z_Malloc(outbuffermax);
3304 memcpy(outbuffer, oldbuffer, outbufferpos);
3307 if(mod_obj_orientation.integer)
3308 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "v %f %f %f\nvn %f %f %f\nvt %f %f\n", v[0], v[2], v[1], vn[0], vn[2], vn[1], vt[0], 1-vt[1]);
3310 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "v %f %f %f\nvn %f %f %f\nvt %f %f\n", v[0], v[1], v[2], vn[0], vn[1], vn[2], vt[0], 1-vt[1]);
3315 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3317 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3320 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3321 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3323 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3324 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3327 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3329 if (outbufferpos >= outbuffermax >> 1)
3332 oldbuffer = outbuffer;
3333 outbuffer = (char *) Z_Malloc(outbuffermax);
3334 memcpy(outbuffer, oldbuffer, outbufferpos);
3340 if(mod_obj_orientation.integer)
3341 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", a,a,a,b,b,b,c,c,c);
3343 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", a,a,a,c,c,c,b,b,b);
3350 // write the obj file
3351 FS_WriteFile(filename, outbuffer, outbufferpos);
3355 Z_Free(texturenames);
3358 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3361 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3363 int countnodes = 0, counttriangles = 0, countframes = 0;
3371 size_t outbufferpos = 0;
3372 size_t outbuffermax = 0x100000;
3373 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3374 const msurface_t *surface;
3375 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3378 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3380 if (outbufferpos >= outbuffermax >> 1)
3383 oldbuffer = outbuffer;
3384 outbuffer = (char *) Z_Malloc(outbuffermax);
3385 memcpy(outbuffer, oldbuffer, outbufferpos);
3389 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3393 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3396 for (poseindex = 0;poseindex < numposes;poseindex++)
3399 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3402 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3406 matrix4x4_t posematrix;
3407 if (outbufferpos >= outbuffermax >> 1)
3410 oldbuffer = outbuffer;
3411 outbuffer = (char *) Z_Malloc(outbuffermax);
3412 memcpy(outbuffer, oldbuffer, outbufferpos);
3416 // strangely the smd angles are for a transposed matrix, so we
3417 // have to generate a transposed matrix, then convert that...
3418 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3419 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3420 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3421 if (angles[0] >= 180) angles[0] -= 360;
3422 if (angles[1] >= 180) angles[1] -= 360;
3423 if (angles[2] >= 180) angles[2] -= 360;
3427 float a = DEG2RAD(angles[ROLL]);
3428 float b = DEG2RAD(angles[PITCH]);
3429 float c = DEG2RAD(angles[YAW]);
3430 float cy, sy, cp, sp, cr, sr;
3432 // smd matrix construction, for comparing
3443 test[1][0] = sr*sp*cy+cr*-sy;
3444 test[1][1] = sr*sp*sy+cr*cy;
3446 test[2][0] = (cr*sp*cy+-sr*-sy);
3447 test[2][1] = (cr*sp*sy+-sr*cy);
3449 test[3][0] = pose[9];
3450 test[3][1] = pose[10];
3451 test[3][2] = pose[11];
3454 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f\n", transformindex, mtest[3][0], mtest[3][1], mtest[3][2], DEG2RAD(angles[ROLL]), DEG2RAD(angles[PITCH]), DEG2RAD(angles[YAW]));
3459 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3464 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3467 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3469 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3472 if (outbufferpos >= outbuffermax >> 1)
3475 oldbuffer = outbuffer;
3476 outbuffer = (char *) Z_Malloc(outbuffermax);
3477 memcpy(outbuffer, oldbuffer, outbufferpos);
3480 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3483 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3485 const int index = e[2-cornerindex];
3486 const float *v = model->surfmesh.data_vertex3f + index * 3;
3487 const float *vn = model->surfmesh.data_normal3f + index * 3;
3488 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3489 const int b = model->surfmesh.blends[index];
3490 if (b < model->num_bones)
3491 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f %f %f\n" , b, v[0], v[1], v[2], vn[0], vn[1], vn[2], vt[0], 1 - vt[1]);
3494 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3495 const unsigned char *wi = w->index;
3496 const unsigned char *wf = w->influence;
3497 if (wf[3]) l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f %f %f 4 %i %f %i %f %i %f %i %f\n", wi[0], v[0], v[1], v[2], vn[0], vn[1], vn[2], vt[0], 1 - vt[1], wi[0], wf[0]/255.0f, wi[1], wf[1]/255.0f, wi[2], wf[2]/255.0f, wi[3], wf[3]/255.0f);
3498 else if (wf[2]) l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f %f %f 3 %i %f %i %f %i %f\n" , wi[0], v[0], v[1], v[2], vn[0], vn[1], vn[2], vt[0], 1 - vt[1], wi[0], wf[0]/255.0f, wi[1], wf[1]/255.0f, wi[2], wf[2]/255.0f);
3499 else if (wf[1]) l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f %f %f 2 %i %f %i %f\n" , wi[0], v[0], v[1], v[2], vn[0], vn[1], vn[2], vt[0], 1 - vt[1], wi[0], wf[0]/255.0f, wi[1], wf[1]/255.0f);
3500 else l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i %f %f %f %f %f %f %f %f\n" , wi[0], v[0], v[1], v[2], vn[0], vn[1], vn[2], vt[0], 1 - vt[1]);
3507 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3512 FS_WriteFile(filename, outbuffer, outbufferpos);
3515 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3522 decompiles a model to editable files
3525 static void Mod_Decompile_f(void)
3527 int i, j, k, l, first, count;
3529 char inname[MAX_QPATH];
3530 char outname[MAX_QPATH];
3531 char mtlname[MAX_QPATH];
3532 char basename[MAX_QPATH];
3533 char animname[MAX_QPATH];
3534 char animname2[MAX_QPATH];
3535 char zymtextbuffer[16384];
3536 char dpmtextbuffer[16384];
3537 char framegroupstextbuffer[16384];
3538 int zymtextsize = 0;
3539 int dpmtextsize = 0;
3540 int framegroupstextsize = 0;
3543 if (Cmd_Argc() != 2)
3545 Con_Print("usage: modeldecompile <filename>\n");
3549 strlcpy(inname, Cmd_Argv(1), sizeof(inname));
3550 FS_StripExtension(inname, basename, sizeof(basename));
3552 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3555 Con_Print("No such model\n");
3558 if (mod->brush.submodel)
3560 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3561 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3562 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3565 if (!mod->surfmesh.num_triangles)
3567 Con_Print("Empty model (or sprite)\n");
3571 // export OBJ if possible (not on sprites)
3572 if (mod->surfmesh.num_triangles)
3574 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3575 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3576 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3579 // export SMD if possible (only for skeletal models)
3580 if (mod->surfmesh.num_triangles && mod->num_bones)
3582 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3583 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3584 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3585 if (l > 0) zymtextsize += l;
3586 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3587 if (l > 0) dpmtextsize += l;
3588 for (i = 0;i < mod->numframes;i = j)
3590 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3591 first = mod->animscenes[i].firstframe;
3592 if (mod->animscenes[i].framecount > 1)
3595 count = mod->animscenes[i].framecount;
3601 // check for additional frames with same name
3602 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3603 if(animname[l] < '0' || animname[l] > '9')
3605 if(k > 0 && animname[k-1] == '_')
3608 count = mod->num_poses - first;
3609 for (j = i + 1;j < mod->numframes;j++)
3611 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3612 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3613 if(animname2[l] < '0' || animname2[l] > '9')
3615 if(k > 0 && animname[k-1] == '_')
3618 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3620 count = mod->animscenes[j].firstframe - first;
3624 // if it's only one frame, use the original frame name
3626 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3629 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3630 Mod_Decompile_SMD(mod, outname, first, count, false);
3631 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3633 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3634 if (l > 0) zymtextsize += l;
3636 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3638 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3639 if (l > 0) dpmtextsize += l;
3641 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3643 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3644 if (l > 0) framegroupstextsize += l;
3648 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3650 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3651 if (framegroupstextsize)
3652 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3656 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3659 memset(state, 0, sizeof(*state));
3660 state->width = width;
3661 state->height = height;
3662 state->currentY = 0;
3663 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3664 for (y = 0;y < state->height;y++)
3666 state->rows[y].currentX = 0;
3667 state->rows[y].rowY = -1;
3671 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3674 state->currentY = 0;
3675 for (y = 0;y < state->height;y++)
3677 state->rows[y].currentX = 0;
3678 state->rows[y].rowY = -1;
3682 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3685 Mem_Free(state->rows);
3686 memset(state, 0, sizeof(*state));
3689 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3691 mod_alloclightmap_row_t *row;
3694 row = state->rows + blockheight;
3695 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3697 if (state->currentY + blockheight <= state->height)
3699 // use the current allocation position
3700 row->rowY = state->currentY;
3702 state->currentY += blockheight;
3706 // find another position
3707 for (y = blockheight;y < state->height;y++)
3709 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3711 row = state->rows + y;
3715 if (y == state->height)
3720 *outx = row->currentX;
3721 row->currentX += blockwidth;
3726 typedef struct lightmapsample_s
3730 float *vertex_color;
3731 unsigned char *lm_bgr;
3732 unsigned char *lm_dir;
3736 typedef struct lightmapvertex_s
3741 float texcoordbase[2];
3742 float texcoordlightmap[2];
3743 float lightcolor[4];
3747 typedef struct lightmaptriangle_s
3755 // 2D modelspace coordinates of min corner
3756 // snapped to lightmap grid but not in grid coordinates
3758 // 2D modelspace to lightmap coordinate scale
3766 typedef struct lightmaplight_s
3777 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3779 #define MAX_LIGHTMAPSAMPLES 64
3780 static int mod_generatelightmaps_numoffsets[3];
3781 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3783 static int mod_generatelightmaps_numlights;
3784 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3786 extern cvar_t r_shadow_lightattenuationdividebias;
3787 extern cvar_t r_shadow_lightattenuationlinearscale;
3789 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3794 float relativepoint[3];
3801 float lightorigin[3];
3805 float lightcolor[3];
3807 for (i = 0;i < 5*3;i++)
3809 for (index = 0;;index++)
3811 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3816 lightradius2 = lightradius * lightradius;
3817 VectorSubtract(lightorigin, pos, relativepoint);
3818 dist2 = VectorLength2(relativepoint);
3819 if (dist2 >= lightradius2)
3821 lightiradius = 1.0f / lightradius;
3822 dist = sqrt(dist2) * lightiradius;
3823 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3824 if (intensity <= 0.0f)
3826 if (model && model->TraceLine)
3828 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3829 if (trace.fraction < 1)
3832 // scale down intensity to add to both ambient and diffuse
3833 //intensity *= 0.5f;
3834 VectorNormalize(relativepoint);
3835 VectorScale(lightcolor, intensity, color);
3836 VectorMA(sample , 0.5f , color, sample );
3837 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3838 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3839 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3840 // calculate a weighted average light direction as well
3841 intensity *= VectorLength(color);
3842 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3844 // calculate the direction we'll use to reduce the sample to a directional light source
3845 VectorCopy(sample + 12, dir);
3846 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3847 VectorNormalize(dir);
3848 // extract the diffuse color along the chosen direction and scale it
3849 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3850 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3851 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3852 // subtract some of diffuse from ambient
3853 VectorMA(sample, -0.333f, diffuse, ambient);
3854 // store the normalized lightdir
3855 VectorCopy(dir, lightdir);
3858 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3862 const msurface_t *surface;
3863 const float *vertex3f = model->surfmesh.data_vertex3f;
3864 const int *element3i = model->surfmesh.data_element3i;
3867 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3869 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3871 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3873 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3875 VectorCopy(vertex3f + 3*e[0], v2[0]);
3876 VectorCopy(vertex3f + 3*e[1], v2[1]);
3877 VectorCopy(vertex3f + 3*e[2], v2[2]);
3878 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3883 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3885 int maxnodes = 1<<14;
3886 svbsp_node_t *nodes;
3891 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3892 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3893 VectorCopy(lightinfo->origin, origin);
3894 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3897 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3898 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3899 if (svbsp.ranoutofnodes)
3902 if (maxnodes > 1<<22)
3908 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3913 if (svbsp.numnodes > 0)
3915 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3916 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3917 lightinfo->svbsp = svbsp;
3922 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3926 lightmaplight_t *lightinfo;
3930 mod_generatelightmaps_numlights = 0;
3931 for (index = 0;;index++)
3933 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3937 mod_generatelightmaps_numlights++;
3939 if (mod_generatelightmaps_numlights > 0)
3941 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3942 lightinfo = mod_generatelightmaps_lightinfo;
3943 for (index = 0;;index++)
3945 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3952 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3954 lightinfo->iradius = 1.0f / lightinfo->radius;
3955 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3956 // TODO: compute svbsp
3957 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3961 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3964 if (mod_generatelightmaps_lightinfo)
3966 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3967 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3968 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3969 Mem_Free(mod_generatelightmaps_lightinfo);
3971 mod_generatelightmaps_lightinfo = NULL;
3972 mod_generatelightmaps_numlights = 0;
3975 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3977 const svbsp_node_t *node;
3978 const svbsp_node_t *nodes = svbsp->nodes;
3983 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3985 return num == -1; // true if empty, false if solid (shadowed)
3988 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3991 float relativepoint[3];
4000 const lightmaplight_t *lightinfo;
4002 for (i = 0;i < 5*3;i++)
4004 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
4006 //R_SampleRTLights(pos, sample, numoffsets, offsets);
4007 VectorSubtract(lightinfo->origin, pos, relativepoint);
4008 // don't accept light from behind a surface, it causes bad shading
4009 if (normal && DotProduct(relativepoint, normal) <= 0)
4011 dist2 = VectorLength2(relativepoint);
4012 if (dist2 >= lightinfo->radius2)
4014 dist = sqrt(dist2) * lightinfo->iradius;
4015 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
4018 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
4022 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
4024 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
4026 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
4029 // for light grid we'd better check visibility of the offset point
4030 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
4031 if (trace.fraction < 1)
4032 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
4035 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
4040 // scale intensity according to how many rays succeeded
4041 // we know one test is valid, half of the rest will fail...
4042 //if (normal && tests > 1)
4043 // intensity *= (tests - 1.0f) / tests;
4044 intensity *= (float)hits / tests;
4046 // scale down intensity to add to both ambient and diffuse
4047 //intensity *= 0.5f;
4048 VectorNormalize(relativepoint);
4049 VectorScale(lightinfo->color, intensity, color);
4050 VectorMA(sample , 0.5f , color, sample );
4051 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
4052 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
4053 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
4054 // calculate a weighted average light direction as well
4055 intensity *= VectorLength(color);
4056 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
4060 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
4066 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
4067 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
4068 VectorCopy(sample + 12, dir);
4069 VectorNormalize(dir);
4070 //VectorAdd(dir, normal, dir);
4071 //VectorNormalize(dir);
4072 f = DotProduct(dir, normal);
4073 f = max(0, f) * 255.0f;
4074 VectorScale(sample, f, color);
4075 //VectorCopy(normal, dir);
4076 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
4077 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
4078 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
4079 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
4081 lm_dir[0] = (unsigned char)dir[2];
4082 lm_dir[1] = (unsigned char)dir[1];
4083 lm_dir[2] = (unsigned char)dir[0];
4087 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
4090 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
4091 VectorCopy(sample, vertex_color);
4094 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
4100 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
4101 // calculate the direction we'll use to reduce the sample to a directional light source
4102 VectorCopy(sample + 12, dir);
4103 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
4104 VectorNormalize(dir);
4105 // extract the diffuse color along the chosen direction and scale it
4106 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
4107 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
4108 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
4109 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
4110 VectorScale(sample, 127.5f, ambient);
4111 VectorMA(ambient, -0.333f, diffuse, ambient);
4112 // encode to the grid format
4113 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
4114 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
4115 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
4116 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
4117 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
4118 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
4119 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
4120 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
4121 else {s->diffusepitch = (unsigned char)(acos(dir[2]) * (127.5f/M_PI));s->diffuseyaw = (unsigned char)(atan2(dir[1], dir[0]) * (127.5f/M_PI));}
4124 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
4129 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
4130 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
4131 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
4132 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
4133 radius[0] = mod_generatelightmaps_lightmapradius.value;
4134 radius[1] = mod_generatelightmaps_vertexradius.value;
4135 radius[2] = mod_generatelightmaps_gridradius.value;
4136 for (i = 0;i < 3;i++)
4138 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
4141 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
4146 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
4148 msurface_t *surface;
4151 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4153 surface = model->data_surfaces + surfaceindex;
4154 surface->lightmaptexture = NULL;
4155 surface->deluxemaptexture = NULL;
4157 if (model->brushq3.data_lightmaps)
4159 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
4160 if (model->brushq3.data_lightmaps[i])
4161 R_FreeTexture(model->brushq3.data_lightmaps[i]);
4162 Mem_Free(model->brushq3.data_lightmaps);
4163 model->brushq3.data_lightmaps = NULL;
4165 if (model->brushq3.data_deluxemaps)
4167 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
4168 if (model->brushq3.data_deluxemaps[i])
4169 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
4170 Mem_Free(model->brushq3.data_deluxemaps);
4171 model->brushq3.data_deluxemaps = NULL;
4175 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
4177 msurface_t *surface;
4183 surfmesh_t oldsurfmesh;
4185 unsigned char *data;
4186 oldsurfmesh = model->surfmesh;
4187 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
4188 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
4190 size += model->surfmesh.num_vertices * sizeof(float[3]);
4191 size += model->surfmesh.num_vertices * sizeof(float[3]);
4192 size += model->surfmesh.num_vertices * sizeof(float[3]);
4193 size += model->surfmesh.num_vertices * sizeof(float[3]);
4194 size += model->surfmesh.num_vertices * sizeof(float[2]);
4195 size += model->surfmesh.num_vertices * sizeof(float[2]);
4196 size += model->surfmesh.num_vertices * sizeof(float[4]);
4197 data = (unsigned char *)Mem_Alloc(model->mempool, size);
4198 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4199 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4200 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4201 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4202 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
4203 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
4204 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
4205 if (model->surfmesh.num_vertices > 65536)
4206 model->surfmesh.data_element3s = NULL;
4208 if (model->surfmesh.data_element3i_indexbuffer)
4209 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
4210 model->surfmesh.data_element3i_indexbuffer = NULL;
4211 if (model->surfmesh.data_element3s_indexbuffer)
4212 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
4213 model->surfmesh.data_element3s_indexbuffer = NULL;
4214 if (model->surfmesh.vbo_vertexbuffer)
4215 R_Mesh_DestroyMeshBuffer(model->surfmesh.vbo_vertexbuffer);
4216 model->surfmesh.vbo_vertexbuffer = 0;
4218 // convert all triangles to unique vertex data
4220 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4222 surface = model->data_surfaces + surfaceindex;
4223 surface->num_firstvertex = outvertexindex;
4224 surface->num_vertices = surface->num_triangles*3;
4225 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4226 for (i = 0;i < surface->num_triangles*3;i++)
4229 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4230 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4231 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4232 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4233 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4234 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4235 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4236 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4237 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4238 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4239 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4240 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4241 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4242 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4243 if (oldsurfmesh.data_texcoordlightmap2f)
4245 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4246 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4248 if (oldsurfmesh.data_lightmapcolor4f)
4250 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4251 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4252 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4253 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4256 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4257 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4261 if (model->surfmesh.data_element3s)
4262 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4263 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4265 // find and update all submodels to use this new surfmesh data
4266 for (i = 0;i < model->brush.numsubmodels;i++)
4267 model->brush.submodels[i]->surfmesh = model->surfmesh;
4270 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
4272 msurface_t *surface;
4278 lightmaptriangle_t *triangle;
4279 // generate lightmap triangle structs
4280 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4281 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4283 surface = model->data_surfaces + surfaceindex;
4284 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4285 for (i = 0;i < surface->num_triangles;i++)
4287 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4288 triangle->triangleindex = surface->num_firsttriangle+i;
4289 triangle->surfaceindex = surfaceindex;
4290 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4291 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4292 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4293 // calculate bounds of triangle
4294 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4295 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4296 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4297 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4298 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4299 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4300 // pick an axial projection based on the triangle normal
4301 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4303 if (fabs(normal[1]) > fabs(normal[axis]))
4305 if (fabs(normal[2]) > fabs(normal[axis]))
4307 triangle->axis = axis;
4312 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4314 if (mod_generatelightmaps_lightmaptriangles)
4315 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4316 mod_generatelightmaps_lightmaptriangles = NULL;
4319 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4321 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4323 msurface_t *surface;
4337 float trianglenormal[3];
4338 float samplecenter[3];
4339 float samplenormal[3];
4345 float lmscalepixels;
4348 float lm_basescalepixels;
4349 int lm_borderpixels;
4353 lightmaptriangle_t *triangle;
4354 unsigned char *lightmappixels;
4355 unsigned char *deluxemappixels;
4356 mod_alloclightmap_state_t lmstate;
4359 // generate lightmap projection information for all triangles
4360 if (model->texturepool == NULL)
4361 model->texturepool = R_AllocTexturePool();
4362 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4363 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4364 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4365 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4366 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4368 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4370 surface = model->data_surfaces + surfaceindex;
4371 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4372 lmscalepixels = lm_basescalepixels;
4373 for (retry = 0;retry < 30;retry++)
4375 // after a couple failed attempts, degrade quality to make it fit
4377 lmscalepixels *= 0.5f;
4378 for (i = 0;i < surface->num_triangles;i++)
4380 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4381 triangle->lightmapindex = lightmapnumber;
4382 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4383 // pick two planar axes for projection
4384 // lightmap coordinates here are in pixels
4385 // lightmap projections are snapped to pixel grid explicitly, such
4386 // that two neighboring triangles sharing an edge and projection
4387 // axis will have identical sampl espacing along their shared edge
4389 for (j = 0;j < 3;j++)
4391 if (j == triangle->axis)
4393 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4394 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4395 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4396 triangle->lmbase[k] = lmmins/lmscalepixels;
4397 triangle->lmscale[k] = lmscalepixels;
4400 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4403 // if all fit in this texture, we're done with this surface
4404 if (i == surface->num_triangles)
4406 // if we haven't maxed out the lightmap size yet, we retry the
4407 // entire surface batch...
4408 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4410 lm_texturesize *= 2;
4413 Mod_AllocLightmap_Free(&lmstate);
4414 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4417 // if we have maxed out the lightmap size, and this triangle does
4418 // not fit in the same texture as the rest of the surface, we have
4419 // to retry the entire surface in a new texture (can only use one)
4420 // with multiple retries, the lightmap quality degrades until it
4421 // fits (or gives up)
4422 if (surfaceindex > 0)
4424 Mod_AllocLightmap_Reset(&lmstate);
4428 Mod_AllocLightmap_Free(&lmstate);
4430 // now put triangles together into lightmap textures, and do not allow
4431 // triangles of a surface to go into different textures (as that would
4432 // require rewriting the surface list)
4433 model->brushq3.deluxemapping_modelspace = true;
4434 model->brushq3.deluxemapping = true;
4435 model->brushq3.num_mergedlightmaps = lightmapnumber;
4436 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4437 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4438 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4439 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4440 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4442 surface = model->data_surfaces + surfaceindex;
4443 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4444 for (i = 0;i < surface->num_triangles;i++)
4446 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4447 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4448 VectorNormalize(trianglenormal);
4449 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4450 axis = triangle->axis;
4451 axis1 = axis == 0 ? 1 : 0;
4452 axis2 = axis == 2 ? 1 : 2;
4453 lmiscale[0] = 1.0f / triangle->lmscale[0];
4454 lmiscale[1] = 1.0f / triangle->lmscale[1];
4455 if (trianglenormal[axis] < 0)
4456 VectorNegate(trianglenormal, trianglenormal);
4457 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4458 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4459 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4460 for (j = 0;j < 3;j++)
4462 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4463 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4464 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4466 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4467 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4468 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4469 Con_Printf("%f:%f %f:%f %f:%f = %f %f\n", triangle->vertex[j][axis1], samplecenter[axis1], triangle->vertex[j][axis2], samplecenter[axis2], triangle->vertex[j][axis], samplecenter[axis], t2f[0], t2f[1]);
4479 forward[1] = 1.0f / triangle->lmscale[0];
4483 left[2] = 1.0f / triangle->lmscale[1];
4488 origin[1] = triangle->lmbase[0];
4489 origin[2] = triangle->lmbase[1];
4492 forward[0] = 1.0f / triangle->lmscale[0];
4497 left[2] = 1.0f / triangle->lmscale[1];
4501 origin[0] = triangle->lmbase[0];
4503 origin[2] = triangle->lmbase[1];
4506 forward[0] = 1.0f / triangle->lmscale[0];
4510 left[1] = 1.0f / triangle->lmscale[1];
4515 origin[0] = triangle->lmbase[0];
4516 origin[1] = triangle->lmbase[1];
4520 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4522 #define LM_DIST_EPSILON (1.0f / 32.0f)
4523 for (y = 0;y < triangle->lmsize[1];y++)
4525 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4526 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4528 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4529 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4530 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4531 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4532 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4538 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4540 model->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(model->texturepool, va(vabuf, sizeof(vabuf), "lightmap%i", lightmapindex), lm_texturesize, lm_texturesize, lightmappixels + lightmapindex * lm_texturesize * lm_texturesize * 4, TEXTYPE_BGRA, TEXF_FORCELINEAR, -1, NULL);
4541 model->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(model->texturepool, va(vabuf, sizeof(vabuf), "deluxemap%i", lightmapindex), lm_texturesize, lm_texturesize, deluxemappixels + lightmapindex * lm_texturesize * lm_texturesize * 4, TEXTYPE_BGRA, TEXF_FORCELINEAR, -1, NULL);
4545 Mem_Free(lightmappixels);
4546 if (deluxemappixels)
4547 Mem_Free(deluxemappixels);
4549 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4551 surface = model->data_surfaces + surfaceindex;
4552 if (!surface->num_triangles)
4554 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4555 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4556 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4557 surface->lightmapinfo = NULL;
4560 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4561 model->brushq1.lightdata = NULL;
4562 model->brushq1.lightmapupdateflags = NULL;
4563 model->brushq1.firstrender = false;
4564 model->brushq1.num_lightstyles = 0;
4565 model->brushq1.data_lightstyleinfo = NULL;
4566 for (i = 0;i < model->brush.numsubmodels;i++)
4568 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4569 model->brush.submodels[i]->brushq1.firstrender = false;
4570 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4571 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4575 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4578 for (i = 0;i < model->surfmesh.num_vertices;i++)
4579 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4582 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4589 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4591 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4592 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4594 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4595 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4597 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4598 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4604 extern cvar_t mod_q3bsp_nolightmaps;
4605 static void Mod_GenerateLightmaps(dp_model_t *model)
4607 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4608 dp_model_t *oldloadmodel = loadmodel;
4611 Mod_GenerateLightmaps_InitSampleOffsets(model);
4612 Mod_GenerateLightmaps_DestroyLightmaps(model);
4613 Mod_GenerateLightmaps_UnweldTriangles(model);
4614 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4615 Mod_GenerateLightmaps_CreateLights(model);
4616 if(!mod_q3bsp_nolightmaps.integer)
4617 Mod_GenerateLightmaps_CreateLightmaps(model);
4618 Mod_GenerateLightmaps_UpdateVertexColors(model);
4619 Mod_GenerateLightmaps_UpdateLightGrid(model);
4620 Mod_GenerateLightmaps_DestroyLights(model);
4621 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4623 loadmodel = oldloadmodel;
4626 static void Mod_GenerateLightmaps_f(void)
4628 if (Cmd_Argc() != 1)
4630 Con_Printf("usage: mod_generatelightmaps\n");
4635 Con_Printf("no worldmodel loaded\n");
4638 Mod_GenerateLightmaps(cl.worldmodel);
4641 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4643 memset(mod, 0, sizeof(*mod));
4644 strlcpy(mod->name, name, sizeof(mod->name));
4645 mod->mempool = Mem_AllocPool(name, 0, NULL);
4646 mod->texturepool = R_AllocTexturePool();
4647 mod->Draw = R_Q1BSP_Draw;
4648 mod->DrawDepth = R_Q1BSP_DrawDepth;
4649 mod->DrawDebug = R_Q1BSP_DrawDebug;
4650 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4651 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4652 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4653 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
4654 mod->DrawLight = R_Q1BSP_DrawLight;
4657 void Mod_Mesh_Destroy(dp_model_t *mod)
4659 Mod_UnloadModel(mod);
4662 // resets the mesh model to have no geometry to render, ready for a new frame -
4663 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4664 void Mod_Mesh_Reset(dp_model_t *mod)
4666 mod->num_surfaces = 0;
4667 mod->surfmesh.num_vertices = 0;
4668 mod->surfmesh.num_triangles = 0;
4669 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4670 mod->DrawSky = NULL; // will be set if a texture needs it
4671 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4674 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4678 for (i = 0; i < mod->num_textures; i++)
4679 if (!strcmp(mod->data_textures[i].name, name))
4680 return mod->data_textures + i;
4681 if (mod->max_textures <= mod->num_textures)
4683 texture_t *oldtextures = mod->data_textures;
4684 mod->max_textures = max(mod->max_textures * 2, 1024);
4685 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4686 // update the pointers
4687 for (i = 0; i < mod->num_surfaces; i++)
4688 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4690 t = &mod->data_textures[mod->num_textures++];
4691 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
4692 switch (defaultdrawflags & DRAWFLAG_MASK)
4694 case DRAWFLAG_ADDITIVE:
4695 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4696 t->currentmaterialflags = t->basematerialflags;
4698 case DRAWFLAG_MODULATE:
4699 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4700 t->currentmaterialflags = t->basematerialflags;
4701 t->customblendfunc[0] = GL_DST_COLOR;
4702 t->customblendfunc[1] = GL_ZERO;
4704 case DRAWFLAG_2XMODULATE:
4705 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4706 t->currentmaterialflags = t->basematerialflags;
4707 t->customblendfunc[0] = GL_DST_COLOR;
4708 t->customblendfunc[1] = GL_SRC_COLOR;
4710 case DRAWFLAG_SCREEN:
4711 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4712 t->currentmaterialflags = t->basematerialflags;
4713 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4714 t->customblendfunc[1] = GL_ONE;
4722 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4725 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4726 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4727 return mod->data_surfaces + mod->num_surfaces - 1;
4728 // create new surface
4729 if (mod->max_surfaces == mod->num_surfaces)
4731 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4732 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4733 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4735 surf = mod->data_surfaces + mod->num_surfaces;
4736 mod->num_surfaces++;
4737 memset(surf, 0, sizeof(*surf));
4738 surf->texture = tex;
4739 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4740 surf->num_firstvertex = mod->surfmesh.num_vertices;
4741 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4742 mod->DrawSky = R_Q1BSP_DrawSky;
4743 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4744 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4748 int Mod_Mesh_IndexForVertex(dp_model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
4750 int hashindex, h, vnum, mask;
4751 surfmesh_t *mesh = &mod->surfmesh;
4752 if (mesh->max_vertices == mesh->num_vertices)
4754 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4755 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4756 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4757 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4758 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4759 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4760 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4761 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4762 // rebuild the hash table
4763 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4764 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4765 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4766 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4767 mask = mod->surfmesh.num_vertexhashsize - 1;
4768 // no need to hash the vertices for the entire model, the latest surface will suffice.
4769 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4771 // this uses prime numbers intentionally for computing the hash
4772 hashindex = (unsigned int)(mesh->data_vertex3f[vnum * 3 + 0] * 2003 + mesh->data_vertex3f[vnum * 3 + 1] * 4001 + mesh->data_vertex3f[vnum * 3 + 2] * 7919 + mesh->data_normal3f[vnum * 3 + 0] * 4097 + mesh->data_normal3f[vnum * 3 + 1] * 257 + mesh->data_normal3f[vnum * 3 + 2] * 17) & mask;
4773 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4774 ; // just iterate until we find the terminator
4775 mesh->data_vertexhash[h] = vnum;
4778 mask = mod->surfmesh.num_vertexhashsize - 1;
4779 // this uses prime numbers intentionally for computing the hash
4780 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4781 // when possible find an identical vertex within the same surface and return it
4782 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4784 if (vnum >= surf->num_firstvertex
4785 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4786 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4787 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4788 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4789 && mesh->data_lightmapcolor4f[vnum * 4 + 0] == r && mesh->data_lightmapcolor4f[vnum * 4 + 1] == g && mesh->data_lightmapcolor4f[vnum * 4 + 2] == b && mesh->data_lightmapcolor4f[vnum * 4 + 3] == a)
4792 // add the new vertex
4793 vnum = mesh->num_vertices++;
4794 if (surf->num_vertices > 0)
4796 if (surf->mins[0] > x) surf->mins[0] = x;
4797 if (surf->mins[1] > y) surf->mins[1] = y;
4798 if (surf->mins[2] > z) surf->mins[2] = z;
4799 if (surf->maxs[0] < x) surf->maxs[0] = x;
4800 if (surf->maxs[1] < y) surf->maxs[1] = y;
4801 if (surf->maxs[2] < z) surf->maxs[2] = z;
4805 VectorSet(surf->mins, x, y, z);
4806 VectorSet(surf->maxs, x, y, z);
4808 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4809 mesh->data_vertexhash[h] = vnum;
4810 mesh->data_vertex3f[vnum * 3 + 0] = x;
4811 mesh->data_vertex3f[vnum * 3 + 1] = y;
4812 mesh->data_vertex3f[vnum * 3 + 2] = z;
4813 mesh->data_normal3f[vnum * 3 + 0] = nx;
4814 mesh->data_normal3f[vnum * 3 + 1] = ny;
4815 mesh->data_normal3f[vnum * 3 + 2] = nz;
4816 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4817 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4818 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4819 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4820 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4821 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4822 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4823 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4827 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4829 surfmesh_t *mesh = &mod->surfmesh;
4830 if (mesh->max_triangles == mesh->num_triangles)
4832 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4833 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4834 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4836 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4837 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4838 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4839 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4840 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4841 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4842 mesh->num_triangles++;
4843 surf->num_triangles++;
4846 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4850 msurface_t *surf, *surf2;
4852 // build the sorted surfaces list properly to reduce material setup
4853 // this is easy because we're just sorting on texture and don't care about the order of textures
4854 mod->nummodelsurfaces = 0;
4855 for (i = 0; i < mod->num_surfaces; i++)
4856 mod->data_surfaces[i].included = false;
4857 for (i = 0; i < mod->num_surfaces; i++)
4859 surf = mod->data_surfaces + i;
4862 tex = surf->texture;
4863 // j = i is intentional
4864 for (j = i; j < mod->num_surfaces; j++)
4866 surf2 = mod->data_surfaces + j;
4867 if (surf2->included)
4869 if (surf2->texture == tex)
4871 surf2->included = true;
4872 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4878 void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4881 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4883 if (mod->surfmesh.num_vertices > 0)
4885 // calculate normalmins/normalmaxs
4886 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4887 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4888 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4890 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4891 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4892 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4893 // expand bounds to include this vertex
4894 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4895 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4896 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4897 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4898 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4899 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4901 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4902 // (fast but less accurate than doing it per vertex)
4903 x2a = mod->normalmins[0] * mod->normalmins[0];
4904 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4905 y2a = mod->normalmins[1] * mod->normalmins[1];
4906 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4907 z2a = mod->normalmins[2] * mod->normalmins[2];
4908 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4912 yawradius = sqrt(x2 + y2);
4913 rotatedradius = sqrt(x2 + y2 + z2);
4914 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4915 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4916 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4917 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4918 mod->radius = rotatedradius;
4919 mod->radius2 = x2 + y2 + z2;
4923 VectorClear(mod->normalmins);
4924 VectorClear(mod->normalmaxs);
4925 VectorClear(mod->yawmins);
4926 VectorClear(mod->yawmaxs);
4927 VectorClear(mod->rotatedmins);
4928 VectorClear(mod->rotatedmaxs);
4934 void Mod_Mesh_Finalize(dp_model_t *mod)
4936 Mod_Mesh_ComputeBounds(mod);
4937 Mod_Mesh_MakeSortedSurfaces(mod);
4938 Mod_BuildTextureVectorsFromNormals(0, mod->surfmesh.num_vertices, mod->surfmesh.num_triangles, mod->surfmesh.data_vertex3f, mod->surfmesh.data_texcoordtexture2f, mod->surfmesh.data_normal3f, mod->surfmesh.data_element3i, mod->surfmesh.data_svector3f, mod->surfmesh.data_tvector3f, true);