2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // models.c -- model loading and caching
22 // models are the only shared resource between a client and server running
23 // on the same machine.
30 cvar_t r_mipskins = {CVAR_CLIENT | CVAR_SAVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
31 cvar_t r_mipnormalmaps = {CVAR_CLIENT | CVAR_SAVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
42 dp_model_t *loadmodel;
44 // Supported model formats
45 static modloader_t loader[] =
47 {"obj", NULL, 0, Mod_OBJ_Load},
48 {NULL, "IDPO", 4, Mod_IDP0_Load},
49 {NULL, "IDP2", 4, Mod_IDP2_Load},
50 {NULL, "IDP3", 4, Mod_IDP3_Load},
51 {NULL, "IDSP", 4, Mod_IDSP_Load},
52 {NULL, "IDS2", 4, Mod_IDS2_Load},
53 {NULL, "\035", 1, Mod_Q1BSP_Load},
54 {NULL, "\036", 1, Mod_HLBSP_Load},
55 {NULL, "BSP2", 4, Mod_BSP2_Load},
56 {NULL, "2PSB", 4, Mod_2PSB_Load},
57 {NULL, "IBSP", 4, Mod_IBSP_Load},
58 {NULL, "ZYMOTICMODEL", 13, Mod_ZYMOTICMODEL_Load},
59 {NULL, "DARKPLACESMODEL", 16, Mod_DARKPLACESMODEL_Load},
60 {NULL, "PSKMODEL", 9, Mod_PSKMODEL_Load},
61 {NULL, "INTERQUAKEMODEL", 16, Mod_INTERQUAKEMODEL_Load},
62 {"map", NULL, 0, Mod_MAP_Load},
66 static mempool_t *mod_mempool;
67 static memexpandablearray_t models;
69 static mempool_t* q3shaders_mem;
70 typedef struct q3shader_hash_entry_s
73 struct q3shader_hash_entry_s* chain;
74 } q3shader_hash_entry_t;
75 #define Q3SHADER_HASH_SIZE 1021
76 typedef struct q3shader_data_s
78 memexpandablearray_t hash_entries;
79 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
80 memexpandablearray_t char_ptrs;
82 static q3shader_data_t* q3shader_data;
84 static void mod_start(void)
87 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
90 SCR_PushLoadingScreen("Loading models", 1.0);
92 for (i = 0;i < nummodels;i++)
93 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
96 for (i = 0;i < nummodels;i++)
97 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
100 SCR_PushLoadingScreen(mod->name, 1.0 / count);
101 Mod_LoadModel(mod, true, false);
102 SCR_PopLoadingScreen(false);
104 SCR_PopLoadingScreen(false);
107 static void mod_shutdown(void)
110 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
113 for (i = 0;i < nummodels;i++)
114 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
115 Mod_UnloadModel(mod);
118 Mod_Skeletal_FreeBuffers();
121 static void mod_newmap(void)
124 int i, j, k, l, surfacenum, ssize, tsize;
125 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
128 for (i = 0;i < nummodels;i++)
130 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
132 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
134 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
135 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
136 if (mod->data_textures[j].shaderpasses[l])
137 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
138 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
140 if (mod->brush.solidskyskinframe)
141 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
142 if (mod->brush.alphaskyskinframe)
143 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
147 if (!cl_stainmaps_clearonload.integer)
150 for (i = 0;i < nummodels;i++)
152 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
154 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
156 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
158 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
159 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
160 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
161 mod->brushq1.lightmapupdateflags[surfacenum] = true;
173 static void Mod_Print_f(cmd_state_t *cmd);
174 static void Mod_Precache_f(cmd_state_t *cmd);
175 static void Mod_Decompile_f(cmd_state_t *cmd);
176 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
179 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
180 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(dp_model_t), 16);
186 Cvar_RegisterVariable(&r_mipskins);
187 Cvar_RegisterVariable(&r_mipnormalmaps);
188 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
189 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
190 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
192 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
193 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
194 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
195 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
196 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
197 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
199 Cmd_AddCommand(CMD_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
200 Cmd_AddCommand(CMD_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
201 Cmd_AddCommand(CMD_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
202 Cmd_AddCommand(CMD_CLIENT, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
205 void Mod_RenderInit(void)
207 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
210 void Mod_UnloadModel (dp_model_t *mod)
212 char name[MAX_QPATH];
214 dp_model_t *parentmodel;
216 if (developer_loading.integer)
217 Con_Printf("unloading model %s\n", mod->name);
219 strlcpy(name, mod->name, sizeof(name));
220 parentmodel = mod->brush.parentmodel;
224 if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
225 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
226 mod->surfmesh.data_element3i_indexbuffer = NULL;
227 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
228 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
229 mod->surfmesh.data_element3s_indexbuffer = NULL;
230 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
231 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
232 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
233 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
234 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
235 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
236 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
237 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
238 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
239 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
240 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
242 // free textures/memory attached to the model
243 R_FreeTexturePool(&mod->texturepool);
244 Mem_FreePool(&mod->mempool);
245 // clear the struct to make it available
246 memset(mod, 0, sizeof(dp_model_t));
247 // restore the fields we want to preserve
248 strlcpy(mod->name, name, sizeof(mod->name));
249 mod->brush.parentmodel = parentmodel;
254 static void R_Model_Null_Draw(entity_render_t *ent)
260 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass);
262 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
277 // REQUIRED: fetch start
278 COM_ParseToken_Simple(&bufptr, true, false, true);
280 break; // end of file
281 if (!strcmp(com_token, "\n"))
282 continue; // empty line
283 start = atoi(com_token);
285 // REQUIRED: fetch length
286 COM_ParseToken_Simple(&bufptr, true, false, true);
287 if (!bufptr || !strcmp(com_token, "\n"))
289 Con_Printf("framegroups file: missing number of frames\n");
292 len = atoi(com_token);
294 // OPTIONAL args start
295 COM_ParseToken_Simple(&bufptr, true, false, true);
297 // OPTIONAL: fetch fps
299 if (bufptr && strcmp(com_token, "\n"))
301 fps = atof(com_token);
302 COM_ParseToken_Simple(&bufptr, true, false, true);
305 // OPTIONAL: fetch loopflag
307 if (bufptr && strcmp(com_token, "\n"))
309 loop = (atoi(com_token) != 0);
310 COM_ParseToken_Simple(&bufptr, true, false, true);
313 // OPTIONAL: fetch name
315 if (bufptr && strcmp(com_token, "\n"))
317 strlcpy(name, com_token, sizeof(name));
318 COM_ParseToken_Simple(&bufptr, true, false, true);
321 // OPTIONAL: remaining unsupported tokens (eat them)
322 while (bufptr && strcmp(com_token, "\n"))
323 COM_ParseToken_Simple(&bufptr, true, false, true);
325 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
328 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
335 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass)
337 dp_model_t *mod = (dp_model_t *) pass;
338 animscene_t *anim = &mod->animscenes[i];
340 strlcpy(anim->name, name, sizeof(anim[i].name));
342 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
343 anim->firstframe = bound(0, start, mod->num_poses - 1);
344 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
345 anim->framerate = max(1, fps);
347 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
350 static void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
355 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
358 Con_Printf("no scene found in framegroups file, aborting\n");
361 mod->numframes = cnt;
364 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
365 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
368 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
371 static void Mod_FindPotentialDeforms(dp_model_t *mod)
375 mod->wantnormals = false;
376 mod->wanttangents = false;
377 for (i = 0;i < mod->num_textures;i++)
379 texture = mod->data_textures + i;
380 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
381 mod->wantnormals = true;
382 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
383 mod->wantnormals = true;
384 for (j = 0;j < Q3MAXDEFORMS;j++)
386 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
388 mod->wanttangents = true;
389 mod->wantnormals = true;
392 if (texture->deforms[j].deform != Q3DEFORM_NONE)
393 mod->wantnormals = true;
405 dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
409 fs_offset_t filesize = 0;
414 if (mod->name[0] == '*') // submodel
417 if (!strcmp(mod->name, "null"))
422 if (mod->loaded || mod->mempool)
423 Mod_UnloadModel(mod);
425 if (developer_loading.integer)
426 Con_Printf("loading model %s\n", mod->name);
429 mod->crc = (unsigned int)-1;
432 VectorClear(mod->normalmins);
433 VectorClear(mod->normalmaxs);
434 VectorClear(mod->yawmins);
435 VectorClear(mod->yawmaxs);
436 VectorClear(mod->rotatedmins);
437 VectorClear(mod->rotatedmaxs);
439 mod->modeldatatypestring = "null";
440 mod->type = mod_null;
441 mod->Draw = R_Model_Null_Draw;
445 // no fatal errors occurred, so this model is ready to use.
454 // even if the model is loaded it still may need reloading...
456 // if it is not loaded or checkdisk is true we need to calculate the crc
457 if (!mod->loaded || checkdisk)
459 if (checkdisk && mod->loaded)
460 Con_DPrintf("checking model %s\n", mod->name);
461 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
464 crc = CRC_Block((unsigned char *)buf, filesize);
465 // we need to reload the model if the crc does not match
471 // if the model is already loaded and checks passed, just return
479 if (developer_loading.integer)
480 Con_Printf("loading model %s\n", mod->name);
482 SCR_PushLoadingScreen(mod->name, 1);
484 // LadyHavoc: unload the existing model in this slot (if there is one)
485 if (mod->loaded || mod->mempool)
486 Mod_UnloadModel(mod);
491 // errors can prevent the corresponding mod->loaded = true;
494 // default lightmap scale
495 mod->lightmapscale = 1;
497 // default model radius and bounding box (mainly for missing models)
499 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
500 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
501 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
502 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
503 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
504 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
508 // load q3 shaders for the first time, or after a level change
515 const char *ext = FS_FileExtension(mod->name);
516 char *bufend = (char *)buf + filesize;
517 // all models use memory, so allocate a memory pool
518 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
520 // call the apropriate loader
523 // Try matching magic bytes.
524 for (i = 0; loader[i].Load; i++)
526 // Headerless formats can just load based on extension. Otherwise match the magic string.
527 if((loader[i].extension && !strcasecmp(ext, loader[i].extension) && !loader[i].header) ||
528 (loader[i].header && !memcmp(buf, loader[i].header, loader[i].headersize)))
531 loader[i].Load(mod, buf, bufend);
534 Mod_FindPotentialDeforms(mod);
536 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
539 Mod_FrameGroupify(mod, (const char *)buf);
548 Con_Printf(CON_ERROR "Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
551 // LadyHavoc: Sys_Error was *ANNOYING*
552 Con_Printf (CON_ERROR "Mod_LoadModel: %s not found\n", mod->name);
554 // no fatal errors occurred, so this model is ready to use.
557 SCR_PopLoadingScreen(false);
562 void Mod_ClearUsed(void)
565 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
567 for (i = 0;i < nummodels;i++)
568 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
572 void Mod_PurgeUnused(void)
575 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
577 for (i = 0;i < nummodels;i++)
579 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
581 Mod_UnloadModel(mod);
582 Mem_ExpandableArray_FreeRecord(&models, mod);
593 dp_model_t *Mod_FindName(const char *name, const char *parentname)
602 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
605 Host_Error ("Mod_ForName: empty name");
607 // search the currently loaded models
608 for (i = 0;i < nummodels;i++)
610 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name) && ((!mod->brush.parentmodel && !parentname[0]) || (mod->brush.parentmodel && parentname[0] && !strcmp(mod->brush.parentmodel->name, parentname))))
617 // no match found, create a new one
618 mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
619 strlcpy(mod->name, name, sizeof(mod->name));
621 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
623 mod->brush.parentmodel = NULL;
633 Loads in a model for the given name
636 dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
639 model = Mod_FindName(name, parentname);
640 if (!model->loaded || checkdisk)
641 Mod_LoadModel(model, crash, checkdisk);
649 Reloads all models if they have changed
652 void Mod_Reload(void)
655 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
658 SCR_PushLoadingScreen("Reloading models", 1.0);
660 for (i = 0;i < nummodels;i++)
661 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
663 for (i = 0;i < nummodels;i++)
664 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
666 SCR_PushLoadingScreen(mod->name, 1.0 / count);
667 Mod_LoadModel(mod, true, true);
668 SCR_PopLoadingScreen(false);
670 SCR_PopLoadingScreen(false);
673 unsigned char *mod_base;
676 //=============================================================================
683 static void Mod_Print_f(cmd_state_t *cmd)
686 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
689 Con_Print("Loaded models:\n");
690 for (i = 0;i < nummodels;i++)
692 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
694 if (mod->brush.numsubmodels)
695 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
697 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
707 static void Mod_Precache_f(cmd_state_t *cmd)
709 if (Cmd_Argc(cmd) == 2)
710 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
712 Con_Print("usage: modelprecache <filename>\n");
715 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
719 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
720 memset(used, 0, numvertices);
721 for (i = 0;i < numelements;i++)
722 used[elements[i]] = 1;
723 for (i = 0, count = 0;i < numvertices;i++)
724 remapvertices[i] = used[i] ? count++ : -1;
729 qboolean Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
731 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
733 int invalidintcount = 0, invalidintexample = 0;
734 int invalidshortcount = 0, invalidshortexample = 0;
735 int invalidmismatchcount = 0, invalidmismatchexample = 0;
738 for (i = 0; i < numelements; i++)
740 if (element3i[i] < first || element3i[i] > last)
743 invalidintexample = i;
749 for (i = 0; i < numelements; i++)
751 if (element3s[i] < first || element3s[i] > last)
754 invalidintexample = i;
758 if (element3i && element3s)
760 for (i = 0; i < numelements; i++)
762 if (element3s[i] != element3i[i])
764 invalidmismatchcount++;
765 invalidmismatchexample = i;
769 if (invalidintcount || invalidshortcount || invalidmismatchcount)
771 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, (void *)element3i, (void *)element3s, filename, fileline);
772 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
773 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
774 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);
775 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
777 // edit the elements to make them safer, as the driver will crash otherwise
779 for (i = 0; i < numelements; i++)
780 if (element3i[i] < first || element3i[i] > last)
781 element3i[i] = first;
783 for (i = 0; i < numelements; i++)
784 if (element3s[i] < first || element3s[i] > last)
785 element3s[i] = first;
786 if (element3i && element3s)
787 for (i = 0; i < numelements; i++)
788 if (element3s[i] != element3i[i])
789 element3s[i] = element3i[i];
796 // warning: this is an expensive function!
797 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
804 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
805 // process each vertex of each triangle and accumulate the results
806 // use area-averaging, to make triangles with a big area have a bigger
807 // weighting on the vertex normal than triangles with a small area
808 // to do so, just add the 'normals' together (the bigger the area
809 // the greater the length of the normal is
811 for (i = 0; i < numtriangles; i++, element += 3)
814 vertex3f + element[0] * 3,
815 vertex3f + element[1] * 3,
816 vertex3f + element[2] * 3,
821 VectorNormalize(areaNormal);
823 for (j = 0;j < 3;j++)
825 vectorNormal = normal3f + element[j] * 3;
826 vectorNormal[0] += areaNormal[0];
827 vectorNormal[1] += areaNormal[1];
828 vectorNormal[2] += areaNormal[2];
831 // and just normalize the accumulated vertex normal in the end
832 vectorNormal = normal3f + 3 * firstvertex;
833 for (i = 0; i < numvertices; i++, vectorNormal += 3)
834 VectorNormalize(vectorNormal);
838 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)
840 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
841 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
842 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
844 // 6 multiply, 9 subtract
845 VectorSubtract(v1, v0, v10);
846 VectorSubtract(v2, v0, v20);
847 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
848 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
849 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
850 // 12 multiply, 10 subtract
851 tc10[1] = tc1[1] - tc0[1];
852 tc20[1] = tc2[1] - tc0[1];
853 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
854 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
855 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
856 tc10[0] = tc1[0] - tc0[0];
857 tc20[0] = tc2[0] - tc0[0];
858 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
859 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
860 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
861 // 12 multiply, 4 add, 6 subtract
862 f = DotProduct(svector3f, normal3f);
863 svector3f[0] -= f * normal3f[0];
864 svector3f[1] -= f * normal3f[1];
865 svector3f[2] -= f * normal3f[2];
866 f = DotProduct(tvector3f, normal3f);
867 tvector3f[0] -= f * normal3f[0];
868 tvector3f[1] -= f * normal3f[1];
869 tvector3f[2] -= f * normal3f[2];
870 // if texture is mapped the wrong way (counterclockwise), the tangents
871 // have to be flipped, this is detected by calculating a normal from the
872 // two tangents, and seeing if it is opposite the surface normal
873 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
874 CrossProduct(tvector3f, svector3f, tangentcross);
875 if (DotProduct(tangentcross, normal3f) < 0)
877 VectorNegate(svector3f, svector3f);
878 VectorNegate(tvector3f, tvector3f);
883 // warning: this is a very expensive function!
884 void Mod_BuildTextureVectorsFromNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const float *texcoord2f, const float *normal3f, const int *elements, float *svector3f, float *tvector3f, qboolean areaweighting)
887 float sdir[3], tdir[3], normal[3], *svec, *tvec;
888 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
889 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
892 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
893 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
894 // process each vertex of each triangle and accumulate the results
895 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
897 v0 = vertex3f + e[0] * 3;
898 v1 = vertex3f + e[1] * 3;
899 v2 = vertex3f + e[2] * 3;
900 tc0 = texcoord2f + e[0] * 2;
901 tc1 = texcoord2f + e[1] * 2;
902 tc2 = texcoord2f + e[2] * 2;
904 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
905 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
907 // calculate the edge directions and surface normal
908 // 6 multiply, 9 subtract
909 VectorSubtract(v1, v0, v10);
910 VectorSubtract(v2, v0, v20);
911 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
912 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
913 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
915 // calculate the tangents
916 // 12 multiply, 10 subtract
917 tc10[1] = tc1[1] - tc0[1];
918 tc20[1] = tc2[1] - tc0[1];
919 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
920 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
921 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
922 tc10[0] = tc1[0] - tc0[0];
923 tc20[0] = tc2[0] - tc0[0];
924 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
925 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
926 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
928 // if texture is mapped the wrong way (counterclockwise), the tangents
929 // have to be flipped, this is detected by calculating a normal from the
930 // two tangents, and seeing if it is opposite the surface normal
931 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
932 CrossProduct(tdir, sdir, tangentcross);
933 if (DotProduct(tangentcross, normal) < 0)
935 VectorNegate(sdir, sdir);
936 VectorNegate(tdir, tdir);
941 VectorNormalize(sdir);
942 VectorNormalize(tdir);
944 for (i = 0;i < 3;i++)
946 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
947 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
950 // make the tangents completely perpendicular to the surface normal, and
951 // then normalize them
952 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
953 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
955 f = -DotProduct(svec, n);
956 VectorMA(svec, f, n, svec);
957 VectorNormalize(svec);
958 f = -DotProduct(tvec, n);
959 VectorMA(tvec, f, n, tvec);
960 VectorNormalize(tvec);
964 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors)
967 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));
968 loadmodel->surfmesh.num_vertices = numvertices;
969 loadmodel->surfmesh.num_triangles = numtriangles;
970 if (loadmodel->surfmesh.num_vertices)
972 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
973 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
974 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
975 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
976 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
977 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
979 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
981 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
983 if (loadmodel->surfmesh.num_triangles)
985 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
986 if (loadmodel->surfmesh.num_vertices <= 65536)
987 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
991 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
993 shadowmesh_t *newmesh;
994 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
995 newmesh->mempool = mempool;
996 newmesh->maxverts = maxverts;
997 newmesh->maxtriangles = maxtriangles;
998 newmesh->numverts = 0;
999 newmesh->numtriangles = 0;
1000 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1001 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1003 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
1004 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
1005 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
1006 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
1010 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
1012 int hashindex, vnum;
1013 shadowmeshvertexhash_t *hash;
1014 // this uses prime numbers intentionally
1015 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
1016 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1018 vnum = (hash - mesh->vertexhashentries);
1019 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1020 return hash - mesh->vertexhashentries;
1022 vnum = mesh->numverts++;
1023 hash = mesh->vertexhashentries + vnum;
1024 hash->next = mesh->vertexhashtable[hashindex];
1025 mesh->vertexhashtable[hashindex] = hash;
1026 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1027 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1028 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1032 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1036 for (i = 0;i < numtris;i++)
1038 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1039 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1040 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1041 mesh->numtriangles++;
1044 // the triangle calculation can take a while, so let's do a keepalive here
1045 CL_KeepaliveMessage(false);
1048 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1050 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1051 CL_KeepaliveMessage(false);
1053 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1056 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1058 if (!mesh->numverts)
1061 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1062 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1064 // upload short indices as a buffer
1065 if (mesh->element3s && !mesh->element3s_indexbuffer)
1066 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1068 // upload int indices as a buffer
1069 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1070 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1072 // vertex buffer is several arrays and we put them in the same buffer
1074 // is this wise? the texcoordtexture2f array is used with dynamic
1075 // vertex/svector/tvector/normal when rendering animated models, on the
1076 // other hand animated models don't use a lot of vertices anyway...
1077 if (!mesh->vbo_vertexbuffer)
1079 mesh->vbooffset_vertex3f = 0;
1080 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1084 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qboolean createvbo)
1086 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1088 if (mesh->vertexhashentries)
1089 Mem_Free(mesh->vertexhashentries);
1090 mesh->vertexhashentries = NULL;
1091 if (mesh->vertexhashtable)
1092 Mem_Free(mesh->vertexhashtable);
1093 mesh->vertexhashtable = NULL;
1094 if (mesh->maxverts > mesh->numverts)
1096 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1097 mesh->maxverts = mesh->numverts;
1099 if (mesh->maxtriangles > mesh->numtriangles)
1101 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1102 mesh->maxtriangles = mesh->numtriangles;
1104 if (mesh->numverts <= 65536)
1107 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1108 for (i = 0;i < mesh->numtriangles*3;i++)
1109 mesh->element3s[i] = mesh->element3i[i];
1112 Mod_ShadowMesh_CreateVBOs(mesh);
1115 // this can take a while, so let's do a keepalive here
1116 CL_KeepaliveMessage(false);
1121 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1124 vec3_t nmins, nmaxs, ncenter, temp;
1125 float nradius2, dist2, *v;
1129 VectorCopy(mesh->vertex3f, nmins);
1130 VectorCopy(mesh->vertex3f, nmaxs);
1131 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1133 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1134 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1135 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1137 // calculate center and radius
1138 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1139 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1140 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1142 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1144 VectorSubtract(v, ncenter, temp);
1145 dist2 = DotProduct(temp, temp);
1146 if (nradius2 < dist2)
1151 VectorCopy(nmins, mins);
1153 VectorCopy(nmaxs, maxs);
1155 VectorCopy(ncenter, center);
1157 *radius = sqrt(nradius2);
1160 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1162 if (mesh->element3i_indexbuffer)
1163 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1164 if (mesh->element3s_indexbuffer)
1165 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1166 if (mesh->vbo_vertexbuffer)
1167 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1169 Mem_Free(mesh->vertex3f);
1170 if (mesh->element3i)
1171 Mem_Free(mesh->element3i);
1172 if (mesh->element3s)
1173 Mem_Free(mesh->element3s);
1174 if (mesh->vertexhashentries)
1175 Mem_Free(mesh->vertexhashentries);
1176 if (mesh->vertexhashtable)
1177 Mem_Free(mesh->vertexhashtable);
1181 void Mod_CreateCollisionMesh(dp_model_t *mod)
1183 int k, numcollisionmeshtriangles;
1184 qboolean usesinglecollisionmesh = false;
1185 const msurface_t *surface = NULL;
1187 mempool_t *mempool = mod->mempool;
1188 if (!mempool && mod->brush.parentmodel)
1189 mempool = mod->brush.parentmodel->mempool;
1190 // make a single combined collision mesh for physics engine use
1191 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1192 numcollisionmeshtriangles = 0;
1193 for (k = 0;k < mod->nummodelsurfaces;k++)
1195 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1196 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1198 usesinglecollisionmesh = true;
1199 numcollisionmeshtriangles = surface->num_triangles;
1202 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1204 numcollisionmeshtriangles += surface->num_triangles;
1206 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1207 if (usesinglecollisionmesh)
1208 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1211 for (k = 0;k < mod->nummodelsurfaces;k++)
1213 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1214 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1216 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1219 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1223 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)
1228 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1229 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1232 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1233 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1234 texcoord2f[0] = tc[0];
1235 texcoord2f[1] = tc[1];
1238 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)
1240 float vup[3], vdown[3], vleft[3], vright[3];
1241 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1242 float sv[3], tv[3], nl[3];
1243 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1244 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1245 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1246 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1247 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1248 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1249 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1250 VectorAdd(svector3f, sv, svector3f);
1251 VectorAdd(tvector3f, tv, tvector3f);
1252 VectorAdd(normal3f, nl, normal3f);
1253 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1254 VectorAdd(svector3f, sv, svector3f);
1255 VectorAdd(tvector3f, tv, tvector3f);
1256 VectorAdd(normal3f, nl, normal3f);
1257 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1258 VectorAdd(svector3f, sv, svector3f);
1259 VectorAdd(tvector3f, tv, tvector3f);
1260 VectorAdd(normal3f, nl, normal3f);
1263 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)
1265 int x, y, ix, iy, *e;
1267 for (y = 0;y < height;y++)
1269 for (x = 0;x < width;x++)
1271 e[0] = (y + 1) * (width + 1) + (x + 0);
1272 e[1] = (y + 0) * (width + 1) + (x + 0);
1273 e[2] = (y + 1) * (width + 1) + (x + 1);
1274 e[3] = (y + 0) * (width + 1) + (x + 0);
1275 e[4] = (y + 0) * (width + 1) + (x + 1);
1276 e[5] = (y + 1) * (width + 1) + (x + 1);
1280 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1281 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1282 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1287 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1291 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1292 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1293 float viewvector[3];
1294 unsigned int firstvertex;
1297 if (chunkwidth < 2 || chunkheight < 2)
1299 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]);
1300 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]);
1301 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1302 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1303 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1304 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1306 // too close for this stepsize, emit as 4 chunks instead
1308 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1309 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1310 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1311 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1314 // emit the geometry at stepsize into our vertex buffer / index buffer
1315 // we add two columns and two rows for skirt
1316 outwidth = chunkwidth+2;
1317 outheight = chunkheight+2;
1318 outwidth2 = outwidth-1;
1319 outheight2 = outheight-1;
1320 outwidth3 = outwidth+1;
1321 outheight3 = outheight+1;
1322 firstvertex = numvertices;
1323 e = model->terrain.element3i + numtriangles;
1324 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1325 v = model->terrain.vertex3f + numvertices;
1326 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1327 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1328 for (ty = 0;ty < outheight;ty++)
1330 for (tx = 0;tx < outwidth;tx++)
1332 *e++ = firstvertex + (ty )*outwidth3+(tx );
1333 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1334 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1335 *e++ = firstvertex + (ty )*outwidth3+(tx );
1336 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1337 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1340 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1341 for (ty = 0;ty <= outheight;ty++)
1343 skirtrow = ty == 0 || ty == outheight;
1344 ry = y+bound(1, ty, outheight)*stepsize;
1345 for (tx = 0;tx <= outwidth;tx++)
1347 skirt = skirtrow || tx == 0 || tx == outwidth;
1348 rx = x+bound(1, tx, outwidth)*stepsize;
1351 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1355 // TODO: emit skirt vertices
1358 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1360 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1361 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1362 Mod_Terrain_BuildChunk(model,
1366 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1369 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1371 offset = bound(0, s[4] - '0', 9);
1372 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1377 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1378 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1379 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1380 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1381 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1382 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1383 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1384 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1385 return offset | Q3WAVEFUNC_NONE;
1388 void Mod_FreeQ3Shaders(void)
1390 Mem_FreePool(&q3shaders_mem);
1393 static void Q3Shader_AddToHash (shader_t* shader)
1395 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1396 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1397 q3shader_hash_entry_t* lastEntry = NULL;
1400 if (strcasecmp (entry->shader.name, shader->name) == 0)
1403 if(shader->dpshaderkill)
1405 // killed shader is a redeclarion? we can safely ignore it
1408 else if(entry->shader.dpshaderkill)
1410 // replace the old shader!
1411 // this will skip the entry allocating part
1412 // below and just replace the shader
1417 unsigned char *start, *end, *start2;
1418 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1419 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1420 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1421 if(memcmp(start, start2, end - start))
1422 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1424 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1429 entry = entry->chain;
1431 while (entry != NULL);
1434 if (lastEntry->shader.name[0] != 0)
1437 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1438 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1440 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1441 lastEntry->chain = newEntry;
1442 newEntry->chain = NULL;
1443 lastEntry = newEntry;
1445 /* else: head of chain, in hash entry array */
1448 memcpy (&entry->shader, shader, sizeof (shader_t));
1451 void Mod_LoadQ3Shaders(void)
1459 q3shaderinfo_layer_t *layer;
1461 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1462 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1463 unsigned long custsurfaceflags[256];
1464 int numcustsurfaceflags;
1465 qboolean dpshaderkill;
1467 Mod_FreeQ3Shaders();
1469 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1470 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1471 sizeof (q3shader_data_t));
1472 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1473 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1474 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1475 q3shaders_mem, sizeof (char**), 256);
1477 // parse custinfoparms.txt
1478 numcustsurfaceflags = 0;
1479 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1481 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1482 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1485 while (COM_ParseToken_QuakeC(&text, false))
1486 if (!strcasecmp(com_token, "}"))
1488 // custom surfaceflags section
1489 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1490 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1493 while(COM_ParseToken_QuakeC(&text, false))
1495 if (!strcasecmp(com_token, "}"))
1497 // register surfaceflag
1498 if (numcustsurfaceflags >= 256)
1500 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1504 j = (int)strlen(com_token)+1;
1505 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1506 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1508 if (COM_ParseToken_QuakeC(&text, false))
1509 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1511 custsurfaceflags[numcustsurfaceflags] = 0;
1512 numcustsurfaceflags++;
1520 search = FS_Search("scripts/*.shader", true, false, NULL);
1523 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1525 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1528 while (COM_ParseToken_QuakeC(&text, false))
1530 memset (&shader, 0, sizeof(shader));
1532 shader.surfaceparms = 0;
1533 shader.surfaceflags = 0;
1534 shader.textureflags = 0;
1535 shader.numlayers = 0;
1536 shader.lighting = false;
1537 shader.vertexalpha = false;
1538 shader.textureblendalpha = false;
1539 shader.skyboxname[0] = 0;
1540 shader.deforms[0].deform = Q3DEFORM_NONE;
1541 shader.dpnortlight = false;
1542 shader.dpshadow = false;
1543 shader.dpnoshadow = false;
1544 shader.dpmeshcollisions = false;
1545 shader.dpshaderkill = false;
1546 shader.dpreflectcube[0] = 0;
1547 shader.reflectmin = 0;
1548 shader.reflectmax = 1;
1549 shader.refractfactor = 1;
1550 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1551 shader.reflectfactor = 1;
1552 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1553 shader.r_water_wateralpha = 1;
1554 shader.r_water_waterscroll[0] = 0;
1555 shader.r_water_waterscroll[1] = 0;
1556 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1557 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1558 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1559 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1560 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1561 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1562 shader.specularscalemod = 1;
1563 shader.specularpowermod = 1;
1564 shader.rtlightambient = 0;
1565 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1566 // JUST GREP FOR "specularscalemod = 1".
1568 strlcpy(shader.name, com_token, sizeof(shader.name));
1569 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1571 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1574 while (COM_ParseToken_QuakeC(&text, false))
1576 if (!strcasecmp(com_token, "}"))
1578 if (!strcasecmp(com_token, "{"))
1580 static q3shaderinfo_layer_t dummy;
1581 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1583 layer = shader.layers + shader.numlayers++;
1587 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1588 memset(&dummy, 0, sizeof(dummy));
1591 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1592 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1593 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1594 layer->blendfunc[0] = GL_ONE;
1595 layer->blendfunc[1] = GL_ZERO;
1596 while (COM_ParseToken_QuakeC(&text, false))
1598 if (!strcasecmp(com_token, "}"))
1600 if (!strcasecmp(com_token, "\n"))
1603 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1605 if (j < TEXTURE_MAXFRAMES + 4)
1607 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1608 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1609 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1611 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1612 numparameters = j + 1;
1614 if (!COM_ParseToken_QuakeC(&text, true))
1617 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1618 // parameter[j][0] = 0;
1619 if (developer_insane.integer)
1621 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1622 for (j = 0;j < numparameters;j++)
1623 Con_DPrintf(" %s", parameter[j]);
1626 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1628 if (numparameters == 2)
1630 if (!strcasecmp(parameter[1], "add"))
1632 layer->blendfunc[0] = GL_ONE;
1633 layer->blendfunc[1] = GL_ONE;
1635 else if (!strcasecmp(parameter[1], "addalpha"))
1637 layer->blendfunc[0] = GL_SRC_ALPHA;
1638 layer->blendfunc[1] = GL_ONE;
1640 else if (!strcasecmp(parameter[1], "filter"))
1642 layer->blendfunc[0] = GL_DST_COLOR;
1643 layer->blendfunc[1] = GL_ZERO;
1645 else if (!strcasecmp(parameter[1], "blend"))
1647 layer->blendfunc[0] = GL_SRC_ALPHA;
1648 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1651 else if (numparameters == 3)
1654 for (k = 0;k < 2;k++)
1656 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1657 layer->blendfunc[k] = GL_ONE;
1658 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1659 layer->blendfunc[k] = GL_ZERO;
1660 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1661 layer->blendfunc[k] = GL_SRC_COLOR;
1662 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1663 layer->blendfunc[k] = GL_SRC_ALPHA;
1664 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1665 layer->blendfunc[k] = GL_DST_COLOR;
1666 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1667 layer->blendfunc[k] = GL_DST_ALPHA;
1668 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1669 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1670 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1671 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1672 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1673 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1674 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1675 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1677 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1681 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1682 layer->alphatest = true;
1683 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1685 if (!strcasecmp(parameter[0], "clampmap"))
1686 layer->clampmap = true;
1687 layer->numframes = 1;
1688 layer->framerate = 1;
1689 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1690 &q3shader_data->char_ptrs);
1691 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1692 if (!strcasecmp(parameter[1], "$lightmap"))
1693 shader.lighting = true;
1695 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1698 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1699 layer->framerate = atof(parameter[1]);
1700 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1701 for (i = 0;i < layer->numframes;i++)
1702 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1704 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1707 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1708 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1709 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1710 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1711 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1712 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1713 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1714 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1715 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1716 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1717 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1718 else if (!strcasecmp(parameter[1], "wave"))
1720 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1721 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1722 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1723 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1725 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1727 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1730 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1731 layer->alphagen.parms[i] = atof(parameter[i+2]);
1732 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1733 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1734 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1735 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1736 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1737 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1738 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1739 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1740 else if (!strcasecmp(parameter[1], "wave"))
1742 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1743 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1744 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1745 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1747 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1749 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1752 // observed values: tcgen environment
1753 // no other values have been observed in real shaders
1754 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1755 layer->tcgen.parms[i] = atof(parameter[i+2]);
1756 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1757 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1758 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1759 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1760 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1761 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1763 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1770 // tcmod stretch sin # # # #
1771 // tcmod stretch triangle # # # #
1772 // tcmod transform # # # # # #
1773 // tcmod turb # # # #
1774 // tcmod turb sin # # # # (this is bogus)
1775 // no other values have been observed in real shaders
1776 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1777 if (!layer->tcmods[tcmodindex].tcmod)
1779 if (tcmodindex < Q3MAXTCMODS)
1781 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1782 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1783 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1784 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1785 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1786 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1787 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1788 else if (!strcasecmp(parameter[1], "stretch"))
1790 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1791 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1792 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1793 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1795 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1796 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1797 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1800 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1802 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1803 if (!strcasecmp(com_token, "}"))
1806 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1807 shader.lighting = true;
1808 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1810 if (layer == shader.layers + 0)
1812 // vertex controlled transparency
1813 shader.vertexalpha = true;
1817 // multilayer terrain shader or similar
1818 shader.textureblendalpha = true;
1819 if (mod_q3shader_force_terrain_alphaflag.integer)
1820 shader.layers[0].dptexflags |= TEXF_ALPHA;
1824 if(mod_q3shader_force_addalpha.integer)
1826 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1827 // this cvar brings back this behaviour
1828 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1829 layer->blendfunc[0] = GL_SRC_ALPHA;
1832 layer->dptexflags = 0;
1833 if (layer->alphatest)
1834 layer->dptexflags |= TEXF_ALPHA;
1835 switch(layer->blendfunc[0])
1838 case GL_ONE_MINUS_SRC_ALPHA:
1839 layer->dptexflags |= TEXF_ALPHA;
1842 switch(layer->blendfunc[1])
1845 case GL_ONE_MINUS_SRC_ALPHA:
1846 layer->dptexflags |= TEXF_ALPHA;
1849 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1850 layer->dptexflags |= TEXF_MIPMAP;
1851 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1852 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1853 if (layer->clampmap)
1854 layer->dptexflags |= TEXF_CLAMP;
1858 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1860 if (j < TEXTURE_MAXFRAMES + 4)
1862 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1863 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1864 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1866 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1867 numparameters = j + 1;
1869 if (!COM_ParseToken_QuakeC(&text, true))
1872 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1873 // parameter[j][0] = 0;
1874 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1876 if (developer_insane.integer)
1878 Con_DPrintf("%s: ", shader.name);
1879 for (j = 0;j < numparameters;j++)
1880 Con_DPrintf(" %s", parameter[j]);
1883 if (numparameters < 1)
1885 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1887 if (!strcasecmp(parameter[1], "alphashadow"))
1888 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1889 else if (!strcasecmp(parameter[1], "areaportal"))
1890 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1891 else if (!strcasecmp(parameter[1], "botclip"))
1892 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1893 else if (!strcasecmp(parameter[1], "clusterportal"))
1894 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1895 else if (!strcasecmp(parameter[1], "detail"))
1896 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1897 else if (!strcasecmp(parameter[1], "donotenter"))
1898 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1899 else if (!strcasecmp(parameter[1], "dust"))
1900 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1901 else if (!strcasecmp(parameter[1], "hint"))
1902 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1903 else if (!strcasecmp(parameter[1], "fog"))
1904 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1905 else if (!strcasecmp(parameter[1], "lava"))
1906 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1907 else if (!strcasecmp(parameter[1], "lightfilter"))
1908 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1909 else if (!strcasecmp(parameter[1], "lightgrid"))
1910 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1911 else if (!strcasecmp(parameter[1], "metalsteps"))
1912 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1913 else if (!strcasecmp(parameter[1], "nodamage"))
1914 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1915 else if (!strcasecmp(parameter[1], "nodlight"))
1916 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1917 else if (!strcasecmp(parameter[1], "nodraw"))
1918 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1919 else if (!strcasecmp(parameter[1], "nodrop"))
1920 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1921 else if (!strcasecmp(parameter[1], "noimpact"))
1922 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1923 else if (!strcasecmp(parameter[1], "nolightmap"))
1924 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1925 else if (!strcasecmp(parameter[1], "nomarks"))
1926 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1927 else if (!strcasecmp(parameter[1], "nomipmaps"))
1928 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1929 else if (!strcasecmp(parameter[1], "nonsolid"))
1930 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1931 else if (!strcasecmp(parameter[1], "origin"))
1932 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1933 else if (!strcasecmp(parameter[1], "playerclip"))
1934 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1935 else if (!strcasecmp(parameter[1], "sky"))
1936 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1937 else if (!strcasecmp(parameter[1], "slick"))
1938 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1939 else if (!strcasecmp(parameter[1], "slime"))
1940 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1941 else if (!strcasecmp(parameter[1], "structural"))
1942 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1943 else if (!strcasecmp(parameter[1], "trans"))
1944 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1945 else if (!strcasecmp(parameter[1], "water"))
1946 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1947 else if (!strcasecmp(parameter[1], "pointlight"))
1948 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1949 else if (!strcasecmp(parameter[1], "antiportal"))
1950 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1951 else if (!strcasecmp(parameter[1], "skip"))
1952 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1955 // try custom surfaceparms
1956 for (j = 0; j < numcustsurfaceflags; j++)
1958 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1960 shader.surfaceflags |= custsurfaceflags[j];
1965 if (j == numcustsurfaceflags)
1966 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1969 else if (!strcasecmp(parameter[0], "dpshadow"))
1970 shader.dpshadow = true;
1971 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1972 shader.dpnoshadow = true;
1973 else if (!strcasecmp(parameter[0], "dpnortlight"))
1974 shader.dpnortlight = true;
1975 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1976 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1977 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1978 shader.dpmeshcollisions = true;
1979 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1980 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1982 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1983 shader.dpshaderkill = dpshaderkill;
1985 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1986 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1988 const char *op = NULL;
1989 if (numparameters >= 3)
1993 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
1994 shader.dpshaderkill = dpshaderkill;
1996 else if (numparameters >= 4 && !strcmp(op, "=="))
1998 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
1999 shader.dpshaderkill = dpshaderkill;
2001 else if (numparameters >= 4 && !strcmp(op, "!="))
2003 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
2004 shader.dpshaderkill = dpshaderkill;
2006 else if (numparameters >= 4 && !strcmp(op, ">"))
2008 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
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;
2028 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2031 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2033 // some q3 skies don't have the sky parm set
2034 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2035 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2037 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2039 // some q3 skies don't have the sky parm set
2040 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2041 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2042 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2044 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2046 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2047 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2049 else if (!strcasecmp(parameter[0], "nomipmaps"))
2050 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2051 else if (!strcasecmp(parameter[0], "nopicmip"))
2052 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2053 else if (!strcasecmp(parameter[0], "polygonoffset"))
2054 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2055 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2057 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2058 if(numparameters >= 2)
2060 shader.biaspolygonfactor = atof(parameter[1]);
2061 if(numparameters >= 3)
2062 shader.biaspolygonoffset = atof(parameter[2]);
2064 shader.biaspolygonoffset = 0;
2067 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2069 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2070 if (!strcasecmp(parameter[1], "sky"))
2071 shader.transparentsort = TRANSPARENTSORT_SKY;
2072 else if (!strcasecmp(parameter[1], "distance"))
2073 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2074 else if (!strcasecmp(parameter[1], "hud"))
2075 shader.transparentsort = TRANSPARENTSORT_HUD;
2077 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2079 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2081 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2082 shader.refractfactor = atof(parameter[1]);
2083 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2085 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2087 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2088 shader.reflectfactor = atof(parameter[1]);
2089 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2091 else if (!strcasecmp(parameter[0], "dpcamera"))
2093 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2095 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2097 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2098 shader.reflectmin = atof(parameter[1]);
2099 shader.reflectmax = atof(parameter[2]);
2100 shader.refractfactor = atof(parameter[3]);
2101 shader.reflectfactor = atof(parameter[4]);
2102 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2103 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2104 shader.r_water_wateralpha = atof(parameter[11]);
2106 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2108 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2109 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2111 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2113 shader.specularscalemod = atof(parameter[1]);
2115 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2117 shader.specularpowermod = atof(parameter[1]);
2119 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2121 shader.rtlightambient = atof(parameter[1]);
2123 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2125 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2126 shader.offsetmapping = OFFSETMAPPING_OFF;
2127 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2128 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2129 else if (!strcasecmp(parameter[1], "linear"))
2130 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2131 else if (!strcasecmp(parameter[1], "relief"))
2132 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2133 if (numparameters >= 3)
2134 shader.offsetscale = atof(parameter[2]);
2135 if (numparameters >= 5)
2137 if(!strcasecmp(parameter[3], "bias"))
2138 shader.offsetbias = atof(parameter[4]);
2139 else if(!strcasecmp(parameter[3], "match"))
2140 shader.offsetbias = 1.0f - atof(parameter[4]);
2141 else if(!strcasecmp(parameter[3], "match8"))
2142 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2143 else if(!strcasecmp(parameter[3], "match16"))
2144 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2147 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2150 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2151 if (!shader.deforms[deformindex].deform)
2153 if (deformindex < Q3MAXDEFORMS)
2155 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2156 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2157 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2158 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2159 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2160 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2161 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2162 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2163 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2164 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2165 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2166 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2167 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2168 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2169 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2170 else if (!strcasecmp(parameter[1], "wave" ))
2172 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2173 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2174 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2175 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2177 else if (!strcasecmp(parameter[1], "move" ))
2179 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2180 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2181 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2182 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2187 // hide this shader if a cvar said it should be killed
2188 if (shader.dpshaderkill)
2189 shader.numlayers = 0;
2190 // fix up multiple reflection types
2191 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2192 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2194 Q3Shader_AddToHash (&shader);
2198 FS_FreeSearch(search);
2199 // free custinfoparm values
2200 for (j = 0; j < numcustsurfaceflags; j++)
2201 Mem_Free(custsurfaceparmnames[j]);
2204 shader_t *Mod_LookupQ3Shader(const char *name)
2206 unsigned short hash;
2207 q3shader_hash_entry_t* entry;
2209 Mod_LoadQ3Shaders();
2210 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2211 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2212 while (entry != NULL)
2214 if (strcasecmp (entry->shader.name, name) == 0)
2215 return &entry->shader;
2216 entry = entry->chain;
2221 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2223 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2224 shaderpass->framerate = 0.0f;
2225 shaderpass->numframes = 1;
2226 shaderpass->blendfunc[0] = GL_ONE;
2227 shaderpass->blendfunc[1] = GL_ZERO;
2228 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2229 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2230 shaderpass->alphatest = false;
2231 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2232 shaderpass->skinframes[0] = skinframe;
2236 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2239 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2240 shaderpass->alphatest = layer->alphatest != 0;
2241 shaderpass->framerate = layer->framerate;
2242 shaderpass->numframes = layer->numframes;
2243 shaderpass->blendfunc[0] = layer->blendfunc[0];
2244 shaderpass->blendfunc[1] = layer->blendfunc[1];
2245 shaderpass->rgbgen = layer->rgbgen;
2246 shaderpass->alphagen = layer->alphagen;
2247 shaderpass->tcgen = layer->tcgen;
2248 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2249 shaderpass->tcmods[j] = layer->tcmods[j];
2250 for (j = 0; j < layer->numframes; j++)
2252 for (int i = 0; layer->texturename[j][i]; i++)
2253 if(layer->texturename[j][i] == '\\')
2254 layer->texturename[j][i] = '/';
2255 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2260 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2262 int texflagsmask, texflagsor;
2263 qboolean success = true;
2267 strlcpy(texture->name, name, sizeof(texture->name));
2268 texture->basealpha = 1.0f;
2269 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2271 // allow disabling of picmip or compression by defaulttexflags
2273 if(!(defaulttexflags & TEXF_PICMIP))
2274 texflagsmask &= ~TEXF_PICMIP;
2275 if(!(defaulttexflags & TEXF_COMPRESS))
2276 texflagsmask &= ~TEXF_COMPRESS;
2278 if(defaulttexflags & TEXF_ISWORLD)
2279 texflagsor |= TEXF_ISWORLD;
2280 if(defaulttexflags & TEXF_ISSPRITE)
2281 texflagsor |= TEXF_ISSPRITE;
2282 // unless later loaded from the shader
2283 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2284 texture->offsetscale = 1;
2285 texture->offsetbias = 0;
2286 texture->specularscalemod = 1;
2287 texture->specularpowermod = 1;
2288 texture->rtlightambient = 0;
2289 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2290 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2291 // JUST GREP FOR "specularscalemod = 1".
2295 if (developer_loading.integer)
2296 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2298 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2300 texture->basematerialflags = MATERIALFLAG_SKY;
2301 if (shader->skyboxname[0] && loadmodel)
2303 // quake3 seems to append a _ to the skybox name, so this must do so as well
2304 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2307 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2308 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2310 texture->basematerialflags = MATERIALFLAG_WALL;
2312 if (shader->layers[0].alphatest)
2313 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2314 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2315 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2316 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2318 texture->biaspolygonoffset += shader->biaspolygonoffset;
2319 texture->biaspolygonfactor += shader->biaspolygonfactor;
2321 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2322 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2323 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2324 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2325 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2326 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2327 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2328 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2329 texture->customblendfunc[0] = GL_ONE;
2330 texture->customblendfunc[1] = GL_ZERO;
2331 texture->transparentsort = shader->transparentsort;
2332 if (shader->numlayers > 0)
2334 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2335 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2337 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2338 * additive GL_ONE GL_ONE
2339 additive weird GL_ONE GL_SRC_ALPHA
2340 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2341 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2342 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2343 brighten GL_DST_COLOR GL_ONE
2344 brighten GL_ONE GL_SRC_COLOR
2345 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2346 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2347 * modulate GL_DST_COLOR GL_ZERO
2348 * modulate GL_ZERO GL_SRC_COLOR
2349 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2350 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2351 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2352 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2353 * no blend GL_ONE GL_ZERO
2354 nothing GL_ZERO GL_ONE
2356 // if not opaque, figure out what blendfunc to use
2357 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2359 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2360 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2361 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2362 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2363 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2364 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2366 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2369 if (!shader->lighting)
2370 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2372 // here be dragons: convert quake3 shaders to material
2373 if (shader->numlayers > 0)
2376 int terrainbackgroundlayer = -1;
2377 int lightmaplayer = -1;
2378 int alphagenspecularlayer = -1;
2379 int rgbgenvertexlayer = -1;
2380 int rgbgendiffuselayer = -1;
2381 int materiallayer = -1;
2382 int endofprelayers = 0;
2383 int firstpostlayer = 0;
2384 int shaderpassindex = 0;
2385 for (i = 0; i < shader->numlayers; i++)
2387 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2389 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2390 rgbgenvertexlayer = i;
2391 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2392 rgbgendiffuselayer = i;
2393 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2394 alphagenspecularlayer = i;
2396 if (shader->numlayers >= 2
2397 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2398 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2399 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2400 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2402 // terrain blend or certain other effects involving alphatest over a regular layer
2403 terrainbackgroundlayer = 0;
2405 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2406 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2408 else if (lightmaplayer == 0)
2410 // ordinary texture but with $lightmap before diffuse
2412 firstpostlayer = lightmaplayer + 2;
2414 else if (lightmaplayer >= 1)
2416 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2417 endofprelayers = lightmaplayer - 1;
2418 materiallayer = lightmaplayer - 1;
2419 firstpostlayer = lightmaplayer + 1;
2421 else if (rgbgenvertexlayer >= 0)
2423 // map models with baked lighting
2424 materiallayer = rgbgenvertexlayer;
2425 endofprelayers = rgbgenvertexlayer;
2426 firstpostlayer = rgbgenvertexlayer + 1;
2427 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2428 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2429 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2431 else if (rgbgendiffuselayer >= 0)
2433 // entity models with dynamic lighting
2434 materiallayer = rgbgendiffuselayer;
2435 endofprelayers = rgbgendiffuselayer;
2436 firstpostlayer = rgbgendiffuselayer + 1;
2437 // 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)...
2438 if (alphagenspecularlayer >= 0)
2439 firstpostlayer = alphagenspecularlayer + 1;
2443 // special effects shaders - treat first as primary layer and do everything else as post
2448 // convert the main material layer
2449 // 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
2450 if (materiallayer >= 0)
2451 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2452 // convert the terrain background blend layer (if any)
2453 if (terrainbackgroundlayer >= 0)
2454 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2455 // convert the prepass layers (if any)
2456 texture->startpreshaderpass = shaderpassindex;
2457 for (i = 0; i < endofprelayers; i++)
2458 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2459 texture->endpreshaderpass = shaderpassindex;
2460 texture->startpostshaderpass = shaderpassindex;
2461 // convert the postpass layers (if any)
2462 for (i = firstpostlayer; i < shader->numlayers; i++)
2463 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2464 texture->startpostshaderpass = shaderpassindex;
2467 if (shader->dpshadow)
2468 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2469 if (shader->dpnoshadow)
2470 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2471 if (shader->dpnortlight)
2472 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2473 if (shader->vertexalpha)
2474 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2475 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2476 texture->reflectmin = shader->reflectmin;
2477 texture->reflectmax = shader->reflectmax;
2478 texture->refractfactor = shader->refractfactor;
2479 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2480 texture->reflectfactor = shader->reflectfactor;
2481 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2482 texture->r_water_wateralpha = shader->r_water_wateralpha;
2483 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2484 texture->offsetmapping = shader->offsetmapping;
2485 texture->offsetscale = shader->offsetscale;
2486 texture->offsetbias = shader->offsetbias;
2487 texture->specularscalemod = shader->specularscalemod;
2488 texture->specularpowermod = shader->specularpowermod;
2489 texture->rtlightambient = shader->rtlightambient;
2490 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2491 if (shader->dpreflectcube[0])
2492 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2494 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2495 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2496 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2497 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2498 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2499 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2500 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2501 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2502 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2504 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2505 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2506 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2507 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2508 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2509 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2510 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2511 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2512 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2513 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2514 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2515 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2516 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2520 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2521 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2523 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2524 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2525 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2526 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2527 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2528 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2529 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2530 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2531 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2532 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2534 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2537 texture->surfaceflags = shader->surfaceflags;
2538 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2539 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2540 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2541 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2542 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2543 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2544 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2545 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2546 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2547 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2548 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2549 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2550 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2551 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2552 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2553 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2554 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2555 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2556 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2557 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2558 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2559 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2560 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2561 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2562 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2563 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2564 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2565 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2566 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2567 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2568 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2569 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2571 if (shader->dpmeshcollisions)
2572 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2573 if (shader->dpshaderkill && developer_extra.integer)
2574 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2576 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2578 if (developer_extra.integer)
2579 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2580 texture->basematerialflags = defaultmaterialflags;
2581 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2583 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2585 if (developer_extra.integer)
2586 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2587 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2588 texture->supercontents = SUPERCONTENTS_SOLID;
2592 if (developer_extra.integer)
2593 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2594 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2596 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2597 texture->supercontents = SUPERCONTENTS_SOLID;
2599 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2601 texture->basematerialflags = MATERIALFLAG_SKY;
2602 texture->supercontents = SUPERCONTENTS_SKY;
2606 texture->basematerialflags = defaultmaterialflags;
2607 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2609 if(cls.state == ca_dedicated)
2611 texture->materialshaderpass = NULL;
2616 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2619 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2620 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2621 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2622 if (texture->q2contents)
2623 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2627 if (!success && warnmissing)
2628 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2631 // init the animation variables
2632 texture->currentframe = texture;
2633 texture->currentmaterialflags = texture->basematerialflags;
2634 if (!texture->materialshaderpass)
2635 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2636 if (!texture->materialshaderpass->skinframes[0])
2637 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2638 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2639 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2643 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2645 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2646 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2648 strlcpy(texture->name, name, sizeof(texture->name));
2649 texture->basealpha = 1.0f;
2650 texture->basematerialflags = materialflags;
2651 texture->supercontents = supercontents;
2653 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2654 texture->offsetscale = 1;
2655 texture->offsetbias = 0;
2656 texture->specularscalemod = 1;
2657 texture->specularpowermod = 1;
2658 texture->rtlightambient = 0;
2659 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2660 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2661 // JUST GREP FOR "specularscalemod = 1".
2663 if (developer_extra.integer)
2664 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2666 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2668 // init the animation variables
2669 texture->currentmaterialflags = texture->basematerialflags;
2670 texture->currentframe = texture;
2671 texture->currentskinframe = skinframe;
2672 texture->backgroundcurrentskinframe = NULL;
2675 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2677 long unsigned int i, j;
2678 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2680 if (texture->shaderpasses[i])
2683 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2684 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2685 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2686 Mem_Free(texture->shaderpasses[i]);
2687 texture->shaderpasses[i] = NULL;
2690 texture->materialshaderpass = NULL;
2691 texture->currentskinframe = NULL;
2692 texture->backgroundcurrentskinframe = NULL;
2695 skinfile_t *Mod_LoadSkinFiles(void)
2697 int i, words, line, wordsoverflow;
2700 skinfile_t *skinfile = NULL, *first = NULL;
2701 skinfileitem_t *skinfileitem;
2702 char word[10][MAX_QPATH];
2707 U_bodyBox,models/players/Legoman/BikerA2.tga
2708 U_RArm,models/players/Legoman/BikerA1.tga
2709 U_LArm,models/players/Legoman/BikerA1.tga
2710 U_armor,common/nodraw
2711 U_sword,common/nodraw
2712 U_shield,common/nodraw
2713 U_homb,common/nodraw
2714 U_backpack,common/nodraw
2715 U_colcha,common/nodraw
2720 memset(word, 0, sizeof(word));
2721 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2723 // If it's the first file we parse
2724 if (skinfile == NULL)
2726 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2731 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2732 skinfile = skinfile->next;
2734 skinfile->next = NULL;
2736 for(line = 0;;line++)
2739 if (!COM_ParseToken_QuakeC(&data, true))
2741 if (!strcmp(com_token, "\n"))
2744 wordsoverflow = false;
2748 strlcpy(word[words++], com_token, sizeof (word[0]));
2750 wordsoverflow = true;
2752 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2755 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);
2758 // words is always >= 1
2759 if (!strcmp(word[0], "replace"))
2763 if (developer_loading.integer)
2764 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2765 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2766 skinfileitem->next = skinfile->items;
2767 skinfile->items = skinfileitem;
2768 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2769 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2772 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]);
2774 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2776 // tag name, like "tag_weapon,"
2777 // not used for anything (not even in Quake3)
2779 else if (words >= 2 && !strcmp(word[1], ","))
2781 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2782 if (developer_loading.integer)
2783 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2784 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2785 skinfileitem->next = skinfile->items;
2786 skinfile->items = skinfileitem;
2787 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2788 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2791 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);
2796 loadmodel->numskins = i;
2800 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2803 skinfileitem_t *skinfileitem, *nextitem;
2804 for (;skinfile;skinfile = next)
2806 next = skinfile->next;
2807 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2809 nextitem = skinfileitem->next;
2810 Mem_Free(skinfileitem);
2816 int Mod_CountSkinFiles(skinfile_t *skinfile)
2819 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2823 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2826 double isnap = 1.0 / snap;
2827 for (i = 0;i < numvertices*numcomponents;i++)
2828 vertices[i] = floor(vertices[i]*isnap)*snap;
2831 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2833 int i, outtriangles;
2834 float edgedir1[3], edgedir2[3], temp[3];
2835 // a degenerate triangle is one with no width (thickness, surface area)
2836 // these are characterized by having all 3 points colinear (along a line)
2837 // or having two points identical
2838 // the simplest check is to calculate the triangle's area
2839 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2841 // calculate first edge
2842 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2843 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2844 CrossProduct(edgedir1, edgedir2, temp);
2845 if (VectorLength2(temp) < 0.001f)
2846 continue; // degenerate triangle (no area)
2847 // valid triangle (has area)
2848 VectorCopy(inelement3i, outelement3i);
2852 return outtriangles;
2855 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2858 int firstvertex, lastvertex;
2859 if (numelements > 0 && elements)
2861 firstvertex = lastvertex = elements[0];
2862 for (i = 1;i < numelements;i++)
2865 firstvertex = min(firstvertex, e);
2866 lastvertex = max(lastvertex, e);
2870 firstvertex = lastvertex = 0;
2871 if (firstvertexpointer)
2872 *firstvertexpointer = firstvertex;
2873 if (lastvertexpointer)
2874 *lastvertexpointer = lastvertex;
2877 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2879 // make an optimal set of texture-sorted batches to draw...
2881 int *firstsurfacefortexture;
2882 int *numsurfacesfortexture;
2883 if (!mod->sortedmodelsurfaces)
2884 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2885 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2886 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2887 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2888 for (j = 0;j < mod->nummodelsurfaces;j++)
2890 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2891 if(!surface->texture)
2893 t = (int)(surface->texture - mod->data_textures);
2894 numsurfacesfortexture[t]++;
2897 for (t = 0;t < mod->num_textures;t++)
2899 firstsurfacefortexture[t] = j;
2900 j += numsurfacesfortexture[t];
2902 for (j = 0;j < mod->nummodelsurfaces;j++)
2904 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2905 if (!surface->texture)
2907 t = (int)(surface->texture - mod->data_textures);
2908 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2910 Mem_Free(firstsurfacefortexture);
2911 Mem_Free(numsurfacesfortexture);
2914 void Mod_BuildVBOs(void)
2916 if(cls.state == ca_dedicated)
2919 if (!loadmodel->surfmesh.num_vertices)
2922 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2925 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2927 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2929 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2930 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2935 // upload short indices as a buffer
2936 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2937 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);
2939 // upload int indices as a buffer
2940 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2941 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);
2943 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2944 // we put several vertex data streams in the same buffer
2945 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2950 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2951 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2952 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2953 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2954 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2955 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2956 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2957 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2958 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2959 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2960 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2961 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2962 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2963 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2964 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2965 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2966 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2967 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2968 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2969 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2970 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2971 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2972 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2973 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2974 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2975 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2976 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2977 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2982 extern cvar_t mod_obj_orientation;
2983 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2985 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2987 const char *texname;
2989 const float *v, *vn, *vt;
2991 size_t outbufferpos = 0;
2992 size_t outbuffermax = 0x100000;
2993 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
2994 const msurface_t *surface;
2995 const int maxtextures = 256;
2996 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
2997 dp_model_t *submodel;
2999 // construct the mtllib file
3000 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3003 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3006 countvertices += surface->num_vertices;
3007 countfaces += surface->num_triangles;
3008 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3009 for (textureindex = 0;textureindex < counttextures;textureindex++)
3010 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3012 if (textureindex < counttextures)
3013 continue; // already wrote this material entry
3014 if (textureindex >= maxtextures)
3015 continue; // just a precaution
3016 textureindex = counttextures++;
3017 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3018 if (outbufferpos >= outbuffermax >> 1)
3021 oldbuffer = outbuffer;
3022 outbuffer = (char *) Z_Malloc(outbuffermax);
3023 memcpy(outbuffer, oldbuffer, outbufferpos);
3026 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");
3031 // write the mtllib file
3032 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3034 // construct the obj file
3036 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);
3040 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)
3042 if (outbufferpos >= outbuffermax >> 1)
3045 oldbuffer = outbuffer;
3046 outbuffer = (char *) Z_Malloc(outbuffermax);
3047 memcpy(outbuffer, oldbuffer, outbufferpos);
3050 if(mod_obj_orientation.integer)
3051 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]);
3053 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]);
3058 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3060 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3063 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3064 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3066 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3067 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3070 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3072 if (outbufferpos >= outbuffermax >> 1)
3075 oldbuffer = outbuffer;
3076 outbuffer = (char *) Z_Malloc(outbuffermax);
3077 memcpy(outbuffer, oldbuffer, outbufferpos);
3083 if(mod_obj_orientation.integer)
3084 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);
3086 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);
3093 // write the obj file
3094 FS_WriteFile(filename, outbuffer, outbufferpos);
3098 Z_Free(texturenames);
3101 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3104 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3106 int countnodes = 0, counttriangles = 0, countframes = 0;
3114 size_t outbufferpos = 0;
3115 size_t outbuffermax = 0x100000;
3116 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3117 const msurface_t *surface;
3118 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3121 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3123 if (outbufferpos >= outbuffermax >> 1)
3126 oldbuffer = outbuffer;
3127 outbuffer = (char *) Z_Malloc(outbuffermax);
3128 memcpy(outbuffer, oldbuffer, outbufferpos);
3132 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3136 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3139 for (poseindex = 0;poseindex < numposes;poseindex++)
3142 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3145 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3149 matrix4x4_t posematrix;
3150 if (outbufferpos >= outbuffermax >> 1)
3153 oldbuffer = outbuffer;
3154 outbuffer = (char *) Z_Malloc(outbuffermax);
3155 memcpy(outbuffer, oldbuffer, outbufferpos);
3159 // strangely the smd angles are for a transposed matrix, so we
3160 // have to generate a transposed matrix, then convert that...
3161 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3162 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3163 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3164 if (angles[0] >= 180) angles[0] -= 360;
3165 if (angles[1] >= 180) angles[1] -= 360;
3166 if (angles[2] >= 180) angles[2] -= 360;
3170 float a = DEG2RAD(angles[ROLL]);
3171 float b = DEG2RAD(angles[PITCH]);
3172 float c = DEG2RAD(angles[YAW]);
3173 float cy, sy, cp, sp, cr, sr;
3175 // smd matrix construction, for comparing
3186 test[1][0] = sr*sp*cy+cr*-sy;
3187 test[1][1] = sr*sp*sy+cr*cy;
3189 test[2][0] = (cr*sp*cy+-sr*-sy);
3190 test[2][1] = (cr*sp*sy+-sr*cy);
3192 test[3][0] = pose[9];
3193 test[3][1] = pose[10];
3194 test[3][2] = pose[11];
3197 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]));
3202 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3207 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3210 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3212 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3215 if (outbufferpos >= outbuffermax >> 1)
3218 oldbuffer = outbuffer;
3219 outbuffer = (char *) Z_Malloc(outbuffermax);
3220 memcpy(outbuffer, oldbuffer, outbufferpos);
3223 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3226 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3228 const int index = e[2-cornerindex];
3229 const float *v = model->surfmesh.data_vertex3f + index * 3;
3230 const float *vn = model->surfmesh.data_normal3f + index * 3;
3231 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3232 const int b = model->surfmesh.blends[index];
3233 if (b < model->num_bones)
3234 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]);
3237 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3238 const unsigned char *wi = w->index;
3239 const unsigned char *wf = w->influence;
3240 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);
3241 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);
3242 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);
3243 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]);
3250 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3255 FS_WriteFile(filename, outbuffer, outbufferpos);
3258 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3265 decompiles a model to editable files
3268 static void Mod_Decompile_f(cmd_state_t *cmd)
3270 int i, j, k, l, first, count;
3272 char inname[MAX_QPATH];
3273 char outname[MAX_QPATH];
3274 char mtlname[MAX_QPATH];
3275 char basename[MAX_QPATH];
3276 char animname[MAX_QPATH];
3277 char animname2[MAX_QPATH];
3278 char zymtextbuffer[16384];
3279 char dpmtextbuffer[16384];
3280 char framegroupstextbuffer[16384];
3281 int zymtextsize = 0;
3282 int dpmtextsize = 0;
3283 int framegroupstextsize = 0;
3286 if (Cmd_Argc(cmd) != 2)
3288 Con_Print("usage: modeldecompile <filename>\n");
3292 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3293 FS_StripExtension(inname, basename, sizeof(basename));
3295 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3298 Con_Print("No such model\n");
3301 if (mod->brush.submodel)
3303 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3304 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3305 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3308 if (!mod->surfmesh.num_triangles)
3310 Con_Print("Empty model (or sprite)\n");
3314 // export OBJ if possible (not on sprites)
3315 if (mod->surfmesh.num_triangles)
3317 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3318 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3319 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3322 // export SMD if possible (only for skeletal models)
3323 if (mod->surfmesh.num_triangles && mod->num_bones)
3325 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3326 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3327 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3328 if (l > 0) zymtextsize += l;
3329 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3330 if (l > 0) dpmtextsize += l;
3331 for (i = 0;i < mod->numframes;i = j)
3333 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3334 first = mod->animscenes[i].firstframe;
3335 if (mod->animscenes[i].framecount > 1)
3338 count = mod->animscenes[i].framecount;
3344 // check for additional frames with same name
3345 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3346 if(animname[l] < '0' || animname[l] > '9')
3348 if(k > 0 && animname[k-1] == '_')
3351 count = mod->num_poses - first;
3352 for (j = i + 1;j < mod->numframes;j++)
3354 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3355 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3356 if(animname2[l] < '0' || animname2[l] > '9')
3358 if(k > 0 && animname[k-1] == '_')
3361 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3363 count = mod->animscenes[j].firstframe - first;
3367 // if it's only one frame, use the original frame name
3369 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3372 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3373 Mod_Decompile_SMD(mod, outname, first, count, false);
3374 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3376 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3377 if (l > 0) zymtextsize += l;
3379 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3381 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3382 if (l > 0) dpmtextsize += l;
3384 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3386 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3387 if (l > 0) framegroupstextsize += l;
3391 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3393 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3394 if (framegroupstextsize)
3395 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3399 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3402 memset(state, 0, sizeof(*state));
3403 state->width = width;
3404 state->height = height;
3405 state->currentY = 0;
3406 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3407 for (y = 0;y < state->height;y++)
3409 state->rows[y].currentX = 0;
3410 state->rows[y].rowY = -1;
3414 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3417 state->currentY = 0;
3418 for (y = 0;y < state->height;y++)
3420 state->rows[y].currentX = 0;
3421 state->rows[y].rowY = -1;
3425 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3428 Mem_Free(state->rows);
3429 memset(state, 0, sizeof(*state));
3432 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3434 mod_alloclightmap_row_t *row;
3437 row = state->rows + blockheight;
3438 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3440 if (state->currentY + blockheight <= state->height)
3442 // use the current allocation position
3443 row->rowY = state->currentY;
3445 state->currentY += blockheight;
3449 // find another position
3450 for (y = blockheight;y < state->height;y++)
3452 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3454 row = state->rows + y;
3458 if (y == state->height)
3463 *outx = row->currentX;
3464 row->currentX += blockwidth;
3469 typedef struct lightmapsample_s
3473 float *vertex_color;
3474 unsigned char *lm_bgr;
3475 unsigned char *lm_dir;
3479 typedef struct lightmapvertex_s
3484 float texcoordbase[2];
3485 float texcoordlightmap[2];
3486 float lightcolor[4];
3490 typedef struct lightmaptriangle_s
3498 // 2D modelspace coordinates of min corner
3499 // snapped to lightmap grid but not in grid coordinates
3501 // 2D modelspace to lightmap coordinate scale
3509 typedef struct lightmaplight_s
3520 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3522 #define MAX_LIGHTMAPSAMPLES 64
3523 static int mod_generatelightmaps_numoffsets[3];
3524 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3526 static int mod_generatelightmaps_numlights;
3527 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3529 extern cvar_t r_shadow_lightattenuationdividebias;
3530 extern cvar_t r_shadow_lightattenuationlinearscale;
3532 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3537 float relativepoint[3];
3544 float lightorigin[3];
3548 float lightcolor[3];
3550 for (i = 0;i < 5*3;i++)
3552 for (index = 0;;index++)
3554 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3559 lightradius2 = lightradius * lightradius;
3560 VectorSubtract(lightorigin, pos, relativepoint);
3561 dist2 = VectorLength2(relativepoint);
3562 if (dist2 >= lightradius2)
3564 lightiradius = 1.0f / lightradius;
3565 dist = sqrt(dist2) * lightiradius;
3566 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3567 if (intensity <= 0.0f)
3569 if (model && model->TraceLine)
3571 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3572 if (trace.fraction < 1)
3575 // scale down intensity to add to both ambient and diffuse
3576 //intensity *= 0.5f;
3577 VectorNormalize(relativepoint);
3578 VectorScale(lightcolor, intensity, color);
3579 VectorMA(sample , 0.5f , color, sample );
3580 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3581 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3582 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3583 // calculate a weighted average light direction as well
3584 intensity *= VectorLength(color);
3585 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3587 // calculate the direction we'll use to reduce the sample to a directional light source
3588 VectorCopy(sample + 12, dir);
3589 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3590 VectorNormalize(dir);
3591 // extract the diffuse color along the chosen direction and scale it
3592 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3593 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3594 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3595 // subtract some of diffuse from ambient
3596 VectorMA(sample, -0.333f, diffuse, ambient);
3597 // store the normalized lightdir
3598 VectorCopy(dir, lightdir);
3601 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3605 const msurface_t *surface;
3606 const float *vertex3f = model->surfmesh.data_vertex3f;
3607 const int *element3i = model->surfmesh.data_element3i;
3610 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3612 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3614 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3616 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3618 VectorCopy(vertex3f + 3*e[0], v2[0]);
3619 VectorCopy(vertex3f + 3*e[1], v2[1]);
3620 VectorCopy(vertex3f + 3*e[2], v2[2]);
3621 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3626 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3628 int maxnodes = 1<<14;
3629 svbsp_node_t *nodes;
3634 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3635 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3636 VectorCopy(lightinfo->origin, origin);
3637 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3640 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3641 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3642 if (svbsp.ranoutofnodes)
3645 if (maxnodes > 1<<22)
3651 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3656 if (svbsp.numnodes > 0)
3658 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3659 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3660 lightinfo->svbsp = svbsp;
3665 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3669 lightmaplight_t *lightinfo;
3673 mod_generatelightmaps_numlights = 0;
3674 for (index = 0;;index++)
3676 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3680 mod_generatelightmaps_numlights++;
3682 if (mod_generatelightmaps_numlights > 0)
3684 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3685 lightinfo = mod_generatelightmaps_lightinfo;
3686 for (index = 0;;index++)
3688 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3695 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3697 lightinfo->iradius = 1.0f / lightinfo->radius;
3698 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3699 // TODO: compute svbsp
3700 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3704 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3707 if (mod_generatelightmaps_lightinfo)
3709 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3710 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3711 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3712 Mem_Free(mod_generatelightmaps_lightinfo);
3714 mod_generatelightmaps_lightinfo = NULL;
3715 mod_generatelightmaps_numlights = 0;
3718 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3720 const svbsp_node_t *node;
3721 const svbsp_node_t *nodes = svbsp->nodes;
3726 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3728 return num == -1; // true if empty, false if solid (shadowed)
3731 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3734 float relativepoint[3];
3743 const lightmaplight_t *lightinfo;
3745 for (i = 0;i < 5*3;i++)
3747 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3749 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3750 VectorSubtract(lightinfo->origin, pos, relativepoint);
3751 // don't accept light from behind a surface, it causes bad shading
3752 if (normal && DotProduct(relativepoint, normal) <= 0)
3754 dist2 = VectorLength2(relativepoint);
3755 if (dist2 >= lightinfo->radius2)
3757 dist = sqrt(dist2) * lightinfo->iradius;
3758 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3761 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3765 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3767 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3769 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3772 // for light grid we'd better check visibility of the offset point
3773 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3774 if (trace.fraction < 1)
3775 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3778 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3783 // scale intensity according to how many rays succeeded
3784 // we know one test is valid, half of the rest will fail...
3785 //if (normal && tests > 1)
3786 // intensity *= (tests - 1.0f) / tests;
3787 intensity *= (float)hits / tests;
3789 // scale down intensity to add to both ambient and diffuse
3790 //intensity *= 0.5f;
3791 VectorNormalize(relativepoint);
3792 VectorScale(lightinfo->color, intensity, color);
3793 VectorMA(sample , 0.5f , color, sample );
3794 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3795 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3796 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3797 // calculate a weighted average light direction as well
3798 intensity *= VectorLength(color);
3799 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3803 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3809 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3810 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3811 VectorCopy(sample + 12, dir);
3812 VectorNormalize(dir);
3813 //VectorAdd(dir, normal, dir);
3814 //VectorNormalize(dir);
3815 f = DotProduct(dir, normal);
3816 f = max(0, f) * 255.0f;
3817 VectorScale(sample, f, color);
3818 //VectorCopy(normal, dir);
3819 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3820 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3821 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3822 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3824 lm_dir[0] = (unsigned char)dir[2];
3825 lm_dir[1] = (unsigned char)dir[1];
3826 lm_dir[2] = (unsigned char)dir[0];
3830 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3833 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3834 VectorCopy(sample, vertex_color);
3837 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3843 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3844 // calculate the direction we'll use to reduce the sample to a directional light source
3845 VectorCopy(sample + 12, dir);
3846 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3847 VectorNormalize(dir);
3848 // extract the diffuse color along the chosen direction and scale it
3849 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3850 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3851 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3852 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3853 VectorScale(sample, 127.5f, ambient);
3854 VectorMA(ambient, -0.333f, diffuse, ambient);
3855 // encode to the grid format
3856 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3857 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3858 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3859 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3860 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3861 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3862 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3863 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3864 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));}
3867 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3872 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3873 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3874 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3875 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3876 radius[0] = mod_generatelightmaps_lightmapradius.value;
3877 radius[1] = mod_generatelightmaps_vertexradius.value;
3878 radius[2] = mod_generatelightmaps_gridradius.value;
3879 for (i = 0;i < 3;i++)
3881 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3884 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3889 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3891 msurface_t *surface;
3894 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3896 surface = model->data_surfaces + surfaceindex;
3897 surface->lightmaptexture = NULL;
3898 surface->deluxemaptexture = NULL;
3900 if (model->brushq3.data_lightmaps)
3902 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3903 if (model->brushq3.data_lightmaps[i])
3904 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3905 Mem_Free(model->brushq3.data_lightmaps);
3906 model->brushq3.data_lightmaps = NULL;
3908 if (model->brushq3.data_deluxemaps)
3910 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3911 if (model->brushq3.data_deluxemaps[i])
3912 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3913 Mem_Free(model->brushq3.data_deluxemaps);
3914 model->brushq3.data_deluxemaps = NULL;
3918 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3920 msurface_t *surface;
3926 surfmesh_t oldsurfmesh;
3928 unsigned char *data;
3929 oldsurfmesh = model->surfmesh;
3930 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3931 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3933 size += model->surfmesh.num_vertices * sizeof(float[3]);
3934 size += model->surfmesh.num_vertices * sizeof(float[3]);
3935 size += model->surfmesh.num_vertices * sizeof(float[3]);
3936 size += model->surfmesh.num_vertices * sizeof(float[3]);
3937 size += model->surfmesh.num_vertices * sizeof(float[2]);
3938 size += model->surfmesh.num_vertices * sizeof(float[2]);
3939 size += model->surfmesh.num_vertices * sizeof(float[4]);
3940 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3941 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3942 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3943 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3944 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3945 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3946 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3947 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3948 if (model->surfmesh.num_vertices > 65536)
3949 model->surfmesh.data_element3s = NULL;
3951 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3952 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3953 model->surfmesh.data_element3i_indexbuffer = NULL;
3954 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3955 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3956 model->surfmesh.data_element3s_indexbuffer = NULL;
3957 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3958 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3959 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3960 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3961 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3962 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3963 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3964 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3965 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3966 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3967 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
3969 // convert all triangles to unique vertex data
3971 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3973 surface = model->data_surfaces + surfaceindex;
3974 surface->num_firstvertex = outvertexindex;
3975 surface->num_vertices = surface->num_triangles*3;
3976 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3977 for (i = 0;i < surface->num_triangles*3;i++)
3980 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3981 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3982 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3983 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3984 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3985 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3986 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3987 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3988 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3989 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3990 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3991 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3992 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
3993 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
3994 if (oldsurfmesh.data_texcoordlightmap2f)
3996 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
3997 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
3999 if (oldsurfmesh.data_lightmapcolor4f)
4001 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4002 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4003 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4004 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4007 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4008 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4012 if (model->surfmesh.data_element3s)
4013 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4014 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4016 // find and update all submodels to use this new surfmesh data
4017 for (i = 0;i < model->brush.numsubmodels;i++)
4018 model->brush.submodels[i]->surfmesh = model->surfmesh;
4021 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
4023 msurface_t *surface;
4029 lightmaptriangle_t *triangle;
4030 // generate lightmap triangle structs
4031 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4032 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4034 surface = model->data_surfaces + surfaceindex;
4035 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4036 for (i = 0;i < surface->num_triangles;i++)
4038 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4039 triangle->triangleindex = surface->num_firsttriangle+i;
4040 triangle->surfaceindex = surfaceindex;
4041 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4042 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4043 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4044 // calculate bounds of triangle
4045 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4046 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4047 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4048 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4049 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4050 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4051 // pick an axial projection based on the triangle normal
4052 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4054 if (fabs(normal[1]) > fabs(normal[axis]))
4056 if (fabs(normal[2]) > fabs(normal[axis]))
4058 triangle->axis = axis;
4063 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4065 if (mod_generatelightmaps_lightmaptriangles)
4066 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4067 mod_generatelightmaps_lightmaptriangles = NULL;
4070 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4072 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4074 msurface_t *surface;
4088 float trianglenormal[3];
4089 float samplecenter[3];
4090 float samplenormal[3];
4096 float lmscalepixels;
4099 float lm_basescalepixels;
4100 int lm_borderpixels;
4104 lightmaptriangle_t *triangle;
4105 unsigned char *lightmappixels;
4106 unsigned char *deluxemappixels;
4107 mod_alloclightmap_state_t lmstate;
4110 // generate lightmap projection information for all triangles
4111 if (model->texturepool == NULL)
4112 model->texturepool = R_AllocTexturePool();
4113 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4114 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4115 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4116 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4117 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4119 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4121 surface = model->data_surfaces + surfaceindex;
4122 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4123 lmscalepixels = lm_basescalepixels;
4124 for (retry = 0;retry < 30;retry++)
4126 // after a couple failed attempts, degrade quality to make it fit
4128 lmscalepixels *= 0.5f;
4129 for (i = 0;i < surface->num_triangles;i++)
4131 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4132 triangle->lightmapindex = lightmapnumber;
4133 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4134 // pick two planar axes for projection
4135 // lightmap coordinates here are in pixels
4136 // lightmap projections are snapped to pixel grid explicitly, such
4137 // that two neighboring triangles sharing an edge and projection
4138 // axis will have identical sample spacing along their shared edge
4140 for (j = 0;j < 3;j++)
4142 if (j == triangle->axis)
4144 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4145 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4146 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4147 triangle->lmbase[k] = lmmins/lmscalepixels;
4148 triangle->lmscale[k] = lmscalepixels;
4151 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4154 // if all fit in this texture, we're done with this surface
4155 if (i == surface->num_triangles)
4157 // if we haven't maxed out the lightmap size yet, we retry the
4158 // entire surface batch...
4159 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4161 lm_texturesize *= 2;
4164 Mod_AllocLightmap_Free(&lmstate);
4165 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4168 // if we have maxed out the lightmap size, and this triangle does
4169 // not fit in the same texture as the rest of the surface, we have
4170 // to retry the entire surface in a new texture (can only use one)
4171 // with multiple retries, the lightmap quality degrades until it
4172 // fits (or gives up)
4173 if (surfaceindex > 0)
4175 Mod_AllocLightmap_Reset(&lmstate);
4179 Mod_AllocLightmap_Free(&lmstate);
4181 // now put triangles together into lightmap textures, and do not allow
4182 // triangles of a surface to go into different textures (as that would
4183 // require rewriting the surface list)
4184 model->brushq3.deluxemapping_modelspace = true;
4185 model->brushq3.deluxemapping = true;
4186 model->brushq3.num_mergedlightmaps = lightmapnumber;
4187 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4188 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4189 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4190 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4191 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4193 surface = model->data_surfaces + surfaceindex;
4194 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4195 for (i = 0;i < surface->num_triangles;i++)
4197 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4198 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4199 VectorNormalize(trianglenormal);
4200 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4201 axis = triangle->axis;
4202 axis1 = axis == 0 ? 1 : 0;
4203 axis2 = axis == 2 ? 1 : 2;
4204 lmiscale[0] = 1.0f / triangle->lmscale[0];
4205 lmiscale[1] = 1.0f / triangle->lmscale[1];
4206 if (trianglenormal[axis] < 0)
4207 VectorNegate(trianglenormal, trianglenormal);
4208 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4209 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4210 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4211 for (j = 0;j < 3;j++)
4213 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4214 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4215 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4217 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4218 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4219 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4220 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]);
4230 forward[1] = 1.0f / triangle->lmscale[0];
4234 left[2] = 1.0f / triangle->lmscale[1];
4239 origin[1] = triangle->lmbase[0];
4240 origin[2] = triangle->lmbase[1];
4243 forward[0] = 1.0f / triangle->lmscale[0];
4248 left[2] = 1.0f / triangle->lmscale[1];
4252 origin[0] = triangle->lmbase[0];
4254 origin[2] = triangle->lmbase[1];
4257 forward[0] = 1.0f / triangle->lmscale[0];
4261 left[1] = 1.0f / triangle->lmscale[1];
4266 origin[0] = triangle->lmbase[0];
4267 origin[1] = triangle->lmbase[1];
4271 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4273 #define LM_DIST_EPSILON (1.0f / 32.0f)
4274 for (y = 0;y < triangle->lmsize[1];y++)
4276 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4277 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4279 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4280 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4281 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4282 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4283 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4289 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4291 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);
4292 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);
4296 Mem_Free(lightmappixels);
4297 if (deluxemappixels)
4298 Mem_Free(deluxemappixels);
4300 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4302 surface = model->data_surfaces + surfaceindex;
4303 if (!surface->num_triangles)
4305 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4306 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4307 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4308 surface->lightmapinfo = NULL;
4311 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4312 model->brushq1.lightdata = NULL;
4313 model->brushq1.lightmapupdateflags = NULL;
4314 model->brushq1.firstrender = false;
4315 model->brushq1.num_lightstyles = 0;
4316 model->brushq1.data_lightstyleinfo = NULL;
4317 for (i = 0;i < model->brush.numsubmodels;i++)
4319 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4320 model->brush.submodels[i]->brushq1.firstrender = false;
4321 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4322 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4326 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4329 for (i = 0;i < model->surfmesh.num_vertices;i++)
4330 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4333 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4340 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4342 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4343 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4345 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4346 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4348 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4349 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4355 extern cvar_t mod_q3bsp_nolightmaps;
4356 static void Mod_GenerateLightmaps(dp_model_t *model)
4358 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4359 dp_model_t *oldloadmodel = loadmodel;
4362 Mod_GenerateLightmaps_InitSampleOffsets(model);
4363 Mod_GenerateLightmaps_DestroyLightmaps(model);
4364 Mod_GenerateLightmaps_UnweldTriangles(model);
4365 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4366 Mod_GenerateLightmaps_CreateLights(model);
4367 if(!mod_q3bsp_nolightmaps.integer)
4368 Mod_GenerateLightmaps_CreateLightmaps(model);
4369 Mod_GenerateLightmaps_UpdateVertexColors(model);
4370 Mod_GenerateLightmaps_UpdateLightGrid(model);
4371 Mod_GenerateLightmaps_DestroyLights(model);
4372 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4374 loadmodel = oldloadmodel;
4377 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4379 if (Cmd_Argc(cmd) != 1)
4381 Con_Printf("usage: mod_generatelightmaps\n");
4386 Con_Printf("no worldmodel loaded\n");
4389 Mod_GenerateLightmaps(cl.worldmodel);
4392 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4394 memset(mod, 0, sizeof(*mod));
4395 strlcpy(mod->name, name, sizeof(mod->name));
4396 mod->mempool = Mem_AllocPool(name, 0, NULL);
4397 mod->texturepool = R_AllocTexturePool();
4398 mod->Draw = R_Mod_Draw;
4399 mod->DrawDepth = R_Mod_DrawDepth;
4400 mod->DrawDebug = R_Mod_DrawDebug;
4401 mod->DrawPrepass = R_Mod_DrawPrepass;
4402 mod->GetLightInfo = R_Mod_GetLightInfo;
4403 mod->DrawShadowMap = R_Mod_DrawShadowMap;
4404 mod->DrawLight = R_Mod_DrawLight;
4407 void Mod_Mesh_Destroy(dp_model_t *mod)
4409 Mod_UnloadModel(mod);
4412 // resets the mesh model to have no geometry to render, ready for a new frame -
4413 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4414 void Mod_Mesh_Reset(dp_model_t *mod)
4416 mod->num_surfaces = 0;
4417 mod->surfmesh.num_vertices = 0;
4418 mod->surfmesh.num_triangles = 0;
4419 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4420 mod->DrawSky = NULL; // will be set if a texture needs it
4421 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4424 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4428 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4429 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4430 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4432 if (mod->max_textures <= mod->num_textures)
4434 texture_t *oldtextures = mod->data_textures;
4435 mod->max_textures = max(mod->max_textures * 2, 1024);
4436 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4437 // update the pointers
4438 for (i = 0; i < mod->num_surfaces; i++)
4439 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4441 t = &mod->data_textures[mod->num_textures++];
4442 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4443 t->mesh_drawflag = drawflag;
4444 t->mesh_defaulttexflags = defaulttexflags;
4445 t->mesh_defaultmaterialflags = defaultmaterialflags;
4446 switch (defaultdrawflags & DRAWFLAG_MASK)
4448 case DRAWFLAG_ADDITIVE:
4449 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4450 t->currentmaterialflags = t->basematerialflags;
4452 case DRAWFLAG_MODULATE:
4453 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4454 t->currentmaterialflags = t->basematerialflags;
4455 t->customblendfunc[0] = GL_DST_COLOR;
4456 t->customblendfunc[1] = GL_ZERO;
4458 case DRAWFLAG_2XMODULATE:
4459 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4460 t->currentmaterialflags = t->basematerialflags;
4461 t->customblendfunc[0] = GL_DST_COLOR;
4462 t->customblendfunc[1] = GL_SRC_COLOR;
4464 case DRAWFLAG_SCREEN:
4465 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4466 t->currentmaterialflags = t->basematerialflags;
4467 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4468 t->customblendfunc[1] = GL_ONE;
4476 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4479 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4480 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4481 return mod->data_surfaces + mod->num_surfaces - 1;
4482 // create new surface
4483 if (mod->max_surfaces == mod->num_surfaces)
4485 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4486 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4487 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4489 surf = mod->data_surfaces + mod->num_surfaces;
4490 mod->num_surfaces++;
4491 memset(surf, 0, sizeof(*surf));
4492 surf->texture = tex;
4493 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4494 surf->num_firstvertex = mod->surfmesh.num_vertices;
4495 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4496 mod->DrawSky = R_Mod_DrawSky;
4497 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4498 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
4502 int Mod_Mesh_IndexForVertex(dp_model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
4504 int hashindex, h, vnum, mask;
4505 surfmesh_t *mesh = &mod->surfmesh;
4506 if (mesh->max_vertices == mesh->num_vertices)
4508 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4509 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4510 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4511 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4512 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4513 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4514 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4515 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4516 // rebuild the hash table
4517 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4518 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4519 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4520 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4521 mask = mod->surfmesh.num_vertexhashsize - 1;
4522 // no need to hash the vertices for the entire model, the latest surface will suffice.
4523 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4525 // this uses prime numbers intentionally for computing the hash
4526 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;
4527 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4528 ; // just iterate until we find the terminator
4529 mesh->data_vertexhash[h] = vnum;
4532 mask = mod->surfmesh.num_vertexhashsize - 1;
4533 // this uses prime numbers intentionally for computing the hash
4534 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4535 // when possible find an identical vertex within the same surface and return it
4536 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4538 if (vnum >= surf->num_firstvertex
4539 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4540 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4541 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4542 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4543 && 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)
4546 // add the new vertex
4547 vnum = mesh->num_vertices++;
4548 if (surf->num_vertices > 0)
4550 if (surf->mins[0] > x) surf->mins[0] = x;
4551 if (surf->mins[1] > y) surf->mins[1] = y;
4552 if (surf->mins[2] > z) surf->mins[2] = z;
4553 if (surf->maxs[0] < x) surf->maxs[0] = x;
4554 if (surf->maxs[1] < y) surf->maxs[1] = y;
4555 if (surf->maxs[2] < z) surf->maxs[2] = z;
4559 VectorSet(surf->mins, x, y, z);
4560 VectorSet(surf->maxs, x, y, z);
4562 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4563 mesh->data_vertexhash[h] = vnum;
4564 mesh->data_vertex3f[vnum * 3 + 0] = x;
4565 mesh->data_vertex3f[vnum * 3 + 1] = y;
4566 mesh->data_vertex3f[vnum * 3 + 2] = z;
4567 mesh->data_normal3f[vnum * 3 + 0] = nx;
4568 mesh->data_normal3f[vnum * 3 + 1] = ny;
4569 mesh->data_normal3f[vnum * 3 + 2] = nz;
4570 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4571 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4572 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4573 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4574 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4575 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4576 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4577 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4581 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4583 surfmesh_t *mesh = &mod->surfmesh;
4584 if (mesh->max_triangles == mesh->num_triangles)
4586 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4587 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4588 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4590 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4591 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4592 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4593 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4594 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4595 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4596 mesh->num_triangles++;
4597 surf->num_triangles++;
4600 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4604 msurface_t *surf, *surf2;
4606 // build the sorted surfaces list properly to reduce material setup
4607 // this is easy because we're just sorting on texture and don't care about the order of textures
4608 mod->nummodelsurfaces = 0;
4609 for (i = 0; i < mod->num_surfaces; i++)
4610 mod->data_surfaces[i].included = false;
4611 for (i = 0; i < mod->num_surfaces; i++)
4613 surf = mod->data_surfaces + i;
4616 tex = surf->texture;
4617 // j = i is intentional
4618 for (j = i; j < mod->num_surfaces; j++)
4620 surf2 = mod->data_surfaces + j;
4621 if (surf2->included)
4623 if (surf2->texture == tex)
4625 surf2->included = true;
4626 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4632 static void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4635 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4637 if (mod->surfmesh.num_vertices > 0)
4639 // calculate normalmins/normalmaxs
4640 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4641 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4642 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4644 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4645 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4646 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4647 // expand bounds to include this vertex
4648 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4649 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4650 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4651 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4652 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4653 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4655 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4656 // (fast but less accurate than doing it per vertex)
4657 x2a = mod->normalmins[0] * mod->normalmins[0];
4658 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4659 y2a = mod->normalmins[1] * mod->normalmins[1];
4660 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4661 z2a = mod->normalmins[2] * mod->normalmins[2];
4662 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4666 yawradius = sqrt(x2 + y2);
4667 rotatedradius = sqrt(x2 + y2 + z2);
4668 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4669 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4670 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4671 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4672 mod->radius = rotatedradius;
4673 mod->radius2 = x2 + y2 + z2;
4677 VectorClear(mod->normalmins);
4678 VectorClear(mod->normalmaxs);
4679 VectorClear(mod->yawmins);
4680 VectorClear(mod->yawmaxs);
4681 VectorClear(mod->rotatedmins);
4682 VectorClear(mod->rotatedmaxs);
4688 void Mod_Mesh_Validate(dp_model_t *mod)
4691 qboolean warned = false;
4692 for (i = 0; i < mod->num_surfaces; i++)
4694 msurface_t *surf = mod->data_surfaces + i;
4695 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4696 int first = surf->num_firstvertex;
4697 int end = surf->num_firstvertex + surf->num_vertices;
4699 for (j = 0;j < surf->num_triangles * 3;j++)
4701 if (e[j] < first || e[j] >= end)
4704 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4712 static void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
4714 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;
4715 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;
4716 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;
4717 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;
4718 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;
4719 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;
4720 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;
4721 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;
4722 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;
4723 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;
4724 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;
4727 void Mod_Mesh_Finalize(dp_model_t *mod)
4729 if (gl_paranoid.integer)
4730 Mod_Mesh_Validate(mod);
4731 Mod_Mesh_ComputeBounds(mod);
4732 Mod_Mesh_MakeSortedSurfaces(mod);
4733 if(!r_refdef.draw2dstage)
4734 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);
4735 Mod_Mesh_UploadDynamicBuffers(mod);