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, "VBSP", 4, Mod_VBSP_Load},
59 {NULL, "ZYMOTICMODEL", 13, Mod_ZYMOTICMODEL_Load},
60 {NULL, "DARKPLACESMODEL", 16, Mod_DARKPLACESMODEL_Load},
61 {NULL, "PSKMODEL", 9, Mod_PSKMODEL_Load},
62 {NULL, "INTERQUAKEMODEL", 16, Mod_INTERQUAKEMODEL_Load},
63 {"map", NULL, 0, Mod_MAP_Load},
67 static mempool_t *mod_mempool;
68 static memexpandablearray_t models;
70 static mempool_t* q3shaders_mem;
71 typedef struct q3shader_hash_entry_s
74 struct q3shader_hash_entry_s* chain;
75 } q3shader_hash_entry_t;
76 #define Q3SHADER_HASH_SIZE 1021
77 typedef struct q3shader_data_s
79 memexpandablearray_t hash_entries;
80 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
81 memexpandablearray_t char_ptrs;
83 static q3shader_data_t* q3shader_data;
85 static void mod_start(void)
88 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
91 SCR_PushLoadingScreen("Loading models", 1.0);
93 for (i = 0;i < nummodels;i++)
94 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
97 for (i = 0;i < nummodels;i++)
98 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
101 SCR_PushLoadingScreen(mod->name, 1.0 / count);
102 Mod_LoadModel(mod, true, false);
103 SCR_PopLoadingScreen(false);
105 SCR_PopLoadingScreen(false);
108 static void mod_shutdown(void)
111 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
114 for (i = 0;i < nummodels;i++)
115 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
116 Mod_UnloadModel(mod);
119 Mod_Skeletal_FreeBuffers();
122 static void mod_newmap(void)
125 int i, j, k, l, surfacenum, ssize, tsize;
126 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
129 for (i = 0;i < nummodels;i++)
131 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
133 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
135 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
136 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
137 if (mod->data_textures[j].shaderpasses[l])
138 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
139 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
141 if (mod->brush.solidskyskinframe)
142 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
143 if (mod->brush.alphaskyskinframe)
144 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
148 if (!cl_stainmaps_clearonload.integer)
151 for (i = 0;i < nummodels;i++)
153 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
155 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
157 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
159 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
160 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
161 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
162 mod->brushq1.lightmapupdateflags[surfacenum] = true;
174 static void Mod_Print_f(cmd_state_t *cmd);
175 static void Mod_Precache_f(cmd_state_t *cmd);
176 static void Mod_Decompile_f(cmd_state_t *cmd);
177 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
180 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
181 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(model_t), 16);
187 Cvar_RegisterVariable(&r_mipskins);
188 Cvar_RegisterVariable(&r_mipnormalmaps);
189 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
190 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
191 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
193 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
194 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
195 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
196 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
197 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
198 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
200 Cmd_AddCommand(CF_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
201 Cmd_AddCommand(CF_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
202 Cmd_AddCommand(CF_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
203 Cmd_AddCommand(CF_CLIENT, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
206 void Mod_RenderInit(void)
208 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
211 void Mod_UnloadModel (model_t *mod)
213 char name[MAX_QPATH];
215 model_t *parentmodel;
217 if (developer_loading.integer)
218 Con_Printf("unloading model %s\n", mod->name);
220 dp_strlcpy(name, mod->name, sizeof(name));
221 parentmodel = mod->brush.parentmodel;
225 if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
226 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
227 mod->surfmesh.data_element3i_indexbuffer = NULL;
228 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
229 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
230 mod->surfmesh.data_element3s_indexbuffer = NULL;
231 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
232 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
233 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
234 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
235 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
236 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
237 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
238 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
239 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
240 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
241 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
243 // free textures/memory attached to the model
244 R_FreeTexturePool(&mod->texturepool);
245 Mem_FreePool(&mod->mempool);
246 // clear the struct to make it available
247 memset(mod, 0, sizeof(model_t));
248 // restore the fields we want to preserve
249 dp_strlcpy(mod->name, name, sizeof(mod->name));
250 mod->brush.parentmodel = parentmodel;
255 static void R_Model_Null_Draw(entity_render_t *ent)
261 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass);
263 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
278 // REQUIRED: fetch start
279 COM_ParseToken_Simple(&bufptr, true, false, true);
281 break; // end of file
282 if (!strcmp(com_token, "\n"))
283 continue; // empty line
284 start = atoi(com_token);
286 // REQUIRED: fetch length
287 COM_ParseToken_Simple(&bufptr, true, false, true);
288 if (!bufptr || !strcmp(com_token, "\n"))
290 Con_Printf("framegroups file: missing number of frames\n");
293 len = atoi(com_token);
295 // OPTIONAL args start
296 COM_ParseToken_Simple(&bufptr, true, false, true);
298 // OPTIONAL: fetch fps
300 if (bufptr && strcmp(com_token, "\n"))
302 fps = atof(com_token);
303 COM_ParseToken_Simple(&bufptr, true, false, true);
306 // OPTIONAL: fetch loopflag
308 if (bufptr && strcmp(com_token, "\n"))
310 loop = (atoi(com_token) != 0);
311 COM_ParseToken_Simple(&bufptr, true, false, true);
314 // OPTIONAL: fetch name
316 if (bufptr && strcmp(com_token, "\n"))
318 dp_strlcpy(name, com_token, sizeof(name));
319 COM_ParseToken_Simple(&bufptr, true, false, true);
322 // OPTIONAL: remaining unsupported tokens (eat them)
323 while (bufptr && strcmp(com_token, "\n"))
324 COM_ParseToken_Simple(&bufptr, true, false, true);
326 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
329 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
336 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass)
338 model_t *mod = (model_t *) pass;
339 animscene_t *anim = &mod->animscenes[i];
341 dp_strlcpy(anim->name, name, sizeof(anim[i].name));
343 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
344 anim->firstframe = bound(0, start, mod->num_poses - 1);
345 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
346 anim->framerate = max(1, fps);
348 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
351 static void Mod_FrameGroupify(model_t *mod, const char *buf)
356 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
359 Con_Printf("no scene found in framegroups file, aborting\n");
362 mod->numframes = cnt;
365 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
366 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
369 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
372 static void Mod_FindPotentialDeforms(model_t *mod)
376 mod->wantnormals = false;
377 mod->wanttangents = false;
378 for (i = 0;i < mod->num_textures;i++)
380 texture = mod->data_textures + i;
381 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
382 mod->wantnormals = true;
383 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
384 mod->wantnormals = true;
385 for (j = 0;j < Q3MAXDEFORMS;j++)
387 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
389 mod->wanttangents = true;
390 mod->wantnormals = true;
393 if (texture->deforms[j].deform != Q3DEFORM_NONE)
394 mod->wantnormals = true;
406 model_t *Mod_LoadModel(model_t *mod, qbool crash, qbool checkdisk)
410 fs_offset_t filesize = 0;
415 if (mod->name[0] == '*') // submodel
418 if (!strcmp(mod->name, "null"))
423 if (mod->loaded || mod->mempool)
424 Mod_UnloadModel(mod);
426 if (developer_loading.integer)
427 Con_Printf("loading model %s\n", mod->name);
430 mod->crc = (unsigned int)-1;
433 VectorClear(mod->normalmins);
434 VectorClear(mod->normalmaxs);
435 VectorClear(mod->yawmins);
436 VectorClear(mod->yawmaxs);
437 VectorClear(mod->rotatedmins);
438 VectorClear(mod->rotatedmaxs);
440 mod->modeldatatypestring = "null";
441 mod->type = mod_null;
442 mod->Draw = R_Model_Null_Draw;
446 // no fatal errors occurred, so this model is ready to use.
455 // even if the model is loaded it still may need reloading...
457 // if it is not loaded or checkdisk is true we need to calculate the crc
458 if (!mod->loaded || checkdisk)
460 if (checkdisk && mod->loaded)
461 Con_DPrintf("checking model %s\n", mod->name);
462 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
465 crc = CRC_Block((unsigned char *)buf, filesize);
466 // we need to reload the model if the crc does not match
472 // if the model is already loaded and checks passed, just return
480 if (developer_loading.integer)
481 Con_Printf("loading model %s\n", mod->name);
483 SCR_PushLoadingScreen(mod->name, 1);
485 // LadyHavoc: unload the existing model in this slot (if there is one)
486 if (mod->loaded || mod->mempool)
487 Mod_UnloadModel(mod);
492 // errors can prevent the corresponding mod->loaded = true;
495 // default lightmap scale
496 mod->lightmapscale = 1;
498 // default model radius and bounding box (mainly for missing models)
500 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
501 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
502 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
503 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
504 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
505 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
509 // load q3 shaders for the first time, or after a level change
516 const char *ext = FS_FileExtension(mod->name);
517 char *bufend = (char *)buf + filesize;
518 // all models use memory, so allocate a memory pool
519 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
521 // We need to have a reference to the base model in case we're parsing submodels
524 // Call the appropriate loader. Try matching magic bytes.
525 for (i = 0; loader[i].Load; i++)
527 // Headerless formats can just load based on extension. Otherwise match the magic string.
528 if((loader[i].extension && !strcasecmp(ext, loader[i].extension) && !loader[i].header) ||
529 (loader[i].header && !memcmp(buf, loader[i].header, loader[i].headersize)))
532 loader[i].Load(mod, buf, bufend);
535 Mod_FindPotentialDeforms(mod);
537 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
540 Mod_FrameGroupify(mod, (const char *)buf);
544 Mod_SetDrawSkyAndWater(mod);
550 Con_Printf(CON_ERROR "Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
553 // LadyHavoc: Sys_Error was *ANNOYING*
554 Con_Printf (CON_ERROR "Mod_LoadModel: %s not found\n", mod->name);
556 // no fatal errors occurred, so this model is ready to use.
559 SCR_PopLoadingScreen(false);
564 void Mod_ClearUsed(void)
567 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
569 for (i = 0;i < nummodels;i++)
570 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
574 void Mod_PurgeUnused(void)
577 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
579 for (i = 0;i < nummodels;i++)
581 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
583 Mod_UnloadModel(mod);
584 Mem_ExpandableArray_FreeRecord(&models, mod);
595 model_t *Mod_FindName(const char *name, const char *parentname)
604 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
607 Host_Error ("Mod_ForName: empty name");
609 // search the currently loaded models
610 for (i = 0;i < nummodels;i++)
612 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))))
619 // no match found, create a new one
620 mod = (model_t *) Mem_ExpandableArray_AllocRecord(&models);
621 dp_strlcpy(mod->name, name, sizeof(mod->name));
623 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
625 mod->brush.parentmodel = NULL;
631 extern qbool vid_opened;
637 Loads in a model for the given name
640 model_t *Mod_ForName(const char *name, qbool crash, qbool checkdisk, const char *parentname)
644 // FIXME: So we don't crash if a server is started early.
648 model = Mod_FindName(name, parentname);
649 if (!model->loaded || checkdisk)
650 Mod_LoadModel(model, crash, checkdisk);
658 Reloads all models if they have changed
661 void Mod_Reload(void)
664 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
667 SCR_PushLoadingScreen("Reloading models", 1.0);
669 for (i = 0;i < nummodels;i++)
670 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
672 for (i = 0;i < nummodels;i++)
673 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
675 SCR_PushLoadingScreen(mod->name, 1.0 / count);
676 Mod_LoadModel(mod, true, true);
677 SCR_PopLoadingScreen(false);
679 SCR_PopLoadingScreen(false);
682 unsigned char *mod_base;
685 //=============================================================================
692 static void Mod_Print_f(cmd_state_t *cmd)
695 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
698 Con_Print("Loaded models:\n");
699 for (i = 0;i < nummodels;i++)
701 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
703 if (mod->brush.numsubmodels)
704 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
706 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
716 static void Mod_Precache_f(cmd_state_t *cmd)
718 if (Cmd_Argc(cmd) == 2)
719 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
721 Con_Print("usage: modelprecache <filename>\n");
724 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
728 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
729 memset(used, 0, numvertices);
730 for (i = 0;i < numelements;i++)
731 used[elements[i]] = 1;
732 for (i = 0, count = 0;i < numvertices;i++)
733 remapvertices[i] = used[i] ? count++ : -1;
738 qbool Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
740 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
742 int invalidintcount = 0, invalidintexample = 0;
743 int invalidshortcount = 0, invalidshortexample = 0;
744 int invalidmismatchcount = 0, invalidmismatchexample = 0;
747 for (i = 0; i < numelements; i++)
749 if (element3i[i] < first || element3i[i] > last)
752 invalidintexample = i;
758 for (i = 0; i < numelements; i++)
760 if (element3s[i] < first || element3s[i] > last)
763 invalidintexample = i;
767 if (element3i && element3s)
769 for (i = 0; i < numelements; i++)
771 if (element3s[i] != element3i[i])
773 invalidmismatchcount++;
774 invalidmismatchexample = i;
778 if (invalidintcount || invalidshortcount || invalidmismatchcount)
780 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, (void *)element3i, (void *)element3s, filename, fileline);
781 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
782 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
783 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);
784 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
786 // edit the elements to make them safer, as the driver will crash otherwise
788 for (i = 0; i < numelements; i++)
789 if (element3i[i] < first || element3i[i] > last)
790 element3i[i] = first;
792 for (i = 0; i < numelements; i++)
793 if (element3s[i] < first || element3s[i] > last)
794 element3s[i] = first;
795 if (element3i && element3s)
796 for (i = 0; i < numelements; i++)
797 if (element3s[i] != element3i[i])
798 element3s[i] = element3i[i];
805 // warning: this is an expensive function!
806 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qbool areaweighting)
813 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
814 // process each vertex of each triangle and accumulate the results
815 // use area-averaging, to make triangles with a big area have a bigger
816 // weighting on the vertex normal than triangles with a small area
817 // to do so, just add the 'normals' together (the bigger the area
818 // the greater the length of the normal is
820 for (i = 0; i < numtriangles; i++, element += 3)
823 vertex3f + element[0] * 3,
824 vertex3f + element[1] * 3,
825 vertex3f + element[2] * 3,
830 VectorNormalize(areaNormal);
832 for (j = 0;j < 3;j++)
834 vectorNormal = normal3f + element[j] * 3;
835 vectorNormal[0] += areaNormal[0];
836 vectorNormal[1] += areaNormal[1];
837 vectorNormal[2] += areaNormal[2];
840 // and just normalize the accumulated vertex normal in the end
841 vectorNormal = normal3f + 3 * firstvertex;
842 for (i = 0; i < numvertices; i++, vectorNormal += 3)
843 VectorNormalize(vectorNormal);
847 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)
849 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
850 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
851 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
853 // 6 multiply, 9 subtract
854 VectorSubtract(v1, v0, v10);
855 VectorSubtract(v2, v0, v20);
856 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
857 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
858 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
859 // 12 multiply, 10 subtract
860 tc10[1] = tc1[1] - tc0[1];
861 tc20[1] = tc2[1] - tc0[1];
862 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
863 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
864 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
865 tc10[0] = tc1[0] - tc0[0];
866 tc20[0] = tc2[0] - tc0[0];
867 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
868 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
869 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
870 // 12 multiply, 4 add, 6 subtract
871 f = DotProduct(svector3f, normal3f);
872 svector3f[0] -= f * normal3f[0];
873 svector3f[1] -= f * normal3f[1];
874 svector3f[2] -= f * normal3f[2];
875 f = DotProduct(tvector3f, normal3f);
876 tvector3f[0] -= f * normal3f[0];
877 tvector3f[1] -= f * normal3f[1];
878 tvector3f[2] -= f * normal3f[2];
879 // if texture is mapped the wrong way (counterclockwise), the tangents
880 // have to be flipped, this is detected by calculating a normal from the
881 // two tangents, and seeing if it is opposite the surface normal
882 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
883 CrossProduct(tvector3f, svector3f, tangentcross);
884 if (DotProduct(tangentcross, normal3f) < 0)
886 VectorNegate(svector3f, svector3f);
887 VectorNegate(tvector3f, tvector3f);
892 // warning: this is a very expensive function!
893 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)
896 float sdir[3], tdir[3], normal[3], *svec, *tvec;
897 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
898 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
901 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
902 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
903 // process each vertex of each triangle and accumulate the results
904 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
906 v0 = vertex3f + e[0] * 3;
907 v1 = vertex3f + e[1] * 3;
908 v2 = vertex3f + e[2] * 3;
909 tc0 = texcoord2f + e[0] * 2;
910 tc1 = texcoord2f + e[1] * 2;
911 tc2 = texcoord2f + e[2] * 2;
913 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
914 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
916 // calculate the edge directions and surface normal
917 // 6 multiply, 9 subtract
918 VectorSubtract(v1, v0, v10);
919 VectorSubtract(v2, v0, v20);
920 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
921 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
922 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
924 // calculate the tangents
925 // 12 multiply, 10 subtract
926 tc10[1] = tc1[1] - tc0[1];
927 tc20[1] = tc2[1] - tc0[1];
928 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
929 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
930 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
931 tc10[0] = tc1[0] - tc0[0];
932 tc20[0] = tc2[0] - tc0[0];
933 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
934 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
935 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
937 // if texture is mapped the wrong way (counterclockwise), the tangents
938 // have to be flipped, this is detected by calculating a normal from the
939 // two tangents, and seeing if it is opposite the surface normal
940 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
941 CrossProduct(tdir, sdir, tangentcross);
942 if (DotProduct(tangentcross, normal) < 0)
944 VectorNegate(sdir, sdir);
945 VectorNegate(tdir, tdir);
950 VectorNormalize(sdir);
951 VectorNormalize(tdir);
953 for (i = 0;i < 3;i++)
955 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
956 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
959 // make the tangents completely perpendicular to the surface normal, and
960 // then normalize them
961 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
962 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
964 f = -DotProduct(svec, n);
965 VectorMA(svec, f, n, svec);
966 VectorNormalize(svec);
967 f = -DotProduct(tvec, n);
968 VectorMA(tvec, f, n, tvec);
969 VectorNormalize(tvec);
973 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qbool lightmapoffsets, qbool vertexcolors)
976 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));
977 loadmodel->surfmesh.num_vertices = numvertices;
978 loadmodel->surfmesh.num_triangles = numtriangles;
979 if (loadmodel->surfmesh.num_vertices)
981 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
982 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
983 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
984 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
985 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
986 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
988 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
990 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
992 if (loadmodel->surfmesh.num_triangles)
994 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
995 if (loadmodel->surfmesh.num_vertices <= 65536)
996 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
1000 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
1002 shadowmesh_t *newmesh;
1003 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
1004 newmesh->mempool = mempool;
1005 newmesh->maxverts = maxverts;
1006 newmesh->maxtriangles = maxtriangles;
1007 newmesh->numverts = 0;
1008 newmesh->numtriangles = 0;
1009 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1010 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1012 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
1013 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
1014 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
1015 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
1019 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
1021 int hashindex, vnum;
1022 shadowmeshvertexhash_t *hash;
1023 // this uses prime numbers intentionally
1024 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
1025 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1027 vnum = (hash - mesh->vertexhashentries);
1028 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1029 return hash - mesh->vertexhashentries;
1031 vnum = mesh->numverts++;
1032 hash = mesh->vertexhashentries + vnum;
1033 hash->next = mesh->vertexhashtable[hashindex];
1034 mesh->vertexhashtable[hashindex] = hash;
1035 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1036 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1037 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1041 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1045 for (i = 0;i < numtris;i++)
1047 if ((mesh->numtriangles * 3 + 2) * sizeof(int) + 1 >= ((memheader_t *)((unsigned char *)mesh->element3i - sizeof(memheader_t)))->size)
1049 // FIXME: we didn't allocate enough space for all the tris, see R_Mod_CompileShadowMap
1050 Con_Print(CON_WARN "Mod_ShadowMesh_AddMesh: insufficient memory allocated!\n");
1053 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1054 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1055 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1056 mesh->numtriangles++;
1059 // the triangle calculation can take a while, so let's do a keepalive here
1060 CL_KeepaliveMessage(false);
1063 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1065 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1066 CL_KeepaliveMessage(false);
1068 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1071 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1073 if (!mesh->numverts)
1076 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1077 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1079 // upload short indices as a buffer
1080 if (mesh->element3s && !mesh->element3s_indexbuffer)
1081 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1083 // upload int indices as a buffer
1084 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1085 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1087 // vertex buffer is several arrays and we put them in the same buffer
1089 // is this wise? the texcoordtexture2f array is used with dynamic
1090 // vertex/svector/tvector/normal when rendering animated models, on the
1091 // other hand animated models don't use a lot of vertices anyway...
1092 if (!mesh->vbo_vertexbuffer)
1094 mesh->vbooffset_vertex3f = 0;
1095 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1099 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qbool createvbo)
1101 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1103 if (mesh->vertexhashentries)
1104 Mem_Free(mesh->vertexhashentries);
1105 mesh->vertexhashentries = NULL;
1106 if (mesh->vertexhashtable)
1107 Mem_Free(mesh->vertexhashtable);
1108 mesh->vertexhashtable = NULL;
1109 if (mesh->maxverts > mesh->numverts)
1111 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1112 mesh->maxverts = mesh->numverts;
1114 if (mesh->maxtriangles > mesh->numtriangles)
1116 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1117 mesh->maxtriangles = mesh->numtriangles;
1119 if (mesh->numverts <= 65536)
1122 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1123 for (i = 0;i < mesh->numtriangles*3;i++)
1124 mesh->element3s[i] = mesh->element3i[i];
1127 Mod_ShadowMesh_CreateVBOs(mesh);
1130 // this can take a while, so let's do a keepalive here
1131 CL_KeepaliveMessage(false);
1136 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1139 vec3_t nmins, nmaxs, ncenter, temp;
1140 float nradius2, dist2, *v;
1144 VectorCopy(mesh->vertex3f, nmins);
1145 VectorCopy(mesh->vertex3f, nmaxs);
1146 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1148 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1149 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1150 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1152 // calculate center and radius
1153 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1154 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1155 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1157 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1159 VectorSubtract(v, ncenter, temp);
1160 dist2 = DotProduct(temp, temp);
1161 if (nradius2 < dist2)
1166 VectorCopy(nmins, mins);
1168 VectorCopy(nmaxs, maxs);
1170 VectorCopy(ncenter, center);
1172 *radius = sqrt(nradius2);
1175 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1177 if (mesh->element3i_indexbuffer)
1178 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1179 if (mesh->element3s_indexbuffer)
1180 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1181 if (mesh->vbo_vertexbuffer)
1182 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1184 Mem_Free(mesh->vertex3f);
1185 if (mesh->element3i)
1186 Mem_Free(mesh->element3i);
1187 if (mesh->element3s)
1188 Mem_Free(mesh->element3s);
1189 if (mesh->vertexhashentries)
1190 Mem_Free(mesh->vertexhashentries);
1191 if (mesh->vertexhashtable)
1192 Mem_Free(mesh->vertexhashtable);
1196 void Mod_CreateCollisionMesh(model_t *mod)
1198 int k, numcollisionmeshtriangles;
1199 qbool usesinglecollisionmesh = false;
1200 const msurface_t *surface = NULL;
1202 mempool_t *mempool = mod->mempool;
1203 if (!mempool && mod->brush.parentmodel)
1204 mempool = mod->brush.parentmodel->mempool;
1205 // make a single combined collision mesh for physics engine use
1206 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1207 numcollisionmeshtriangles = 0;
1208 for (k = mod->submodelsurfaces_start;k < mod->submodelsurfaces_end;k++)
1210 surface = mod->data_surfaces + k;
1211 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1213 usesinglecollisionmesh = true;
1214 numcollisionmeshtriangles = surface->num_triangles;
1217 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1219 numcollisionmeshtriangles += surface->num_triangles;
1221 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1222 if (usesinglecollisionmesh)
1223 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1226 for (k = mod->submodelsurfaces_start; k < mod->submodelsurfaces_end; k++)
1228 surface = mod->data_surfaces + k;
1229 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1231 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1234 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1238 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)
1243 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1244 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1247 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1248 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1249 texcoord2f[0] = tc[0];
1250 texcoord2f[1] = tc[1];
1253 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)
1255 float vup[3], vdown[3], vleft[3], vright[3];
1256 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1257 float sv[3], tv[3], nl[3];
1258 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1259 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1260 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1261 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1262 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1263 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1264 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1265 VectorAdd(svector3f, sv, svector3f);
1266 VectorAdd(tvector3f, tv, tvector3f);
1267 VectorAdd(normal3f, nl, normal3f);
1268 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1269 VectorAdd(svector3f, sv, svector3f);
1270 VectorAdd(tvector3f, tv, tvector3f);
1271 VectorAdd(normal3f, nl, normal3f);
1272 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1273 VectorAdd(svector3f, sv, svector3f);
1274 VectorAdd(tvector3f, tv, tvector3f);
1275 VectorAdd(normal3f, nl, normal3f);
1278 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)
1280 int x, y, ix, iy, *e;
1282 for (y = 0;y < height;y++)
1284 for (x = 0;x < width;x++)
1286 e[0] = (y + 1) * (width + 1) + (x + 0);
1287 e[1] = (y + 0) * (width + 1) + (x + 0);
1288 e[2] = (y + 1) * (width + 1) + (x + 1);
1289 e[3] = (y + 0) * (width + 1) + (x + 0);
1290 e[4] = (y + 0) * (width + 1) + (x + 1);
1291 e[5] = (y + 1) * (width + 1) + (x + 1);
1295 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1296 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1297 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1302 void Mod_Terrain_SurfaceRecurseChunk(model_t *model, int stepsize, int x, int y)
1306 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1307 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1308 float viewvector[3];
1309 unsigned int firstvertex;
1312 if (chunkwidth < 2 || chunkheight < 2)
1314 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]);
1315 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]);
1316 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1317 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1318 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1319 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1321 // too close for this stepsize, emit as 4 chunks instead
1323 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1324 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1325 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1326 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1329 // emit the geometry at stepsize into our vertex buffer / index buffer
1330 // we add two columns and two rows for skirt
1331 outwidth = chunkwidth+2;
1332 outheight = chunkheight+2;
1333 outwidth2 = outwidth-1;
1334 outheight2 = outheight-1;
1335 outwidth3 = outwidth+1;
1336 outheight3 = outheight+1;
1337 firstvertex = numvertices;
1338 e = model->terrain.element3i + numtriangles;
1339 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1340 v = model->terrain.vertex3f + numvertices;
1341 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1342 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1343 for (ty = 0;ty < outheight;ty++)
1345 for (tx = 0;tx < outwidth;tx++)
1347 *e++ = firstvertex + (ty )*outwidth3+(tx );
1348 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1349 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1350 *e++ = firstvertex + (ty )*outwidth3+(tx );
1351 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1352 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1355 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1356 for (ty = 0;ty <= outheight;ty++)
1358 skirtrow = ty == 0 || ty == outheight;
1359 ry = y+bound(1, ty, outheight)*stepsize;
1360 for (tx = 0;tx <= outwidth;tx++)
1362 skirt = skirtrow || tx == 0 || tx == outwidth;
1363 rx = x+bound(1, tx, outwidth)*stepsize;
1366 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1370 // TODO: emit skirt vertices
1373 void Mod_Terrain_UpdateSurfacesForViewOrigin(model_t *model)
1375 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1376 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1377 Mod_Terrain_BuildChunk(model,
1381 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1384 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1386 offset = bound(0, s[4] - '0', 9);
1387 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1392 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1393 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1394 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1395 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1396 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1397 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1398 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1399 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1400 return offset | Q3WAVEFUNC_NONE;
1403 void Mod_FreeQ3Shaders(void)
1405 Mem_FreePool(&q3shaders_mem);
1408 static void Q3Shader_AddToHash (shader_t* shader)
1410 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1411 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1412 q3shader_hash_entry_t* lastEntry = NULL;
1415 if (strcasecmp (entry->shader.name, shader->name) == 0)
1418 if(shader->dpshaderkill)
1420 // killed shader is a redeclarion? we can safely ignore it
1423 else if(entry->shader.dpshaderkill)
1425 // replace the old shader!
1426 // this will skip the entry allocating part
1427 // below and just replace the shader
1432 unsigned char *start, *end, *start2;
1433 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1434 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1435 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1436 if(memcmp(start, start2, end - start))
1437 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1439 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1444 entry = entry->chain;
1446 while (entry != NULL);
1449 if (lastEntry->shader.name[0] != 0)
1452 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1453 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1455 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1456 lastEntry->chain = newEntry;
1457 newEntry->chain = NULL;
1458 lastEntry = newEntry;
1460 /* else: head of chain, in hash entry array */
1463 memcpy (&entry->shader, shader, sizeof (shader_t));
1466 void Mod_LoadQ3Shaders(void)
1474 q3shaderinfo_layer_t *layer;
1476 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1477 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1478 unsigned long custsurfaceflags[256];
1479 int numcustsurfaceflags;
1482 Mod_FreeQ3Shaders();
1484 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1485 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1486 sizeof (q3shader_data_t));
1487 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1488 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1489 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1490 q3shaders_mem, sizeof (char**), 256);
1492 // parse custinfoparms.txt
1493 numcustsurfaceflags = 0;
1494 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1496 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1497 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1500 while (COM_ParseToken_QuakeC(&text, false))
1501 if (!strcasecmp(com_token, "}"))
1503 // custom surfaceflags section
1504 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1505 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1508 while(COM_ParseToken_QuakeC(&text, false))
1510 if (!strcasecmp(com_token, "}"))
1512 // register surfaceflag
1513 if (numcustsurfaceflags >= 256)
1515 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1519 j = (int)strlen(com_token)+1;
1520 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1521 dp_strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1523 if (COM_ParseToken_QuakeC(&text, false))
1524 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1526 custsurfaceflags[numcustsurfaceflags] = 0;
1527 numcustsurfaceflags++;
1535 search = FS_Search("scripts/*.shader", true, false, NULL);
1538 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1540 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1543 while (COM_ParseToken_QuakeC(&text, false))
1545 memset (&shader, 0, sizeof(shader));
1547 shader.surfaceparms = 0;
1548 shader.surfaceflags = 0;
1549 shader.textureflags = 0;
1550 shader.numlayers = 0;
1551 shader.lighting = false;
1552 shader.vertexalpha = false;
1553 shader.textureblendalpha = false;
1554 shader.skyboxname[0] = 0;
1555 shader.deforms[0].deform = Q3DEFORM_NONE;
1556 shader.dpnortlight = false;
1557 shader.dpshadow = false;
1558 shader.dpnoshadow = false;
1559 shader.dpmeshcollisions = false;
1560 shader.dpshaderkill = false;
1561 shader.dpreflectcube[0] = 0;
1562 shader.reflectmin = 0;
1563 shader.reflectmax = 1;
1564 shader.refractfactor = 1;
1565 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1566 shader.reflectfactor = 1;
1567 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1568 shader.r_water_wateralpha = 1;
1569 shader.r_water_waterscroll[0] = 0;
1570 shader.r_water_waterscroll[1] = 0;
1571 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1572 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1573 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1574 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1575 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1576 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1577 shader.specularscalemod = 1;
1578 shader.specularpowermod = 1;
1579 shader.rtlightambient = 0;
1580 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1581 // JUST GREP FOR "specularscalemod = 1".
1583 dp_strlcpy(shader.name, com_token, sizeof(shader.name));
1584 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1586 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1589 while (COM_ParseToken_QuakeC(&text, false))
1591 if (!strcasecmp(com_token, "}"))
1593 if (!strcasecmp(com_token, "{"))
1595 static q3shaderinfo_layer_t dummy;
1596 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1598 layer = shader.layers + shader.numlayers++;
1602 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1603 memset(&dummy, 0, sizeof(dummy));
1606 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1607 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1608 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1609 layer->blendfunc[0] = GL_ONE;
1610 layer->blendfunc[1] = GL_ZERO;
1611 while (COM_ParseToken_QuakeC(&text, false))
1613 if (!strcasecmp(com_token, "}"))
1615 if (!strcasecmp(com_token, "\n"))
1618 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1620 if (j < TEXTURE_MAXFRAMES + 4)
1622 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1623 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1624 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1626 dp_strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1627 numparameters = j + 1;
1629 if (!COM_ParseToken_QuakeC(&text, true))
1632 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1633 // parameter[j][0] = 0;
1634 if (developer_insane.integer)
1636 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1637 for (j = 0;j < numparameters;j++)
1638 Con_DPrintf(" %s", parameter[j]);
1641 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1643 if (numparameters == 2)
1645 if (!strcasecmp(parameter[1], "add"))
1647 layer->blendfunc[0] = GL_ONE;
1648 layer->blendfunc[1] = GL_ONE;
1650 else if (!strcasecmp(parameter[1], "addalpha"))
1652 layer->blendfunc[0] = GL_SRC_ALPHA;
1653 layer->blendfunc[1] = GL_ONE;
1655 else if (!strcasecmp(parameter[1], "filter"))
1657 layer->blendfunc[0] = GL_DST_COLOR;
1658 layer->blendfunc[1] = GL_ZERO;
1660 else if (!strcasecmp(parameter[1], "blend"))
1662 layer->blendfunc[0] = GL_SRC_ALPHA;
1663 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1666 else if (numparameters == 3)
1669 for (k = 0;k < 2;k++)
1671 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1672 layer->blendfunc[k] = GL_ONE;
1673 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1674 layer->blendfunc[k] = GL_ZERO;
1675 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1676 layer->blendfunc[k] = GL_SRC_COLOR;
1677 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1678 layer->blendfunc[k] = GL_SRC_ALPHA;
1679 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1680 layer->blendfunc[k] = GL_DST_COLOR;
1681 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1682 layer->blendfunc[k] = GL_DST_ALPHA;
1683 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1684 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1685 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1686 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1687 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1688 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1689 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1690 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1692 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1696 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1697 layer->alphatest = true;
1698 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1700 if (!strcasecmp(parameter[0], "clampmap"))
1701 layer->clampmap = true;
1702 layer->numframes = 1;
1703 layer->framerate = 1;
1704 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1705 &q3shader_data->char_ptrs);
1706 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1707 if (!strcasecmp(parameter[1], "$lightmap"))
1708 shader.lighting = true;
1710 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1713 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1714 layer->framerate = atof(parameter[1]);
1715 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1716 for (i = 0;i < layer->numframes;i++)
1717 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1719 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1722 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1723 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1724 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1725 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1726 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1727 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1728 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1729 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1730 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1731 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1732 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1733 else if (!strcasecmp(parameter[1], "wave"))
1735 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1736 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1737 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1738 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1740 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1742 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1745 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1746 layer->alphagen.parms[i] = atof(parameter[i+2]);
1747 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1748 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1749 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1750 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1751 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1752 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1753 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1754 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1755 else if (!strcasecmp(parameter[1], "wave"))
1757 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1758 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1759 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1760 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1762 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1764 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1767 // observed values: tcgen environment
1768 // no other values have been observed in real shaders
1769 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1770 layer->tcgen.parms[i] = atof(parameter[i+2]);
1771 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1772 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1773 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1774 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1775 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1776 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1778 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1785 // tcmod stretch sin # # # #
1786 // tcmod stretch triangle # # # #
1787 // tcmod transform # # # # # #
1788 // tcmod turb # # # #
1789 // tcmod turb sin # # # # (this is bogus)
1790 // no other values have been observed in real shaders
1791 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1792 if (!layer->tcmods[tcmodindex].tcmod)
1794 if (tcmodindex < Q3MAXTCMODS)
1796 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1797 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1798 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1799 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1800 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1801 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1802 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1803 else if (!strcasecmp(parameter[1], "stretch"))
1805 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1806 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1807 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1808 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1810 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1811 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1812 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1815 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1817 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1818 if (!strcasecmp(com_token, "}"))
1821 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1822 shader.lighting = true;
1823 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1825 if (layer == shader.layers + 0)
1827 // vertex controlled transparency
1828 shader.vertexalpha = true;
1832 // multilayer terrain shader or similar
1833 shader.textureblendalpha = true;
1834 if (mod_q3shader_force_terrain_alphaflag.integer)
1835 shader.layers[0].dptexflags |= TEXF_ALPHA;
1839 if(mod_q3shader_force_addalpha.integer)
1841 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1842 // this cvar brings back this behaviour
1843 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1844 layer->blendfunc[0] = GL_SRC_ALPHA;
1847 layer->dptexflags = 0;
1848 if (layer->alphatest)
1849 layer->dptexflags |= TEXF_ALPHA;
1850 switch(layer->blendfunc[0])
1853 case GL_ONE_MINUS_SRC_ALPHA:
1854 layer->dptexflags |= TEXF_ALPHA;
1857 switch(layer->blendfunc[1])
1860 case GL_ONE_MINUS_SRC_ALPHA:
1861 layer->dptexflags |= TEXF_ALPHA;
1864 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1865 layer->dptexflags |= TEXF_MIPMAP;
1866 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1867 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1868 if (layer->clampmap)
1869 layer->dptexflags |= TEXF_CLAMP;
1873 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1875 if (j < TEXTURE_MAXFRAMES + 4)
1877 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1878 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1879 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1881 dp_strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1882 numparameters = j + 1;
1884 if (!COM_ParseToken_QuakeC(&text, true))
1887 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1888 // parameter[j][0] = 0;
1889 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1891 if (developer_insane.integer)
1893 Con_DPrintf("%s: ", shader.name);
1894 for (j = 0;j < numparameters;j++)
1895 Con_DPrintf(" %s", parameter[j]);
1898 if (numparameters < 1)
1900 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1902 if (!strcasecmp(parameter[1], "alphashadow"))
1903 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1904 else if (!strcasecmp(parameter[1], "areaportal"))
1905 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1906 else if (!strcasecmp(parameter[1], "botclip"))
1907 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1908 else if (!strcasecmp(parameter[1], "clusterportal"))
1909 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1910 else if (!strcasecmp(parameter[1], "detail"))
1911 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1912 else if (!strcasecmp(parameter[1], "donotenter"))
1913 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1914 else if (!strcasecmp(parameter[1], "dust"))
1915 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1916 else if (!strcasecmp(parameter[1], "hint"))
1917 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1918 else if (!strcasecmp(parameter[1], "fog"))
1919 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1920 else if (!strcasecmp(parameter[1], "lava"))
1921 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1922 else if (!strcasecmp(parameter[1], "lightfilter"))
1923 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1924 else if (!strcasecmp(parameter[1], "lightgrid"))
1925 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1926 else if (!strcasecmp(parameter[1], "metalsteps"))
1927 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1928 else if (!strcasecmp(parameter[1], "nodamage"))
1929 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1930 else if (!strcasecmp(parameter[1], "nodlight"))
1931 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1932 else if (!strcasecmp(parameter[1], "nodraw"))
1933 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1934 else if (!strcasecmp(parameter[1], "nodrop"))
1935 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1936 else if (!strcasecmp(parameter[1], "noimpact"))
1937 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1938 else if (!strcasecmp(parameter[1], "nolightmap"))
1939 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1940 else if (!strcasecmp(parameter[1], "nomarks"))
1941 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1942 else if (!strcasecmp(parameter[1], "nomipmaps"))
1943 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1944 else if (!strcasecmp(parameter[1], "nonsolid"))
1945 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1946 else if (!strcasecmp(parameter[1], "origin"))
1947 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1948 else if (!strcasecmp(parameter[1], "playerclip"))
1949 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1950 else if (!strcasecmp(parameter[1], "sky"))
1951 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1952 else if (!strcasecmp(parameter[1], "slick"))
1953 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1954 else if (!strcasecmp(parameter[1], "slime"))
1955 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1956 else if (!strcasecmp(parameter[1], "structural"))
1957 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1958 else if (!strcasecmp(parameter[1], "trans"))
1959 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1960 else if (!strcasecmp(parameter[1], "water"))
1961 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1962 else if (!strcasecmp(parameter[1], "pointlight"))
1963 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1964 else if (!strcasecmp(parameter[1], "antiportal"))
1965 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1966 else if (!strcasecmp(parameter[1], "skip"))
1967 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1970 // try custom surfaceparms
1971 for (j = 0; j < numcustsurfaceflags; j++)
1973 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1975 shader.surfaceflags |= custsurfaceflags[j];
1980 if (j == numcustsurfaceflags)
1981 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1984 else if (!strcasecmp(parameter[0], "dpshadow"))
1985 shader.dpshadow = true;
1986 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1987 shader.dpnoshadow = true;
1988 else if (!strcasecmp(parameter[0], "dpnortlight"))
1989 shader.dpnortlight = true;
1990 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1991 dp_strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1992 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1993 shader.dpmeshcollisions = true;
1994 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1995 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1997 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1998 shader.dpshaderkill = dpshaderkill;
2000 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
2001 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
2003 const char *op = NULL;
2004 if (numparameters >= 3)
2008 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
2009 shader.dpshaderkill = dpshaderkill;
2011 else if (numparameters >= 4 && !strcmp(op, "=="))
2013 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
2014 shader.dpshaderkill = dpshaderkill;
2016 else if (numparameters >= 4 && !strcmp(op, "!="))
2018 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
2019 shader.dpshaderkill = dpshaderkill;
2021 else if (numparameters >= 4 && !strcmp(op, ">"))
2023 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
2024 shader.dpshaderkill = dpshaderkill;
2026 else if (numparameters >= 4 && !strcmp(op, "<"))
2028 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
2029 shader.dpshaderkill = dpshaderkill;
2031 else if (numparameters >= 4 && !strcmp(op, ">="))
2033 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
2034 shader.dpshaderkill = dpshaderkill;
2036 else if (numparameters >= 4 && !strcmp(op, "<="))
2038 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
2039 shader.dpshaderkill = dpshaderkill;
2043 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2046 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2048 // some q3 skies don't have the sky parm set
2049 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2050 dp_strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2052 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2054 // some q3 skies don't have the sky parm set
2055 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2056 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2057 dp_strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2059 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2061 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2062 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2064 else if (!strcasecmp(parameter[0], "nomipmaps"))
2065 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2066 else if (!strcasecmp(parameter[0], "nopicmip"))
2067 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2068 else if (!strcasecmp(parameter[0], "polygonoffset"))
2069 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2070 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2072 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2073 if(numparameters >= 2)
2075 shader.biaspolygonfactor = atof(parameter[1]);
2076 if(numparameters >= 3)
2077 shader.biaspolygonoffset = atof(parameter[2]);
2079 shader.biaspolygonoffset = 0;
2082 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2084 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2085 if (!strcasecmp(parameter[1], "sky"))
2086 shader.transparentsort = TRANSPARENTSORT_SKY;
2087 else if (!strcasecmp(parameter[1], "distance"))
2088 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2089 else if (!strcasecmp(parameter[1], "hud"))
2090 shader.transparentsort = TRANSPARENTSORT_HUD;
2092 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2094 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2096 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2097 shader.refractfactor = atof(parameter[1]);
2098 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2100 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2102 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2103 shader.reflectfactor = atof(parameter[1]);
2104 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2106 else if (!strcasecmp(parameter[0], "dpcamera"))
2108 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2110 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2112 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2113 shader.reflectmin = atof(parameter[1]);
2114 shader.reflectmax = atof(parameter[2]);
2115 shader.refractfactor = atof(parameter[3]);
2116 shader.reflectfactor = atof(parameter[4]);
2117 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2118 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2119 shader.r_water_wateralpha = atof(parameter[11]);
2121 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2123 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2124 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2126 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2128 shader.specularscalemod = atof(parameter[1]);
2130 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2132 shader.specularpowermod = atof(parameter[1]);
2134 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2136 shader.rtlightambient = atof(parameter[1]);
2138 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2140 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2141 shader.offsetmapping = OFFSETMAPPING_OFF;
2142 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2143 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2144 else if (!strcasecmp(parameter[1], "linear"))
2145 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2146 else if (!strcasecmp(parameter[1], "relief"))
2147 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2148 if (numparameters >= 3)
2149 shader.offsetscale = atof(parameter[2]);
2150 if (numparameters >= 5)
2152 if(!strcasecmp(parameter[3], "bias"))
2153 shader.offsetbias = atof(parameter[4]);
2154 else if(!strcasecmp(parameter[3], "match"))
2155 shader.offsetbias = 1.0f - atof(parameter[4]);
2156 else if(!strcasecmp(parameter[3], "match8"))
2157 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2158 else if(!strcasecmp(parameter[3], "match16"))
2159 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2162 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2165 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2166 if (!shader.deforms[deformindex].deform)
2168 if (deformindex < Q3MAXDEFORMS)
2170 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2171 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2172 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2173 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2174 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2175 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2176 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2177 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2178 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2179 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2180 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2181 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2182 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2183 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2184 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2185 else if (!strcasecmp(parameter[1], "wave" ))
2187 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2188 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2189 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2190 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2192 else if (!strcasecmp(parameter[1], "move" ))
2194 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2195 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2196 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2197 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2202 // hide this shader if a cvar said it should be killed
2203 if (shader.dpshaderkill)
2204 shader.numlayers = 0;
2205 // fix up multiple reflection types
2206 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2207 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2209 Q3Shader_AddToHash (&shader);
2213 FS_FreeSearch(search);
2214 // free custinfoparm values
2215 for (j = 0; j < numcustsurfaceflags; j++)
2216 Mem_Free(custsurfaceparmnames[j]);
2219 shader_t *Mod_LookupQ3Shader(const char *name)
2221 unsigned short hash;
2222 q3shader_hash_entry_t* entry;
2224 Mod_LoadQ3Shaders();
2225 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2226 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2227 while (entry != NULL)
2229 if (strcasecmp (entry->shader.name, name) == 0)
2230 return &entry->shader;
2231 entry = entry->chain;
2236 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2238 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2239 shaderpass->framerate = 0.0f;
2240 shaderpass->numframes = 1;
2241 shaderpass->blendfunc[0] = GL_ONE;
2242 shaderpass->blendfunc[1] = GL_ZERO;
2243 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2244 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2245 shaderpass->alphatest = false;
2246 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2247 shaderpass->skinframes[0] = skinframe;
2251 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2254 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2255 shaderpass->alphatest = layer->alphatest != 0;
2256 shaderpass->framerate = layer->framerate;
2257 shaderpass->numframes = layer->numframes;
2258 shaderpass->blendfunc[0] = layer->blendfunc[0];
2259 shaderpass->blendfunc[1] = layer->blendfunc[1];
2260 shaderpass->rgbgen = layer->rgbgen;
2261 shaderpass->alphagen = layer->alphagen;
2262 shaderpass->tcgen = layer->tcgen;
2263 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2264 shaderpass->tcmods[j] = layer->tcmods[j];
2265 for (j = 0; j < layer->numframes; j++)
2266 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2270 qbool Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qbool warnmissing, qbool fallback, int defaulttexflags, int defaultmaterialflags)
2272 int texflagsmask, texflagsor;
2273 qbool success = true;
2277 dp_strlcpy(texture->name, name, sizeof(texture->name));
2278 texture->basealpha = 1.0f;
2279 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2281 // allow disabling of picmip or compression by defaulttexflags
2283 if(!(defaulttexflags & TEXF_PICMIP))
2284 texflagsmask &= ~TEXF_PICMIP;
2285 if(!(defaulttexflags & TEXF_COMPRESS))
2286 texflagsmask &= ~TEXF_COMPRESS;
2288 if(defaulttexflags & TEXF_ISWORLD)
2289 texflagsor |= TEXF_ISWORLD;
2290 if(defaulttexflags & TEXF_ISSPRITE)
2291 texflagsor |= TEXF_ISSPRITE;
2292 // unless later loaded from the shader
2293 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2294 texture->offsetscale = 1;
2295 texture->offsetbias = 0;
2296 texture->specularscalemod = 1;
2297 texture->specularpowermod = 1;
2298 texture->rtlightambient = 0;
2299 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2300 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2301 // JUST GREP FOR "specularscalemod = 1".
2305 if (developer_loading.integer)
2306 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2308 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2310 texture->basematerialflags = MATERIALFLAG_SKY;
2311 if (shader->skyboxname[0] && loadmodel)
2313 // quake3 seems to append a _ to the skybox name, so this must do so as well
2314 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2317 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2318 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2320 texture->basematerialflags = MATERIALFLAG_WALL;
2322 if (shader->layers[0].alphatest)
2323 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2324 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2325 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2326 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2328 texture->biaspolygonoffset += shader->biaspolygonoffset;
2329 texture->biaspolygonfactor += shader->biaspolygonfactor;
2331 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2332 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2333 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2334 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2335 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2336 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2337 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2338 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2339 texture->customblendfunc[0] = GL_ONE;
2340 texture->customblendfunc[1] = GL_ZERO;
2341 texture->transparentsort = shader->transparentsort;
2342 if (shader->numlayers > 0)
2344 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2345 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2347 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2348 * additive GL_ONE GL_ONE
2349 additive weird GL_ONE GL_SRC_ALPHA
2350 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2351 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2352 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2353 brighten GL_DST_COLOR GL_ONE
2354 brighten GL_ONE GL_SRC_COLOR
2355 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2356 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2357 * modulate GL_DST_COLOR GL_ZERO
2358 * modulate GL_ZERO GL_SRC_COLOR
2359 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2360 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2361 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2362 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2363 * no blend GL_ONE GL_ZERO
2364 nothing GL_ZERO GL_ONE
2366 // if not opaque, figure out what blendfunc to use
2367 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2369 if (shader->layers[0].blendfunc[0] == GL_ONE && 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)
2372 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2373 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2374 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2376 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2379 if (!shader->lighting)
2380 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2382 // here be dragons: convert quake3 shaders to material
2383 if (shader->numlayers > 0)
2386 int terrainbackgroundlayer = -1;
2387 int lightmaplayer = -1;
2388 int alphagenspecularlayer = -1;
2389 int rgbgenvertexlayer = -1;
2390 int rgbgendiffuselayer = -1;
2391 int materiallayer = -1;
2392 int endofprelayers = 0;
2393 int firstpostlayer = 0;
2394 int shaderpassindex = 0;
2395 for (i = 0; i < shader->numlayers; i++)
2397 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2399 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2400 rgbgenvertexlayer = i;
2401 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2402 rgbgendiffuselayer = i;
2403 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2404 alphagenspecularlayer = i;
2406 if (shader->numlayers >= 2
2407 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2408 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2409 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2410 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2412 // terrain blend or certain other effects involving alphatest over a regular layer
2413 terrainbackgroundlayer = 0;
2415 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2416 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2418 else if (lightmaplayer == 0)
2420 // ordinary texture but with $lightmap before diffuse
2422 firstpostlayer = lightmaplayer + 2;
2424 else if (lightmaplayer >= 1)
2426 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2427 endofprelayers = lightmaplayer - 1;
2428 materiallayer = lightmaplayer - 1;
2429 firstpostlayer = lightmaplayer + 1;
2431 else if (rgbgenvertexlayer >= 0)
2433 // map models with baked lighting
2434 materiallayer = rgbgenvertexlayer;
2435 endofprelayers = rgbgenvertexlayer;
2436 firstpostlayer = rgbgenvertexlayer + 1;
2437 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2438 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2439 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2441 else if (rgbgendiffuselayer >= 0)
2443 // entity models with dynamic lighting
2444 materiallayer = rgbgendiffuselayer;
2445 endofprelayers = rgbgendiffuselayer;
2446 firstpostlayer = rgbgendiffuselayer + 1;
2447 // 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)...
2448 if (alphagenspecularlayer >= 0)
2449 firstpostlayer = alphagenspecularlayer + 1;
2453 // special effects shaders - treat first as primary layer and do everything else as post
2458 // convert the main material layer
2459 // 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
2460 if (materiallayer >= 0)
2461 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2462 // convert the terrain background blend layer (if any)
2463 if (terrainbackgroundlayer >= 0)
2464 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2465 // convert the prepass layers (if any)
2466 texture->startpreshaderpass = shaderpassindex;
2467 for (i = 0; i < endofprelayers; i++)
2468 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2469 texture->endpreshaderpass = shaderpassindex;
2470 texture->startpostshaderpass = shaderpassindex;
2471 // convert the postpass layers (if any)
2472 for (i = firstpostlayer; i < shader->numlayers; i++)
2473 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2474 texture->startpostshaderpass = shaderpassindex;
2477 if (shader->dpshadow)
2478 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2479 if (shader->dpnoshadow)
2480 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2481 if (shader->dpnortlight)
2482 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2483 if (shader->vertexalpha)
2484 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2485 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2486 texture->reflectmin = shader->reflectmin;
2487 texture->reflectmax = shader->reflectmax;
2488 texture->refractfactor = shader->refractfactor;
2489 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2490 texture->reflectfactor = shader->reflectfactor;
2491 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2492 texture->r_water_wateralpha = shader->r_water_wateralpha;
2493 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2494 texture->offsetmapping = shader->offsetmapping;
2495 texture->offsetscale = shader->offsetscale;
2496 texture->offsetbias = shader->offsetbias;
2497 texture->specularscalemod = shader->specularscalemod;
2498 texture->specularpowermod = shader->specularpowermod;
2499 texture->rtlightambient = shader->rtlightambient;
2500 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2501 if (shader->dpreflectcube[0])
2502 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2504 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2505 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2506 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2507 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2508 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2509 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2510 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2511 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2512 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2514 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2515 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2516 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2518 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2520 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2523 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2524 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2525 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2526 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2527 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2528 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2529 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2530 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2531 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2532 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2534 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2536 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2537 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2539 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2540 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2541 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2542 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2543 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2544 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2545 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2547 texture->surfaceflags = shader->surfaceflags;
2548 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2549 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2550 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2551 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2552 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2553 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2554 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2555 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2556 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2557 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2558 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2559 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2560 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2561 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2562 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2563 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2564 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2565 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2566 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2567 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2568 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2569 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2570 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2571 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2572 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2573 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2574 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2575 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2576 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2577 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2578 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2579 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2581 if (shader->dpmeshcollisions)
2582 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2583 if (shader->dpshaderkill && developer_extra.integer)
2584 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2586 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2588 if (developer_extra.integer)
2589 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2590 texture->basematerialflags = defaultmaterialflags;
2591 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2593 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2595 if (developer_extra.integer)
2596 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2597 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2598 texture->supercontents = SUPERCONTENTS_SOLID;
2602 if (developer_extra.integer)
2603 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2604 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2606 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2607 texture->supercontents = SUPERCONTENTS_SOLID;
2609 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2611 texture->basematerialflags = MATERIALFLAG_SKY;
2612 texture->supercontents = SUPERCONTENTS_SKY;
2616 texture->basematerialflags = defaultmaterialflags;
2617 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2619 if(cls.state == ca_dedicated)
2621 texture->materialshaderpass = NULL;
2626 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2629 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2630 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2631 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2632 if (texture->q2contents)
2633 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2637 if (!success && warnmissing)
2638 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2641 // init the animation variables
2642 texture->currentframe = texture;
2643 texture->currentmaterialflags = texture->basematerialflags;
2644 if (!texture->materialshaderpass)
2645 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2646 if (!texture->materialshaderpass->skinframes[0])
2647 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2648 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2649 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2653 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2655 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2656 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2658 dp_strlcpy(texture->name, name, sizeof(texture->name));
2659 texture->basealpha = 1.0f;
2660 texture->basematerialflags = materialflags;
2661 texture->supercontents = supercontents;
2663 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2664 texture->offsetscale = 1;
2665 texture->offsetbias = 0;
2666 texture->specularscalemod = 1;
2667 texture->specularpowermod = 1;
2668 texture->rtlightambient = 0;
2669 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2670 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2671 // JUST GREP FOR "specularscalemod = 1".
2673 if (developer_extra.integer)
2674 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2676 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2678 // init the animation variables
2679 texture->currentmaterialflags = texture->basematerialflags;
2680 texture->currentframe = texture;
2681 texture->currentskinframe = skinframe;
2682 texture->backgroundcurrentskinframe = NULL;
2685 void Mod_UnloadCustomMaterial(texture_t *texture, qbool purgeskins)
2687 long unsigned int i, j;
2688 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2690 if (texture->shaderpasses[i])
2693 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2694 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2695 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2696 Mem_Free(texture->shaderpasses[i]);
2697 texture->shaderpasses[i] = NULL;
2700 texture->materialshaderpass = NULL;
2701 texture->currentskinframe = NULL;
2702 texture->backgroundcurrentskinframe = NULL;
2705 skinfile_t *Mod_LoadSkinFiles(void)
2707 int i, words, line, wordsoverflow;
2710 skinfile_t *skinfile = NULL, *first = NULL;
2711 skinfileitem_t *skinfileitem;
2712 char word[10][MAX_QPATH];
2717 U_bodyBox,models/players/Legoman/BikerA2.tga
2718 U_RArm,models/players/Legoman/BikerA1.tga
2719 U_LArm,models/players/Legoman/BikerA1.tga
2720 U_armor,common/nodraw
2721 U_sword,common/nodraw
2722 U_shield,common/nodraw
2723 U_homb,common/nodraw
2724 U_backpack,common/nodraw
2725 U_colcha,common/nodraw
2730 memset(word, 0, sizeof(word));
2731 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2733 // If it's the first file we parse
2734 if (skinfile == NULL)
2736 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2741 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2742 skinfile = skinfile->next;
2744 skinfile->next = NULL;
2746 for(line = 0;;line++)
2749 if (!COM_ParseToken_QuakeC(&data, true))
2751 if (!strcmp(com_token, "\n"))
2754 wordsoverflow = false;
2758 dp_strlcpy(word[words++], com_token, sizeof (word[0]));
2760 wordsoverflow = true;
2762 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2765 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);
2768 // words is always >= 1
2769 if (!strcmp(word[0], "replace"))
2773 if (developer_loading.integer)
2774 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2775 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2776 skinfileitem->next = skinfile->items;
2777 skinfile->items = skinfileitem;
2778 dp_strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2779 dp_strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2782 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]);
2784 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2786 // tag name, like "tag_weapon,"
2787 // not used for anything (not even in Quake3)
2789 else if (words >= 2 && !strcmp(word[1], ","))
2791 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2792 if (developer_loading.integer)
2793 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2794 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2795 skinfileitem->next = skinfile->items;
2796 skinfile->items = skinfileitem;
2797 dp_strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2798 dp_strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2801 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);
2806 loadmodel->numskins = i;
2810 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2813 skinfileitem_t *skinfileitem, *nextitem;
2814 for (;skinfile;skinfile = next)
2816 next = skinfile->next;
2817 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2819 nextitem = skinfileitem->next;
2820 Mem_Free(skinfileitem);
2826 int Mod_CountSkinFiles(skinfile_t *skinfile)
2829 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2833 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2836 double isnap = 1.0 / snap;
2837 for (i = 0;i < numvertices*numcomponents;i++)
2838 vertices[i] = floor(vertices[i]*isnap)*snap;
2841 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2843 int i, outtriangles;
2844 float edgedir1[3], edgedir2[3], temp[3];
2845 // a degenerate triangle is one with no width (thickness, surface area)
2846 // these are characterized by having all 3 points colinear (along a line)
2847 // or having two points identical
2848 // the simplest check is to calculate the triangle's area
2849 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2851 // calculate first edge
2852 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2853 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2854 CrossProduct(edgedir1, edgedir2, temp);
2855 if (VectorLength2(temp) < 0.001f)
2856 continue; // degenerate triangle (no area)
2857 // valid triangle (has area)
2858 VectorCopy(inelement3i, outelement3i);
2862 return outtriangles;
2865 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2868 int firstvertex, lastvertex;
2869 if (numelements > 0 && elements)
2871 firstvertex = lastvertex = elements[0];
2872 for (i = 1;i < numelements;i++)
2875 firstvertex = min(firstvertex, e);
2876 lastvertex = max(lastvertex, e);
2880 firstvertex = lastvertex = 0;
2881 if (firstvertexpointer)
2882 *firstvertexpointer = firstvertex;
2883 if (lastvertexpointer)
2884 *lastvertexpointer = lastvertex;
2887 void Mod_SetDrawSkyAndWater(model_t* mod)
2890 uint64_t basematerialflags = 0;
2891 // by default assume there is no sky or water used in this model
2892 mod->DrawSky = NULL;
2893 mod->DrawAddWaterPlanes = NULL;
2894 // combine all basematerialflags observed in the submodelsurfaces range, then check for special flags
2895 for (j = mod->submodelsurfaces_start; j < mod->submodelsurfaces_end; j++)
2896 if (mod->data_surfaces[j].texture)
2897 basematerialflags |= mod->data_surfaces[j].texture->basematerialflags;
2898 if (basematerialflags & MATERIALFLAG_SKY)
2899 mod->DrawSky = R_Mod_DrawSky;
2900 if (basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
2901 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
2904 typedef struct Mod_MakeSortedSurfaces_qsortsurface_s
2907 q3deffect_t* effect;
2909 rtexture_t* lightmaptexture;
2911 Mod_MakeSortedSurfaces_qsortsurface_t;
2913 static int Mod_MakeSortedSurfaces_qsortfunc(const void *a, const void *b)
2915 const Mod_MakeSortedSurfaces_qsortsurface_t* l = (Mod_MakeSortedSurfaces_qsortsurface_t*)a;
2916 const Mod_MakeSortedSurfaces_qsortsurface_t* r = (Mod_MakeSortedSurfaces_qsortsurface_t*)b;
2917 if (l->effect < r->effect)
2919 if (l->effect > r->effect)
2921 if (l->texture < r->texture)
2923 if (l->texture > r->texture)
2925 if (l->lightmaptexture < r->lightmaptexture)
2927 if (l->lightmaptexture > r->lightmaptexture)
2929 if (l->surfaceindex < r->surfaceindex)
2931 if (l->surfaceindex > r->surfaceindex)
2936 void Mod_MakeSortedSurfaces(model_t *mod)
2938 // make an optimal set of texture-sorted batches to draw...
2940 Mod_MakeSortedSurfaces_qsortsurface_t *info;
2942 if(cls.state == ca_dedicated)
2945 info = (Mod_MakeSortedSurfaces_qsortsurface_t*)Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*info));
2946 if (!mod->modelsurfaces_sorted)
2947 mod->modelsurfaces_sorted = (int *) Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*mod->modelsurfaces_sorted));
2948 // the goal is to sort by submodel (can't change which submodel a surface belongs to), and then by effects and textures
2949 for (j = 0; j < mod->num_surfaces; j++)
2951 info[j].surfaceindex = j;
2952 info[j].effect = mod->data_surfaces[j].effect;
2953 info[j].texture = mod->data_surfaces[j].texture;
2954 info[j].lightmaptexture = mod->data_surfaces[j].lightmaptexture;
2956 for (k = 0; k < mod->brush.numsubmodels; k++)
2957 if (mod->brush.submodels[k]->submodelsurfaces_end > mod->brush.submodels[k]->submodelsurfaces_start + 1)
2958 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);
2959 for (j = 0; j < mod->num_surfaces; j++)
2960 mod->modelsurfaces_sorted[j] = info[j].surfaceindex;
2964 void Mod_BuildVBOs(void)
2966 if(cls.state == ca_dedicated)
2969 if (!loadmodel->surfmesh.num_vertices)
2972 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2975 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2977 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2979 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2980 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2985 // upload short indices as a buffer
2986 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2987 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);
2989 // upload int indices as a buffer
2990 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2991 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);
2993 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2994 // we put several vertex data streams in the same buffer
2995 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
3000 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3001 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3002 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3003 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3004 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3005 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3006 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
3007 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3008 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3009 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
3010 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3011 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3012 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3013 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3014 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3015 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3016 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
3017 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3018 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3019 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3020 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3021 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3022 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3023 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3024 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3025 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3026 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3027 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3032 extern cvar_t mod_obj_orientation;
3033 static void Mod_Decompile_OBJ(model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3035 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3037 const char *texname;
3039 const float *v, *vn, *vt;
3041 size_t outbufferpos = 0;
3042 size_t outbuffermax = 0x100000;
3043 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3044 const msurface_t *surface;
3045 const int maxtextures = 256;
3046 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3049 // construct the mtllib file
3050 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3053 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3056 countvertices += surface->num_vertices;
3057 countfaces += surface->num_triangles;
3058 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3059 for (textureindex = 0;textureindex < counttextures;textureindex++)
3060 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3062 if (textureindex < counttextures)
3063 continue; // already wrote this material entry
3064 if (textureindex >= maxtextures)
3065 continue; // just a precaution
3066 textureindex = counttextures++;
3067 dp_strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3068 if (outbufferpos >= outbuffermax >> 1)
3071 oldbuffer = outbuffer;
3072 outbuffer = (char *) Z_Malloc(outbuffermax);
3073 memcpy(outbuffer, oldbuffer, outbufferpos);
3076 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");
3081 // write the mtllib file
3082 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3084 // construct the obj file
3086 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);
3090 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)
3092 if (outbufferpos >= outbuffermax >> 1)
3095 oldbuffer = outbuffer;
3096 outbuffer = (char *) Z_Malloc(outbuffermax);
3097 memcpy(outbuffer, oldbuffer, outbufferpos);
3100 if(mod_obj_orientation.integer)
3101 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]);
3103 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]);
3108 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3110 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3113 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3114 for (surfaceindex = submodel->submodelsurfaces_start;surfaceindex < submodel->submodelsurfaces_end;surfaceindex++)
3116 surface = model->data_surfaces + surfaceindex;
3117 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3120 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3122 if (outbufferpos >= outbuffermax >> 1)
3125 oldbuffer = outbuffer;
3126 outbuffer = (char *) Z_Malloc(outbuffermax);
3127 memcpy(outbuffer, oldbuffer, outbufferpos);
3133 if(mod_obj_orientation.integer)
3134 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);
3136 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);
3143 // write the obj file
3144 FS_WriteFile(filename, outbuffer, outbufferpos);
3148 Z_Free(texturenames);
3151 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3154 static void Mod_Decompile_SMD(model_t *model, const char *filename, int firstpose, int numposes, qbool writetriangles)
3156 int countnodes = 0, counttriangles = 0, countframes = 0;
3164 size_t outbufferpos = 0;
3165 size_t outbuffermax = 0x100000;
3166 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3167 const msurface_t *surface;
3168 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3171 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3173 if (outbufferpos >= outbuffermax >> 1)
3176 oldbuffer = outbuffer;
3177 outbuffer = (char *) Z_Malloc(outbuffermax);
3178 memcpy(outbuffer, oldbuffer, outbufferpos);
3182 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3186 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3189 for (poseindex = 0;poseindex < numposes;poseindex++)
3192 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3195 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3199 matrix4x4_t posematrix;
3200 if (outbufferpos >= outbuffermax >> 1)
3203 oldbuffer = outbuffer;
3204 outbuffer = (char *) Z_Malloc(outbuffermax);
3205 memcpy(outbuffer, oldbuffer, outbufferpos);
3209 // strangely the smd angles are for a transposed matrix, so we
3210 // have to generate a transposed matrix, then convert that...
3211 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3212 Matrix4x4_ToArray12FloatGL(&posematrix, mtest);
3213 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3214 if (angles[0] >= 180) angles[0] -= 360;
3215 if (angles[1] >= 180) angles[1] -= 360;
3216 if (angles[2] >= 180) angles[2] -= 360;
3220 float a = DEG2RAD(angles[ROLL]);
3221 float b = DEG2RAD(angles[PITCH]);
3222 float c = DEG2RAD(angles[YAW]);
3223 float cy, sy, cp, sp, cr, sr;
3225 // smd matrix construction, for comparing
3236 test[1][0] = sr*sp*cy+cr*-sy;
3237 test[1][1] = sr*sp*sy+cr*cy;
3239 test[2][0] = (cr*sp*cy+-sr*-sy);
3240 test[2][1] = (cr*sp*sy+-sr*cy);
3242 test[3][0] = pose[9];
3243 test[3][1] = pose[10];
3244 test[3][2] = pose[11];
3247 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]));
3252 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3257 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3260 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3262 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3265 if (outbufferpos >= outbuffermax >> 1)
3268 oldbuffer = outbuffer;
3269 outbuffer = (char *) Z_Malloc(outbuffermax);
3270 memcpy(outbuffer, oldbuffer, outbufferpos);
3273 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3276 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3278 const int index = e[2-cornerindex];
3279 const float *v = model->surfmesh.data_vertex3f + index * 3;
3280 const float *vn = model->surfmesh.data_normal3f + index * 3;
3281 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3282 const int b = model->surfmesh.blends[index];
3283 if (b < model->num_bones)
3284 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]);
3287 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3288 const unsigned char *wi = w->index;
3289 const unsigned char *wf = w->influence;
3290 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);
3291 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);
3292 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);
3293 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]);
3300 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3305 FS_WriteFile(filename, outbuffer, outbufferpos);
3308 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3315 decompiles a model to editable files
3318 static void Mod_Decompile_f(cmd_state_t *cmd)
3320 int i, j, k, l, first, count;
3322 char inname[MAX_QPATH];
3323 char outname[MAX_QPATH];
3324 char mtlname[MAX_QPATH];
3325 char basename[MAX_QPATH];
3326 char animname[MAX_QPATH];
3327 char animname2[MAX_QPATH];
3328 char zymtextbuffer[16384];
3329 char dpmtextbuffer[16384];
3330 char framegroupstextbuffer[16384];
3331 int zymtextsize = 0;
3332 int dpmtextsize = 0;
3333 int framegroupstextsize = 0;
3336 if (Cmd_Argc(cmd) != 2)
3338 Con_Print("usage: modeldecompile <filename>\n");
3342 dp_strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3343 FS_StripExtension(inname, basename, sizeof(basename));
3345 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3348 Con_Print("No such model\n");
3351 if (mod->brush.submodel)
3353 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3354 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3355 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3358 if (!mod->surfmesh.num_triangles)
3360 Con_Print("Empty model (or sprite)\n");
3364 // export OBJ if possible (not on sprites)
3365 if (mod->surfmesh.num_triangles)
3367 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3368 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3369 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3372 // export SMD if possible (only for skeletal models)
3373 if (mod->surfmesh.num_triangles && mod->num_bones)
3375 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3376 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3377 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3378 if (l > 0) zymtextsize += l;
3379 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3380 if (l > 0) dpmtextsize += l;
3381 for (i = 0;i < mod->numframes;i = j)
3383 dp_strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3384 first = mod->animscenes[i].firstframe;
3385 if (mod->animscenes[i].framecount > 1)
3388 count = mod->animscenes[i].framecount;
3394 // check for additional frames with same name
3395 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3396 if(animname[l] < '0' || animname[l] > '9')
3398 if(k > 0 && animname[k-1] == '_')
3401 count = mod->num_poses - first;
3402 for (j = i + 1;j < mod->numframes;j++)
3404 dp_strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3405 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3406 if(animname2[l] < '0' || animname2[l] > '9')
3408 if(k > 0 && animname[k-1] == '_')
3411 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3413 count = mod->animscenes[j].firstframe - first;
3417 // if it's only one frame, use the original frame name
3419 dp_strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3422 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3423 Mod_Decompile_SMD(mod, outname, first, count, false);
3424 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3426 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3427 if (l > 0) zymtextsize += l;
3429 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3431 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3432 if (l > 0) dpmtextsize += l;
3434 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3436 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3437 if (l > 0) framegroupstextsize += l;
3441 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3443 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3444 if (framegroupstextsize)
3445 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3449 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3452 memset(state, 0, sizeof(*state));
3453 state->width = width;
3454 state->height = height;
3455 state->currentY = 0;
3456 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3457 for (y = 0;y < state->height;y++)
3459 state->rows[y].currentX = 0;
3460 state->rows[y].rowY = -1;
3464 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3467 state->currentY = 0;
3468 for (y = 0;y < state->height;y++)
3470 state->rows[y].currentX = 0;
3471 state->rows[y].rowY = -1;
3475 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3478 Mem_Free(state->rows);
3479 memset(state, 0, sizeof(*state));
3482 qbool Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3484 mod_alloclightmap_row_t *row;
3487 row = state->rows + blockheight;
3488 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3490 if (state->currentY + blockheight <= state->height)
3492 // use the current allocation position
3493 row->rowY = state->currentY;
3495 state->currentY += blockheight;
3499 // find another position
3500 for (y = blockheight;y < state->height;y++)
3502 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3504 row = state->rows + y;
3508 if (y == state->height)
3513 *outx = row->currentX;
3514 row->currentX += blockwidth;
3519 typedef struct lightmapsample_s
3523 float *vertex_color;
3524 unsigned char *lm_bgr;
3525 unsigned char *lm_dir;
3529 typedef struct lightmapvertex_s
3534 float texcoordbase[2];
3535 float texcoordlightmap[2];
3536 float lightcolor[4];
3540 typedef struct lightmaptriangle_s
3548 // 2D modelspace coordinates of min corner
3549 // snapped to lightmap grid but not in grid coordinates
3551 // 2D modelspace to lightmap coordinate scale
3559 typedef struct lightmaplight_s
3570 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3572 #define MAX_LIGHTMAPSAMPLES 64
3573 static int mod_generatelightmaps_numoffsets[3];
3574 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3576 static int mod_generatelightmaps_numlights;
3577 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3579 extern cvar_t r_shadow_lightattenuationdividebias;
3580 extern cvar_t r_shadow_lightattenuationlinearscale;
3582 static void Mod_GenerateLightmaps_LightPoint(model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3587 float relativepoint[3];
3594 float lightorigin[3];
3598 float lightcolor[3];
3600 for (i = 0;i < 5*3;i++)
3602 for (index = 0;;index++)
3604 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3609 lightradius2 = lightradius * lightradius;
3610 VectorSubtract(lightorigin, pos, relativepoint);
3611 dist2 = VectorLength2(relativepoint);
3612 if (dist2 >= lightradius2)
3614 lightiradius = 1.0f / lightradius;
3615 dist = sqrt(dist2) * lightiradius;
3616 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3617 if (intensity <= 0.0f)
3619 if (model && model->TraceLine)
3621 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3622 if (trace.fraction < 1)
3625 // scale down intensity to add to both ambient and diffuse
3626 //intensity *= 0.5f;
3627 VectorNormalize(relativepoint);
3628 VectorScale(lightcolor, intensity, color);
3629 VectorMA(sample , 0.5f , color, sample );
3630 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3631 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3632 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3633 // calculate a weighted average light direction as well
3634 intensity *= VectorLength(color);
3635 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3637 // calculate the direction we'll use to reduce the sample to a directional light source
3638 VectorCopy(sample + 12, dir);
3639 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3640 VectorNormalize(dir);
3641 // extract the diffuse color along the chosen direction and scale it
3642 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3643 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3644 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3645 // subtract some of diffuse from ambient
3646 VectorMA(sample, -0.333f, diffuse, ambient);
3647 // store the normalized lightdir
3648 VectorCopy(dir, lightdir);
3651 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3655 const msurface_t *surface;
3656 const float *vertex3f = model->surfmesh.data_vertex3f;
3657 const int *element3i = model->surfmesh.data_element3i;
3660 for (surfaceindex = model->submodelsurfaces_start;surfaceindex < model->submodelsurfaces_end;surfaceindex++)
3662 surface = model->data_surfaces + surfaceindex;
3663 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3665 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3667 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3669 VectorCopy(vertex3f + 3*e[0], v2[0]);
3670 VectorCopy(vertex3f + 3*e[1], v2[1]);
3671 VectorCopy(vertex3f + 3*e[2], v2[2]);
3672 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3677 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model_t *model, lightmaplight_t *lightinfo)
3679 int maxnodes = 1<<14;
3680 svbsp_node_t *nodes;
3685 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3686 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3687 VectorCopy(lightinfo->origin, origin);
3688 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3691 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3692 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3693 if (svbsp.ranoutofnodes)
3696 if (maxnodes > 1<<22)
3702 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3707 if (svbsp.numnodes > 0)
3709 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3710 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3711 lightinfo->svbsp = svbsp;
3716 static void Mod_GenerateLightmaps_CreateLights(model_t *model)
3720 lightmaplight_t *lightinfo;
3724 mod_generatelightmaps_numlights = 0;
3725 for (index = 0;;index++)
3727 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3731 mod_generatelightmaps_numlights++;
3733 if (mod_generatelightmaps_numlights > 0)
3735 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3736 lightinfo = mod_generatelightmaps_lightinfo;
3737 for (index = 0;;index++)
3739 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3746 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3748 lightinfo->iradius = 1.0f / lightinfo->radius;
3749 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3750 // TODO: compute svbsp
3751 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3755 static void Mod_GenerateLightmaps_DestroyLights(model_t *model)
3758 if (mod_generatelightmaps_lightinfo)
3760 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3761 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3762 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3763 Mem_Free(mod_generatelightmaps_lightinfo);
3765 mod_generatelightmaps_lightinfo = NULL;
3766 mod_generatelightmaps_numlights = 0;
3769 static qbool Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3771 const svbsp_node_t *node;
3772 const svbsp_node_t *nodes = svbsp->nodes;
3777 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3779 return num == -1; // true if empty, false if solid (shadowed)
3782 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3785 float relativepoint[3];
3794 const lightmaplight_t *lightinfo;
3796 for (i = 0;i < 5*3;i++)
3798 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3800 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3801 VectorSubtract(lightinfo->origin, pos, relativepoint);
3802 // don't accept light from behind a surface, it causes bad shading
3803 if (normal && DotProduct(relativepoint, normal) <= 0)
3805 dist2 = VectorLength2(relativepoint);
3806 if (dist2 >= lightinfo->radius2)
3808 dist = sqrt(dist2) * lightinfo->iradius;
3809 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3812 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3816 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3818 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3820 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3823 // for light grid we'd better check visibility of the offset point
3824 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3825 if (trace.fraction < 1)
3826 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3829 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3834 // scale intensity according to how many rays succeeded
3835 // we know one test is valid, half of the rest will fail...
3836 //if (normal && tests > 1)
3837 // intensity *= (tests - 1.0f) / tests;
3838 intensity *= (float)hits / tests;
3840 // scale down intensity to add to both ambient and diffuse
3841 //intensity *= 0.5f;
3842 VectorNormalize(relativepoint);
3843 VectorScale(lightinfo->color, intensity, color);
3844 VectorMA(sample , 0.5f , color, sample );
3845 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3846 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3847 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3848 // calculate a weighted average light direction as well
3849 intensity *= VectorLength(color);
3850 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3854 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3860 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3861 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3862 VectorCopy(sample + 12, dir);
3863 VectorNormalize(dir);
3864 //VectorAdd(dir, normal, dir);
3865 //VectorNormalize(dir);
3866 f = DotProduct(dir, normal);
3867 f = max(0, f) * 255.0f;
3868 VectorScale(sample, f, color);
3869 //VectorCopy(normal, dir);
3870 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3871 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3872 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3873 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3875 lm_dir[0] = (unsigned char)dir[2];
3876 lm_dir[1] = (unsigned char)dir[1];
3877 lm_dir[2] = (unsigned char)dir[0];
3881 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3884 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3885 VectorCopy(sample, vertex_color);
3888 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3894 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3895 // calculate the direction we'll use to reduce the sample to a directional light source
3896 VectorCopy(sample + 12, dir);
3897 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3898 VectorNormalize(dir);
3899 // extract the diffuse color along the chosen direction and scale it
3900 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3901 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3902 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3903 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3904 VectorScale(sample, 127.5f, ambient);
3905 VectorMA(ambient, -0.333f, diffuse, ambient);
3906 // encode to the grid format
3907 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3908 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3909 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3910 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3911 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3912 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3913 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3914 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3915 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));}
3918 static void Mod_GenerateLightmaps_InitSampleOffsets(model_t *model)
3923 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3924 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3925 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3926 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3927 radius[0] = mod_generatelightmaps_lightmapradius.value;
3928 radius[1] = mod_generatelightmaps_vertexradius.value;
3929 radius[2] = mod_generatelightmaps_gridradius.value;
3930 for (i = 0;i < 3;i++)
3932 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3935 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3940 static void Mod_GenerateLightmaps_DestroyLightmaps(model_t *model)
3942 msurface_t *surface;
3945 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3947 surface = model->data_surfaces + surfaceindex;
3948 surface->lightmaptexture = NULL;
3949 surface->deluxemaptexture = NULL;
3951 if (model->brushq3.data_lightmaps)
3953 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3954 if (model->brushq3.data_lightmaps[i])
3955 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3956 Mem_Free(model->brushq3.data_lightmaps);
3957 model->brushq3.data_lightmaps = NULL;
3959 if (model->brushq3.data_deluxemaps)
3961 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3962 if (model->brushq3.data_deluxemaps[i])
3963 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3964 Mem_Free(model->brushq3.data_deluxemaps);
3965 model->brushq3.data_deluxemaps = NULL;
3969 static void Mod_GenerateLightmaps_UnweldTriangles(model_t *model)
3971 msurface_t *surface;
3977 surfmesh_t oldsurfmesh;
3979 unsigned char *data;
3980 oldsurfmesh = model->surfmesh;
3981 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3982 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3984 size += model->surfmesh.num_vertices * sizeof(float[3]);
3985 size += model->surfmesh.num_vertices * sizeof(float[3]);
3986 size += model->surfmesh.num_vertices * sizeof(float[3]);
3987 size += model->surfmesh.num_vertices * sizeof(float[3]);
3988 size += model->surfmesh.num_vertices * sizeof(float[2]);
3989 size += model->surfmesh.num_vertices * sizeof(float[2]);
3990 size += model->surfmesh.num_vertices * sizeof(float[4]);
3991 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3992 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3993 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3994 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3995 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3996 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3997 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3998 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3999 if (model->surfmesh.num_vertices > 65536)
4000 model->surfmesh.data_element3s = NULL;
4002 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
4003 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
4004 model->surfmesh.data_element3i_indexbuffer = NULL;
4005 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
4006 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
4007 model->surfmesh.data_element3s_indexbuffer = NULL;
4008 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
4009 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
4010 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
4011 model->surfmesh.data_svector3f_vertexbuffer = NULL;
4012 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
4013 model->surfmesh.data_normal3f_vertexbuffer = NULL;
4014 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
4015 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
4016 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
4017 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
4018 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
4020 // convert all triangles to unique vertex data
4022 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4024 surface = model->data_surfaces + surfaceindex;
4025 surface->num_firstvertex = outvertexindex;
4026 surface->num_vertices = surface->num_triangles*3;
4027 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4028 for (i = 0;i < surface->num_triangles*3;i++)
4031 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4032 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4033 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4034 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4035 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4036 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4037 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4038 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4039 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4040 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4041 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4042 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4043 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4044 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4045 if (oldsurfmesh.data_texcoordlightmap2f)
4047 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4048 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4050 if (oldsurfmesh.data_lightmapcolor4f)
4052 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4053 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4054 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4055 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4058 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4059 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4063 if (model->surfmesh.data_element3s)
4064 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4065 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4067 // find and update all submodels to use this new surfmesh data
4068 for (i = 0;i < model->brush.numsubmodels;i++)
4069 model->brush.submodels[i]->surfmesh = model->surfmesh;
4072 static void Mod_GenerateLightmaps_CreateTriangleInformation(model_t *model)
4074 msurface_t *surface;
4080 lightmaptriangle_t *triangle;
4081 // generate lightmap triangle structs
4082 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4083 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4085 surface = model->data_surfaces + surfaceindex;
4086 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4087 for (i = 0;i < surface->num_triangles;i++)
4089 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4090 triangle->triangleindex = surface->num_firsttriangle+i;
4091 triangle->surfaceindex = surfaceindex;
4092 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4093 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4094 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4095 // calculate bounds of triangle
4096 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4097 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4098 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4099 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4100 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4101 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4102 // pick an axial projection based on the triangle normal
4103 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4105 if (fabs(normal[1]) > fabs(normal[axis]))
4107 if (fabs(normal[2]) > fabs(normal[axis]))
4109 triangle->axis = axis;
4114 static void Mod_GenerateLightmaps_DestroyTriangleInformation(model_t *model)
4116 if (mod_generatelightmaps_lightmaptriangles)
4117 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4118 mod_generatelightmaps_lightmaptriangles = NULL;
4121 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4123 static void Mod_GenerateLightmaps_CreateLightmaps(model_t *model)
4125 msurface_t *surface;
4139 float trianglenormal[3];
4140 float samplecenter[3];
4141 float samplenormal[3];
4147 float lmscalepixels;
4150 float lm_basescalepixels;
4151 int lm_borderpixels;
4155 lightmaptriangle_t *triangle;
4156 unsigned char *lightmappixels;
4157 unsigned char *deluxemappixels;
4158 mod_alloclightmap_state_t lmstate;
4161 // generate lightmap projection information for all triangles
4162 if (model->texturepool == NULL)
4163 model->texturepool = R_AllocTexturePool();
4164 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4165 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4166 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4167 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4168 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4170 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4172 surface = model->data_surfaces + surfaceindex;
4173 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4174 lmscalepixels = lm_basescalepixels;
4175 for (retry = 0;retry < 30;retry++)
4177 // after a couple failed attempts, degrade quality to make it fit
4179 lmscalepixels *= 0.5f;
4180 for (i = 0;i < surface->num_triangles;i++)
4182 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4183 triangle->lightmapindex = lightmapnumber;
4184 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4185 // pick two planar axes for projection
4186 // lightmap coordinates here are in pixels
4187 // lightmap projections are snapped to pixel grid explicitly, such
4188 // that two neighboring triangles sharing an edge and projection
4189 // axis will have identical sample spacing along their shared edge
4191 for (j = 0;j < 3;j++)
4193 if (j == triangle->axis)
4195 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4196 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4197 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4198 triangle->lmbase[k] = lmmins/lmscalepixels;
4199 triangle->lmscale[k] = lmscalepixels;
4202 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4205 // if all fit in this texture, we're done with this surface
4206 if (i == surface->num_triangles)
4208 // if we haven't maxed out the lightmap size yet, we retry the
4209 // entire surface batch...
4210 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4212 lm_texturesize *= 2;
4215 Mod_AllocLightmap_Free(&lmstate);
4216 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4219 // if we have maxed out the lightmap size, and this triangle does
4220 // not fit in the same texture as the rest of the surface, we have
4221 // to retry the entire surface in a new texture (can only use one)
4222 // with multiple retries, the lightmap quality degrades until it
4223 // fits (or gives up)
4224 if (surfaceindex > 0)
4226 Mod_AllocLightmap_Reset(&lmstate);
4230 Mod_AllocLightmap_Free(&lmstate);
4232 // now put triangles together into lightmap textures, and do not allow
4233 // triangles of a surface to go into different textures (as that would
4234 // require rewriting the surface list)
4235 model->brushq3.deluxemapping_modelspace = true;
4236 model->brushq3.deluxemapping = true;
4237 model->brushq3.num_mergedlightmaps = lightmapnumber;
4238 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4239 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4240 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4241 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4242 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4244 surface = model->data_surfaces + surfaceindex;
4245 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4246 for (i = 0;i < surface->num_triangles;i++)
4248 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4249 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4250 VectorNormalize(trianglenormal);
4251 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4252 axis = triangle->axis;
4253 axis1 = axis == 0 ? 1 : 0;
4254 axis2 = axis == 2 ? 1 : 2;
4255 lmiscale[0] = 1.0f / triangle->lmscale[0];
4256 lmiscale[1] = 1.0f / triangle->lmscale[1];
4257 if (trianglenormal[axis] < 0)
4258 VectorNegate(trianglenormal, trianglenormal);
4259 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4260 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4261 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4262 for (j = 0;j < 3;j++)
4264 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4265 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4266 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4268 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4269 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4270 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4271 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]);
4281 forward[1] = 1.0f / triangle->lmscale[0];
4285 left[2] = 1.0f / triangle->lmscale[1];
4290 origin[1] = triangle->lmbase[0];
4291 origin[2] = triangle->lmbase[1];
4294 forward[0] = 1.0f / triangle->lmscale[0];
4299 left[2] = 1.0f / triangle->lmscale[1];
4303 origin[0] = triangle->lmbase[0];
4305 origin[2] = triangle->lmbase[1];
4308 forward[0] = 1.0f / triangle->lmscale[0];
4312 left[1] = 1.0f / triangle->lmscale[1];
4317 origin[0] = triangle->lmbase[0];
4318 origin[1] = triangle->lmbase[1];
4322 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4324 #define LM_DIST_EPSILON (1.0f / 32.0f)
4325 for (y = 0;y < triangle->lmsize[1];y++)
4327 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4328 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4330 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4331 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4332 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4333 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4334 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4340 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4342 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);
4343 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);
4347 Mem_Free(lightmappixels);
4348 if (deluxemappixels)
4349 Mem_Free(deluxemappixels);
4351 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4353 surface = model->data_surfaces + surfaceindex;
4354 if (!surface->num_triangles)
4356 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4357 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4358 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4359 surface->lightmapinfo = NULL;
4362 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4363 model->brushq1.lightdata = NULL;
4364 model->brushq1.lightmapupdateflags = NULL;
4365 model->brushq1.firstrender = false;
4366 model->brushq1.num_lightstyles = 0;
4367 model->brushq1.data_lightstyleinfo = NULL;
4368 for (i = 0;i < model->brush.numsubmodels;i++)
4370 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4371 model->brush.submodels[i]->brushq1.firstrender = false;
4372 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4373 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4377 static void Mod_GenerateLightmaps_UpdateVertexColors(model_t *model)
4380 for (i = 0;i < model->surfmesh.num_vertices;i++)
4381 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4384 static void Mod_GenerateLightmaps_UpdateLightGrid(model_t *model)
4391 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4393 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4394 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4396 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4397 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4399 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4400 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4406 extern cvar_t mod_q3bsp_nolightmaps;
4407 static void Mod_GenerateLightmaps(model_t *model)
4409 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4410 model_t *oldloadmodel = loadmodel;
4413 Mod_GenerateLightmaps_InitSampleOffsets(model);
4414 Mod_GenerateLightmaps_DestroyLightmaps(model);
4415 Mod_GenerateLightmaps_UnweldTriangles(model);
4416 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4417 Mod_GenerateLightmaps_CreateLights(model);
4418 if(!mod_q3bsp_nolightmaps.integer)
4419 Mod_GenerateLightmaps_CreateLightmaps(model);
4420 Mod_GenerateLightmaps_UpdateVertexColors(model);
4421 Mod_GenerateLightmaps_UpdateLightGrid(model);
4422 Mod_GenerateLightmaps_DestroyLights(model);
4423 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4425 loadmodel = oldloadmodel;
4428 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4430 if (Cmd_Argc(cmd) != 1)
4432 Con_Printf("usage: mod_generatelightmaps\n");
4437 Con_Printf("no worldmodel loaded\n");
4440 Mod_GenerateLightmaps(cl.worldmodel);
4443 void Mod_Mesh_Create(model_t *mod, const char *name)
4445 memset(mod, 0, sizeof(*mod));
4446 dp_strlcpy(mod->name, name, sizeof(mod->name));
4447 mod->mempool = Mem_AllocPool(name, 0, NULL);
4448 mod->texturepool = R_AllocTexturePool();
4449 mod->Draw = R_Mod_Draw;
4450 mod->DrawDepth = R_Mod_DrawDepth;
4451 mod->DrawDebug = R_Mod_DrawDebug;
4452 mod->DrawPrepass = R_Mod_DrawPrepass;
4453 mod->GetLightInfo = R_Mod_GetLightInfo;
4454 mod->DrawShadowMap = R_Mod_DrawShadowMap;
4455 mod->DrawLight = R_Mod_DrawLight;
4458 void Mod_Mesh_Destroy(model_t *mod)
4460 Mod_UnloadModel(mod);
4463 // resets the mesh model to have no geometry to render, ready for a new frame -
4464 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4465 void Mod_Mesh_Reset(model_t *mod)
4467 mod->num_surfaces = 0;
4468 mod->surfmesh.num_vertices = 0;
4469 mod->surfmesh.num_triangles = 0;
4470 if (mod->surfmesh.data_vertexhash) // UBSan: memset arg 1 isn't allowed to be null, but sometimes this is NULL.
4471 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4472 mod->DrawSky = NULL; // will be set if a texture needs it
4473 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4476 texture_t *Mod_Mesh_GetTexture(model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4480 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4481 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4482 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4484 if (mod->max_textures <= mod->num_textures)
4486 texture_t *oldtextures = mod->data_textures;
4487 mod->max_textures = max(mod->max_textures * 2, 1024);
4488 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4489 // update the pointers
4490 for (i = 0; i < mod->num_surfaces; i++)
4491 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4493 t = &mod->data_textures[mod->num_textures++];
4494 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4495 t->mesh_drawflag = drawflag;
4496 t->mesh_defaulttexflags = defaulttexflags;
4497 t->mesh_defaultmaterialflags = defaultmaterialflags;
4498 switch (defaultdrawflags & DRAWFLAG_MASK)
4500 case DRAWFLAG_ADDITIVE:
4501 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4502 t->currentmaterialflags = t->basematerialflags;
4504 case DRAWFLAG_MODULATE:
4505 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4506 t->currentmaterialflags = t->basematerialflags;
4507 t->customblendfunc[0] = GL_DST_COLOR;
4508 t->customblendfunc[1] = GL_ZERO;
4510 case DRAWFLAG_2XMODULATE:
4511 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4512 t->currentmaterialflags = t->basematerialflags;
4513 t->customblendfunc[0] = GL_DST_COLOR;
4514 t->customblendfunc[1] = GL_SRC_COLOR;
4516 case DRAWFLAG_SCREEN:
4517 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4518 t->currentmaterialflags = t->basematerialflags;
4519 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4520 t->customblendfunc[1] = GL_ONE;
4528 msurface_t *Mod_Mesh_AddSurface(model_t *mod, texture_t *tex, qbool batchwithprevioussurface)
4531 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4532 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4533 return mod->data_surfaces + mod->num_surfaces - 1;
4534 // create new surface
4535 if (mod->max_surfaces == mod->num_surfaces)
4537 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4538 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4539 mod->modelsurfaces_sorted = (int *)Mem_Realloc(mod->mempool, mod->modelsurfaces_sorted, mod->max_surfaces * sizeof(*mod->modelsurfaces_sorted));
4541 surf = mod->data_surfaces + mod->num_surfaces;
4542 mod->num_surfaces++;
4543 memset(surf, 0, sizeof(*surf));
4544 surf->texture = tex;
4545 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4546 surf->num_firstvertex = mod->surfmesh.num_vertices;
4547 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4548 mod->DrawSky = R_Mod_DrawSky;
4549 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4550 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
4554 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)
4556 int hashindex, h, vnum, mask;
4557 surfmesh_t *mesh = &mod->surfmesh;
4558 if (mesh->max_vertices == mesh->num_vertices)
4560 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4561 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4562 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4563 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4564 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4565 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4566 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4567 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4568 // rebuild the hash table
4569 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4570 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4571 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4572 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4573 mask = mod->surfmesh.num_vertexhashsize - 1;
4574 // no need to hash the vertices for the entire model, the latest surface will suffice.
4575 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4577 // this uses prime numbers intentionally for computing the hash
4578 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;
4579 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4580 ; // just iterate until we find the terminator
4581 mesh->data_vertexhash[h] = vnum;
4584 mask = mod->surfmesh.num_vertexhashsize - 1;
4585 // this uses prime numbers intentionally for computing the hash
4586 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4587 // when possible find an identical vertex within the same surface and return it
4588 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4590 if (vnum >= surf->num_firstvertex
4591 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4592 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4593 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4594 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4595 && 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)
4598 // add the new vertex
4599 vnum = mesh->num_vertices++;
4600 if (surf->num_vertices > 0)
4602 if (surf->mins[0] > x) surf->mins[0] = x;
4603 if (surf->mins[1] > y) surf->mins[1] = y;
4604 if (surf->mins[2] > z) surf->mins[2] = z;
4605 if (surf->maxs[0] < x) surf->maxs[0] = x;
4606 if (surf->maxs[1] < y) surf->maxs[1] = y;
4607 if (surf->maxs[2] < z) surf->maxs[2] = z;
4611 VectorSet(surf->mins, x, y, z);
4612 VectorSet(surf->maxs, x, y, z);
4614 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4615 mesh->data_vertexhash[h] = vnum;
4616 mesh->data_vertex3f[vnum * 3 + 0] = x;
4617 mesh->data_vertex3f[vnum * 3 + 1] = y;
4618 mesh->data_vertex3f[vnum * 3 + 2] = z;
4619 mesh->data_normal3f[vnum * 3 + 0] = nx;
4620 mesh->data_normal3f[vnum * 3 + 1] = ny;
4621 mesh->data_normal3f[vnum * 3 + 2] = nz;
4622 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4623 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4624 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4625 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4626 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4627 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4628 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4629 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4633 void Mod_Mesh_AddTriangle(model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4635 surfmesh_t *mesh = &mod->surfmesh;
4636 if (mesh->max_triangles == mesh->num_triangles)
4638 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4639 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4640 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4642 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4643 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4644 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4645 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4646 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4647 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4648 mesh->num_triangles++;
4649 surf->num_triangles++;
4652 static void Mod_Mesh_MakeSortedSurfaces(model_t *mod)
4657 // build the sorted surfaces list properly to reduce material setup
4658 // this is easy because we're just sorting on texture and don't care about the order of textures
4659 mod->submodelsurfaces_start = 0;
4660 mod->submodelsurfaces_end = 0;
4661 for (i = 0; i < mod->num_surfaces; i++)
4662 mod->data_surfaces[i].included = false;
4663 for (i = 0; i < mod->num_surfaces; i++)
4665 if (mod->data_surfaces[i].included)
4667 tex = mod->data_surfaces[i].texture;
4668 // j = i is intentional
4669 for (j = i; j < mod->num_surfaces; j++)
4671 if (!mod->data_surfaces[j].included && mod->data_surfaces[j].texture == tex)
4673 mod->data_surfaces[j].included = 1;
4674 mod->modelsurfaces_sorted[mod->submodelsurfaces_end++] = j;
4680 static void Mod_Mesh_ComputeBounds(model_t *mod)
4683 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4685 if (mod->surfmesh.num_vertices > 0)
4687 // calculate normalmins/normalmaxs
4688 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4689 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4690 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4692 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4693 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4694 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4695 // expand bounds to include this vertex
4696 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4697 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4698 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4699 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4700 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4701 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4703 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4704 // (fast but less accurate than doing it per vertex)
4705 x2a = mod->normalmins[0] * mod->normalmins[0];
4706 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4707 y2a = mod->normalmins[1] * mod->normalmins[1];
4708 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4709 z2a = mod->normalmins[2] * mod->normalmins[2];
4710 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4714 yawradius = sqrt(x2 + y2);
4715 rotatedradius = sqrt(x2 + y2 + z2);
4716 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4717 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4718 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4719 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4720 mod->radius = rotatedradius;
4721 mod->radius2 = x2 + y2 + z2;
4725 VectorClear(mod->normalmins);
4726 VectorClear(mod->normalmaxs);
4727 VectorClear(mod->yawmins);
4728 VectorClear(mod->yawmaxs);
4729 VectorClear(mod->rotatedmins);
4730 VectorClear(mod->rotatedmaxs);
4736 void Mod_Mesh_Validate(model_t *mod)
4739 qbool warned = false;
4740 for (i = 0; i < mod->num_surfaces; i++)
4742 msurface_t *surf = mod->data_surfaces + i;
4743 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4744 int first = surf->num_firstvertex;
4745 int end = surf->num_firstvertex + surf->num_vertices;
4747 for (j = 0;j < surf->num_triangles * 3;j++)
4749 if (e[j] < first || e[j] >= end)
4752 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4760 static void Mod_Mesh_UploadDynamicBuffers(model_t *mod)
4762 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;
4763 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;
4764 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;
4765 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;
4766 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;
4767 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;
4768 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;
4769 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;
4770 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;
4771 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;
4772 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;
4775 void Mod_Mesh_Finalize(model_t *mod)
4777 if (gl_paranoid.integer)
4778 Mod_Mesh_Validate(mod);
4779 Mod_Mesh_ComputeBounds(mod);
4780 Mod_Mesh_MakeSortedSurfaces(mod);
4781 if(!r_refdef.draw2dstage)
4782 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);
4783 Mod_Mesh_UploadDynamicBuffers(mod);