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 void Mod_ValidateElements(int *elements, int numtriangles, int firstvertex, int numverts, const char *filename, int fileline)
821 int i, warned = false, endvertex = firstvertex + numverts;
822 for (i = 0;i < numtriangles * 3;i++)
824 if (elements[i] < firstvertex || elements[i] >= endvertex)
829 Con_Printf("Mod_ValidateElements: out of bounds elements detected at %s:%d\n", filename, fileline);
831 elements[i] = firstvertex;
836 // warning: this is an expensive function!
837 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
844 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
845 // process each vertex of each triangle and accumulate the results
846 // use area-averaging, to make triangles with a big area have a bigger
847 // weighting on the vertex normal than triangles with a small area
848 // to do so, just add the 'normals' together (the bigger the area
849 // the greater the length of the normal is
851 for (i = 0; i < numtriangles; i++, element += 3)
854 vertex3f + element[0] * 3,
855 vertex3f + element[1] * 3,
856 vertex3f + element[2] * 3,
861 VectorNormalize(areaNormal);
863 for (j = 0;j < 3;j++)
865 vectorNormal = normal3f + element[j] * 3;
866 vectorNormal[0] += areaNormal[0];
867 vectorNormal[1] += areaNormal[1];
868 vectorNormal[2] += areaNormal[2];
871 // and just normalize the accumulated vertex normal in the end
872 vectorNormal = normal3f + 3 * firstvertex;
873 for (i = 0; i < numvertices; i++, vectorNormal += 3)
874 VectorNormalize(vectorNormal);
878 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)
880 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
881 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
882 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
884 // 6 multiply, 9 subtract
885 VectorSubtract(v1, v0, v10);
886 VectorSubtract(v2, v0, v20);
887 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
888 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
889 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
890 // 12 multiply, 10 subtract
891 tc10[1] = tc1[1] - tc0[1];
892 tc20[1] = tc2[1] - tc0[1];
893 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
894 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
895 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
896 tc10[0] = tc1[0] - tc0[0];
897 tc20[0] = tc2[0] - tc0[0];
898 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
899 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
900 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
901 // 12 multiply, 4 add, 6 subtract
902 f = DotProduct(svector3f, normal3f);
903 svector3f[0] -= f * normal3f[0];
904 svector3f[1] -= f * normal3f[1];
905 svector3f[2] -= f * normal3f[2];
906 f = DotProduct(tvector3f, normal3f);
907 tvector3f[0] -= f * normal3f[0];
908 tvector3f[1] -= f * normal3f[1];
909 tvector3f[2] -= f * normal3f[2];
910 // if texture is mapped the wrong way (counterclockwise), the tangents
911 // have to be flipped, this is detected by calculating a normal from the
912 // two tangents, and seeing if it is opposite the surface normal
913 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
914 CrossProduct(tvector3f, svector3f, tangentcross);
915 if (DotProduct(tangentcross, normal3f) < 0)
917 VectorNegate(svector3f, svector3f);
918 VectorNegate(tvector3f, tvector3f);
923 // warning: this is a very expensive function!
924 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)
927 float sdir[3], tdir[3], normal[3], *svec, *tvec;
928 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
929 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
932 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
933 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
934 // process each vertex of each triangle and accumulate the results
935 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
937 v0 = vertex3f + e[0] * 3;
938 v1 = vertex3f + e[1] * 3;
939 v2 = vertex3f + e[2] * 3;
940 tc0 = texcoord2f + e[0] * 2;
941 tc1 = texcoord2f + e[1] * 2;
942 tc2 = texcoord2f + e[2] * 2;
944 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
945 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
947 // calculate the edge directions and surface normal
948 // 6 multiply, 9 subtract
949 VectorSubtract(v1, v0, v10);
950 VectorSubtract(v2, v0, v20);
951 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
952 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
953 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
955 // calculate the tangents
956 // 12 multiply, 10 subtract
957 tc10[1] = tc1[1] - tc0[1];
958 tc20[1] = tc2[1] - tc0[1];
959 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
960 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
961 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
962 tc10[0] = tc1[0] - tc0[0];
963 tc20[0] = tc2[0] - tc0[0];
964 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
965 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
966 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
968 // if texture is mapped the wrong way (counterclockwise), the tangents
969 // have to be flipped, this is detected by calculating a normal from the
970 // two tangents, and seeing if it is opposite the surface normal
971 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
972 CrossProduct(tdir, sdir, tangentcross);
973 if (DotProduct(tangentcross, normal) < 0)
975 VectorNegate(sdir, sdir);
976 VectorNegate(tdir, tdir);
981 VectorNormalize(sdir);
982 VectorNormalize(tdir);
984 for (i = 0;i < 3;i++)
986 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
987 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
990 // make the tangents completely perpendicular to the surface normal, and
991 // then normalize them
992 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
993 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
995 f = -DotProduct(svec, n);
996 VectorMA(svec, f, n, svec);
997 VectorNormalize(svec);
998 f = -DotProduct(tvec, n);
999 VectorMA(tvec, f, n, tvec);
1000 VectorNormalize(tvec);
1004 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors, qboolean neighbors)
1006 unsigned char *data;
1007 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));
1008 loadmodel->surfmesh.num_vertices = numvertices;
1009 loadmodel->surfmesh.num_triangles = numtriangles;
1010 if (loadmodel->surfmesh.num_vertices)
1012 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1013 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1014 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1015 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1016 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
1017 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
1019 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
1020 if (lightmapoffsets)
1021 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
1023 if (loadmodel->surfmesh.num_triangles)
1025 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
1027 loadmodel->surfmesh.data_neighbor3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
1028 if (loadmodel->surfmesh.num_vertices <= 65536)
1029 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
1033 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)
1035 shadowmesh_t *newmesh;
1036 unsigned char *data;
1038 size = sizeof(shadowmesh_t);
1039 size += maxverts * sizeof(float[3]);
1041 size += maxverts * sizeof(float[11]);
1042 size += maxtriangles * sizeof(int[3]);
1043 if (maxverts <= 65536)
1044 size += maxtriangles * sizeof(unsigned short[3]);
1046 size += maxtriangles * sizeof(int[3]);
1048 size += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *) + maxverts * sizeof(shadowmeshvertexhash_t);
1049 data = (unsigned char *)Mem_Alloc(mempool, size);
1050 newmesh = (shadowmesh_t *)data;data += sizeof(*newmesh);
1051 newmesh->map_diffuse = map_diffuse;
1052 newmesh->map_specular = map_specular;
1053 newmesh->map_normal = map_normal;
1054 newmesh->maxverts = maxverts;
1055 newmesh->maxtriangles = maxtriangles;
1056 newmesh->numverts = 0;
1057 newmesh->numtriangles = 0;
1058 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1059 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1061 newmesh->vertex3f = (float *)data;data += maxverts * sizeof(float[3]);
1064 newmesh->svector3f = (float *)data;data += maxverts * sizeof(float[3]);
1065 newmesh->tvector3f = (float *)data;data += maxverts * sizeof(float[3]);
1066 newmesh->normal3f = (float *)data;data += maxverts * sizeof(float[3]);
1067 newmesh->texcoord2f = (float *)data;data += maxverts * sizeof(float[2]);
1069 newmesh->element3i = (int *)data;data += maxtriangles * sizeof(int[3]);
1072 newmesh->neighbor3i = (int *)data;data += maxtriangles * sizeof(int[3]);
1076 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)data;data += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *);
1077 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)data;data += maxverts * sizeof(shadowmeshvertexhash_t);
1079 if (maxverts <= 65536)
1080 newmesh->element3s = (unsigned short *)data;data += maxtriangles * sizeof(unsigned short[3]);
1084 shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh, int light, int neighbors)
1086 shadowmesh_t *newmesh;
1087 newmesh = Mod_ShadowMesh_Alloc(mempool, oldmesh->numverts, oldmesh->numtriangles, oldmesh->map_diffuse, oldmesh->map_specular, oldmesh->map_normal, light, neighbors, false);
1088 newmesh->numverts = oldmesh->numverts;
1089 newmesh->numtriangles = oldmesh->numtriangles;
1090 memcpy(newmesh->sideoffsets, oldmesh->sideoffsets, sizeof(oldmesh->sideoffsets));
1091 memcpy(newmesh->sidetotals, oldmesh->sidetotals, sizeof(oldmesh->sidetotals));
1093 memcpy(newmesh->vertex3f, oldmesh->vertex3f, oldmesh->numverts * sizeof(float[3]));
1094 if (newmesh->svector3f && oldmesh->svector3f)
1096 memcpy(newmesh->svector3f, oldmesh->svector3f, oldmesh->numverts * sizeof(float[3]));
1097 memcpy(newmesh->tvector3f, oldmesh->tvector3f, oldmesh->numverts * sizeof(float[3]));
1098 memcpy(newmesh->normal3f, oldmesh->normal3f, oldmesh->numverts * sizeof(float[3]));
1099 memcpy(newmesh->texcoord2f, oldmesh->texcoord2f, oldmesh->numverts * sizeof(float[2]));
1101 memcpy(newmesh->element3i, oldmesh->element3i, oldmesh->numtriangles * sizeof(int[3]));
1102 if (newmesh->neighbor3i && oldmesh->neighbor3i)
1103 memcpy(newmesh->neighbor3i, oldmesh->neighbor3i, oldmesh->numtriangles * sizeof(int[3]));
1107 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, float *vertex14f)
1109 int hashindex, vnum;
1110 shadowmeshvertexhash_t *hash;
1111 // this uses prime numbers intentionally
1112 hashindex = (unsigned int) (vertex14f[0] * 2003 + vertex14f[1] * 4001 + vertex14f[2] * 7919) % SHADOWMESHVERTEXHASH;
1113 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1115 vnum = (hash - mesh->vertexhashentries);
1116 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]))
1117 && (mesh->svector3f == NULL || (mesh->svector3f[vnum * 3 + 0] == vertex14f[3] && mesh->svector3f[vnum * 3 + 1] == vertex14f[4] && mesh->svector3f[vnum * 3 + 2] == vertex14f[5]))
1118 && (mesh->tvector3f == NULL || (mesh->tvector3f[vnum * 3 + 0] == vertex14f[6] && mesh->tvector3f[vnum * 3 + 1] == vertex14f[7] && mesh->tvector3f[vnum * 3 + 2] == vertex14f[8]))
1119 && (mesh->normal3f == NULL || (mesh->normal3f[vnum * 3 + 0] == vertex14f[9] && mesh->normal3f[vnum * 3 + 1] == vertex14f[10] && mesh->normal3f[vnum * 3 + 2] == vertex14f[11]))
1120 && (mesh->texcoord2f == NULL || (mesh->texcoord2f[vnum * 2 + 0] == vertex14f[12] && mesh->texcoord2f[vnum * 2 + 1] == vertex14f[13])))
1121 return hash - mesh->vertexhashentries;
1123 vnum = mesh->numverts++;
1124 hash = mesh->vertexhashentries + vnum;
1125 hash->next = mesh->vertexhashtable[hashindex];
1126 mesh->vertexhashtable[hashindex] = hash;
1127 if (mesh->vertex3f) {mesh->vertex3f[vnum * 3 + 0] = vertex14f[0];mesh->vertex3f[vnum * 3 + 1] = vertex14f[1];mesh->vertex3f[vnum * 3 + 2] = vertex14f[2];}
1128 if (mesh->svector3f) {mesh->svector3f[vnum * 3 + 0] = vertex14f[3];mesh->svector3f[vnum * 3 + 1] = vertex14f[4];mesh->svector3f[vnum * 3 + 2] = vertex14f[5];}
1129 if (mesh->tvector3f) {mesh->tvector3f[vnum * 3 + 0] = vertex14f[6];mesh->tvector3f[vnum * 3 + 1] = vertex14f[7];mesh->tvector3f[vnum * 3 + 2] = vertex14f[8];}
1130 if (mesh->normal3f) {mesh->normal3f[vnum * 3 + 0] = vertex14f[9];mesh->normal3f[vnum * 3 + 1] = vertex14f[10];mesh->normal3f[vnum * 3 + 2] = vertex14f[11];}
1131 if (mesh->texcoord2f) {mesh->texcoord2f[vnum * 2 + 0] = vertex14f[12];mesh->texcoord2f[vnum * 2 + 1] = vertex14f[13];}
1135 void Mod_ShadowMesh_AddTriangle(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, float *vertex14f)
1137 if (mesh->numtriangles == 0)
1139 // set the properties on this empty mesh to be more favorable...
1140 // (note: this case only occurs for the first triangle added to a new mesh chain)
1141 mesh->map_diffuse = map_diffuse;
1142 mesh->map_specular = map_specular;
1143 mesh->map_normal = map_normal;
1145 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)
1147 if (mesh->next == NULL)
1148 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);
1151 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 0);
1152 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 1);
1153 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 2);
1154 mesh->numtriangles++;
1157 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)
1160 float vbuf[3*14], *v;
1161 memset(vbuf, 0, sizeof(vbuf));
1162 for (i = 0;i < numtris;i++)
1164 for (j = 0, v = vbuf;j < 3;j++, v += 14)
1169 v[0] = vertex3f[e * 3 + 0];
1170 v[1] = vertex3f[e * 3 + 1];
1171 v[2] = vertex3f[e * 3 + 2];
1175 v[3] = svector3f[e * 3 + 0];
1176 v[4] = svector3f[e * 3 + 1];
1177 v[5] = svector3f[e * 3 + 2];
1181 v[6] = tvector3f[e * 3 + 0];
1182 v[7] = tvector3f[e * 3 + 1];
1183 v[8] = tvector3f[e * 3 + 2];
1187 v[9] = normal3f[e * 3 + 0];
1188 v[10] = normal3f[e * 3 + 1];
1189 v[11] = normal3f[e * 3 + 2];
1193 v[12] = texcoord2f[e * 2 + 0];
1194 v[13] = texcoord2f[e * 2 + 1];
1197 Mod_ShadowMesh_AddTriangle(mempool, mesh, map_diffuse, map_specular, map_normal, vbuf);
1200 // the triangle calculation can take a while, so let's do a keepalive here
1201 CL_KeepaliveMessage(false);
1204 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)
1206 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1207 CL_KeepaliveMessage(false);
1209 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles, map_diffuse, map_specular, map_normal, light, neighbors, expandable);
1212 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh, mempool_t *mempool)
1214 if (!mesh->numverts)
1217 // build r_vertexmesh_t array
1218 // (compressed interleaved array for D3D)
1219 if (!mesh->vertexmesh && mesh->texcoord2f && vid.useinterleavedarrays)
1222 int numvertices = mesh->numverts;
1223 r_vertexmesh_t *vertexmesh;
1224 mesh->vertexmesh = vertexmesh = (r_vertexmesh_t*)Mem_Alloc(mempool, numvertices * sizeof(*mesh->vertexmesh));
1225 for (vertexindex = 0;vertexindex < numvertices;vertexindex++, vertexmesh++)
1227 VectorCopy(mesh->vertex3f + 3*vertexindex, vertexmesh->vertex3f);
1228 VectorScale(mesh->svector3f + 3*vertexindex, 1.0f, vertexmesh->svector3f);
1229 VectorScale(mesh->tvector3f + 3*vertexindex, 1.0f, vertexmesh->tvector3f);
1230 VectorScale(mesh->normal3f + 3*vertexindex, 1.0f, vertexmesh->normal3f);
1231 Vector2Copy(mesh->texcoord2f + 2*vertexindex, vertexmesh->texcoordtexture2f);
1235 // upload short indices as a buffer
1236 if (mesh->element3s && !mesh->element3s_indexbuffer)
1237 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1239 // upload int indices as a buffer
1240 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1241 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1243 // vertex buffer is several arrays and we put them in the same buffer
1245 // is this wise? the texcoordtexture2f array is used with dynamic
1246 // vertex/svector/tvector/normal when rendering animated models, on the
1247 // other hand animated models don't use a lot of vertices anyway...
1248 if (!mesh->vbo_vertexbuffer && !vid.useinterleavedarrays)
1253 mesh->vbooffset_vertexmesh = size;if (mesh->vertexmesh ) size += mesh->numverts * sizeof(r_vertexmesh_t);
1254 mesh->vbooffset_vertex3f = size;if (mesh->vertex3f ) size += mesh->numverts * sizeof(float[3]);
1255 mesh->vbooffset_svector3f = size;if (mesh->svector3f ) size += mesh->numverts * sizeof(float[3]);
1256 mesh->vbooffset_tvector3f = size;if (mesh->tvector3f ) size += mesh->numverts * sizeof(float[3]);
1257 mesh->vbooffset_normal3f = size;if (mesh->normal3f ) size += mesh->numverts * sizeof(float[3]);
1258 mesh->vbooffset_texcoord2f = size;if (mesh->texcoord2f ) size += mesh->numverts * sizeof(float[2]);
1259 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
1260 if (mesh->vertexmesh ) memcpy(mem + mesh->vbooffset_vertexmesh , mesh->vertexmesh , mesh->numverts * sizeof(r_vertexmesh_t));
1261 if (mesh->vertex3f ) memcpy(mem + mesh->vbooffset_vertex3f , mesh->vertex3f , mesh->numverts * sizeof(float[3]));
1262 if (mesh->svector3f ) memcpy(mem + mesh->vbooffset_svector3f , mesh->svector3f , mesh->numverts * sizeof(float[3]));
1263 if (mesh->tvector3f ) memcpy(mem + mesh->vbooffset_tvector3f , mesh->tvector3f , mesh->numverts * sizeof(float[3]));
1264 if (mesh->normal3f ) memcpy(mem + mesh->vbooffset_normal3f , mesh->normal3f , mesh->numverts * sizeof(float[3]));
1265 if (mesh->texcoord2f ) memcpy(mem + mesh->vbooffset_texcoord2f , mesh->texcoord2f , mesh->numverts * sizeof(float[2]));
1266 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, "shadowmesh", false, false, false, false);
1271 shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, qboolean light, qboolean neighbors, qboolean createvbo)
1273 shadowmesh_t *mesh, *newmesh, *nextmesh;
1274 // reallocate meshs to conserve space
1275 for (mesh = firstmesh, firstmesh = NULL;mesh;mesh = nextmesh)
1277 nextmesh = mesh->next;
1278 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1280 newmesh = Mod_ShadowMesh_ReAlloc(mempool, mesh, light, neighbors);
1281 newmesh->next = firstmesh;
1282 firstmesh = newmesh;
1283 if (newmesh->element3s)
1286 for (i = 0;i < newmesh->numtriangles*3;i++)
1287 newmesh->element3s[i] = newmesh->element3i[i];
1290 Mod_ShadowMesh_CreateVBOs(newmesh, mempool);
1295 // this can take a while, so let's do a keepalive here
1296 CL_KeepaliveMessage(false);
1301 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *firstmesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1305 vec3_t nmins, nmaxs, ncenter, temp;
1306 float nradius2, dist2, *v;
1310 for (mesh = firstmesh;mesh;mesh = mesh->next)
1312 if (mesh == firstmesh)
1314 VectorCopy(mesh->vertex3f, nmins);
1315 VectorCopy(mesh->vertex3f, nmaxs);
1317 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1319 if (nmins[0] > v[0]) nmins[0] = v[0];if (nmaxs[0] < v[0]) nmaxs[0] = v[0];
1320 if (nmins[1] > v[1]) nmins[1] = v[1];if (nmaxs[1] < v[1]) nmaxs[1] = v[1];
1321 if (nmins[2] > v[2]) nmins[2] = v[2];if (nmaxs[2] < v[2]) nmaxs[2] = v[2];
1324 // calculate center and radius
1325 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1326 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1327 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1329 for (mesh = firstmesh;mesh;mesh = mesh->next)
1331 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1333 VectorSubtract(v, ncenter, temp);
1334 dist2 = DotProduct(temp, temp);
1335 if (nradius2 < dist2)
1341 VectorCopy(nmins, mins);
1343 VectorCopy(nmaxs, maxs);
1345 VectorCopy(ncenter, center);
1347 *radius = sqrt(nradius2);
1350 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1352 shadowmesh_t *nextmesh;
1353 for (;mesh;mesh = nextmesh)
1355 if (mesh->element3i_indexbuffer)
1356 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1357 if (mesh->element3s_indexbuffer)
1358 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1359 if (mesh->vbo_vertexbuffer)
1360 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1361 nextmesh = mesh->next;
1366 void Mod_CreateCollisionMesh(dp_model_t *mod)
1368 int k, numcollisionmeshtriangles;
1369 qboolean usesinglecollisionmesh = false;
1370 const msurface_t *surface = NULL;
1372 mempool_t *mempool = mod->mempool;
1373 if (!mempool && mod->brush.parentmodel)
1374 mempool = mod->brush.parentmodel->mempool;
1375 // make a single combined collision mesh for physics engine use
1376 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1377 numcollisionmeshtriangles = 0;
1378 for (k = 0;k < mod->nummodelsurfaces;k++)
1380 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1381 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1383 usesinglecollisionmesh = true;
1384 numcollisionmeshtriangles = surface->num_triangles;
1387 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1389 numcollisionmeshtriangles += surface->num_triangles;
1391 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles, NULL, NULL, NULL, false, false, true);
1392 if (usesinglecollisionmesh)
1393 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));
1396 for (k = 0;k < mod->nummodelsurfaces;k++)
1398 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1399 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1401 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));
1404 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mempool, mod->brush.collisionmesh, false, false, false);
1408 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)
1413 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1414 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1417 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1418 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1419 texcoord2f[0] = tc[0];
1420 texcoord2f[1] = tc[1];
1423 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)
1425 float vup[3], vdown[3], vleft[3], vright[3];
1426 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1427 float sv[3], tv[3], nl[3];
1428 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1429 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1430 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1431 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1432 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1433 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1434 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1435 VectorAdd(svector3f, sv, svector3f);
1436 VectorAdd(tvector3f, tv, tvector3f);
1437 VectorAdd(normal3f, nl, normal3f);
1438 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1439 VectorAdd(svector3f, sv, svector3f);
1440 VectorAdd(tvector3f, tv, tvector3f);
1441 VectorAdd(normal3f, nl, normal3f);
1442 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1443 VectorAdd(svector3f, sv, svector3f);
1444 VectorAdd(tvector3f, tv, tvector3f);
1445 VectorAdd(normal3f, nl, normal3f);
1448 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)
1450 int x, y, ix, iy, *e;
1452 for (y = 0;y < height;y++)
1454 for (x = 0;x < width;x++)
1456 e[0] = (y + 1) * (width + 1) + (x + 0);
1457 e[1] = (y + 0) * (width + 1) + (x + 0);
1458 e[2] = (y + 1) * (width + 1) + (x + 1);
1459 e[3] = (y + 0) * (width + 1) + (x + 0);
1460 e[4] = (y + 0) * (width + 1) + (x + 1);
1461 e[5] = (y + 1) * (width + 1) + (x + 1);
1465 Mod_BuildTriangleNeighbors(neighbor3i, element3i, width*height*2);
1466 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1467 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1468 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1473 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1477 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1478 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1479 float viewvector[3];
1480 unsigned int firstvertex;
1483 if (chunkwidth < 2 || chunkheight < 2)
1485 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]);
1486 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]);
1487 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1488 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1489 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1490 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1492 // too close for this stepsize, emit as 4 chunks instead
1494 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1495 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1496 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1497 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1500 // emit the geometry at stepsize into our vertex buffer / index buffer
1501 // we add two columns and two rows for skirt
1502 outwidth = chunkwidth+2;
1503 outheight = chunkheight+2;
1504 outwidth2 = outwidth-1;
1505 outheight2 = outheight-1;
1506 outwidth3 = outwidth+1;
1507 outheight3 = outheight+1;
1508 firstvertex = numvertices;
1509 e = model->terrain.element3i + numtriangles;
1510 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1511 v = model->terrain.vertex3f + numvertices;
1512 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1513 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1514 for (ty = 0;ty < outheight;ty++)
1516 for (tx = 0;tx < outwidth;tx++)
1518 *e++ = firstvertex + (ty )*outwidth3+(tx );
1519 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1520 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1521 *e++ = firstvertex + (ty )*outwidth3+(tx );
1522 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1523 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1526 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1527 for (ty = 0;ty <= outheight;ty++)
1529 skirtrow = ty == 0 || ty == outheight;
1530 ry = y+bound(1, ty, outheight)*stepsize;
1531 for (tx = 0;tx <= outwidth;tx++)
1533 skirt = skirtrow || tx == 0 || tx == outwidth;
1534 rx = x+bound(1, tx, outwidth)*stepsize;
1537 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1541 // TODO: emit skirt vertices
1544 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1546 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1547 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1548 Mod_Terrain_BuildChunk(model,
1552 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1555 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1557 offset = bound(0, s[4] - '0', 9);
1558 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1563 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1564 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1565 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1566 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1567 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1568 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1569 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1570 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1571 return offset | Q3WAVEFUNC_NONE;
1574 void Mod_FreeQ3Shaders(void)
1576 Mem_FreePool(&q3shaders_mem);
1579 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
1581 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1582 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1583 q3shader_hash_entry_t* lastEntry = NULL;
1586 if (strcasecmp (entry->shader.name, shader->name) == 0)
1589 if(shader->dpshaderkill)
1591 // killed shader is a redeclarion? we can safely ignore it
1594 else if(entry->shader.dpshaderkill)
1596 // replace the old shader!
1597 // this will skip the entry allocating part
1598 // below and just replace the shader
1603 unsigned char *start, *end, *start2;
1604 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1605 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1606 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1607 if(memcmp(start, start2, end - start))
1608 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1610 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1615 entry = entry->chain;
1617 while (entry != NULL);
1620 if (lastEntry->shader.name[0] != 0)
1623 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1624 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1626 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1627 lastEntry->chain = newEntry;
1628 newEntry->chain = NULL;
1629 lastEntry = newEntry;
1631 /* else: head of chain, in hash entry array */
1634 memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
1637 extern cvar_t mod_noshader_default_offsetmapping;
1638 extern cvar_t mod_q3shader_default_offsetmapping;
1639 extern cvar_t mod_q3shader_default_offsetmapping_scale;
1640 extern cvar_t mod_q3shader_default_offsetmapping_bias;
1641 extern cvar_t mod_q3shader_default_polygonoffset;
1642 extern cvar_t mod_q3shader_default_polygonfactor;
1643 extern cvar_t mod_q3shader_force_addalpha;
1644 extern cvar_t mod_q3shader_force_terrain_alphaflag;
1645 void Mod_LoadQ3Shaders(void)
1652 q3shaderinfo_t shader;
1653 q3shaderinfo_layer_t *layer;
1655 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1656 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1657 unsigned long custsurfaceflags[256];
1658 int numcustsurfaceflags;
1659 qboolean dpshaderkill;
1661 Mod_FreeQ3Shaders();
1663 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1664 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1665 sizeof (q3shader_data_t));
1666 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1667 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1668 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1669 q3shaders_mem, sizeof (char**), 256);
1671 // parse custinfoparms.txt
1672 numcustsurfaceflags = 0;
1673 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1675 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1676 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1679 while (COM_ParseToken_QuakeC(&text, false))
1680 if (!strcasecmp(com_token, "}"))
1682 // custom surfaceflags section
1683 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1684 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1687 while(COM_ParseToken_QuakeC(&text, false))
1689 if (!strcasecmp(com_token, "}"))
1691 // register surfaceflag
1692 if (numcustsurfaceflags >= 256)
1694 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1698 j = (int)strlen(com_token)+1;
1699 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1700 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1702 if (COM_ParseToken_QuakeC(&text, false))
1703 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1705 custsurfaceflags[numcustsurfaceflags] = 0;
1706 numcustsurfaceflags++;
1714 search = FS_Search("scripts/*.shader", true, false);
1717 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1719 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1722 while (COM_ParseToken_QuakeC(&text, false))
1724 memset (&shader, 0, sizeof(shader));
1726 shader.surfaceparms = 0;
1727 shader.surfaceflags = 0;
1728 shader.textureflags = 0;
1729 shader.numlayers = 0;
1730 shader.lighting = false;
1731 shader.vertexalpha = false;
1732 shader.textureblendalpha = false;
1733 shader.skyboxname[0] = 0;
1734 shader.deforms[0].deform = Q3DEFORM_NONE;
1735 shader.dpnortlight = false;
1736 shader.dpshadow = false;
1737 shader.dpnoshadow = false;
1738 shader.dpmeshcollisions = false;
1739 shader.dpshaderkill = false;
1740 shader.dpreflectcube[0] = 0;
1741 shader.reflectmin = 0;
1742 shader.reflectmax = 1;
1743 shader.refractfactor = 1;
1744 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1745 shader.reflectfactor = 1;
1746 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1747 shader.r_water_wateralpha = 1;
1748 shader.r_water_waterscroll[0] = 0;
1749 shader.r_water_waterscroll[1] = 0;
1750 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1751 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1752 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1753 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1754 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1755 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1756 shader.specularscalemod = 1;
1757 shader.specularpowermod = 1;
1758 shader.rtlightambient = 0;
1759 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1760 // JUST GREP FOR "specularscalemod = 1".
1762 strlcpy(shader.name, com_token, sizeof(shader.name));
1763 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1765 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1768 while (COM_ParseToken_QuakeC(&text, false))
1770 if (!strcasecmp(com_token, "}"))
1772 if (!strcasecmp(com_token, "{"))
1774 static q3shaderinfo_layer_t dummy;
1775 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1777 layer = shader.layers + shader.numlayers++;
1781 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1782 memset(&dummy, 0, sizeof(dummy));
1785 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1786 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1787 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1788 layer->blendfunc[0] = GL_ONE;
1789 layer->blendfunc[1] = GL_ZERO;
1790 while (COM_ParseToken_QuakeC(&text, false))
1792 if (!strcasecmp(com_token, "}"))
1794 if (!strcasecmp(com_token, "\n"))
1797 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1799 if (j < TEXTURE_MAXFRAMES + 4)
1801 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1802 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1803 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1805 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1806 numparameters = j + 1;
1808 if (!COM_ParseToken_QuakeC(&text, true))
1811 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1812 // parameter[j][0] = 0;
1813 if (developer_insane.integer)
1815 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1816 for (j = 0;j < numparameters;j++)
1817 Con_DPrintf(" %s", parameter[j]);
1820 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1822 if (numparameters == 2)
1824 if (!strcasecmp(parameter[1], "add"))
1826 layer->blendfunc[0] = GL_ONE;
1827 layer->blendfunc[1] = GL_ONE;
1829 else if (!strcasecmp(parameter[1], "addalpha"))
1831 layer->blendfunc[0] = GL_SRC_ALPHA;
1832 layer->blendfunc[1] = GL_ONE;
1834 else if (!strcasecmp(parameter[1], "filter"))
1836 layer->blendfunc[0] = GL_DST_COLOR;
1837 layer->blendfunc[1] = GL_ZERO;
1839 else if (!strcasecmp(parameter[1], "blend"))
1841 layer->blendfunc[0] = GL_SRC_ALPHA;
1842 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1845 else if (numparameters == 3)
1848 for (k = 0;k < 2;k++)
1850 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1851 layer->blendfunc[k] = GL_ONE;
1852 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1853 layer->blendfunc[k] = GL_ZERO;
1854 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1855 layer->blendfunc[k] = GL_SRC_COLOR;
1856 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1857 layer->blendfunc[k] = GL_SRC_ALPHA;
1858 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1859 layer->blendfunc[k] = GL_DST_COLOR;
1860 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1861 layer->blendfunc[k] = GL_DST_ALPHA;
1862 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1863 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1864 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1865 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1866 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1867 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1868 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1869 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1871 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1875 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1876 layer->alphatest = true;
1877 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1879 if (!strcasecmp(parameter[0], "clampmap"))
1880 layer->clampmap = true;
1881 layer->numframes = 1;
1882 layer->framerate = 1;
1883 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1884 &q3shader_data->char_ptrs);
1885 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1886 if (!strcasecmp(parameter[1], "$lightmap"))
1887 shader.lighting = true;
1889 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1892 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1893 layer->framerate = atof(parameter[1]);
1894 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1895 for (i = 0;i < layer->numframes;i++)
1896 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1898 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1901 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1902 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1903 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1904 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1905 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1906 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1907 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1908 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1909 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1910 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1911 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1912 else if (!strcasecmp(parameter[1], "wave"))
1914 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1915 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1916 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1917 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1919 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1921 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1924 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1925 layer->alphagen.parms[i] = atof(parameter[i+2]);
1926 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1927 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1928 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1929 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1930 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1931 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1932 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1933 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1934 else if (!strcasecmp(parameter[1], "wave"))
1936 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1937 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1938 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1939 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1941 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1943 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1946 // observed values: tcgen environment
1947 // no other values have been observed in real shaders
1948 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1949 layer->tcgen.parms[i] = atof(parameter[i+2]);
1950 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1951 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1952 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1953 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1954 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1955 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1957 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1964 // tcmod stretch sin # # # #
1965 // tcmod stretch triangle # # # #
1966 // tcmod transform # # # # # #
1967 // tcmod turb # # # #
1968 // tcmod turb sin # # # # (this is bogus)
1969 // no other values have been observed in real shaders
1970 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1971 if (!layer->tcmods[tcmodindex].tcmod)
1973 if (tcmodindex < Q3MAXTCMODS)
1975 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1976 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1977 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1978 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1979 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1980 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1981 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1982 else if (!strcasecmp(parameter[1], "stretch"))
1984 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1985 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1986 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1987 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1989 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1990 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1991 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1994 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1996 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1997 if (!strcasecmp(com_token, "}"))
2000 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2001 shader.lighting = true;
2002 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
2004 if (layer == shader.layers + 0)
2006 // vertex controlled transparency
2007 shader.vertexalpha = true;
2011 // multilayer terrain shader or similar
2012 shader.textureblendalpha = true;
2013 if (mod_q3shader_force_terrain_alphaflag.integer)
2014 shader.layers[0].dptexflags |= TEXF_ALPHA;
2018 if(mod_q3shader_force_addalpha.integer)
2020 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
2021 // this cvar brings back this behaviour
2022 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
2023 layer->blendfunc[0] = GL_SRC_ALPHA;
2026 layer->dptexflags = 0;
2027 if (layer->alphatest)
2028 layer->dptexflags |= TEXF_ALPHA;
2029 switch(layer->blendfunc[0])
2032 case GL_ONE_MINUS_SRC_ALPHA:
2033 layer->dptexflags |= TEXF_ALPHA;
2036 switch(layer->blendfunc[1])
2039 case GL_ONE_MINUS_SRC_ALPHA:
2040 layer->dptexflags |= TEXF_ALPHA;
2043 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
2044 layer->dptexflags |= TEXF_MIPMAP;
2045 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
2046 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
2047 if (layer->clampmap)
2048 layer->dptexflags |= TEXF_CLAMP;
2052 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
2054 if (j < TEXTURE_MAXFRAMES + 4)
2056 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
2057 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
2058 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
2060 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
2061 numparameters = j + 1;
2063 if (!COM_ParseToken_QuakeC(&text, true))
2066 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
2067 // parameter[j][0] = 0;
2068 if (fileindex == 0 && !strcasecmp(com_token, "}"))
2070 if (developer_insane.integer)
2072 Con_DPrintf("%s: ", shader.name);
2073 for (j = 0;j < numparameters;j++)
2074 Con_DPrintf(" %s", parameter[j]);
2077 if (numparameters < 1)
2079 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
2081 if (!strcasecmp(parameter[1], "alphashadow"))
2082 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
2083 else if (!strcasecmp(parameter[1], "areaportal"))
2084 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
2085 else if (!strcasecmp(parameter[1], "botclip"))
2086 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
2087 else if (!strcasecmp(parameter[1], "clusterportal"))
2088 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
2089 else if (!strcasecmp(parameter[1], "detail"))
2090 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
2091 else if (!strcasecmp(parameter[1], "donotenter"))
2092 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
2093 else if (!strcasecmp(parameter[1], "dust"))
2094 shader.surfaceparms |= Q3SURFACEPARM_DUST;
2095 else if (!strcasecmp(parameter[1], "hint"))
2096 shader.surfaceparms |= Q3SURFACEPARM_HINT;
2097 else if (!strcasecmp(parameter[1], "fog"))
2098 shader.surfaceparms |= Q3SURFACEPARM_FOG;
2099 else if (!strcasecmp(parameter[1], "lava"))
2100 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
2101 else if (!strcasecmp(parameter[1], "lightfilter"))
2102 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
2103 else if (!strcasecmp(parameter[1], "lightgrid"))
2104 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
2105 else if (!strcasecmp(parameter[1], "metalsteps"))
2106 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
2107 else if (!strcasecmp(parameter[1], "nodamage"))
2108 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
2109 else if (!strcasecmp(parameter[1], "nodlight"))
2110 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
2111 else if (!strcasecmp(parameter[1], "nodraw"))
2112 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
2113 else if (!strcasecmp(parameter[1], "nodrop"))
2114 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
2115 else if (!strcasecmp(parameter[1], "noimpact"))
2116 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
2117 else if (!strcasecmp(parameter[1], "nolightmap"))
2118 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
2119 else if (!strcasecmp(parameter[1], "nomarks"))
2120 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
2121 else if (!strcasecmp(parameter[1], "nomipmaps"))
2122 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2123 else if (!strcasecmp(parameter[1], "nonsolid"))
2124 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
2125 else if (!strcasecmp(parameter[1], "origin"))
2126 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
2127 else if (!strcasecmp(parameter[1], "playerclip"))
2128 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
2129 else if (!strcasecmp(parameter[1], "sky"))
2130 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2131 else if (!strcasecmp(parameter[1], "slick"))
2132 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
2133 else if (!strcasecmp(parameter[1], "slime"))
2134 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
2135 else if (!strcasecmp(parameter[1], "structural"))
2136 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
2137 else if (!strcasecmp(parameter[1], "trans"))
2138 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
2139 else if (!strcasecmp(parameter[1], "water"))
2140 shader.surfaceparms |= Q3SURFACEPARM_WATER;
2141 else if (!strcasecmp(parameter[1], "pointlight"))
2142 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
2143 else if (!strcasecmp(parameter[1], "antiportal"))
2144 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
2145 else if (!strcasecmp(parameter[1], "skip"))
2146 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
2149 // try custom surfaceparms
2150 for (j = 0; j < numcustsurfaceflags; j++)
2152 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
2154 shader.surfaceflags |= custsurfaceflags[j];
2159 if (j == numcustsurfaceflags)
2160 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
2163 else if (!strcasecmp(parameter[0], "dpshadow"))
2164 shader.dpshadow = true;
2165 else if (!strcasecmp(parameter[0], "dpnoshadow"))
2166 shader.dpnoshadow = true;
2167 else if (!strcasecmp(parameter[0], "dpnortlight"))
2168 shader.dpnortlight = true;
2169 else if (!strcasecmp(parameter[0], "dpreflectcube"))
2170 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
2171 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
2172 shader.dpmeshcollisions = true;
2173 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
2174 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
2176 if (Cvar_VariableValue(parameter[1]) == 0.0f)
2177 shader.dpshaderkill = dpshaderkill;
2179 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
2180 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
2182 const char *op = NULL;
2183 if (numparameters >= 3)
2187 if (Cvar_VariableValue(parameter[1]) != 0.0f)
2188 shader.dpshaderkill = dpshaderkill;
2190 else if (numparameters >= 4 && !strcmp(op, "=="))
2192 if (Cvar_VariableValue(parameter[1]) == atof(parameter[3]))
2193 shader.dpshaderkill = dpshaderkill;
2195 else if (numparameters >= 4 && !strcmp(op, "!="))
2197 if (Cvar_VariableValue(parameter[1]) != atof(parameter[3]))
2198 shader.dpshaderkill = dpshaderkill;
2200 else if (numparameters >= 4 && !strcmp(op, ">"))
2202 if (Cvar_VariableValue(parameter[1]) > atof(parameter[3]))
2203 shader.dpshaderkill = dpshaderkill;
2205 else if (numparameters >= 4 && !strcmp(op, "<"))
2207 if (Cvar_VariableValue(parameter[1]) < atof(parameter[3]))
2208 shader.dpshaderkill = dpshaderkill;
2210 else if (numparameters >= 4 && !strcmp(op, ">="))
2212 if (Cvar_VariableValue(parameter[1]) >= atof(parameter[3]))
2213 shader.dpshaderkill = dpshaderkill;
2215 else if (numparameters >= 4 && !strcmp(op, "<="))
2217 if (Cvar_VariableValue(parameter[1]) <= atof(parameter[3]))
2218 shader.dpshaderkill = dpshaderkill;
2222 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2225 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2227 // some q3 skies don't have the sky parm set
2228 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2229 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2231 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2233 // some q3 skies don't have the sky parm set
2234 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2235 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2236 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2238 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2240 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2241 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2243 else if (!strcasecmp(parameter[0], "nomipmaps"))
2244 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2245 else if (!strcasecmp(parameter[0], "nopicmip"))
2246 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2247 else if (!strcasecmp(parameter[0], "polygonoffset"))
2248 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2249 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2251 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2252 if(numparameters >= 2)
2254 shader.biaspolygonfactor = atof(parameter[1]);
2255 if(numparameters >= 3)
2256 shader.biaspolygonoffset = atof(parameter[2]);
2258 shader.biaspolygonoffset = 0;
2261 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2263 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2264 if (!strcasecmp(parameter[1], "sky"))
2265 shader.transparentsort = TRANSPARENTSORT_SKY;
2266 else if (!strcasecmp(parameter[1], "distance"))
2267 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2268 else if (!strcasecmp(parameter[1], "hud"))
2269 shader.transparentsort = TRANSPARENTSORT_HUD;
2271 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2273 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2275 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2276 shader.refractfactor = atof(parameter[1]);
2277 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2279 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2281 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2282 shader.reflectfactor = atof(parameter[1]);
2283 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2285 else if (!strcasecmp(parameter[0], "dpcamera"))
2287 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2289 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2291 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2292 shader.reflectmin = atof(parameter[1]);
2293 shader.reflectmax = atof(parameter[2]);
2294 shader.refractfactor = atof(parameter[3]);
2295 shader.reflectfactor = atof(parameter[4]);
2296 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2297 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2298 shader.r_water_wateralpha = atof(parameter[11]);
2300 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2302 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2303 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2305 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2307 shader.specularscalemod = atof(parameter[1]);
2309 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2311 shader.specularpowermod = atof(parameter[1]);
2313 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2315 shader.rtlightambient = atof(parameter[1]);
2317 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2319 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2320 shader.offsetmapping = OFFSETMAPPING_OFF;
2321 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2322 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2323 else if (!strcasecmp(parameter[1], "linear"))
2324 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2325 else if (!strcasecmp(parameter[1], "relief"))
2326 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2327 if (numparameters >= 3)
2328 shader.offsetscale = atof(parameter[2]);
2329 if (numparameters >= 5)
2331 if(!strcasecmp(parameter[3], "bias"))
2332 shader.offsetbias = atof(parameter[4]);
2333 else if(!strcasecmp(parameter[3], "match"))
2334 shader.offsetbias = 1.0f - atof(parameter[4]);
2335 else if(!strcasecmp(parameter[3], "match8"))
2336 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2337 else if(!strcasecmp(parameter[3], "match16"))
2338 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2341 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2344 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2345 if (!shader.deforms[deformindex].deform)
2347 if (deformindex < Q3MAXDEFORMS)
2349 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2350 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2351 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2352 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2353 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2354 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2355 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2356 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2357 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2358 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2359 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2360 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2361 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2362 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2363 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2364 else if (!strcasecmp(parameter[1], "wave" ))
2366 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2367 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2368 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2369 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2371 else if (!strcasecmp(parameter[1], "move" ))
2373 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2374 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2375 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2376 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2381 // hide this shader if a cvar said it should be killed
2382 if (shader.dpshaderkill)
2383 shader.numlayers = 0;
2384 // fix up multiple reflection types
2385 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2386 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2388 Q3Shader_AddToHash (&shader);
2392 FS_FreeSearch(search);
2393 // free custinfoparm values
2394 for (j = 0; j < numcustsurfaceflags; j++)
2395 Mem_Free(custsurfaceparmnames[j]);
2398 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
2400 unsigned short hash;
2401 q3shader_hash_entry_t* entry;
2403 Mod_LoadQ3Shaders();
2404 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2405 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2406 while (entry != NULL)
2408 if (strcasecmp (entry->shader.name, name) == 0)
2409 return &entry->shader;
2410 entry = entry->chain;
2415 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2417 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2418 shaderpass->framerate = 0.0f;
2419 shaderpass->numframes = 1;
2420 shaderpass->blendfunc[0] = GL_ONE;
2421 shaderpass->blendfunc[1] = GL_ZERO;
2422 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2423 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2424 shaderpass->alphatest = false;
2425 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2426 shaderpass->skinframes[0] = skinframe;
2430 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2433 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2434 shaderpass->alphatest = layer->alphatest != 0;
2435 shaderpass->framerate = layer->framerate;
2436 shaderpass->numframes = layer->numframes;
2437 shaderpass->blendfunc[0] = layer->blendfunc[0];
2438 shaderpass->blendfunc[1] = layer->blendfunc[1];
2439 shaderpass->rgbgen = layer->rgbgen;
2440 shaderpass->alphagen = layer->alphagen;
2441 shaderpass->tcgen = layer->tcgen;
2442 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2443 shaderpass->tcmods[j] = layer->tcmods[j];
2444 for (j = 0; j < layer->numframes; j++)
2445 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2449 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2451 int texflagsmask, texflagsor;
2452 qboolean success = true;
2453 q3shaderinfo_t *shader;
2456 strlcpy(texture->name, name, sizeof(texture->name));
2457 texture->basealpha = 1.0f;
2458 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2460 // allow disabling of picmip or compression by defaulttexflags
2462 if(!(defaulttexflags & TEXF_PICMIP))
2463 texflagsmask &= ~TEXF_PICMIP;
2464 if(!(defaulttexflags & TEXF_COMPRESS))
2465 texflagsmask &= ~TEXF_COMPRESS;
2467 if(defaulttexflags & TEXF_ISWORLD)
2468 texflagsor |= TEXF_ISWORLD;
2469 if(defaulttexflags & TEXF_ISSPRITE)
2470 texflagsor |= TEXF_ISSPRITE;
2471 // unless later loaded from the shader
2472 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2473 texture->offsetscale = 1;
2474 texture->offsetbias = 0;
2475 texture->specularscalemod = 1;
2476 texture->specularpowermod = 1;
2477 texture->rtlightambient = 0;
2478 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2479 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2480 // JUST GREP FOR "specularscalemod = 1".
2484 if (developer_loading.integer)
2485 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2487 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2489 texture->basematerialflags = MATERIALFLAG_SKY;
2490 if (shader->skyboxname[0] && loadmodel)
2492 // quake3 seems to append a _ to the skybox name, so this must do so as well
2493 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2496 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2497 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2499 texture->basematerialflags = MATERIALFLAG_WALL;
2501 if (shader->layers[0].alphatest)
2502 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2503 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2504 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2505 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2507 texture->biaspolygonoffset += shader->biaspolygonoffset;
2508 texture->biaspolygonfactor += shader->biaspolygonfactor;
2510 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2511 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2512 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2513 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2514 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2515 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2516 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2517 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2518 texture->customblendfunc[0] = GL_ONE;
2519 texture->customblendfunc[1] = GL_ZERO;
2520 texture->transparentsort = shader->transparentsort;
2521 if (shader->numlayers > 0)
2523 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2524 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2526 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2527 * additive GL_ONE GL_ONE
2528 additive weird GL_ONE GL_SRC_ALPHA
2529 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2530 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2531 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2532 brighten GL_DST_COLOR GL_ONE
2533 brighten GL_ONE GL_SRC_COLOR
2534 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2535 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2536 * modulate GL_DST_COLOR GL_ZERO
2537 * modulate GL_ZERO GL_SRC_COLOR
2538 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2539 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2540 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2541 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2542 * no blend GL_ONE GL_ZERO
2543 nothing GL_ZERO GL_ONE
2545 // if not opaque, figure out what blendfunc to use
2546 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2548 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2549 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2550 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2551 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2552 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2553 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2555 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2558 if (!shader->lighting)
2559 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2561 // here be dragons: convert quake3 shaders to material
2562 if (shader->numlayers > 0)
2565 int terrainbackgroundlayer = -1;
2566 int lightmaplayer = -1;
2567 int alphagenspecularlayer = -1;
2568 int rgbgenvertexlayer = -1;
2569 int rgbgendiffuselayer = -1;
2570 int materiallayer = -1;
2571 int endofprelayers = 0;
2572 int firstpostlayer = 0;
2573 int shaderpassindex = 0;
2574 for (i = 0; i < shader->numlayers; i++)
2576 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2578 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2579 rgbgenvertexlayer = i;
2580 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2581 rgbgendiffuselayer = i;
2582 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2583 alphagenspecularlayer = i;
2585 if (shader->numlayers >= 2
2586 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2587 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2588 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2589 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2591 // terrain blend or certain other effects involving alphatest over a regular layer
2592 terrainbackgroundlayer = 0;
2594 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2595 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2597 else if (lightmaplayer == 0)
2599 // ordinary texture but with $lightmap before diffuse
2601 firstpostlayer = lightmaplayer + 2;
2603 else if (lightmaplayer >= 1)
2605 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2606 endofprelayers = lightmaplayer - 1;
2607 materiallayer = lightmaplayer - 1;
2608 firstpostlayer = lightmaplayer + 1;
2610 else if (rgbgenvertexlayer >= 0)
2612 // map models with baked lighting
2613 materiallayer = rgbgenvertexlayer;
2614 endofprelayers = rgbgenvertexlayer;
2615 firstpostlayer = rgbgenvertexlayer + 1;
2616 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2617 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2618 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR;
2620 else if (rgbgendiffuselayer >= 0)
2622 // entity models with dynamic lighting
2623 materiallayer = rgbgendiffuselayer;
2624 endofprelayers = rgbgendiffuselayer;
2625 firstpostlayer = rgbgendiffuselayer + 1;
2626 // 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)...
2627 if (alphagenspecularlayer >= 0)
2628 firstpostlayer = alphagenspecularlayer + 1;
2632 // special effects shaders - treat first as primary layer and do everything else as post
2637 // convert the main material layer
2638 // 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
2639 if (materiallayer >= 0)
2640 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2641 // convert the terrain background blend layer (if any)
2642 if (terrainbackgroundlayer >= 0)
2643 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2644 // convert the prepass layers (if any)
2645 texture->startpreshaderpass = shaderpassindex;
2646 for (i = 0; i < endofprelayers; i++)
2647 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2648 texture->endpreshaderpass = shaderpassindex;
2649 texture->startpostshaderpass = shaderpassindex;
2650 // convert the postpass layers (if any)
2651 for (i = firstpostlayer; i < shader->numlayers; i++)
2652 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2653 texture->startpostshaderpass = shaderpassindex;
2656 if (shader->dpshadow)
2657 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2658 if (shader->dpnoshadow)
2659 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2660 if (shader->dpnortlight)
2661 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2662 if (shader->vertexalpha)
2663 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2664 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2665 texture->reflectmin = shader->reflectmin;
2666 texture->reflectmax = shader->reflectmax;
2667 texture->refractfactor = shader->refractfactor;
2668 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2669 texture->reflectfactor = shader->reflectfactor;
2670 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2671 texture->r_water_wateralpha = shader->r_water_wateralpha;
2672 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2673 texture->offsetmapping = shader->offsetmapping;
2674 texture->offsetscale = shader->offsetscale;
2675 texture->offsetbias = shader->offsetbias;
2676 texture->specularscalemod = shader->specularscalemod;
2677 texture->specularpowermod = shader->specularpowermod;
2678 texture->rtlightambient = shader->rtlightambient;
2679 if (shader->dpreflectcube[0])
2680 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2682 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2683 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2684 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2685 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2686 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2687 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2688 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2689 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2690 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2692 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2693 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2694 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2695 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2696 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2697 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2698 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2699 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2700 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2701 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2702 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2703 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2704 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2705 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2706 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2707 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2708 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2709 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2710 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2711 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2712 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2713 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2714 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2715 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2716 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2717 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2718 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2719 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2720 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2721 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2722 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2723 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2725 texture->surfaceflags = shader->surfaceflags;
2726 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2727 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2728 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2729 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2730 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2731 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2732 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2733 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2734 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2735 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2736 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2737 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2738 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2739 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2740 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2741 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2742 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2743 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2744 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2745 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2746 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2747 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2748 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2749 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2750 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2751 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2752 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2753 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2754 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2755 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2756 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2757 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2759 if (shader->dpmeshcollisions)
2760 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2761 if (shader->dpshaderkill && developer_extra.integer)
2762 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2764 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2766 if (developer_extra.integer)
2767 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2768 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2770 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2772 if (developer_extra.integer)
2773 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2774 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2775 texture->supercontents = SUPERCONTENTS_SOLID;
2779 if (developer_extra.integer)
2780 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2781 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2783 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2784 texture->supercontents = SUPERCONTENTS_SOLID;
2786 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2788 texture->basematerialflags = MATERIALFLAG_SKY;
2789 texture->supercontents = SUPERCONTENTS_SKY;
2793 texture->basematerialflags = defaultmaterialflags;
2794 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2796 if(cls.state == ca_dedicated)
2798 texture->materialshaderpass = NULL;
2803 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2806 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2807 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2808 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2809 if (texture->q2contents)
2810 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2814 if (!success && warnmissing)
2815 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2818 // init the animation variables
2819 texture->currentframe = texture;
2820 texture->currentmaterialflags = texture->basematerialflags;
2821 if (!texture->materialshaderpass)
2822 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2823 if (!texture->materialshaderpass->skinframes[0])
2824 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2825 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2826 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2830 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2832 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2833 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2835 strlcpy(texture->name, name, sizeof(texture->name));
2836 texture->basealpha = 1.0f;
2837 texture->basematerialflags = materialflags;
2838 texture->supercontents = supercontents;
2840 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2841 texture->offsetscale = 1;
2842 texture->offsetbias = 0;
2843 texture->specularscalemod = 1;
2844 texture->specularpowermod = 1;
2845 texture->rtlightambient = 0;
2846 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2847 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2848 // JUST GREP FOR "specularscalemod = 1".
2850 if (developer_extra.integer)
2851 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2853 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2855 // init the animation variables
2856 texture->currentmaterialflags = texture->basematerialflags;
2857 texture->currentframe = texture;
2858 texture->currentskinframe = skinframe;
2859 texture->backgroundcurrentskinframe = NULL;
2862 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2865 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2867 if (texture->shaderpasses[i])
2870 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2871 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2872 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2873 Mem_Free(texture->shaderpasses[i]);
2874 texture->shaderpasses[i] = NULL;
2877 texture->materialshaderpass = NULL;
2878 texture->currentskinframe = NULL;
2879 texture->backgroundcurrentskinframe = NULL;
2882 skinfile_t *Mod_LoadSkinFiles(void)
2884 int i, words, line, wordsoverflow;
2887 skinfile_t *skinfile = NULL, *first = NULL;
2888 skinfileitem_t *skinfileitem;
2889 char word[10][MAX_QPATH];
2894 U_bodyBox,models/players/Legoman/BikerA2.tga
2895 U_RArm,models/players/Legoman/BikerA1.tga
2896 U_LArm,models/players/Legoman/BikerA1.tga
2897 U_armor,common/nodraw
2898 U_sword,common/nodraw
2899 U_shield,common/nodraw
2900 U_homb,common/nodraw
2901 U_backpack,common/nodraw
2902 U_colcha,common/nodraw
2907 memset(word, 0, sizeof(word));
2908 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2910 // If it's the first file we parse
2911 if (skinfile == NULL)
2913 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2918 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2919 skinfile = skinfile->next;
2921 skinfile->next = NULL;
2923 for(line = 0;;line++)
2926 if (!COM_ParseToken_QuakeC(&data, true))
2928 if (!strcmp(com_token, "\n"))
2931 wordsoverflow = false;
2935 strlcpy(word[words++], com_token, sizeof (word[0]));
2937 wordsoverflow = true;
2939 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2942 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);
2945 // words is always >= 1
2946 if (!strcmp(word[0], "replace"))
2950 if (developer_loading.integer)
2951 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2952 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2953 skinfileitem->next = skinfile->items;
2954 skinfile->items = skinfileitem;
2955 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2956 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2959 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]);
2961 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2963 // tag name, like "tag_weapon,"
2964 // not used for anything (not even in Quake3)
2966 else if (words >= 2 && !strcmp(word[1], ","))
2968 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2969 if (developer_loading.integer)
2970 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2971 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2972 skinfileitem->next = skinfile->items;
2973 skinfile->items = skinfileitem;
2974 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2975 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2978 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);
2983 loadmodel->numskins = i;
2987 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2990 skinfileitem_t *skinfileitem, *nextitem;
2991 for (;skinfile;skinfile = next)
2993 next = skinfile->next;
2994 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2996 nextitem = skinfileitem->next;
2997 Mem_Free(skinfileitem);
3003 int Mod_CountSkinFiles(skinfile_t *skinfile)
3006 for (i = 0;skinfile;skinfile = skinfile->next, i++);
3010 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
3013 double isnap = 1.0 / snap;
3014 for (i = 0;i < numvertices*numcomponents;i++)
3015 vertices[i] = floor(vertices[i]*isnap)*snap;
3018 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
3020 int i, outtriangles;
3021 float edgedir1[3], edgedir2[3], temp[3];
3022 // a degenerate triangle is one with no width (thickness, surface area)
3023 // these are characterized by having all 3 points colinear (along a line)
3024 // or having two points identical
3025 // the simplest check is to calculate the triangle's area
3026 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
3028 // calculate first edge
3029 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
3030 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
3031 CrossProduct(edgedir1, edgedir2, temp);
3032 if (VectorLength2(temp) < 0.001f)
3033 continue; // degenerate triangle (no area)
3034 // valid triangle (has area)
3035 VectorCopy(inelement3i, outelement3i);
3039 return outtriangles;
3042 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
3045 int firstvertex, lastvertex;
3046 if (numelements > 0 && elements)
3048 firstvertex = lastvertex = elements[0];
3049 for (i = 1;i < numelements;i++)
3052 firstvertex = min(firstvertex, e);
3053 lastvertex = max(lastvertex, e);
3057 firstvertex = lastvertex = 0;
3058 if (firstvertexpointer)
3059 *firstvertexpointer = firstvertex;
3060 if (lastvertexpointer)
3061 *lastvertexpointer = lastvertex;
3064 void Mod_MakeSortedSurfaces(dp_model_t *mod)
3066 // make an optimal set of texture-sorted batches to draw...
3068 int *firstsurfacefortexture;
3069 int *numsurfacesfortexture;
3070 if (!mod->sortedmodelsurfaces)
3071 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
3072 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
3073 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
3074 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
3075 for (j = 0;j < mod->nummodelsurfaces;j++)
3077 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
3078 t = (int)(surface->texture - mod->data_textures);
3079 numsurfacesfortexture[t]++;
3082 for (t = 0;t < mod->num_textures;t++)
3084 firstsurfacefortexture[t] = j;
3085 j += numsurfacesfortexture[t];
3087 for (j = 0;j < mod->nummodelsurfaces;j++)
3089 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
3090 t = (int)(surface->texture - mod->data_textures);
3091 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
3093 Mem_Free(firstsurfacefortexture);
3094 Mem_Free(numsurfacesfortexture);
3097 void Mod_BuildVBOs(void)
3099 if (!loadmodel->surfmesh.num_vertices)
3102 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
3105 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
3107 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
3109 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
3110 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
3115 // build r_vertexmesh_t array
3116 // (compressed interleaved array for D3D)
3117 if (!loadmodel->surfmesh.data_vertexmesh && vid.useinterleavedarrays)
3120 int numvertices = loadmodel->surfmesh.num_vertices;
3121 r_vertexmesh_t *vertexmesh;
3122 loadmodel->surfmesh.data_vertexmesh = vertexmesh = (r_vertexmesh_t*)Mem_Alloc(loadmodel->mempool, numvertices * sizeof(r_vertexmesh_t));
3123 for (vertexindex = 0;vertexindex < numvertices;vertexindex++, vertexmesh++)
3125 VectorCopy(loadmodel->surfmesh.data_vertex3f + 3*vertexindex, vertexmesh->vertex3f);
3126 VectorScale(loadmodel->surfmesh.data_svector3f + 3*vertexindex, 1.0f, vertexmesh->svector3f);
3127 VectorScale(loadmodel->surfmesh.data_tvector3f + 3*vertexindex, 1.0f, vertexmesh->tvector3f);
3128 VectorScale(loadmodel->surfmesh.data_normal3f + 3*vertexindex, 1.0f, vertexmesh->normal3f);
3129 if (loadmodel->surfmesh.data_lightmapcolor4f)
3130 Vector4Copy(loadmodel->surfmesh.data_lightmapcolor4f + 4*vertexindex, vertexmesh->color4f);
3131 Vector2Copy(loadmodel->surfmesh.data_texcoordtexture2f + 2*vertexindex, vertexmesh->texcoordtexture2f);
3132 if (loadmodel->surfmesh.data_texcoordlightmap2f)
3133 Vector2Scale(loadmodel->surfmesh.data_texcoordlightmap2f + 2*vertexindex, 1.0f, vertexmesh->texcoordlightmap2f);
3134 if (loadmodel->surfmesh.data_skeletalindex4ub)
3135 Vector4Copy(loadmodel->surfmesh.data_skeletalindex4ub + 4*vertexindex, vertexmesh->skeletalindex4ub);
3136 if (loadmodel->surfmesh.data_skeletalweight4ub)
3137 Vector4Copy(loadmodel->surfmesh.data_skeletalweight4ub + 4*vertexindex, vertexmesh->skeletalweight4ub);
3141 // upload short indices as a buffer
3142 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
3143 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);
3145 // upload int indices as a buffer
3146 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
3147 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);
3149 // only build a vbo if one has not already been created (this is important for brush models which load specially)
3150 // vertex buffer is several arrays and we put them in the same buffer
3152 // is this wise? the texcoordtexture2f array is used with dynamic
3153 // vertex/svector/tvector/normal when rendering animated models, on the
3154 // other hand animated models don't use a lot of vertices anyway...
3155 if (!loadmodel->surfmesh.vbo_vertexbuffer && !vid.useinterleavedarrays)
3160 loadmodel->surfmesh.vbooffset_vertexmesh = size;if (loadmodel->surfmesh.data_vertexmesh ) size += loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t);
3161 loadmodel->surfmesh.vbooffset_vertex3f = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3162 loadmodel->surfmesh.vbooffset_svector3f = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3163 loadmodel->surfmesh.vbooffset_tvector3f = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3164 loadmodel->surfmesh.vbooffset_normal3f = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3165 loadmodel->surfmesh.vbooffset_texcoordtexture2f = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3166 loadmodel->surfmesh.vbooffset_texcoordlightmap2f = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3167 loadmodel->surfmesh.vbooffset_lightmapcolor4f = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
3168 loadmodel->surfmesh.vbooffset_skeletalindex4ub = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3169 loadmodel->surfmesh.vbooffset_skeletalweight4ub = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3170 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
3171 if (loadmodel->surfmesh.data_vertexmesh ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertexmesh , loadmodel->surfmesh.data_vertexmesh , loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t));
3172 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertex3f , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3173 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_svector3f , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3174 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_tvector3f , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3175 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_normal3f , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3176 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordtexture2f , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3177 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordlightmap2f, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3178 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.vbooffset_lightmapcolor4f , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
3179 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalindex4ub , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3180 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalweight4ub , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3181 loadmodel->surfmesh.vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3186 extern cvar_t mod_obj_orientation;
3187 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3189 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3191 const char *texname;
3193 const float *v, *vn, *vt;
3195 size_t outbufferpos = 0;
3196 size_t outbuffermax = 0x100000;
3197 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3198 const msurface_t *surface;
3199 const int maxtextures = 256;
3200 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3201 dp_model_t *submodel;
3203 // construct the mtllib file
3204 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3207 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3210 countvertices += surface->num_vertices;
3211 countfaces += surface->num_triangles;
3212 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3213 for (textureindex = 0;textureindex < counttextures;textureindex++)
3214 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3216 if (textureindex < counttextures)
3217 continue; // already wrote this material entry
3218 if (textureindex >= maxtextures)
3219 continue; // just a precaution
3220 textureindex = counttextures++;
3221 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3222 if (outbufferpos >= outbuffermax >> 1)
3225 oldbuffer = outbuffer;
3226 outbuffer = (char *) Z_Malloc(outbuffermax);
3227 memcpy(outbuffer, oldbuffer, outbufferpos);
3230 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");
3235 // write the mtllib file
3236 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3238 // construct the obj file
3240 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);
3244 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)
3246 if (outbufferpos >= outbuffermax >> 1)
3249 oldbuffer = outbuffer;
3250 outbuffer = (char *) Z_Malloc(outbuffermax);
3251 memcpy(outbuffer, oldbuffer, outbufferpos);
3254 if(mod_obj_orientation.integer)
3255 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]);
3257 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]);
3262 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3264 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3267 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3268 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3270 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3271 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3274 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3276 if (outbufferpos >= outbuffermax >> 1)
3279 oldbuffer = outbuffer;
3280 outbuffer = (char *) Z_Malloc(outbuffermax);
3281 memcpy(outbuffer, oldbuffer, outbufferpos);
3287 if(mod_obj_orientation.integer)
3288 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);
3290 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);
3297 // write the obj file
3298 FS_WriteFile(filename, outbuffer, outbufferpos);
3302 Z_Free(texturenames);
3305 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3308 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3310 int countnodes = 0, counttriangles = 0, countframes = 0;
3318 size_t outbufferpos = 0;
3319 size_t outbuffermax = 0x100000;
3320 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3321 const msurface_t *surface;
3322 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3325 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3327 if (outbufferpos >= outbuffermax >> 1)
3330 oldbuffer = outbuffer;
3331 outbuffer = (char *) Z_Malloc(outbuffermax);
3332 memcpy(outbuffer, oldbuffer, outbufferpos);
3336 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3340 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3343 for (poseindex = 0;poseindex < numposes;poseindex++)
3346 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3349 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3353 matrix4x4_t posematrix;
3354 if (outbufferpos >= outbuffermax >> 1)
3357 oldbuffer = outbuffer;
3358 outbuffer = (char *) Z_Malloc(outbuffermax);
3359 memcpy(outbuffer, oldbuffer, outbufferpos);
3363 // strangely the smd angles are for a transposed matrix, so we
3364 // have to generate a transposed matrix, then convert that...
3365 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3366 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3367 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3368 if (angles[0] >= 180) angles[0] -= 360;
3369 if (angles[1] >= 180) angles[1] -= 360;
3370 if (angles[2] >= 180) angles[2] -= 360;
3374 float a = DEG2RAD(angles[ROLL]);
3375 float b = DEG2RAD(angles[PITCH]);
3376 float c = DEG2RAD(angles[YAW]);
3377 float cy, sy, cp, sp, cr, sr;
3379 // smd matrix construction, for comparing
3390 test[1][0] = sr*sp*cy+cr*-sy;
3391 test[1][1] = sr*sp*sy+cr*cy;
3393 test[2][0] = (cr*sp*cy+-sr*-sy);
3394 test[2][1] = (cr*sp*sy+-sr*cy);
3396 test[3][0] = pose[9];
3397 test[3][1] = pose[10];
3398 test[3][2] = pose[11];
3401 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]));
3406 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3411 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3414 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3416 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3419 if (outbufferpos >= outbuffermax >> 1)
3422 oldbuffer = outbuffer;
3423 outbuffer = (char *) Z_Malloc(outbuffermax);
3424 memcpy(outbuffer, oldbuffer, outbufferpos);
3427 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3430 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3432 const int index = e[2-cornerindex];
3433 const float *v = model->surfmesh.data_vertex3f + index * 3;
3434 const float *vn = model->surfmesh.data_normal3f + index * 3;
3435 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3436 const int b = model->surfmesh.blends[index];
3437 if (b < model->num_bones)
3438 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]);
3441 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3442 const unsigned char *wi = w->index;
3443 const unsigned char *wf = w->influence;
3444 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);
3445 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);
3446 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);
3447 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]);
3454 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3459 FS_WriteFile(filename, outbuffer, outbufferpos);
3462 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3469 decompiles a model to editable files
3472 static void Mod_Decompile_f(void)
3474 int i, j, k, l, first, count;
3476 char inname[MAX_QPATH];
3477 char outname[MAX_QPATH];
3478 char mtlname[MAX_QPATH];
3479 char basename[MAX_QPATH];
3480 char animname[MAX_QPATH];
3481 char animname2[MAX_QPATH];
3482 char zymtextbuffer[16384];
3483 char dpmtextbuffer[16384];
3484 char framegroupstextbuffer[16384];
3485 int zymtextsize = 0;
3486 int dpmtextsize = 0;
3487 int framegroupstextsize = 0;
3490 if (Cmd_Argc() != 2)
3492 Con_Print("usage: modeldecompile <filename>\n");
3496 strlcpy(inname, Cmd_Argv(1), sizeof(inname));
3497 FS_StripExtension(inname, basename, sizeof(basename));
3499 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3502 Con_Print("No such model\n");
3505 if (mod->brush.submodel)
3507 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3508 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3509 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3512 if (!mod->surfmesh.num_triangles)
3514 Con_Print("Empty model (or sprite)\n");
3518 // export OBJ if possible (not on sprites)
3519 if (mod->surfmesh.num_triangles)
3521 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3522 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3523 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3526 // export SMD if possible (only for skeletal models)
3527 if (mod->surfmesh.num_triangles && mod->num_bones)
3529 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3530 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3531 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3532 if (l > 0) zymtextsize += l;
3533 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3534 if (l > 0) dpmtextsize += l;
3535 for (i = 0;i < mod->numframes;i = j)
3537 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3538 first = mod->animscenes[i].firstframe;
3539 if (mod->animscenes[i].framecount > 1)
3542 count = mod->animscenes[i].framecount;
3548 // check for additional frames with same name
3549 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3550 if(animname[l] < '0' || animname[l] > '9')
3552 if(k > 0 && animname[k-1] == '_')
3555 count = mod->num_poses - first;
3556 for (j = i + 1;j < mod->numframes;j++)
3558 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3559 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3560 if(animname2[l] < '0' || animname2[l] > '9')
3562 if(k > 0 && animname[k-1] == '_')
3565 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3567 count = mod->animscenes[j].firstframe - first;
3571 // if it's only one frame, use the original frame name
3573 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3576 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3577 Mod_Decompile_SMD(mod, outname, first, count, false);
3578 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3580 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3581 if (l > 0) zymtextsize += l;
3583 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3585 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3586 if (l > 0) dpmtextsize += l;
3588 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3590 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3591 if (l > 0) framegroupstextsize += l;
3595 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3597 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3598 if (framegroupstextsize)
3599 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3603 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3606 memset(state, 0, sizeof(*state));
3607 state->width = width;
3608 state->height = height;
3609 state->currentY = 0;
3610 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3611 for (y = 0;y < state->height;y++)
3613 state->rows[y].currentX = 0;
3614 state->rows[y].rowY = -1;
3618 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3621 state->currentY = 0;
3622 for (y = 0;y < state->height;y++)
3624 state->rows[y].currentX = 0;
3625 state->rows[y].rowY = -1;
3629 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3632 Mem_Free(state->rows);
3633 memset(state, 0, sizeof(*state));
3636 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3638 mod_alloclightmap_row_t *row;
3641 row = state->rows + blockheight;
3642 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3644 if (state->currentY + blockheight <= state->height)
3646 // use the current allocation position
3647 row->rowY = state->currentY;
3649 state->currentY += blockheight;
3653 // find another position
3654 for (y = blockheight;y < state->height;y++)
3656 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3658 row = state->rows + y;
3662 if (y == state->height)
3667 *outx = row->currentX;
3668 row->currentX += blockwidth;
3673 typedef struct lightmapsample_s
3677 float *vertex_color;
3678 unsigned char *lm_bgr;
3679 unsigned char *lm_dir;
3683 typedef struct lightmapvertex_s
3688 float texcoordbase[2];
3689 float texcoordlightmap[2];
3690 float lightcolor[4];
3694 typedef struct lightmaptriangle_s
3702 // 2D modelspace coordinates of min corner
3703 // snapped to lightmap grid but not in grid coordinates
3705 // 2D modelspace to lightmap coordinate scale
3713 typedef struct lightmaplight_s
3724 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3726 #define MAX_LIGHTMAPSAMPLES 64
3727 static int mod_generatelightmaps_numoffsets[3];
3728 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3730 static int mod_generatelightmaps_numlights;
3731 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3733 extern cvar_t r_shadow_lightattenuationdividebias;
3734 extern cvar_t r_shadow_lightattenuationlinearscale;
3736 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3741 float relativepoint[3];
3748 float lightorigin[3];
3752 float lightcolor[3];
3754 for (i = 0;i < 5*3;i++)
3756 for (index = 0;;index++)
3758 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3763 lightradius2 = lightradius * lightradius;
3764 VectorSubtract(lightorigin, pos, relativepoint);
3765 dist2 = VectorLength2(relativepoint);
3766 if (dist2 >= lightradius2)
3768 lightiradius = 1.0f / lightradius;
3769 dist = sqrt(dist2) * lightiradius;
3770 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3771 if (intensity <= 0.0f)
3773 if (model && model->TraceLine)
3775 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3776 if (trace.fraction < 1)
3779 // scale down intensity to add to both ambient and diffuse
3780 //intensity *= 0.5f;
3781 VectorNormalize(relativepoint);
3782 VectorScale(lightcolor, intensity, color);
3783 VectorMA(sample , 0.5f , color, sample );
3784 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3785 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3786 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3787 // calculate a weighted average light direction as well
3788 intensity *= VectorLength(color);
3789 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3791 // calculate the direction we'll use to reduce the sample to a directional light source
3792 VectorCopy(sample + 12, dir);
3793 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3794 VectorNormalize(dir);
3795 // extract the diffuse color along the chosen direction and scale it
3796 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3797 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3798 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3799 // subtract some of diffuse from ambient
3800 VectorMA(sample, -0.333f, diffuse, ambient);
3801 // store the normalized lightdir
3802 VectorCopy(dir, lightdir);
3805 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3809 const msurface_t *surface;
3810 const float *vertex3f = model->surfmesh.data_vertex3f;
3811 const int *element3i = model->surfmesh.data_element3i;
3814 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3816 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3818 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3820 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3822 VectorCopy(vertex3f + 3*e[0], v2[0]);
3823 VectorCopy(vertex3f + 3*e[1], v2[1]);
3824 VectorCopy(vertex3f + 3*e[2], v2[2]);
3825 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3830 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3832 int maxnodes = 1<<14;
3833 svbsp_node_t *nodes;
3838 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3839 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3840 VectorCopy(lightinfo->origin, origin);
3841 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3844 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3845 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3846 if (svbsp.ranoutofnodes)
3849 if (maxnodes > 1<<22)
3855 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3860 if (svbsp.numnodes > 0)
3862 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3863 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3864 lightinfo->svbsp = svbsp;
3869 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3873 lightmaplight_t *lightinfo;
3877 mod_generatelightmaps_numlights = 0;
3878 for (index = 0;;index++)
3880 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3884 mod_generatelightmaps_numlights++;
3886 if (mod_generatelightmaps_numlights > 0)
3888 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3889 lightinfo = mod_generatelightmaps_lightinfo;
3890 for (index = 0;;index++)
3892 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3899 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3901 lightinfo->iradius = 1.0f / lightinfo->radius;
3902 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3903 // TODO: compute svbsp
3904 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3908 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3911 if (mod_generatelightmaps_lightinfo)
3913 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3914 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3915 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3916 Mem_Free(mod_generatelightmaps_lightinfo);
3918 mod_generatelightmaps_lightinfo = NULL;
3919 mod_generatelightmaps_numlights = 0;
3922 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3924 const svbsp_node_t *node;
3925 const svbsp_node_t *nodes = svbsp->nodes;
3930 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3932 return num == -1; // true if empty, false if solid (shadowed)
3935 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3938 float relativepoint[3];
3947 const lightmaplight_t *lightinfo;
3949 for (i = 0;i < 5*3;i++)
3951 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3953 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3954 VectorSubtract(lightinfo->origin, pos, relativepoint);
3955 // don't accept light from behind a surface, it causes bad shading
3956 if (normal && DotProduct(relativepoint, normal) <= 0)
3958 dist2 = VectorLength2(relativepoint);
3959 if (dist2 >= lightinfo->radius2)
3961 dist = sqrt(dist2) * lightinfo->iradius;
3962 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3965 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3969 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3971 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3973 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3976 // for light grid we'd better check visibility of the offset point
3977 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3978 if (trace.fraction < 1)
3979 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3982 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3987 // scale intensity according to how many rays succeeded
3988 // we know one test is valid, half of the rest will fail...
3989 //if (normal && tests > 1)
3990 // intensity *= (tests - 1.0f) / tests;
3991 intensity *= (float)hits / tests;
3993 // scale down intensity to add to both ambient and diffuse
3994 //intensity *= 0.5f;
3995 VectorNormalize(relativepoint);
3996 VectorScale(lightinfo->color, intensity, color);
3997 VectorMA(sample , 0.5f , color, sample );
3998 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3999 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
4000 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
4001 // calculate a weighted average light direction as well
4002 intensity *= VectorLength(color);
4003 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
4007 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
4013 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
4014 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
4015 VectorCopy(sample + 12, dir);
4016 VectorNormalize(dir);
4017 //VectorAdd(dir, normal, dir);
4018 //VectorNormalize(dir);
4019 f = DotProduct(dir, normal);
4020 f = max(0, f) * 255.0f;
4021 VectorScale(sample, f, color);
4022 //VectorCopy(normal, dir);
4023 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
4024 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
4025 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
4026 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
4028 lm_dir[0] = (unsigned char)dir[2];
4029 lm_dir[1] = (unsigned char)dir[1];
4030 lm_dir[2] = (unsigned char)dir[0];
4034 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
4037 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
4038 VectorCopy(sample, vertex_color);
4041 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
4047 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
4048 // calculate the direction we'll use to reduce the sample to a directional light source
4049 VectorCopy(sample + 12, dir);
4050 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
4051 VectorNormalize(dir);
4052 // extract the diffuse color along the chosen direction and scale it
4053 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
4054 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
4055 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
4056 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
4057 VectorScale(sample, 127.5f, ambient);
4058 VectorMA(ambient, -0.333f, diffuse, ambient);
4059 // encode to the grid format
4060 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
4061 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
4062 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
4063 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
4064 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
4065 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
4066 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
4067 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
4068 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));}
4071 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
4076 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
4077 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
4078 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
4079 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
4080 radius[0] = mod_generatelightmaps_lightmapradius.value;
4081 radius[1] = mod_generatelightmaps_vertexradius.value;
4082 radius[2] = mod_generatelightmaps_gridradius.value;
4083 for (i = 0;i < 3;i++)
4085 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
4088 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
4093 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
4095 msurface_t *surface;
4098 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4100 surface = model->data_surfaces + surfaceindex;
4101 surface->lightmaptexture = NULL;
4102 surface->deluxemaptexture = NULL;
4104 if (model->brushq3.data_lightmaps)
4106 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
4107 if (model->brushq3.data_lightmaps[i])
4108 R_FreeTexture(model->brushq3.data_lightmaps[i]);
4109 Mem_Free(model->brushq3.data_lightmaps);
4110 model->brushq3.data_lightmaps = NULL;
4112 if (model->brushq3.data_deluxemaps)
4114 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
4115 if (model->brushq3.data_deluxemaps[i])
4116 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
4117 Mem_Free(model->brushq3.data_deluxemaps);
4118 model->brushq3.data_deluxemaps = NULL;
4122 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
4124 msurface_t *surface;
4130 surfmesh_t oldsurfmesh;
4132 unsigned char *data;
4133 oldsurfmesh = model->surfmesh;
4134 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
4135 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
4137 size += model->surfmesh.num_vertices * sizeof(float[3]);
4138 size += model->surfmesh.num_vertices * sizeof(float[3]);
4139 size += model->surfmesh.num_vertices * sizeof(float[3]);
4140 size += model->surfmesh.num_vertices * sizeof(float[3]);
4141 size += model->surfmesh.num_vertices * sizeof(float[2]);
4142 size += model->surfmesh.num_vertices * sizeof(float[2]);
4143 size += model->surfmesh.num_vertices * sizeof(float[4]);
4144 data = (unsigned char *)Mem_Alloc(model->mempool, size);
4145 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4146 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4147 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4148 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4149 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
4150 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
4151 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
4152 if (model->surfmesh.num_vertices > 65536)
4153 model->surfmesh.data_element3s = NULL;
4155 if (model->surfmesh.data_element3i_indexbuffer)
4156 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
4157 model->surfmesh.data_element3i_indexbuffer = NULL;
4158 if (model->surfmesh.data_element3s_indexbuffer)
4159 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
4160 model->surfmesh.data_element3s_indexbuffer = NULL;
4161 if (model->surfmesh.vbo_vertexbuffer)
4162 R_Mesh_DestroyMeshBuffer(model->surfmesh.vbo_vertexbuffer);
4163 model->surfmesh.vbo_vertexbuffer = 0;
4165 // convert all triangles to unique vertex data
4167 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4169 surface = model->data_surfaces + surfaceindex;
4170 surface->num_firstvertex = outvertexindex;
4171 surface->num_vertices = surface->num_triangles*3;
4172 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4173 for (i = 0;i < surface->num_triangles*3;i++)
4176 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4177 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4178 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4179 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4180 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4181 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4182 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4183 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4184 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4185 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4186 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4187 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4188 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4189 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4190 if (oldsurfmesh.data_texcoordlightmap2f)
4192 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4193 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4195 if (oldsurfmesh.data_lightmapcolor4f)
4197 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4198 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4199 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4200 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4203 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4204 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4208 if (model->surfmesh.data_element3s)
4209 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4210 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4212 // find and update all submodels to use this new surfmesh data
4213 for (i = 0;i < model->brush.numsubmodels;i++)
4214 model->brush.submodels[i]->surfmesh = model->surfmesh;
4217 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
4219 msurface_t *surface;
4225 lightmaptriangle_t *triangle;
4226 // generate lightmap triangle structs
4227 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4228 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4230 surface = model->data_surfaces + surfaceindex;
4231 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4232 for (i = 0;i < surface->num_triangles;i++)
4234 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4235 triangle->triangleindex = surface->num_firsttriangle+i;
4236 triangle->surfaceindex = surfaceindex;
4237 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4238 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4239 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4240 // calculate bounds of triangle
4241 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4242 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4243 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4244 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4245 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4246 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4247 // pick an axial projection based on the triangle normal
4248 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4250 if (fabs(normal[1]) > fabs(normal[axis]))
4252 if (fabs(normal[2]) > fabs(normal[axis]))
4254 triangle->axis = axis;
4259 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4261 if (mod_generatelightmaps_lightmaptriangles)
4262 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4263 mod_generatelightmaps_lightmaptriangles = NULL;
4266 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4268 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4270 msurface_t *surface;
4284 float trianglenormal[3];
4285 float samplecenter[3];
4286 float samplenormal[3];
4292 float lmscalepixels;
4295 float lm_basescalepixels;
4296 int lm_borderpixels;
4300 lightmaptriangle_t *triangle;
4301 unsigned char *lightmappixels;
4302 unsigned char *deluxemappixels;
4303 mod_alloclightmap_state_t lmstate;
4306 // generate lightmap projection information for all triangles
4307 if (model->texturepool == NULL)
4308 model->texturepool = R_AllocTexturePool();
4309 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4310 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4311 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4312 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4313 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4315 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4317 surface = model->data_surfaces + surfaceindex;
4318 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4319 lmscalepixels = lm_basescalepixels;
4320 for (retry = 0;retry < 30;retry++)
4322 // after a couple failed attempts, degrade quality to make it fit
4324 lmscalepixels *= 0.5f;
4325 for (i = 0;i < surface->num_triangles;i++)
4327 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4328 triangle->lightmapindex = lightmapnumber;
4329 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4330 // pick two planar axes for projection
4331 // lightmap coordinates here are in pixels
4332 // lightmap projections are snapped to pixel grid explicitly, such
4333 // that two neighboring triangles sharing an edge and projection
4334 // axis will have identical sampl espacing along their shared edge
4336 for (j = 0;j < 3;j++)
4338 if (j == triangle->axis)
4340 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4341 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4342 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4343 triangle->lmbase[k] = lmmins/lmscalepixels;
4344 triangle->lmscale[k] = lmscalepixels;
4347 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4350 // if all fit in this texture, we're done with this surface
4351 if (i == surface->num_triangles)
4353 // if we haven't maxed out the lightmap size yet, we retry the
4354 // entire surface batch...
4355 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4357 lm_texturesize *= 2;
4360 Mod_AllocLightmap_Free(&lmstate);
4361 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4364 // if we have maxed out the lightmap size, and this triangle does
4365 // not fit in the same texture as the rest of the surface, we have
4366 // to retry the entire surface in a new texture (can only use one)
4367 // with multiple retries, the lightmap quality degrades until it
4368 // fits (or gives up)
4369 if (surfaceindex > 0)
4371 Mod_AllocLightmap_Reset(&lmstate);
4375 Mod_AllocLightmap_Free(&lmstate);
4377 // now put triangles together into lightmap textures, and do not allow
4378 // triangles of a surface to go into different textures (as that would
4379 // require rewriting the surface list)
4380 model->brushq3.deluxemapping_modelspace = true;
4381 model->brushq3.deluxemapping = true;
4382 model->brushq3.num_mergedlightmaps = lightmapnumber;
4383 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4384 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4385 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4386 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4387 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4389 surface = model->data_surfaces + surfaceindex;
4390 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4391 for (i = 0;i < surface->num_triangles;i++)
4393 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4394 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4395 VectorNormalize(trianglenormal);
4396 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4397 axis = triangle->axis;
4398 axis1 = axis == 0 ? 1 : 0;
4399 axis2 = axis == 2 ? 1 : 2;
4400 lmiscale[0] = 1.0f / triangle->lmscale[0];
4401 lmiscale[1] = 1.0f / triangle->lmscale[1];
4402 if (trianglenormal[axis] < 0)
4403 VectorNegate(trianglenormal, trianglenormal);
4404 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4405 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4406 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4407 for (j = 0;j < 3;j++)
4409 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4410 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4411 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4413 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4414 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4415 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4416 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]);
4426 forward[1] = 1.0f / triangle->lmscale[0];
4430 left[2] = 1.0f / triangle->lmscale[1];
4435 origin[1] = triangle->lmbase[0];
4436 origin[2] = triangle->lmbase[1];
4439 forward[0] = 1.0f / triangle->lmscale[0];
4444 left[2] = 1.0f / triangle->lmscale[1];
4448 origin[0] = triangle->lmbase[0];
4450 origin[2] = triangle->lmbase[1];
4453 forward[0] = 1.0f / triangle->lmscale[0];
4457 left[1] = 1.0f / triangle->lmscale[1];
4462 origin[0] = triangle->lmbase[0];
4463 origin[1] = triangle->lmbase[1];
4467 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4469 #define LM_DIST_EPSILON (1.0f / 32.0f)
4470 for (y = 0;y < triangle->lmsize[1];y++)
4472 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4473 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4475 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4476 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4477 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4478 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4479 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4485 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4487 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);
4488 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);
4492 Mem_Free(lightmappixels);
4493 if (deluxemappixels)
4494 Mem_Free(deluxemappixels);
4496 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4498 surface = model->data_surfaces + surfaceindex;
4499 if (!surface->num_triangles)
4501 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4502 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4503 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4504 surface->lightmapinfo = NULL;
4507 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4508 model->brushq1.lightdata = NULL;
4509 model->brushq1.lightmapupdateflags = NULL;
4510 model->brushq1.firstrender = false;
4511 model->brushq1.num_lightstyles = 0;
4512 model->brushq1.data_lightstyleinfo = NULL;
4513 for (i = 0;i < model->brush.numsubmodels;i++)
4515 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4516 model->brush.submodels[i]->brushq1.firstrender = false;
4517 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4518 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4522 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4525 for (i = 0;i < model->surfmesh.num_vertices;i++)
4526 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4529 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4536 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4538 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4539 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4541 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4542 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4544 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4545 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4551 extern cvar_t mod_q3bsp_nolightmaps;
4552 static void Mod_GenerateLightmaps(dp_model_t *model)
4554 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4555 dp_model_t *oldloadmodel = loadmodel;
4558 Mod_GenerateLightmaps_InitSampleOffsets(model);
4559 Mod_GenerateLightmaps_DestroyLightmaps(model);
4560 Mod_GenerateLightmaps_UnweldTriangles(model);
4561 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4562 Mod_GenerateLightmaps_CreateLights(model);
4563 if(!mod_q3bsp_nolightmaps.integer)
4564 Mod_GenerateLightmaps_CreateLightmaps(model);
4565 Mod_GenerateLightmaps_UpdateVertexColors(model);
4566 Mod_GenerateLightmaps_UpdateLightGrid(model);
4567 Mod_GenerateLightmaps_DestroyLights(model);
4568 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4570 loadmodel = oldloadmodel;
4573 static void Mod_GenerateLightmaps_f(void)
4575 if (Cmd_Argc() != 1)
4577 Con_Printf("usage: mod_generatelightmaps\n");
4582 Con_Printf("no worldmodel loaded\n");
4585 Mod_GenerateLightmaps(cl.worldmodel);
4588 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4590 memset(mod, 0, sizeof(*mod));
4591 strlcpy(mod->name, name, sizeof(mod->name));
4592 mod->mempool = Mem_AllocPool(name, 0, NULL);
4593 mod->texturepool = R_AllocTexturePool();
4594 mod->Draw = R_Q1BSP_Draw;
4595 mod->DrawDepth = R_Q1BSP_DrawDepth;
4596 mod->DrawDebug = R_Q1BSP_DrawDebug;
4597 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4598 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4599 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4600 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
4601 mod->DrawLight = R_Q1BSP_DrawLight;
4604 void Mod_Mesh_Destroy(dp_model_t *mod)
4606 Mod_UnloadModel(mod);
4609 // resets the mesh model to have no geometry to render, ready for a new frame -
4610 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4611 void Mod_Mesh_Reset(dp_model_t *mod)
4613 mod->num_surfaces = 0;
4614 mod->surfmesh.num_vertices = 0;
4615 mod->surfmesh.num_triangles = 0;
4616 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4617 mod->DrawSky = NULL; // will be set if a texture needs it
4618 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4621 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4625 for (i = 0; i < mod->num_textures; i++)
4626 if (!strcmp(mod->data_textures[i].name, name))
4627 return mod->data_textures + i;
4628 if (mod->max_textures <= mod->num_textures)
4630 texture_t *oldtextures = mod->data_textures;
4631 mod->max_textures = max(mod->max_textures * 2, 1024);
4632 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4633 // update the pointers
4634 for (i = 0; i < mod->num_surfaces; i++)
4635 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4637 t = &mod->data_textures[mod->num_textures++];
4638 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
4639 switch (defaultdrawflags & DRAWFLAG_MASK)
4641 case DRAWFLAG_ADDITIVE:
4642 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4643 t->currentmaterialflags = t->basematerialflags;
4645 case DRAWFLAG_MODULATE:
4646 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4647 t->currentmaterialflags = t->basematerialflags;
4648 t->customblendfunc[0] = GL_DST_COLOR;
4649 t->customblendfunc[1] = GL_ZERO;
4651 case DRAWFLAG_2XMODULATE:
4652 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4653 t->currentmaterialflags = t->basematerialflags;
4654 t->customblendfunc[0] = GL_DST_COLOR;
4655 t->customblendfunc[1] = GL_SRC_COLOR;
4657 case DRAWFLAG_SCREEN:
4658 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4659 t->currentmaterialflags = t->basematerialflags;
4660 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4661 t->customblendfunc[1] = GL_ONE;
4669 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4672 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4673 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4674 return mod->data_surfaces + mod->num_surfaces - 1;
4675 // create new surface
4676 if (mod->max_surfaces == mod->num_surfaces)
4678 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4679 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4680 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4682 surf = mod->data_surfaces + mod->num_surfaces;
4683 mod->num_surfaces++;
4684 memset(surf, 0, sizeof(*surf));
4685 surf->texture = tex;
4686 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4687 surf->num_firstvertex = mod->surfmesh.num_vertices;
4688 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4689 mod->DrawSky = R_Q1BSP_DrawSky;
4690 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4691 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4695 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)
4697 int hashindex, h, vnum, mask;
4698 surfmesh_t *mesh = &mod->surfmesh;
4699 if (mesh->max_vertices == mesh->num_vertices)
4701 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4702 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4703 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4704 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4705 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4706 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4707 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4708 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4709 // rebuild the hash table
4710 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4711 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4712 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4713 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4714 mask = mod->surfmesh.num_vertexhashsize - 1;
4715 // no need to hash the vertices for the entire model, the latest surface will suffice.
4716 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4718 // this uses prime numbers intentionally for computing the hash
4719 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;
4720 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4721 ; // just iterate until we find the terminator
4722 mesh->data_vertexhash[h] = vnum;
4725 mask = mod->surfmesh.num_vertexhashsize - 1;
4726 // this uses prime numbers intentionally for computing the hash
4727 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4728 // when possible find an identical vertex within the same surface and return it
4729 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4731 if (vnum >= surf->num_firstvertex
4732 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4733 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4734 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4735 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4736 && 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)
4739 // add the new vertex
4740 vnum = mesh->num_vertices++;
4741 if (surf->num_vertices > 0)
4743 if (surf->mins[0] > x) surf->mins[0] = x;
4744 if (surf->mins[1] > y) surf->mins[1] = y;
4745 if (surf->mins[2] > z) surf->mins[2] = z;
4746 if (surf->maxs[0] < x) surf->maxs[0] = x;
4747 if (surf->maxs[1] < y) surf->maxs[1] = y;
4748 if (surf->maxs[2] < z) surf->maxs[2] = z;
4752 VectorSet(surf->mins, x, y, z);
4753 VectorSet(surf->maxs, x, y, z);
4755 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4756 mesh->data_vertexhash[h] = vnum;
4757 mesh->data_vertex3f[vnum * 3 + 0] = x;
4758 mesh->data_vertex3f[vnum * 3 + 1] = y;
4759 mesh->data_vertex3f[vnum * 3 + 2] = z;
4760 mesh->data_normal3f[vnum * 3 + 0] = nx;
4761 mesh->data_normal3f[vnum * 3 + 1] = ny;
4762 mesh->data_normal3f[vnum * 3 + 2] = nz;
4763 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4764 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4765 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4766 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4767 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4768 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4769 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4770 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4774 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4776 surfmesh_t *mesh = &mod->surfmesh;
4777 if (mesh->max_triangles == mesh->num_triangles)
4779 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4780 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4781 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4783 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4784 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4785 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4786 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4787 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4788 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4789 mesh->num_triangles++;
4790 surf->num_triangles++;
4793 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4797 msurface_t *surf, *surf2;
4799 // build the sorted surfaces list properly to reduce material setup
4800 // this is easy because we're just sorting on texture and don't care about the order of textures
4801 mod->nummodelsurfaces = 0;
4802 for (i = 0; i < mod->num_surfaces; i++)
4803 mod->data_surfaces[i].included = false;
4804 for (i = 0; i < mod->num_surfaces; i++)
4806 surf = mod->data_surfaces + i;
4809 tex = surf->texture;
4810 // j = i is intentional
4811 for (j = i; j < mod->num_surfaces; j++)
4813 surf2 = mod->data_surfaces + j;
4814 if (surf2->included)
4816 if (surf2->texture == tex)
4818 surf2->included = true;
4819 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4825 void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4828 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4830 if (mod->surfmesh.num_vertices > 0)
4832 // calculate normalmins/normalmaxs
4833 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4834 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4835 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4837 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4838 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4839 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4840 // expand bounds to include this vertex
4841 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4842 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4843 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4844 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4845 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4846 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4848 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4849 // (fast but less accurate than doing it per vertex)
4850 x2a = mod->normalmins[0] * mod->normalmins[0];
4851 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4852 y2a = mod->normalmins[1] * mod->normalmins[1];
4853 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4854 z2a = mod->normalmins[2] * mod->normalmins[2];
4855 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4859 yawradius = sqrt(x2 + y2);
4860 rotatedradius = sqrt(x2 + y2 + z2);
4861 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4862 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4863 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4864 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4865 mod->radius = rotatedradius;
4866 mod->radius2 = x2 + y2 + z2;
4870 VectorClear(mod->normalmins);
4871 VectorClear(mod->normalmaxs);
4872 VectorClear(mod->yawmins);
4873 VectorClear(mod->yawmaxs);
4874 VectorClear(mod->rotatedmins);
4875 VectorClear(mod->rotatedmaxs);
4881 void Mod_Mesh_Finalize(dp_model_t *mod)
4883 Mod_Mesh_ComputeBounds(mod);
4884 Mod_Mesh_MakeSortedSurfaces(mod);
4885 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);