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, (void *)element3i, (void *)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++)
2232 for (int i = 0; layer->texturename[j][i]; i++)
2233 if(layer->texturename[j][i] == '\\')
2234 layer->texturename[j][i] = '/';
2235 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2240 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2242 int texflagsmask, texflagsor;
2243 qboolean success = true;
2244 q3shaderinfo_t *shader;
2247 strlcpy(texture->name, name, sizeof(texture->name));
2248 texture->basealpha = 1.0f;
2249 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2251 // allow disabling of picmip or compression by defaulttexflags
2253 if(!(defaulttexflags & TEXF_PICMIP))
2254 texflagsmask &= ~TEXF_PICMIP;
2255 if(!(defaulttexflags & TEXF_COMPRESS))
2256 texflagsmask &= ~TEXF_COMPRESS;
2258 if(defaulttexflags & TEXF_ISWORLD)
2259 texflagsor |= TEXF_ISWORLD;
2260 if(defaulttexflags & TEXF_ISSPRITE)
2261 texflagsor |= TEXF_ISSPRITE;
2262 // unless later loaded from the shader
2263 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2264 texture->offsetscale = 1;
2265 texture->offsetbias = 0;
2266 texture->specularscalemod = 1;
2267 texture->specularpowermod = 1;
2268 texture->rtlightambient = 0;
2269 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2270 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2271 // JUST GREP FOR "specularscalemod = 1".
2275 if (developer_loading.integer)
2276 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2278 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2280 texture->basematerialflags = MATERIALFLAG_SKY;
2281 if (shader->skyboxname[0] && loadmodel)
2283 // quake3 seems to append a _ to the skybox name, so this must do so as well
2284 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2287 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2288 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2290 texture->basematerialflags = MATERIALFLAG_WALL;
2292 if (shader->layers[0].alphatest)
2293 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2294 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2295 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2296 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2298 texture->biaspolygonoffset += shader->biaspolygonoffset;
2299 texture->biaspolygonfactor += shader->biaspolygonfactor;
2301 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2302 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2303 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2304 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2305 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2306 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2307 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2308 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2309 texture->customblendfunc[0] = GL_ONE;
2310 texture->customblendfunc[1] = GL_ZERO;
2311 texture->transparentsort = shader->transparentsort;
2312 if (shader->numlayers > 0)
2314 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2315 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2317 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2318 * additive GL_ONE GL_ONE
2319 additive weird GL_ONE GL_SRC_ALPHA
2320 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2321 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2322 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2323 brighten GL_DST_COLOR GL_ONE
2324 brighten GL_ONE GL_SRC_COLOR
2325 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2326 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2327 * modulate GL_DST_COLOR GL_ZERO
2328 * modulate GL_ZERO GL_SRC_COLOR
2329 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2330 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2331 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2332 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2333 * no blend GL_ONE GL_ZERO
2334 nothing GL_ZERO GL_ONE
2336 // if not opaque, figure out what blendfunc to use
2337 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2339 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2340 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2341 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2342 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2343 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2344 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2346 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2349 if (!shader->lighting)
2350 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2352 // here be dragons: convert quake3 shaders to material
2353 if (shader->numlayers > 0)
2356 int terrainbackgroundlayer = -1;
2357 int lightmaplayer = -1;
2358 int alphagenspecularlayer = -1;
2359 int rgbgenvertexlayer = -1;
2360 int rgbgendiffuselayer = -1;
2361 int materiallayer = -1;
2362 int endofprelayers = 0;
2363 int firstpostlayer = 0;
2364 int shaderpassindex = 0;
2365 for (i = 0; i < shader->numlayers; i++)
2367 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2369 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2370 rgbgenvertexlayer = i;
2371 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2372 rgbgendiffuselayer = i;
2373 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2374 alphagenspecularlayer = i;
2376 if (shader->numlayers >= 2
2377 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2378 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2379 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2380 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2382 // terrain blend or certain other effects involving alphatest over a regular layer
2383 terrainbackgroundlayer = 0;
2385 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2386 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2388 else if (lightmaplayer == 0)
2390 // ordinary texture but with $lightmap before diffuse
2392 firstpostlayer = lightmaplayer + 2;
2394 else if (lightmaplayer >= 1)
2396 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2397 endofprelayers = lightmaplayer - 1;
2398 materiallayer = lightmaplayer - 1;
2399 firstpostlayer = lightmaplayer + 1;
2401 else if (rgbgenvertexlayer >= 0)
2403 // map models with baked lighting
2404 materiallayer = rgbgenvertexlayer;
2405 endofprelayers = rgbgenvertexlayer;
2406 firstpostlayer = rgbgenvertexlayer + 1;
2407 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2408 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2409 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2411 else if (rgbgendiffuselayer >= 0)
2413 // entity models with dynamic lighting
2414 materiallayer = rgbgendiffuselayer;
2415 endofprelayers = rgbgendiffuselayer;
2416 firstpostlayer = rgbgendiffuselayer + 1;
2417 // 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)...
2418 if (alphagenspecularlayer >= 0)
2419 firstpostlayer = alphagenspecularlayer + 1;
2423 // special effects shaders - treat first as primary layer and do everything else as post
2428 // convert the main material layer
2429 // 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
2430 if (materiallayer >= 0)
2431 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2432 // convert the terrain background blend layer (if any)
2433 if (terrainbackgroundlayer >= 0)
2434 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2435 // convert the prepass layers (if any)
2436 texture->startpreshaderpass = shaderpassindex;
2437 for (i = 0; i < endofprelayers; i++)
2438 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2439 texture->endpreshaderpass = shaderpassindex;
2440 texture->startpostshaderpass = shaderpassindex;
2441 // convert the postpass layers (if any)
2442 for (i = firstpostlayer; i < shader->numlayers; i++)
2443 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2444 texture->startpostshaderpass = shaderpassindex;
2447 if (shader->dpshadow)
2448 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2449 if (shader->dpnoshadow)
2450 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2451 if (shader->dpnortlight)
2452 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2453 if (shader->vertexalpha)
2454 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2455 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2456 texture->reflectmin = shader->reflectmin;
2457 texture->reflectmax = shader->reflectmax;
2458 texture->refractfactor = shader->refractfactor;
2459 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2460 texture->reflectfactor = shader->reflectfactor;
2461 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2462 texture->r_water_wateralpha = shader->r_water_wateralpha;
2463 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2464 texture->offsetmapping = shader->offsetmapping;
2465 texture->offsetscale = shader->offsetscale;
2466 texture->offsetbias = shader->offsetbias;
2467 texture->specularscalemod = shader->specularscalemod;
2468 texture->specularpowermod = shader->specularpowermod;
2469 texture->rtlightambient = shader->rtlightambient;
2470 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2471 if (shader->dpreflectcube[0])
2472 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2474 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2475 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2476 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2477 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2478 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2479 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2480 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2481 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2482 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2484 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2485 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2486 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2487 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2488 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2489 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2490 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2491 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2492 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2493 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2494 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2495 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2496 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2497 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2498 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2499 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2500 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2501 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2502 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2503 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2504 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2505 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2506 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2507 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2508 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2509 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2510 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2511 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2512 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2513 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2514 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2515 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2517 texture->surfaceflags = shader->surfaceflags;
2518 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2520 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2523 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2524 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2525 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2526 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2527 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2528 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2529 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2530 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2531 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2532 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2534 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2535 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2536 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2537 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2538 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2539 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2540 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2541 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2542 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2543 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2544 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2545 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2546 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2547 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2548 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2549 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2551 if (shader->dpmeshcollisions)
2552 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2553 if (shader->dpshaderkill && developer_extra.integer)
2554 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2556 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2558 if (developer_extra.integer)
2559 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2560 texture->basematerialflags = defaultmaterialflags;
2561 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2563 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2565 if (developer_extra.integer)
2566 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2567 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2568 texture->supercontents = SUPERCONTENTS_SOLID;
2572 if (developer_extra.integer)
2573 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2574 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2576 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2577 texture->supercontents = SUPERCONTENTS_SOLID;
2579 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2581 texture->basematerialflags = MATERIALFLAG_SKY;
2582 texture->supercontents = SUPERCONTENTS_SKY;
2586 texture->basematerialflags = defaultmaterialflags;
2587 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2589 if(cls.state == ca_dedicated)
2591 texture->materialshaderpass = NULL;
2596 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2599 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2600 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2601 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2602 if (texture->q2contents)
2603 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2608 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2611 // init the animation variables
2612 texture->currentframe = texture;
2613 texture->currentmaterialflags = texture->basematerialflags;
2614 if (!texture->materialshaderpass)
2615 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2616 if (!texture->materialshaderpass->skinframes[0])
2617 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2618 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2619 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2623 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2625 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2626 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2628 strlcpy(texture->name, name, sizeof(texture->name));
2629 texture->basealpha = 1.0f;
2630 texture->basematerialflags = materialflags;
2631 texture->supercontents = supercontents;
2633 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2634 texture->offsetscale = 1;
2635 texture->offsetbias = 0;
2636 texture->specularscalemod = 1;
2637 texture->specularpowermod = 1;
2638 texture->rtlightambient = 0;
2639 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2640 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2641 // JUST GREP FOR "specularscalemod = 1".
2643 if (developer_extra.integer)
2644 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2646 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2648 // init the animation variables
2649 texture->currentmaterialflags = texture->basematerialflags;
2650 texture->currentframe = texture;
2651 texture->currentskinframe = skinframe;
2652 texture->backgroundcurrentskinframe = NULL;
2655 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2657 long unsigned int i, j;
2658 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2660 if (texture->shaderpasses[i])
2663 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2664 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2665 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2666 Mem_Free(texture->shaderpasses[i]);
2667 texture->shaderpasses[i] = NULL;
2670 texture->materialshaderpass = NULL;
2671 texture->currentskinframe = NULL;
2672 texture->backgroundcurrentskinframe = NULL;
2675 skinfile_t *Mod_LoadSkinFiles(void)
2677 int i, words, line, wordsoverflow;
2680 skinfile_t *skinfile = NULL, *first = NULL;
2681 skinfileitem_t *skinfileitem;
2682 char word[10][MAX_QPATH];
2687 U_bodyBox,models/players/Legoman/BikerA2.tga
2688 U_RArm,models/players/Legoman/BikerA1.tga
2689 U_LArm,models/players/Legoman/BikerA1.tga
2690 U_armor,common/nodraw
2691 U_sword,common/nodraw
2692 U_shield,common/nodraw
2693 U_homb,common/nodraw
2694 U_backpack,common/nodraw
2695 U_colcha,common/nodraw
2700 memset(word, 0, sizeof(word));
2701 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2703 // If it's the first file we parse
2704 if (skinfile == NULL)
2706 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2711 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2712 skinfile = skinfile->next;
2714 skinfile->next = NULL;
2716 for(line = 0;;line++)
2719 if (!COM_ParseToken_QuakeC(&data, true))
2721 if (!strcmp(com_token, "\n"))
2724 wordsoverflow = false;
2728 strlcpy(word[words++], com_token, sizeof (word[0]));
2730 wordsoverflow = true;
2732 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2735 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);
2738 // words is always >= 1
2739 if (!strcmp(word[0], "replace"))
2743 if (developer_loading.integer)
2744 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2745 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2746 skinfileitem->next = skinfile->items;
2747 skinfile->items = skinfileitem;
2748 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2749 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2752 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]);
2754 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2756 // tag name, like "tag_weapon,"
2757 // not used for anything (not even in Quake3)
2759 else if (words >= 2 && !strcmp(word[1], ","))
2761 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2762 if (developer_loading.integer)
2763 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2764 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2765 skinfileitem->next = skinfile->items;
2766 skinfile->items = skinfileitem;
2767 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2768 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2771 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);
2776 loadmodel->numskins = i;
2780 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2783 skinfileitem_t *skinfileitem, *nextitem;
2784 for (;skinfile;skinfile = next)
2786 next = skinfile->next;
2787 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2789 nextitem = skinfileitem->next;
2790 Mem_Free(skinfileitem);
2796 int Mod_CountSkinFiles(skinfile_t *skinfile)
2799 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2803 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2806 double isnap = 1.0 / snap;
2807 for (i = 0;i < numvertices*numcomponents;i++)
2808 vertices[i] = floor(vertices[i]*isnap)*snap;
2811 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2813 int i, outtriangles;
2814 float edgedir1[3], edgedir2[3], temp[3];
2815 // a degenerate triangle is one with no width (thickness, surface area)
2816 // these are characterized by having all 3 points colinear (along a line)
2817 // or having two points identical
2818 // the simplest check is to calculate the triangle's area
2819 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2821 // calculate first edge
2822 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2823 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2824 CrossProduct(edgedir1, edgedir2, temp);
2825 if (VectorLength2(temp) < 0.001f)
2826 continue; // degenerate triangle (no area)
2827 // valid triangle (has area)
2828 VectorCopy(inelement3i, outelement3i);
2832 return outtriangles;
2835 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2838 int firstvertex, lastvertex;
2839 if (numelements > 0 && elements)
2841 firstvertex = lastvertex = elements[0];
2842 for (i = 1;i < numelements;i++)
2845 firstvertex = min(firstvertex, e);
2846 lastvertex = max(lastvertex, e);
2850 firstvertex = lastvertex = 0;
2851 if (firstvertexpointer)
2852 *firstvertexpointer = firstvertex;
2853 if (lastvertexpointer)
2854 *lastvertexpointer = lastvertex;
2857 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2859 // make an optimal set of texture-sorted batches to draw...
2861 int *firstsurfacefortexture;
2862 int *numsurfacesfortexture;
2863 if (!mod->sortedmodelsurfaces)
2864 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2865 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2866 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2867 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2868 for (j = 0;j < mod->nummodelsurfaces;j++)
2870 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2871 if(!surface->texture)
2873 t = (int)(surface->texture - mod->data_textures);
2874 numsurfacesfortexture[t]++;
2877 for (t = 0;t < mod->num_textures;t++)
2879 firstsurfacefortexture[t] = j;
2880 j += numsurfacesfortexture[t];
2882 for (j = 0;j < mod->nummodelsurfaces;j++)
2884 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2885 if (!surface->texture)
2887 t = (int)(surface->texture - mod->data_textures);
2888 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2890 Mem_Free(firstsurfacefortexture);
2891 Mem_Free(numsurfacesfortexture);
2894 void Mod_BuildVBOs(void)
2896 if(cls.state == ca_dedicated)
2899 if (!loadmodel->surfmesh.num_vertices)
2902 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2905 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2907 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2909 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2910 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2915 // upload short indices as a buffer
2916 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2917 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);
2919 // upload int indices as a buffer
2920 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2921 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);
2923 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2924 // we put several vertex data streams in the same buffer
2925 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2930 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2931 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2932 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2933 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2934 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2935 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2936 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2937 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2938 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2939 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2940 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2941 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2942 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2943 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2944 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2945 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2946 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2947 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2948 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2949 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2950 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2951 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2952 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2953 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2954 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2955 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2956 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2957 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2962 extern cvar_t mod_obj_orientation;
2963 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2965 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2967 const char *texname;
2969 const float *v, *vn, *vt;
2971 size_t outbufferpos = 0;
2972 size_t outbuffermax = 0x100000;
2973 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
2974 const msurface_t *surface;
2975 const int maxtextures = 256;
2976 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
2977 dp_model_t *submodel;
2979 // construct the mtllib file
2980 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
2983 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
2986 countvertices += surface->num_vertices;
2987 countfaces += surface->num_triangles;
2988 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
2989 for (textureindex = 0;textureindex < counttextures;textureindex++)
2990 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
2992 if (textureindex < counttextures)
2993 continue; // already wrote this material entry
2994 if (textureindex >= maxtextures)
2995 continue; // just a precaution
2996 textureindex = counttextures++;
2997 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
2998 if (outbufferpos >= outbuffermax >> 1)
3001 oldbuffer = outbuffer;
3002 outbuffer = (char *) Z_Malloc(outbuffermax);
3003 memcpy(outbuffer, oldbuffer, outbufferpos);
3006 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");
3011 // write the mtllib file
3012 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3014 // construct the obj file
3016 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);
3020 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)
3022 if (outbufferpos >= outbuffermax >> 1)
3025 oldbuffer = outbuffer;
3026 outbuffer = (char *) Z_Malloc(outbuffermax);
3027 memcpy(outbuffer, oldbuffer, outbufferpos);
3030 if(mod_obj_orientation.integer)
3031 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]);
3033 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]);
3038 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3040 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3043 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3044 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3046 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3047 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3050 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3052 if (outbufferpos >= outbuffermax >> 1)
3055 oldbuffer = outbuffer;
3056 outbuffer = (char *) Z_Malloc(outbuffermax);
3057 memcpy(outbuffer, oldbuffer, outbufferpos);
3063 if(mod_obj_orientation.integer)
3064 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);
3066 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);
3073 // write the obj file
3074 FS_WriteFile(filename, outbuffer, outbufferpos);
3078 Z_Free(texturenames);
3081 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3084 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3086 int countnodes = 0, counttriangles = 0, countframes = 0;
3094 size_t outbufferpos = 0;
3095 size_t outbuffermax = 0x100000;
3096 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3097 const msurface_t *surface;
3098 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3101 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3103 if (outbufferpos >= outbuffermax >> 1)
3106 oldbuffer = outbuffer;
3107 outbuffer = (char *) Z_Malloc(outbuffermax);
3108 memcpy(outbuffer, oldbuffer, outbufferpos);
3112 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3116 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3119 for (poseindex = 0;poseindex < numposes;poseindex++)
3122 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3125 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3129 matrix4x4_t posematrix;
3130 if (outbufferpos >= outbuffermax >> 1)
3133 oldbuffer = outbuffer;
3134 outbuffer = (char *) Z_Malloc(outbuffermax);
3135 memcpy(outbuffer, oldbuffer, outbufferpos);
3139 // strangely the smd angles are for a transposed matrix, so we
3140 // have to generate a transposed matrix, then convert that...
3141 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3142 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3143 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3144 if (angles[0] >= 180) angles[0] -= 360;
3145 if (angles[1] >= 180) angles[1] -= 360;
3146 if (angles[2] >= 180) angles[2] -= 360;
3150 float a = DEG2RAD(angles[ROLL]);
3151 float b = DEG2RAD(angles[PITCH]);
3152 float c = DEG2RAD(angles[YAW]);
3153 float cy, sy, cp, sp, cr, sr;
3155 // smd matrix construction, for comparing
3166 test[1][0] = sr*sp*cy+cr*-sy;
3167 test[1][1] = sr*sp*sy+cr*cy;
3169 test[2][0] = (cr*sp*cy+-sr*-sy);
3170 test[2][1] = (cr*sp*sy+-sr*cy);
3172 test[3][0] = pose[9];
3173 test[3][1] = pose[10];
3174 test[3][2] = pose[11];
3177 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]));
3182 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3187 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3190 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3192 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3195 if (outbufferpos >= outbuffermax >> 1)
3198 oldbuffer = outbuffer;
3199 outbuffer = (char *) Z_Malloc(outbuffermax);
3200 memcpy(outbuffer, oldbuffer, outbufferpos);
3203 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3206 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3208 const int index = e[2-cornerindex];
3209 const float *v = model->surfmesh.data_vertex3f + index * 3;
3210 const float *vn = model->surfmesh.data_normal3f + index * 3;
3211 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3212 const int b = model->surfmesh.blends[index];
3213 if (b < model->num_bones)
3214 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]);
3217 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3218 const unsigned char *wi = w->index;
3219 const unsigned char *wf = w->influence;
3220 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);
3221 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);
3222 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);
3223 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]);
3230 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3235 FS_WriteFile(filename, outbuffer, outbufferpos);
3238 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3245 decompiles a model to editable files
3248 static void Mod_Decompile_f(cmd_state_t *cmd)
3250 int i, j, k, l, first, count;
3252 char inname[MAX_QPATH];
3253 char outname[MAX_QPATH];
3254 char mtlname[MAX_QPATH];
3255 char basename[MAX_QPATH];
3256 char animname[MAX_QPATH];
3257 char animname2[MAX_QPATH];
3258 char zymtextbuffer[16384];
3259 char dpmtextbuffer[16384];
3260 char framegroupstextbuffer[16384];
3261 int zymtextsize = 0;
3262 int dpmtextsize = 0;
3263 int framegroupstextsize = 0;
3266 if (Cmd_Argc(cmd) != 2)
3268 Con_Print("usage: modeldecompile <filename>\n");
3272 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3273 FS_StripExtension(inname, basename, sizeof(basename));
3275 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3278 Con_Print("No such model\n");
3281 if (mod->brush.submodel)
3283 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3284 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3285 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3288 if (!mod->surfmesh.num_triangles)
3290 Con_Print("Empty model (or sprite)\n");
3294 // export OBJ if possible (not on sprites)
3295 if (mod->surfmesh.num_triangles)
3297 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3298 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3299 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3302 // export SMD if possible (only for skeletal models)
3303 if (mod->surfmesh.num_triangles && mod->num_bones)
3305 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3306 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3307 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3308 if (l > 0) zymtextsize += l;
3309 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3310 if (l > 0) dpmtextsize += l;
3311 for (i = 0;i < mod->numframes;i = j)
3313 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3314 first = mod->animscenes[i].firstframe;
3315 if (mod->animscenes[i].framecount > 1)
3318 count = mod->animscenes[i].framecount;
3324 // check for additional frames with same name
3325 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3326 if(animname[l] < '0' || animname[l] > '9')
3328 if(k > 0 && animname[k-1] == '_')
3331 count = mod->num_poses - first;
3332 for (j = i + 1;j < mod->numframes;j++)
3334 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3335 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3336 if(animname2[l] < '0' || animname2[l] > '9')
3338 if(k > 0 && animname[k-1] == '_')
3341 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3343 count = mod->animscenes[j].firstframe - first;
3347 // if it's only one frame, use the original frame name
3349 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3352 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3353 Mod_Decompile_SMD(mod, outname, first, count, false);
3354 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3356 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3357 if (l > 0) zymtextsize += l;
3359 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3361 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3362 if (l > 0) dpmtextsize += l;
3364 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3366 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3367 if (l > 0) framegroupstextsize += l;
3371 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3373 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3374 if (framegroupstextsize)
3375 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3379 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3382 memset(state, 0, sizeof(*state));
3383 state->width = width;
3384 state->height = height;
3385 state->currentY = 0;
3386 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3387 for (y = 0;y < state->height;y++)
3389 state->rows[y].currentX = 0;
3390 state->rows[y].rowY = -1;
3394 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3397 state->currentY = 0;
3398 for (y = 0;y < state->height;y++)
3400 state->rows[y].currentX = 0;
3401 state->rows[y].rowY = -1;
3405 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3408 Mem_Free(state->rows);
3409 memset(state, 0, sizeof(*state));
3412 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3414 mod_alloclightmap_row_t *row;
3417 row = state->rows + blockheight;
3418 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3420 if (state->currentY + blockheight <= state->height)
3422 // use the current allocation position
3423 row->rowY = state->currentY;
3425 state->currentY += blockheight;
3429 // find another position
3430 for (y = blockheight;y < state->height;y++)
3432 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3434 row = state->rows + y;
3438 if (y == state->height)
3443 *outx = row->currentX;
3444 row->currentX += blockwidth;
3449 typedef struct lightmapsample_s
3453 float *vertex_color;
3454 unsigned char *lm_bgr;
3455 unsigned char *lm_dir;
3459 typedef struct lightmapvertex_s
3464 float texcoordbase[2];
3465 float texcoordlightmap[2];
3466 float lightcolor[4];
3470 typedef struct lightmaptriangle_s
3478 // 2D modelspace coordinates of min corner
3479 // snapped to lightmap grid but not in grid coordinates
3481 // 2D modelspace to lightmap coordinate scale
3489 typedef struct lightmaplight_s
3500 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3502 #define MAX_LIGHTMAPSAMPLES 64
3503 static int mod_generatelightmaps_numoffsets[3];
3504 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3506 static int mod_generatelightmaps_numlights;
3507 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3509 extern cvar_t r_shadow_lightattenuationdividebias;
3510 extern cvar_t r_shadow_lightattenuationlinearscale;
3512 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3517 float relativepoint[3];
3524 float lightorigin[3];
3528 float lightcolor[3];
3530 for (i = 0;i < 5*3;i++)
3532 for (index = 0;;index++)
3534 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3539 lightradius2 = lightradius * lightradius;
3540 VectorSubtract(lightorigin, pos, relativepoint);
3541 dist2 = VectorLength2(relativepoint);
3542 if (dist2 >= lightradius2)
3544 lightiradius = 1.0f / lightradius;
3545 dist = sqrt(dist2) * lightiradius;
3546 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3547 if (intensity <= 0.0f)
3549 if (model && model->TraceLine)
3551 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3552 if (trace.fraction < 1)
3555 // scale down intensity to add to both ambient and diffuse
3556 //intensity *= 0.5f;
3557 VectorNormalize(relativepoint);
3558 VectorScale(lightcolor, intensity, color);
3559 VectorMA(sample , 0.5f , color, sample );
3560 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3561 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3562 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3563 // calculate a weighted average light direction as well
3564 intensity *= VectorLength(color);
3565 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3567 // calculate the direction we'll use to reduce the sample to a directional light source
3568 VectorCopy(sample + 12, dir);
3569 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3570 VectorNormalize(dir);
3571 // extract the diffuse color along the chosen direction and scale it
3572 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3573 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3574 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3575 // subtract some of diffuse from ambient
3576 VectorMA(sample, -0.333f, diffuse, ambient);
3577 // store the normalized lightdir
3578 VectorCopy(dir, lightdir);
3581 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3585 const msurface_t *surface;
3586 const float *vertex3f = model->surfmesh.data_vertex3f;
3587 const int *element3i = model->surfmesh.data_element3i;
3590 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3592 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3594 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3596 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3598 VectorCopy(vertex3f + 3*e[0], v2[0]);
3599 VectorCopy(vertex3f + 3*e[1], v2[1]);
3600 VectorCopy(vertex3f + 3*e[2], v2[2]);
3601 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3606 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3608 int maxnodes = 1<<14;
3609 svbsp_node_t *nodes;
3614 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3615 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3616 VectorCopy(lightinfo->origin, origin);
3617 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3620 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3621 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3622 if (svbsp.ranoutofnodes)
3625 if (maxnodes > 1<<22)
3631 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3636 if (svbsp.numnodes > 0)
3638 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3639 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3640 lightinfo->svbsp = svbsp;
3645 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3649 lightmaplight_t *lightinfo;
3653 mod_generatelightmaps_numlights = 0;
3654 for (index = 0;;index++)
3656 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3660 mod_generatelightmaps_numlights++;
3662 if (mod_generatelightmaps_numlights > 0)
3664 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3665 lightinfo = mod_generatelightmaps_lightinfo;
3666 for (index = 0;;index++)
3668 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3675 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3677 lightinfo->iradius = 1.0f / lightinfo->radius;
3678 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3679 // TODO: compute svbsp
3680 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3684 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3687 if (mod_generatelightmaps_lightinfo)
3689 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3690 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3691 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3692 Mem_Free(mod_generatelightmaps_lightinfo);
3694 mod_generatelightmaps_lightinfo = NULL;
3695 mod_generatelightmaps_numlights = 0;
3698 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3700 const svbsp_node_t *node;
3701 const svbsp_node_t *nodes = svbsp->nodes;
3706 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3708 return num == -1; // true if empty, false if solid (shadowed)
3711 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3714 float relativepoint[3];
3723 const lightmaplight_t *lightinfo;
3725 for (i = 0;i < 5*3;i++)
3727 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3729 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3730 VectorSubtract(lightinfo->origin, pos, relativepoint);
3731 // don't accept light from behind a surface, it causes bad shading
3732 if (normal && DotProduct(relativepoint, normal) <= 0)
3734 dist2 = VectorLength2(relativepoint);
3735 if (dist2 >= lightinfo->radius2)
3737 dist = sqrt(dist2) * lightinfo->iradius;
3738 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3741 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3745 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3747 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3749 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3752 // for light grid we'd better check visibility of the offset point
3753 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3754 if (trace.fraction < 1)
3755 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3758 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3763 // scale intensity according to how many rays succeeded
3764 // we know one test is valid, half of the rest will fail...
3765 //if (normal && tests > 1)
3766 // intensity *= (tests - 1.0f) / tests;
3767 intensity *= (float)hits / tests;
3769 // scale down intensity to add to both ambient and diffuse
3770 //intensity *= 0.5f;
3771 VectorNormalize(relativepoint);
3772 VectorScale(lightinfo->color, intensity, color);
3773 VectorMA(sample , 0.5f , color, sample );
3774 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3775 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3776 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3777 // calculate a weighted average light direction as well
3778 intensity *= VectorLength(color);
3779 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3783 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3789 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3790 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3791 VectorCopy(sample + 12, dir);
3792 VectorNormalize(dir);
3793 //VectorAdd(dir, normal, dir);
3794 //VectorNormalize(dir);
3795 f = DotProduct(dir, normal);
3796 f = max(0, f) * 255.0f;
3797 VectorScale(sample, f, color);
3798 //VectorCopy(normal, dir);
3799 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3800 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3801 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3802 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3804 lm_dir[0] = (unsigned char)dir[2];
3805 lm_dir[1] = (unsigned char)dir[1];
3806 lm_dir[2] = (unsigned char)dir[0];
3810 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3813 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3814 VectorCopy(sample, vertex_color);
3817 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3823 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3824 // calculate the direction we'll use to reduce the sample to a directional light source
3825 VectorCopy(sample + 12, dir);
3826 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3827 VectorNormalize(dir);
3828 // extract the diffuse color along the chosen direction and scale it
3829 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3830 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3831 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3832 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3833 VectorScale(sample, 127.5f, ambient);
3834 VectorMA(ambient, -0.333f, diffuse, ambient);
3835 // encode to the grid format
3836 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3837 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3838 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3839 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3840 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3841 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3842 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3843 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3844 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));}
3847 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3852 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3853 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3854 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3855 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3856 radius[0] = mod_generatelightmaps_lightmapradius.value;
3857 radius[1] = mod_generatelightmaps_vertexradius.value;
3858 radius[2] = mod_generatelightmaps_gridradius.value;
3859 for (i = 0;i < 3;i++)
3861 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3864 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3869 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3871 msurface_t *surface;
3874 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3876 surface = model->data_surfaces + surfaceindex;
3877 surface->lightmaptexture = NULL;
3878 surface->deluxemaptexture = NULL;
3880 if (model->brushq3.data_lightmaps)
3882 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3883 if (model->brushq3.data_lightmaps[i])
3884 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3885 Mem_Free(model->brushq3.data_lightmaps);
3886 model->brushq3.data_lightmaps = NULL;
3888 if (model->brushq3.data_deluxemaps)
3890 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3891 if (model->brushq3.data_deluxemaps[i])
3892 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3893 Mem_Free(model->brushq3.data_deluxemaps);
3894 model->brushq3.data_deluxemaps = NULL;
3898 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3900 msurface_t *surface;
3906 surfmesh_t oldsurfmesh;
3908 unsigned char *data;
3909 oldsurfmesh = model->surfmesh;
3910 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3911 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3913 size += model->surfmesh.num_vertices * sizeof(float[3]);
3914 size += model->surfmesh.num_vertices * sizeof(float[3]);
3915 size += model->surfmesh.num_vertices * sizeof(float[3]);
3916 size += model->surfmesh.num_vertices * sizeof(float[3]);
3917 size += model->surfmesh.num_vertices * sizeof(float[2]);
3918 size += model->surfmesh.num_vertices * sizeof(float[2]);
3919 size += model->surfmesh.num_vertices * sizeof(float[4]);
3920 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3921 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3922 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3923 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3924 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3925 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3926 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3927 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3928 if (model->surfmesh.num_vertices > 65536)
3929 model->surfmesh.data_element3s = NULL;
3931 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3932 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3933 model->surfmesh.data_element3i_indexbuffer = NULL;
3934 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3935 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3936 model->surfmesh.data_element3s_indexbuffer = NULL;
3937 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3938 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3939 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3940 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3941 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3942 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3943 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3944 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3945 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3946 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3947 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
3949 // convert all triangles to unique vertex data
3951 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3953 surface = model->data_surfaces + surfaceindex;
3954 surface->num_firstvertex = outvertexindex;
3955 surface->num_vertices = surface->num_triangles*3;
3956 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3957 for (i = 0;i < surface->num_triangles*3;i++)
3960 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3961 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3962 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3963 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3964 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3965 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3966 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3967 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3968 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3969 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3970 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3971 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3972 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
3973 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
3974 if (oldsurfmesh.data_texcoordlightmap2f)
3976 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
3977 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
3979 if (oldsurfmesh.data_lightmapcolor4f)
3981 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
3982 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
3983 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
3984 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
3987 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
3988 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
3992 if (model->surfmesh.data_element3s)
3993 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
3994 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
3996 // find and update all submodels to use this new surfmesh data
3997 for (i = 0;i < model->brush.numsubmodels;i++)
3998 model->brush.submodels[i]->surfmesh = model->surfmesh;
4001 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
4003 msurface_t *surface;
4009 lightmaptriangle_t *triangle;
4010 // generate lightmap triangle structs
4011 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4012 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4014 surface = model->data_surfaces + surfaceindex;
4015 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4016 for (i = 0;i < surface->num_triangles;i++)
4018 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4019 triangle->triangleindex = surface->num_firsttriangle+i;
4020 triangle->surfaceindex = surfaceindex;
4021 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4022 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4023 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4024 // calculate bounds of triangle
4025 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4026 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4027 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4028 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4029 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4030 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4031 // pick an axial projection based on the triangle normal
4032 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4034 if (fabs(normal[1]) > fabs(normal[axis]))
4036 if (fabs(normal[2]) > fabs(normal[axis]))
4038 triangle->axis = axis;
4043 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4045 if (mod_generatelightmaps_lightmaptriangles)
4046 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4047 mod_generatelightmaps_lightmaptriangles = NULL;
4050 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4052 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4054 msurface_t *surface;
4068 float trianglenormal[3];
4069 float samplecenter[3];
4070 float samplenormal[3];
4076 float lmscalepixels;
4079 float lm_basescalepixels;
4080 int lm_borderpixels;
4084 lightmaptriangle_t *triangle;
4085 unsigned char *lightmappixels;
4086 unsigned char *deluxemappixels;
4087 mod_alloclightmap_state_t lmstate;
4090 // generate lightmap projection information for all triangles
4091 if (model->texturepool == NULL)
4092 model->texturepool = R_AllocTexturePool();
4093 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4094 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4095 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4096 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4097 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4099 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4101 surface = model->data_surfaces + surfaceindex;
4102 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4103 lmscalepixels = lm_basescalepixels;
4104 for (retry = 0;retry < 30;retry++)
4106 // after a couple failed attempts, degrade quality to make it fit
4108 lmscalepixels *= 0.5f;
4109 for (i = 0;i < surface->num_triangles;i++)
4111 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4112 triangle->lightmapindex = lightmapnumber;
4113 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4114 // pick two planar axes for projection
4115 // lightmap coordinates here are in pixels
4116 // lightmap projections are snapped to pixel grid explicitly, such
4117 // that two neighboring triangles sharing an edge and projection
4118 // axis will have identical sample spacing along their shared edge
4120 for (j = 0;j < 3;j++)
4122 if (j == triangle->axis)
4124 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4125 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4126 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4127 triangle->lmbase[k] = lmmins/lmscalepixels;
4128 triangle->lmscale[k] = lmscalepixels;
4131 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4134 // if all fit in this texture, we're done with this surface
4135 if (i == surface->num_triangles)
4137 // if we haven't maxed out the lightmap size yet, we retry the
4138 // entire surface batch...
4139 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4141 lm_texturesize *= 2;
4144 Mod_AllocLightmap_Free(&lmstate);
4145 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4148 // if we have maxed out the lightmap size, and this triangle does
4149 // not fit in the same texture as the rest of the surface, we have
4150 // to retry the entire surface in a new texture (can only use one)
4151 // with multiple retries, the lightmap quality degrades until it
4152 // fits (or gives up)
4153 if (surfaceindex > 0)
4155 Mod_AllocLightmap_Reset(&lmstate);
4159 Mod_AllocLightmap_Free(&lmstate);
4161 // now put triangles together into lightmap textures, and do not allow
4162 // triangles of a surface to go into different textures (as that would
4163 // require rewriting the surface list)
4164 model->brushq3.deluxemapping_modelspace = true;
4165 model->brushq3.deluxemapping = true;
4166 model->brushq3.num_mergedlightmaps = lightmapnumber;
4167 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4168 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4169 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4170 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4171 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4173 surface = model->data_surfaces + surfaceindex;
4174 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4175 for (i = 0;i < surface->num_triangles;i++)
4177 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4178 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4179 VectorNormalize(trianglenormal);
4180 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4181 axis = triangle->axis;
4182 axis1 = axis == 0 ? 1 : 0;
4183 axis2 = axis == 2 ? 1 : 2;
4184 lmiscale[0] = 1.0f / triangle->lmscale[0];
4185 lmiscale[1] = 1.0f / triangle->lmscale[1];
4186 if (trianglenormal[axis] < 0)
4187 VectorNegate(trianglenormal, trianglenormal);
4188 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4189 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4190 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4191 for (j = 0;j < 3;j++)
4193 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4194 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4195 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4197 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4198 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4199 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4200 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]);
4210 forward[1] = 1.0f / triangle->lmscale[0];
4214 left[2] = 1.0f / triangle->lmscale[1];
4219 origin[1] = triangle->lmbase[0];
4220 origin[2] = triangle->lmbase[1];
4223 forward[0] = 1.0f / triangle->lmscale[0];
4228 left[2] = 1.0f / triangle->lmscale[1];
4232 origin[0] = triangle->lmbase[0];
4234 origin[2] = triangle->lmbase[1];
4237 forward[0] = 1.0f / triangle->lmscale[0];
4241 left[1] = 1.0f / triangle->lmscale[1];
4246 origin[0] = triangle->lmbase[0];
4247 origin[1] = triangle->lmbase[1];
4251 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4253 #define LM_DIST_EPSILON (1.0f / 32.0f)
4254 for (y = 0;y < triangle->lmsize[1];y++)
4256 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4257 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4259 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4260 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4261 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4262 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4263 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4269 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4271 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);
4272 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);
4276 Mem_Free(lightmappixels);
4277 if (deluxemappixels)
4278 Mem_Free(deluxemappixels);
4280 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4282 surface = model->data_surfaces + surfaceindex;
4283 if (!surface->num_triangles)
4285 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4286 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4287 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4288 surface->lightmapinfo = NULL;
4291 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4292 model->brushq1.lightdata = NULL;
4293 model->brushq1.lightmapupdateflags = NULL;
4294 model->brushq1.firstrender = false;
4295 model->brushq1.num_lightstyles = 0;
4296 model->brushq1.data_lightstyleinfo = NULL;
4297 for (i = 0;i < model->brush.numsubmodels;i++)
4299 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4300 model->brush.submodels[i]->brushq1.firstrender = false;
4301 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4302 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4306 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4309 for (i = 0;i < model->surfmesh.num_vertices;i++)
4310 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4313 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4320 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4322 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4323 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4325 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4326 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4328 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4329 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4335 extern cvar_t mod_q3bsp_nolightmaps;
4336 static void Mod_GenerateLightmaps(dp_model_t *model)
4338 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4339 dp_model_t *oldloadmodel = loadmodel;
4342 Mod_GenerateLightmaps_InitSampleOffsets(model);
4343 Mod_GenerateLightmaps_DestroyLightmaps(model);
4344 Mod_GenerateLightmaps_UnweldTriangles(model);
4345 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4346 Mod_GenerateLightmaps_CreateLights(model);
4347 if(!mod_q3bsp_nolightmaps.integer)
4348 Mod_GenerateLightmaps_CreateLightmaps(model);
4349 Mod_GenerateLightmaps_UpdateVertexColors(model);
4350 Mod_GenerateLightmaps_UpdateLightGrid(model);
4351 Mod_GenerateLightmaps_DestroyLights(model);
4352 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4354 loadmodel = oldloadmodel;
4357 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4359 if (Cmd_Argc(cmd) != 1)
4361 Con_Printf("usage: mod_generatelightmaps\n");
4366 Con_Printf("no worldmodel loaded\n");
4369 Mod_GenerateLightmaps(cl.worldmodel);
4372 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4374 memset(mod, 0, sizeof(*mod));
4375 strlcpy(mod->name, name, sizeof(mod->name));
4376 mod->mempool = Mem_AllocPool(name, 0, NULL);
4377 mod->texturepool = R_AllocTexturePool();
4378 mod->Draw = R_Q1BSP_Draw;
4379 mod->DrawDepth = R_Q1BSP_DrawDepth;
4380 mod->DrawDebug = R_Q1BSP_DrawDebug;
4381 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4382 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4383 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4384 mod->DrawLight = R_Q1BSP_DrawLight;
4387 void Mod_Mesh_Destroy(dp_model_t *mod)
4389 Mod_UnloadModel(mod);
4392 // resets the mesh model to have no geometry to render, ready for a new frame -
4393 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4394 void Mod_Mesh_Reset(dp_model_t *mod)
4396 mod->num_surfaces = 0;
4397 mod->surfmesh.num_vertices = 0;
4398 mod->surfmesh.num_triangles = 0;
4399 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4400 mod->DrawSky = NULL; // will be set if a texture needs it
4401 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4404 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4408 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4409 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4410 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4412 if (mod->max_textures <= mod->num_textures)
4414 texture_t *oldtextures = mod->data_textures;
4415 mod->max_textures = max(mod->max_textures * 2, 1024);
4416 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4417 // update the pointers
4418 for (i = 0; i < mod->num_surfaces; i++)
4419 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4421 t = &mod->data_textures[mod->num_textures++];
4422 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4423 t->mesh_drawflag = drawflag;
4424 t->mesh_defaulttexflags = defaulttexflags;
4425 t->mesh_defaultmaterialflags = defaultmaterialflags;
4426 switch (defaultdrawflags & DRAWFLAG_MASK)
4428 case DRAWFLAG_ADDITIVE:
4429 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4430 t->currentmaterialflags = t->basematerialflags;
4432 case DRAWFLAG_MODULATE:
4433 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4434 t->currentmaterialflags = t->basematerialflags;
4435 t->customblendfunc[0] = GL_DST_COLOR;
4436 t->customblendfunc[1] = GL_ZERO;
4438 case DRAWFLAG_2XMODULATE:
4439 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4440 t->currentmaterialflags = t->basematerialflags;
4441 t->customblendfunc[0] = GL_DST_COLOR;
4442 t->customblendfunc[1] = GL_SRC_COLOR;
4444 case DRAWFLAG_SCREEN:
4445 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4446 t->currentmaterialflags = t->basematerialflags;
4447 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4448 t->customblendfunc[1] = GL_ONE;
4456 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4459 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4460 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4461 return mod->data_surfaces + mod->num_surfaces - 1;
4462 // create new surface
4463 if (mod->max_surfaces == mod->num_surfaces)
4465 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4466 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4467 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4469 surf = mod->data_surfaces + mod->num_surfaces;
4470 mod->num_surfaces++;
4471 memset(surf, 0, sizeof(*surf));
4472 surf->texture = tex;
4473 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4474 surf->num_firstvertex = mod->surfmesh.num_vertices;
4475 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4476 mod->DrawSky = R_Q1BSP_DrawSky;
4477 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4478 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4482 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)
4484 int hashindex, h, vnum, mask;
4485 surfmesh_t *mesh = &mod->surfmesh;
4486 if (mesh->max_vertices == mesh->num_vertices)
4488 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4489 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4490 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4491 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4492 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4493 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4494 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4495 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4496 // rebuild the hash table
4497 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4498 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4499 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4500 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4501 mask = mod->surfmesh.num_vertexhashsize - 1;
4502 // no need to hash the vertices for the entire model, the latest surface will suffice.
4503 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4505 // this uses prime numbers intentionally for computing the hash
4506 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;
4507 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4508 ; // just iterate until we find the terminator
4509 mesh->data_vertexhash[h] = vnum;
4512 mask = mod->surfmesh.num_vertexhashsize - 1;
4513 // this uses prime numbers intentionally for computing the hash
4514 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4515 // when possible find an identical vertex within the same surface and return it
4516 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4518 if (vnum >= surf->num_firstvertex
4519 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4520 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4521 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4522 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4523 && 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)
4526 // add the new vertex
4527 vnum = mesh->num_vertices++;
4528 if (surf->num_vertices > 0)
4530 if (surf->mins[0] > x) surf->mins[0] = x;
4531 if (surf->mins[1] > y) surf->mins[1] = y;
4532 if (surf->mins[2] > z) surf->mins[2] = z;
4533 if (surf->maxs[0] < x) surf->maxs[0] = x;
4534 if (surf->maxs[1] < y) surf->maxs[1] = y;
4535 if (surf->maxs[2] < z) surf->maxs[2] = z;
4539 VectorSet(surf->mins, x, y, z);
4540 VectorSet(surf->maxs, x, y, z);
4542 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4543 mesh->data_vertexhash[h] = vnum;
4544 mesh->data_vertex3f[vnum * 3 + 0] = x;
4545 mesh->data_vertex3f[vnum * 3 + 1] = y;
4546 mesh->data_vertex3f[vnum * 3 + 2] = z;
4547 mesh->data_normal3f[vnum * 3 + 0] = nx;
4548 mesh->data_normal3f[vnum * 3 + 1] = ny;
4549 mesh->data_normal3f[vnum * 3 + 2] = nz;
4550 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4551 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4552 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4553 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4554 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4555 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4556 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4557 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4561 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4563 surfmesh_t *mesh = &mod->surfmesh;
4564 if (mesh->max_triangles == mesh->num_triangles)
4566 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4567 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4568 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4570 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4571 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4572 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4573 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4574 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4575 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4576 mesh->num_triangles++;
4577 surf->num_triangles++;
4580 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4584 msurface_t *surf, *surf2;
4586 // build the sorted surfaces list properly to reduce material setup
4587 // this is easy because we're just sorting on texture and don't care about the order of textures
4588 mod->nummodelsurfaces = 0;
4589 for (i = 0; i < mod->num_surfaces; i++)
4590 mod->data_surfaces[i].included = false;
4591 for (i = 0; i < mod->num_surfaces; i++)
4593 surf = mod->data_surfaces + i;
4596 tex = surf->texture;
4597 // j = i is intentional
4598 for (j = i; j < mod->num_surfaces; j++)
4600 surf2 = mod->data_surfaces + j;
4601 if (surf2->included)
4603 if (surf2->texture == tex)
4605 surf2->included = true;
4606 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4612 static void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4615 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4617 if (mod->surfmesh.num_vertices > 0)
4619 // calculate normalmins/normalmaxs
4620 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4621 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4622 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4624 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4625 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4626 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4627 // expand bounds to include this vertex
4628 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4629 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4630 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4631 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4632 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4633 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4635 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4636 // (fast but less accurate than doing it per vertex)
4637 x2a = mod->normalmins[0] * mod->normalmins[0];
4638 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4639 y2a = mod->normalmins[1] * mod->normalmins[1];
4640 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4641 z2a = mod->normalmins[2] * mod->normalmins[2];
4642 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4646 yawradius = sqrt(x2 + y2);
4647 rotatedradius = sqrt(x2 + y2 + z2);
4648 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4649 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4650 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4651 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4652 mod->radius = rotatedradius;
4653 mod->radius2 = x2 + y2 + z2;
4657 VectorClear(mod->normalmins);
4658 VectorClear(mod->normalmaxs);
4659 VectorClear(mod->yawmins);
4660 VectorClear(mod->yawmaxs);
4661 VectorClear(mod->rotatedmins);
4662 VectorClear(mod->rotatedmaxs);
4668 void Mod_Mesh_Validate(dp_model_t *mod)
4671 qboolean warned = false;
4672 for (i = 0; i < mod->num_surfaces; i++)
4674 msurface_t *surf = mod->data_surfaces + i;
4675 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4676 int first = surf->num_firstvertex;
4677 int end = surf->num_firstvertex + surf->num_vertices;
4679 for (j = 0;j < surf->num_triangles * 3;j++)
4681 if (e[j] < first || e[j] >= end)
4684 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4692 static void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
4694 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;
4695 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;
4696 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;
4697 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;
4698 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;
4699 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;
4700 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;
4701 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;
4702 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;
4703 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;
4704 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;
4707 void Mod_Mesh_Finalize(dp_model_t *mod)
4709 if (gl_paranoid.integer)
4710 Mod_Mesh_Validate(mod);
4711 Mod_Mesh_ComputeBounds(mod);
4712 Mod_Mesh_MakeSortedSurfaces(mod);
4713 if(!r_refdef.draw2dstage)
4714 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);
4715 Mod_Mesh_UploadDynamicBuffers(mod);