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_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"};
31 cvar_t r_mipnormalmaps = {CVAR_SAVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CVAR_SAVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CVAR_SAVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CVAR_SAVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CVAR_SAVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CVAR_SAVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CVAR_SAVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CVAR_SAVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
42 dp_model_t *loadmodel;
44 static mempool_t *mod_mempool;
45 static memexpandablearray_t models;
47 static mempool_t* q3shaders_mem;
48 typedef struct q3shader_hash_entry_s
50 q3shaderinfo_t shader;
51 struct q3shader_hash_entry_s* chain;
52 } q3shader_hash_entry_t;
53 #define Q3SHADER_HASH_SIZE 1021
54 typedef struct q3shader_data_s
56 memexpandablearray_t hash_entries;
57 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
58 memexpandablearray_t char_ptrs;
60 static q3shader_data_t* q3shader_data;
62 static void mod_start(void)
65 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
68 SCR_PushLoadingScreen(false, "Loading models", 1.0);
70 for (i = 0;i < nummodels;i++)
71 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
74 for (i = 0;i < nummodels;i++)
75 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
78 SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
79 Mod_LoadModel(mod, true, false);
80 SCR_PopLoadingScreen(false);
82 SCR_PopLoadingScreen(false);
85 static void mod_shutdown(void)
88 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
91 for (i = 0;i < nummodels;i++)
92 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
96 Mod_Skeletal_FreeBuffers();
99 static void mod_newmap(void)
102 int i, j, k, l, surfacenum, ssize, tsize;
103 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
106 for (i = 0;i < nummodels;i++)
108 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
110 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
112 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
113 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
114 if (mod->data_textures[j].shaderpasses[l])
115 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
116 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
118 if (mod->brush.solidskyskinframe)
119 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
120 if (mod->brush.alphaskyskinframe)
121 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
125 if (!cl_stainmaps_clearonload.integer)
128 for (i = 0;i < nummodels;i++)
130 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
132 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
134 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
136 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
137 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
138 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
139 mod->brushq1.lightmapupdateflags[surfacenum] = true;
151 static void Mod_Print(void);
152 static void Mod_Precache (void);
153 static void Mod_Decompile_f(void);
154 static void Mod_GenerateLightmaps_f(void);
157 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
158 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(dp_model_t), 16);
164 Cvar_RegisterVariable(&r_mipskins);
165 Cvar_RegisterVariable(&r_mipnormalmaps);
166 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
167 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
168 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
170 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
171 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
172 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
173 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
174 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
175 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
177 Cmd_AddCommand ("modellist", Mod_Print, "prints a list of loaded models");
178 Cmd_AddCommand ("modelprecache", Mod_Precache, "load a model");
179 Cmd_AddCommand ("modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
180 Cmd_AddCommand ("mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
183 void Mod_RenderInit(void)
185 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
188 void Mod_UnloadModel (dp_model_t *mod)
190 char name[MAX_QPATH];
192 dp_model_t *parentmodel;
194 if (developer_loading.integer)
195 Con_Printf("unloading model %s\n", mod->name);
197 strlcpy(name, mod->name, sizeof(name));
198 parentmodel = mod->brush.parentmodel;
202 if (mod->surfmesh.data_element3i_indexbuffer)
203 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
204 mod->surfmesh.data_element3i_indexbuffer = NULL;
205 if (mod->surfmesh.data_element3s_indexbuffer)
206 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
207 mod->surfmesh.data_element3s_indexbuffer = NULL;
208 if (mod->surfmesh.vbo_vertexbuffer)
209 R_Mesh_DestroyMeshBuffer(mod->surfmesh.vbo_vertexbuffer);
210 mod->surfmesh.vbo_vertexbuffer = NULL;
212 // free textures/memory attached to the model
213 R_FreeTexturePool(&mod->texturepool);
214 Mem_FreePool(&mod->mempool);
215 // clear the struct to make it available
216 memset(mod, 0, sizeof(dp_model_t));
217 // restore the fields we want to preserve
218 strlcpy(mod->name, name, sizeof(mod->name));
219 mod->brush.parentmodel = parentmodel;
224 static void R_Model_Null_Draw(entity_render_t *ent)
230 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass);
232 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
247 // REQUIRED: fetch start
248 COM_ParseToken_Simple(&bufptr, true, false, true);
250 break; // end of file
251 if (!strcmp(com_token, "\n"))
252 continue; // empty line
253 start = atoi(com_token);
255 // REQUIRED: fetch length
256 COM_ParseToken_Simple(&bufptr, true, false, true);
257 if (!bufptr || !strcmp(com_token, "\n"))
259 Con_Printf("framegroups file: missing number of frames\n");
262 len = atoi(com_token);
264 // OPTIONAL args start
265 COM_ParseToken_Simple(&bufptr, true, false, true);
267 // OPTIONAL: fetch fps
269 if (bufptr && strcmp(com_token, "\n"))
271 fps = atof(com_token);
272 COM_ParseToken_Simple(&bufptr, true, false, true);
275 // OPTIONAL: fetch loopflag
277 if (bufptr && strcmp(com_token, "\n"))
279 loop = (atoi(com_token) != 0);
280 COM_ParseToken_Simple(&bufptr, true, false, true);
283 // OPTIONAL: fetch name
285 if (bufptr && strcmp(com_token, "\n"))
287 strlcpy(name, com_token, sizeof(name));
288 COM_ParseToken_Simple(&bufptr, true, false, true);
291 // OPTIONAL: remaining unsupported tokens (eat them)
292 while (bufptr && strcmp(com_token, "\n"))
293 COM_ParseToken_Simple(&bufptr, true, false, true);
295 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
298 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
305 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass)
307 dp_model_t *mod = (dp_model_t *) pass;
308 animscene_t *anim = &mod->animscenes[i];
310 strlcpy(anim->name, name, sizeof(anim[i].name));
312 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
313 anim->firstframe = bound(0, start, mod->num_poses - 1);
314 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
315 anim->framerate = max(1, fps);
317 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
320 static void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
325 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
328 Con_Printf("no scene found in framegroups file, aborting\n");
331 mod->numframes = cnt;
334 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
335 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
338 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
341 static void Mod_FindPotentialDeforms(dp_model_t *mod)
345 mod->wantnormals = false;
346 mod->wanttangents = false;
347 for (i = 0;i < mod->num_textures;i++)
349 texture = mod->data_textures + i;
350 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
351 mod->wantnormals = true;
352 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
353 mod->wantnormals = true;
354 for (j = 0;j < Q3MAXDEFORMS;j++)
356 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
358 mod->wanttangents = true;
359 mod->wantnormals = true;
362 if (texture->deforms[j].deform != Q3DEFORM_NONE)
363 mod->wantnormals = true;
375 dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
380 fs_offset_t filesize = 0;
385 if (mod->name[0] == '*') // submodel
388 if (!strcmp(mod->name, "null"))
393 if (mod->loaded || mod->mempool)
394 Mod_UnloadModel(mod);
396 if (developer_loading.integer)
397 Con_Printf("loading model %s\n", mod->name);
400 mod->crc = (unsigned int)-1;
403 VectorClear(mod->normalmins);
404 VectorClear(mod->normalmaxs);
405 VectorClear(mod->yawmins);
406 VectorClear(mod->yawmaxs);
407 VectorClear(mod->rotatedmins);
408 VectorClear(mod->rotatedmaxs);
410 mod->modeldatatypestring = "null";
411 mod->type = mod_null;
412 mod->Draw = R_Model_Null_Draw;
416 // no fatal errors occurred, so this model is ready to use.
425 // even if the model is loaded it still may need reloading...
427 // if it is not loaded or checkdisk is true we need to calculate the crc
428 if (!mod->loaded || checkdisk)
430 if (checkdisk && mod->loaded)
431 Con_DPrintf("checking model %s\n", mod->name);
432 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
435 crc = CRC_Block((unsigned char *)buf, filesize);
436 // we need to reload the model if the crc does not match
442 // if the model is already loaded and checks passed, just return
450 if (developer_loading.integer)
451 Con_Printf("loading model %s\n", mod->name);
453 SCR_PushLoadingScreen(true, mod->name, 1);
455 // LordHavoc: unload the existing model in this slot (if there is one)
456 if (mod->loaded || mod->mempool)
457 Mod_UnloadModel(mod);
462 // errors can prevent the corresponding mod->loaded = true;
465 // default lightmap scale
466 mod->lightmapscale = 1;
468 // default model radius and bounding box (mainly for missing models)
470 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
471 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
472 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
473 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
474 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
475 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
479 // load q3 shaders for the first time, or after a level change
485 char *bufend = (char *)buf + filesize;
487 // all models use memory, so allocate a memory pool
488 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
490 num = LittleLong(*((int *)buf));
491 // call the apropriate loader
493 if (!strcasecmp(FS_FileExtension(mod->name), "obj")) Mod_OBJ_Load(mod, buf, bufend);
494 else if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
495 else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend);
496 else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend);
497 else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend);
498 else if (!memcmp(buf, "IDS2", 4)) Mod_IDS2_Load(mod, buf, bufend);
499 else if (!memcmp(buf, "IBSP", 4)) Mod_IBSP_Load(mod, buf, bufend);
500 else if (!memcmp(buf, "ZYMOTICMODEL", 12)) Mod_ZYMOTICMODEL_Load(mod, buf, bufend);
501 else if (!memcmp(buf, "DARKPLACESMODEL", 16)) Mod_DARKPLACESMODEL_Load(mod, buf, bufend);
502 else if (!memcmp(buf, "ACTRHEAD", 8)) Mod_PSKMODEL_Load(mod, buf, bufend);
503 else if (!memcmp(buf, "INTERQUAKEMODEL", 16)) Mod_INTERQUAKEMODEL_Load(mod, buf, bufend);
504 else if (strlen(mod->name) >= 4 && !strcmp(mod->name + strlen(mod->name) - 4, ".map")) Mod_MAP_Load(mod, buf, bufend);
505 else if (num == BSPVERSION || num == 30 || !memcmp(buf, "BSP2", 4) || !memcmp(buf, "2PSB", 4)) Mod_Q1BSP_Load(mod, buf, bufend);
506 else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
509 Mod_FindPotentialDeforms(mod);
511 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
514 Mod_FrameGroupify(mod, (const char *)buf);
522 // LordHavoc: Sys_Error was *ANNOYING*
523 Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
526 // no fatal errors occurred, so this model is ready to use.
529 SCR_PopLoadingScreen(false);
534 void Mod_ClearUsed(void)
537 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
539 for (i = 0;i < nummodels;i++)
540 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
544 void Mod_PurgeUnused(void)
547 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
549 for (i = 0;i < nummodels;i++)
551 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
553 Mod_UnloadModel(mod);
554 Mem_ExpandableArray_FreeRecord(&models, mod);
565 dp_model_t *Mod_FindName(const char *name, const char *parentname)
574 // if we're not dedicatd, the renderer calls will crash without video
577 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
580 Host_Error ("Mod_ForName: empty name");
582 // search the currently loaded models
583 for (i = 0;i < nummodels;i++)
585 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))))
592 // no match found, create a new one
593 mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
594 strlcpy(mod->name, name, sizeof(mod->name));
596 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
598 mod->brush.parentmodel = NULL;
608 Loads in a model for the given name
611 dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
614 model = Mod_FindName(name, parentname);
615 if (!model->loaded || checkdisk)
616 Mod_LoadModel(model, crash, checkdisk);
624 Reloads all models if they have changed
627 void Mod_Reload(void)
630 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
633 SCR_PushLoadingScreen(false, "Reloading models", 1.0);
635 for (i = 0;i < nummodels;i++)
636 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
638 for (i = 0;i < nummodels;i++)
639 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
641 SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
642 Mod_LoadModel(mod, true, true);
643 SCR_PopLoadingScreen(false);
645 SCR_PopLoadingScreen(false);
648 unsigned char *mod_base;
651 //=============================================================================
658 static void Mod_Print(void)
661 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
664 Con_Print("Loaded models:\n");
665 for (i = 0;i < nummodels;i++)
667 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
669 if (mod->brush.numsubmodels)
670 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
672 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
682 static void Mod_Precache(void)
685 Mod_ForName(Cmd_Argv(1), false, true, Cmd_Argv(1)[0] == '*' ? cl.model_name[1] : NULL);
687 Con_Print("usage: modelprecache <filename>\n");
690 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
694 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
695 memset(used, 0, numvertices);
696 for (i = 0;i < numelements;i++)
697 used[elements[i]] = 1;
698 for (i = 0, count = 0;i < numvertices;i++)
699 remapvertices[i] = used[i] ? count++ : -1;
704 qboolean Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
706 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
708 int invalidintcount = 0, invalidintexample = 0;
709 int invalidshortcount = 0, invalidshortexample = 0;
710 int invalidmismatchcount = 0, invalidmismatchexample = 0;
713 for (i = 0; i < numelements; i++)
715 if (element3i[i] < first || element3i[i] > last)
718 invalidintexample = i;
724 for (i = 0; i < numelements; i++)
726 if (element3s[i] < first || element3s[i] > last)
729 invalidintexample = i;
733 if (element3i && element3s)
735 for (i = 0; i < numelements; i++)
737 if (element3s[i] != element3i[i])
739 invalidmismatchcount++;
740 invalidmismatchexample = i;
744 if (invalidintcount || invalidshortcount || invalidmismatchcount)
746 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, element3i, element3s, filename, fileline);
747 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
748 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
749 Con_Printf(", %i elements mismatch between element3i and element3s (example: element3s[%i] is %i and element3i[%i] is %i)", invalidmismatchcount, invalidmismatchexample, element3s ? element3s[invalidmismatchexample] : 0, invalidmismatchexample, element3i ? element3i[invalidmismatchexample] : 0);
750 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
752 // edit the elements to make them safer, as the driver will crash otherwise
754 for (i = 0; i < numelements; i++)
755 if (element3i[i] < first || element3i[i] > last)
756 element3i[i] = first;
758 for (i = 0; i < numelements; i++)
759 if (element3s[i] < first || element3s[i] > last)
760 element3s[i] = first;
761 if (element3i && element3s)
762 for (i = 0; i < numelements; i++)
763 if (element3s[i] != element3i[i])
764 element3s[i] = element3i[i];
771 // warning: this is an expensive function!
772 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
779 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
780 // process each vertex of each triangle and accumulate the results
781 // use area-averaging, to make triangles with a big area have a bigger
782 // weighting on the vertex normal than triangles with a small area
783 // to do so, just add the 'normals' together (the bigger the area
784 // the greater the length of the normal is
786 for (i = 0; i < numtriangles; i++, element += 3)
789 vertex3f + element[0] * 3,
790 vertex3f + element[1] * 3,
791 vertex3f + element[2] * 3,
796 VectorNormalize(areaNormal);
798 for (j = 0;j < 3;j++)
800 vectorNormal = normal3f + element[j] * 3;
801 vectorNormal[0] += areaNormal[0];
802 vectorNormal[1] += areaNormal[1];
803 vectorNormal[2] += areaNormal[2];
806 // and just normalize the accumulated vertex normal in the end
807 vectorNormal = normal3f + 3 * firstvertex;
808 for (i = 0; i < numvertices; i++, vectorNormal += 3)
809 VectorNormalize(vectorNormal);
813 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)
815 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
816 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
817 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
819 // 6 multiply, 9 subtract
820 VectorSubtract(v1, v0, v10);
821 VectorSubtract(v2, v0, v20);
822 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
823 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
824 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
825 // 12 multiply, 10 subtract
826 tc10[1] = tc1[1] - tc0[1];
827 tc20[1] = tc2[1] - tc0[1];
828 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
829 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
830 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
831 tc10[0] = tc1[0] - tc0[0];
832 tc20[0] = tc2[0] - tc0[0];
833 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
834 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
835 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
836 // 12 multiply, 4 add, 6 subtract
837 f = DotProduct(svector3f, normal3f);
838 svector3f[0] -= f * normal3f[0];
839 svector3f[1] -= f * normal3f[1];
840 svector3f[2] -= f * normal3f[2];
841 f = DotProduct(tvector3f, normal3f);
842 tvector3f[0] -= f * normal3f[0];
843 tvector3f[1] -= f * normal3f[1];
844 tvector3f[2] -= f * normal3f[2];
845 // if texture is mapped the wrong way (counterclockwise), the tangents
846 // have to be flipped, this is detected by calculating a normal from the
847 // two tangents, and seeing if it is opposite the surface normal
848 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
849 CrossProduct(tvector3f, svector3f, tangentcross);
850 if (DotProduct(tangentcross, normal3f) < 0)
852 VectorNegate(svector3f, svector3f);
853 VectorNegate(tvector3f, tvector3f);
858 // warning: this is a very expensive function!
859 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)
862 float sdir[3], tdir[3], normal[3], *svec, *tvec;
863 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
864 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
867 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
868 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
869 // process each vertex of each triangle and accumulate the results
870 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
872 v0 = vertex3f + e[0] * 3;
873 v1 = vertex3f + e[1] * 3;
874 v2 = vertex3f + e[2] * 3;
875 tc0 = texcoord2f + e[0] * 2;
876 tc1 = texcoord2f + e[1] * 2;
877 tc2 = texcoord2f + e[2] * 2;
879 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
880 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
882 // calculate the edge directions and surface normal
883 // 6 multiply, 9 subtract
884 VectorSubtract(v1, v0, v10);
885 VectorSubtract(v2, v0, v20);
886 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
887 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
888 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
890 // calculate the tangents
891 // 12 multiply, 10 subtract
892 tc10[1] = tc1[1] - tc0[1];
893 tc20[1] = tc2[1] - tc0[1];
894 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
895 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
896 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
897 tc10[0] = tc1[0] - tc0[0];
898 tc20[0] = tc2[0] - tc0[0];
899 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
900 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
901 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
903 // if texture is mapped the wrong way (counterclockwise), the tangents
904 // have to be flipped, this is detected by calculating a normal from the
905 // two tangents, and seeing if it is opposite the surface normal
906 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
907 CrossProduct(tdir, sdir, tangentcross);
908 if (DotProduct(tangentcross, normal) < 0)
910 VectorNegate(sdir, sdir);
911 VectorNegate(tdir, tdir);
916 VectorNormalize(sdir);
917 VectorNormalize(tdir);
919 for (i = 0;i < 3;i++)
921 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
922 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
925 // make the tangents completely perpendicular to the surface normal, and
926 // then normalize them
927 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
928 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
930 f = -DotProduct(svec, n);
931 VectorMA(svec, f, n, svec);
932 VectorNormalize(svec);
933 f = -DotProduct(tvec, n);
934 VectorMA(tvec, f, n, tvec);
935 VectorNormalize(tvec);
939 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors)
942 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 * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0));
943 loadmodel->surfmesh.num_vertices = numvertices;
944 loadmodel->surfmesh.num_triangles = numtriangles;
945 if (loadmodel->surfmesh.num_vertices)
947 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
948 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
949 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
950 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
951 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
952 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
954 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
956 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
958 if (loadmodel->surfmesh.num_triangles)
960 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
961 if (loadmodel->surfmesh.num_vertices <= 65536)
962 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
966 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
968 shadowmesh_t *newmesh;
969 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
970 newmesh->mempool = mempool;
971 newmesh->maxverts = maxverts;
972 newmesh->maxtriangles = maxtriangles;
973 newmesh->numverts = 0;
974 newmesh->numtriangles = 0;
975 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
976 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
978 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
979 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
980 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
981 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
985 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
988 shadowmeshvertexhash_t *hash;
989 // this uses prime numbers intentionally
990 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
991 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
993 vnum = (hash - mesh->vertexhashentries);
994 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
995 return hash - mesh->vertexhashentries;
997 vnum = mesh->numverts++;
998 hash = mesh->vertexhashentries + vnum;
999 hash->next = mesh->vertexhashtable[hashindex];
1000 mesh->vertexhashtable[hashindex] = hash;
1001 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1002 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1003 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1007 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1011 for (i = 0;i < numtris;i++)
1013 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1014 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1015 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1016 mesh->numtriangles++;
1019 // the triangle calculation can take a while, so let's do a keepalive here
1020 CL_KeepaliveMessage(false);
1023 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1025 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1026 CL_KeepaliveMessage(false);
1028 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1031 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1033 if (!mesh->numverts)
1036 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1037 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1039 // upload short indices as a buffer
1040 if (mesh->element3s && !mesh->element3s_indexbuffer)
1041 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1043 // upload int indices as a buffer
1044 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1045 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1047 // vertex buffer is several arrays and we put them in the same buffer
1049 // is this wise? the texcoordtexture2f array is used with dynamic
1050 // vertex/svector/tvector/normal when rendering animated models, on the
1051 // other hand animated models don't use a lot of vertices anyway...
1052 if (!mesh->vbo_vertexbuffer)
1054 mesh->vbooffset_vertex3f = 0;
1055 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1059 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qboolean createvbo)
1061 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1063 if (mesh->vertexhashentries)
1064 Mem_Free(mesh->vertexhashentries);
1065 mesh->vertexhashentries = NULL;
1066 if (mesh->vertexhashtable)
1067 Mem_Free(mesh->vertexhashtable);
1068 mesh->vertexhashtable = NULL;
1069 if (mesh->maxverts > mesh->numverts)
1071 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1072 mesh->maxverts = mesh->numverts;
1074 if (mesh->maxtriangles > mesh->numtriangles)
1076 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1077 mesh->maxtriangles = mesh->numtriangles;
1079 if (mesh->numverts <= 65536)
1082 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1083 for (i = 0;i < mesh->numtriangles*3;i++)
1084 mesh->element3s[i] = mesh->element3i[i];
1087 Mod_ShadowMesh_CreateVBOs(mesh);
1090 // this can take a while, so let's do a keepalive here
1091 CL_KeepaliveMessage(false);
1096 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1099 vec3_t nmins, nmaxs, ncenter, temp;
1100 float nradius2, dist2, *v;
1104 VectorCopy(mesh->vertex3f, nmins);
1105 VectorCopy(mesh->vertex3f, nmaxs);
1106 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1108 if (nmins[0] > v[0]) nmins[0] = v[0];if (nmaxs[0] < v[0]) nmaxs[0] = v[0];
1109 if (nmins[1] > v[1]) nmins[1] = v[1];if (nmaxs[1] < v[1]) nmaxs[1] = v[1];
1110 if (nmins[2] > v[2]) nmins[2] = v[2];if (nmaxs[2] < v[2]) nmaxs[2] = v[2];
1112 // calculate center and radius
1113 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1114 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1115 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1117 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1119 VectorSubtract(v, ncenter, temp);
1120 dist2 = DotProduct(temp, temp);
1121 if (nradius2 < dist2)
1126 VectorCopy(nmins, mins);
1128 VectorCopy(nmaxs, maxs);
1130 VectorCopy(ncenter, center);
1132 *radius = sqrt(nradius2);
1135 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1137 if (mesh->element3i_indexbuffer)
1138 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1139 if (mesh->element3s_indexbuffer)
1140 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1141 if (mesh->vbo_vertexbuffer)
1142 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1144 Mem_Free(mesh->vertex3f);
1145 if (mesh->element3i)
1146 Mem_Free(mesh->element3i);
1147 if (mesh->element3s)
1148 Mem_Free(mesh->element3s);
1149 if (mesh->vertexhashentries)
1150 Mem_Free(mesh->vertexhashentries);
1151 if (mesh->vertexhashtable)
1152 Mem_Free(mesh->vertexhashtable);
1156 void Mod_CreateCollisionMesh(dp_model_t *mod)
1158 int k, numcollisionmeshtriangles;
1159 qboolean usesinglecollisionmesh = false;
1160 const msurface_t *surface = NULL;
1162 mempool_t *mempool = mod->mempool;
1163 if (!mempool && mod->brush.parentmodel)
1164 mempool = mod->brush.parentmodel->mempool;
1165 // make a single combined collision mesh for physics engine use
1166 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1167 numcollisionmeshtriangles = 0;
1168 for (k = 0;k < mod->nummodelsurfaces;k++)
1170 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1171 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1173 usesinglecollisionmesh = true;
1174 numcollisionmeshtriangles = surface->num_triangles;
1177 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1179 numcollisionmeshtriangles += surface->num_triangles;
1181 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1182 if (usesinglecollisionmesh)
1183 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1186 for (k = 0;k < mod->nummodelsurfaces;k++)
1188 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1189 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1191 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1194 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1198 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)
1203 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1204 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1207 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1208 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1209 texcoord2f[0] = tc[0];
1210 texcoord2f[1] = tc[1];
1213 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)
1215 float vup[3], vdown[3], vleft[3], vright[3];
1216 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1217 float sv[3], tv[3], nl[3];
1218 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1219 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1220 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1221 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1222 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1223 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1224 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1225 VectorAdd(svector3f, sv, svector3f);
1226 VectorAdd(tvector3f, tv, tvector3f);
1227 VectorAdd(normal3f, nl, normal3f);
1228 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1229 VectorAdd(svector3f, sv, svector3f);
1230 VectorAdd(tvector3f, tv, tvector3f);
1231 VectorAdd(normal3f, nl, normal3f);
1232 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1233 VectorAdd(svector3f, sv, svector3f);
1234 VectorAdd(tvector3f, tv, tvector3f);
1235 VectorAdd(normal3f, nl, normal3f);
1238 static void Mod_ConstructTerrainPatchFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int x1, int y1, int width, int height, int *element3i, float *vertex3f, float *svector3f, float *tvector3f, float *normal3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
1240 int x, y, ix, iy, *e;
1242 for (y = 0;y < height;y++)
1244 for (x = 0;x < width;x++)
1246 e[0] = (y + 1) * (width + 1) + (x + 0);
1247 e[1] = (y + 0) * (width + 1) + (x + 0);
1248 e[2] = (y + 1) * (width + 1) + (x + 1);
1249 e[3] = (y + 0) * (width + 1) + (x + 0);
1250 e[4] = (y + 0) * (width + 1) + (x + 1);
1251 e[5] = (y + 1) * (width + 1) + (x + 1);
1255 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1256 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1257 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1262 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1266 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1267 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1268 float viewvector[3];
1269 unsigned int firstvertex;
1272 if (chunkwidth < 2 || chunkheight < 2)
1274 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]);
1275 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]);
1276 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1277 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1278 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1279 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1281 // too close for this stepsize, emit as 4 chunks instead
1283 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1284 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1285 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1286 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1289 // emit the geometry at stepsize into our vertex buffer / index buffer
1290 // we add two columns and two rows for skirt
1291 outwidth = chunkwidth+2;
1292 outheight = chunkheight+2;
1293 outwidth2 = outwidth-1;
1294 outheight2 = outheight-1;
1295 outwidth3 = outwidth+1;
1296 outheight3 = outheight+1;
1297 firstvertex = numvertices;
1298 e = model->terrain.element3i + numtriangles;
1299 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1300 v = model->terrain.vertex3f + numvertices;
1301 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1302 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1303 for (ty = 0;ty < outheight;ty++)
1305 for (tx = 0;tx < outwidth;tx++)
1307 *e++ = firstvertex + (ty )*outwidth3+(tx );
1308 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1309 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1310 *e++ = firstvertex + (ty )*outwidth3+(tx );
1311 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1312 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1315 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1316 for (ty = 0;ty <= outheight;ty++)
1318 skirtrow = ty == 0 || ty == outheight;
1319 ry = y+bound(1, ty, outheight)*stepsize;
1320 for (tx = 0;tx <= outwidth;tx++)
1322 skirt = skirtrow || tx == 0 || tx == outwidth;
1323 rx = x+bound(1, tx, outwidth)*stepsize;
1326 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1330 // TODO: emit skirt vertices
1333 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1335 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1336 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1337 Mod_Terrain_BuildChunk(model,
1341 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1344 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1346 offset = bound(0, s[4] - '0', 9);
1347 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1352 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1353 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1354 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1355 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1356 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1357 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1358 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1359 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1360 return offset | Q3WAVEFUNC_NONE;
1363 void Mod_FreeQ3Shaders(void)
1365 Mem_FreePool(&q3shaders_mem);
1368 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
1370 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1371 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1372 q3shader_hash_entry_t* lastEntry = NULL;
1375 if (strcasecmp (entry->shader.name, shader->name) == 0)
1378 if(shader->dpshaderkill)
1380 // killed shader is a redeclarion? we can safely ignore it
1383 else if(entry->shader.dpshaderkill)
1385 // replace the old shader!
1386 // this will skip the entry allocating part
1387 // below and just replace the shader
1392 unsigned char *start, *end, *start2;
1393 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1394 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1395 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1396 if(memcmp(start, start2, end - start))
1397 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1399 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1404 entry = entry->chain;
1406 while (entry != NULL);
1409 if (lastEntry->shader.name[0] != 0)
1412 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1413 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1415 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1416 lastEntry->chain = newEntry;
1417 newEntry->chain = NULL;
1418 lastEntry = newEntry;
1420 /* else: head of chain, in hash entry array */
1423 memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
1426 extern cvar_t mod_noshader_default_offsetmapping;
1427 extern cvar_t mod_q3shader_default_offsetmapping;
1428 extern cvar_t mod_q3shader_default_offsetmapping_scale;
1429 extern cvar_t mod_q3shader_default_offsetmapping_bias;
1430 extern cvar_t mod_q3shader_default_polygonoffset;
1431 extern cvar_t mod_q3shader_default_polygonfactor;
1432 extern cvar_t mod_q3shader_force_addalpha;
1433 extern cvar_t mod_q3shader_force_terrain_alphaflag;
1434 void Mod_LoadQ3Shaders(void)
1441 q3shaderinfo_t shader;
1442 q3shaderinfo_layer_t *layer;
1444 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1445 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1446 unsigned long custsurfaceflags[256];
1447 int numcustsurfaceflags;
1448 qboolean dpshaderkill;
1450 Mod_FreeQ3Shaders();
1452 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1453 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1454 sizeof (q3shader_data_t));
1455 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1456 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1457 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1458 q3shaders_mem, sizeof (char**), 256);
1460 // parse custinfoparms.txt
1461 numcustsurfaceflags = 0;
1462 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1464 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1465 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1468 while (COM_ParseToken_QuakeC(&text, false))
1469 if (!strcasecmp(com_token, "}"))
1471 // custom surfaceflags section
1472 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1473 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1476 while(COM_ParseToken_QuakeC(&text, false))
1478 if (!strcasecmp(com_token, "}"))
1480 // register surfaceflag
1481 if (numcustsurfaceflags >= 256)
1483 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1487 j = (int)strlen(com_token)+1;
1488 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1489 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1491 if (COM_ParseToken_QuakeC(&text, false))
1492 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1494 custsurfaceflags[numcustsurfaceflags] = 0;
1495 numcustsurfaceflags++;
1503 search = FS_Search("scripts/*.shader", true, false);
1506 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1508 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1511 while (COM_ParseToken_QuakeC(&text, false))
1513 memset (&shader, 0, sizeof(shader));
1515 shader.surfaceparms = 0;
1516 shader.surfaceflags = 0;
1517 shader.textureflags = 0;
1518 shader.numlayers = 0;
1519 shader.lighting = false;
1520 shader.vertexalpha = false;
1521 shader.textureblendalpha = false;
1522 shader.skyboxname[0] = 0;
1523 shader.deforms[0].deform = Q3DEFORM_NONE;
1524 shader.dpnortlight = false;
1525 shader.dpshadow = false;
1526 shader.dpnoshadow = false;
1527 shader.dpmeshcollisions = false;
1528 shader.dpshaderkill = false;
1529 shader.dpreflectcube[0] = 0;
1530 shader.reflectmin = 0;
1531 shader.reflectmax = 1;
1532 shader.refractfactor = 1;
1533 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1534 shader.reflectfactor = 1;
1535 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1536 shader.r_water_wateralpha = 1;
1537 shader.r_water_waterscroll[0] = 0;
1538 shader.r_water_waterscroll[1] = 0;
1539 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1540 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1541 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1542 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1543 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1544 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1545 shader.specularscalemod = 1;
1546 shader.specularpowermod = 1;
1547 shader.rtlightambient = 0;
1548 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1549 // JUST GREP FOR "specularscalemod = 1".
1551 strlcpy(shader.name, com_token, sizeof(shader.name));
1552 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1554 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1557 while (COM_ParseToken_QuakeC(&text, false))
1559 if (!strcasecmp(com_token, "}"))
1561 if (!strcasecmp(com_token, "{"))
1563 static q3shaderinfo_layer_t dummy;
1564 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1566 layer = shader.layers + shader.numlayers++;
1570 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1571 memset(&dummy, 0, sizeof(dummy));
1574 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1575 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1576 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1577 layer->blendfunc[0] = GL_ONE;
1578 layer->blendfunc[1] = GL_ZERO;
1579 while (COM_ParseToken_QuakeC(&text, false))
1581 if (!strcasecmp(com_token, "}"))
1583 if (!strcasecmp(com_token, "\n"))
1586 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1588 if (j < TEXTURE_MAXFRAMES + 4)
1590 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1591 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1592 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1594 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1595 numparameters = j + 1;
1597 if (!COM_ParseToken_QuakeC(&text, true))
1600 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1601 // parameter[j][0] = 0;
1602 if (developer_insane.integer)
1604 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1605 for (j = 0;j < numparameters;j++)
1606 Con_DPrintf(" %s", parameter[j]);
1609 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1611 if (numparameters == 2)
1613 if (!strcasecmp(parameter[1], "add"))
1615 layer->blendfunc[0] = GL_ONE;
1616 layer->blendfunc[1] = GL_ONE;
1618 else if (!strcasecmp(parameter[1], "addalpha"))
1620 layer->blendfunc[0] = GL_SRC_ALPHA;
1621 layer->blendfunc[1] = GL_ONE;
1623 else if (!strcasecmp(parameter[1], "filter"))
1625 layer->blendfunc[0] = GL_DST_COLOR;
1626 layer->blendfunc[1] = GL_ZERO;
1628 else if (!strcasecmp(parameter[1], "blend"))
1630 layer->blendfunc[0] = GL_SRC_ALPHA;
1631 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1634 else if (numparameters == 3)
1637 for (k = 0;k < 2;k++)
1639 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1640 layer->blendfunc[k] = GL_ONE;
1641 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1642 layer->blendfunc[k] = GL_ZERO;
1643 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1644 layer->blendfunc[k] = GL_SRC_COLOR;
1645 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1646 layer->blendfunc[k] = GL_SRC_ALPHA;
1647 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1648 layer->blendfunc[k] = GL_DST_COLOR;
1649 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1650 layer->blendfunc[k] = GL_DST_ALPHA;
1651 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1652 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1653 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1654 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1655 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1656 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1657 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1658 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1660 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1664 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1665 layer->alphatest = true;
1666 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1668 if (!strcasecmp(parameter[0], "clampmap"))
1669 layer->clampmap = true;
1670 layer->numframes = 1;
1671 layer->framerate = 1;
1672 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1673 &q3shader_data->char_ptrs);
1674 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1675 if (!strcasecmp(parameter[1], "$lightmap"))
1676 shader.lighting = true;
1678 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1681 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1682 layer->framerate = atof(parameter[1]);
1683 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1684 for (i = 0;i < layer->numframes;i++)
1685 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1687 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1690 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1691 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1692 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1693 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1694 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1695 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1696 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1697 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1698 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1699 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1700 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1701 else if (!strcasecmp(parameter[1], "wave"))
1703 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1704 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1705 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1706 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1708 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1710 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1713 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1714 layer->alphagen.parms[i] = atof(parameter[i+2]);
1715 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1716 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1717 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1718 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1719 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1720 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1721 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1722 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1723 else if (!strcasecmp(parameter[1], "wave"))
1725 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1726 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1727 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1728 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1730 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1732 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1735 // observed values: tcgen environment
1736 // no other values have been observed in real shaders
1737 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1738 layer->tcgen.parms[i] = atof(parameter[i+2]);
1739 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1740 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1741 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1742 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1743 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1744 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1746 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1753 // tcmod stretch sin # # # #
1754 // tcmod stretch triangle # # # #
1755 // tcmod transform # # # # # #
1756 // tcmod turb # # # #
1757 // tcmod turb sin # # # # (this is bogus)
1758 // no other values have been observed in real shaders
1759 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1760 if (!layer->tcmods[tcmodindex].tcmod)
1762 if (tcmodindex < Q3MAXTCMODS)
1764 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1765 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1766 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1767 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1768 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1769 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1770 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1771 else if (!strcasecmp(parameter[1], "stretch"))
1773 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1774 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1775 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1776 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1778 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1779 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1780 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1783 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1785 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1786 if (!strcasecmp(com_token, "}"))
1789 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1790 shader.lighting = true;
1791 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1793 if (layer == shader.layers + 0)
1795 // vertex controlled transparency
1796 shader.vertexalpha = true;
1800 // multilayer terrain shader or similar
1801 shader.textureblendalpha = true;
1802 if (mod_q3shader_force_terrain_alphaflag.integer)
1803 shader.layers[0].dptexflags |= TEXF_ALPHA;
1807 if(mod_q3shader_force_addalpha.integer)
1809 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1810 // this cvar brings back this behaviour
1811 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1812 layer->blendfunc[0] = GL_SRC_ALPHA;
1815 layer->dptexflags = 0;
1816 if (layer->alphatest)
1817 layer->dptexflags |= TEXF_ALPHA;
1818 switch(layer->blendfunc[0])
1821 case GL_ONE_MINUS_SRC_ALPHA:
1822 layer->dptexflags |= TEXF_ALPHA;
1825 switch(layer->blendfunc[1])
1828 case GL_ONE_MINUS_SRC_ALPHA:
1829 layer->dptexflags |= TEXF_ALPHA;
1832 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1833 layer->dptexflags |= TEXF_MIPMAP;
1834 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1835 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1836 if (layer->clampmap)
1837 layer->dptexflags |= TEXF_CLAMP;
1841 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1843 if (j < TEXTURE_MAXFRAMES + 4)
1845 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1846 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1847 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1849 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1850 numparameters = j + 1;
1852 if (!COM_ParseToken_QuakeC(&text, true))
1855 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1856 // parameter[j][0] = 0;
1857 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1859 if (developer_insane.integer)
1861 Con_DPrintf("%s: ", shader.name);
1862 for (j = 0;j < numparameters;j++)
1863 Con_DPrintf(" %s", parameter[j]);
1866 if (numparameters < 1)
1868 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1870 if (!strcasecmp(parameter[1], "alphashadow"))
1871 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1872 else if (!strcasecmp(parameter[1], "areaportal"))
1873 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1874 else if (!strcasecmp(parameter[1], "botclip"))
1875 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1876 else if (!strcasecmp(parameter[1], "clusterportal"))
1877 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1878 else if (!strcasecmp(parameter[1], "detail"))
1879 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1880 else if (!strcasecmp(parameter[1], "donotenter"))
1881 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1882 else if (!strcasecmp(parameter[1], "dust"))
1883 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1884 else if (!strcasecmp(parameter[1], "hint"))
1885 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1886 else if (!strcasecmp(parameter[1], "fog"))
1887 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1888 else if (!strcasecmp(parameter[1], "lava"))
1889 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1890 else if (!strcasecmp(parameter[1], "lightfilter"))
1891 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1892 else if (!strcasecmp(parameter[1], "lightgrid"))
1893 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1894 else if (!strcasecmp(parameter[1], "metalsteps"))
1895 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1896 else if (!strcasecmp(parameter[1], "nodamage"))
1897 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1898 else if (!strcasecmp(parameter[1], "nodlight"))
1899 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1900 else if (!strcasecmp(parameter[1], "nodraw"))
1901 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1902 else if (!strcasecmp(parameter[1], "nodrop"))
1903 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1904 else if (!strcasecmp(parameter[1], "noimpact"))
1905 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1906 else if (!strcasecmp(parameter[1], "nolightmap"))
1907 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1908 else if (!strcasecmp(parameter[1], "nomarks"))
1909 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1910 else if (!strcasecmp(parameter[1], "nomipmaps"))
1911 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1912 else if (!strcasecmp(parameter[1], "nonsolid"))
1913 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1914 else if (!strcasecmp(parameter[1], "origin"))
1915 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1916 else if (!strcasecmp(parameter[1], "playerclip"))
1917 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1918 else if (!strcasecmp(parameter[1], "sky"))
1919 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1920 else if (!strcasecmp(parameter[1], "slick"))
1921 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1922 else if (!strcasecmp(parameter[1], "slime"))
1923 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1924 else if (!strcasecmp(parameter[1], "structural"))
1925 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1926 else if (!strcasecmp(parameter[1], "trans"))
1927 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1928 else if (!strcasecmp(parameter[1], "water"))
1929 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1930 else if (!strcasecmp(parameter[1], "pointlight"))
1931 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1932 else if (!strcasecmp(parameter[1], "antiportal"))
1933 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1934 else if (!strcasecmp(parameter[1], "skip"))
1935 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1938 // try custom surfaceparms
1939 for (j = 0; j < numcustsurfaceflags; j++)
1941 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1943 shader.surfaceflags |= custsurfaceflags[j];
1948 if (j == numcustsurfaceflags)
1949 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1952 else if (!strcasecmp(parameter[0], "dpshadow"))
1953 shader.dpshadow = true;
1954 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1955 shader.dpnoshadow = true;
1956 else if (!strcasecmp(parameter[0], "dpnortlight"))
1957 shader.dpnortlight = true;
1958 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1959 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1960 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1961 shader.dpmeshcollisions = true;
1962 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1963 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1965 if (Cvar_VariableValue(parameter[1]) == 0.0f)
1966 shader.dpshaderkill = dpshaderkill;
1968 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1969 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1971 const char *op = NULL;
1972 if (numparameters >= 3)
1976 if (Cvar_VariableValue(parameter[1]) != 0.0f)
1977 shader.dpshaderkill = dpshaderkill;
1979 else if (numparameters >= 4 && !strcmp(op, "=="))
1981 if (Cvar_VariableValue(parameter[1]) == atof(parameter[3]))
1982 shader.dpshaderkill = dpshaderkill;
1984 else if (numparameters >= 4 && !strcmp(op, "!="))
1986 if (Cvar_VariableValue(parameter[1]) != atof(parameter[3]))
1987 shader.dpshaderkill = dpshaderkill;
1989 else if (numparameters >= 4 && !strcmp(op, ">"))
1991 if (Cvar_VariableValue(parameter[1]) > atof(parameter[3]))
1992 shader.dpshaderkill = dpshaderkill;
1994 else if (numparameters >= 4 && !strcmp(op, "<"))
1996 if (Cvar_VariableValue(parameter[1]) < atof(parameter[3]))
1997 shader.dpshaderkill = dpshaderkill;
1999 else if (numparameters >= 4 && !strcmp(op, ">="))
2001 if (Cvar_VariableValue(parameter[1]) >= atof(parameter[3]))
2002 shader.dpshaderkill = dpshaderkill;
2004 else if (numparameters >= 4 && !strcmp(op, "<="))
2006 if (Cvar_VariableValue(parameter[1]) <= atof(parameter[3]))
2007 shader.dpshaderkill = dpshaderkill;
2011 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2014 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2016 // some q3 skies don't have the sky parm set
2017 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2018 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2020 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2022 // some q3 skies don't have the sky parm set
2023 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2024 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2025 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2027 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2029 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2030 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2032 else if (!strcasecmp(parameter[0], "nomipmaps"))
2033 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2034 else if (!strcasecmp(parameter[0], "nopicmip"))
2035 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2036 else if (!strcasecmp(parameter[0], "polygonoffset"))
2037 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2038 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2040 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2041 if(numparameters >= 2)
2043 shader.biaspolygonfactor = atof(parameter[1]);
2044 if(numparameters >= 3)
2045 shader.biaspolygonoffset = atof(parameter[2]);
2047 shader.biaspolygonoffset = 0;
2050 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2052 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2053 if (!strcasecmp(parameter[1], "sky"))
2054 shader.transparentsort = TRANSPARENTSORT_SKY;
2055 else if (!strcasecmp(parameter[1], "distance"))
2056 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2057 else if (!strcasecmp(parameter[1], "hud"))
2058 shader.transparentsort = TRANSPARENTSORT_HUD;
2060 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2062 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2064 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2065 shader.refractfactor = atof(parameter[1]);
2066 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2068 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2070 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2071 shader.reflectfactor = atof(parameter[1]);
2072 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2074 else if (!strcasecmp(parameter[0], "dpcamera"))
2076 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2078 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2080 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2081 shader.reflectmin = atof(parameter[1]);
2082 shader.reflectmax = atof(parameter[2]);
2083 shader.refractfactor = atof(parameter[3]);
2084 shader.reflectfactor = atof(parameter[4]);
2085 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2086 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2087 shader.r_water_wateralpha = atof(parameter[11]);
2089 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2091 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2092 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2094 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2096 shader.specularscalemod = atof(parameter[1]);
2098 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2100 shader.specularpowermod = atof(parameter[1]);
2102 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2104 shader.rtlightambient = atof(parameter[1]);
2106 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2108 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2109 shader.offsetmapping = OFFSETMAPPING_OFF;
2110 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2111 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2112 else if (!strcasecmp(parameter[1], "linear"))
2113 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2114 else if (!strcasecmp(parameter[1], "relief"))
2115 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2116 if (numparameters >= 3)
2117 shader.offsetscale = atof(parameter[2]);
2118 if (numparameters >= 5)
2120 if(!strcasecmp(parameter[3], "bias"))
2121 shader.offsetbias = atof(parameter[4]);
2122 else if(!strcasecmp(parameter[3], "match"))
2123 shader.offsetbias = 1.0f - atof(parameter[4]);
2124 else if(!strcasecmp(parameter[3], "match8"))
2125 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2126 else if(!strcasecmp(parameter[3], "match16"))
2127 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2130 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2133 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2134 if (!shader.deforms[deformindex].deform)
2136 if (deformindex < Q3MAXDEFORMS)
2138 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2139 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2140 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2141 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2142 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2143 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2144 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2145 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2146 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2147 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2148 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2149 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2150 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2151 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2152 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2153 else if (!strcasecmp(parameter[1], "wave" ))
2155 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2156 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2157 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2158 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2160 else if (!strcasecmp(parameter[1], "move" ))
2162 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2163 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2164 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2165 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2170 // hide this shader if a cvar said it should be killed
2171 if (shader.dpshaderkill)
2172 shader.numlayers = 0;
2173 // fix up multiple reflection types
2174 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2175 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2177 Q3Shader_AddToHash (&shader);
2181 FS_FreeSearch(search);
2182 // free custinfoparm values
2183 for (j = 0; j < numcustsurfaceflags; j++)
2184 Mem_Free(custsurfaceparmnames[j]);
2187 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
2189 unsigned short hash;
2190 q3shader_hash_entry_t* entry;
2192 Mod_LoadQ3Shaders();
2193 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2194 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2195 while (entry != NULL)
2197 if (strcasecmp (entry->shader.name, name) == 0)
2198 return &entry->shader;
2199 entry = entry->chain;
2204 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2206 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2207 shaderpass->framerate = 0.0f;
2208 shaderpass->numframes = 1;
2209 shaderpass->blendfunc[0] = GL_ONE;
2210 shaderpass->blendfunc[1] = GL_ZERO;
2211 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2212 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2213 shaderpass->alphatest = false;
2214 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2215 shaderpass->skinframes[0] = skinframe;
2219 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2222 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2223 shaderpass->alphatest = layer->alphatest != 0;
2224 shaderpass->framerate = layer->framerate;
2225 shaderpass->numframes = layer->numframes;
2226 shaderpass->blendfunc[0] = layer->blendfunc[0];
2227 shaderpass->blendfunc[1] = layer->blendfunc[1];
2228 shaderpass->rgbgen = layer->rgbgen;
2229 shaderpass->alphagen = layer->alphagen;
2230 shaderpass->tcgen = layer->tcgen;
2231 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2232 shaderpass->tcmods[j] = layer->tcmods[j];
2233 for (j = 0; j < layer->numframes; j++)
2234 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2238 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2240 int texflagsmask, texflagsor;
2241 qboolean success = true;
2242 q3shaderinfo_t *shader;
2245 strlcpy(texture->name, name, sizeof(texture->name));
2246 texture->basealpha = 1.0f;
2247 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2249 // allow disabling of picmip or compression by defaulttexflags
2251 if(!(defaulttexflags & TEXF_PICMIP))
2252 texflagsmask &= ~TEXF_PICMIP;
2253 if(!(defaulttexflags & TEXF_COMPRESS))
2254 texflagsmask &= ~TEXF_COMPRESS;
2256 if(defaulttexflags & TEXF_ISWORLD)
2257 texflagsor |= TEXF_ISWORLD;
2258 if(defaulttexflags & TEXF_ISSPRITE)
2259 texflagsor |= TEXF_ISSPRITE;
2260 // unless later loaded from the shader
2261 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2262 texture->offsetscale = 1;
2263 texture->offsetbias = 0;
2264 texture->specularscalemod = 1;
2265 texture->specularpowermod = 1;
2266 texture->rtlightambient = 0;
2267 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2268 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2269 // JUST GREP FOR "specularscalemod = 1".
2273 if (developer_loading.integer)
2274 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2276 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2278 texture->basematerialflags = MATERIALFLAG_SKY;
2279 if (shader->skyboxname[0] && loadmodel)
2281 // quake3 seems to append a _ to the skybox name, so this must do so as well
2282 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2285 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2286 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2288 texture->basematerialflags = MATERIALFLAG_WALL;
2290 if (shader->layers[0].alphatest)
2291 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2292 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2293 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2294 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2296 texture->biaspolygonoffset += shader->biaspolygonoffset;
2297 texture->biaspolygonfactor += shader->biaspolygonfactor;
2299 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2300 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2301 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2302 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2303 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2304 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2305 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2306 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2307 texture->customblendfunc[0] = GL_ONE;
2308 texture->customblendfunc[1] = GL_ZERO;
2309 texture->transparentsort = shader->transparentsort;
2310 if (shader->numlayers > 0)
2312 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2313 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2315 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2316 * additive GL_ONE GL_ONE
2317 additive weird GL_ONE GL_SRC_ALPHA
2318 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2319 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2320 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2321 brighten GL_DST_COLOR GL_ONE
2322 brighten GL_ONE GL_SRC_COLOR
2323 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2324 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2325 * modulate GL_DST_COLOR GL_ZERO
2326 * modulate GL_ZERO GL_SRC_COLOR
2327 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2328 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2329 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2330 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2331 * no blend GL_ONE GL_ZERO
2332 nothing GL_ZERO GL_ONE
2334 // if not opaque, figure out what blendfunc to use
2335 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2337 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2338 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2339 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2340 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2341 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2342 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2344 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2347 if (!shader->lighting)
2348 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2350 // here be dragons: convert quake3 shaders to material
2351 if (shader->numlayers > 0)
2354 int terrainbackgroundlayer = -1;
2355 int lightmaplayer = -1;
2356 int alphagenspecularlayer = -1;
2357 int rgbgenvertexlayer = -1;
2358 int rgbgendiffuselayer = -1;
2359 int materiallayer = -1;
2360 int endofprelayers = 0;
2361 int firstpostlayer = 0;
2362 int shaderpassindex = 0;
2363 for (i = 0; i < shader->numlayers; i++)
2365 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2367 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2368 rgbgenvertexlayer = i;
2369 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2370 rgbgendiffuselayer = i;
2371 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2372 alphagenspecularlayer = i;
2374 if (shader->numlayers >= 2
2375 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2376 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2377 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2378 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2380 // terrain blend or certain other effects involving alphatest over a regular layer
2381 terrainbackgroundlayer = 0;
2383 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2384 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2386 else if (lightmaplayer == 0)
2388 // ordinary texture but with $lightmap before diffuse
2390 firstpostlayer = lightmaplayer + 2;
2392 else if (lightmaplayer >= 1)
2394 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2395 endofprelayers = lightmaplayer - 1;
2396 materiallayer = lightmaplayer - 1;
2397 firstpostlayer = lightmaplayer + 1;
2399 else if (rgbgenvertexlayer >= 0)
2401 // map models with baked lighting
2402 materiallayer = rgbgenvertexlayer;
2403 endofprelayers = rgbgenvertexlayer;
2404 firstpostlayer = rgbgenvertexlayer + 1;
2405 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2406 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2407 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR;
2409 else if (rgbgendiffuselayer >= 0)
2411 // entity models with dynamic lighting
2412 materiallayer = rgbgendiffuselayer;
2413 endofprelayers = rgbgendiffuselayer;
2414 firstpostlayer = rgbgendiffuselayer + 1;
2415 // 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)...
2416 if (alphagenspecularlayer >= 0)
2417 firstpostlayer = alphagenspecularlayer + 1;
2421 // special effects shaders - treat first as primary layer and do everything else as post
2426 // convert the main material layer
2427 // 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
2428 if (materiallayer >= 0)
2429 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2430 // convert the terrain background blend layer (if any)
2431 if (terrainbackgroundlayer >= 0)
2432 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2433 // convert the prepass layers (if any)
2434 texture->startpreshaderpass = shaderpassindex;
2435 for (i = 0; i < endofprelayers; i++)
2436 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2437 texture->endpreshaderpass = shaderpassindex;
2438 texture->startpostshaderpass = shaderpassindex;
2439 // convert the postpass layers (if any)
2440 for (i = firstpostlayer; i < shader->numlayers; i++)
2441 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2442 texture->startpostshaderpass = shaderpassindex;
2445 if (shader->dpshadow)
2446 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2447 if (shader->dpnoshadow)
2448 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2449 if (shader->dpnortlight)
2450 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2451 if (shader->vertexalpha)
2452 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2453 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2454 texture->reflectmin = shader->reflectmin;
2455 texture->reflectmax = shader->reflectmax;
2456 texture->refractfactor = shader->refractfactor;
2457 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2458 texture->reflectfactor = shader->reflectfactor;
2459 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2460 texture->r_water_wateralpha = shader->r_water_wateralpha;
2461 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2462 texture->offsetmapping = shader->offsetmapping;
2463 texture->offsetscale = shader->offsetscale;
2464 texture->offsetbias = shader->offsetbias;
2465 texture->specularscalemod = shader->specularscalemod;
2466 texture->specularpowermod = shader->specularpowermod;
2467 texture->rtlightambient = shader->rtlightambient;
2468 if (shader->dpreflectcube[0])
2469 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2471 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2472 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2473 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2474 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2475 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2476 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2477 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2478 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2479 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2481 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2482 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2483 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2484 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2485 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2486 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2487 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2488 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2489 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2490 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2491 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2492 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2493 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2494 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2495 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2496 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2497 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2498 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2499 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2500 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2501 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2502 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2503 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2504 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2505 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2506 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2507 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2508 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2509 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2510 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2511 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2512 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2514 texture->surfaceflags = shader->surfaceflags;
2515 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2516 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2520 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2522 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2523 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2524 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2525 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2526 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2527 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2528 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2529 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2530 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2531 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2532 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2533 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2534 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2535 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2536 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2537 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2539 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2540 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2541 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2542 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2543 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2544 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2545 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2546 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2548 if (shader->dpmeshcollisions)
2549 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2550 if (shader->dpshaderkill && developer_extra.integer)
2551 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2553 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2555 if (developer_extra.integer)
2556 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2557 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2559 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2561 if (developer_extra.integer)
2562 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2563 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2564 texture->supercontents = SUPERCONTENTS_SOLID;
2568 if (developer_extra.integer)
2569 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2570 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2572 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2573 texture->supercontents = SUPERCONTENTS_SOLID;
2575 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2577 texture->basematerialflags = MATERIALFLAG_SKY;
2578 texture->supercontents = SUPERCONTENTS_SKY;
2582 texture->basematerialflags = defaultmaterialflags;
2583 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2585 if(cls.state == ca_dedicated)
2587 texture->materialshaderpass = NULL;
2592 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2595 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2596 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2597 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2598 if (texture->q2contents)
2599 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2603 if (!success && warnmissing)
2604 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2607 // init the animation variables
2608 texture->currentframe = texture;
2609 texture->currentmaterialflags = texture->basematerialflags;
2610 if (!texture->materialshaderpass)
2611 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2612 if (!texture->materialshaderpass->skinframes[0])
2613 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2614 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2615 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2619 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2621 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2622 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2624 strlcpy(texture->name, name, sizeof(texture->name));
2625 texture->basealpha = 1.0f;
2626 texture->basematerialflags = materialflags;
2627 texture->supercontents = supercontents;
2629 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2630 texture->offsetscale = 1;
2631 texture->offsetbias = 0;
2632 texture->specularscalemod = 1;
2633 texture->specularpowermod = 1;
2634 texture->rtlightambient = 0;
2635 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2636 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2637 // JUST GREP FOR "specularscalemod = 1".
2639 if (developer_extra.integer)
2640 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2642 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2644 // init the animation variables
2645 texture->currentmaterialflags = texture->basematerialflags;
2646 texture->currentframe = texture;
2647 texture->currentskinframe = skinframe;
2648 texture->backgroundcurrentskinframe = NULL;
2651 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2654 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2656 if (texture->shaderpasses[i])
2659 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2660 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2661 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2662 Mem_Free(texture->shaderpasses[i]);
2663 texture->shaderpasses[i] = NULL;
2666 texture->materialshaderpass = NULL;
2667 texture->currentskinframe = NULL;
2668 texture->backgroundcurrentskinframe = NULL;
2671 skinfile_t *Mod_LoadSkinFiles(void)
2673 int i, words, line, wordsoverflow;
2676 skinfile_t *skinfile = NULL, *first = NULL;
2677 skinfileitem_t *skinfileitem;
2678 char word[10][MAX_QPATH];
2683 U_bodyBox,models/players/Legoman/BikerA2.tga
2684 U_RArm,models/players/Legoman/BikerA1.tga
2685 U_LArm,models/players/Legoman/BikerA1.tga
2686 U_armor,common/nodraw
2687 U_sword,common/nodraw
2688 U_shield,common/nodraw
2689 U_homb,common/nodraw
2690 U_backpack,common/nodraw
2691 U_colcha,common/nodraw
2696 memset(word, 0, sizeof(word));
2697 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2699 // If it's the first file we parse
2700 if (skinfile == NULL)
2702 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2707 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2708 skinfile = skinfile->next;
2710 skinfile->next = NULL;
2712 for(line = 0;;line++)
2715 if (!COM_ParseToken_QuakeC(&data, true))
2717 if (!strcmp(com_token, "\n"))
2720 wordsoverflow = false;
2724 strlcpy(word[words++], com_token, sizeof (word[0]));
2726 wordsoverflow = true;
2728 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2731 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);
2734 // words is always >= 1
2735 if (!strcmp(word[0], "replace"))
2739 if (developer_loading.integer)
2740 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2741 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2742 skinfileitem->next = skinfile->items;
2743 skinfile->items = skinfileitem;
2744 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2745 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2748 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]);
2750 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2752 // tag name, like "tag_weapon,"
2753 // not used for anything (not even in Quake3)
2755 else if (words >= 2 && !strcmp(word[1], ","))
2757 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2758 if (developer_loading.integer)
2759 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2760 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2761 skinfileitem->next = skinfile->items;
2762 skinfile->items = skinfileitem;
2763 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2764 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2767 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);
2772 loadmodel->numskins = i;
2776 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2779 skinfileitem_t *skinfileitem, *nextitem;
2780 for (;skinfile;skinfile = next)
2782 next = skinfile->next;
2783 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2785 nextitem = skinfileitem->next;
2786 Mem_Free(skinfileitem);
2792 int Mod_CountSkinFiles(skinfile_t *skinfile)
2795 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2799 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2802 double isnap = 1.0 / snap;
2803 for (i = 0;i < numvertices*numcomponents;i++)
2804 vertices[i] = floor(vertices[i]*isnap)*snap;
2807 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2809 int i, outtriangles;
2810 float edgedir1[3], edgedir2[3], temp[3];
2811 // a degenerate triangle is one with no width (thickness, surface area)
2812 // these are characterized by having all 3 points colinear (along a line)
2813 // or having two points identical
2814 // the simplest check is to calculate the triangle's area
2815 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2817 // calculate first edge
2818 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2819 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2820 CrossProduct(edgedir1, edgedir2, temp);
2821 if (VectorLength2(temp) < 0.001f)
2822 continue; // degenerate triangle (no area)
2823 // valid triangle (has area)
2824 VectorCopy(inelement3i, outelement3i);
2828 return outtriangles;
2831 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2834 int firstvertex, lastvertex;
2835 if (numelements > 0 && elements)
2837 firstvertex = lastvertex = elements[0];
2838 for (i = 1;i < numelements;i++)
2841 firstvertex = min(firstvertex, e);
2842 lastvertex = max(lastvertex, e);
2846 firstvertex = lastvertex = 0;
2847 if (firstvertexpointer)
2848 *firstvertexpointer = firstvertex;
2849 if (lastvertexpointer)
2850 *lastvertexpointer = lastvertex;
2853 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2855 // make an optimal set of texture-sorted batches to draw...
2857 int *firstsurfacefortexture;
2858 int *numsurfacesfortexture;
2859 if (!mod->sortedmodelsurfaces)
2860 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2861 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2862 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2863 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2864 for (j = 0;j < mod->nummodelsurfaces;j++)
2866 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2867 t = (int)(surface->texture - mod->data_textures);
2868 numsurfacesfortexture[t]++;
2871 for (t = 0;t < mod->num_textures;t++)
2873 firstsurfacefortexture[t] = j;
2874 j += numsurfacesfortexture[t];
2876 for (j = 0;j < mod->nummodelsurfaces;j++)
2878 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2879 t = (int)(surface->texture - mod->data_textures);
2880 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2882 Mem_Free(firstsurfacefortexture);
2883 Mem_Free(numsurfacesfortexture);
2886 void Mod_BuildVBOs(void)
2888 if (!loadmodel->surfmesh.num_vertices)
2891 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2894 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2896 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2898 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2899 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2904 // upload short indices as a buffer
2905 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2906 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);
2908 // upload int indices as a buffer
2909 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2910 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);
2912 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2913 // vertex buffer is several arrays and we put them in the same buffer
2915 // is this wise? the texcoordtexture2f array is used with dynamic
2916 // vertex/svector/tvector/normal when rendering animated models, on the
2917 // other hand animated models don't use a lot of vertices anyway...
2918 if (!loadmodel->surfmesh.vbo_vertexbuffer)
2923 loadmodel->surfmesh.vbooffset_vertex3f = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2924 loadmodel->surfmesh.vbooffset_svector3f = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2925 loadmodel->surfmesh.vbooffset_tvector3f = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2926 loadmodel->surfmesh.vbooffset_normal3f = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2927 loadmodel->surfmesh.vbooffset_texcoordtexture2f = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2928 loadmodel->surfmesh.vbooffset_texcoordlightmap2f = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2929 loadmodel->surfmesh.vbooffset_lightmapcolor4f = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2930 loadmodel->surfmesh.vbooffset_skeletalindex4ub = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2931 loadmodel->surfmesh.vbooffset_skeletalweight4ub = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2932 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2933 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertex3f , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2934 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_svector3f , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2935 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_tvector3f , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2936 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_normal3f , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2937 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordtexture2f , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2938 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordlightmap2f, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2939 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.vbooffset_lightmapcolor4f , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2940 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalindex4ub , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2941 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalweight4ub , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2942 loadmodel->surfmesh.vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2947 extern cvar_t mod_obj_orientation;
2948 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2950 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2952 const char *texname;
2954 const float *v, *vn, *vt;
2956 size_t outbufferpos = 0;
2957 size_t outbuffermax = 0x100000;
2958 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
2959 const msurface_t *surface;
2960 const int maxtextures = 256;
2961 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
2962 dp_model_t *submodel;
2964 // construct the mtllib file
2965 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
2968 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
2971 countvertices += surface->num_vertices;
2972 countfaces += surface->num_triangles;
2973 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
2974 for (textureindex = 0;textureindex < counttextures;textureindex++)
2975 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
2977 if (textureindex < counttextures)
2978 continue; // already wrote this material entry
2979 if (textureindex >= maxtextures)
2980 continue; // just a precaution
2981 textureindex = counttextures++;
2982 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
2983 if (outbufferpos >= outbuffermax >> 1)
2986 oldbuffer = outbuffer;
2987 outbuffer = (char *) Z_Malloc(outbuffermax);
2988 memcpy(outbuffer, oldbuffer, outbufferpos);
2991 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");
2996 // write the mtllib file
2997 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
2999 // construct the obj file
3001 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);
3005 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)
3007 if (outbufferpos >= outbuffermax >> 1)
3010 oldbuffer = outbuffer;
3011 outbuffer = (char *) Z_Malloc(outbuffermax);
3012 memcpy(outbuffer, oldbuffer, outbufferpos);
3015 if(mod_obj_orientation.integer)
3016 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]);
3018 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]);
3023 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3025 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3028 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3029 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3031 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3032 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3035 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3037 if (outbufferpos >= outbuffermax >> 1)
3040 oldbuffer = outbuffer;
3041 outbuffer = (char *) Z_Malloc(outbuffermax);
3042 memcpy(outbuffer, oldbuffer, outbufferpos);
3048 if(mod_obj_orientation.integer)
3049 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);
3051 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);
3058 // write the obj file
3059 FS_WriteFile(filename, outbuffer, outbufferpos);
3063 Z_Free(texturenames);
3066 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3069 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3071 int countnodes = 0, counttriangles = 0, countframes = 0;
3079 size_t outbufferpos = 0;
3080 size_t outbuffermax = 0x100000;
3081 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3082 const msurface_t *surface;
3083 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3086 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3088 if (outbufferpos >= outbuffermax >> 1)
3091 oldbuffer = outbuffer;
3092 outbuffer = (char *) Z_Malloc(outbuffermax);
3093 memcpy(outbuffer, oldbuffer, outbufferpos);
3097 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3101 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3104 for (poseindex = 0;poseindex < numposes;poseindex++)
3107 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3110 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3114 matrix4x4_t posematrix;
3115 if (outbufferpos >= outbuffermax >> 1)
3118 oldbuffer = outbuffer;
3119 outbuffer = (char *) Z_Malloc(outbuffermax);
3120 memcpy(outbuffer, oldbuffer, outbufferpos);
3124 // strangely the smd angles are for a transposed matrix, so we
3125 // have to generate a transposed matrix, then convert that...
3126 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3127 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3128 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3129 if (angles[0] >= 180) angles[0] -= 360;
3130 if (angles[1] >= 180) angles[1] -= 360;
3131 if (angles[2] >= 180) angles[2] -= 360;
3135 float a = DEG2RAD(angles[ROLL]);
3136 float b = DEG2RAD(angles[PITCH]);
3137 float c = DEG2RAD(angles[YAW]);
3138 float cy, sy, cp, sp, cr, sr;
3140 // smd matrix construction, for comparing
3151 test[1][0] = sr*sp*cy+cr*-sy;
3152 test[1][1] = sr*sp*sy+cr*cy;
3154 test[2][0] = (cr*sp*cy+-sr*-sy);
3155 test[2][1] = (cr*sp*sy+-sr*cy);
3157 test[3][0] = pose[9];
3158 test[3][1] = pose[10];
3159 test[3][2] = pose[11];
3162 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]));
3167 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3172 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3175 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3177 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3180 if (outbufferpos >= outbuffermax >> 1)
3183 oldbuffer = outbuffer;
3184 outbuffer = (char *) Z_Malloc(outbuffermax);
3185 memcpy(outbuffer, oldbuffer, outbufferpos);
3188 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3191 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3193 const int index = e[2-cornerindex];
3194 const float *v = model->surfmesh.data_vertex3f + index * 3;
3195 const float *vn = model->surfmesh.data_normal3f + index * 3;
3196 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3197 const int b = model->surfmesh.blends[index];
3198 if (b < model->num_bones)
3199 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]);
3202 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3203 const unsigned char *wi = w->index;
3204 const unsigned char *wf = w->influence;
3205 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);
3206 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);
3207 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);
3208 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]);
3215 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3220 FS_WriteFile(filename, outbuffer, outbufferpos);
3223 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3230 decompiles a model to editable files
3233 static void Mod_Decompile_f(void)
3235 int i, j, k, l, first, count;
3237 char inname[MAX_QPATH];
3238 char outname[MAX_QPATH];
3239 char mtlname[MAX_QPATH];
3240 char basename[MAX_QPATH];
3241 char animname[MAX_QPATH];
3242 char animname2[MAX_QPATH];
3243 char zymtextbuffer[16384];
3244 char dpmtextbuffer[16384];
3245 char framegroupstextbuffer[16384];
3246 int zymtextsize = 0;
3247 int dpmtextsize = 0;
3248 int framegroupstextsize = 0;
3251 if (Cmd_Argc() != 2)
3253 Con_Print("usage: modeldecompile <filename>\n");
3257 strlcpy(inname, Cmd_Argv(1), sizeof(inname));
3258 FS_StripExtension(inname, basename, sizeof(basename));
3260 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3263 Con_Print("No such model\n");
3266 if (mod->brush.submodel)
3268 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3269 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3270 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3273 if (!mod->surfmesh.num_triangles)
3275 Con_Print("Empty model (or sprite)\n");
3279 // export OBJ if possible (not on sprites)
3280 if (mod->surfmesh.num_triangles)
3282 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3283 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3284 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3287 // export SMD if possible (only for skeletal models)
3288 if (mod->surfmesh.num_triangles && mod->num_bones)
3290 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3291 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3292 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3293 if (l > 0) zymtextsize += l;
3294 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3295 if (l > 0) dpmtextsize += l;
3296 for (i = 0;i < mod->numframes;i = j)
3298 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3299 first = mod->animscenes[i].firstframe;
3300 if (mod->animscenes[i].framecount > 1)
3303 count = mod->animscenes[i].framecount;
3309 // check for additional frames with same name
3310 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3311 if(animname[l] < '0' || animname[l] > '9')
3313 if(k > 0 && animname[k-1] == '_')
3316 count = mod->num_poses - first;
3317 for (j = i + 1;j < mod->numframes;j++)
3319 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3320 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3321 if(animname2[l] < '0' || animname2[l] > '9')
3323 if(k > 0 && animname[k-1] == '_')
3326 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3328 count = mod->animscenes[j].firstframe - first;
3332 // if it's only one frame, use the original frame name
3334 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3337 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3338 Mod_Decompile_SMD(mod, outname, first, count, false);
3339 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3341 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3342 if (l > 0) zymtextsize += l;
3344 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3346 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3347 if (l > 0) dpmtextsize += l;
3349 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3351 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3352 if (l > 0) framegroupstextsize += l;
3356 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3358 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3359 if (framegroupstextsize)
3360 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3364 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3367 memset(state, 0, sizeof(*state));
3368 state->width = width;
3369 state->height = height;
3370 state->currentY = 0;
3371 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3372 for (y = 0;y < state->height;y++)
3374 state->rows[y].currentX = 0;
3375 state->rows[y].rowY = -1;
3379 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3382 state->currentY = 0;
3383 for (y = 0;y < state->height;y++)
3385 state->rows[y].currentX = 0;
3386 state->rows[y].rowY = -1;
3390 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3393 Mem_Free(state->rows);
3394 memset(state, 0, sizeof(*state));
3397 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3399 mod_alloclightmap_row_t *row;
3402 row = state->rows + blockheight;
3403 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3405 if (state->currentY + blockheight <= state->height)
3407 // use the current allocation position
3408 row->rowY = state->currentY;
3410 state->currentY += blockheight;
3414 // find another position
3415 for (y = blockheight;y < state->height;y++)
3417 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3419 row = state->rows + y;
3423 if (y == state->height)
3428 *outx = row->currentX;
3429 row->currentX += blockwidth;
3434 typedef struct lightmapsample_s
3438 float *vertex_color;
3439 unsigned char *lm_bgr;
3440 unsigned char *lm_dir;
3444 typedef struct lightmapvertex_s
3449 float texcoordbase[2];
3450 float texcoordlightmap[2];
3451 float lightcolor[4];
3455 typedef struct lightmaptriangle_s
3463 // 2D modelspace coordinates of min corner
3464 // snapped to lightmap grid but not in grid coordinates
3466 // 2D modelspace to lightmap coordinate scale
3474 typedef struct lightmaplight_s
3485 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3487 #define MAX_LIGHTMAPSAMPLES 64
3488 static int mod_generatelightmaps_numoffsets[3];
3489 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3491 static int mod_generatelightmaps_numlights;
3492 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3494 extern cvar_t r_shadow_lightattenuationdividebias;
3495 extern cvar_t r_shadow_lightattenuationlinearscale;
3497 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3502 float relativepoint[3];
3509 float lightorigin[3];
3513 float lightcolor[3];
3515 for (i = 0;i < 5*3;i++)
3517 for (index = 0;;index++)
3519 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3524 lightradius2 = lightradius * lightradius;
3525 VectorSubtract(lightorigin, pos, relativepoint);
3526 dist2 = VectorLength2(relativepoint);
3527 if (dist2 >= lightradius2)
3529 lightiradius = 1.0f / lightradius;
3530 dist = sqrt(dist2) * lightiradius;
3531 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3532 if (intensity <= 0.0f)
3534 if (model && model->TraceLine)
3536 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3537 if (trace.fraction < 1)
3540 // scale down intensity to add to both ambient and diffuse
3541 //intensity *= 0.5f;
3542 VectorNormalize(relativepoint);
3543 VectorScale(lightcolor, intensity, color);
3544 VectorMA(sample , 0.5f , color, sample );
3545 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3546 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3547 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3548 // calculate a weighted average light direction as well
3549 intensity *= VectorLength(color);
3550 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3552 // calculate the direction we'll use to reduce the sample to a directional light source
3553 VectorCopy(sample + 12, dir);
3554 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3555 VectorNormalize(dir);
3556 // extract the diffuse color along the chosen direction and scale it
3557 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3558 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3559 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3560 // subtract some of diffuse from ambient
3561 VectorMA(sample, -0.333f, diffuse, ambient);
3562 // store the normalized lightdir
3563 VectorCopy(dir, lightdir);
3566 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3570 const msurface_t *surface;
3571 const float *vertex3f = model->surfmesh.data_vertex3f;
3572 const int *element3i = model->surfmesh.data_element3i;
3575 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3577 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3579 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3581 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3583 VectorCopy(vertex3f + 3*e[0], v2[0]);
3584 VectorCopy(vertex3f + 3*e[1], v2[1]);
3585 VectorCopy(vertex3f + 3*e[2], v2[2]);
3586 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3591 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3593 int maxnodes = 1<<14;
3594 svbsp_node_t *nodes;
3599 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3600 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3601 VectorCopy(lightinfo->origin, origin);
3602 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3605 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3606 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3607 if (svbsp.ranoutofnodes)
3610 if (maxnodes > 1<<22)
3616 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3621 if (svbsp.numnodes > 0)
3623 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3624 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3625 lightinfo->svbsp = svbsp;
3630 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3634 lightmaplight_t *lightinfo;
3638 mod_generatelightmaps_numlights = 0;
3639 for (index = 0;;index++)
3641 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3645 mod_generatelightmaps_numlights++;
3647 if (mod_generatelightmaps_numlights > 0)
3649 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3650 lightinfo = mod_generatelightmaps_lightinfo;
3651 for (index = 0;;index++)
3653 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3660 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3662 lightinfo->iradius = 1.0f / lightinfo->radius;
3663 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3664 // TODO: compute svbsp
3665 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3669 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3672 if (mod_generatelightmaps_lightinfo)
3674 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3675 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3676 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3677 Mem_Free(mod_generatelightmaps_lightinfo);
3679 mod_generatelightmaps_lightinfo = NULL;
3680 mod_generatelightmaps_numlights = 0;
3683 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3685 const svbsp_node_t *node;
3686 const svbsp_node_t *nodes = svbsp->nodes;
3691 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3693 return num == -1; // true if empty, false if solid (shadowed)
3696 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3699 float relativepoint[3];
3708 const lightmaplight_t *lightinfo;
3710 for (i = 0;i < 5*3;i++)
3712 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3714 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3715 VectorSubtract(lightinfo->origin, pos, relativepoint);
3716 // don't accept light from behind a surface, it causes bad shading
3717 if (normal && DotProduct(relativepoint, normal) <= 0)
3719 dist2 = VectorLength2(relativepoint);
3720 if (dist2 >= lightinfo->radius2)
3722 dist = sqrt(dist2) * lightinfo->iradius;
3723 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3726 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3730 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3732 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3734 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3737 // for light grid we'd better check visibility of the offset point
3738 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3739 if (trace.fraction < 1)
3740 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3743 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3748 // scale intensity according to how many rays succeeded
3749 // we know one test is valid, half of the rest will fail...
3750 //if (normal && tests > 1)
3751 // intensity *= (tests - 1.0f) / tests;
3752 intensity *= (float)hits / tests;
3754 // scale down intensity to add to both ambient and diffuse
3755 //intensity *= 0.5f;
3756 VectorNormalize(relativepoint);
3757 VectorScale(lightinfo->color, intensity, color);
3758 VectorMA(sample , 0.5f , color, sample );
3759 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3760 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3761 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3762 // calculate a weighted average light direction as well
3763 intensity *= VectorLength(color);
3764 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3768 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3774 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3775 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3776 VectorCopy(sample + 12, dir);
3777 VectorNormalize(dir);
3778 //VectorAdd(dir, normal, dir);
3779 //VectorNormalize(dir);
3780 f = DotProduct(dir, normal);
3781 f = max(0, f) * 255.0f;
3782 VectorScale(sample, f, color);
3783 //VectorCopy(normal, dir);
3784 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3785 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3786 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3787 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3789 lm_dir[0] = (unsigned char)dir[2];
3790 lm_dir[1] = (unsigned char)dir[1];
3791 lm_dir[2] = (unsigned char)dir[0];
3795 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3798 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3799 VectorCopy(sample, vertex_color);
3802 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3808 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3809 // calculate the direction we'll use to reduce the sample to a directional light source
3810 VectorCopy(sample + 12, dir);
3811 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3812 VectorNormalize(dir);
3813 // extract the diffuse color along the chosen direction and scale it
3814 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3815 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3816 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3817 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3818 VectorScale(sample, 127.5f, ambient);
3819 VectorMA(ambient, -0.333f, diffuse, ambient);
3820 // encode to the grid format
3821 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3822 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3823 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3824 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3825 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3826 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3827 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3828 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3829 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));}
3832 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3837 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3838 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3839 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3840 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3841 radius[0] = mod_generatelightmaps_lightmapradius.value;
3842 radius[1] = mod_generatelightmaps_vertexradius.value;
3843 radius[2] = mod_generatelightmaps_gridradius.value;
3844 for (i = 0;i < 3;i++)
3846 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3849 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3854 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3856 msurface_t *surface;
3859 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3861 surface = model->data_surfaces + surfaceindex;
3862 surface->lightmaptexture = NULL;
3863 surface->deluxemaptexture = NULL;
3865 if (model->brushq3.data_lightmaps)
3867 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3868 if (model->brushq3.data_lightmaps[i])
3869 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3870 Mem_Free(model->brushq3.data_lightmaps);
3871 model->brushq3.data_lightmaps = NULL;
3873 if (model->brushq3.data_deluxemaps)
3875 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3876 if (model->brushq3.data_deluxemaps[i])
3877 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3878 Mem_Free(model->brushq3.data_deluxemaps);
3879 model->brushq3.data_deluxemaps = NULL;
3883 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3885 msurface_t *surface;
3891 surfmesh_t oldsurfmesh;
3893 unsigned char *data;
3894 oldsurfmesh = model->surfmesh;
3895 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3896 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3898 size += model->surfmesh.num_vertices * sizeof(float[3]);
3899 size += model->surfmesh.num_vertices * sizeof(float[3]);
3900 size += model->surfmesh.num_vertices * sizeof(float[3]);
3901 size += model->surfmesh.num_vertices * sizeof(float[3]);
3902 size += model->surfmesh.num_vertices * sizeof(float[2]);
3903 size += model->surfmesh.num_vertices * sizeof(float[2]);
3904 size += model->surfmesh.num_vertices * sizeof(float[4]);
3905 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3906 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3907 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3908 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3909 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3910 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3911 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3912 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3913 if (model->surfmesh.num_vertices > 65536)
3914 model->surfmesh.data_element3s = NULL;
3916 if (model->surfmesh.data_element3i_indexbuffer)
3917 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3918 model->surfmesh.data_element3i_indexbuffer = NULL;
3919 if (model->surfmesh.data_element3s_indexbuffer)
3920 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3921 model->surfmesh.data_element3s_indexbuffer = NULL;
3922 if (model->surfmesh.vbo_vertexbuffer)
3923 R_Mesh_DestroyMeshBuffer(model->surfmesh.vbo_vertexbuffer);
3924 model->surfmesh.vbo_vertexbuffer = 0;
3926 // convert all triangles to unique vertex data
3928 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3930 surface = model->data_surfaces + surfaceindex;
3931 surface->num_firstvertex = outvertexindex;
3932 surface->num_vertices = surface->num_triangles*3;
3933 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3934 for (i = 0;i < surface->num_triangles*3;i++)
3937 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3938 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3939 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3940 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3941 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3942 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3943 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3944 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3945 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3946 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3947 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3948 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3949 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
3950 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
3951 if (oldsurfmesh.data_texcoordlightmap2f)
3953 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
3954 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
3956 if (oldsurfmesh.data_lightmapcolor4f)
3958 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
3959 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
3960 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
3961 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
3964 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
3965 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
3969 if (model->surfmesh.data_element3s)
3970 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
3971 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
3973 // find and update all submodels to use this new surfmesh data
3974 for (i = 0;i < model->brush.numsubmodels;i++)
3975 model->brush.submodels[i]->surfmesh = model->surfmesh;
3978 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
3980 msurface_t *surface;
3986 lightmaptriangle_t *triangle;
3987 // generate lightmap triangle structs
3988 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
3989 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3991 surface = model->data_surfaces + surfaceindex;
3992 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
3993 for (i = 0;i < surface->num_triangles;i++)
3995 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
3996 triangle->triangleindex = surface->num_firsttriangle+i;
3997 triangle->surfaceindex = surfaceindex;
3998 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
3999 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4000 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4001 // calculate bounds of triangle
4002 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4003 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4004 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4005 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4006 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4007 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4008 // pick an axial projection based on the triangle normal
4009 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4011 if (fabs(normal[1]) > fabs(normal[axis]))
4013 if (fabs(normal[2]) > fabs(normal[axis]))
4015 triangle->axis = axis;
4020 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4022 if (mod_generatelightmaps_lightmaptriangles)
4023 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4024 mod_generatelightmaps_lightmaptriangles = NULL;
4027 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4029 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4031 msurface_t *surface;
4045 float trianglenormal[3];
4046 float samplecenter[3];
4047 float samplenormal[3];
4053 float lmscalepixels;
4056 float lm_basescalepixels;
4057 int lm_borderpixels;
4061 lightmaptriangle_t *triangle;
4062 unsigned char *lightmappixels;
4063 unsigned char *deluxemappixels;
4064 mod_alloclightmap_state_t lmstate;
4067 // generate lightmap projection information for all triangles
4068 if (model->texturepool == NULL)
4069 model->texturepool = R_AllocTexturePool();
4070 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4071 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4072 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4073 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4074 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4076 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4078 surface = model->data_surfaces + surfaceindex;
4079 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4080 lmscalepixels = lm_basescalepixels;
4081 for (retry = 0;retry < 30;retry++)
4083 // after a couple failed attempts, degrade quality to make it fit
4085 lmscalepixels *= 0.5f;
4086 for (i = 0;i < surface->num_triangles;i++)
4088 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4089 triangle->lightmapindex = lightmapnumber;
4090 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4091 // pick two planar axes for projection
4092 // lightmap coordinates here are in pixels
4093 // lightmap projections are snapped to pixel grid explicitly, such
4094 // that two neighboring triangles sharing an edge and projection
4095 // axis will have identical sample spacing along their shared edge
4097 for (j = 0;j < 3;j++)
4099 if (j == triangle->axis)
4101 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4102 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4103 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4104 triangle->lmbase[k] = lmmins/lmscalepixels;
4105 triangle->lmscale[k] = lmscalepixels;
4108 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4111 // if all fit in this texture, we're done with this surface
4112 if (i == surface->num_triangles)
4114 // if we haven't maxed out the lightmap size yet, we retry the
4115 // entire surface batch...
4116 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4118 lm_texturesize *= 2;
4121 Mod_AllocLightmap_Free(&lmstate);
4122 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4125 // if we have maxed out the lightmap size, and this triangle does
4126 // not fit in the same texture as the rest of the surface, we have
4127 // to retry the entire surface in a new texture (can only use one)
4128 // with multiple retries, the lightmap quality degrades until it
4129 // fits (or gives up)
4130 if (surfaceindex > 0)
4132 Mod_AllocLightmap_Reset(&lmstate);
4136 Mod_AllocLightmap_Free(&lmstate);
4138 // now put triangles together into lightmap textures, and do not allow
4139 // triangles of a surface to go into different textures (as that would
4140 // require rewriting the surface list)
4141 model->brushq3.deluxemapping_modelspace = true;
4142 model->brushq3.deluxemapping = true;
4143 model->brushq3.num_mergedlightmaps = lightmapnumber;
4144 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4145 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4146 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4147 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4148 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4150 surface = model->data_surfaces + surfaceindex;
4151 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4152 for (i = 0;i < surface->num_triangles;i++)
4154 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4155 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4156 VectorNormalize(trianglenormal);
4157 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4158 axis = triangle->axis;
4159 axis1 = axis == 0 ? 1 : 0;
4160 axis2 = axis == 2 ? 1 : 2;
4161 lmiscale[0] = 1.0f / triangle->lmscale[0];
4162 lmiscale[1] = 1.0f / triangle->lmscale[1];
4163 if (trianglenormal[axis] < 0)
4164 VectorNegate(trianglenormal, trianglenormal);
4165 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4166 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4167 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4168 for (j = 0;j < 3;j++)
4170 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4171 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4172 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4174 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4175 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4176 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4177 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]);
4187 forward[1] = 1.0f / triangle->lmscale[0];
4191 left[2] = 1.0f / triangle->lmscale[1];
4196 origin[1] = triangle->lmbase[0];
4197 origin[2] = triangle->lmbase[1];
4200 forward[0] = 1.0f / triangle->lmscale[0];
4205 left[2] = 1.0f / triangle->lmscale[1];
4209 origin[0] = triangle->lmbase[0];
4211 origin[2] = triangle->lmbase[1];
4214 forward[0] = 1.0f / triangle->lmscale[0];
4218 left[1] = 1.0f / triangle->lmscale[1];
4223 origin[0] = triangle->lmbase[0];
4224 origin[1] = triangle->lmbase[1];
4228 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4230 #define LM_DIST_EPSILON (1.0f / 32.0f)
4231 for (y = 0;y < triangle->lmsize[1];y++)
4233 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4234 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4236 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4237 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4238 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4239 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4240 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4246 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4248 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);
4249 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);
4253 Mem_Free(lightmappixels);
4254 if (deluxemappixels)
4255 Mem_Free(deluxemappixels);
4257 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4259 surface = model->data_surfaces + surfaceindex;
4260 if (!surface->num_triangles)
4262 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4263 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4264 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4265 surface->lightmapinfo = NULL;
4268 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4269 model->brushq1.lightdata = NULL;
4270 model->brushq1.lightmapupdateflags = NULL;
4271 model->brushq1.firstrender = false;
4272 model->brushq1.num_lightstyles = 0;
4273 model->brushq1.data_lightstyleinfo = NULL;
4274 for (i = 0;i < model->brush.numsubmodels;i++)
4276 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4277 model->brush.submodels[i]->brushq1.firstrender = false;
4278 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4279 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4283 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4286 for (i = 0;i < model->surfmesh.num_vertices;i++)
4287 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4290 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4297 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4299 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4300 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4302 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4303 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4305 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4306 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4312 extern cvar_t mod_q3bsp_nolightmaps;
4313 static void Mod_GenerateLightmaps(dp_model_t *model)
4315 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4316 dp_model_t *oldloadmodel = loadmodel;
4319 Mod_GenerateLightmaps_InitSampleOffsets(model);
4320 Mod_GenerateLightmaps_DestroyLightmaps(model);
4321 Mod_GenerateLightmaps_UnweldTriangles(model);
4322 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4323 Mod_GenerateLightmaps_CreateLights(model);
4324 if(!mod_q3bsp_nolightmaps.integer)
4325 Mod_GenerateLightmaps_CreateLightmaps(model);
4326 Mod_GenerateLightmaps_UpdateVertexColors(model);
4327 Mod_GenerateLightmaps_UpdateLightGrid(model);
4328 Mod_GenerateLightmaps_DestroyLights(model);
4329 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4331 loadmodel = oldloadmodel;
4334 static void Mod_GenerateLightmaps_f(void)
4336 if (Cmd_Argc() != 1)
4338 Con_Printf("usage: mod_generatelightmaps\n");
4343 Con_Printf("no worldmodel loaded\n");
4346 Mod_GenerateLightmaps(cl.worldmodel);
4349 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4351 memset(mod, 0, sizeof(*mod));
4352 strlcpy(mod->name, name, sizeof(mod->name));
4353 mod->mempool = Mem_AllocPool(name, 0, NULL);
4354 mod->texturepool = R_AllocTexturePool();
4355 mod->Draw = R_Q1BSP_Draw;
4356 mod->DrawDepth = R_Q1BSP_DrawDepth;
4357 mod->DrawDebug = R_Q1BSP_DrawDebug;
4358 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4359 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4360 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4361 mod->DrawLight = R_Q1BSP_DrawLight;
4364 void Mod_Mesh_Destroy(dp_model_t *mod)
4366 Mod_UnloadModel(mod);
4369 // resets the mesh model to have no geometry to render, ready for a new frame -
4370 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4371 void Mod_Mesh_Reset(dp_model_t *mod)
4373 mod->num_surfaces = 0;
4374 mod->surfmesh.num_vertices = 0;
4375 mod->surfmesh.num_triangles = 0;
4376 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4377 mod->DrawSky = NULL; // will be set if a texture needs it
4378 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4381 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4385 for (i = 0; i < mod->num_textures; i++)
4386 if (!strcmp(mod->data_textures[i].name, name))
4387 return mod->data_textures + i;
4388 if (mod->max_textures <= mod->num_textures)
4390 texture_t *oldtextures = mod->data_textures;
4391 mod->max_textures = max(mod->max_textures * 2, 1024);
4392 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4393 // update the pointers
4394 for (i = 0; i < mod->num_surfaces; i++)
4395 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4397 t = &mod->data_textures[mod->num_textures++];
4398 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
4399 switch (defaultdrawflags & DRAWFLAG_MASK)
4401 case DRAWFLAG_ADDITIVE:
4402 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4403 t->currentmaterialflags = t->basematerialflags;
4405 case DRAWFLAG_MODULATE:
4406 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4407 t->currentmaterialflags = t->basematerialflags;
4408 t->customblendfunc[0] = GL_DST_COLOR;
4409 t->customblendfunc[1] = GL_ZERO;
4411 case DRAWFLAG_2XMODULATE:
4412 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4413 t->currentmaterialflags = t->basematerialflags;
4414 t->customblendfunc[0] = GL_DST_COLOR;
4415 t->customblendfunc[1] = GL_SRC_COLOR;
4417 case DRAWFLAG_SCREEN:
4418 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4419 t->currentmaterialflags = t->basematerialflags;
4420 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4421 t->customblendfunc[1] = GL_ONE;
4429 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4432 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4433 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4434 return mod->data_surfaces + mod->num_surfaces - 1;
4435 // create new surface
4436 if (mod->max_surfaces == mod->num_surfaces)
4438 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4439 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4440 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4442 surf = mod->data_surfaces + mod->num_surfaces;
4443 mod->num_surfaces++;
4444 memset(surf, 0, sizeof(*surf));
4445 surf->texture = tex;
4446 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4447 surf->num_firstvertex = mod->surfmesh.num_vertices;
4448 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4449 mod->DrawSky = R_Q1BSP_DrawSky;
4450 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4451 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4455 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)
4457 int hashindex, h, vnum, mask;
4458 surfmesh_t *mesh = &mod->surfmesh;
4459 if (mesh->max_vertices == mesh->num_vertices)
4461 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4462 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4463 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4464 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4465 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4466 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4467 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4468 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4469 // rebuild the hash table
4470 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4471 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4472 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4473 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4474 mask = mod->surfmesh.num_vertexhashsize - 1;
4475 // no need to hash the vertices for the entire model, the latest surface will suffice.
4476 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4478 // this uses prime numbers intentionally for computing the hash
4479 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;
4480 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4481 ; // just iterate until we find the terminator
4482 mesh->data_vertexhash[h] = vnum;
4485 mask = mod->surfmesh.num_vertexhashsize - 1;
4486 // this uses prime numbers intentionally for computing the hash
4487 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4488 // when possible find an identical vertex within the same surface and return it
4489 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4491 if (vnum >= surf->num_firstvertex
4492 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4493 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4494 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4495 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4496 && 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)
4499 // add the new vertex
4500 vnum = mesh->num_vertices++;
4501 if (surf->num_vertices > 0)
4503 if (surf->mins[0] > x) surf->mins[0] = x;
4504 if (surf->mins[1] > y) surf->mins[1] = y;
4505 if (surf->mins[2] > z) surf->mins[2] = z;
4506 if (surf->maxs[0] < x) surf->maxs[0] = x;
4507 if (surf->maxs[1] < y) surf->maxs[1] = y;
4508 if (surf->maxs[2] < z) surf->maxs[2] = z;
4512 VectorSet(surf->mins, x, y, z);
4513 VectorSet(surf->maxs, x, y, z);
4515 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4516 mesh->data_vertexhash[h] = vnum;
4517 mesh->data_vertex3f[vnum * 3 + 0] = x;
4518 mesh->data_vertex3f[vnum * 3 + 1] = y;
4519 mesh->data_vertex3f[vnum * 3 + 2] = z;
4520 mesh->data_normal3f[vnum * 3 + 0] = nx;
4521 mesh->data_normal3f[vnum * 3 + 1] = ny;
4522 mesh->data_normal3f[vnum * 3 + 2] = nz;
4523 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4524 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4525 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4526 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4527 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4528 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4529 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4530 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4534 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4536 surfmesh_t *mesh = &mod->surfmesh;
4537 if (mesh->max_triangles == mesh->num_triangles)
4539 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4540 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4541 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4543 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4544 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4545 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4546 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4547 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4548 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4549 mesh->num_triangles++;
4550 surf->num_triangles++;
4553 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4557 msurface_t *surf, *surf2;
4559 // build the sorted surfaces list properly to reduce material setup
4560 // this is easy because we're just sorting on texture and don't care about the order of textures
4561 mod->nummodelsurfaces = 0;
4562 for (i = 0; i < mod->num_surfaces; i++)
4563 mod->data_surfaces[i].included = false;
4564 for (i = 0; i < mod->num_surfaces; i++)
4566 surf = mod->data_surfaces + i;
4569 tex = surf->texture;
4570 // j = i is intentional
4571 for (j = i; j < mod->num_surfaces; j++)
4573 surf2 = mod->data_surfaces + j;
4574 if (surf2->included)
4576 if (surf2->texture == tex)
4578 surf2->included = true;
4579 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4585 void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4588 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4590 if (mod->surfmesh.num_vertices > 0)
4592 // calculate normalmins/normalmaxs
4593 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4594 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4595 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4597 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4598 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4599 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4600 // expand bounds to include this vertex
4601 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4602 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4603 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4604 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4605 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4606 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4608 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4609 // (fast but less accurate than doing it per vertex)
4610 x2a = mod->normalmins[0] * mod->normalmins[0];
4611 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4612 y2a = mod->normalmins[1] * mod->normalmins[1];
4613 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4614 z2a = mod->normalmins[2] * mod->normalmins[2];
4615 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4619 yawradius = sqrt(x2 + y2);
4620 rotatedradius = sqrt(x2 + y2 + z2);
4621 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4622 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4623 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4624 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4625 mod->radius = rotatedradius;
4626 mod->radius2 = x2 + y2 + z2;
4630 VectorClear(mod->normalmins);
4631 VectorClear(mod->normalmaxs);
4632 VectorClear(mod->yawmins);
4633 VectorClear(mod->yawmaxs);
4634 VectorClear(mod->rotatedmins);
4635 VectorClear(mod->rotatedmaxs);
4641 void Mod_Mesh_Finalize(dp_model_t *mod)
4643 Mod_Mesh_ComputeBounds(mod);
4644 Mod_Mesh_MakeSortedSurfaces(mod);
4645 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);