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 = {CF_CLIENT | CF_ARCHIVE, "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 = {CF_CLIENT | CF_ARCHIVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
44 // Supported model formats
45 static modloader_t loader[] =
47 {"obj", NULL, 0, Mod_OBJ_Load},
48 {NULL, "IDPO", 4, Mod_IDP0_Load},
49 {NULL, "IDP2", 4, Mod_IDP2_Load},
50 {NULL, "IDP3", 4, Mod_IDP3_Load},
51 {NULL, "IDSP", 4, Mod_IDSP_Load},
52 {NULL, "IDS2", 4, Mod_IDS2_Load},
53 {NULL, "\035", 1, Mod_Q1BSP_Load},
54 {NULL, "\036", 1, Mod_HLBSP_Load},
55 {NULL, "BSP2", 4, Mod_BSP2_Load},
56 {NULL, "2PSB", 4, Mod_2PSB_Load},
57 {NULL, "IBSP", 4, Mod_IBSP_Load},
58 {NULL, "ZYMOTICMODEL", 13, Mod_ZYMOTICMODEL_Load},
59 {NULL, "DARKPLACESMODEL", 16, Mod_DARKPLACESMODEL_Load},
60 {NULL, "PSKMODEL", 9, Mod_PSKMODEL_Load},
61 {NULL, "INTERQUAKEMODEL", 16, Mod_INTERQUAKEMODEL_Load},
62 {"map", NULL, 0, Mod_MAP_Load},
66 static mempool_t *mod_mempool;
67 static memexpandablearray_t models;
69 static mempool_t* q3shaders_mem;
70 typedef struct q3shader_hash_entry_s
73 struct q3shader_hash_entry_s* chain;
74 } q3shader_hash_entry_t;
75 #define Q3SHADER_HASH_SIZE 1021
76 typedef struct q3shader_data_s
78 memexpandablearray_t hash_entries;
79 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
80 memexpandablearray_t char_ptrs;
82 static q3shader_data_t* q3shader_data;
84 static void mod_start(void)
87 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
90 SCR_PushLoadingScreen("Loading models", 1.0);
92 for (i = 0;i < nummodels;i++)
93 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
96 for (i = 0;i < nummodels;i++)
97 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
100 SCR_PushLoadingScreen(mod->name, 1.0 / count);
101 Mod_LoadModel(mod, true, false);
102 SCR_PopLoadingScreen(false);
104 SCR_PopLoadingScreen(false);
107 static void mod_shutdown(void)
110 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
113 for (i = 0;i < nummodels;i++)
114 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
115 Mod_UnloadModel(mod);
118 Mod_Skeletal_FreeBuffers();
121 static void mod_newmap(void)
124 int i, j, k, l, surfacenum, ssize, tsize;
125 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
128 for (i = 0;i < nummodels;i++)
130 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
132 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
134 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
135 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
136 if (mod->data_textures[j].shaderpasses[l])
137 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
138 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
140 if (mod->brush.solidskyskinframe)
141 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
142 if (mod->brush.alphaskyskinframe)
143 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
147 if (!cl_stainmaps_clearonload.integer)
150 for (i = 0;i < nummodels;i++)
152 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
154 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
156 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
158 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
159 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
160 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
161 mod->brushq1.lightmapupdateflags[surfacenum] = true;
173 static void Mod_Print_f(cmd_state_t *cmd);
174 static void Mod_Precache_f(cmd_state_t *cmd);
175 static void Mod_Decompile_f(cmd_state_t *cmd);
176 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
179 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
180 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(model_t), 16);
186 Cvar_RegisterVariable(&r_mipskins);
187 Cvar_RegisterVariable(&r_mipnormalmaps);
188 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
189 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
190 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
192 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
193 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
194 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
195 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
196 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
197 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
199 Cmd_AddCommand(CF_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
200 Cmd_AddCommand(CF_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
201 Cmd_AddCommand(CF_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
202 Cmd_AddCommand(CF_CLIENT, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
205 void Mod_RenderInit(void)
207 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
210 void Mod_UnloadModel (model_t *mod)
212 char name[MAX_QPATH];
214 model_t *parentmodel;
216 if (developer_loading.integer)
217 Con_Printf("unloading model %s\n", mod->name);
219 strlcpy(name, mod->name, sizeof(name));
220 parentmodel = mod->brush.parentmodel;
224 if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
225 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
226 mod->surfmesh.data_element3i_indexbuffer = NULL;
227 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
228 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
229 mod->surfmesh.data_element3s_indexbuffer = NULL;
230 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
231 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
232 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
233 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
234 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
235 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
236 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
237 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
238 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
239 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
240 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
242 // free textures/memory attached to the model
243 R_FreeTexturePool(&mod->texturepool);
244 Mem_FreePool(&mod->mempool);
245 // clear the struct to make it available
246 memset(mod, 0, sizeof(model_t));
247 // restore the fields we want to preserve
248 strlcpy(mod->name, name, sizeof(mod->name));
249 mod->brush.parentmodel = parentmodel;
254 static void R_Model_Null_Draw(entity_render_t *ent)
260 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass);
262 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
277 // REQUIRED: fetch start
278 COM_ParseToken_Simple(&bufptr, true, false, true);
280 break; // end of file
281 if (!strcmp(com_token, "\n"))
282 continue; // empty line
283 start = atoi(com_token);
285 // REQUIRED: fetch length
286 COM_ParseToken_Simple(&bufptr, true, false, true);
287 if (!bufptr || !strcmp(com_token, "\n"))
289 Con_Printf("framegroups file: missing number of frames\n");
292 len = atoi(com_token);
294 // OPTIONAL args start
295 COM_ParseToken_Simple(&bufptr, true, false, true);
297 // OPTIONAL: fetch fps
299 if (bufptr && strcmp(com_token, "\n"))
301 fps = atof(com_token);
302 COM_ParseToken_Simple(&bufptr, true, false, true);
305 // OPTIONAL: fetch loopflag
307 if (bufptr && strcmp(com_token, "\n"))
309 loop = (atoi(com_token) != 0);
310 COM_ParseToken_Simple(&bufptr, true, false, true);
313 // OPTIONAL: fetch name
315 if (bufptr && strcmp(com_token, "\n"))
317 strlcpy(name, com_token, sizeof(name));
318 COM_ParseToken_Simple(&bufptr, true, false, true);
321 // OPTIONAL: remaining unsupported tokens (eat them)
322 while (bufptr && strcmp(com_token, "\n"))
323 COM_ParseToken_Simple(&bufptr, true, false, true);
325 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
328 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
335 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass)
337 model_t *mod = (model_t *) pass;
338 animscene_t *anim = &mod->animscenes[i];
340 strlcpy(anim->name, name, sizeof(anim[i].name));
342 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
343 anim->firstframe = bound(0, start, mod->num_poses - 1);
344 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
345 anim->framerate = max(1, fps);
347 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
350 static void Mod_FrameGroupify(model_t *mod, const char *buf)
355 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
358 Con_Printf("no scene found in framegroups file, aborting\n");
361 mod->numframes = cnt;
364 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
365 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
368 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
371 static void Mod_FindPotentialDeforms(model_t *mod)
375 mod->wantnormals = false;
376 mod->wanttangents = false;
377 for (i = 0;i < mod->num_textures;i++)
379 texture = mod->data_textures + i;
380 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
381 mod->wantnormals = true;
382 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
383 mod->wantnormals = true;
384 for (j = 0;j < Q3MAXDEFORMS;j++)
386 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
388 mod->wanttangents = true;
389 mod->wantnormals = true;
392 if (texture->deforms[j].deform != Q3DEFORM_NONE)
393 mod->wantnormals = true;
405 model_t *Mod_LoadModel(model_t *mod, qbool crash, qbool checkdisk)
409 fs_offset_t filesize = 0;
414 if (mod->name[0] == '*') // submodel
417 if (!strcmp(mod->name, "null"))
422 if (mod->loaded || mod->mempool)
423 Mod_UnloadModel(mod);
425 if (developer_loading.integer)
426 Con_Printf("loading model %s\n", mod->name);
429 mod->crc = (unsigned int)-1;
432 VectorClear(mod->normalmins);
433 VectorClear(mod->normalmaxs);
434 VectorClear(mod->yawmins);
435 VectorClear(mod->yawmaxs);
436 VectorClear(mod->rotatedmins);
437 VectorClear(mod->rotatedmaxs);
439 mod->modeldatatypestring = "null";
440 mod->type = mod_null;
441 mod->Draw = R_Model_Null_Draw;
445 // no fatal errors occurred, so this model is ready to use.
454 // even if the model is loaded it still may need reloading...
456 // if it is not loaded or checkdisk is true we need to calculate the crc
457 if (!mod->loaded || checkdisk)
459 if (checkdisk && mod->loaded)
460 Con_DPrintf("checking model %s\n", mod->name);
461 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
464 crc = CRC_Block((unsigned char *)buf, filesize);
465 // we need to reload the model if the crc does not match
471 // if the model is already loaded and checks passed, just return
479 if (developer_loading.integer)
480 Con_Printf("loading model %s\n", mod->name);
482 SCR_PushLoadingScreen(mod->name, 1);
484 // LadyHavoc: unload the existing model in this slot (if there is one)
485 if (mod->loaded || mod->mempool)
486 Mod_UnloadModel(mod);
491 // errors can prevent the corresponding mod->loaded = true;
494 // default lightmap scale
495 mod->lightmapscale = 1;
497 // default model radius and bounding box (mainly for missing models)
499 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
500 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
501 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
502 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
503 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
504 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
508 // load q3 shaders for the first time, or after a level change
515 const char *ext = FS_FileExtension(mod->name);
516 char *bufend = (char *)buf + filesize;
517 // all models use memory, so allocate a memory pool
518 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
520 // call the apropriate loader
523 // Try matching magic bytes.
524 for (i = 0; loader[i].Load; i++)
526 // Headerless formats can just load based on extension. Otherwise match the magic string.
527 if((loader[i].extension && !strcasecmp(ext, loader[i].extension) && !loader[i].header) ||
528 (loader[i].header && !memcmp(buf, loader[i].header, loader[i].headersize)))
531 loader[i].Load(mod, buf, bufend);
534 Mod_FindPotentialDeforms(mod);
536 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
539 Mod_FrameGroupify(mod, (const char *)buf);
543 Mod_SetDrawSkyAndWater(mod);
549 Con_Printf(CON_ERROR "Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
552 // LadyHavoc: Sys_Error was *ANNOYING*
553 Con_Printf (CON_ERROR "Mod_LoadModel: %s not found\n", mod->name);
555 // no fatal errors occurred, so this model is ready to use.
558 SCR_PopLoadingScreen(false);
563 void Mod_ClearUsed(void)
566 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
568 for (i = 0;i < nummodels;i++)
569 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
573 void Mod_PurgeUnused(void)
576 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
578 for (i = 0;i < nummodels;i++)
580 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
582 Mod_UnloadModel(mod);
583 Mem_ExpandableArray_FreeRecord(&models, mod);
594 model_t *Mod_FindName(const char *name, const char *parentname)
603 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
606 Host_Error ("Mod_ForName: empty name");
608 // search the currently loaded models
609 for (i = 0;i < nummodels;i++)
611 if ((mod = (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))))
618 // no match found, create a new one
619 mod = (model_t *) Mem_ExpandableArray_AllocRecord(&models);
620 strlcpy(mod->name, name, sizeof(mod->name));
622 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
624 mod->brush.parentmodel = NULL;
630 extern qbool vid_opened;
636 Loads in a model for the given name
639 model_t *Mod_ForName(const char *name, qbool crash, qbool checkdisk, const char *parentname)
643 // FIXME: So we don't crash if a server is started early.
647 model = Mod_FindName(name, parentname);
648 if (!model->loaded || checkdisk)
649 Mod_LoadModel(model, crash, checkdisk);
657 Reloads all models if they have changed
660 void Mod_Reload(void)
663 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
666 SCR_PushLoadingScreen("Reloading models", 1.0);
668 for (i = 0;i < nummodels;i++)
669 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
671 for (i = 0;i < nummodels;i++)
672 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
674 SCR_PushLoadingScreen(mod->name, 1.0 / count);
675 Mod_LoadModel(mod, true, true);
676 SCR_PopLoadingScreen(false);
678 SCR_PopLoadingScreen(false);
681 unsigned char *mod_base;
684 //=============================================================================
691 static void Mod_Print_f(cmd_state_t *cmd)
694 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
697 Con_Print("Loaded models:\n");
698 for (i = 0;i < nummodels;i++)
700 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
702 if (mod->brush.numsubmodels)
703 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
705 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
715 static void Mod_Precache_f(cmd_state_t *cmd)
717 if (Cmd_Argc(cmd) == 2)
718 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
720 Con_Print("usage: modelprecache <filename>\n");
723 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
727 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
728 memset(used, 0, numvertices);
729 for (i = 0;i < numelements;i++)
730 used[elements[i]] = 1;
731 for (i = 0, count = 0;i < numvertices;i++)
732 remapvertices[i] = used[i] ? count++ : -1;
737 qbool Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
739 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
741 int invalidintcount = 0, invalidintexample = 0;
742 int invalidshortcount = 0, invalidshortexample = 0;
743 int invalidmismatchcount = 0, invalidmismatchexample = 0;
746 for (i = 0; i < numelements; i++)
748 if (element3i[i] < first || element3i[i] > last)
751 invalidintexample = i;
757 for (i = 0; i < numelements; i++)
759 if (element3s[i] < first || element3s[i] > last)
762 invalidintexample = i;
766 if (element3i && element3s)
768 for (i = 0; i < numelements; i++)
770 if (element3s[i] != element3i[i])
772 invalidmismatchcount++;
773 invalidmismatchexample = i;
777 if (invalidintcount || invalidshortcount || invalidmismatchcount)
779 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, (void *)element3i, (void *)element3s, filename, fileline);
780 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
781 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
782 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);
783 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
785 // edit the elements to make them safer, as the driver will crash otherwise
787 for (i = 0; i < numelements; i++)
788 if (element3i[i] < first || element3i[i] > last)
789 element3i[i] = first;
791 for (i = 0; i < numelements; i++)
792 if (element3s[i] < first || element3s[i] > last)
793 element3s[i] = first;
794 if (element3i && element3s)
795 for (i = 0; i < numelements; i++)
796 if (element3s[i] != element3i[i])
797 element3s[i] = element3i[i];
804 // warning: this is an expensive function!
805 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qbool areaweighting)
812 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
813 // process each vertex of each triangle and accumulate the results
814 // use area-averaging, to make triangles with a big area have a bigger
815 // weighting on the vertex normal than triangles with a small area
816 // to do so, just add the 'normals' together (the bigger the area
817 // the greater the length of the normal is
819 for (i = 0; i < numtriangles; i++, element += 3)
822 vertex3f + element[0] * 3,
823 vertex3f + element[1] * 3,
824 vertex3f + element[2] * 3,
829 VectorNormalize(areaNormal);
831 for (j = 0;j < 3;j++)
833 vectorNormal = normal3f + element[j] * 3;
834 vectorNormal[0] += areaNormal[0];
835 vectorNormal[1] += areaNormal[1];
836 vectorNormal[2] += areaNormal[2];
839 // and just normalize the accumulated vertex normal in the end
840 vectorNormal = normal3f + 3 * firstvertex;
841 for (i = 0; i < numvertices; i++, vectorNormal += 3)
842 VectorNormalize(vectorNormal);
846 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)
848 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
849 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
850 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
852 // 6 multiply, 9 subtract
853 VectorSubtract(v1, v0, v10);
854 VectorSubtract(v2, v0, v20);
855 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
856 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
857 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
858 // 12 multiply, 10 subtract
859 tc10[1] = tc1[1] - tc0[1];
860 tc20[1] = tc2[1] - tc0[1];
861 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
862 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
863 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
864 tc10[0] = tc1[0] - tc0[0];
865 tc20[0] = tc2[0] - tc0[0];
866 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
867 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
868 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
869 // 12 multiply, 4 add, 6 subtract
870 f = DotProduct(svector3f, normal3f);
871 svector3f[0] -= f * normal3f[0];
872 svector3f[1] -= f * normal3f[1];
873 svector3f[2] -= f * normal3f[2];
874 f = DotProduct(tvector3f, normal3f);
875 tvector3f[0] -= f * normal3f[0];
876 tvector3f[1] -= f * normal3f[1];
877 tvector3f[2] -= f * normal3f[2];
878 // if texture is mapped the wrong way (counterclockwise), the tangents
879 // have to be flipped, this is detected by calculating a normal from the
880 // two tangents, and seeing if it is opposite the surface normal
881 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
882 CrossProduct(tvector3f, svector3f, tangentcross);
883 if (DotProduct(tangentcross, normal3f) < 0)
885 VectorNegate(svector3f, svector3f);
886 VectorNegate(tvector3f, tvector3f);
891 // warning: this is a very expensive function!
892 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, qbool areaweighting)
895 float sdir[3], tdir[3], normal[3], *svec, *tvec;
896 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
897 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
900 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
901 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
902 // process each vertex of each triangle and accumulate the results
903 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
905 v0 = vertex3f + e[0] * 3;
906 v1 = vertex3f + e[1] * 3;
907 v2 = vertex3f + e[2] * 3;
908 tc0 = texcoord2f + e[0] * 2;
909 tc1 = texcoord2f + e[1] * 2;
910 tc2 = texcoord2f + e[2] * 2;
912 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
913 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
915 // calculate the edge directions and surface normal
916 // 6 multiply, 9 subtract
917 VectorSubtract(v1, v0, v10);
918 VectorSubtract(v2, v0, v20);
919 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
920 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
921 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
923 // calculate the tangents
924 // 12 multiply, 10 subtract
925 tc10[1] = tc1[1] - tc0[1];
926 tc20[1] = tc2[1] - tc0[1];
927 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
928 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
929 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
930 tc10[0] = tc1[0] - tc0[0];
931 tc20[0] = tc2[0] - tc0[0];
932 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
933 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
934 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
936 // if texture is mapped the wrong way (counterclockwise), the tangents
937 // have to be flipped, this is detected by calculating a normal from the
938 // two tangents, and seeing if it is opposite the surface normal
939 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
940 CrossProduct(tdir, sdir, tangentcross);
941 if (DotProduct(tangentcross, normal) < 0)
943 VectorNegate(sdir, sdir);
944 VectorNegate(tdir, tdir);
949 VectorNormalize(sdir);
950 VectorNormalize(tdir);
952 for (i = 0;i < 3;i++)
954 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
955 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
958 // make the tangents completely perpendicular to the surface normal, and
959 // then normalize them
960 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
961 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
963 f = -DotProduct(svec, n);
964 VectorMA(svec, f, n, svec);
965 VectorNormalize(svec);
966 f = -DotProduct(tvec, n);
967 VectorMA(tvec, f, n, tvec);
968 VectorNormalize(tvec);
972 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qbool lightmapoffsets, qbool vertexcolors)
975 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));
976 loadmodel->surfmesh.num_vertices = numvertices;
977 loadmodel->surfmesh.num_triangles = numtriangles;
978 if (loadmodel->surfmesh.num_vertices)
980 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
981 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
982 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
983 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
984 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
985 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
987 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
989 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
991 if (loadmodel->surfmesh.num_triangles)
993 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
994 if (loadmodel->surfmesh.num_vertices <= 65536)
995 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
999 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
1001 shadowmesh_t *newmesh;
1002 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
1003 newmesh->mempool = mempool;
1004 newmesh->maxverts = maxverts;
1005 newmesh->maxtriangles = maxtriangles;
1006 newmesh->numverts = 0;
1007 newmesh->numtriangles = 0;
1008 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1009 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1011 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
1012 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
1013 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
1014 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
1018 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
1020 int hashindex, vnum;
1021 shadowmeshvertexhash_t *hash;
1022 // this uses prime numbers intentionally
1023 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
1024 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1026 vnum = (hash - mesh->vertexhashentries);
1027 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1028 return hash - mesh->vertexhashentries;
1030 vnum = mesh->numverts++;
1031 hash = mesh->vertexhashentries + vnum;
1032 hash->next = mesh->vertexhashtable[hashindex];
1033 mesh->vertexhashtable[hashindex] = hash;
1034 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1035 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1036 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1040 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1044 for (i = 0;i < numtris;i++)
1046 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1047 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1048 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1049 mesh->numtriangles++;
1052 // the triangle calculation can take a while, so let's do a keepalive here
1053 CL_KeepaliveMessage(false);
1056 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1058 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1059 CL_KeepaliveMessage(false);
1061 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1064 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1066 if (!mesh->numverts)
1069 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1070 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1072 // upload short indices as a buffer
1073 if (mesh->element3s && !mesh->element3s_indexbuffer)
1074 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1076 // upload int indices as a buffer
1077 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1078 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1080 // vertex buffer is several arrays and we put them in the same buffer
1082 // is this wise? the texcoordtexture2f array is used with dynamic
1083 // vertex/svector/tvector/normal when rendering animated models, on the
1084 // other hand animated models don't use a lot of vertices anyway...
1085 if (!mesh->vbo_vertexbuffer)
1087 mesh->vbooffset_vertex3f = 0;
1088 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1092 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qbool createvbo)
1094 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1096 if (mesh->vertexhashentries)
1097 Mem_Free(mesh->vertexhashentries);
1098 mesh->vertexhashentries = NULL;
1099 if (mesh->vertexhashtable)
1100 Mem_Free(mesh->vertexhashtable);
1101 mesh->vertexhashtable = NULL;
1102 if (mesh->maxverts > mesh->numverts)
1104 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1105 mesh->maxverts = mesh->numverts;
1107 if (mesh->maxtriangles > mesh->numtriangles)
1109 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1110 mesh->maxtriangles = mesh->numtriangles;
1112 if (mesh->numverts <= 65536)
1115 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1116 for (i = 0;i < mesh->numtriangles*3;i++)
1117 mesh->element3s[i] = mesh->element3i[i];
1120 Mod_ShadowMesh_CreateVBOs(mesh);
1123 // this can take a while, so let's do a keepalive here
1124 CL_KeepaliveMessage(false);
1129 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1132 vec3_t nmins, nmaxs, ncenter, temp;
1133 float nradius2, dist2, *v;
1137 VectorCopy(mesh->vertex3f, nmins);
1138 VectorCopy(mesh->vertex3f, nmaxs);
1139 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1141 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1142 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1143 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1145 // calculate center and radius
1146 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1147 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1148 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1150 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1152 VectorSubtract(v, ncenter, temp);
1153 dist2 = DotProduct(temp, temp);
1154 if (nradius2 < dist2)
1159 VectorCopy(nmins, mins);
1161 VectorCopy(nmaxs, maxs);
1163 VectorCopy(ncenter, center);
1165 *radius = sqrt(nradius2);
1168 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1170 if (mesh->element3i_indexbuffer)
1171 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1172 if (mesh->element3s_indexbuffer)
1173 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1174 if (mesh->vbo_vertexbuffer)
1175 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1177 Mem_Free(mesh->vertex3f);
1178 if (mesh->element3i)
1179 Mem_Free(mesh->element3i);
1180 if (mesh->element3s)
1181 Mem_Free(mesh->element3s);
1182 if (mesh->vertexhashentries)
1183 Mem_Free(mesh->vertexhashentries);
1184 if (mesh->vertexhashtable)
1185 Mem_Free(mesh->vertexhashtable);
1189 void Mod_CreateCollisionMesh(model_t *mod)
1191 int k, numcollisionmeshtriangles;
1192 qbool usesinglecollisionmesh = false;
1193 const msurface_t *surface = NULL;
1195 mempool_t *mempool = mod->mempool;
1196 if (!mempool && mod->brush.parentmodel)
1197 mempool = mod->brush.parentmodel->mempool;
1198 // make a single combined collision mesh for physics engine use
1199 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1200 numcollisionmeshtriangles = 0;
1201 for (k = mod->submodelsurfaces_start;k < mod->submodelsurfaces_end;k++)
1203 surface = mod->data_surfaces + k;
1204 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1206 usesinglecollisionmesh = true;
1207 numcollisionmeshtriangles = surface->num_triangles;
1210 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1212 numcollisionmeshtriangles += surface->num_triangles;
1214 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1215 if (usesinglecollisionmesh)
1216 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1219 for (k = mod->submodelsurfaces_start; k < mod->submodelsurfaces_end; k++)
1221 surface = mod->data_surfaces + k;
1222 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1224 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1227 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1231 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)
1236 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1237 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1240 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1241 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1242 texcoord2f[0] = tc[0];
1243 texcoord2f[1] = tc[1];
1246 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)
1248 float vup[3], vdown[3], vleft[3], vright[3];
1249 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1250 float sv[3], tv[3], nl[3];
1251 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1252 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1253 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1254 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1255 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1256 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1257 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1258 VectorAdd(svector3f, sv, svector3f);
1259 VectorAdd(tvector3f, tv, tvector3f);
1260 VectorAdd(normal3f, nl, normal3f);
1261 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1262 VectorAdd(svector3f, sv, svector3f);
1263 VectorAdd(tvector3f, tv, tvector3f);
1264 VectorAdd(normal3f, nl, normal3f);
1265 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1266 VectorAdd(svector3f, sv, svector3f);
1267 VectorAdd(tvector3f, tv, tvector3f);
1268 VectorAdd(normal3f, nl, normal3f);
1271 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)
1273 int x, y, ix, iy, *e;
1275 for (y = 0;y < height;y++)
1277 for (x = 0;x < width;x++)
1279 e[0] = (y + 1) * (width + 1) + (x + 0);
1280 e[1] = (y + 0) * (width + 1) + (x + 0);
1281 e[2] = (y + 1) * (width + 1) + (x + 1);
1282 e[3] = (y + 0) * (width + 1) + (x + 0);
1283 e[4] = (y + 0) * (width + 1) + (x + 1);
1284 e[5] = (y + 1) * (width + 1) + (x + 1);
1288 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1289 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1290 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1295 void Mod_Terrain_SurfaceRecurseChunk(model_t *model, int stepsize, int x, int y)
1299 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1300 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1301 float viewvector[3];
1302 unsigned int firstvertex;
1305 if (chunkwidth < 2 || chunkheight < 2)
1307 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]);
1308 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]);
1309 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1310 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1311 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1312 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1314 // too close for this stepsize, emit as 4 chunks instead
1316 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1317 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1318 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1319 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1322 // emit the geometry at stepsize into our vertex buffer / index buffer
1323 // we add two columns and two rows for skirt
1324 outwidth = chunkwidth+2;
1325 outheight = chunkheight+2;
1326 outwidth2 = outwidth-1;
1327 outheight2 = outheight-1;
1328 outwidth3 = outwidth+1;
1329 outheight3 = outheight+1;
1330 firstvertex = numvertices;
1331 e = model->terrain.element3i + numtriangles;
1332 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1333 v = model->terrain.vertex3f + numvertices;
1334 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1335 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1336 for (ty = 0;ty < outheight;ty++)
1338 for (tx = 0;tx < outwidth;tx++)
1340 *e++ = firstvertex + (ty )*outwidth3+(tx );
1341 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1342 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1343 *e++ = firstvertex + (ty )*outwidth3+(tx );
1344 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1345 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1348 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1349 for (ty = 0;ty <= outheight;ty++)
1351 skirtrow = ty == 0 || ty == outheight;
1352 ry = y+bound(1, ty, outheight)*stepsize;
1353 for (tx = 0;tx <= outwidth;tx++)
1355 skirt = skirtrow || tx == 0 || tx == outwidth;
1356 rx = x+bound(1, tx, outwidth)*stepsize;
1359 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1363 // TODO: emit skirt vertices
1366 void Mod_Terrain_UpdateSurfacesForViewOrigin(model_t *model)
1368 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1369 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1370 Mod_Terrain_BuildChunk(model,
1374 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1377 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1379 offset = bound(0, s[4] - '0', 9);
1380 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1385 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1386 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1387 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1388 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1389 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1390 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1391 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1392 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1393 return offset | Q3WAVEFUNC_NONE;
1396 void Mod_FreeQ3Shaders(void)
1398 Mem_FreePool(&q3shaders_mem);
1401 static void Q3Shader_AddToHash (shader_t* shader)
1403 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1404 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1405 q3shader_hash_entry_t* lastEntry = NULL;
1408 if (strcasecmp (entry->shader.name, shader->name) == 0)
1411 if(shader->dpshaderkill)
1413 // killed shader is a redeclarion? we can safely ignore it
1416 else if(entry->shader.dpshaderkill)
1418 // replace the old shader!
1419 // this will skip the entry allocating part
1420 // below and just replace the shader
1425 unsigned char *start, *end, *start2;
1426 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1427 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1428 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1429 if(memcmp(start, start2, end - start))
1430 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1432 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1437 entry = entry->chain;
1439 while (entry != NULL);
1442 if (lastEntry->shader.name[0] != 0)
1445 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1446 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1448 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1449 lastEntry->chain = newEntry;
1450 newEntry->chain = NULL;
1451 lastEntry = newEntry;
1453 /* else: head of chain, in hash entry array */
1456 memcpy (&entry->shader, shader, sizeof (shader_t));
1459 void Mod_LoadQ3Shaders(void)
1467 q3shaderinfo_layer_t *layer;
1469 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1470 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1471 unsigned long custsurfaceflags[256];
1472 int numcustsurfaceflags;
1475 Mod_FreeQ3Shaders();
1477 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1478 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1479 sizeof (q3shader_data_t));
1480 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1481 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1482 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1483 q3shaders_mem, sizeof (char**), 256);
1485 // parse custinfoparms.txt
1486 numcustsurfaceflags = 0;
1487 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1489 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1490 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1493 while (COM_ParseToken_QuakeC(&text, false))
1494 if (!strcasecmp(com_token, "}"))
1496 // custom surfaceflags section
1497 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1498 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1501 while(COM_ParseToken_QuakeC(&text, false))
1503 if (!strcasecmp(com_token, "}"))
1505 // register surfaceflag
1506 if (numcustsurfaceflags >= 256)
1508 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1512 j = (int)strlen(com_token)+1;
1513 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1514 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1516 if (COM_ParseToken_QuakeC(&text, false))
1517 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1519 custsurfaceflags[numcustsurfaceflags] = 0;
1520 numcustsurfaceflags++;
1528 search = FS_Search("scripts/*.shader", true, false, NULL);
1531 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1533 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1536 while (COM_ParseToken_QuakeC(&text, false))
1538 memset (&shader, 0, sizeof(shader));
1540 shader.surfaceparms = 0;
1541 shader.surfaceflags = 0;
1542 shader.textureflags = 0;
1543 shader.numlayers = 0;
1544 shader.lighting = false;
1545 shader.vertexalpha = false;
1546 shader.textureblendalpha = false;
1547 shader.skyboxname[0] = 0;
1548 shader.deforms[0].deform = Q3DEFORM_NONE;
1549 shader.dpnortlight = false;
1550 shader.dpshadow = false;
1551 shader.dpnoshadow = false;
1552 shader.dpmeshcollisions = false;
1553 shader.dpshaderkill = false;
1554 shader.dpreflectcube[0] = 0;
1555 shader.reflectmin = 0;
1556 shader.reflectmax = 1;
1557 shader.refractfactor = 1;
1558 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1559 shader.reflectfactor = 1;
1560 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1561 shader.r_water_wateralpha = 1;
1562 shader.r_water_waterscroll[0] = 0;
1563 shader.r_water_waterscroll[1] = 0;
1564 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1565 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1566 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1567 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1568 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1569 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1570 shader.specularscalemod = 1;
1571 shader.specularpowermod = 1;
1572 shader.rtlightambient = 0;
1573 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1574 // JUST GREP FOR "specularscalemod = 1".
1576 strlcpy(shader.name, com_token, sizeof(shader.name));
1577 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1579 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1582 while (COM_ParseToken_QuakeC(&text, false))
1584 if (!strcasecmp(com_token, "}"))
1586 if (!strcasecmp(com_token, "{"))
1588 static q3shaderinfo_layer_t dummy;
1589 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1591 layer = shader.layers + shader.numlayers++;
1595 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1596 memset(&dummy, 0, sizeof(dummy));
1599 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1600 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1601 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1602 layer->blendfunc[0] = GL_ONE;
1603 layer->blendfunc[1] = GL_ZERO;
1604 while (COM_ParseToken_QuakeC(&text, false))
1606 if (!strcasecmp(com_token, "}"))
1608 if (!strcasecmp(com_token, "\n"))
1611 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1613 if (j < TEXTURE_MAXFRAMES + 4)
1615 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1616 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1617 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1619 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1620 numparameters = j + 1;
1622 if (!COM_ParseToken_QuakeC(&text, true))
1625 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1626 // parameter[j][0] = 0;
1627 if (developer_insane.integer)
1629 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1630 for (j = 0;j < numparameters;j++)
1631 Con_DPrintf(" %s", parameter[j]);
1634 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1636 if (numparameters == 2)
1638 if (!strcasecmp(parameter[1], "add"))
1640 layer->blendfunc[0] = GL_ONE;
1641 layer->blendfunc[1] = GL_ONE;
1643 else if (!strcasecmp(parameter[1], "addalpha"))
1645 layer->blendfunc[0] = GL_SRC_ALPHA;
1646 layer->blendfunc[1] = GL_ONE;
1648 else if (!strcasecmp(parameter[1], "filter"))
1650 layer->blendfunc[0] = GL_DST_COLOR;
1651 layer->blendfunc[1] = GL_ZERO;
1653 else if (!strcasecmp(parameter[1], "blend"))
1655 layer->blendfunc[0] = GL_SRC_ALPHA;
1656 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1659 else if (numparameters == 3)
1662 for (k = 0;k < 2;k++)
1664 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1665 layer->blendfunc[k] = GL_ONE;
1666 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1667 layer->blendfunc[k] = GL_ZERO;
1668 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1669 layer->blendfunc[k] = GL_SRC_COLOR;
1670 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1671 layer->blendfunc[k] = GL_SRC_ALPHA;
1672 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1673 layer->blendfunc[k] = GL_DST_COLOR;
1674 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1675 layer->blendfunc[k] = GL_DST_ALPHA;
1676 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1677 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1678 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1679 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1680 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1681 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1682 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1683 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1685 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1689 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1690 layer->alphatest = true;
1691 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1693 if (!strcasecmp(parameter[0], "clampmap"))
1694 layer->clampmap = true;
1695 layer->numframes = 1;
1696 layer->framerate = 1;
1697 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1698 &q3shader_data->char_ptrs);
1699 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1700 if (!strcasecmp(parameter[1], "$lightmap"))
1701 shader.lighting = true;
1703 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1706 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1707 layer->framerate = atof(parameter[1]);
1708 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1709 for (i = 0;i < layer->numframes;i++)
1710 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1712 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1715 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1716 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1717 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1718 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1719 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1720 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1721 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1722 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1723 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1724 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1725 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1726 else if (!strcasecmp(parameter[1], "wave"))
1728 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1729 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1730 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1731 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1733 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1735 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1738 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1739 layer->alphagen.parms[i] = atof(parameter[i+2]);
1740 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1741 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1742 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1743 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1744 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1745 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1746 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1747 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1748 else if (!strcasecmp(parameter[1], "wave"))
1750 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1751 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1752 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1753 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1755 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1757 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1760 // observed values: tcgen environment
1761 // no other values have been observed in real shaders
1762 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1763 layer->tcgen.parms[i] = atof(parameter[i+2]);
1764 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1765 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1766 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1767 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1768 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1769 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1771 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1778 // tcmod stretch sin # # # #
1779 // tcmod stretch triangle # # # #
1780 // tcmod transform # # # # # #
1781 // tcmod turb # # # #
1782 // tcmod turb sin # # # # (this is bogus)
1783 // no other values have been observed in real shaders
1784 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1785 if (!layer->tcmods[tcmodindex].tcmod)
1787 if (tcmodindex < Q3MAXTCMODS)
1789 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1790 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1791 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1792 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1793 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1794 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1795 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1796 else if (!strcasecmp(parameter[1], "stretch"))
1798 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1799 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1800 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1801 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1803 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1804 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1805 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1808 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1810 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1811 if (!strcasecmp(com_token, "}"))
1814 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1815 shader.lighting = true;
1816 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1818 if (layer == shader.layers + 0)
1820 // vertex controlled transparency
1821 shader.vertexalpha = true;
1825 // multilayer terrain shader or similar
1826 shader.textureblendalpha = true;
1827 if (mod_q3shader_force_terrain_alphaflag.integer)
1828 shader.layers[0].dptexflags |= TEXF_ALPHA;
1832 if(mod_q3shader_force_addalpha.integer)
1834 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1835 // this cvar brings back this behaviour
1836 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1837 layer->blendfunc[0] = GL_SRC_ALPHA;
1840 layer->dptexflags = 0;
1841 if (layer->alphatest)
1842 layer->dptexflags |= TEXF_ALPHA;
1843 switch(layer->blendfunc[0])
1846 case GL_ONE_MINUS_SRC_ALPHA:
1847 layer->dptexflags |= TEXF_ALPHA;
1850 switch(layer->blendfunc[1])
1853 case GL_ONE_MINUS_SRC_ALPHA:
1854 layer->dptexflags |= TEXF_ALPHA;
1857 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1858 layer->dptexflags |= TEXF_MIPMAP;
1859 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1860 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1861 if (layer->clampmap)
1862 layer->dptexflags |= TEXF_CLAMP;
1866 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1868 if (j < TEXTURE_MAXFRAMES + 4)
1870 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1871 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1872 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1874 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1875 numparameters = j + 1;
1877 if (!COM_ParseToken_QuakeC(&text, true))
1880 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1881 // parameter[j][0] = 0;
1882 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1884 if (developer_insane.integer)
1886 Con_DPrintf("%s: ", shader.name);
1887 for (j = 0;j < numparameters;j++)
1888 Con_DPrintf(" %s", parameter[j]);
1891 if (numparameters < 1)
1893 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1895 if (!strcasecmp(parameter[1], "alphashadow"))
1896 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1897 else if (!strcasecmp(parameter[1], "areaportal"))
1898 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1899 else if (!strcasecmp(parameter[1], "botclip"))
1900 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1901 else if (!strcasecmp(parameter[1], "clusterportal"))
1902 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1903 else if (!strcasecmp(parameter[1], "detail"))
1904 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1905 else if (!strcasecmp(parameter[1], "donotenter"))
1906 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1907 else if (!strcasecmp(parameter[1], "dust"))
1908 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1909 else if (!strcasecmp(parameter[1], "hint"))
1910 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1911 else if (!strcasecmp(parameter[1], "fog"))
1912 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1913 else if (!strcasecmp(parameter[1], "lava"))
1914 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1915 else if (!strcasecmp(parameter[1], "lightfilter"))
1916 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1917 else if (!strcasecmp(parameter[1], "lightgrid"))
1918 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1919 else if (!strcasecmp(parameter[1], "metalsteps"))
1920 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1921 else if (!strcasecmp(parameter[1], "nodamage"))
1922 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1923 else if (!strcasecmp(parameter[1], "nodlight"))
1924 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1925 else if (!strcasecmp(parameter[1], "nodraw"))
1926 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1927 else if (!strcasecmp(parameter[1], "nodrop"))
1928 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1929 else if (!strcasecmp(parameter[1], "noimpact"))
1930 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1931 else if (!strcasecmp(parameter[1], "nolightmap"))
1932 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1933 else if (!strcasecmp(parameter[1], "nomarks"))
1934 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1935 else if (!strcasecmp(parameter[1], "nomipmaps"))
1936 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1937 else if (!strcasecmp(parameter[1], "nonsolid"))
1938 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1939 else if (!strcasecmp(parameter[1], "origin"))
1940 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1941 else if (!strcasecmp(parameter[1], "playerclip"))
1942 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1943 else if (!strcasecmp(parameter[1], "sky"))
1944 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1945 else if (!strcasecmp(parameter[1], "slick"))
1946 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1947 else if (!strcasecmp(parameter[1], "slime"))
1948 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1949 else if (!strcasecmp(parameter[1], "structural"))
1950 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1951 else if (!strcasecmp(parameter[1], "trans"))
1952 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1953 else if (!strcasecmp(parameter[1], "water"))
1954 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1955 else if (!strcasecmp(parameter[1], "pointlight"))
1956 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1957 else if (!strcasecmp(parameter[1], "antiportal"))
1958 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1959 else if (!strcasecmp(parameter[1], "skip"))
1960 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1963 // try custom surfaceparms
1964 for (j = 0; j < numcustsurfaceflags; j++)
1966 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1968 shader.surfaceflags |= custsurfaceflags[j];
1973 if (j == numcustsurfaceflags)
1974 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1977 else if (!strcasecmp(parameter[0], "dpshadow"))
1978 shader.dpshadow = true;
1979 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1980 shader.dpnoshadow = true;
1981 else if (!strcasecmp(parameter[0], "dpnortlight"))
1982 shader.dpnortlight = true;
1983 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1984 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1985 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1986 shader.dpmeshcollisions = true;
1987 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1988 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1990 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1991 shader.dpshaderkill = dpshaderkill;
1993 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1994 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1996 const char *op = NULL;
1997 if (numparameters >= 3)
2001 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
2002 shader.dpshaderkill = dpshaderkill;
2004 else if (numparameters >= 4 && !strcmp(op, "=="))
2006 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
2007 shader.dpshaderkill = dpshaderkill;
2009 else if (numparameters >= 4 && !strcmp(op, "!="))
2011 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
2012 shader.dpshaderkill = dpshaderkill;
2014 else if (numparameters >= 4 && !strcmp(op, ">"))
2016 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
2017 shader.dpshaderkill = dpshaderkill;
2019 else if (numparameters >= 4 && !strcmp(op, "<"))
2021 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
2022 shader.dpshaderkill = dpshaderkill;
2024 else if (numparameters >= 4 && !strcmp(op, ">="))
2026 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
2027 shader.dpshaderkill = dpshaderkill;
2029 else if (numparameters >= 4 && !strcmp(op, "<="))
2031 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
2032 shader.dpshaderkill = dpshaderkill;
2036 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2039 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2041 // some q3 skies don't have the sky parm set
2042 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2043 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2045 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2047 // some q3 skies don't have the sky parm set
2048 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2049 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2050 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2052 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2054 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2055 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2057 else if (!strcasecmp(parameter[0], "nomipmaps"))
2058 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2059 else if (!strcasecmp(parameter[0], "nopicmip"))
2060 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2061 else if (!strcasecmp(parameter[0], "polygonoffset"))
2062 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2063 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2065 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2066 if(numparameters >= 2)
2068 shader.biaspolygonfactor = atof(parameter[1]);
2069 if(numparameters >= 3)
2070 shader.biaspolygonoffset = atof(parameter[2]);
2072 shader.biaspolygonoffset = 0;
2075 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2077 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2078 if (!strcasecmp(parameter[1], "sky"))
2079 shader.transparentsort = TRANSPARENTSORT_SKY;
2080 else if (!strcasecmp(parameter[1], "distance"))
2081 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2082 else if (!strcasecmp(parameter[1], "hud"))
2083 shader.transparentsort = TRANSPARENTSORT_HUD;
2085 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2087 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2089 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2090 shader.refractfactor = atof(parameter[1]);
2091 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2093 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2095 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2096 shader.reflectfactor = atof(parameter[1]);
2097 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2099 else if (!strcasecmp(parameter[0], "dpcamera"))
2101 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2103 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2105 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2106 shader.reflectmin = atof(parameter[1]);
2107 shader.reflectmax = atof(parameter[2]);
2108 shader.refractfactor = atof(parameter[3]);
2109 shader.reflectfactor = atof(parameter[4]);
2110 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2111 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2112 shader.r_water_wateralpha = atof(parameter[11]);
2114 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2116 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2117 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2119 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2121 shader.specularscalemod = atof(parameter[1]);
2123 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2125 shader.specularpowermod = atof(parameter[1]);
2127 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2129 shader.rtlightambient = atof(parameter[1]);
2131 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2133 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2134 shader.offsetmapping = OFFSETMAPPING_OFF;
2135 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2136 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2137 else if (!strcasecmp(parameter[1], "linear"))
2138 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2139 else if (!strcasecmp(parameter[1], "relief"))
2140 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2141 if (numparameters >= 3)
2142 shader.offsetscale = atof(parameter[2]);
2143 if (numparameters >= 5)
2145 if(!strcasecmp(parameter[3], "bias"))
2146 shader.offsetbias = atof(parameter[4]);
2147 else if(!strcasecmp(parameter[3], "match"))
2148 shader.offsetbias = 1.0f - atof(parameter[4]);
2149 else if(!strcasecmp(parameter[3], "match8"))
2150 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2151 else if(!strcasecmp(parameter[3], "match16"))
2152 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2155 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2158 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2159 if (!shader.deforms[deformindex].deform)
2161 if (deformindex < Q3MAXDEFORMS)
2163 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2164 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2165 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2166 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2167 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2168 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2169 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2170 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2171 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2172 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2173 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2174 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2175 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2176 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2177 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2178 else if (!strcasecmp(parameter[1], "wave" ))
2180 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2181 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2182 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2183 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2185 else if (!strcasecmp(parameter[1], "move" ))
2187 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2188 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2189 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2190 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2195 // hide this shader if a cvar said it should be killed
2196 if (shader.dpshaderkill)
2197 shader.numlayers = 0;
2198 // fix up multiple reflection types
2199 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2200 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2202 Q3Shader_AddToHash (&shader);
2206 FS_FreeSearch(search);
2207 // free custinfoparm values
2208 for (j = 0; j < numcustsurfaceflags; j++)
2209 Mem_Free(custsurfaceparmnames[j]);
2212 shader_t *Mod_LookupQ3Shader(const char *name)
2214 unsigned short hash;
2215 q3shader_hash_entry_t* entry;
2217 Mod_LoadQ3Shaders();
2218 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2219 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2220 while (entry != NULL)
2222 if (strcasecmp (entry->shader.name, name) == 0)
2223 return &entry->shader;
2224 entry = entry->chain;
2229 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2231 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2232 shaderpass->framerate = 0.0f;
2233 shaderpass->numframes = 1;
2234 shaderpass->blendfunc[0] = GL_ONE;
2235 shaderpass->blendfunc[1] = GL_ZERO;
2236 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2237 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2238 shaderpass->alphatest = false;
2239 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2240 shaderpass->skinframes[0] = skinframe;
2244 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2247 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2248 shaderpass->alphatest = layer->alphatest != 0;
2249 shaderpass->framerate = layer->framerate;
2250 shaderpass->numframes = layer->numframes;
2251 shaderpass->blendfunc[0] = layer->blendfunc[0];
2252 shaderpass->blendfunc[1] = layer->blendfunc[1];
2253 shaderpass->rgbgen = layer->rgbgen;
2254 shaderpass->alphagen = layer->alphagen;
2255 shaderpass->tcgen = layer->tcgen;
2256 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2257 shaderpass->tcmods[j] = layer->tcmods[j];
2258 for (j = 0; j < layer->numframes; j++)
2260 for (int i = 0; layer->texturename[j][i]; i++)
2261 if(layer->texturename[j][i] == '\\')
2262 layer->texturename[j][i] = '/';
2263 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2268 qbool Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qbool warnmissing, qbool fallback, int defaulttexflags, int defaultmaterialflags)
2270 int texflagsmask, texflagsor;
2271 qbool success = true;
2275 strlcpy(texture->name, name, sizeof(texture->name));
2276 texture->basealpha = 1.0f;
2277 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2279 // allow disabling of picmip or compression by defaulttexflags
2281 if(!(defaulttexflags & TEXF_PICMIP))
2282 texflagsmask &= ~TEXF_PICMIP;
2283 if(!(defaulttexflags & TEXF_COMPRESS))
2284 texflagsmask &= ~TEXF_COMPRESS;
2286 if(defaulttexflags & TEXF_ISWORLD)
2287 texflagsor |= TEXF_ISWORLD;
2288 if(defaulttexflags & TEXF_ISSPRITE)
2289 texflagsor |= TEXF_ISSPRITE;
2290 // unless later loaded from the shader
2291 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2292 texture->offsetscale = 1;
2293 texture->offsetbias = 0;
2294 texture->specularscalemod = 1;
2295 texture->specularpowermod = 1;
2296 texture->rtlightambient = 0;
2297 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2298 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2299 // JUST GREP FOR "specularscalemod = 1".
2303 if (developer_loading.integer)
2304 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2306 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2308 texture->basematerialflags = MATERIALFLAG_SKY;
2309 if (shader->skyboxname[0] && loadmodel)
2311 // quake3 seems to append a _ to the skybox name, so this must do so as well
2312 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2315 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2316 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2318 texture->basematerialflags = MATERIALFLAG_WALL;
2320 if (shader->layers[0].alphatest)
2321 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2322 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2323 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2324 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2326 texture->biaspolygonoffset += shader->biaspolygonoffset;
2327 texture->biaspolygonfactor += shader->biaspolygonfactor;
2329 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2330 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2331 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2332 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2333 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2334 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2335 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2336 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2337 texture->customblendfunc[0] = GL_ONE;
2338 texture->customblendfunc[1] = GL_ZERO;
2339 texture->transparentsort = shader->transparentsort;
2340 if (shader->numlayers > 0)
2342 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2343 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2345 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2346 * additive GL_ONE GL_ONE
2347 additive weird GL_ONE GL_SRC_ALPHA
2348 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2349 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2350 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2351 brighten GL_DST_COLOR GL_ONE
2352 brighten GL_ONE GL_SRC_COLOR
2353 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2354 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2355 * modulate GL_DST_COLOR GL_ZERO
2356 * modulate GL_ZERO GL_SRC_COLOR
2357 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2358 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2359 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2360 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2361 * no blend GL_ONE GL_ZERO
2362 nothing GL_ZERO GL_ONE
2364 // if not opaque, figure out what blendfunc to use
2365 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2367 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2368 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2369 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2370 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2371 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2372 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2374 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2377 if (!shader->lighting)
2378 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2380 // here be dragons: convert quake3 shaders to material
2381 if (shader->numlayers > 0)
2384 int terrainbackgroundlayer = -1;
2385 int lightmaplayer = -1;
2386 int alphagenspecularlayer = -1;
2387 int rgbgenvertexlayer = -1;
2388 int rgbgendiffuselayer = -1;
2389 int materiallayer = -1;
2390 int endofprelayers = 0;
2391 int firstpostlayer = 0;
2392 int shaderpassindex = 0;
2393 for (i = 0; i < shader->numlayers; i++)
2395 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2397 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2398 rgbgenvertexlayer = i;
2399 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2400 rgbgendiffuselayer = i;
2401 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2402 alphagenspecularlayer = i;
2404 if (shader->numlayers >= 2
2405 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2406 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2407 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2408 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2410 // terrain blend or certain other effects involving alphatest over a regular layer
2411 terrainbackgroundlayer = 0;
2413 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2414 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2416 else if (lightmaplayer == 0)
2418 // ordinary texture but with $lightmap before diffuse
2420 firstpostlayer = lightmaplayer + 2;
2422 else if (lightmaplayer >= 1)
2424 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2425 endofprelayers = lightmaplayer - 1;
2426 materiallayer = lightmaplayer - 1;
2427 firstpostlayer = lightmaplayer + 1;
2429 else if (rgbgenvertexlayer >= 0)
2431 // map models with baked lighting
2432 materiallayer = rgbgenvertexlayer;
2433 endofprelayers = rgbgenvertexlayer;
2434 firstpostlayer = rgbgenvertexlayer + 1;
2435 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2436 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2437 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2439 else if (rgbgendiffuselayer >= 0)
2441 // entity models with dynamic lighting
2442 materiallayer = rgbgendiffuselayer;
2443 endofprelayers = rgbgendiffuselayer;
2444 firstpostlayer = rgbgendiffuselayer + 1;
2445 // 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)...
2446 if (alphagenspecularlayer >= 0)
2447 firstpostlayer = alphagenspecularlayer + 1;
2451 // special effects shaders - treat first as primary layer and do everything else as post
2456 // convert the main material layer
2457 // 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
2458 if (materiallayer >= 0)
2459 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2460 // convert the terrain background blend layer (if any)
2461 if (terrainbackgroundlayer >= 0)
2462 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2463 // convert the prepass layers (if any)
2464 texture->startpreshaderpass = shaderpassindex;
2465 for (i = 0; i < endofprelayers; i++)
2466 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2467 texture->endpreshaderpass = shaderpassindex;
2468 texture->startpostshaderpass = shaderpassindex;
2469 // convert the postpass layers (if any)
2470 for (i = firstpostlayer; i < shader->numlayers; i++)
2471 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2472 texture->startpostshaderpass = shaderpassindex;
2475 if (shader->dpshadow)
2476 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2477 if (shader->dpnoshadow)
2478 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2479 if (shader->dpnortlight)
2480 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2481 if (shader->vertexalpha)
2482 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2483 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2484 texture->reflectmin = shader->reflectmin;
2485 texture->reflectmax = shader->reflectmax;
2486 texture->refractfactor = shader->refractfactor;
2487 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2488 texture->reflectfactor = shader->reflectfactor;
2489 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2490 texture->r_water_wateralpha = shader->r_water_wateralpha;
2491 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2492 texture->offsetmapping = shader->offsetmapping;
2493 texture->offsetscale = shader->offsetscale;
2494 texture->offsetbias = shader->offsetbias;
2495 texture->specularscalemod = shader->specularscalemod;
2496 texture->specularpowermod = shader->specularpowermod;
2497 texture->rtlightambient = shader->rtlightambient;
2498 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2499 if (shader->dpreflectcube[0])
2500 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2502 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2503 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2504 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2505 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2506 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2507 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2508 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2509 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2510 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2512 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2513 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2514 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2515 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2516 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2518 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2520 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2523 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2524 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2525 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2526 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2527 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2528 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2529 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2530 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2531 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2532 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2533 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2534 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2536 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2537 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2539 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2540 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2541 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2542 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2543 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2545 texture->surfaceflags = shader->surfaceflags;
2546 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2547 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2548 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2549 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2550 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2551 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2552 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2553 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2554 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2555 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2556 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2557 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2558 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2559 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2560 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2561 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2562 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2563 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2564 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2565 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2566 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2567 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2568 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2569 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2570 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2571 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2572 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2573 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2574 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2575 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2576 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2577 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2579 if (shader->dpmeshcollisions)
2580 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2581 if (shader->dpshaderkill && developer_extra.integer)
2582 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2584 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2586 if (developer_extra.integer)
2587 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2588 texture->basematerialflags = defaultmaterialflags;
2589 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2591 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2593 if (developer_extra.integer)
2594 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2595 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2596 texture->supercontents = SUPERCONTENTS_SOLID;
2600 if (developer_extra.integer)
2601 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2602 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2604 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2605 texture->supercontents = SUPERCONTENTS_SOLID;
2607 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2609 texture->basematerialflags = MATERIALFLAG_SKY;
2610 texture->supercontents = SUPERCONTENTS_SKY;
2614 texture->basematerialflags = defaultmaterialflags;
2615 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2617 if(cls.state == ca_dedicated)
2619 texture->materialshaderpass = NULL;
2624 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2627 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2628 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2629 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2630 if (texture->q2contents)
2631 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2635 if (!success && warnmissing)
2636 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2639 // init the animation variables
2640 texture->currentframe = texture;
2641 texture->currentmaterialflags = texture->basematerialflags;
2642 if (!texture->materialshaderpass)
2643 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2644 if (!texture->materialshaderpass->skinframes[0])
2645 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2646 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2647 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2651 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2653 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2654 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2656 strlcpy(texture->name, name, sizeof(texture->name));
2657 texture->basealpha = 1.0f;
2658 texture->basematerialflags = materialflags;
2659 texture->supercontents = supercontents;
2661 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2662 texture->offsetscale = 1;
2663 texture->offsetbias = 0;
2664 texture->specularscalemod = 1;
2665 texture->specularpowermod = 1;
2666 texture->rtlightambient = 0;
2667 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2668 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2669 // JUST GREP FOR "specularscalemod = 1".
2671 if (developer_extra.integer)
2672 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2674 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2676 // init the animation variables
2677 texture->currentmaterialflags = texture->basematerialflags;
2678 texture->currentframe = texture;
2679 texture->currentskinframe = skinframe;
2680 texture->backgroundcurrentskinframe = NULL;
2683 void Mod_UnloadCustomMaterial(texture_t *texture, qbool purgeskins)
2685 long unsigned int i, j;
2686 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2688 if (texture->shaderpasses[i])
2691 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2692 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2693 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2694 Mem_Free(texture->shaderpasses[i]);
2695 texture->shaderpasses[i] = NULL;
2698 texture->materialshaderpass = NULL;
2699 texture->currentskinframe = NULL;
2700 texture->backgroundcurrentskinframe = NULL;
2703 skinfile_t *Mod_LoadSkinFiles(void)
2705 int i, words, line, wordsoverflow;
2708 skinfile_t *skinfile = NULL, *first = NULL;
2709 skinfileitem_t *skinfileitem;
2710 char word[10][MAX_QPATH];
2715 U_bodyBox,models/players/Legoman/BikerA2.tga
2716 U_RArm,models/players/Legoman/BikerA1.tga
2717 U_LArm,models/players/Legoman/BikerA1.tga
2718 U_armor,common/nodraw
2719 U_sword,common/nodraw
2720 U_shield,common/nodraw
2721 U_homb,common/nodraw
2722 U_backpack,common/nodraw
2723 U_colcha,common/nodraw
2728 memset(word, 0, sizeof(word));
2729 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2731 // If it's the first file we parse
2732 if (skinfile == NULL)
2734 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2739 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2740 skinfile = skinfile->next;
2742 skinfile->next = NULL;
2744 for(line = 0;;line++)
2747 if (!COM_ParseToken_QuakeC(&data, true))
2749 if (!strcmp(com_token, "\n"))
2752 wordsoverflow = false;
2756 strlcpy(word[words++], com_token, sizeof (word[0]));
2758 wordsoverflow = true;
2760 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2763 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);
2766 // words is always >= 1
2767 if (!strcmp(word[0], "replace"))
2771 if (developer_loading.integer)
2772 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2773 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2774 skinfileitem->next = skinfile->items;
2775 skinfile->items = skinfileitem;
2776 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2777 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2780 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]);
2782 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2784 // tag name, like "tag_weapon,"
2785 // not used for anything (not even in Quake3)
2787 else if (words >= 2 && !strcmp(word[1], ","))
2789 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2790 if (developer_loading.integer)
2791 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2792 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2793 skinfileitem->next = skinfile->items;
2794 skinfile->items = skinfileitem;
2795 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2796 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2799 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);
2804 loadmodel->numskins = i;
2808 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2811 skinfileitem_t *skinfileitem, *nextitem;
2812 for (;skinfile;skinfile = next)
2814 next = skinfile->next;
2815 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2817 nextitem = skinfileitem->next;
2818 Mem_Free(skinfileitem);
2824 int Mod_CountSkinFiles(skinfile_t *skinfile)
2827 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2831 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2834 double isnap = 1.0 / snap;
2835 for (i = 0;i < numvertices*numcomponents;i++)
2836 vertices[i] = floor(vertices[i]*isnap)*snap;
2839 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2841 int i, outtriangles;
2842 float edgedir1[3], edgedir2[3], temp[3];
2843 // a degenerate triangle is one with no width (thickness, surface area)
2844 // these are characterized by having all 3 points colinear (along a line)
2845 // or having two points identical
2846 // the simplest check is to calculate the triangle's area
2847 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2849 // calculate first edge
2850 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2851 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2852 CrossProduct(edgedir1, edgedir2, temp);
2853 if (VectorLength2(temp) < 0.001f)
2854 continue; // degenerate triangle (no area)
2855 // valid triangle (has area)
2856 VectorCopy(inelement3i, outelement3i);
2860 return outtriangles;
2863 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2866 int firstvertex, lastvertex;
2867 if (numelements > 0 && elements)
2869 firstvertex = lastvertex = elements[0];
2870 for (i = 1;i < numelements;i++)
2873 firstvertex = min(firstvertex, e);
2874 lastvertex = max(lastvertex, e);
2878 firstvertex = lastvertex = 0;
2879 if (firstvertexpointer)
2880 *firstvertexpointer = firstvertex;
2881 if (lastvertexpointer)
2882 *lastvertexpointer = lastvertex;
2885 void Mod_SetDrawSkyAndWater(model_t* mod)
2888 uint64_t basematerialflags = 0;
2889 // by default assume there is no sky or water used in this model
2890 mod->DrawSky = NULL;
2891 mod->DrawAddWaterPlanes = NULL;
2892 // combine all basematerialflags observed in the submodelsurfaces range, then check for special flags
2893 for (j = mod->submodelsurfaces_start; j < mod->submodelsurfaces_end; j++)
2894 if (mod->data_surfaces[j].texture)
2895 basematerialflags |= mod->data_surfaces[j].texture->basematerialflags;
2896 if (basematerialflags & MATERIALFLAG_SKY)
2897 mod->DrawSky = R_Mod_DrawSky;
2898 if (basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
2899 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
2902 typedef struct Mod_MakeSortedSurfaces_qsortsurface_s
2905 q3deffect_t* effect;
2907 rtexture_t* lightmaptexture;
2909 Mod_MakeSortedSurfaces_qsortsurface_t;
2911 static int Mod_MakeSortedSurfaces_qsortfunc(const void *a, const void *b)
2913 const Mod_MakeSortedSurfaces_qsortsurface_t* l = (Mod_MakeSortedSurfaces_qsortsurface_t*)a;
2914 const Mod_MakeSortedSurfaces_qsortsurface_t* r = (Mod_MakeSortedSurfaces_qsortsurface_t*)b;
2915 if (l->effect < r->effect)
2917 if (l->effect > r->effect)
2919 if (l->texture < r->texture)
2921 if (l->texture > r->texture)
2923 if (l->lightmaptexture < r->lightmaptexture)
2925 if (l->lightmaptexture > r->lightmaptexture)
2927 if (l->surfaceindex < r->surfaceindex)
2929 if (l->surfaceindex > r->surfaceindex)
2934 void Mod_MakeSortedSurfaces(model_t *mod)
2936 // make an optimal set of texture-sorted batches to draw...
2938 Mod_MakeSortedSurfaces_qsortsurface_t *info = (Mod_MakeSortedSurfaces_qsortsurface_t*)R_FrameData_Alloc(mod->num_surfaces * sizeof(*info));
2939 if (!mod->modelsurfaces_sorted)
2940 mod->modelsurfaces_sorted = (int *) Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*mod->modelsurfaces_sorted));
2941 // the goal is to sort by submodel (can't change which submodel a surface belongs to), and then by effects and textures
2942 for (j = 0; j < mod->num_surfaces; j++)
2944 info[j].surfaceindex = j;
2945 info[j].effect = mod->data_surfaces[j].effect;
2946 info[j].texture = mod->data_surfaces[j].texture;
2947 info[j].lightmaptexture = mod->data_surfaces[j].lightmaptexture;
2949 for (k = 0; k < mod->brush.numsubmodels; k++)
2950 if (mod->brush.submodels[k]->submodelsurfaces_end > mod->brush.submodels[k]->submodelsurfaces_start + 1)
2951 qsort(info + mod->brush.submodels[k]->submodelsurfaces_start, (size_t)mod->brush.submodels[k]->submodelsurfaces_end - mod->brush.submodels[k]->submodelsurfaces_start, sizeof(*info), Mod_MakeSortedSurfaces_qsortfunc);
2952 for (j = 0; j < mod->num_surfaces; j++)
2953 mod->modelsurfaces_sorted[j] = info[j].surfaceindex;
2956 void Mod_BuildVBOs(void)
2958 if(cls.state == ca_dedicated)
2961 if (!loadmodel->surfmesh.num_vertices)
2964 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2967 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2969 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2971 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2972 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2977 // upload short indices as a buffer
2978 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2979 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);
2981 // upload int indices as a buffer
2982 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2983 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);
2985 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2986 // we put several vertex data streams in the same buffer
2987 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2992 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2993 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2994 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2995 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2996 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2997 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2998 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2999 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3000 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3001 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
3002 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3003 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3004 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3005 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3006 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3007 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3008 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
3009 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3010 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3011 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3012 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3013 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3014 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3015 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3016 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3017 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3018 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3019 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3024 extern cvar_t mod_obj_orientation;
3025 static void Mod_Decompile_OBJ(model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3027 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3029 const char *texname;
3031 const float *v, *vn, *vt;
3033 size_t outbufferpos = 0;
3034 size_t outbuffermax = 0x100000;
3035 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3036 const msurface_t *surface;
3037 const int maxtextures = 256;
3038 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3041 // construct the mtllib file
3042 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3045 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3048 countvertices += surface->num_vertices;
3049 countfaces += surface->num_triangles;
3050 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3051 for (textureindex = 0;textureindex < counttextures;textureindex++)
3052 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3054 if (textureindex < counttextures)
3055 continue; // already wrote this material entry
3056 if (textureindex >= maxtextures)
3057 continue; // just a precaution
3058 textureindex = counttextures++;
3059 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3060 if (outbufferpos >= outbuffermax >> 1)
3063 oldbuffer = outbuffer;
3064 outbuffer = (char *) Z_Malloc(outbuffermax);
3065 memcpy(outbuffer, oldbuffer, outbufferpos);
3068 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");
3073 // write the mtllib file
3074 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3076 // construct the obj file
3078 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);
3082 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)
3084 if (outbufferpos >= outbuffermax >> 1)
3087 oldbuffer = outbuffer;
3088 outbuffer = (char *) Z_Malloc(outbuffermax);
3089 memcpy(outbuffer, oldbuffer, outbufferpos);
3092 if(mod_obj_orientation.integer)
3093 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]);
3095 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]);
3100 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3102 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3105 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3106 for (surfaceindex = submodel->submodelsurfaces_start;surfaceindex < submodel->submodelsurfaces_end;surfaceindex++)
3108 surface = model->data_surfaces + surfaceindex;
3109 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3112 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3114 if (outbufferpos >= outbuffermax >> 1)
3117 oldbuffer = outbuffer;
3118 outbuffer = (char *) Z_Malloc(outbuffermax);
3119 memcpy(outbuffer, oldbuffer, outbufferpos);
3125 if(mod_obj_orientation.integer)
3126 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);
3128 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);
3135 // write the obj file
3136 FS_WriteFile(filename, outbuffer, outbufferpos);
3140 Z_Free(texturenames);
3143 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3146 static void Mod_Decompile_SMD(model_t *model, const char *filename, int firstpose, int numposes, qbool writetriangles)
3148 int countnodes = 0, counttriangles = 0, countframes = 0;
3156 size_t outbufferpos = 0;
3157 size_t outbuffermax = 0x100000;
3158 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3159 const msurface_t *surface;
3160 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3163 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3165 if (outbufferpos >= outbuffermax >> 1)
3168 oldbuffer = outbuffer;
3169 outbuffer = (char *) Z_Malloc(outbuffermax);
3170 memcpy(outbuffer, oldbuffer, outbufferpos);
3174 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3178 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3181 for (poseindex = 0;poseindex < numposes;poseindex++)
3184 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3187 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3191 matrix4x4_t posematrix;
3192 if (outbufferpos >= outbuffermax >> 1)
3195 oldbuffer = outbuffer;
3196 outbuffer = (char *) Z_Malloc(outbuffermax);
3197 memcpy(outbuffer, oldbuffer, outbufferpos);
3201 // strangely the smd angles are for a transposed matrix, so we
3202 // have to generate a transposed matrix, then convert that...
3203 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3204 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3205 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3206 if (angles[0] >= 180) angles[0] -= 360;
3207 if (angles[1] >= 180) angles[1] -= 360;
3208 if (angles[2] >= 180) angles[2] -= 360;
3212 float a = DEG2RAD(angles[ROLL]);
3213 float b = DEG2RAD(angles[PITCH]);
3214 float c = DEG2RAD(angles[YAW]);
3215 float cy, sy, cp, sp, cr, sr;
3217 // smd matrix construction, for comparing
3228 test[1][0] = sr*sp*cy+cr*-sy;
3229 test[1][1] = sr*sp*sy+cr*cy;
3231 test[2][0] = (cr*sp*cy+-sr*-sy);
3232 test[2][1] = (cr*sp*sy+-sr*cy);
3234 test[3][0] = pose[9];
3235 test[3][1] = pose[10];
3236 test[3][2] = pose[11];
3239 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]));
3244 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3249 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3252 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3254 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3257 if (outbufferpos >= outbuffermax >> 1)
3260 oldbuffer = outbuffer;
3261 outbuffer = (char *) Z_Malloc(outbuffermax);
3262 memcpy(outbuffer, oldbuffer, outbufferpos);
3265 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3268 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3270 const int index = e[2-cornerindex];
3271 const float *v = model->surfmesh.data_vertex3f + index * 3;
3272 const float *vn = model->surfmesh.data_normal3f + index * 3;
3273 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3274 const int b = model->surfmesh.blends[index];
3275 if (b < model->num_bones)
3276 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]);
3279 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3280 const unsigned char *wi = w->index;
3281 const unsigned char *wf = w->influence;
3282 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);
3283 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);
3284 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);
3285 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]);
3292 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3297 FS_WriteFile(filename, outbuffer, outbufferpos);
3300 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3307 decompiles a model to editable files
3310 static void Mod_Decompile_f(cmd_state_t *cmd)
3312 int i, j, k, l, first, count;
3314 char inname[MAX_QPATH];
3315 char outname[MAX_QPATH];
3316 char mtlname[MAX_QPATH];
3317 char basename[MAX_QPATH];
3318 char animname[MAX_QPATH];
3319 char animname2[MAX_QPATH];
3320 char zymtextbuffer[16384];
3321 char dpmtextbuffer[16384];
3322 char framegroupstextbuffer[16384];
3323 int zymtextsize = 0;
3324 int dpmtextsize = 0;
3325 int framegroupstextsize = 0;
3328 if (Cmd_Argc(cmd) != 2)
3330 Con_Print("usage: modeldecompile <filename>\n");
3334 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3335 FS_StripExtension(inname, basename, sizeof(basename));
3337 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3340 Con_Print("No such model\n");
3343 if (mod->brush.submodel)
3345 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3346 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3347 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3350 if (!mod->surfmesh.num_triangles)
3352 Con_Print("Empty model (or sprite)\n");
3356 // export OBJ if possible (not on sprites)
3357 if (mod->surfmesh.num_triangles)
3359 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3360 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3361 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3364 // export SMD if possible (only for skeletal models)
3365 if (mod->surfmesh.num_triangles && mod->num_bones)
3367 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3368 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3369 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3370 if (l > 0) zymtextsize += l;
3371 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3372 if (l > 0) dpmtextsize += l;
3373 for (i = 0;i < mod->numframes;i = j)
3375 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3376 first = mod->animscenes[i].firstframe;
3377 if (mod->animscenes[i].framecount > 1)
3380 count = mod->animscenes[i].framecount;
3386 // check for additional frames with same name
3387 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3388 if(animname[l] < '0' || animname[l] > '9')
3390 if(k > 0 && animname[k-1] == '_')
3393 count = mod->num_poses - first;
3394 for (j = i + 1;j < mod->numframes;j++)
3396 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3397 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3398 if(animname2[l] < '0' || animname2[l] > '9')
3400 if(k > 0 && animname[k-1] == '_')
3403 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3405 count = mod->animscenes[j].firstframe - first;
3409 // if it's only one frame, use the original frame name
3411 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3414 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3415 Mod_Decompile_SMD(mod, outname, first, count, false);
3416 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3418 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3419 if (l > 0) zymtextsize += l;
3421 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3423 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3424 if (l > 0) dpmtextsize += l;
3426 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3428 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3429 if (l > 0) framegroupstextsize += l;
3433 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3435 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3436 if (framegroupstextsize)
3437 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3441 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3444 memset(state, 0, sizeof(*state));
3445 state->width = width;
3446 state->height = height;
3447 state->currentY = 0;
3448 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3449 for (y = 0;y < state->height;y++)
3451 state->rows[y].currentX = 0;
3452 state->rows[y].rowY = -1;
3456 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3459 state->currentY = 0;
3460 for (y = 0;y < state->height;y++)
3462 state->rows[y].currentX = 0;
3463 state->rows[y].rowY = -1;
3467 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3470 Mem_Free(state->rows);
3471 memset(state, 0, sizeof(*state));
3474 qbool Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3476 mod_alloclightmap_row_t *row;
3479 row = state->rows + blockheight;
3480 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3482 if (state->currentY + blockheight <= state->height)
3484 // use the current allocation position
3485 row->rowY = state->currentY;
3487 state->currentY += blockheight;
3491 // find another position
3492 for (y = blockheight;y < state->height;y++)
3494 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3496 row = state->rows + y;
3500 if (y == state->height)
3505 *outx = row->currentX;
3506 row->currentX += blockwidth;
3511 typedef struct lightmapsample_s
3515 float *vertex_color;
3516 unsigned char *lm_bgr;
3517 unsigned char *lm_dir;
3521 typedef struct lightmapvertex_s
3526 float texcoordbase[2];
3527 float texcoordlightmap[2];
3528 float lightcolor[4];
3532 typedef struct lightmaptriangle_s
3540 // 2D modelspace coordinates of min corner
3541 // snapped to lightmap grid but not in grid coordinates
3543 // 2D modelspace to lightmap coordinate scale
3551 typedef struct lightmaplight_s
3562 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3564 #define MAX_LIGHTMAPSAMPLES 64
3565 static int mod_generatelightmaps_numoffsets[3];
3566 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3568 static int mod_generatelightmaps_numlights;
3569 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3571 extern cvar_t r_shadow_lightattenuationdividebias;
3572 extern cvar_t r_shadow_lightattenuationlinearscale;
3574 static void Mod_GenerateLightmaps_LightPoint(model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3579 float relativepoint[3];
3586 float lightorigin[3];
3590 float lightcolor[3];
3592 for (i = 0;i < 5*3;i++)
3594 for (index = 0;;index++)
3596 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3601 lightradius2 = lightradius * lightradius;
3602 VectorSubtract(lightorigin, pos, relativepoint);
3603 dist2 = VectorLength2(relativepoint);
3604 if (dist2 >= lightradius2)
3606 lightiradius = 1.0f / lightradius;
3607 dist = sqrt(dist2) * lightiradius;
3608 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3609 if (intensity <= 0.0f)
3611 if (model && model->TraceLine)
3613 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3614 if (trace.fraction < 1)
3617 // scale down intensity to add to both ambient and diffuse
3618 //intensity *= 0.5f;
3619 VectorNormalize(relativepoint);
3620 VectorScale(lightcolor, intensity, color);
3621 VectorMA(sample , 0.5f , color, sample );
3622 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3623 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3624 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3625 // calculate a weighted average light direction as well
3626 intensity *= VectorLength(color);
3627 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3629 // calculate the direction we'll use to reduce the sample to a directional light source
3630 VectorCopy(sample + 12, dir);
3631 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3632 VectorNormalize(dir);
3633 // extract the diffuse color along the chosen direction and scale it
3634 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3635 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3636 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3637 // subtract some of diffuse from ambient
3638 VectorMA(sample, -0.333f, diffuse, ambient);
3639 // store the normalized lightdir
3640 VectorCopy(dir, lightdir);
3643 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3647 const msurface_t *surface;
3648 const float *vertex3f = model->surfmesh.data_vertex3f;
3649 const int *element3i = model->surfmesh.data_element3i;
3652 for (surfaceindex = model->submodelsurfaces_start;surfaceindex < model->submodelsurfaces_end;surfaceindex++)
3654 surface = model->data_surfaces + surfaceindex;
3655 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3657 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3659 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3661 VectorCopy(vertex3f + 3*e[0], v2[0]);
3662 VectorCopy(vertex3f + 3*e[1], v2[1]);
3663 VectorCopy(vertex3f + 3*e[2], v2[2]);
3664 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3669 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model_t *model, lightmaplight_t *lightinfo)
3671 int maxnodes = 1<<14;
3672 svbsp_node_t *nodes;
3677 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3678 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3679 VectorCopy(lightinfo->origin, origin);
3680 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3683 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3684 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3685 if (svbsp.ranoutofnodes)
3688 if (maxnodes > 1<<22)
3694 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3699 if (svbsp.numnodes > 0)
3701 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3702 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3703 lightinfo->svbsp = svbsp;
3708 static void Mod_GenerateLightmaps_CreateLights(model_t *model)
3712 lightmaplight_t *lightinfo;
3716 mod_generatelightmaps_numlights = 0;
3717 for (index = 0;;index++)
3719 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3723 mod_generatelightmaps_numlights++;
3725 if (mod_generatelightmaps_numlights > 0)
3727 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3728 lightinfo = mod_generatelightmaps_lightinfo;
3729 for (index = 0;;index++)
3731 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3738 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3740 lightinfo->iradius = 1.0f / lightinfo->radius;
3741 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3742 // TODO: compute svbsp
3743 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3747 static void Mod_GenerateLightmaps_DestroyLights(model_t *model)
3750 if (mod_generatelightmaps_lightinfo)
3752 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3753 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3754 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3755 Mem_Free(mod_generatelightmaps_lightinfo);
3757 mod_generatelightmaps_lightinfo = NULL;
3758 mod_generatelightmaps_numlights = 0;
3761 static qbool Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3763 const svbsp_node_t *node;
3764 const svbsp_node_t *nodes = svbsp->nodes;
3769 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3771 return num == -1; // true if empty, false if solid (shadowed)
3774 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3777 float relativepoint[3];
3786 const lightmaplight_t *lightinfo;
3788 for (i = 0;i < 5*3;i++)
3790 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3792 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3793 VectorSubtract(lightinfo->origin, pos, relativepoint);
3794 // don't accept light from behind a surface, it causes bad shading
3795 if (normal && DotProduct(relativepoint, normal) <= 0)
3797 dist2 = VectorLength2(relativepoint);
3798 if (dist2 >= lightinfo->radius2)
3800 dist = sqrt(dist2) * lightinfo->iradius;
3801 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3804 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3808 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3810 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3812 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3815 // for light grid we'd better check visibility of the offset point
3816 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3817 if (trace.fraction < 1)
3818 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3821 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3826 // scale intensity according to how many rays succeeded
3827 // we know one test is valid, half of the rest will fail...
3828 //if (normal && tests > 1)
3829 // intensity *= (tests - 1.0f) / tests;
3830 intensity *= (float)hits / tests;
3832 // scale down intensity to add to both ambient and diffuse
3833 //intensity *= 0.5f;
3834 VectorNormalize(relativepoint);
3835 VectorScale(lightinfo->color, intensity, color);
3836 VectorMA(sample , 0.5f , color, sample );
3837 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3838 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3839 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3840 // calculate a weighted average light direction as well
3841 intensity *= VectorLength(color);
3842 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3846 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3852 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3853 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3854 VectorCopy(sample + 12, dir);
3855 VectorNormalize(dir);
3856 //VectorAdd(dir, normal, dir);
3857 //VectorNormalize(dir);
3858 f = DotProduct(dir, normal);
3859 f = max(0, f) * 255.0f;
3860 VectorScale(sample, f, color);
3861 //VectorCopy(normal, dir);
3862 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3863 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3864 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3865 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3867 lm_dir[0] = (unsigned char)dir[2];
3868 lm_dir[1] = (unsigned char)dir[1];
3869 lm_dir[2] = (unsigned char)dir[0];
3873 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3876 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3877 VectorCopy(sample, vertex_color);
3880 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3886 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3887 // calculate the direction we'll use to reduce the sample to a directional light source
3888 VectorCopy(sample + 12, dir);
3889 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3890 VectorNormalize(dir);
3891 // extract the diffuse color along the chosen direction and scale it
3892 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3893 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3894 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3895 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3896 VectorScale(sample, 127.5f, ambient);
3897 VectorMA(ambient, -0.333f, diffuse, ambient);
3898 // encode to the grid format
3899 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3900 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3901 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3902 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3903 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3904 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3905 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3906 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3907 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));}
3910 static void Mod_GenerateLightmaps_InitSampleOffsets(model_t *model)
3915 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3916 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3917 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3918 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3919 radius[0] = mod_generatelightmaps_lightmapradius.value;
3920 radius[1] = mod_generatelightmaps_vertexradius.value;
3921 radius[2] = mod_generatelightmaps_gridradius.value;
3922 for (i = 0;i < 3;i++)
3924 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3927 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3932 static void Mod_GenerateLightmaps_DestroyLightmaps(model_t *model)
3934 msurface_t *surface;
3937 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3939 surface = model->data_surfaces + surfaceindex;
3940 surface->lightmaptexture = NULL;
3941 surface->deluxemaptexture = NULL;
3943 if (model->brushq3.data_lightmaps)
3945 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3946 if (model->brushq3.data_lightmaps[i])
3947 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3948 Mem_Free(model->brushq3.data_lightmaps);
3949 model->brushq3.data_lightmaps = NULL;
3951 if (model->brushq3.data_deluxemaps)
3953 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3954 if (model->brushq3.data_deluxemaps[i])
3955 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3956 Mem_Free(model->brushq3.data_deluxemaps);
3957 model->brushq3.data_deluxemaps = NULL;
3961 static void Mod_GenerateLightmaps_UnweldTriangles(model_t *model)
3963 msurface_t *surface;
3969 surfmesh_t oldsurfmesh;
3971 unsigned char *data;
3972 oldsurfmesh = model->surfmesh;
3973 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3974 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3976 size += model->surfmesh.num_vertices * sizeof(float[3]);
3977 size += model->surfmesh.num_vertices * sizeof(float[3]);
3978 size += model->surfmesh.num_vertices * sizeof(float[3]);
3979 size += model->surfmesh.num_vertices * sizeof(float[3]);
3980 size += model->surfmesh.num_vertices * sizeof(float[2]);
3981 size += model->surfmesh.num_vertices * sizeof(float[2]);
3982 size += model->surfmesh.num_vertices * sizeof(float[4]);
3983 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3984 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3985 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3986 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3987 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3988 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3989 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3990 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3991 if (model->surfmesh.num_vertices > 65536)
3992 model->surfmesh.data_element3s = NULL;
3994 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3995 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3996 model->surfmesh.data_element3i_indexbuffer = NULL;
3997 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3998 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3999 model->surfmesh.data_element3s_indexbuffer = NULL;
4000 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
4001 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
4002 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
4003 model->surfmesh.data_svector3f_vertexbuffer = NULL;
4004 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
4005 model->surfmesh.data_normal3f_vertexbuffer = NULL;
4006 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
4007 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
4008 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
4009 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
4010 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
4012 // convert all triangles to unique vertex data
4014 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4016 surface = model->data_surfaces + surfaceindex;
4017 surface->num_firstvertex = outvertexindex;
4018 surface->num_vertices = surface->num_triangles*3;
4019 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4020 for (i = 0;i < surface->num_triangles*3;i++)
4023 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4024 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4025 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4026 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4027 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4028 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4029 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4030 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4031 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4032 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4033 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4034 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4035 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4036 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4037 if (oldsurfmesh.data_texcoordlightmap2f)
4039 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4040 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4042 if (oldsurfmesh.data_lightmapcolor4f)
4044 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4045 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4046 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4047 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4050 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4051 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4055 if (model->surfmesh.data_element3s)
4056 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4057 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4059 // find and update all submodels to use this new surfmesh data
4060 for (i = 0;i < model->brush.numsubmodels;i++)
4061 model->brush.submodels[i]->surfmesh = model->surfmesh;
4064 static void Mod_GenerateLightmaps_CreateTriangleInformation(model_t *model)
4066 msurface_t *surface;
4072 lightmaptriangle_t *triangle;
4073 // generate lightmap triangle structs
4074 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4075 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4077 surface = model->data_surfaces + surfaceindex;
4078 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4079 for (i = 0;i < surface->num_triangles;i++)
4081 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4082 triangle->triangleindex = surface->num_firsttriangle+i;
4083 triangle->surfaceindex = surfaceindex;
4084 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4085 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4086 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4087 // calculate bounds of triangle
4088 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4089 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4090 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4091 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4092 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4093 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4094 // pick an axial projection based on the triangle normal
4095 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4097 if (fabs(normal[1]) > fabs(normal[axis]))
4099 if (fabs(normal[2]) > fabs(normal[axis]))
4101 triangle->axis = axis;
4106 static void Mod_GenerateLightmaps_DestroyTriangleInformation(model_t *model)
4108 if (mod_generatelightmaps_lightmaptriangles)
4109 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4110 mod_generatelightmaps_lightmaptriangles = NULL;
4113 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4115 static void Mod_GenerateLightmaps_CreateLightmaps(model_t *model)
4117 msurface_t *surface;
4131 float trianglenormal[3];
4132 float samplecenter[3];
4133 float samplenormal[3];
4139 float lmscalepixels;
4142 float lm_basescalepixels;
4143 int lm_borderpixels;
4147 lightmaptriangle_t *triangle;
4148 unsigned char *lightmappixels;
4149 unsigned char *deluxemappixels;
4150 mod_alloclightmap_state_t lmstate;
4153 // generate lightmap projection information for all triangles
4154 if (model->texturepool == NULL)
4155 model->texturepool = R_AllocTexturePool();
4156 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4157 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4158 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4159 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4160 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4162 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4164 surface = model->data_surfaces + surfaceindex;
4165 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4166 lmscalepixels = lm_basescalepixels;
4167 for (retry = 0;retry < 30;retry++)
4169 // after a couple failed attempts, degrade quality to make it fit
4171 lmscalepixels *= 0.5f;
4172 for (i = 0;i < surface->num_triangles;i++)
4174 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4175 triangle->lightmapindex = lightmapnumber;
4176 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4177 // pick two planar axes for projection
4178 // lightmap coordinates here are in pixels
4179 // lightmap projections are snapped to pixel grid explicitly, such
4180 // that two neighboring triangles sharing an edge and projection
4181 // axis will have identical sample spacing along their shared edge
4183 for (j = 0;j < 3;j++)
4185 if (j == triangle->axis)
4187 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4188 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4189 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4190 triangle->lmbase[k] = lmmins/lmscalepixels;
4191 triangle->lmscale[k] = lmscalepixels;
4194 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4197 // if all fit in this texture, we're done with this surface
4198 if (i == surface->num_triangles)
4200 // if we haven't maxed out the lightmap size yet, we retry the
4201 // entire surface batch...
4202 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4204 lm_texturesize *= 2;
4207 Mod_AllocLightmap_Free(&lmstate);
4208 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4211 // if we have maxed out the lightmap size, and this triangle does
4212 // not fit in the same texture as the rest of the surface, we have
4213 // to retry the entire surface in a new texture (can only use one)
4214 // with multiple retries, the lightmap quality degrades until it
4215 // fits (or gives up)
4216 if (surfaceindex > 0)
4218 Mod_AllocLightmap_Reset(&lmstate);
4222 Mod_AllocLightmap_Free(&lmstate);
4224 // now put triangles together into lightmap textures, and do not allow
4225 // triangles of a surface to go into different textures (as that would
4226 // require rewriting the surface list)
4227 model->brushq3.deluxemapping_modelspace = true;
4228 model->brushq3.deluxemapping = true;
4229 model->brushq3.num_mergedlightmaps = lightmapnumber;
4230 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4231 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4232 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4233 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4234 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4236 surface = model->data_surfaces + surfaceindex;
4237 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4238 for (i = 0;i < surface->num_triangles;i++)
4240 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4241 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4242 VectorNormalize(trianglenormal);
4243 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4244 axis = triangle->axis;
4245 axis1 = axis == 0 ? 1 : 0;
4246 axis2 = axis == 2 ? 1 : 2;
4247 lmiscale[0] = 1.0f / triangle->lmscale[0];
4248 lmiscale[1] = 1.0f / triangle->lmscale[1];
4249 if (trianglenormal[axis] < 0)
4250 VectorNegate(trianglenormal, trianglenormal);
4251 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4252 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4253 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4254 for (j = 0;j < 3;j++)
4256 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4257 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4258 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4260 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4261 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4262 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4263 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]);
4273 forward[1] = 1.0f / triangle->lmscale[0];
4277 left[2] = 1.0f / triangle->lmscale[1];
4282 origin[1] = triangle->lmbase[0];
4283 origin[2] = triangle->lmbase[1];
4286 forward[0] = 1.0f / triangle->lmscale[0];
4291 left[2] = 1.0f / triangle->lmscale[1];
4295 origin[0] = triangle->lmbase[0];
4297 origin[2] = triangle->lmbase[1];
4300 forward[0] = 1.0f / triangle->lmscale[0];
4304 left[1] = 1.0f / triangle->lmscale[1];
4309 origin[0] = triangle->lmbase[0];
4310 origin[1] = triangle->lmbase[1];
4314 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4316 #define LM_DIST_EPSILON (1.0f / 32.0f)
4317 for (y = 0;y < triangle->lmsize[1];y++)
4319 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4320 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4322 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4323 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4324 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4325 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4326 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4332 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4334 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);
4335 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);
4339 Mem_Free(lightmappixels);
4340 if (deluxemappixels)
4341 Mem_Free(deluxemappixels);
4343 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4345 surface = model->data_surfaces + surfaceindex;
4346 if (!surface->num_triangles)
4348 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4349 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4350 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4351 surface->lightmapinfo = NULL;
4354 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4355 model->brushq1.lightdata = NULL;
4356 model->brushq1.lightmapupdateflags = NULL;
4357 model->brushq1.firstrender = false;
4358 model->brushq1.num_lightstyles = 0;
4359 model->brushq1.data_lightstyleinfo = NULL;
4360 for (i = 0;i < model->brush.numsubmodels;i++)
4362 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4363 model->brush.submodels[i]->brushq1.firstrender = false;
4364 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4365 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4369 static void Mod_GenerateLightmaps_UpdateVertexColors(model_t *model)
4372 for (i = 0;i < model->surfmesh.num_vertices;i++)
4373 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4376 static void Mod_GenerateLightmaps_UpdateLightGrid(model_t *model)
4383 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4385 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4386 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4388 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4389 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4391 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4392 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4398 extern cvar_t mod_q3bsp_nolightmaps;
4399 static void Mod_GenerateLightmaps(model_t *model)
4401 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4402 model_t *oldloadmodel = loadmodel;
4405 Mod_GenerateLightmaps_InitSampleOffsets(model);
4406 Mod_GenerateLightmaps_DestroyLightmaps(model);
4407 Mod_GenerateLightmaps_UnweldTriangles(model);
4408 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4409 Mod_GenerateLightmaps_CreateLights(model);
4410 if(!mod_q3bsp_nolightmaps.integer)
4411 Mod_GenerateLightmaps_CreateLightmaps(model);
4412 Mod_GenerateLightmaps_UpdateVertexColors(model);
4413 Mod_GenerateLightmaps_UpdateLightGrid(model);
4414 Mod_GenerateLightmaps_DestroyLights(model);
4415 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4417 loadmodel = oldloadmodel;
4420 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4422 if (Cmd_Argc(cmd) != 1)
4424 Con_Printf("usage: mod_generatelightmaps\n");
4429 Con_Printf("no worldmodel loaded\n");
4432 Mod_GenerateLightmaps(cl.worldmodel);
4435 void Mod_Mesh_Create(model_t *mod, const char *name)
4437 memset(mod, 0, sizeof(*mod));
4438 strlcpy(mod->name, name, sizeof(mod->name));
4439 mod->mempool = Mem_AllocPool(name, 0, NULL);
4440 mod->texturepool = R_AllocTexturePool();
4441 mod->Draw = R_Mod_Draw;
4442 mod->DrawDepth = R_Mod_DrawDepth;
4443 mod->DrawDebug = R_Mod_DrawDebug;
4444 mod->DrawPrepass = R_Mod_DrawPrepass;
4445 mod->GetLightInfo = R_Mod_GetLightInfo;
4446 mod->DrawShadowMap = R_Mod_DrawShadowMap;
4447 mod->DrawLight = R_Mod_DrawLight;
4450 void Mod_Mesh_Destroy(model_t *mod)
4452 Mod_UnloadModel(mod);
4455 // resets the mesh model to have no geometry to render, ready for a new frame -
4456 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4457 void Mod_Mesh_Reset(model_t *mod)
4459 mod->num_surfaces = 0;
4460 mod->surfmesh.num_vertices = 0;
4461 mod->surfmesh.num_triangles = 0;
4462 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4463 mod->DrawSky = NULL; // will be set if a texture needs it
4464 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4467 texture_t *Mod_Mesh_GetTexture(model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4471 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4472 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4473 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4475 if (mod->max_textures <= mod->num_textures)
4477 texture_t *oldtextures = mod->data_textures;
4478 mod->max_textures = max(mod->max_textures * 2, 1024);
4479 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4480 // update the pointers
4481 for (i = 0; i < mod->num_surfaces; i++)
4482 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4484 t = &mod->data_textures[mod->num_textures++];
4485 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4486 t->mesh_drawflag = drawflag;
4487 t->mesh_defaulttexflags = defaulttexflags;
4488 t->mesh_defaultmaterialflags = defaultmaterialflags;
4489 switch (defaultdrawflags & DRAWFLAG_MASK)
4491 case DRAWFLAG_ADDITIVE:
4492 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4493 t->currentmaterialflags = t->basematerialflags;
4495 case DRAWFLAG_MODULATE:
4496 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4497 t->currentmaterialflags = t->basematerialflags;
4498 t->customblendfunc[0] = GL_DST_COLOR;
4499 t->customblendfunc[1] = GL_ZERO;
4501 case DRAWFLAG_2XMODULATE:
4502 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4503 t->currentmaterialflags = t->basematerialflags;
4504 t->customblendfunc[0] = GL_DST_COLOR;
4505 t->customblendfunc[1] = GL_SRC_COLOR;
4507 case DRAWFLAG_SCREEN:
4508 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4509 t->currentmaterialflags = t->basematerialflags;
4510 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4511 t->customblendfunc[1] = GL_ONE;
4519 msurface_t *Mod_Mesh_AddSurface(model_t *mod, texture_t *tex, qbool batchwithprevioussurface)
4522 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4523 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4524 return mod->data_surfaces + mod->num_surfaces - 1;
4525 // create new surface
4526 if (mod->max_surfaces == mod->num_surfaces)
4528 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4529 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4530 mod->modelsurfaces_sorted = (int *)Mem_Realloc(mod->mempool, mod->modelsurfaces_sorted, mod->max_surfaces * sizeof(*mod->modelsurfaces_sorted));
4532 surf = mod->data_surfaces + mod->num_surfaces;
4533 mod->num_surfaces++;
4534 memset(surf, 0, sizeof(*surf));
4535 surf->texture = tex;
4536 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4537 surf->num_firstvertex = mod->surfmesh.num_vertices;
4538 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4539 mod->DrawSky = R_Mod_DrawSky;
4540 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4541 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
4545 static void Mod_Mesh_RebuildHashTable(model_t *mod, msurface_t *surf)
4547 int hashindex, h, vnum, mask;
4548 surfmesh_t *mesh = &mod->surfmesh;
4550 // rebuild the hash table
4551 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4552 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4553 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4554 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4555 mask = mod->surfmesh.num_vertexhashsize - 1;
4556 // no need to hash the vertices for the entire model, the latest surface will suffice.
4557 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4559 // this uses prime numbers intentionally for computing the hash
4560 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;
4561 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4562 ; // just iterate until we find the terminator
4563 mesh->data_vertexhash[h] = vnum;
4567 void Mod_Mesh_CheckResize_Vertex(model_t *mod, msurface_t *surf)
4569 surfmesh_t *mesh = &mod->surfmesh;
4570 if (mesh->max_vertices == mesh->num_vertices)
4572 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4573 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4574 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4575 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4576 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4577 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4578 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4579 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4580 Mod_Mesh_RebuildHashTable(mod, surf);
4584 int Mod_Mesh_AddVertex(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)
4587 surfmesh_t *mesh = &mod->surfmesh;
4589 // add the new vertex
4590 vnum = mesh->num_vertices++;
4591 if (surf->num_vertices > 0)
4593 if (surf->mins[0] > x) surf->mins[0] = x;
4594 if (surf->mins[1] > y) surf->mins[1] = y;
4595 if (surf->mins[2] > z) surf->mins[2] = z;
4596 if (surf->maxs[0] < x) surf->maxs[0] = x;
4597 if (surf->maxs[1] < y) surf->maxs[1] = y;
4598 if (surf->maxs[2] < z) surf->maxs[2] = z;
4602 VectorSet(surf->mins, x, y, z);
4603 VectorSet(surf->maxs, x, y, z);
4605 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4606 mesh->data_vertex3f[vnum * 3 + 0] = x;
4607 mesh->data_vertex3f[vnum * 3 + 1] = y;
4608 mesh->data_vertex3f[vnum * 3 + 2] = z;
4609 mesh->data_normal3f[vnum * 3 + 0] = nx;
4610 mesh->data_normal3f[vnum * 3 + 1] = ny;
4611 mesh->data_normal3f[vnum * 3 + 2] = nz;
4612 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4613 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4614 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4615 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4616 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4617 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4618 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4619 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4623 int Mod_Mesh_IndexForVertex(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)
4625 int hashindex, h, vnum, mask;
4626 surfmesh_t *mesh = &mod->surfmesh;
4628 Mod_Mesh_CheckResize_Vertex(mod, surf);
4630 mask = mod->surfmesh.num_vertexhashsize - 1;
4631 // this uses prime numbers intentionally for computing the hash
4632 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4633 // when possible find an identical vertex within the same surface and return it
4634 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4636 if (vnum >= surf->num_firstvertex
4637 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4638 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4639 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4640 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4641 && 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)
4644 vnum = Mod_Mesh_AddVertex(mod, surf, x, y, z, nx, ny, nz, s, t, u, v, r, g, b, a);
4645 mesh->data_vertexhash[h] = vnum;
4649 void Mod_Mesh_AddTriangle(model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4651 surfmesh_t *mesh = &mod->surfmesh;
4652 if (mesh->max_triangles == mesh->num_triangles)
4654 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4655 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4656 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4658 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4659 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4660 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4661 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4662 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4663 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4664 mesh->num_triangles++;
4665 surf->num_triangles++;
4668 static void Mod_Mesh_MakeSortedSurfaces(model_t *mod)
4672 unsigned char* included = (unsigned char *)R_FrameData_Alloc(mod->num_surfaces * sizeof(unsigned char));
4674 // build the sorted surfaces list properly to reduce material setup
4675 // this is easy because we're just sorting on texture and don't care about the order of textures
4676 mod->submodelsurfaces_start = 0;
4677 mod->submodelsurfaces_end = 0;
4678 for (i = 0; i < mod->num_surfaces; i++)
4680 for (i = 0; i < mod->num_surfaces; i++)
4684 tex = mod->data_surfaces[i].texture;
4685 // j = i is intentional
4686 for (j = i; j < mod->num_surfaces; j++)
4688 if (!included[j] && mod->data_surfaces[j].texture == tex)
4691 mod->modelsurfaces_sorted[mod->submodelsurfaces_end++] = j;
4697 static void Mod_Mesh_ComputeBounds(model_t *mod)
4700 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4702 if (mod->surfmesh.num_vertices > 0)
4704 // calculate normalmins/normalmaxs
4705 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4706 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4707 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4709 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4710 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4711 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4712 // expand bounds to include this vertex
4713 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4714 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4715 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4716 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4717 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4718 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4720 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4721 // (fast but less accurate than doing it per vertex)
4722 x2a = mod->normalmins[0] * mod->normalmins[0];
4723 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4724 y2a = mod->normalmins[1] * mod->normalmins[1];
4725 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4726 z2a = mod->normalmins[2] * mod->normalmins[2];
4727 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4731 yawradius = sqrt(x2 + y2);
4732 rotatedradius = sqrt(x2 + y2 + z2);
4733 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4734 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4735 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4736 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4737 mod->radius = rotatedradius;
4738 mod->radius2 = x2 + y2 + z2;
4742 VectorClear(mod->normalmins);
4743 VectorClear(mod->normalmaxs);
4744 VectorClear(mod->yawmins);
4745 VectorClear(mod->yawmaxs);
4746 VectorClear(mod->rotatedmins);
4747 VectorClear(mod->rotatedmaxs);
4753 void Mod_Mesh_Validate(model_t *mod)
4756 qbool warned = false;
4757 for (i = 0; i < mod->num_surfaces; i++)
4759 msurface_t *surf = mod->data_surfaces + i;
4760 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4761 int first = surf->num_firstvertex;
4762 int end = surf->num_firstvertex + surf->num_vertices;
4764 for (j = 0;j < surf->num_triangles * 3;j++)
4766 if (e[j] < first || e[j] >= end)
4769 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4777 static void Mod_Mesh_UploadDynamicBuffers(model_t *mod)
4779 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;
4780 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;
4781 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;
4782 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;
4783 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;
4784 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;
4785 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;
4786 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;
4787 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;
4788 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;
4789 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;
4792 void Mod_Mesh_Finalize(model_t *mod)
4794 if (gl_paranoid.integer)
4795 Mod_Mesh_Validate(mod);
4796 Mod_Mesh_ComputeBounds(mod);
4797 Mod_Mesh_MakeSortedSurfaces(mod);
4798 if(!r_refdef.draw2dstage)
4799 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);
4800 Mod_Mesh_UploadDynamicBuffers(mod);