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_CLIENT | 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_CLIENT | CVAR_SAVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CVAR_CLIENT | 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("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(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_f(cmd_state_t *cmd);
152 static void Mod_Precache_f(cmd_state_t *cmd);
153 static void Mod_Decompile_f(cmd_state_t *cmd);
154 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
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(CMD_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
178 Cmd_AddCommand(CMD_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
179 Cmd_AddCommand(CMD_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
180 Cmd_AddCommand(CMD_CLIENT, "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 && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
203 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
204 mod->surfmesh.data_element3i_indexbuffer = NULL;
205 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
206 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
207 mod->surfmesh.data_element3s_indexbuffer = NULL;
208 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
209 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
210 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
211 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
212 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
213 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
214 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
215 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
216 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
217 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
218 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
220 // free textures/memory attached to the model
221 R_FreeTexturePool(&mod->texturepool);
222 Mem_FreePool(&mod->mempool);
223 // clear the struct to make it available
224 memset(mod, 0, sizeof(dp_model_t));
225 // restore the fields we want to preserve
226 strlcpy(mod->name, name, sizeof(mod->name));
227 mod->brush.parentmodel = parentmodel;
232 static void R_Model_Null_Draw(entity_render_t *ent)
238 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass);
240 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
255 // REQUIRED: fetch start
256 COM_ParseToken_Simple(&bufptr, true, false, true);
258 break; // end of file
259 if (!strcmp(com_token, "\n"))
260 continue; // empty line
261 start = atoi(com_token);
263 // REQUIRED: fetch length
264 COM_ParseToken_Simple(&bufptr, true, false, true);
265 if (!bufptr || !strcmp(com_token, "\n"))
267 Con_Printf("framegroups file: missing number of frames\n");
270 len = atoi(com_token);
272 // OPTIONAL args start
273 COM_ParseToken_Simple(&bufptr, true, false, true);
275 // OPTIONAL: fetch fps
277 if (bufptr && strcmp(com_token, "\n"))
279 fps = atof(com_token);
280 COM_ParseToken_Simple(&bufptr, true, false, true);
283 // OPTIONAL: fetch loopflag
285 if (bufptr && strcmp(com_token, "\n"))
287 loop = (atoi(com_token) != 0);
288 COM_ParseToken_Simple(&bufptr, true, false, true);
291 // OPTIONAL: fetch name
293 if (bufptr && strcmp(com_token, "\n"))
295 strlcpy(name, com_token, sizeof(name));
296 COM_ParseToken_Simple(&bufptr, true, false, true);
299 // OPTIONAL: remaining unsupported tokens (eat them)
300 while (bufptr && strcmp(com_token, "\n"))
301 COM_ParseToken_Simple(&bufptr, true, false, true);
303 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
306 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
313 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass)
315 dp_model_t *mod = (dp_model_t *) pass;
316 animscene_t *anim = &mod->animscenes[i];
318 strlcpy(anim->name, name, sizeof(anim[i].name));
320 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
321 anim->firstframe = bound(0, start, mod->num_poses - 1);
322 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
323 anim->framerate = max(1, fps);
325 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
328 static void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
333 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
336 Con_Printf("no scene found in framegroups file, aborting\n");
339 mod->numframes = cnt;
342 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
343 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
346 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
349 static void Mod_FindPotentialDeforms(dp_model_t *mod)
353 mod->wantnormals = false;
354 mod->wanttangents = false;
355 for (i = 0;i < mod->num_textures;i++)
357 texture = mod->data_textures + i;
358 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
359 mod->wantnormals = true;
360 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
361 mod->wantnormals = true;
362 for (j = 0;j < Q3MAXDEFORMS;j++)
364 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
366 mod->wanttangents = true;
367 mod->wantnormals = true;
370 if (texture->deforms[j].deform != Q3DEFORM_NONE)
371 mod->wantnormals = true;
383 dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
388 fs_offset_t filesize = 0;
393 if (mod->name[0] == '*') // submodel
396 if (!strcmp(mod->name, "null"))
401 if (mod->loaded || mod->mempool)
402 Mod_UnloadModel(mod);
404 if (developer_loading.integer)
405 Con_Printf("loading model %s\n", mod->name);
408 mod->crc = (unsigned int)-1;
411 VectorClear(mod->normalmins);
412 VectorClear(mod->normalmaxs);
413 VectorClear(mod->yawmins);
414 VectorClear(mod->yawmaxs);
415 VectorClear(mod->rotatedmins);
416 VectorClear(mod->rotatedmaxs);
418 mod->modeldatatypestring = "null";
419 mod->type = mod_null;
420 mod->Draw = R_Model_Null_Draw;
424 // no fatal errors occurred, so this model is ready to use.
433 // even if the model is loaded it still may need reloading...
435 // if it is not loaded or checkdisk is true we need to calculate the crc
436 if (!mod->loaded || checkdisk)
438 if (checkdisk && mod->loaded)
439 Con_DPrintf("checking model %s\n", mod->name);
440 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
443 crc = CRC_Block((unsigned char *)buf, filesize);
444 // we need to reload the model if the crc does not match
450 // if the model is already loaded and checks passed, just return
458 if (developer_loading.integer)
459 Con_Printf("loading model %s\n", mod->name);
461 SCR_PushLoadingScreen(mod->name, 1);
463 // LadyHavoc: unload the existing model in this slot (if there is one)
464 if (mod->loaded || mod->mempool)
465 Mod_UnloadModel(mod);
470 // errors can prevent the corresponding mod->loaded = true;
473 // default lightmap scale
474 mod->lightmapscale = 1;
476 // default model radius and bounding box (mainly for missing models)
478 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
479 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
480 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
481 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
482 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
483 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
487 // load q3 shaders for the first time, or after a level change
493 char *bufend = (char *)buf + filesize;
495 // all models use memory, so allocate a memory pool
496 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
498 num = LittleLong(*((int *)buf));
499 // call the apropriate loader
501 if (!strcasecmp(FS_FileExtension(mod->name), "obj")) Mod_OBJ_Load(mod, buf, bufend);
502 else if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
503 else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend);
504 else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend);
505 else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend);
506 else if (!memcmp(buf, "IDS2", 4)) Mod_IDS2_Load(mod, buf, bufend);
507 else if (!memcmp(buf, "IBSP", 4)) Mod_IBSP_Load(mod, buf, bufend);
508 else if (!memcmp(buf, "ZYMOTICMODEL", 12)) Mod_ZYMOTICMODEL_Load(mod, buf, bufend);
509 else if (!memcmp(buf, "DARKPLACESMODEL", 16)) Mod_DARKPLACESMODEL_Load(mod, buf, bufend);
510 else if (!memcmp(buf, "ACTRHEAD", 8)) Mod_PSKMODEL_Load(mod, buf, bufend);
511 else if (!memcmp(buf, "INTERQUAKEMODEL", 16)) Mod_INTERQUAKEMODEL_Load(mod, buf, bufend);
512 else if (strlen(mod->name) >= 4 && !strcmp(mod->name + strlen(mod->name) - 4, ".map")) Mod_MAP_Load(mod, buf, bufend);
513 else if (num == BSPVERSION || num == 30 || !memcmp(buf, "BSP2", 4) || !memcmp(buf, "2PSB", 4)) Mod_Q1BSP_Load(mod, buf, bufend);
514 else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
517 Mod_FindPotentialDeforms(mod);
519 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
522 Mod_FrameGroupify(mod, (const char *)buf);
530 // LadyHavoc: Sys_Error was *ANNOYING*
531 Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
534 // no fatal errors occurred, so this model is ready to use.
537 SCR_PopLoadingScreen(false);
542 void Mod_ClearUsed(void)
545 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
547 for (i = 0;i < nummodels;i++)
548 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
552 void Mod_PurgeUnused(void)
555 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
557 for (i = 0;i < nummodels;i++)
559 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
561 Mod_UnloadModel(mod);
562 Mem_ExpandableArray_FreeRecord(&models, mod);
573 dp_model_t *Mod_FindName(const char *name, const char *parentname)
582 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
585 Host_Error ("Mod_ForName: empty name");
587 // search the currently loaded models
588 for (i = 0;i < nummodels;i++)
590 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))))
597 // no match found, create a new one
598 mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
599 strlcpy(mod->name, name, sizeof(mod->name));
601 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
603 mod->brush.parentmodel = NULL;
613 Loads in a model for the given name
616 dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
619 model = Mod_FindName(name, parentname);
620 if (!model->loaded || checkdisk)
621 Mod_LoadModel(model, crash, checkdisk);
629 Reloads all models if they have changed
632 void Mod_Reload(void)
635 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
638 SCR_PushLoadingScreen("Reloading models", 1.0);
640 for (i = 0;i < nummodels;i++)
641 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
643 for (i = 0;i < nummodels;i++)
644 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
646 SCR_PushLoadingScreen(mod->name, 1.0 / count);
647 Mod_LoadModel(mod, true, true);
648 SCR_PopLoadingScreen(false);
650 SCR_PopLoadingScreen(false);
653 unsigned char *mod_base;
656 //=============================================================================
663 static void Mod_Print_f(cmd_state_t *cmd)
666 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
669 Con_Print("Loaded models:\n");
670 for (i = 0;i < nummodels;i++)
672 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
674 if (mod->brush.numsubmodels)
675 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
677 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
687 static void Mod_Precache_f(cmd_state_t *cmd)
689 if (Cmd_Argc(cmd) == 2)
690 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
692 Con_Print("usage: modelprecache <filename>\n");
695 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
699 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
700 memset(used, 0, numvertices);
701 for (i = 0;i < numelements;i++)
702 used[elements[i]] = 1;
703 for (i = 0, count = 0;i < numvertices;i++)
704 remapvertices[i] = used[i] ? count++ : -1;
709 qboolean Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
711 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
713 int invalidintcount = 0, invalidintexample = 0;
714 int invalidshortcount = 0, invalidshortexample = 0;
715 int invalidmismatchcount = 0, invalidmismatchexample = 0;
718 for (i = 0; i < numelements; i++)
720 if (element3i[i] < first || element3i[i] > last)
723 invalidintexample = i;
729 for (i = 0; i < numelements; i++)
731 if (element3s[i] < first || element3s[i] > last)
734 invalidintexample = i;
738 if (element3i && element3s)
740 for (i = 0; i < numelements; i++)
742 if (element3s[i] != element3i[i])
744 invalidmismatchcount++;
745 invalidmismatchexample = i;
749 if (invalidintcount || invalidshortcount || invalidmismatchcount)
751 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, element3i, element3s, filename, fileline);
752 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
753 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
754 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);
755 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
757 // edit the elements to make them safer, as the driver will crash otherwise
759 for (i = 0; i < numelements; i++)
760 if (element3i[i] < first || element3i[i] > last)
761 element3i[i] = first;
763 for (i = 0; i < numelements; i++)
764 if (element3s[i] < first || element3s[i] > last)
765 element3s[i] = first;
766 if (element3i && element3s)
767 for (i = 0; i < numelements; i++)
768 if (element3s[i] != element3i[i])
769 element3s[i] = element3i[i];
776 // warning: this is an expensive function!
777 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
784 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
785 // process each vertex of each triangle and accumulate the results
786 // use area-averaging, to make triangles with a big area have a bigger
787 // weighting on the vertex normal than triangles with a small area
788 // to do so, just add the 'normals' together (the bigger the area
789 // the greater the length of the normal is
791 for (i = 0; i < numtriangles; i++, element += 3)
794 vertex3f + element[0] * 3,
795 vertex3f + element[1] * 3,
796 vertex3f + element[2] * 3,
801 VectorNormalize(areaNormal);
803 for (j = 0;j < 3;j++)
805 vectorNormal = normal3f + element[j] * 3;
806 vectorNormal[0] += areaNormal[0];
807 vectorNormal[1] += areaNormal[1];
808 vectorNormal[2] += areaNormal[2];
811 // and just normalize the accumulated vertex normal in the end
812 vectorNormal = normal3f + 3 * firstvertex;
813 for (i = 0; i < numvertices; i++, vectorNormal += 3)
814 VectorNormalize(vectorNormal);
818 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)
820 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
821 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
822 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
824 // 6 multiply, 9 subtract
825 VectorSubtract(v1, v0, v10);
826 VectorSubtract(v2, v0, v20);
827 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
828 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
829 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
830 // 12 multiply, 10 subtract
831 tc10[1] = tc1[1] - tc0[1];
832 tc20[1] = tc2[1] - tc0[1];
833 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
834 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
835 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
836 tc10[0] = tc1[0] - tc0[0];
837 tc20[0] = tc2[0] - tc0[0];
838 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
839 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
840 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
841 // 12 multiply, 4 add, 6 subtract
842 f = DotProduct(svector3f, normal3f);
843 svector3f[0] -= f * normal3f[0];
844 svector3f[1] -= f * normal3f[1];
845 svector3f[2] -= f * normal3f[2];
846 f = DotProduct(tvector3f, normal3f);
847 tvector3f[0] -= f * normal3f[0];
848 tvector3f[1] -= f * normal3f[1];
849 tvector3f[2] -= f * normal3f[2];
850 // if texture is mapped the wrong way (counterclockwise), the tangents
851 // have to be flipped, this is detected by calculating a normal from the
852 // two tangents, and seeing if it is opposite the surface normal
853 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
854 CrossProduct(tvector3f, svector3f, tangentcross);
855 if (DotProduct(tangentcross, normal3f) < 0)
857 VectorNegate(svector3f, svector3f);
858 VectorNegate(tvector3f, tvector3f);
863 // warning: this is a very expensive function!
864 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)
867 float sdir[3], tdir[3], normal[3], *svec, *tvec;
868 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
869 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
872 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
873 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
874 // process each vertex of each triangle and accumulate the results
875 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
877 v0 = vertex3f + e[0] * 3;
878 v1 = vertex3f + e[1] * 3;
879 v2 = vertex3f + e[2] * 3;
880 tc0 = texcoord2f + e[0] * 2;
881 tc1 = texcoord2f + e[1] * 2;
882 tc2 = texcoord2f + e[2] * 2;
884 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
885 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
887 // calculate the edge directions and surface normal
888 // 6 multiply, 9 subtract
889 VectorSubtract(v1, v0, v10);
890 VectorSubtract(v2, v0, v20);
891 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
892 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
893 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
895 // calculate the tangents
896 // 12 multiply, 10 subtract
897 tc10[1] = tc1[1] - tc0[1];
898 tc20[1] = tc2[1] - tc0[1];
899 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
900 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
901 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
902 tc10[0] = tc1[0] - tc0[0];
903 tc20[0] = tc2[0] - tc0[0];
904 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
905 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
906 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
908 // if texture is mapped the wrong way (counterclockwise), the tangents
909 // have to be flipped, this is detected by calculating a normal from the
910 // two tangents, and seeing if it is opposite the surface normal
911 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
912 CrossProduct(tdir, sdir, tangentcross);
913 if (DotProduct(tangentcross, normal) < 0)
915 VectorNegate(sdir, sdir);
916 VectorNegate(tdir, tdir);
921 VectorNormalize(sdir);
922 VectorNormalize(tdir);
924 for (i = 0;i < 3;i++)
926 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
927 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
930 // make the tangents completely perpendicular to the surface normal, and
931 // then normalize them
932 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
933 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
935 f = -DotProduct(svec, n);
936 VectorMA(svec, f, n, svec);
937 VectorNormalize(svec);
938 f = -DotProduct(tvec, n);
939 VectorMA(tvec, f, n, tvec);
940 VectorNormalize(tvec);
944 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors)
947 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));
948 loadmodel->surfmesh.num_vertices = numvertices;
949 loadmodel->surfmesh.num_triangles = numtriangles;
950 if (loadmodel->surfmesh.num_vertices)
952 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
953 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
954 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
955 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
956 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
957 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
959 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
961 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
963 if (loadmodel->surfmesh.num_triangles)
965 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
966 if (loadmodel->surfmesh.num_vertices <= 65536)
967 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
971 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
973 shadowmesh_t *newmesh;
974 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
975 newmesh->mempool = mempool;
976 newmesh->maxverts = maxverts;
977 newmesh->maxtriangles = maxtriangles;
978 newmesh->numverts = 0;
979 newmesh->numtriangles = 0;
980 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
981 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
983 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
984 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
985 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
986 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
990 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
993 shadowmeshvertexhash_t *hash;
994 // this uses prime numbers intentionally
995 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
996 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
998 vnum = (hash - mesh->vertexhashentries);
999 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1000 return hash - mesh->vertexhashentries;
1002 vnum = mesh->numverts++;
1003 hash = mesh->vertexhashentries + vnum;
1004 hash->next = mesh->vertexhashtable[hashindex];
1005 mesh->vertexhashtable[hashindex] = hash;
1006 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1007 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1008 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1012 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1016 for (i = 0;i < numtris;i++)
1018 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1019 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1020 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1021 mesh->numtriangles++;
1024 // the triangle calculation can take a while, so let's do a keepalive here
1025 CL_KeepaliveMessage(false);
1028 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1030 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1031 CL_KeepaliveMessage(false);
1033 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1036 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1038 if (!mesh->numverts)
1041 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1042 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1044 // upload short indices as a buffer
1045 if (mesh->element3s && !mesh->element3s_indexbuffer)
1046 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1048 // upload int indices as a buffer
1049 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1050 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1052 // vertex buffer is several arrays and we put them in the same buffer
1054 // is this wise? the texcoordtexture2f array is used with dynamic
1055 // vertex/svector/tvector/normal when rendering animated models, on the
1056 // other hand animated models don't use a lot of vertices anyway...
1057 if (!mesh->vbo_vertexbuffer)
1059 mesh->vbooffset_vertex3f = 0;
1060 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1064 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qboolean createvbo)
1066 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1068 if (mesh->vertexhashentries)
1069 Mem_Free(mesh->vertexhashentries);
1070 mesh->vertexhashentries = NULL;
1071 if (mesh->vertexhashtable)
1072 Mem_Free(mesh->vertexhashtable);
1073 mesh->vertexhashtable = NULL;
1074 if (mesh->maxverts > mesh->numverts)
1076 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1077 mesh->maxverts = mesh->numverts;
1079 if (mesh->maxtriangles > mesh->numtriangles)
1081 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1082 mesh->maxtriangles = mesh->numtriangles;
1084 if (mesh->numverts <= 65536)
1087 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1088 for (i = 0;i < mesh->numtriangles*3;i++)
1089 mesh->element3s[i] = mesh->element3i[i];
1092 Mod_ShadowMesh_CreateVBOs(mesh);
1095 // this can take a while, so let's do a keepalive here
1096 CL_KeepaliveMessage(false);
1101 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1104 vec3_t nmins, nmaxs, ncenter, temp;
1105 float nradius2, dist2, *v;
1109 VectorCopy(mesh->vertex3f, nmins);
1110 VectorCopy(mesh->vertex3f, nmaxs);
1111 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1113 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1114 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1115 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1117 // calculate center and radius
1118 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1119 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1120 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1122 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1124 VectorSubtract(v, ncenter, temp);
1125 dist2 = DotProduct(temp, temp);
1126 if (nradius2 < dist2)
1131 VectorCopy(nmins, mins);
1133 VectorCopy(nmaxs, maxs);
1135 VectorCopy(ncenter, center);
1137 *radius = sqrt(nradius2);
1140 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1142 if (mesh->element3i_indexbuffer)
1143 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1144 if (mesh->element3s_indexbuffer)
1145 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1146 if (mesh->vbo_vertexbuffer)
1147 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1149 Mem_Free(mesh->vertex3f);
1150 if (mesh->element3i)
1151 Mem_Free(mesh->element3i);
1152 if (mesh->element3s)
1153 Mem_Free(mesh->element3s);
1154 if (mesh->vertexhashentries)
1155 Mem_Free(mesh->vertexhashentries);
1156 if (mesh->vertexhashtable)
1157 Mem_Free(mesh->vertexhashtable);
1161 void Mod_CreateCollisionMesh(dp_model_t *mod)
1163 int k, numcollisionmeshtriangles;
1164 qboolean usesinglecollisionmesh = false;
1165 const msurface_t *surface = NULL;
1167 mempool_t *mempool = mod->mempool;
1168 if (!mempool && mod->brush.parentmodel)
1169 mempool = mod->brush.parentmodel->mempool;
1170 // make a single combined collision mesh for physics engine use
1171 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1172 numcollisionmeshtriangles = 0;
1173 for (k = 0;k < mod->nummodelsurfaces;k++)
1175 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1176 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1178 usesinglecollisionmesh = true;
1179 numcollisionmeshtriangles = surface->num_triangles;
1182 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1184 numcollisionmeshtriangles += surface->num_triangles;
1186 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1187 if (usesinglecollisionmesh)
1188 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1191 for (k = 0;k < mod->nummodelsurfaces;k++)
1193 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1194 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1196 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1199 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1203 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)
1208 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1209 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1212 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1213 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1214 texcoord2f[0] = tc[0];
1215 texcoord2f[1] = tc[1];
1218 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)
1220 float vup[3], vdown[3], vleft[3], vright[3];
1221 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1222 float sv[3], tv[3], nl[3];
1223 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1224 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1225 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1226 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1227 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1228 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1229 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1230 VectorAdd(svector3f, sv, svector3f);
1231 VectorAdd(tvector3f, tv, tvector3f);
1232 VectorAdd(normal3f, nl, normal3f);
1233 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1234 VectorAdd(svector3f, sv, svector3f);
1235 VectorAdd(tvector3f, tv, tvector3f);
1236 VectorAdd(normal3f, nl, normal3f);
1237 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1238 VectorAdd(svector3f, sv, svector3f);
1239 VectorAdd(tvector3f, tv, tvector3f);
1240 VectorAdd(normal3f, nl, normal3f);
1243 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)
1245 int x, y, ix, iy, *e;
1247 for (y = 0;y < height;y++)
1249 for (x = 0;x < width;x++)
1251 e[0] = (y + 1) * (width + 1) + (x + 0);
1252 e[1] = (y + 0) * (width + 1) + (x + 0);
1253 e[2] = (y + 1) * (width + 1) + (x + 1);
1254 e[3] = (y + 0) * (width + 1) + (x + 0);
1255 e[4] = (y + 0) * (width + 1) + (x + 1);
1256 e[5] = (y + 1) * (width + 1) + (x + 1);
1260 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1261 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1262 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1267 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1271 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1272 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1273 float viewvector[3];
1274 unsigned int firstvertex;
1277 if (chunkwidth < 2 || chunkheight < 2)
1279 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]);
1280 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]);
1281 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1282 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1283 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1284 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1286 // too close for this stepsize, emit as 4 chunks instead
1288 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1289 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1290 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1291 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1294 // emit the geometry at stepsize into our vertex buffer / index buffer
1295 // we add two columns and two rows for skirt
1296 outwidth = chunkwidth+2;
1297 outheight = chunkheight+2;
1298 outwidth2 = outwidth-1;
1299 outheight2 = outheight-1;
1300 outwidth3 = outwidth+1;
1301 outheight3 = outheight+1;
1302 firstvertex = numvertices;
1303 e = model->terrain.element3i + numtriangles;
1304 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1305 v = model->terrain.vertex3f + numvertices;
1306 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1307 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1308 for (ty = 0;ty < outheight;ty++)
1310 for (tx = 0;tx < outwidth;tx++)
1312 *e++ = firstvertex + (ty )*outwidth3+(tx );
1313 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1314 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1315 *e++ = firstvertex + (ty )*outwidth3+(tx );
1316 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1317 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1320 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1321 for (ty = 0;ty <= outheight;ty++)
1323 skirtrow = ty == 0 || ty == outheight;
1324 ry = y+bound(1, ty, outheight)*stepsize;
1325 for (tx = 0;tx <= outwidth;tx++)
1327 skirt = skirtrow || tx == 0 || tx == outwidth;
1328 rx = x+bound(1, tx, outwidth)*stepsize;
1331 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1335 // TODO: emit skirt vertices
1338 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1340 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1341 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1342 Mod_Terrain_BuildChunk(model,
1346 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1349 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1351 offset = bound(0, s[4] - '0', 9);
1352 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1357 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1358 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1359 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1360 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1361 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1362 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1363 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1364 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1365 return offset | Q3WAVEFUNC_NONE;
1368 void Mod_FreeQ3Shaders(void)
1370 Mem_FreePool(&q3shaders_mem);
1373 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
1375 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1376 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1377 q3shader_hash_entry_t* lastEntry = NULL;
1380 if (strcasecmp (entry->shader.name, shader->name) == 0)
1383 if(shader->dpshaderkill)
1385 // killed shader is a redeclarion? we can safely ignore it
1388 else if(entry->shader.dpshaderkill)
1390 // replace the old shader!
1391 // this will skip the entry allocating part
1392 // below and just replace the shader
1397 unsigned char *start, *end, *start2;
1398 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1399 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1400 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1401 if(memcmp(start, start2, end - start))
1402 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1404 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1409 entry = entry->chain;
1411 while (entry != NULL);
1414 if (lastEntry->shader.name[0] != 0)
1417 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1418 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1420 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1421 lastEntry->chain = newEntry;
1422 newEntry->chain = NULL;
1423 lastEntry = newEntry;
1425 /* else: head of chain, in hash entry array */
1428 memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
1431 void Mod_LoadQ3Shaders(void)
1438 q3shaderinfo_t shader;
1439 q3shaderinfo_layer_t *layer;
1441 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1442 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1443 unsigned long custsurfaceflags[256];
1444 int numcustsurfaceflags;
1445 qboolean dpshaderkill;
1447 Mod_FreeQ3Shaders();
1449 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1450 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1451 sizeof (q3shader_data_t));
1452 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1453 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1454 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1455 q3shaders_mem, sizeof (char**), 256);
1457 // parse custinfoparms.txt
1458 numcustsurfaceflags = 0;
1459 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1461 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1462 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1465 while (COM_ParseToken_QuakeC(&text, false))
1466 if (!strcasecmp(com_token, "}"))
1468 // custom surfaceflags section
1469 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1470 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1473 while(COM_ParseToken_QuakeC(&text, false))
1475 if (!strcasecmp(com_token, "}"))
1477 // register surfaceflag
1478 if (numcustsurfaceflags >= 256)
1480 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1484 j = (int)strlen(com_token)+1;
1485 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1486 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1488 if (COM_ParseToken_QuakeC(&text, false))
1489 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1491 custsurfaceflags[numcustsurfaceflags] = 0;
1492 numcustsurfaceflags++;
1500 search = FS_Search("scripts/*.shader", true, false);
1503 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1505 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1508 while (COM_ParseToken_QuakeC(&text, false))
1510 memset (&shader, 0, sizeof(shader));
1512 shader.surfaceparms = 0;
1513 shader.surfaceflags = 0;
1514 shader.textureflags = 0;
1515 shader.numlayers = 0;
1516 shader.lighting = false;
1517 shader.vertexalpha = false;
1518 shader.textureblendalpha = false;
1519 shader.skyboxname[0] = 0;
1520 shader.deforms[0].deform = Q3DEFORM_NONE;
1521 shader.dpnortlight = false;
1522 shader.dpshadow = false;
1523 shader.dpnoshadow = false;
1524 shader.dpmeshcollisions = false;
1525 shader.dpshaderkill = false;
1526 shader.dpreflectcube[0] = 0;
1527 shader.reflectmin = 0;
1528 shader.reflectmax = 1;
1529 shader.refractfactor = 1;
1530 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1531 shader.reflectfactor = 1;
1532 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1533 shader.r_water_wateralpha = 1;
1534 shader.r_water_waterscroll[0] = 0;
1535 shader.r_water_waterscroll[1] = 0;
1536 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1537 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1538 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1539 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1540 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1541 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1542 shader.specularscalemod = 1;
1543 shader.specularpowermod = 1;
1544 shader.rtlightambient = 0;
1545 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1546 // JUST GREP FOR "specularscalemod = 1".
1548 strlcpy(shader.name, com_token, sizeof(shader.name));
1549 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1551 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1554 while (COM_ParseToken_QuakeC(&text, false))
1556 if (!strcasecmp(com_token, "}"))
1558 if (!strcasecmp(com_token, "{"))
1560 static q3shaderinfo_layer_t dummy;
1561 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1563 layer = shader.layers + shader.numlayers++;
1567 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1568 memset(&dummy, 0, sizeof(dummy));
1571 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1572 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1573 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1574 layer->blendfunc[0] = GL_ONE;
1575 layer->blendfunc[1] = GL_ZERO;
1576 while (COM_ParseToken_QuakeC(&text, false))
1578 if (!strcasecmp(com_token, "}"))
1580 if (!strcasecmp(com_token, "\n"))
1583 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1585 if (j < TEXTURE_MAXFRAMES + 4)
1587 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1588 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1589 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1591 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1592 numparameters = j + 1;
1594 if (!COM_ParseToken_QuakeC(&text, true))
1597 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1598 // parameter[j][0] = 0;
1599 if (developer_insane.integer)
1601 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1602 for (j = 0;j < numparameters;j++)
1603 Con_DPrintf(" %s", parameter[j]);
1606 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1608 if (numparameters == 2)
1610 if (!strcasecmp(parameter[1], "add"))
1612 layer->blendfunc[0] = GL_ONE;
1613 layer->blendfunc[1] = GL_ONE;
1615 else if (!strcasecmp(parameter[1], "addalpha"))
1617 layer->blendfunc[0] = GL_SRC_ALPHA;
1618 layer->blendfunc[1] = GL_ONE;
1620 else if (!strcasecmp(parameter[1], "filter"))
1622 layer->blendfunc[0] = GL_DST_COLOR;
1623 layer->blendfunc[1] = GL_ZERO;
1625 else if (!strcasecmp(parameter[1], "blend"))
1627 layer->blendfunc[0] = GL_SRC_ALPHA;
1628 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1631 else if (numparameters == 3)
1634 for (k = 0;k < 2;k++)
1636 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1637 layer->blendfunc[k] = GL_ONE;
1638 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1639 layer->blendfunc[k] = GL_ZERO;
1640 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1641 layer->blendfunc[k] = GL_SRC_COLOR;
1642 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1643 layer->blendfunc[k] = GL_SRC_ALPHA;
1644 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1645 layer->blendfunc[k] = GL_DST_COLOR;
1646 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1647 layer->blendfunc[k] = GL_DST_ALPHA;
1648 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1649 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1650 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1651 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1652 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1653 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1654 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1655 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1657 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1661 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1662 layer->alphatest = true;
1663 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1665 if (!strcasecmp(parameter[0], "clampmap"))
1666 layer->clampmap = true;
1667 layer->numframes = 1;
1668 layer->framerate = 1;
1669 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1670 &q3shader_data->char_ptrs);
1671 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1672 if (!strcasecmp(parameter[1], "$lightmap"))
1673 shader.lighting = true;
1675 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1678 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1679 layer->framerate = atof(parameter[1]);
1680 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1681 for (i = 0;i < layer->numframes;i++)
1682 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1684 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1687 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1688 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1689 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1690 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1691 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1692 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1693 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1694 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1695 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1696 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1697 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1698 else if (!strcasecmp(parameter[1], "wave"))
1700 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1701 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1702 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1703 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1705 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1707 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1710 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1711 layer->alphagen.parms[i] = atof(parameter[i+2]);
1712 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1713 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1714 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1715 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1716 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1717 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1718 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1719 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1720 else if (!strcasecmp(parameter[1], "wave"))
1722 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1723 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1724 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1725 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1727 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1729 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1732 // observed values: tcgen environment
1733 // no other values have been observed in real shaders
1734 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1735 layer->tcgen.parms[i] = atof(parameter[i+2]);
1736 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1737 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1738 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1739 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1740 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1741 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1743 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1750 // tcmod stretch sin # # # #
1751 // tcmod stretch triangle # # # #
1752 // tcmod transform # # # # # #
1753 // tcmod turb # # # #
1754 // tcmod turb sin # # # # (this is bogus)
1755 // no other values have been observed in real shaders
1756 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1757 if (!layer->tcmods[tcmodindex].tcmod)
1759 if (tcmodindex < Q3MAXTCMODS)
1761 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1762 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1763 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1764 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1765 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1766 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1767 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1768 else if (!strcasecmp(parameter[1], "stretch"))
1770 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1771 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1772 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1773 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1775 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1776 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1777 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1780 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1782 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1783 if (!strcasecmp(com_token, "}"))
1786 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1787 shader.lighting = true;
1788 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1790 if (layer == shader.layers + 0)
1792 // vertex controlled transparency
1793 shader.vertexalpha = true;
1797 // multilayer terrain shader or similar
1798 shader.textureblendalpha = true;
1799 if (mod_q3shader_force_terrain_alphaflag.integer)
1800 shader.layers[0].dptexflags |= TEXF_ALPHA;
1804 if(mod_q3shader_force_addalpha.integer)
1806 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1807 // this cvar brings back this behaviour
1808 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1809 layer->blendfunc[0] = GL_SRC_ALPHA;
1812 layer->dptexflags = 0;
1813 if (layer->alphatest)
1814 layer->dptexflags |= TEXF_ALPHA;
1815 switch(layer->blendfunc[0])
1818 case GL_ONE_MINUS_SRC_ALPHA:
1819 layer->dptexflags |= TEXF_ALPHA;
1822 switch(layer->blendfunc[1])
1825 case GL_ONE_MINUS_SRC_ALPHA:
1826 layer->dptexflags |= TEXF_ALPHA;
1829 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1830 layer->dptexflags |= TEXF_MIPMAP;
1831 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1832 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1833 if (layer->clampmap)
1834 layer->dptexflags |= TEXF_CLAMP;
1838 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1840 if (j < TEXTURE_MAXFRAMES + 4)
1842 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1843 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1844 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1846 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1847 numparameters = j + 1;
1849 if (!COM_ParseToken_QuakeC(&text, true))
1852 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1853 // parameter[j][0] = 0;
1854 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1856 if (developer_insane.integer)
1858 Con_DPrintf("%s: ", shader.name);
1859 for (j = 0;j < numparameters;j++)
1860 Con_DPrintf(" %s", parameter[j]);
1863 if (numparameters < 1)
1865 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1867 if (!strcasecmp(parameter[1], "alphashadow"))
1868 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1869 else if (!strcasecmp(parameter[1], "areaportal"))
1870 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1871 else if (!strcasecmp(parameter[1], "botclip"))
1872 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1873 else if (!strcasecmp(parameter[1], "clusterportal"))
1874 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1875 else if (!strcasecmp(parameter[1], "detail"))
1876 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1877 else if (!strcasecmp(parameter[1], "donotenter"))
1878 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1879 else if (!strcasecmp(parameter[1], "dust"))
1880 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1881 else if (!strcasecmp(parameter[1], "hint"))
1882 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1883 else if (!strcasecmp(parameter[1], "fog"))
1884 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1885 else if (!strcasecmp(parameter[1], "lava"))
1886 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1887 else if (!strcasecmp(parameter[1], "lightfilter"))
1888 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1889 else if (!strcasecmp(parameter[1], "lightgrid"))
1890 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1891 else if (!strcasecmp(parameter[1], "metalsteps"))
1892 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1893 else if (!strcasecmp(parameter[1], "nodamage"))
1894 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1895 else if (!strcasecmp(parameter[1], "nodlight"))
1896 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1897 else if (!strcasecmp(parameter[1], "nodraw"))
1898 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1899 else if (!strcasecmp(parameter[1], "nodrop"))
1900 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1901 else if (!strcasecmp(parameter[1], "noimpact"))
1902 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1903 else if (!strcasecmp(parameter[1], "nolightmap"))
1904 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1905 else if (!strcasecmp(parameter[1], "nomarks"))
1906 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1907 else if (!strcasecmp(parameter[1], "nomipmaps"))
1908 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1909 else if (!strcasecmp(parameter[1], "nonsolid"))
1910 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1911 else if (!strcasecmp(parameter[1], "origin"))
1912 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1913 else if (!strcasecmp(parameter[1], "playerclip"))
1914 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1915 else if (!strcasecmp(parameter[1], "sky"))
1916 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1917 else if (!strcasecmp(parameter[1], "slick"))
1918 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1919 else if (!strcasecmp(parameter[1], "slime"))
1920 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1921 else if (!strcasecmp(parameter[1], "structural"))
1922 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1923 else if (!strcasecmp(parameter[1], "trans"))
1924 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1925 else if (!strcasecmp(parameter[1], "water"))
1926 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1927 else if (!strcasecmp(parameter[1], "pointlight"))
1928 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1929 else if (!strcasecmp(parameter[1], "antiportal"))
1930 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1931 else if (!strcasecmp(parameter[1], "skip"))
1932 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1935 // try custom surfaceparms
1936 for (j = 0; j < numcustsurfaceflags; j++)
1938 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1940 shader.surfaceflags |= custsurfaceflags[j];
1945 if (j == numcustsurfaceflags)
1946 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1949 else if (!strcasecmp(parameter[0], "dpshadow"))
1950 shader.dpshadow = true;
1951 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1952 shader.dpnoshadow = true;
1953 else if (!strcasecmp(parameter[0], "dpnortlight"))
1954 shader.dpnortlight = true;
1955 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1956 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1957 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1958 shader.dpmeshcollisions = true;
1959 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1960 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1962 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1963 shader.dpshaderkill = dpshaderkill;
1965 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1966 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1968 const char *op = NULL;
1969 if (numparameters >= 3)
1973 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
1974 shader.dpshaderkill = dpshaderkill;
1976 else if (numparameters >= 4 && !strcmp(op, "=="))
1978 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
1979 shader.dpshaderkill = dpshaderkill;
1981 else if (numparameters >= 4 && !strcmp(op, "!="))
1983 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
1984 shader.dpshaderkill = dpshaderkill;
1986 else if (numparameters >= 4 && !strcmp(op, ">"))
1988 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
1989 shader.dpshaderkill = dpshaderkill;
1991 else if (numparameters >= 4 && !strcmp(op, "<"))
1993 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
1994 shader.dpshaderkill = dpshaderkill;
1996 else if (numparameters >= 4 && !strcmp(op, ">="))
1998 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
1999 shader.dpshaderkill = dpshaderkill;
2001 else if (numparameters >= 4 && !strcmp(op, "<="))
2003 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
2004 shader.dpshaderkill = dpshaderkill;
2008 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2011 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2013 // some q3 skies don't have the sky parm set
2014 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2015 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2017 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2019 // some q3 skies don't have the sky parm set
2020 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2021 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2022 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2024 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2026 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2027 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2029 else if (!strcasecmp(parameter[0], "nomipmaps"))
2030 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2031 else if (!strcasecmp(parameter[0], "nopicmip"))
2032 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2033 else if (!strcasecmp(parameter[0], "polygonoffset"))
2034 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2035 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2037 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2038 if(numparameters >= 2)
2040 shader.biaspolygonfactor = atof(parameter[1]);
2041 if(numparameters >= 3)
2042 shader.biaspolygonoffset = atof(parameter[2]);
2044 shader.biaspolygonoffset = 0;
2047 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2049 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2050 if (!strcasecmp(parameter[1], "sky"))
2051 shader.transparentsort = TRANSPARENTSORT_SKY;
2052 else if (!strcasecmp(parameter[1], "distance"))
2053 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2054 else if (!strcasecmp(parameter[1], "hud"))
2055 shader.transparentsort = TRANSPARENTSORT_HUD;
2057 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2059 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2061 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2062 shader.refractfactor = atof(parameter[1]);
2063 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2065 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2067 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2068 shader.reflectfactor = atof(parameter[1]);
2069 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2071 else if (!strcasecmp(parameter[0], "dpcamera"))
2073 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2075 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2077 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2078 shader.reflectmin = atof(parameter[1]);
2079 shader.reflectmax = atof(parameter[2]);
2080 shader.refractfactor = atof(parameter[3]);
2081 shader.reflectfactor = atof(parameter[4]);
2082 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2083 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2084 shader.r_water_wateralpha = atof(parameter[11]);
2086 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2088 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2089 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2091 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2093 shader.specularscalemod = atof(parameter[1]);
2095 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2097 shader.specularpowermod = atof(parameter[1]);
2099 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2101 shader.rtlightambient = atof(parameter[1]);
2103 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2105 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2106 shader.offsetmapping = OFFSETMAPPING_OFF;
2107 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2108 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2109 else if (!strcasecmp(parameter[1], "linear"))
2110 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2111 else if (!strcasecmp(parameter[1], "relief"))
2112 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2113 if (numparameters >= 3)
2114 shader.offsetscale = atof(parameter[2]);
2115 if (numparameters >= 5)
2117 if(!strcasecmp(parameter[3], "bias"))
2118 shader.offsetbias = atof(parameter[4]);
2119 else if(!strcasecmp(parameter[3], "match"))
2120 shader.offsetbias = 1.0f - atof(parameter[4]);
2121 else if(!strcasecmp(parameter[3], "match8"))
2122 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2123 else if(!strcasecmp(parameter[3], "match16"))
2124 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2127 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2130 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2131 if (!shader.deforms[deformindex].deform)
2133 if (deformindex < Q3MAXDEFORMS)
2135 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2136 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2137 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2138 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2139 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2140 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2141 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2142 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2143 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2144 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2145 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2146 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2147 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2148 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2149 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2150 else if (!strcasecmp(parameter[1], "wave" ))
2152 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2153 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2154 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2155 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2157 else if (!strcasecmp(parameter[1], "move" ))
2159 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2160 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2161 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2162 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2167 // hide this shader if a cvar said it should be killed
2168 if (shader.dpshaderkill)
2169 shader.numlayers = 0;
2170 // fix up multiple reflection types
2171 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2172 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2174 Q3Shader_AddToHash (&shader);
2178 FS_FreeSearch(search);
2179 // free custinfoparm values
2180 for (j = 0; j < numcustsurfaceflags; j++)
2181 Mem_Free(custsurfaceparmnames[j]);
2184 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
2186 unsigned short hash;
2187 q3shader_hash_entry_t* entry;
2189 Mod_LoadQ3Shaders();
2190 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2191 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2192 while (entry != NULL)
2194 if (strcasecmp (entry->shader.name, name) == 0)
2195 return &entry->shader;
2196 entry = entry->chain;
2201 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2203 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2204 shaderpass->framerate = 0.0f;
2205 shaderpass->numframes = 1;
2206 shaderpass->blendfunc[0] = GL_ONE;
2207 shaderpass->blendfunc[1] = GL_ZERO;
2208 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2209 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2210 shaderpass->alphatest = false;
2211 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2212 shaderpass->skinframes[0] = skinframe;
2216 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2219 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2220 shaderpass->alphatest = layer->alphatest != 0;
2221 shaderpass->framerate = layer->framerate;
2222 shaderpass->numframes = layer->numframes;
2223 shaderpass->blendfunc[0] = layer->blendfunc[0];
2224 shaderpass->blendfunc[1] = layer->blendfunc[1];
2225 shaderpass->rgbgen = layer->rgbgen;
2226 shaderpass->alphagen = layer->alphagen;
2227 shaderpass->tcgen = layer->tcgen;
2228 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2229 shaderpass->tcmods[j] = layer->tcmods[j];
2230 for (j = 0; j < layer->numframes; j++)
2231 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2235 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2237 int texflagsmask, texflagsor;
2238 qboolean success = true;
2239 q3shaderinfo_t *shader;
2242 strlcpy(texture->name, name, sizeof(texture->name));
2243 texture->basealpha = 1.0f;
2244 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2246 // allow disabling of picmip or compression by defaulttexflags
2248 if(!(defaulttexflags & TEXF_PICMIP))
2249 texflagsmask &= ~TEXF_PICMIP;
2250 if(!(defaulttexflags & TEXF_COMPRESS))
2251 texflagsmask &= ~TEXF_COMPRESS;
2253 if(defaulttexflags & TEXF_ISWORLD)
2254 texflagsor |= TEXF_ISWORLD;
2255 if(defaulttexflags & TEXF_ISSPRITE)
2256 texflagsor |= TEXF_ISSPRITE;
2257 // unless later loaded from the shader
2258 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2259 texture->offsetscale = 1;
2260 texture->offsetbias = 0;
2261 texture->specularscalemod = 1;
2262 texture->specularpowermod = 1;
2263 texture->rtlightambient = 0;
2264 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2265 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2266 // JUST GREP FOR "specularscalemod = 1".
2270 if (developer_loading.integer)
2271 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2273 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2275 texture->basematerialflags = MATERIALFLAG_SKY;
2276 if (shader->skyboxname[0] && loadmodel)
2278 // quake3 seems to append a _ to the skybox name, so this must do so as well
2279 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2282 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2283 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2285 texture->basematerialflags = MATERIALFLAG_WALL;
2287 if (shader->layers[0].alphatest)
2288 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2289 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2290 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2291 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2293 texture->biaspolygonoffset += shader->biaspolygonoffset;
2294 texture->biaspolygonfactor += shader->biaspolygonfactor;
2296 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2297 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2298 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2299 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2300 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2301 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2302 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2303 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2304 texture->customblendfunc[0] = GL_ONE;
2305 texture->customblendfunc[1] = GL_ZERO;
2306 texture->transparentsort = shader->transparentsort;
2307 if (shader->numlayers > 0)
2309 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2310 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2312 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2313 * additive GL_ONE GL_ONE
2314 additive weird GL_ONE GL_SRC_ALPHA
2315 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2316 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2317 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2318 brighten GL_DST_COLOR GL_ONE
2319 brighten GL_ONE GL_SRC_COLOR
2320 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2321 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2322 * modulate GL_DST_COLOR GL_ZERO
2323 * modulate GL_ZERO GL_SRC_COLOR
2324 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2325 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2326 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2327 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2328 * no blend GL_ONE GL_ZERO
2329 nothing GL_ZERO GL_ONE
2331 // if not opaque, figure out what blendfunc to use
2332 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2334 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2335 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2336 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2337 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2338 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2339 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2341 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2344 if (!shader->lighting)
2345 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2347 // here be dragons: convert quake3 shaders to material
2348 if (shader->numlayers > 0)
2351 int terrainbackgroundlayer = -1;
2352 int lightmaplayer = -1;
2353 int alphagenspecularlayer = -1;
2354 int rgbgenvertexlayer = -1;
2355 int rgbgendiffuselayer = -1;
2356 int materiallayer = -1;
2357 int endofprelayers = 0;
2358 int firstpostlayer = 0;
2359 int shaderpassindex = 0;
2360 for (i = 0; i < shader->numlayers; i++)
2362 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2364 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2365 rgbgenvertexlayer = i;
2366 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2367 rgbgendiffuselayer = i;
2368 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2369 alphagenspecularlayer = i;
2371 if (shader->numlayers >= 2
2372 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2373 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2374 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2375 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2377 // terrain blend or certain other effects involving alphatest over a regular layer
2378 terrainbackgroundlayer = 0;
2380 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2381 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2383 else if (lightmaplayer == 0)
2385 // ordinary texture but with $lightmap before diffuse
2387 firstpostlayer = lightmaplayer + 2;
2389 else if (lightmaplayer >= 1)
2391 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2392 endofprelayers = lightmaplayer - 1;
2393 materiallayer = lightmaplayer - 1;
2394 firstpostlayer = lightmaplayer + 1;
2396 else if (rgbgenvertexlayer >= 0)
2398 // map models with baked lighting
2399 materiallayer = rgbgenvertexlayer;
2400 endofprelayers = rgbgenvertexlayer;
2401 firstpostlayer = rgbgenvertexlayer + 1;
2402 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2403 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2404 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2406 else if (rgbgendiffuselayer >= 0)
2408 // entity models with dynamic lighting
2409 materiallayer = rgbgendiffuselayer;
2410 endofprelayers = rgbgendiffuselayer;
2411 firstpostlayer = rgbgendiffuselayer + 1;
2412 // 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)...
2413 if (alphagenspecularlayer >= 0)
2414 firstpostlayer = alphagenspecularlayer + 1;
2418 // special effects shaders - treat first as primary layer and do everything else as post
2423 // convert the main material layer
2424 // 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
2425 if (materiallayer >= 0)
2426 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2427 // convert the terrain background blend layer (if any)
2428 if (terrainbackgroundlayer >= 0)
2429 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2430 // convert the prepass layers (if any)
2431 texture->startpreshaderpass = shaderpassindex;
2432 for (i = 0; i < endofprelayers; i++)
2433 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2434 texture->endpreshaderpass = shaderpassindex;
2435 texture->startpostshaderpass = shaderpassindex;
2436 // convert the postpass layers (if any)
2437 for (i = firstpostlayer; i < shader->numlayers; i++)
2438 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2439 texture->startpostshaderpass = shaderpassindex;
2442 if (shader->dpshadow)
2443 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2444 if (shader->dpnoshadow)
2445 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2446 if (shader->dpnortlight)
2447 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2448 if (shader->vertexalpha)
2449 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2450 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2451 texture->reflectmin = shader->reflectmin;
2452 texture->reflectmax = shader->reflectmax;
2453 texture->refractfactor = shader->refractfactor;
2454 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2455 texture->reflectfactor = shader->reflectfactor;
2456 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2457 texture->r_water_wateralpha = shader->r_water_wateralpha;
2458 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2459 texture->offsetmapping = shader->offsetmapping;
2460 texture->offsetscale = shader->offsetscale;
2461 texture->offsetbias = shader->offsetbias;
2462 texture->specularscalemod = shader->specularscalemod;
2463 texture->specularpowermod = shader->specularpowermod;
2464 texture->rtlightambient = shader->rtlightambient;
2465 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2466 if (shader->dpreflectcube[0])
2467 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2469 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2470 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2471 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2472 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2473 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2474 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2475 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2476 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2477 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2479 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2480 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2481 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2482 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2483 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2484 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2485 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2486 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2487 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2488 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2489 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2490 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2491 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2492 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2493 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2494 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2495 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2496 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2497 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2498 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2499 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2500 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2501 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2502 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2503 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2504 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2505 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2506 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2507 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2508 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2509 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2510 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2512 texture->surfaceflags = shader->surfaceflags;
2513 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2514 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2515 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2516 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2520 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2521 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2522 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2523 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2524 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2525 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2526 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2527 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2528 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2529 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2530 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2531 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2532 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2534 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2536 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2537 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2539 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2540 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2541 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2542 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2543 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2544 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2546 if (shader->dpmeshcollisions)
2547 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2548 if (shader->dpshaderkill && developer_extra.integer)
2549 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2551 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2553 if (developer_extra.integer)
2554 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2555 texture->basematerialflags = defaultmaterialflags;
2556 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2558 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2560 if (developer_extra.integer)
2561 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2562 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2563 texture->supercontents = SUPERCONTENTS_SOLID;
2567 if (developer_extra.integer)
2568 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2569 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2571 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2572 texture->supercontents = SUPERCONTENTS_SOLID;
2574 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2576 texture->basematerialflags = MATERIALFLAG_SKY;
2577 texture->supercontents = SUPERCONTENTS_SKY;
2581 texture->basematerialflags = defaultmaterialflags;
2582 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2584 if(cls.state == ca_dedicated)
2586 texture->materialshaderpass = NULL;
2591 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2594 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2595 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2596 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2597 if (texture->q2contents)
2598 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2602 if (!success && warnmissing)
2603 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2606 // init the animation variables
2607 texture->currentframe = texture;
2608 texture->currentmaterialflags = texture->basematerialflags;
2609 if (!texture->materialshaderpass)
2610 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2611 if (!texture->materialshaderpass->skinframes[0])
2612 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2613 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2614 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2618 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2620 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2621 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2623 strlcpy(texture->name, name, sizeof(texture->name));
2624 texture->basealpha = 1.0f;
2625 texture->basematerialflags = materialflags;
2626 texture->supercontents = supercontents;
2628 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2629 texture->offsetscale = 1;
2630 texture->offsetbias = 0;
2631 texture->specularscalemod = 1;
2632 texture->specularpowermod = 1;
2633 texture->rtlightambient = 0;
2634 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2635 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2636 // JUST GREP FOR "specularscalemod = 1".
2638 if (developer_extra.integer)
2639 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2641 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2643 // init the animation variables
2644 texture->currentmaterialflags = texture->basematerialflags;
2645 texture->currentframe = texture;
2646 texture->currentskinframe = skinframe;
2647 texture->backgroundcurrentskinframe = NULL;
2650 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2652 long unsigned int i, j;
2653 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2655 if (texture->shaderpasses[i])
2658 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2659 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2660 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2661 Mem_Free(texture->shaderpasses[i]);
2662 texture->shaderpasses[i] = NULL;
2665 texture->materialshaderpass = NULL;
2666 texture->currentskinframe = NULL;
2667 texture->backgroundcurrentskinframe = NULL;
2670 skinfile_t *Mod_LoadSkinFiles(void)
2672 int i, words, line, wordsoverflow;
2675 skinfile_t *skinfile = NULL, *first = NULL;
2676 skinfileitem_t *skinfileitem;
2677 char word[10][MAX_QPATH];
2682 U_bodyBox,models/players/Legoman/BikerA2.tga
2683 U_RArm,models/players/Legoman/BikerA1.tga
2684 U_LArm,models/players/Legoman/BikerA1.tga
2685 U_armor,common/nodraw
2686 U_sword,common/nodraw
2687 U_shield,common/nodraw
2688 U_homb,common/nodraw
2689 U_backpack,common/nodraw
2690 U_colcha,common/nodraw
2695 memset(word, 0, sizeof(word));
2696 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2698 // If it's the first file we parse
2699 if (skinfile == NULL)
2701 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2706 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2707 skinfile = skinfile->next;
2709 skinfile->next = NULL;
2711 for(line = 0;;line++)
2714 if (!COM_ParseToken_QuakeC(&data, true))
2716 if (!strcmp(com_token, "\n"))
2719 wordsoverflow = false;
2723 strlcpy(word[words++], com_token, sizeof (word[0]));
2725 wordsoverflow = true;
2727 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2730 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);
2733 // words is always >= 1
2734 if (!strcmp(word[0], "replace"))
2738 if (developer_loading.integer)
2739 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2740 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2741 skinfileitem->next = skinfile->items;
2742 skinfile->items = skinfileitem;
2743 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2744 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2747 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]);
2749 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2751 // tag name, like "tag_weapon,"
2752 // not used for anything (not even in Quake3)
2754 else if (words >= 2 && !strcmp(word[1], ","))
2756 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2757 if (developer_loading.integer)
2758 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2759 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2760 skinfileitem->next = skinfile->items;
2761 skinfile->items = skinfileitem;
2762 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2763 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2766 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);
2771 loadmodel->numskins = i;
2775 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2778 skinfileitem_t *skinfileitem, *nextitem;
2779 for (;skinfile;skinfile = next)
2781 next = skinfile->next;
2782 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2784 nextitem = skinfileitem->next;
2785 Mem_Free(skinfileitem);
2791 int Mod_CountSkinFiles(skinfile_t *skinfile)
2794 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2798 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2801 double isnap = 1.0 / snap;
2802 for (i = 0;i < numvertices*numcomponents;i++)
2803 vertices[i] = floor(vertices[i]*isnap)*snap;
2806 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2808 int i, outtriangles;
2809 float edgedir1[3], edgedir2[3], temp[3];
2810 // a degenerate triangle is one with no width (thickness, surface area)
2811 // these are characterized by having all 3 points colinear (along a line)
2812 // or having two points identical
2813 // the simplest check is to calculate the triangle's area
2814 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2816 // calculate first edge
2817 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2818 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2819 CrossProduct(edgedir1, edgedir2, temp);
2820 if (VectorLength2(temp) < 0.001f)
2821 continue; // degenerate triangle (no area)
2822 // valid triangle (has area)
2823 VectorCopy(inelement3i, outelement3i);
2827 return outtriangles;
2830 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2833 int firstvertex, lastvertex;
2834 if (numelements > 0 && elements)
2836 firstvertex = lastvertex = elements[0];
2837 for (i = 1;i < numelements;i++)
2840 firstvertex = min(firstvertex, e);
2841 lastvertex = max(lastvertex, e);
2845 firstvertex = lastvertex = 0;
2846 if (firstvertexpointer)
2847 *firstvertexpointer = firstvertex;
2848 if (lastvertexpointer)
2849 *lastvertexpointer = lastvertex;
2852 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2854 // make an optimal set of texture-sorted batches to draw...
2856 int *firstsurfacefortexture;
2857 int *numsurfacesfortexture;
2858 if (!mod->sortedmodelsurfaces)
2859 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2860 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2861 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2862 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2863 for (j = 0;j < mod->nummodelsurfaces;j++)
2865 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2866 if(!surface->texture)
2868 t = (int)(surface->texture - mod->data_textures);
2869 numsurfacesfortexture[t]++;
2872 for (t = 0;t < mod->num_textures;t++)
2874 firstsurfacefortexture[t] = j;
2875 j += numsurfacesfortexture[t];
2877 for (j = 0;j < mod->nummodelsurfaces;j++)
2879 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2880 if (!surface->texture)
2882 t = (int)(surface->texture - mod->data_textures);
2883 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2885 Mem_Free(firstsurfacefortexture);
2886 Mem_Free(numsurfacesfortexture);
2889 void Mod_BuildVBOs(void)
2891 if(cls.state == ca_dedicated)
2894 if (!loadmodel->surfmesh.num_vertices)
2897 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2900 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2902 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2904 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2905 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2910 // upload short indices as a buffer
2911 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2912 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);
2914 // upload int indices as a buffer
2915 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2916 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);
2918 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2919 // we put several vertex data streams in the same buffer
2920 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2925 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2926 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2927 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2928 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2929 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2930 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2931 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2932 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2933 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2934 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2935 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2936 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2937 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2938 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2939 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2940 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2941 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2942 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2943 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2944 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2945 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2946 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2947 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2948 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2949 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2950 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2951 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2952 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2957 extern cvar_t mod_obj_orientation;
2958 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2960 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2962 const char *texname;
2964 const float *v, *vn, *vt;
2966 size_t outbufferpos = 0;
2967 size_t outbuffermax = 0x100000;
2968 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
2969 const msurface_t *surface;
2970 const int maxtextures = 256;
2971 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
2972 dp_model_t *submodel;
2974 // construct the mtllib file
2975 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
2978 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
2981 countvertices += surface->num_vertices;
2982 countfaces += surface->num_triangles;
2983 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
2984 for (textureindex = 0;textureindex < counttextures;textureindex++)
2985 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
2987 if (textureindex < counttextures)
2988 continue; // already wrote this material entry
2989 if (textureindex >= maxtextures)
2990 continue; // just a precaution
2991 textureindex = counttextures++;
2992 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
2993 if (outbufferpos >= outbuffermax >> 1)
2996 oldbuffer = outbuffer;
2997 outbuffer = (char *) Z_Malloc(outbuffermax);
2998 memcpy(outbuffer, oldbuffer, outbufferpos);
3001 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");
3006 // write the mtllib file
3007 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3009 // construct the obj file
3011 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);
3015 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)
3017 if (outbufferpos >= outbuffermax >> 1)
3020 oldbuffer = outbuffer;
3021 outbuffer = (char *) Z_Malloc(outbuffermax);
3022 memcpy(outbuffer, oldbuffer, outbufferpos);
3025 if(mod_obj_orientation.integer)
3026 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]);
3028 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]);
3033 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3035 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3038 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3039 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3041 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3042 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3045 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3047 if (outbufferpos >= outbuffermax >> 1)
3050 oldbuffer = outbuffer;
3051 outbuffer = (char *) Z_Malloc(outbuffermax);
3052 memcpy(outbuffer, oldbuffer, outbufferpos);
3058 if(mod_obj_orientation.integer)
3059 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);
3061 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);
3068 // write the obj file
3069 FS_WriteFile(filename, outbuffer, outbufferpos);
3073 Z_Free(texturenames);
3076 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3079 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3081 int countnodes = 0, counttriangles = 0, countframes = 0;
3089 size_t outbufferpos = 0;
3090 size_t outbuffermax = 0x100000;
3091 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3092 const msurface_t *surface;
3093 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3096 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3098 if (outbufferpos >= outbuffermax >> 1)
3101 oldbuffer = outbuffer;
3102 outbuffer = (char *) Z_Malloc(outbuffermax);
3103 memcpy(outbuffer, oldbuffer, outbufferpos);
3107 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3111 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3114 for (poseindex = 0;poseindex < numposes;poseindex++)
3117 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3120 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3124 matrix4x4_t posematrix;
3125 if (outbufferpos >= outbuffermax >> 1)
3128 oldbuffer = outbuffer;
3129 outbuffer = (char *) Z_Malloc(outbuffermax);
3130 memcpy(outbuffer, oldbuffer, outbufferpos);
3134 // strangely the smd angles are for a transposed matrix, so we
3135 // have to generate a transposed matrix, then convert that...
3136 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3137 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3138 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3139 if (angles[0] >= 180) angles[0] -= 360;
3140 if (angles[1] >= 180) angles[1] -= 360;
3141 if (angles[2] >= 180) angles[2] -= 360;
3145 float a = DEG2RAD(angles[ROLL]);
3146 float b = DEG2RAD(angles[PITCH]);
3147 float c = DEG2RAD(angles[YAW]);
3148 float cy, sy, cp, sp, cr, sr;
3150 // smd matrix construction, for comparing
3161 test[1][0] = sr*sp*cy+cr*-sy;
3162 test[1][1] = sr*sp*sy+cr*cy;
3164 test[2][0] = (cr*sp*cy+-sr*-sy);
3165 test[2][1] = (cr*sp*sy+-sr*cy);
3167 test[3][0] = pose[9];
3168 test[3][1] = pose[10];
3169 test[3][2] = pose[11];
3172 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]));
3177 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3182 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3185 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3187 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3190 if (outbufferpos >= outbuffermax >> 1)
3193 oldbuffer = outbuffer;
3194 outbuffer = (char *) Z_Malloc(outbuffermax);
3195 memcpy(outbuffer, oldbuffer, outbufferpos);
3198 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3201 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3203 const int index = e[2-cornerindex];
3204 const float *v = model->surfmesh.data_vertex3f + index * 3;
3205 const float *vn = model->surfmesh.data_normal3f + index * 3;
3206 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3207 const int b = model->surfmesh.blends[index];
3208 if (b < model->num_bones)
3209 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]);
3212 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3213 const unsigned char *wi = w->index;
3214 const unsigned char *wf = w->influence;
3215 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);
3216 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);
3217 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);
3218 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]);
3225 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3230 FS_WriteFile(filename, outbuffer, outbufferpos);
3233 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3240 decompiles a model to editable files
3243 static void Mod_Decompile_f(cmd_state_t *cmd)
3245 int i, j, k, l, first, count;
3247 char inname[MAX_QPATH];
3248 char outname[MAX_QPATH];
3249 char mtlname[MAX_QPATH];
3250 char basename[MAX_QPATH];
3251 char animname[MAX_QPATH];
3252 char animname2[MAX_QPATH];
3253 char zymtextbuffer[16384];
3254 char dpmtextbuffer[16384];
3255 char framegroupstextbuffer[16384];
3256 int zymtextsize = 0;
3257 int dpmtextsize = 0;
3258 int framegroupstextsize = 0;
3261 if (Cmd_Argc(cmd) != 2)
3263 Con_Print("usage: modeldecompile <filename>\n");
3267 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3268 FS_StripExtension(inname, basename, sizeof(basename));
3270 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3273 Con_Print("No such model\n");
3276 if (mod->brush.submodel)
3278 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3279 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3280 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3283 if (!mod->surfmesh.num_triangles)
3285 Con_Print("Empty model (or sprite)\n");
3289 // export OBJ if possible (not on sprites)
3290 if (mod->surfmesh.num_triangles)
3292 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3293 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3294 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3297 // export SMD if possible (only for skeletal models)
3298 if (mod->surfmesh.num_triangles && mod->num_bones)
3300 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3301 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3302 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3303 if (l > 0) zymtextsize += l;
3304 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3305 if (l > 0) dpmtextsize += l;
3306 for (i = 0;i < mod->numframes;i = j)
3308 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3309 first = mod->animscenes[i].firstframe;
3310 if (mod->animscenes[i].framecount > 1)
3313 count = mod->animscenes[i].framecount;
3319 // check for additional frames with same name
3320 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3321 if(animname[l] < '0' || animname[l] > '9')
3323 if(k > 0 && animname[k-1] == '_')
3326 count = mod->num_poses - first;
3327 for (j = i + 1;j < mod->numframes;j++)
3329 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3330 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3331 if(animname2[l] < '0' || animname2[l] > '9')
3333 if(k > 0 && animname[k-1] == '_')
3336 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3338 count = mod->animscenes[j].firstframe - first;
3342 // if it's only one frame, use the original frame name
3344 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3347 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3348 Mod_Decompile_SMD(mod, outname, first, count, false);
3349 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3351 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3352 if (l > 0) zymtextsize += l;
3354 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3356 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3357 if (l > 0) dpmtextsize += l;
3359 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3361 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3362 if (l > 0) framegroupstextsize += l;
3366 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3368 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3369 if (framegroupstextsize)
3370 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3374 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3377 memset(state, 0, sizeof(*state));
3378 state->width = width;
3379 state->height = height;
3380 state->currentY = 0;
3381 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3382 for (y = 0;y < state->height;y++)
3384 state->rows[y].currentX = 0;
3385 state->rows[y].rowY = -1;
3389 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3392 state->currentY = 0;
3393 for (y = 0;y < state->height;y++)
3395 state->rows[y].currentX = 0;
3396 state->rows[y].rowY = -1;
3400 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3403 Mem_Free(state->rows);
3404 memset(state, 0, sizeof(*state));
3407 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3409 mod_alloclightmap_row_t *row;
3412 row = state->rows + blockheight;
3413 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3415 if (state->currentY + blockheight <= state->height)
3417 // use the current allocation position
3418 row->rowY = state->currentY;
3420 state->currentY += blockheight;
3424 // find another position
3425 for (y = blockheight;y < state->height;y++)
3427 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3429 row = state->rows + y;
3433 if (y == state->height)
3438 *outx = row->currentX;
3439 row->currentX += blockwidth;
3444 typedef struct lightmapsample_s
3448 float *vertex_color;
3449 unsigned char *lm_bgr;
3450 unsigned char *lm_dir;
3454 typedef struct lightmapvertex_s
3459 float texcoordbase[2];
3460 float texcoordlightmap[2];
3461 float lightcolor[4];
3465 typedef struct lightmaptriangle_s
3473 // 2D modelspace coordinates of min corner
3474 // snapped to lightmap grid but not in grid coordinates
3476 // 2D modelspace to lightmap coordinate scale
3484 typedef struct lightmaplight_s
3495 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3497 #define MAX_LIGHTMAPSAMPLES 64
3498 static int mod_generatelightmaps_numoffsets[3];
3499 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3501 static int mod_generatelightmaps_numlights;
3502 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3504 extern cvar_t r_shadow_lightattenuationdividebias;
3505 extern cvar_t r_shadow_lightattenuationlinearscale;
3507 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3512 float relativepoint[3];
3519 float lightorigin[3];
3523 float lightcolor[3];
3525 for (i = 0;i < 5*3;i++)
3527 for (index = 0;;index++)
3529 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3534 lightradius2 = lightradius * lightradius;
3535 VectorSubtract(lightorigin, pos, relativepoint);
3536 dist2 = VectorLength2(relativepoint);
3537 if (dist2 >= lightradius2)
3539 lightiradius = 1.0f / lightradius;
3540 dist = sqrt(dist2) * lightiradius;
3541 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3542 if (intensity <= 0.0f)
3544 if (model && model->TraceLine)
3546 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3547 if (trace.fraction < 1)
3550 // scale down intensity to add to both ambient and diffuse
3551 //intensity *= 0.5f;
3552 VectorNormalize(relativepoint);
3553 VectorScale(lightcolor, intensity, color);
3554 VectorMA(sample , 0.5f , color, sample );
3555 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3556 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3557 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3558 // calculate a weighted average light direction as well
3559 intensity *= VectorLength(color);
3560 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3562 // calculate the direction we'll use to reduce the sample to a directional light source
3563 VectorCopy(sample + 12, dir);
3564 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3565 VectorNormalize(dir);
3566 // extract the diffuse color along the chosen direction and scale it
3567 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3568 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3569 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3570 // subtract some of diffuse from ambient
3571 VectorMA(sample, -0.333f, diffuse, ambient);
3572 // store the normalized lightdir
3573 VectorCopy(dir, lightdir);
3576 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3580 const msurface_t *surface;
3581 const float *vertex3f = model->surfmesh.data_vertex3f;
3582 const int *element3i = model->surfmesh.data_element3i;
3585 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3587 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3589 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3591 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3593 VectorCopy(vertex3f + 3*e[0], v2[0]);
3594 VectorCopy(vertex3f + 3*e[1], v2[1]);
3595 VectorCopy(vertex3f + 3*e[2], v2[2]);
3596 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3601 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3603 int maxnodes = 1<<14;
3604 svbsp_node_t *nodes;
3609 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3610 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3611 VectorCopy(lightinfo->origin, origin);
3612 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3615 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3616 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3617 if (svbsp.ranoutofnodes)
3620 if (maxnodes > 1<<22)
3626 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3631 if (svbsp.numnodes > 0)
3633 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3634 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3635 lightinfo->svbsp = svbsp;
3640 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3644 lightmaplight_t *lightinfo;
3648 mod_generatelightmaps_numlights = 0;
3649 for (index = 0;;index++)
3651 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3655 mod_generatelightmaps_numlights++;
3657 if (mod_generatelightmaps_numlights > 0)
3659 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3660 lightinfo = mod_generatelightmaps_lightinfo;
3661 for (index = 0;;index++)
3663 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3670 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3672 lightinfo->iradius = 1.0f / lightinfo->radius;
3673 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3674 // TODO: compute svbsp
3675 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3679 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3682 if (mod_generatelightmaps_lightinfo)
3684 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3685 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3686 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3687 Mem_Free(mod_generatelightmaps_lightinfo);
3689 mod_generatelightmaps_lightinfo = NULL;
3690 mod_generatelightmaps_numlights = 0;
3693 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3695 const svbsp_node_t *node;
3696 const svbsp_node_t *nodes = svbsp->nodes;
3701 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3703 return num == -1; // true if empty, false if solid (shadowed)
3706 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3709 float relativepoint[3];
3718 const lightmaplight_t *lightinfo;
3720 for (i = 0;i < 5*3;i++)
3722 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3724 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3725 VectorSubtract(lightinfo->origin, pos, relativepoint);
3726 // don't accept light from behind a surface, it causes bad shading
3727 if (normal && DotProduct(relativepoint, normal) <= 0)
3729 dist2 = VectorLength2(relativepoint);
3730 if (dist2 >= lightinfo->radius2)
3732 dist = sqrt(dist2) * lightinfo->iradius;
3733 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3736 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3740 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3742 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3744 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3747 // for light grid we'd better check visibility of the offset point
3748 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3749 if (trace.fraction < 1)
3750 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3753 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3758 // scale intensity according to how many rays succeeded
3759 // we know one test is valid, half of the rest will fail...
3760 //if (normal && tests > 1)
3761 // intensity *= (tests - 1.0f) / tests;
3762 intensity *= (float)hits / tests;
3764 // scale down intensity to add to both ambient and diffuse
3765 //intensity *= 0.5f;
3766 VectorNormalize(relativepoint);
3767 VectorScale(lightinfo->color, intensity, color);
3768 VectorMA(sample , 0.5f , color, sample );
3769 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3770 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3771 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3772 // calculate a weighted average light direction as well
3773 intensity *= VectorLength(color);
3774 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3778 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3784 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3785 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3786 VectorCopy(sample + 12, dir);
3787 VectorNormalize(dir);
3788 //VectorAdd(dir, normal, dir);
3789 //VectorNormalize(dir);
3790 f = DotProduct(dir, normal);
3791 f = max(0, f) * 255.0f;
3792 VectorScale(sample, f, color);
3793 //VectorCopy(normal, dir);
3794 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3795 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3796 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3797 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3799 lm_dir[0] = (unsigned char)dir[2];
3800 lm_dir[1] = (unsigned char)dir[1];
3801 lm_dir[2] = (unsigned char)dir[0];
3805 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3808 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3809 VectorCopy(sample, vertex_color);
3812 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3818 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3819 // calculate the direction we'll use to reduce the sample to a directional light source
3820 VectorCopy(sample + 12, dir);
3821 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3822 VectorNormalize(dir);
3823 // extract the diffuse color along the chosen direction and scale it
3824 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3825 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3826 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3827 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3828 VectorScale(sample, 127.5f, ambient);
3829 VectorMA(ambient, -0.333f, diffuse, ambient);
3830 // encode to the grid format
3831 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3832 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3833 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3834 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3835 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3836 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3837 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3838 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3839 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));}
3842 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3847 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3848 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3849 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3850 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3851 radius[0] = mod_generatelightmaps_lightmapradius.value;
3852 radius[1] = mod_generatelightmaps_vertexradius.value;
3853 radius[2] = mod_generatelightmaps_gridradius.value;
3854 for (i = 0;i < 3;i++)
3856 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3859 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3864 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3866 msurface_t *surface;
3869 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3871 surface = model->data_surfaces + surfaceindex;
3872 surface->lightmaptexture = NULL;
3873 surface->deluxemaptexture = NULL;
3875 if (model->brushq3.data_lightmaps)
3877 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3878 if (model->brushq3.data_lightmaps[i])
3879 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3880 Mem_Free(model->brushq3.data_lightmaps);
3881 model->brushq3.data_lightmaps = NULL;
3883 if (model->brushq3.data_deluxemaps)
3885 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3886 if (model->brushq3.data_deluxemaps[i])
3887 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3888 Mem_Free(model->brushq3.data_deluxemaps);
3889 model->brushq3.data_deluxemaps = NULL;
3893 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3895 msurface_t *surface;
3901 surfmesh_t oldsurfmesh;
3903 unsigned char *data;
3904 oldsurfmesh = model->surfmesh;
3905 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3906 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3908 size += model->surfmesh.num_vertices * sizeof(float[3]);
3909 size += model->surfmesh.num_vertices * sizeof(float[3]);
3910 size += model->surfmesh.num_vertices * sizeof(float[3]);
3911 size += model->surfmesh.num_vertices * sizeof(float[3]);
3912 size += model->surfmesh.num_vertices * sizeof(float[2]);
3913 size += model->surfmesh.num_vertices * sizeof(float[2]);
3914 size += model->surfmesh.num_vertices * sizeof(float[4]);
3915 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3916 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3917 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3918 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3919 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3920 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3921 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3922 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3923 if (model->surfmesh.num_vertices > 65536)
3924 model->surfmesh.data_element3s = NULL;
3926 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3927 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3928 model->surfmesh.data_element3i_indexbuffer = NULL;
3929 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3930 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3931 model->surfmesh.data_element3s_indexbuffer = NULL;
3932 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3933 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3934 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3935 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3936 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3937 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3938 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3939 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3940 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3941 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3942 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
3944 // convert all triangles to unique vertex data
3946 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3948 surface = model->data_surfaces + surfaceindex;
3949 surface->num_firstvertex = outvertexindex;
3950 surface->num_vertices = surface->num_triangles*3;
3951 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3952 for (i = 0;i < surface->num_triangles*3;i++)
3955 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3956 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3957 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3958 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3959 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3960 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3961 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3962 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3963 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3964 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3965 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3966 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3967 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
3968 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
3969 if (oldsurfmesh.data_texcoordlightmap2f)
3971 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
3972 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
3974 if (oldsurfmesh.data_lightmapcolor4f)
3976 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
3977 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
3978 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
3979 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
3982 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
3983 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
3987 if (model->surfmesh.data_element3s)
3988 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
3989 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
3991 // find and update all submodels to use this new surfmesh data
3992 for (i = 0;i < model->brush.numsubmodels;i++)
3993 model->brush.submodels[i]->surfmesh = model->surfmesh;
3996 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
3998 msurface_t *surface;
4004 lightmaptriangle_t *triangle;
4005 // generate lightmap triangle structs
4006 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4007 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4009 surface = model->data_surfaces + surfaceindex;
4010 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4011 for (i = 0;i < surface->num_triangles;i++)
4013 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4014 triangle->triangleindex = surface->num_firsttriangle+i;
4015 triangle->surfaceindex = surfaceindex;
4016 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4017 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4018 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4019 // calculate bounds of triangle
4020 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4021 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4022 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4023 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4024 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4025 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4026 // pick an axial projection based on the triangle normal
4027 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4029 if (fabs(normal[1]) > fabs(normal[axis]))
4031 if (fabs(normal[2]) > fabs(normal[axis]))
4033 triangle->axis = axis;
4038 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4040 if (mod_generatelightmaps_lightmaptriangles)
4041 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4042 mod_generatelightmaps_lightmaptriangles = NULL;
4045 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4047 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4049 msurface_t *surface;
4063 float trianglenormal[3];
4064 float samplecenter[3];
4065 float samplenormal[3];
4071 float lmscalepixels;
4074 float lm_basescalepixels;
4075 int lm_borderpixels;
4079 lightmaptriangle_t *triangle;
4080 unsigned char *lightmappixels;
4081 unsigned char *deluxemappixels;
4082 mod_alloclightmap_state_t lmstate;
4085 // generate lightmap projection information for all triangles
4086 if (model->texturepool == NULL)
4087 model->texturepool = R_AllocTexturePool();
4088 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4089 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4090 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4091 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4092 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4094 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4096 surface = model->data_surfaces + surfaceindex;
4097 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4098 lmscalepixels = lm_basescalepixels;
4099 for (retry = 0;retry < 30;retry++)
4101 // after a couple failed attempts, degrade quality to make it fit
4103 lmscalepixels *= 0.5f;
4104 for (i = 0;i < surface->num_triangles;i++)
4106 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4107 triangle->lightmapindex = lightmapnumber;
4108 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4109 // pick two planar axes for projection
4110 // lightmap coordinates here are in pixels
4111 // lightmap projections are snapped to pixel grid explicitly, such
4112 // that two neighboring triangles sharing an edge and projection
4113 // axis will have identical sample spacing along their shared edge
4115 for (j = 0;j < 3;j++)
4117 if (j == triangle->axis)
4119 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4120 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4121 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4122 triangle->lmbase[k] = lmmins/lmscalepixels;
4123 triangle->lmscale[k] = lmscalepixels;
4126 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4129 // if all fit in this texture, we're done with this surface
4130 if (i == surface->num_triangles)
4132 // if we haven't maxed out the lightmap size yet, we retry the
4133 // entire surface batch...
4134 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4136 lm_texturesize *= 2;
4139 Mod_AllocLightmap_Free(&lmstate);
4140 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4143 // if we have maxed out the lightmap size, and this triangle does
4144 // not fit in the same texture as the rest of the surface, we have
4145 // to retry the entire surface in a new texture (can only use one)
4146 // with multiple retries, the lightmap quality degrades until it
4147 // fits (or gives up)
4148 if (surfaceindex > 0)
4150 Mod_AllocLightmap_Reset(&lmstate);
4154 Mod_AllocLightmap_Free(&lmstate);
4156 // now put triangles together into lightmap textures, and do not allow
4157 // triangles of a surface to go into different textures (as that would
4158 // require rewriting the surface list)
4159 model->brushq3.deluxemapping_modelspace = true;
4160 model->brushq3.deluxemapping = true;
4161 model->brushq3.num_mergedlightmaps = lightmapnumber;
4162 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4163 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4164 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4165 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4166 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4168 surface = model->data_surfaces + surfaceindex;
4169 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4170 for (i = 0;i < surface->num_triangles;i++)
4172 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4173 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4174 VectorNormalize(trianglenormal);
4175 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4176 axis = triangle->axis;
4177 axis1 = axis == 0 ? 1 : 0;
4178 axis2 = axis == 2 ? 1 : 2;
4179 lmiscale[0] = 1.0f / triangle->lmscale[0];
4180 lmiscale[1] = 1.0f / triangle->lmscale[1];
4181 if (trianglenormal[axis] < 0)
4182 VectorNegate(trianglenormal, trianglenormal);
4183 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4184 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4185 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4186 for (j = 0;j < 3;j++)
4188 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4189 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4190 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4192 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4193 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4194 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4195 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]);
4205 forward[1] = 1.0f / triangle->lmscale[0];
4209 left[2] = 1.0f / triangle->lmscale[1];
4214 origin[1] = triangle->lmbase[0];
4215 origin[2] = triangle->lmbase[1];
4218 forward[0] = 1.0f / triangle->lmscale[0];
4223 left[2] = 1.0f / triangle->lmscale[1];
4227 origin[0] = triangle->lmbase[0];
4229 origin[2] = triangle->lmbase[1];
4232 forward[0] = 1.0f / triangle->lmscale[0];
4236 left[1] = 1.0f / triangle->lmscale[1];
4241 origin[0] = triangle->lmbase[0];
4242 origin[1] = triangle->lmbase[1];
4246 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4248 #define LM_DIST_EPSILON (1.0f / 32.0f)
4249 for (y = 0;y < triangle->lmsize[1];y++)
4251 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4252 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4254 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4255 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4256 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4257 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4258 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4264 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4266 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);
4267 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);
4271 Mem_Free(lightmappixels);
4272 if (deluxemappixels)
4273 Mem_Free(deluxemappixels);
4275 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4277 surface = model->data_surfaces + surfaceindex;
4278 if (!surface->num_triangles)
4280 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4281 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4282 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4283 surface->lightmapinfo = NULL;
4286 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4287 model->brushq1.lightdata = NULL;
4288 model->brushq1.lightmapupdateflags = NULL;
4289 model->brushq1.firstrender = false;
4290 model->brushq1.num_lightstyles = 0;
4291 model->brushq1.data_lightstyleinfo = NULL;
4292 for (i = 0;i < model->brush.numsubmodels;i++)
4294 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4295 model->brush.submodels[i]->brushq1.firstrender = false;
4296 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4297 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4301 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4304 for (i = 0;i < model->surfmesh.num_vertices;i++)
4305 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4308 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4315 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4317 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4318 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4320 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4321 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4323 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4324 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4330 extern cvar_t mod_q3bsp_nolightmaps;
4331 static void Mod_GenerateLightmaps(dp_model_t *model)
4333 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4334 dp_model_t *oldloadmodel = loadmodel;
4337 Mod_GenerateLightmaps_InitSampleOffsets(model);
4338 Mod_GenerateLightmaps_DestroyLightmaps(model);
4339 Mod_GenerateLightmaps_UnweldTriangles(model);
4340 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4341 Mod_GenerateLightmaps_CreateLights(model);
4342 if(!mod_q3bsp_nolightmaps.integer)
4343 Mod_GenerateLightmaps_CreateLightmaps(model);
4344 Mod_GenerateLightmaps_UpdateVertexColors(model);
4345 Mod_GenerateLightmaps_UpdateLightGrid(model);
4346 Mod_GenerateLightmaps_DestroyLights(model);
4347 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4349 loadmodel = oldloadmodel;
4352 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4354 if (Cmd_Argc(cmd) != 1)
4356 Con_Printf("usage: mod_generatelightmaps\n");
4361 Con_Printf("no worldmodel loaded\n");
4364 Mod_GenerateLightmaps(cl.worldmodel);
4367 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4369 memset(mod, 0, sizeof(*mod));
4370 strlcpy(mod->name, name, sizeof(mod->name));
4371 mod->mempool = Mem_AllocPool(name, 0, NULL);
4372 mod->texturepool = R_AllocTexturePool();
4373 mod->Draw = R_Q1BSP_Draw;
4374 mod->DrawDepth = R_Q1BSP_DrawDepth;
4375 mod->DrawDebug = R_Q1BSP_DrawDebug;
4376 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4377 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4378 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4379 mod->DrawLight = R_Q1BSP_DrawLight;
4382 void Mod_Mesh_Destroy(dp_model_t *mod)
4384 Mod_UnloadModel(mod);
4387 // resets the mesh model to have no geometry to render, ready for a new frame -
4388 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4389 void Mod_Mesh_Reset(dp_model_t *mod)
4391 mod->num_surfaces = 0;
4392 mod->surfmesh.num_vertices = 0;
4393 mod->surfmesh.num_triangles = 0;
4394 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4395 mod->DrawSky = NULL; // will be set if a texture needs it
4396 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4399 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4403 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4404 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4405 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4407 if (mod->max_textures <= mod->num_textures)
4409 texture_t *oldtextures = mod->data_textures;
4410 mod->max_textures = max(mod->max_textures * 2, 1024);
4411 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4412 // update the pointers
4413 for (i = 0; i < mod->num_surfaces; i++)
4414 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4416 t = &mod->data_textures[mod->num_textures++];
4417 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4418 t->mesh_drawflag = drawflag;
4419 t->mesh_defaulttexflags = defaulttexflags;
4420 t->mesh_defaultmaterialflags = defaultmaterialflags;
4421 switch (defaultdrawflags & DRAWFLAG_MASK)
4423 case DRAWFLAG_ADDITIVE:
4424 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4425 t->currentmaterialflags = t->basematerialflags;
4427 case DRAWFLAG_MODULATE:
4428 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4429 t->currentmaterialflags = t->basematerialflags;
4430 t->customblendfunc[0] = GL_DST_COLOR;
4431 t->customblendfunc[1] = GL_ZERO;
4433 case DRAWFLAG_2XMODULATE:
4434 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4435 t->currentmaterialflags = t->basematerialflags;
4436 t->customblendfunc[0] = GL_DST_COLOR;
4437 t->customblendfunc[1] = GL_SRC_COLOR;
4439 case DRAWFLAG_SCREEN:
4440 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4441 t->currentmaterialflags = t->basematerialflags;
4442 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4443 t->customblendfunc[1] = GL_ONE;
4451 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4454 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4455 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4456 return mod->data_surfaces + mod->num_surfaces - 1;
4457 // create new surface
4458 if (mod->max_surfaces == mod->num_surfaces)
4460 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4461 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4462 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4464 surf = mod->data_surfaces + mod->num_surfaces;
4465 mod->num_surfaces++;
4466 memset(surf, 0, sizeof(*surf));
4467 surf->texture = tex;
4468 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4469 surf->num_firstvertex = mod->surfmesh.num_vertices;
4470 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4471 mod->DrawSky = R_Q1BSP_DrawSky;
4472 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4473 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4477 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)
4479 int hashindex, h, vnum, mask;
4480 surfmesh_t *mesh = &mod->surfmesh;
4481 if (mesh->max_vertices == mesh->num_vertices)
4483 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4484 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4485 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4486 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4487 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4488 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4489 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4490 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4491 // rebuild the hash table
4492 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4493 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4494 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4495 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4496 mask = mod->surfmesh.num_vertexhashsize - 1;
4497 // no need to hash the vertices for the entire model, the latest surface will suffice.
4498 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4500 // this uses prime numbers intentionally for computing the hash
4501 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;
4502 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4503 ; // just iterate until we find the terminator
4504 mesh->data_vertexhash[h] = vnum;
4507 mask = mod->surfmesh.num_vertexhashsize - 1;
4508 // this uses prime numbers intentionally for computing the hash
4509 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4510 // when possible find an identical vertex within the same surface and return it
4511 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4513 if (vnum >= surf->num_firstvertex
4514 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4515 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4516 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4517 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4518 && 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)
4521 // add the new vertex
4522 vnum = mesh->num_vertices++;
4523 if (surf->num_vertices > 0)
4525 if (surf->mins[0] > x) surf->mins[0] = x;
4526 if (surf->mins[1] > y) surf->mins[1] = y;
4527 if (surf->mins[2] > z) surf->mins[2] = z;
4528 if (surf->maxs[0] < x) surf->maxs[0] = x;
4529 if (surf->maxs[1] < y) surf->maxs[1] = y;
4530 if (surf->maxs[2] < z) surf->maxs[2] = z;
4534 VectorSet(surf->mins, x, y, z);
4535 VectorSet(surf->maxs, x, y, z);
4537 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4538 mesh->data_vertexhash[h] = vnum;
4539 mesh->data_vertex3f[vnum * 3 + 0] = x;
4540 mesh->data_vertex3f[vnum * 3 + 1] = y;
4541 mesh->data_vertex3f[vnum * 3 + 2] = z;
4542 mesh->data_normal3f[vnum * 3 + 0] = nx;
4543 mesh->data_normal3f[vnum * 3 + 1] = ny;
4544 mesh->data_normal3f[vnum * 3 + 2] = nz;
4545 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4546 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4547 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4548 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4549 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4550 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4551 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4552 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4556 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4558 surfmesh_t *mesh = &mod->surfmesh;
4559 if (mesh->max_triangles == mesh->num_triangles)
4561 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4562 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4563 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4565 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4566 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4567 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4568 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4569 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4570 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4571 mesh->num_triangles++;
4572 surf->num_triangles++;
4575 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4579 msurface_t *surf, *surf2;
4581 // build the sorted surfaces list properly to reduce material setup
4582 // this is easy because we're just sorting on texture and don't care about the order of textures
4583 mod->nummodelsurfaces = 0;
4584 for (i = 0; i < mod->num_surfaces; i++)
4585 mod->data_surfaces[i].included = false;
4586 for (i = 0; i < mod->num_surfaces; i++)
4588 surf = mod->data_surfaces + i;
4591 tex = surf->texture;
4592 // j = i is intentional
4593 for (j = i; j < mod->num_surfaces; j++)
4595 surf2 = mod->data_surfaces + j;
4596 if (surf2->included)
4598 if (surf2->texture == tex)
4600 surf2->included = true;
4601 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4607 static void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4610 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4612 if (mod->surfmesh.num_vertices > 0)
4614 // calculate normalmins/normalmaxs
4615 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4616 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4617 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4619 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4620 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4621 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4622 // expand bounds to include this vertex
4623 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4624 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4625 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4626 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4627 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4628 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4630 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4631 // (fast but less accurate than doing it per vertex)
4632 x2a = mod->normalmins[0] * mod->normalmins[0];
4633 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4634 y2a = mod->normalmins[1] * mod->normalmins[1];
4635 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4636 z2a = mod->normalmins[2] * mod->normalmins[2];
4637 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4641 yawradius = sqrt(x2 + y2);
4642 rotatedradius = sqrt(x2 + y2 + z2);
4643 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4644 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4645 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4646 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4647 mod->radius = rotatedradius;
4648 mod->radius2 = x2 + y2 + z2;
4652 VectorClear(mod->normalmins);
4653 VectorClear(mod->normalmaxs);
4654 VectorClear(mod->yawmins);
4655 VectorClear(mod->yawmaxs);
4656 VectorClear(mod->rotatedmins);
4657 VectorClear(mod->rotatedmaxs);
4663 void Mod_Mesh_Validate(dp_model_t *mod)
4666 qboolean warned = false;
4667 for (i = 0; i < mod->num_surfaces; i++)
4669 msurface_t *surf = mod->data_surfaces + i;
4670 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4671 int first = surf->num_firstvertex;
4672 int end = surf->num_firstvertex + surf->num_vertices;
4674 for (j = 0;j < surf->num_triangles * 3;j++)
4676 if (e[j] < first || e[j] >= end)
4679 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4687 static void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
4689 mod->surfmesh.data_element3s_indexbuffer = mod->surfmesh.data_element3s ? R_BufferData_Store(mod->surfmesh.num_triangles * sizeof(short[3]), mod->surfmesh.data_element3s, R_BUFFERDATA_INDEX16, &mod->surfmesh.data_element3s_bufferoffset) : NULL;
4690 mod->surfmesh.data_element3i_indexbuffer = mod->surfmesh.data_element3i ? R_BufferData_Store(mod->surfmesh.num_triangles * sizeof(int[3]), mod->surfmesh.data_element3i, R_BUFFERDATA_INDEX32, &mod->surfmesh.data_element3i_bufferoffset) : NULL;
4691 mod->surfmesh.data_vertex3f_vertexbuffer = mod->surfmesh.data_vertex3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_vertex3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_vertex3f_bufferoffset) : NULL;
4692 mod->surfmesh.data_svector3f_vertexbuffer = mod->surfmesh.data_svector3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_svector3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_svector3f_bufferoffset) : NULL;
4693 mod->surfmesh.data_tvector3f_vertexbuffer = mod->surfmesh.data_tvector3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_tvector3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_tvector3f_bufferoffset) : NULL;
4694 mod->surfmesh.data_normal3f_vertexbuffer = mod->surfmesh.data_normal3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_normal3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_normal3f_bufferoffset) : NULL;
4695 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = mod->surfmesh.data_texcoordtexture2f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[2]), mod->surfmesh.data_texcoordtexture2f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_texcoordtexture2f_bufferoffset) : NULL;
4696 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = mod->surfmesh.data_texcoordlightmap2f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[2]), mod->surfmesh.data_texcoordlightmap2f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_texcoordlightmap2f_bufferoffset) : NULL;
4697 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = mod->surfmesh.data_lightmapcolor4f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[4]), mod->surfmesh.data_lightmapcolor4f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_lightmapcolor4f_bufferoffset) : NULL;
4698 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = mod->surfmesh.data_skeletalindex4ub ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(unsigned char[4]), mod->surfmesh.data_skeletalindex4ub, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_skeletalindex4ub_bufferoffset) : NULL;
4699 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = mod->surfmesh.data_skeletalweight4ub ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(unsigned char[4]), mod->surfmesh.data_skeletalweight4ub, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_skeletalweight4ub_bufferoffset) : NULL;
4702 void Mod_Mesh_Finalize(dp_model_t *mod)
4704 if (gl_paranoid.integer)
4705 Mod_Mesh_Validate(mod);
4706 Mod_Mesh_ComputeBounds(mod);
4707 Mod_Mesh_MakeSortedSurfaces(mod);
4708 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);
4709 Mod_Mesh_UploadDynamicBuffers(mod);