2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // models.c -- model loading and caching
22 // models are the only shared resource between a client and server running
23 // on the same machine.
30 cvar_t r_mipskins = {CF_CLIENT | CF_ARCHIVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
31 cvar_t r_mipnormalmaps = {CF_CLIENT | CF_ARCHIVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
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(CF_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
200 Cmd_AddCommand(CF_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
201 Cmd_AddCommand(CF_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
202 Cmd_AddCommand(CF_CLIENT, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
205 void Mod_RenderInit(void)
207 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
210 void Mod_UnloadModel (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, qbool loop, const char *name, void *pass);
262 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
277 // REQUIRED: fetch start
278 COM_ParseToken_Simple(&bufptr, true, false, true);
280 break; // end of file
281 if (!strcmp(com_token, "\n"))
282 continue; // empty line
283 start = atoi(com_token);
285 // REQUIRED: fetch length
286 COM_ParseToken_Simple(&bufptr, true, false, true);
287 if (!bufptr || !strcmp(com_token, "\n"))
289 Con_Printf("framegroups file: missing number of frames\n");
292 len = atoi(com_token);
294 // OPTIONAL args start
295 COM_ParseToken_Simple(&bufptr, true, false, true);
297 // OPTIONAL: fetch fps
299 if (bufptr && strcmp(com_token, "\n"))
301 fps = atof(com_token);
302 COM_ParseToken_Simple(&bufptr, true, false, true);
305 // OPTIONAL: fetch loopflag
307 if (bufptr && strcmp(com_token, "\n"))
309 loop = (atoi(com_token) != 0);
310 COM_ParseToken_Simple(&bufptr, true, false, true);
313 // OPTIONAL: fetch name
315 if (bufptr && strcmp(com_token, "\n"))
317 strlcpy(name, com_token, sizeof(name));
318 COM_ParseToken_Simple(&bufptr, true, false, true);
321 // OPTIONAL: remaining unsupported tokens (eat them)
322 while (bufptr && strcmp(com_token, "\n"))
323 COM_ParseToken_Simple(&bufptr, true, false, true);
325 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
328 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
335 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass)
337 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, qbool crash, qbool checkdisk)
409 fs_offset_t filesize = 0;
414 if (mod->name[0] == '*') // submodel
417 if (!strcmp(mod->name, "null"))
422 if (mod->loaded || mod->mempool)
423 Mod_UnloadModel(mod);
425 if (developer_loading.integer)
426 Con_Printf("loading model %s\n", mod->name);
429 mod->crc = (unsigned int)-1;
432 VectorClear(mod->normalmins);
433 VectorClear(mod->normalmaxs);
434 VectorClear(mod->yawmins);
435 VectorClear(mod->yawmaxs);
436 VectorClear(mod->rotatedmins);
437 VectorClear(mod->rotatedmaxs);
439 mod->modeldatatypestring = "null";
440 mod->type = mod_null;
441 mod->Draw = R_Model_Null_Draw;
445 // no fatal errors occurred, so this model is ready to use.
454 // even if the model is loaded it still may need reloading...
456 // if it is not loaded or checkdisk is true we need to calculate the crc
457 if (!mod->loaded || checkdisk)
459 if (checkdisk && mod->loaded)
460 Con_DPrintf("checking model %s\n", mod->name);
461 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
464 crc = CRC_Block((unsigned char *)buf, filesize);
465 // we need to reload the model if the crc does not match
471 // if the model is already loaded and checks passed, just return
479 if (developer_loading.integer)
480 Con_Printf("loading model %s\n", mod->name);
482 SCR_PushLoadingScreen(mod->name, 1);
484 // LadyHavoc: unload the existing model in this slot (if there is one)
485 if (mod->loaded || mod->mempool)
486 Mod_UnloadModel(mod);
491 // errors can prevent the corresponding mod->loaded = true;
494 // default lightmap scale
495 mod->lightmapscale = 1;
497 // default model radius and bounding box (mainly for missing models)
499 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
500 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
501 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
502 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
503 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
504 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
508 // load q3 shaders for the first time, or after a level change
515 const char *ext = FS_FileExtension(mod->name);
516 char *bufend = (char *)buf + filesize;
517 // all models use memory, so allocate a memory pool
518 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
520 // call the apropriate loader
523 // Try matching magic bytes.
524 for (i = 0; loader[i].Load; i++)
526 // Headerless formats can just load based on extension. Otherwise match the magic string.
527 if((loader[i].extension && !strcasecmp(ext, loader[i].extension) && !loader[i].header) ||
528 (loader[i].header && !memcmp(buf, loader[i].header, loader[i].headersize)))
531 loader[i].Load(mod, buf, bufend);
534 Mod_FindPotentialDeforms(mod);
536 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
539 Mod_FrameGroupify(mod, (const char *)buf);
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;
629 extern qbool vid_opened;
635 Loads in a model for the given name
638 dp_model_t *Mod_ForName(const char *name, qbool crash, qbool checkdisk, const char *parentname)
642 // FIXME: So we don't crash if a server is started early.
646 model = Mod_FindName(name, parentname);
647 if (!model->loaded || checkdisk)
648 Mod_LoadModel(model, crash, checkdisk);
656 Reloads all models if they have changed
659 void Mod_Reload(void)
662 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
665 SCR_PushLoadingScreen("Reloading models", 1.0);
667 for (i = 0;i < nummodels;i++)
668 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
670 for (i = 0;i < nummodels;i++)
671 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
673 SCR_PushLoadingScreen(mod->name, 1.0 / count);
674 Mod_LoadModel(mod, true, true);
675 SCR_PopLoadingScreen(false);
677 SCR_PopLoadingScreen(false);
680 unsigned char *mod_base;
683 //=============================================================================
690 static void Mod_Print_f(cmd_state_t *cmd)
693 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
696 Con_Print("Loaded models:\n");
697 for (i = 0;i < nummodels;i++)
699 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
701 if (mod->brush.numsubmodels)
702 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
704 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
714 static void Mod_Precache_f(cmd_state_t *cmd)
716 if (Cmd_Argc(cmd) == 2)
717 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
719 Con_Print("usage: modelprecache <filename>\n");
722 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
726 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
727 memset(used, 0, numvertices);
728 for (i = 0;i < numelements;i++)
729 used[elements[i]] = 1;
730 for (i = 0, count = 0;i < numvertices;i++)
731 remapvertices[i] = used[i] ? count++ : -1;
736 qbool Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
738 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
740 int invalidintcount = 0, invalidintexample = 0;
741 int invalidshortcount = 0, invalidshortexample = 0;
742 int invalidmismatchcount = 0, invalidmismatchexample = 0;
745 for (i = 0; i < numelements; i++)
747 if (element3i[i] < first || element3i[i] > last)
750 invalidintexample = i;
756 for (i = 0; i < numelements; i++)
758 if (element3s[i] < first || element3s[i] > last)
761 invalidintexample = i;
765 if (element3i && element3s)
767 for (i = 0; i < numelements; i++)
769 if (element3s[i] != element3i[i])
771 invalidmismatchcount++;
772 invalidmismatchexample = i;
776 if (invalidintcount || invalidshortcount || invalidmismatchcount)
778 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, (void *)element3i, (void *)element3s, filename, fileline);
779 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
780 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
781 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);
782 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
784 // edit the elements to make them safer, as the driver will crash otherwise
786 for (i = 0; i < numelements; i++)
787 if (element3i[i] < first || element3i[i] > last)
788 element3i[i] = first;
790 for (i = 0; i < numelements; i++)
791 if (element3s[i] < first || element3s[i] > last)
792 element3s[i] = first;
793 if (element3i && element3s)
794 for (i = 0; i < numelements; i++)
795 if (element3s[i] != element3i[i])
796 element3s[i] = element3i[i];
803 // warning: this is an expensive function!
804 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qbool areaweighting)
811 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
812 // process each vertex of each triangle and accumulate the results
813 // use area-averaging, to make triangles with a big area have a bigger
814 // weighting on the vertex normal than triangles with a small area
815 // to do so, just add the 'normals' together (the bigger the area
816 // the greater the length of the normal is
818 for (i = 0; i < numtriangles; i++, element += 3)
821 vertex3f + element[0] * 3,
822 vertex3f + element[1] * 3,
823 vertex3f + element[2] * 3,
828 VectorNormalize(areaNormal);
830 for (j = 0;j < 3;j++)
832 vectorNormal = normal3f + element[j] * 3;
833 vectorNormal[0] += areaNormal[0];
834 vectorNormal[1] += areaNormal[1];
835 vectorNormal[2] += areaNormal[2];
838 // and just normalize the accumulated vertex normal in the end
839 vectorNormal = normal3f + 3 * firstvertex;
840 for (i = 0; i < numvertices; i++, vectorNormal += 3)
841 VectorNormalize(vectorNormal);
845 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)
847 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
848 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
849 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
851 // 6 multiply, 9 subtract
852 VectorSubtract(v1, v0, v10);
853 VectorSubtract(v2, v0, v20);
854 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
855 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
856 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
857 // 12 multiply, 10 subtract
858 tc10[1] = tc1[1] - tc0[1];
859 tc20[1] = tc2[1] - tc0[1];
860 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
861 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
862 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
863 tc10[0] = tc1[0] - tc0[0];
864 tc20[0] = tc2[0] - tc0[0];
865 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
866 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
867 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
868 // 12 multiply, 4 add, 6 subtract
869 f = DotProduct(svector3f, normal3f);
870 svector3f[0] -= f * normal3f[0];
871 svector3f[1] -= f * normal3f[1];
872 svector3f[2] -= f * normal3f[2];
873 f = DotProduct(tvector3f, normal3f);
874 tvector3f[0] -= f * normal3f[0];
875 tvector3f[1] -= f * normal3f[1];
876 tvector3f[2] -= f * normal3f[2];
877 // if texture is mapped the wrong way (counterclockwise), the tangents
878 // have to be flipped, this is detected by calculating a normal from the
879 // two tangents, and seeing if it is opposite the surface normal
880 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
881 CrossProduct(tvector3f, svector3f, tangentcross);
882 if (DotProduct(tangentcross, normal3f) < 0)
884 VectorNegate(svector3f, svector3f);
885 VectorNegate(tvector3f, tvector3f);
890 // warning: this is a very expensive function!
891 void Mod_BuildTextureVectorsFromNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const float *texcoord2f, const float *normal3f, const int *elements, float *svector3f, float *tvector3f, qbool areaweighting)
894 float sdir[3], tdir[3], normal[3], *svec, *tvec;
895 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
896 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
899 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
900 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
901 // process each vertex of each triangle and accumulate the results
902 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
904 v0 = vertex3f + e[0] * 3;
905 v1 = vertex3f + e[1] * 3;
906 v2 = vertex3f + e[2] * 3;
907 tc0 = texcoord2f + e[0] * 2;
908 tc1 = texcoord2f + e[1] * 2;
909 tc2 = texcoord2f + e[2] * 2;
911 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
912 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
914 // calculate the edge directions and surface normal
915 // 6 multiply, 9 subtract
916 VectorSubtract(v1, v0, v10);
917 VectorSubtract(v2, v0, v20);
918 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
919 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
920 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
922 // calculate the tangents
923 // 12 multiply, 10 subtract
924 tc10[1] = tc1[1] - tc0[1];
925 tc20[1] = tc2[1] - tc0[1];
926 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
927 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
928 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
929 tc10[0] = tc1[0] - tc0[0];
930 tc20[0] = tc2[0] - tc0[0];
931 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
932 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
933 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
935 // if texture is mapped the wrong way (counterclockwise), the tangents
936 // have to be flipped, this is detected by calculating a normal from the
937 // two tangents, and seeing if it is opposite the surface normal
938 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
939 CrossProduct(tdir, sdir, tangentcross);
940 if (DotProduct(tangentcross, normal) < 0)
942 VectorNegate(sdir, sdir);
943 VectorNegate(tdir, tdir);
948 VectorNormalize(sdir);
949 VectorNormalize(tdir);
951 for (i = 0;i < 3;i++)
953 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
954 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
957 // make the tangents completely perpendicular to the surface normal, and
958 // then normalize them
959 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
960 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
962 f = -DotProduct(svec, n);
963 VectorMA(svec, f, n, svec);
964 VectorNormalize(svec);
965 f = -DotProduct(tvec, n);
966 VectorMA(tvec, f, n, tvec);
967 VectorNormalize(tvec);
971 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qbool lightmapoffsets, qbool vertexcolors)
974 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));
975 loadmodel->surfmesh.num_vertices = numvertices;
976 loadmodel->surfmesh.num_triangles = numtriangles;
977 if (loadmodel->surfmesh.num_vertices)
979 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
980 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
981 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
982 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
983 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
984 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
986 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
988 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
990 if (loadmodel->surfmesh.num_triangles)
992 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
993 if (loadmodel->surfmesh.num_vertices <= 65536)
994 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
998 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
1000 shadowmesh_t *newmesh;
1001 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
1002 newmesh->mempool = mempool;
1003 newmesh->maxverts = maxverts;
1004 newmesh->maxtriangles = maxtriangles;
1005 newmesh->numverts = 0;
1006 newmesh->numtriangles = 0;
1007 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1008 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1010 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
1011 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
1012 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
1013 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
1017 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
1019 int hashindex, vnum;
1020 shadowmeshvertexhash_t *hash;
1021 // this uses prime numbers intentionally
1022 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
1023 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1025 vnum = (hash - mesh->vertexhashentries);
1026 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1027 return hash - mesh->vertexhashentries;
1029 vnum = mesh->numverts++;
1030 hash = mesh->vertexhashentries + vnum;
1031 hash->next = mesh->vertexhashtable[hashindex];
1032 mesh->vertexhashtable[hashindex] = hash;
1033 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1034 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1035 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1039 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1043 for (i = 0;i < numtris;i++)
1045 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1046 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1047 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1048 mesh->numtriangles++;
1051 // the triangle calculation can take a while, so let's do a keepalive here
1052 CL_KeepaliveMessage(false);
1055 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1057 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1058 CL_KeepaliveMessage(false);
1060 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1063 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1065 if (!mesh->numverts)
1068 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1069 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1071 // upload short indices as a buffer
1072 if (mesh->element3s && !mesh->element3s_indexbuffer)
1073 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1075 // upload int indices as a buffer
1076 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1077 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1079 // vertex buffer is several arrays and we put them in the same buffer
1081 // is this wise? the texcoordtexture2f array is used with dynamic
1082 // vertex/svector/tvector/normal when rendering animated models, on the
1083 // other hand animated models don't use a lot of vertices anyway...
1084 if (!mesh->vbo_vertexbuffer)
1086 mesh->vbooffset_vertex3f = 0;
1087 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1091 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qbool createvbo)
1093 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1095 if (mesh->vertexhashentries)
1096 Mem_Free(mesh->vertexhashentries);
1097 mesh->vertexhashentries = NULL;
1098 if (mesh->vertexhashtable)
1099 Mem_Free(mesh->vertexhashtable);
1100 mesh->vertexhashtable = NULL;
1101 if (mesh->maxverts > mesh->numverts)
1103 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1104 mesh->maxverts = mesh->numverts;
1106 if (mesh->maxtriangles > mesh->numtriangles)
1108 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1109 mesh->maxtriangles = mesh->numtriangles;
1111 if (mesh->numverts <= 65536)
1114 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1115 for (i = 0;i < mesh->numtriangles*3;i++)
1116 mesh->element3s[i] = mesh->element3i[i];
1119 Mod_ShadowMesh_CreateVBOs(mesh);
1122 // this can take a while, so let's do a keepalive here
1123 CL_KeepaliveMessage(false);
1128 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1131 vec3_t nmins, nmaxs, ncenter, temp;
1132 float nradius2, dist2, *v;
1136 VectorCopy(mesh->vertex3f, nmins);
1137 VectorCopy(mesh->vertex3f, nmaxs);
1138 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1140 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1141 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1142 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1144 // calculate center and radius
1145 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1146 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1147 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1149 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1151 VectorSubtract(v, ncenter, temp);
1152 dist2 = DotProduct(temp, temp);
1153 if (nradius2 < dist2)
1158 VectorCopy(nmins, mins);
1160 VectorCopy(nmaxs, maxs);
1162 VectorCopy(ncenter, center);
1164 *radius = sqrt(nradius2);
1167 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1169 if (mesh->element3i_indexbuffer)
1170 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1171 if (mesh->element3s_indexbuffer)
1172 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1173 if (mesh->vbo_vertexbuffer)
1174 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1176 Mem_Free(mesh->vertex3f);
1177 if (mesh->element3i)
1178 Mem_Free(mesh->element3i);
1179 if (mesh->element3s)
1180 Mem_Free(mesh->element3s);
1181 if (mesh->vertexhashentries)
1182 Mem_Free(mesh->vertexhashentries);
1183 if (mesh->vertexhashtable)
1184 Mem_Free(mesh->vertexhashtable);
1188 void Mod_CreateCollisionMesh(dp_model_t *mod)
1190 int k, numcollisionmeshtriangles;
1191 qbool usesinglecollisionmesh = false;
1192 const msurface_t *surface = NULL;
1194 mempool_t *mempool = mod->mempool;
1195 if (!mempool && mod->brush.parentmodel)
1196 mempool = mod->brush.parentmodel->mempool;
1197 // make a single combined collision mesh for physics engine use
1198 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1199 numcollisionmeshtriangles = 0;
1200 for (k = 0;k < mod->nummodelsurfaces;k++)
1202 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1203 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1205 usesinglecollisionmesh = true;
1206 numcollisionmeshtriangles = surface->num_triangles;
1209 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1211 numcollisionmeshtriangles += surface->num_triangles;
1213 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1214 if (usesinglecollisionmesh)
1215 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1218 for (k = 0;k < mod->nummodelsurfaces;k++)
1220 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1221 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1223 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1226 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1230 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)
1235 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1236 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1239 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1240 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1241 texcoord2f[0] = tc[0];
1242 texcoord2f[1] = tc[1];
1245 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)
1247 float vup[3], vdown[3], vleft[3], vright[3];
1248 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1249 float sv[3], tv[3], nl[3];
1250 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1251 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1252 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1253 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1254 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1255 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1256 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1257 VectorAdd(svector3f, sv, svector3f);
1258 VectorAdd(tvector3f, tv, tvector3f);
1259 VectorAdd(normal3f, nl, normal3f);
1260 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1261 VectorAdd(svector3f, sv, svector3f);
1262 VectorAdd(tvector3f, tv, tvector3f);
1263 VectorAdd(normal3f, nl, normal3f);
1264 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1265 VectorAdd(svector3f, sv, svector3f);
1266 VectorAdd(tvector3f, tv, tvector3f);
1267 VectorAdd(normal3f, nl, normal3f);
1270 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)
1272 int x, y, ix, iy, *e;
1274 for (y = 0;y < height;y++)
1276 for (x = 0;x < width;x++)
1278 e[0] = (y + 1) * (width + 1) + (x + 0);
1279 e[1] = (y + 0) * (width + 1) + (x + 0);
1280 e[2] = (y + 1) * (width + 1) + (x + 1);
1281 e[3] = (y + 0) * (width + 1) + (x + 0);
1282 e[4] = (y + 0) * (width + 1) + (x + 1);
1283 e[5] = (y + 1) * (width + 1) + (x + 1);
1287 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1288 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1289 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1294 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1298 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1299 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1300 float viewvector[3];
1301 unsigned int firstvertex;
1304 if (chunkwidth < 2 || chunkheight < 2)
1306 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]);
1307 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]);
1308 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1309 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1310 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1311 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1313 // too close for this stepsize, emit as 4 chunks instead
1315 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1316 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1317 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1318 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1321 // emit the geometry at stepsize into our vertex buffer / index buffer
1322 // we add two columns and two rows for skirt
1323 outwidth = chunkwidth+2;
1324 outheight = chunkheight+2;
1325 outwidth2 = outwidth-1;
1326 outheight2 = outheight-1;
1327 outwidth3 = outwidth+1;
1328 outheight3 = outheight+1;
1329 firstvertex = numvertices;
1330 e = model->terrain.element3i + numtriangles;
1331 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1332 v = model->terrain.vertex3f + numvertices;
1333 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1334 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1335 for (ty = 0;ty < outheight;ty++)
1337 for (tx = 0;tx < outwidth;tx++)
1339 *e++ = firstvertex + (ty )*outwidth3+(tx );
1340 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1341 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1342 *e++ = firstvertex + (ty )*outwidth3+(tx );
1343 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1344 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1347 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1348 for (ty = 0;ty <= outheight;ty++)
1350 skirtrow = ty == 0 || ty == outheight;
1351 ry = y+bound(1, ty, outheight)*stepsize;
1352 for (tx = 0;tx <= outwidth;tx++)
1354 skirt = skirtrow || tx == 0 || tx == outwidth;
1355 rx = x+bound(1, tx, outwidth)*stepsize;
1358 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1362 // TODO: emit skirt vertices
1365 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1367 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1368 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1369 Mod_Terrain_BuildChunk(model,
1373 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1376 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1378 offset = bound(0, s[4] - '0', 9);
1379 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1384 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1385 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1386 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1387 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1388 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1389 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1390 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1391 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1392 return offset | Q3WAVEFUNC_NONE;
1395 void Mod_FreeQ3Shaders(void)
1397 Mem_FreePool(&q3shaders_mem);
1400 static void Q3Shader_AddToHash (shader_t* shader)
1402 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1403 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1404 q3shader_hash_entry_t* lastEntry = NULL;
1407 if (strcasecmp (entry->shader.name, shader->name) == 0)
1410 if(shader->dpshaderkill)
1412 // killed shader is a redeclarion? we can safely ignore it
1415 else if(entry->shader.dpshaderkill)
1417 // replace the old shader!
1418 // this will skip the entry allocating part
1419 // below and just replace the shader
1424 unsigned char *start, *end, *start2;
1425 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1426 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1427 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1428 if(memcmp(start, start2, end - start))
1429 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1431 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1436 entry = entry->chain;
1438 while (entry != NULL);
1441 if (lastEntry->shader.name[0] != 0)
1444 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1445 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1447 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1448 lastEntry->chain = newEntry;
1449 newEntry->chain = NULL;
1450 lastEntry = newEntry;
1452 /* else: head of chain, in hash entry array */
1455 memcpy (&entry->shader, shader, sizeof (shader_t));
1458 void Mod_LoadQ3Shaders(void)
1466 q3shaderinfo_layer_t *layer;
1468 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1469 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1470 unsigned long custsurfaceflags[256];
1471 int numcustsurfaceflags;
1474 Mod_FreeQ3Shaders();
1476 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1477 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1478 sizeof (q3shader_data_t));
1479 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1480 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1481 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1482 q3shaders_mem, sizeof (char**), 256);
1484 // parse custinfoparms.txt
1485 numcustsurfaceflags = 0;
1486 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1488 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1489 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1492 while (COM_ParseToken_QuakeC(&text, false))
1493 if (!strcasecmp(com_token, "}"))
1495 // custom surfaceflags section
1496 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1497 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1500 while(COM_ParseToken_QuakeC(&text, false))
1502 if (!strcasecmp(com_token, "}"))
1504 // register surfaceflag
1505 if (numcustsurfaceflags >= 256)
1507 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1511 j = (int)strlen(com_token)+1;
1512 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1513 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1515 if (COM_ParseToken_QuakeC(&text, false))
1516 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1518 custsurfaceflags[numcustsurfaceflags] = 0;
1519 numcustsurfaceflags++;
1527 search = FS_Search("scripts/*.shader", true, false, NULL);
1530 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1532 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1535 while (COM_ParseToken_QuakeC(&text, false))
1537 memset (&shader, 0, sizeof(shader));
1539 shader.surfaceparms = 0;
1540 shader.surfaceflags = 0;
1541 shader.textureflags = 0;
1542 shader.numlayers = 0;
1543 shader.lighting = false;
1544 shader.vertexalpha = false;
1545 shader.textureblendalpha = false;
1546 shader.skyboxname[0] = 0;
1547 shader.deforms[0].deform = Q3DEFORM_NONE;
1548 shader.dpnortlight = false;
1549 shader.dpshadow = false;
1550 shader.dpnoshadow = false;
1551 shader.dpmeshcollisions = false;
1552 shader.dpshaderkill = false;
1553 shader.dpreflectcube[0] = 0;
1554 shader.reflectmin = 0;
1555 shader.reflectmax = 1;
1556 shader.refractfactor = 1;
1557 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1558 shader.reflectfactor = 1;
1559 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1560 shader.r_water_wateralpha = 1;
1561 shader.r_water_waterscroll[0] = 0;
1562 shader.r_water_waterscroll[1] = 0;
1563 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1564 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1565 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1566 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1567 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1568 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1569 shader.specularscalemod = 1;
1570 shader.specularpowermod = 1;
1571 shader.rtlightambient = 0;
1572 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1573 // JUST GREP FOR "specularscalemod = 1".
1575 strlcpy(shader.name, com_token, sizeof(shader.name));
1576 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1578 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1581 while (COM_ParseToken_QuakeC(&text, false))
1583 if (!strcasecmp(com_token, "}"))
1585 if (!strcasecmp(com_token, "{"))
1587 static q3shaderinfo_layer_t dummy;
1588 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1590 layer = shader.layers + shader.numlayers++;
1594 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1595 memset(&dummy, 0, sizeof(dummy));
1598 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1599 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1600 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1601 layer->blendfunc[0] = GL_ONE;
1602 layer->blendfunc[1] = GL_ZERO;
1603 while (COM_ParseToken_QuakeC(&text, false))
1605 if (!strcasecmp(com_token, "}"))
1607 if (!strcasecmp(com_token, "\n"))
1610 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1612 if (j < TEXTURE_MAXFRAMES + 4)
1614 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1615 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1616 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1618 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1619 numparameters = j + 1;
1621 if (!COM_ParseToken_QuakeC(&text, true))
1624 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1625 // parameter[j][0] = 0;
1626 if (developer_insane.integer)
1628 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1629 for (j = 0;j < numparameters;j++)
1630 Con_DPrintf(" %s", parameter[j]);
1633 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1635 if (numparameters == 2)
1637 if (!strcasecmp(parameter[1], "add"))
1639 layer->blendfunc[0] = GL_ONE;
1640 layer->blendfunc[1] = GL_ONE;
1642 else if (!strcasecmp(parameter[1], "addalpha"))
1644 layer->blendfunc[0] = GL_SRC_ALPHA;
1645 layer->blendfunc[1] = GL_ONE;
1647 else if (!strcasecmp(parameter[1], "filter"))
1649 layer->blendfunc[0] = GL_DST_COLOR;
1650 layer->blendfunc[1] = GL_ZERO;
1652 else if (!strcasecmp(parameter[1], "blend"))
1654 layer->blendfunc[0] = GL_SRC_ALPHA;
1655 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1658 else if (numparameters == 3)
1661 for (k = 0;k < 2;k++)
1663 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1664 layer->blendfunc[k] = GL_ONE;
1665 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1666 layer->blendfunc[k] = GL_ZERO;
1667 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1668 layer->blendfunc[k] = GL_SRC_COLOR;
1669 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1670 layer->blendfunc[k] = GL_SRC_ALPHA;
1671 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1672 layer->blendfunc[k] = GL_DST_COLOR;
1673 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1674 layer->blendfunc[k] = GL_DST_ALPHA;
1675 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1676 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1677 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1678 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1679 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1680 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1681 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1682 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1684 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1688 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1689 layer->alphatest = true;
1690 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1692 if (!strcasecmp(parameter[0], "clampmap"))
1693 layer->clampmap = true;
1694 layer->numframes = 1;
1695 layer->framerate = 1;
1696 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1697 &q3shader_data->char_ptrs);
1698 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1699 if (!strcasecmp(parameter[1], "$lightmap"))
1700 shader.lighting = true;
1702 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1705 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1706 layer->framerate = atof(parameter[1]);
1707 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1708 for (i = 0;i < layer->numframes;i++)
1709 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1711 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1714 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1715 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1716 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1717 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1718 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1719 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1720 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1721 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1722 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1723 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1724 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1725 else if (!strcasecmp(parameter[1], "wave"))
1727 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1728 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1729 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1730 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1732 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1734 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1737 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1738 layer->alphagen.parms[i] = atof(parameter[i+2]);
1739 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1740 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1741 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1742 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1743 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1744 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1745 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1746 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1747 else if (!strcasecmp(parameter[1], "wave"))
1749 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1750 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1751 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1752 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1754 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1756 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1759 // observed values: tcgen environment
1760 // no other values have been observed in real shaders
1761 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1762 layer->tcgen.parms[i] = atof(parameter[i+2]);
1763 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1764 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1765 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1766 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1767 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1768 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1770 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1777 // tcmod stretch sin # # # #
1778 // tcmod stretch triangle # # # #
1779 // tcmod transform # # # # # #
1780 // tcmod turb # # # #
1781 // tcmod turb sin # # # # (this is bogus)
1782 // no other values have been observed in real shaders
1783 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1784 if (!layer->tcmods[tcmodindex].tcmod)
1786 if (tcmodindex < Q3MAXTCMODS)
1788 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1789 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1790 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1791 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1792 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1793 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1794 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1795 else if (!strcasecmp(parameter[1], "stretch"))
1797 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1798 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1799 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1800 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1802 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1803 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1804 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1807 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1809 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1810 if (!strcasecmp(com_token, "}"))
1813 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1814 shader.lighting = true;
1815 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1817 if (layer == shader.layers + 0)
1819 // vertex controlled transparency
1820 shader.vertexalpha = true;
1824 // multilayer terrain shader or similar
1825 shader.textureblendalpha = true;
1826 if (mod_q3shader_force_terrain_alphaflag.integer)
1827 shader.layers[0].dptexflags |= TEXF_ALPHA;
1831 if(mod_q3shader_force_addalpha.integer)
1833 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1834 // this cvar brings back this behaviour
1835 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1836 layer->blendfunc[0] = GL_SRC_ALPHA;
1839 layer->dptexflags = 0;
1840 if (layer->alphatest)
1841 layer->dptexflags |= TEXF_ALPHA;
1842 switch(layer->blendfunc[0])
1845 case GL_ONE_MINUS_SRC_ALPHA:
1846 layer->dptexflags |= TEXF_ALPHA;
1849 switch(layer->blendfunc[1])
1852 case GL_ONE_MINUS_SRC_ALPHA:
1853 layer->dptexflags |= TEXF_ALPHA;
1856 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1857 layer->dptexflags |= TEXF_MIPMAP;
1858 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1859 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1860 if (layer->clampmap)
1861 layer->dptexflags |= TEXF_CLAMP;
1865 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1867 if (j < TEXTURE_MAXFRAMES + 4)
1869 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1870 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1871 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1873 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1874 numparameters = j + 1;
1876 if (!COM_ParseToken_QuakeC(&text, true))
1879 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1880 // parameter[j][0] = 0;
1881 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1883 if (developer_insane.integer)
1885 Con_DPrintf("%s: ", shader.name);
1886 for (j = 0;j < numparameters;j++)
1887 Con_DPrintf(" %s", parameter[j]);
1890 if (numparameters < 1)
1892 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1894 if (!strcasecmp(parameter[1], "alphashadow"))
1895 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1896 else if (!strcasecmp(parameter[1], "areaportal"))
1897 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1898 else if (!strcasecmp(parameter[1], "botclip"))
1899 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1900 else if (!strcasecmp(parameter[1], "clusterportal"))
1901 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1902 else if (!strcasecmp(parameter[1], "detail"))
1903 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1904 else if (!strcasecmp(parameter[1], "donotenter"))
1905 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1906 else if (!strcasecmp(parameter[1], "dust"))
1907 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1908 else if (!strcasecmp(parameter[1], "hint"))
1909 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1910 else if (!strcasecmp(parameter[1], "fog"))
1911 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1912 else if (!strcasecmp(parameter[1], "lava"))
1913 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1914 else if (!strcasecmp(parameter[1], "lightfilter"))
1915 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1916 else if (!strcasecmp(parameter[1], "lightgrid"))
1917 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1918 else if (!strcasecmp(parameter[1], "metalsteps"))
1919 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1920 else if (!strcasecmp(parameter[1], "nodamage"))
1921 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1922 else if (!strcasecmp(parameter[1], "nodlight"))
1923 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1924 else if (!strcasecmp(parameter[1], "nodraw"))
1925 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1926 else if (!strcasecmp(parameter[1], "nodrop"))
1927 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1928 else if (!strcasecmp(parameter[1], "noimpact"))
1929 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1930 else if (!strcasecmp(parameter[1], "nolightmap"))
1931 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1932 else if (!strcasecmp(parameter[1], "nomarks"))
1933 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1934 else if (!strcasecmp(parameter[1], "nomipmaps"))
1935 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1936 else if (!strcasecmp(parameter[1], "nonsolid"))
1937 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1938 else if (!strcasecmp(parameter[1], "origin"))
1939 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1940 else if (!strcasecmp(parameter[1], "playerclip"))
1941 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1942 else if (!strcasecmp(parameter[1], "sky"))
1943 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1944 else if (!strcasecmp(parameter[1], "slick"))
1945 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1946 else if (!strcasecmp(parameter[1], "slime"))
1947 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1948 else if (!strcasecmp(parameter[1], "structural"))
1949 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1950 else if (!strcasecmp(parameter[1], "trans"))
1951 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1952 else if (!strcasecmp(parameter[1], "water"))
1953 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1954 else if (!strcasecmp(parameter[1], "pointlight"))
1955 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1956 else if (!strcasecmp(parameter[1], "antiportal"))
1957 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1958 else if (!strcasecmp(parameter[1], "skip"))
1959 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1962 // try custom surfaceparms
1963 for (j = 0; j < numcustsurfaceflags; j++)
1965 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1967 shader.surfaceflags |= custsurfaceflags[j];
1972 if (j == numcustsurfaceflags)
1973 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1976 else if (!strcasecmp(parameter[0], "dpshadow"))
1977 shader.dpshadow = true;
1978 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1979 shader.dpnoshadow = true;
1980 else if (!strcasecmp(parameter[0], "dpnortlight"))
1981 shader.dpnortlight = true;
1982 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1983 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1984 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1985 shader.dpmeshcollisions = true;
1986 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1987 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1989 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1990 shader.dpshaderkill = dpshaderkill;
1992 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1993 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1995 const char *op = NULL;
1996 if (numparameters >= 3)
2000 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
2001 shader.dpshaderkill = dpshaderkill;
2003 else if (numparameters >= 4 && !strcmp(op, "=="))
2005 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
2006 shader.dpshaderkill = dpshaderkill;
2008 else if (numparameters >= 4 && !strcmp(op, "!="))
2010 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
2011 shader.dpshaderkill = dpshaderkill;
2013 else if (numparameters >= 4 && !strcmp(op, ">"))
2015 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
2016 shader.dpshaderkill = dpshaderkill;
2018 else if (numparameters >= 4 && !strcmp(op, "<"))
2020 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
2021 shader.dpshaderkill = dpshaderkill;
2023 else if (numparameters >= 4 && !strcmp(op, ">="))
2025 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
2026 shader.dpshaderkill = dpshaderkill;
2028 else if (numparameters >= 4 && !strcmp(op, "<="))
2030 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
2031 shader.dpshaderkill = dpshaderkill;
2035 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2038 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2040 // some q3 skies don't have the sky parm set
2041 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2042 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2044 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2046 // some q3 skies don't have the sky parm set
2047 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2048 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2049 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2051 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2053 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2054 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2056 else if (!strcasecmp(parameter[0], "nomipmaps"))
2057 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2058 else if (!strcasecmp(parameter[0], "nopicmip"))
2059 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2060 else if (!strcasecmp(parameter[0], "polygonoffset"))
2061 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2062 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2064 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2065 if(numparameters >= 2)
2067 shader.biaspolygonfactor = atof(parameter[1]);
2068 if(numparameters >= 3)
2069 shader.biaspolygonoffset = atof(parameter[2]);
2071 shader.biaspolygonoffset = 0;
2074 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2076 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2077 if (!strcasecmp(parameter[1], "sky"))
2078 shader.transparentsort = TRANSPARENTSORT_SKY;
2079 else if (!strcasecmp(parameter[1], "distance"))
2080 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2081 else if (!strcasecmp(parameter[1], "hud"))
2082 shader.transparentsort = TRANSPARENTSORT_HUD;
2084 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2086 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2088 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2089 shader.refractfactor = atof(parameter[1]);
2090 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2092 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2094 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2095 shader.reflectfactor = atof(parameter[1]);
2096 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2098 else if (!strcasecmp(parameter[0], "dpcamera"))
2100 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2102 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2104 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2105 shader.reflectmin = atof(parameter[1]);
2106 shader.reflectmax = atof(parameter[2]);
2107 shader.refractfactor = atof(parameter[3]);
2108 shader.reflectfactor = atof(parameter[4]);
2109 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2110 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2111 shader.r_water_wateralpha = atof(parameter[11]);
2113 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2115 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2116 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2118 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2120 shader.specularscalemod = atof(parameter[1]);
2122 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2124 shader.specularpowermod = atof(parameter[1]);
2126 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2128 shader.rtlightambient = atof(parameter[1]);
2130 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2132 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2133 shader.offsetmapping = OFFSETMAPPING_OFF;
2134 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2135 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2136 else if (!strcasecmp(parameter[1], "linear"))
2137 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2138 else if (!strcasecmp(parameter[1], "relief"))
2139 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2140 if (numparameters >= 3)
2141 shader.offsetscale = atof(parameter[2]);
2142 if (numparameters >= 5)
2144 if(!strcasecmp(parameter[3], "bias"))
2145 shader.offsetbias = atof(parameter[4]);
2146 else if(!strcasecmp(parameter[3], "match"))
2147 shader.offsetbias = 1.0f - atof(parameter[4]);
2148 else if(!strcasecmp(parameter[3], "match8"))
2149 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2150 else if(!strcasecmp(parameter[3], "match16"))
2151 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2154 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2157 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2158 if (!shader.deforms[deformindex].deform)
2160 if (deformindex < Q3MAXDEFORMS)
2162 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2163 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2164 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2165 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2166 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2167 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2168 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2169 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2170 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2171 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2172 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2173 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2174 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2175 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2176 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2177 else if (!strcasecmp(parameter[1], "wave" ))
2179 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2180 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2181 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2182 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2184 else if (!strcasecmp(parameter[1], "move" ))
2186 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2187 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2188 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2189 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2194 // hide this shader if a cvar said it should be killed
2195 if (shader.dpshaderkill)
2196 shader.numlayers = 0;
2197 // fix up multiple reflection types
2198 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2199 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2201 Q3Shader_AddToHash (&shader);
2205 FS_FreeSearch(search);
2206 // free custinfoparm values
2207 for (j = 0; j < numcustsurfaceflags; j++)
2208 Mem_Free(custsurfaceparmnames[j]);
2211 shader_t *Mod_LookupQ3Shader(const char *name)
2213 unsigned short hash;
2214 q3shader_hash_entry_t* entry;
2216 Mod_LoadQ3Shaders();
2217 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2218 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2219 while (entry != NULL)
2221 if (strcasecmp (entry->shader.name, name) == 0)
2222 return &entry->shader;
2223 entry = entry->chain;
2228 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2230 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2231 shaderpass->framerate = 0.0f;
2232 shaderpass->numframes = 1;
2233 shaderpass->blendfunc[0] = GL_ONE;
2234 shaderpass->blendfunc[1] = GL_ZERO;
2235 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2236 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2237 shaderpass->alphatest = false;
2238 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2239 shaderpass->skinframes[0] = skinframe;
2243 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2246 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2247 shaderpass->alphatest = layer->alphatest != 0;
2248 shaderpass->framerate = layer->framerate;
2249 shaderpass->numframes = layer->numframes;
2250 shaderpass->blendfunc[0] = layer->blendfunc[0];
2251 shaderpass->blendfunc[1] = layer->blendfunc[1];
2252 shaderpass->rgbgen = layer->rgbgen;
2253 shaderpass->alphagen = layer->alphagen;
2254 shaderpass->tcgen = layer->tcgen;
2255 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2256 shaderpass->tcmods[j] = layer->tcmods[j];
2257 for (j = 0; j < layer->numframes; j++)
2259 for (int i = 0; layer->texturename[j][i]; i++)
2260 if(layer->texturename[j][i] == '\\')
2261 layer->texturename[j][i] = '/';
2262 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2267 qbool Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qbool warnmissing, qbool fallback, int defaulttexflags, int defaultmaterialflags)
2269 int texflagsmask, texflagsor;
2270 qbool success = true;
2274 strlcpy(texture->name, name, sizeof(texture->name));
2275 texture->basealpha = 1.0f;
2276 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2278 // allow disabling of picmip or compression by defaulttexflags
2280 if(!(defaulttexflags & TEXF_PICMIP))
2281 texflagsmask &= ~TEXF_PICMIP;
2282 if(!(defaulttexflags & TEXF_COMPRESS))
2283 texflagsmask &= ~TEXF_COMPRESS;
2285 if(defaulttexflags & TEXF_ISWORLD)
2286 texflagsor |= TEXF_ISWORLD;
2287 if(defaulttexflags & TEXF_ISSPRITE)
2288 texflagsor |= TEXF_ISSPRITE;
2289 // unless later loaded from the shader
2290 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2291 texture->offsetscale = 1;
2292 texture->offsetbias = 0;
2293 texture->specularscalemod = 1;
2294 texture->specularpowermod = 1;
2295 texture->rtlightambient = 0;
2296 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2297 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2298 // JUST GREP FOR "specularscalemod = 1".
2302 if (developer_loading.integer)
2303 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2305 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2307 texture->basematerialflags = MATERIALFLAG_SKY;
2308 if (shader->skyboxname[0] && loadmodel)
2310 // quake3 seems to append a _ to the skybox name, so this must do so as well
2311 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2314 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2315 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2317 texture->basematerialflags = MATERIALFLAG_WALL;
2319 if (shader->layers[0].alphatest)
2320 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2321 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2322 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2323 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2325 texture->biaspolygonoffset += shader->biaspolygonoffset;
2326 texture->biaspolygonfactor += shader->biaspolygonfactor;
2328 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2329 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2330 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2331 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2332 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2333 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2334 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2335 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2336 texture->customblendfunc[0] = GL_ONE;
2337 texture->customblendfunc[1] = GL_ZERO;
2338 texture->transparentsort = shader->transparentsort;
2339 if (shader->numlayers > 0)
2341 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2342 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2344 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2345 * additive GL_ONE GL_ONE
2346 additive weird GL_ONE GL_SRC_ALPHA
2347 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2348 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2349 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2350 brighten GL_DST_COLOR GL_ONE
2351 brighten GL_ONE GL_SRC_COLOR
2352 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2353 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2354 * modulate GL_DST_COLOR GL_ZERO
2355 * modulate GL_ZERO GL_SRC_COLOR
2356 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2357 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2358 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2359 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2360 * no blend GL_ONE GL_ZERO
2361 nothing GL_ZERO GL_ONE
2363 // if not opaque, figure out what blendfunc to use
2364 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2366 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2367 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2368 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2369 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2370 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2371 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2373 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2376 if (!shader->lighting)
2377 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2379 // here be dragons: convert quake3 shaders to material
2380 if (shader->numlayers > 0)
2383 int terrainbackgroundlayer = -1;
2384 int lightmaplayer = -1;
2385 int alphagenspecularlayer = -1;
2386 int rgbgenvertexlayer = -1;
2387 int rgbgendiffuselayer = -1;
2388 int materiallayer = -1;
2389 int endofprelayers = 0;
2390 int firstpostlayer = 0;
2391 int shaderpassindex = 0;
2392 for (i = 0; i < shader->numlayers; i++)
2394 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2396 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2397 rgbgenvertexlayer = i;
2398 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2399 rgbgendiffuselayer = i;
2400 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2401 alphagenspecularlayer = i;
2403 if (shader->numlayers >= 2
2404 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2405 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2406 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2407 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2409 // terrain blend or certain other effects involving alphatest over a regular layer
2410 terrainbackgroundlayer = 0;
2412 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2413 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2415 else if (lightmaplayer == 0)
2417 // ordinary texture but with $lightmap before diffuse
2419 firstpostlayer = lightmaplayer + 2;
2421 else if (lightmaplayer >= 1)
2423 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2424 endofprelayers = lightmaplayer - 1;
2425 materiallayer = lightmaplayer - 1;
2426 firstpostlayer = lightmaplayer + 1;
2428 else if (rgbgenvertexlayer >= 0)
2430 // map models with baked lighting
2431 materiallayer = rgbgenvertexlayer;
2432 endofprelayers = rgbgenvertexlayer;
2433 firstpostlayer = rgbgenvertexlayer + 1;
2434 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2435 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2436 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2438 else if (rgbgendiffuselayer >= 0)
2440 // entity models with dynamic lighting
2441 materiallayer = rgbgendiffuselayer;
2442 endofprelayers = rgbgendiffuselayer;
2443 firstpostlayer = rgbgendiffuselayer + 1;
2444 // 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)...
2445 if (alphagenspecularlayer >= 0)
2446 firstpostlayer = alphagenspecularlayer + 1;
2450 // special effects shaders - treat first as primary layer and do everything else as post
2455 // convert the main material layer
2456 // 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
2457 if (materiallayer >= 0)
2458 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2459 // convert the terrain background blend layer (if any)
2460 if (terrainbackgroundlayer >= 0)
2461 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2462 // convert the prepass layers (if any)
2463 texture->startpreshaderpass = shaderpassindex;
2464 for (i = 0; i < endofprelayers; i++)
2465 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2466 texture->endpreshaderpass = shaderpassindex;
2467 texture->startpostshaderpass = shaderpassindex;
2468 // convert the postpass layers (if any)
2469 for (i = firstpostlayer; i < shader->numlayers; i++)
2470 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2471 texture->startpostshaderpass = shaderpassindex;
2474 if (shader->dpshadow)
2475 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2476 if (shader->dpnoshadow)
2477 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2478 if (shader->dpnortlight)
2479 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2480 if (shader->vertexalpha)
2481 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2482 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2483 texture->reflectmin = shader->reflectmin;
2484 texture->reflectmax = shader->reflectmax;
2485 texture->refractfactor = shader->refractfactor;
2486 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2487 texture->reflectfactor = shader->reflectfactor;
2488 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2489 texture->r_water_wateralpha = shader->r_water_wateralpha;
2490 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2491 texture->offsetmapping = shader->offsetmapping;
2492 texture->offsetscale = shader->offsetscale;
2493 texture->offsetbias = shader->offsetbias;
2494 texture->specularscalemod = shader->specularscalemod;
2495 texture->specularpowermod = shader->specularpowermod;
2496 texture->rtlightambient = shader->rtlightambient;
2497 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2498 if (shader->dpreflectcube[0])
2499 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2501 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2502 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2503 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2504 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2505 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2506 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2507 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2508 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2509 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2511 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2512 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2513 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2514 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2515 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2516 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2517 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2520 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2523 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2524 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2525 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2526 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2527 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2528 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2529 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2530 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2531 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2532 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2534 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2536 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2537 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2539 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2540 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2541 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2542 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2544 texture->surfaceflags = shader->surfaceflags;
2545 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2546 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2547 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2548 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2549 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2550 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2551 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2552 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2553 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2554 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2555 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2556 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2557 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2558 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2559 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2560 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2561 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2562 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2563 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2564 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2565 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2566 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2567 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2568 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2569 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2570 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2571 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2572 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2573 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2574 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2575 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2576 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2578 if (shader->dpmeshcollisions)
2579 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2580 if (shader->dpshaderkill && developer_extra.integer)
2581 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2583 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2585 if (developer_extra.integer)
2586 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2587 texture->basematerialflags = defaultmaterialflags;
2588 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2590 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2592 if (developer_extra.integer)
2593 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2594 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2595 texture->supercontents = SUPERCONTENTS_SOLID;
2599 if (developer_extra.integer)
2600 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2601 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2603 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2604 texture->supercontents = SUPERCONTENTS_SOLID;
2606 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2608 texture->basematerialflags = MATERIALFLAG_SKY;
2609 texture->supercontents = SUPERCONTENTS_SKY;
2613 texture->basematerialflags = defaultmaterialflags;
2614 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2616 if(cls.state == ca_dedicated)
2618 texture->materialshaderpass = NULL;
2623 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2626 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2627 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2628 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2629 if (texture->q2contents)
2630 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2634 if (!success && warnmissing)
2635 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2638 // init the animation variables
2639 texture->currentframe = texture;
2640 texture->currentmaterialflags = texture->basematerialflags;
2641 if (!texture->materialshaderpass)
2642 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2643 if (!texture->materialshaderpass->skinframes[0])
2644 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2645 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2646 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2650 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2652 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2653 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2655 strlcpy(texture->name, name, sizeof(texture->name));
2656 texture->basealpha = 1.0f;
2657 texture->basematerialflags = materialflags;
2658 texture->supercontents = supercontents;
2660 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2661 texture->offsetscale = 1;
2662 texture->offsetbias = 0;
2663 texture->specularscalemod = 1;
2664 texture->specularpowermod = 1;
2665 texture->rtlightambient = 0;
2666 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2667 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2668 // JUST GREP FOR "specularscalemod = 1".
2670 if (developer_extra.integer)
2671 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2673 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2675 // init the animation variables
2676 texture->currentmaterialflags = texture->basematerialflags;
2677 texture->currentframe = texture;
2678 texture->currentskinframe = skinframe;
2679 texture->backgroundcurrentskinframe = NULL;
2682 void Mod_UnloadCustomMaterial(texture_t *texture, qbool purgeskins)
2684 long unsigned int i, j;
2685 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2687 if (texture->shaderpasses[i])
2690 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2691 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2692 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2693 Mem_Free(texture->shaderpasses[i]);
2694 texture->shaderpasses[i] = NULL;
2697 texture->materialshaderpass = NULL;
2698 texture->currentskinframe = NULL;
2699 texture->backgroundcurrentskinframe = NULL;
2702 skinfile_t *Mod_LoadSkinFiles(void)
2704 int i, words, line, wordsoverflow;
2707 skinfile_t *skinfile = NULL, *first = NULL;
2708 skinfileitem_t *skinfileitem;
2709 char word[10][MAX_QPATH];
2714 U_bodyBox,models/players/Legoman/BikerA2.tga
2715 U_RArm,models/players/Legoman/BikerA1.tga
2716 U_LArm,models/players/Legoman/BikerA1.tga
2717 U_armor,common/nodraw
2718 U_sword,common/nodraw
2719 U_shield,common/nodraw
2720 U_homb,common/nodraw
2721 U_backpack,common/nodraw
2722 U_colcha,common/nodraw
2727 memset(word, 0, sizeof(word));
2728 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2730 // If it's the first file we parse
2731 if (skinfile == NULL)
2733 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2738 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2739 skinfile = skinfile->next;
2741 skinfile->next = NULL;
2743 for(line = 0;;line++)
2746 if (!COM_ParseToken_QuakeC(&data, true))
2748 if (!strcmp(com_token, "\n"))
2751 wordsoverflow = false;
2755 strlcpy(word[words++], com_token, sizeof (word[0]));
2757 wordsoverflow = true;
2759 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2762 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);
2765 // words is always >= 1
2766 if (!strcmp(word[0], "replace"))
2770 if (developer_loading.integer)
2771 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2772 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2773 skinfileitem->next = skinfile->items;
2774 skinfile->items = skinfileitem;
2775 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2776 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2779 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]);
2781 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2783 // tag name, like "tag_weapon,"
2784 // not used for anything (not even in Quake3)
2786 else if (words >= 2 && !strcmp(word[1], ","))
2788 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2789 if (developer_loading.integer)
2790 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2791 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2792 skinfileitem->next = skinfile->items;
2793 skinfile->items = skinfileitem;
2794 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2795 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2798 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);
2803 loadmodel->numskins = i;
2807 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2810 skinfileitem_t *skinfileitem, *nextitem;
2811 for (;skinfile;skinfile = next)
2813 next = skinfile->next;
2814 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2816 nextitem = skinfileitem->next;
2817 Mem_Free(skinfileitem);
2823 int Mod_CountSkinFiles(skinfile_t *skinfile)
2826 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2830 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2833 double isnap = 1.0 / snap;
2834 for (i = 0;i < numvertices*numcomponents;i++)
2835 vertices[i] = floor(vertices[i]*isnap)*snap;
2838 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2840 int i, outtriangles;
2841 float edgedir1[3], edgedir2[3], temp[3];
2842 // a degenerate triangle is one with no width (thickness, surface area)
2843 // these are characterized by having all 3 points colinear (along a line)
2844 // or having two points identical
2845 // the simplest check is to calculate the triangle's area
2846 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2848 // calculate first edge
2849 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2850 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2851 CrossProduct(edgedir1, edgedir2, temp);
2852 if (VectorLength2(temp) < 0.001f)
2853 continue; // degenerate triangle (no area)
2854 // valid triangle (has area)
2855 VectorCopy(inelement3i, outelement3i);
2859 return outtriangles;
2862 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2865 int firstvertex, lastvertex;
2866 if (numelements > 0 && elements)
2868 firstvertex = lastvertex = elements[0];
2869 for (i = 1;i < numelements;i++)
2872 firstvertex = min(firstvertex, e);
2873 lastvertex = max(lastvertex, e);
2877 firstvertex = lastvertex = 0;
2878 if (firstvertexpointer)
2879 *firstvertexpointer = firstvertex;
2880 if (lastvertexpointer)
2881 *lastvertexpointer = lastvertex;
2884 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2886 // make an optimal set of texture-sorted batches to draw...
2888 int *firstsurfacefortexture;
2889 int *numsurfacesfortexture;
2890 if (!mod->sortedmodelsurfaces)
2891 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2892 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2893 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2894 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2895 for (j = 0;j < mod->nummodelsurfaces;j++)
2897 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2898 if(!surface->texture)
2900 t = (int)(surface->texture - mod->data_textures);
2901 numsurfacesfortexture[t]++;
2904 for (t = 0;t < mod->num_textures;t++)
2906 firstsurfacefortexture[t] = j;
2907 j += numsurfacesfortexture[t];
2909 for (j = 0;j < mod->nummodelsurfaces;j++)
2911 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2912 if (!surface->texture)
2914 t = (int)(surface->texture - mod->data_textures);
2915 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2917 Mem_Free(firstsurfacefortexture);
2918 Mem_Free(numsurfacesfortexture);
2921 void Mod_BuildVBOs(void)
2923 if(cls.state == ca_dedicated)
2926 if (!loadmodel->surfmesh.num_vertices)
2929 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2932 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2934 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2936 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2937 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2942 // upload short indices as a buffer
2943 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2944 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);
2946 // upload int indices as a buffer
2947 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2948 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);
2950 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2951 // we put several vertex data streams in the same buffer
2952 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2957 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2958 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2959 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2960 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2961 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2962 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2963 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2964 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2965 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2966 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2967 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2968 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2969 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2970 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2971 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2972 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2973 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2974 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2975 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2976 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2977 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2978 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2979 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2980 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2981 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2982 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2983 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2984 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2989 extern cvar_t mod_obj_orientation;
2990 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2992 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2994 const char *texname;
2996 const float *v, *vn, *vt;
2998 size_t outbufferpos = 0;
2999 size_t outbuffermax = 0x100000;
3000 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3001 const msurface_t *surface;
3002 const int maxtextures = 256;
3003 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3004 dp_model_t *submodel;
3006 // construct the mtllib file
3007 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3010 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3013 countvertices += surface->num_vertices;
3014 countfaces += surface->num_triangles;
3015 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3016 for (textureindex = 0;textureindex < counttextures;textureindex++)
3017 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3019 if (textureindex < counttextures)
3020 continue; // already wrote this material entry
3021 if (textureindex >= maxtextures)
3022 continue; // just a precaution
3023 textureindex = counttextures++;
3024 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3025 if (outbufferpos >= outbuffermax >> 1)
3028 oldbuffer = outbuffer;
3029 outbuffer = (char *) Z_Malloc(outbuffermax);
3030 memcpy(outbuffer, oldbuffer, outbufferpos);
3033 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");
3038 // write the mtllib file
3039 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3041 // construct the obj file
3043 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);
3047 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)
3049 if (outbufferpos >= outbuffermax >> 1)
3052 oldbuffer = outbuffer;
3053 outbuffer = (char *) Z_Malloc(outbuffermax);
3054 memcpy(outbuffer, oldbuffer, outbufferpos);
3057 if(mod_obj_orientation.integer)
3058 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]);
3060 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]);
3065 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3067 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3070 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3071 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3073 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3074 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3077 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3079 if (outbufferpos >= outbuffermax >> 1)
3082 oldbuffer = outbuffer;
3083 outbuffer = (char *) Z_Malloc(outbuffermax);
3084 memcpy(outbuffer, oldbuffer, outbufferpos);
3090 if(mod_obj_orientation.integer)
3091 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);
3093 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);
3100 // write the obj file
3101 FS_WriteFile(filename, outbuffer, outbufferpos);
3105 Z_Free(texturenames);
3108 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3111 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qbool writetriangles)
3113 int countnodes = 0, counttriangles = 0, countframes = 0;
3121 size_t outbufferpos = 0;
3122 size_t outbuffermax = 0x100000;
3123 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3124 const msurface_t *surface;
3125 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3128 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3130 if (outbufferpos >= outbuffermax >> 1)
3133 oldbuffer = outbuffer;
3134 outbuffer = (char *) Z_Malloc(outbuffermax);
3135 memcpy(outbuffer, oldbuffer, outbufferpos);
3139 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3143 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3146 for (poseindex = 0;poseindex < numposes;poseindex++)
3149 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3152 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3156 matrix4x4_t posematrix;
3157 if (outbufferpos >= outbuffermax >> 1)
3160 oldbuffer = outbuffer;
3161 outbuffer = (char *) Z_Malloc(outbuffermax);
3162 memcpy(outbuffer, oldbuffer, outbufferpos);
3166 // strangely the smd angles are for a transposed matrix, so we
3167 // have to generate a transposed matrix, then convert that...
3168 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3169 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3170 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3171 if (angles[0] >= 180) angles[0] -= 360;
3172 if (angles[1] >= 180) angles[1] -= 360;
3173 if (angles[2] >= 180) angles[2] -= 360;
3177 float a = DEG2RAD(angles[ROLL]);
3178 float b = DEG2RAD(angles[PITCH]);
3179 float c = DEG2RAD(angles[YAW]);
3180 float cy, sy, cp, sp, cr, sr;
3182 // smd matrix construction, for comparing
3193 test[1][0] = sr*sp*cy+cr*-sy;
3194 test[1][1] = sr*sp*sy+cr*cy;
3196 test[2][0] = (cr*sp*cy+-sr*-sy);
3197 test[2][1] = (cr*sp*sy+-sr*cy);
3199 test[3][0] = pose[9];
3200 test[3][1] = pose[10];
3201 test[3][2] = pose[11];
3204 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]));
3209 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3214 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3217 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3219 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3222 if (outbufferpos >= outbuffermax >> 1)
3225 oldbuffer = outbuffer;
3226 outbuffer = (char *) Z_Malloc(outbuffermax);
3227 memcpy(outbuffer, oldbuffer, outbufferpos);
3230 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3233 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3235 const int index = e[2-cornerindex];
3236 const float *v = model->surfmesh.data_vertex3f + index * 3;
3237 const float *vn = model->surfmesh.data_normal3f + index * 3;
3238 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3239 const int b = model->surfmesh.blends[index];
3240 if (b < model->num_bones)
3241 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]);
3244 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3245 const unsigned char *wi = w->index;
3246 const unsigned char *wf = w->influence;
3247 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);
3248 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);
3249 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);
3250 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]);
3257 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3262 FS_WriteFile(filename, outbuffer, outbufferpos);
3265 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3272 decompiles a model to editable files
3275 static void Mod_Decompile_f(cmd_state_t *cmd)
3277 int i, j, k, l, first, count;
3279 char inname[MAX_QPATH];
3280 char outname[MAX_QPATH];
3281 char mtlname[MAX_QPATH];
3282 char basename[MAX_QPATH];
3283 char animname[MAX_QPATH];
3284 char animname2[MAX_QPATH];
3285 char zymtextbuffer[16384];
3286 char dpmtextbuffer[16384];
3287 char framegroupstextbuffer[16384];
3288 int zymtextsize = 0;
3289 int dpmtextsize = 0;
3290 int framegroupstextsize = 0;
3293 if (Cmd_Argc(cmd) != 2)
3295 Con_Print("usage: modeldecompile <filename>\n");
3299 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3300 FS_StripExtension(inname, basename, sizeof(basename));
3302 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3305 Con_Print("No such model\n");
3308 if (mod->brush.submodel)
3310 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3311 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3312 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3315 if (!mod->surfmesh.num_triangles)
3317 Con_Print("Empty model (or sprite)\n");
3321 // export OBJ if possible (not on sprites)
3322 if (mod->surfmesh.num_triangles)
3324 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3325 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3326 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3329 // export SMD if possible (only for skeletal models)
3330 if (mod->surfmesh.num_triangles && mod->num_bones)
3332 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3333 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3334 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3335 if (l > 0) zymtextsize += l;
3336 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3337 if (l > 0) dpmtextsize += l;
3338 for (i = 0;i < mod->numframes;i = j)
3340 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3341 first = mod->animscenes[i].firstframe;
3342 if (mod->animscenes[i].framecount > 1)
3345 count = mod->animscenes[i].framecount;
3351 // check for additional frames with same name
3352 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3353 if(animname[l] < '0' || animname[l] > '9')
3355 if(k > 0 && animname[k-1] == '_')
3358 count = mod->num_poses - first;
3359 for (j = i + 1;j < mod->numframes;j++)
3361 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3362 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3363 if(animname2[l] < '0' || animname2[l] > '9')
3365 if(k > 0 && animname[k-1] == '_')
3368 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3370 count = mod->animscenes[j].firstframe - first;
3374 // if it's only one frame, use the original frame name
3376 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3379 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3380 Mod_Decompile_SMD(mod, outname, first, count, false);
3381 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3383 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3384 if (l > 0) zymtextsize += l;
3386 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3388 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3389 if (l > 0) dpmtextsize += l;
3391 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3393 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3394 if (l > 0) framegroupstextsize += l;
3398 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3400 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3401 if (framegroupstextsize)
3402 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3406 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3409 memset(state, 0, sizeof(*state));
3410 state->width = width;
3411 state->height = height;
3412 state->currentY = 0;
3413 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3414 for (y = 0;y < state->height;y++)
3416 state->rows[y].currentX = 0;
3417 state->rows[y].rowY = -1;
3421 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3424 state->currentY = 0;
3425 for (y = 0;y < state->height;y++)
3427 state->rows[y].currentX = 0;
3428 state->rows[y].rowY = -1;
3432 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3435 Mem_Free(state->rows);
3436 memset(state, 0, sizeof(*state));
3439 qbool Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3441 mod_alloclightmap_row_t *row;
3444 row = state->rows + blockheight;
3445 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3447 if (state->currentY + blockheight <= state->height)
3449 // use the current allocation position
3450 row->rowY = state->currentY;
3452 state->currentY += blockheight;
3456 // find another position
3457 for (y = blockheight;y < state->height;y++)
3459 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3461 row = state->rows + y;
3465 if (y == state->height)
3470 *outx = row->currentX;
3471 row->currentX += blockwidth;
3476 typedef struct lightmapsample_s
3480 float *vertex_color;
3481 unsigned char *lm_bgr;
3482 unsigned char *lm_dir;
3486 typedef struct lightmapvertex_s
3491 float texcoordbase[2];
3492 float texcoordlightmap[2];
3493 float lightcolor[4];
3497 typedef struct lightmaptriangle_s
3505 // 2D modelspace coordinates of min corner
3506 // snapped to lightmap grid but not in grid coordinates
3508 // 2D modelspace to lightmap coordinate scale
3516 typedef struct lightmaplight_s
3527 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3529 #define MAX_LIGHTMAPSAMPLES 64
3530 static int mod_generatelightmaps_numoffsets[3];
3531 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3533 static int mod_generatelightmaps_numlights;
3534 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3536 extern cvar_t r_shadow_lightattenuationdividebias;
3537 extern cvar_t r_shadow_lightattenuationlinearscale;
3539 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3544 float relativepoint[3];
3551 float lightorigin[3];
3555 float lightcolor[3];
3557 for (i = 0;i < 5*3;i++)
3559 for (index = 0;;index++)
3561 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3566 lightradius2 = lightradius * lightradius;
3567 VectorSubtract(lightorigin, pos, relativepoint);
3568 dist2 = VectorLength2(relativepoint);
3569 if (dist2 >= lightradius2)
3571 lightiradius = 1.0f / lightradius;
3572 dist = sqrt(dist2) * lightiradius;
3573 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3574 if (intensity <= 0.0f)
3576 if (model && model->TraceLine)
3578 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3579 if (trace.fraction < 1)
3582 // scale down intensity to add to both ambient and diffuse
3583 //intensity *= 0.5f;
3584 VectorNormalize(relativepoint);
3585 VectorScale(lightcolor, intensity, color);
3586 VectorMA(sample , 0.5f , color, sample );
3587 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3588 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3589 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3590 // calculate a weighted average light direction as well
3591 intensity *= VectorLength(color);
3592 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3594 // calculate the direction we'll use to reduce the sample to a directional light source
3595 VectorCopy(sample + 12, dir);
3596 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3597 VectorNormalize(dir);
3598 // extract the diffuse color along the chosen direction and scale it
3599 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3600 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3601 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3602 // subtract some of diffuse from ambient
3603 VectorMA(sample, -0.333f, diffuse, ambient);
3604 // store the normalized lightdir
3605 VectorCopy(dir, lightdir);
3608 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3612 const msurface_t *surface;
3613 const float *vertex3f = model->surfmesh.data_vertex3f;
3614 const int *element3i = model->surfmesh.data_element3i;
3617 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3619 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3621 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3623 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3625 VectorCopy(vertex3f + 3*e[0], v2[0]);
3626 VectorCopy(vertex3f + 3*e[1], v2[1]);
3627 VectorCopy(vertex3f + 3*e[2], v2[2]);
3628 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3633 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3635 int maxnodes = 1<<14;
3636 svbsp_node_t *nodes;
3641 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3642 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3643 VectorCopy(lightinfo->origin, origin);
3644 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3647 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3648 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3649 if (svbsp.ranoutofnodes)
3652 if (maxnodes > 1<<22)
3658 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3663 if (svbsp.numnodes > 0)
3665 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3666 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3667 lightinfo->svbsp = svbsp;
3672 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3676 lightmaplight_t *lightinfo;
3680 mod_generatelightmaps_numlights = 0;
3681 for (index = 0;;index++)
3683 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3687 mod_generatelightmaps_numlights++;
3689 if (mod_generatelightmaps_numlights > 0)
3691 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3692 lightinfo = mod_generatelightmaps_lightinfo;
3693 for (index = 0;;index++)
3695 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3702 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3704 lightinfo->iradius = 1.0f / lightinfo->radius;
3705 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3706 // TODO: compute svbsp
3707 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3711 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3714 if (mod_generatelightmaps_lightinfo)
3716 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3717 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3718 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3719 Mem_Free(mod_generatelightmaps_lightinfo);
3721 mod_generatelightmaps_lightinfo = NULL;
3722 mod_generatelightmaps_numlights = 0;
3725 static qbool Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3727 const svbsp_node_t *node;
3728 const svbsp_node_t *nodes = svbsp->nodes;
3733 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3735 return num == -1; // true if empty, false if solid (shadowed)
3738 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3741 float relativepoint[3];
3750 const lightmaplight_t *lightinfo;
3752 for (i = 0;i < 5*3;i++)
3754 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3756 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3757 VectorSubtract(lightinfo->origin, pos, relativepoint);
3758 // don't accept light from behind a surface, it causes bad shading
3759 if (normal && DotProduct(relativepoint, normal) <= 0)
3761 dist2 = VectorLength2(relativepoint);
3762 if (dist2 >= lightinfo->radius2)
3764 dist = sqrt(dist2) * lightinfo->iradius;
3765 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3768 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3772 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3774 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3776 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3779 // for light grid we'd better check visibility of the offset point
3780 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3781 if (trace.fraction < 1)
3782 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3785 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3790 // scale intensity according to how many rays succeeded
3791 // we know one test is valid, half of the rest will fail...
3792 //if (normal && tests > 1)
3793 // intensity *= (tests - 1.0f) / tests;
3794 intensity *= (float)hits / tests;
3796 // scale down intensity to add to both ambient and diffuse
3797 //intensity *= 0.5f;
3798 VectorNormalize(relativepoint);
3799 VectorScale(lightinfo->color, intensity, color);
3800 VectorMA(sample , 0.5f , color, sample );
3801 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3802 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3803 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3804 // calculate a weighted average light direction as well
3805 intensity *= VectorLength(color);
3806 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3810 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3816 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3817 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3818 VectorCopy(sample + 12, dir);
3819 VectorNormalize(dir);
3820 //VectorAdd(dir, normal, dir);
3821 //VectorNormalize(dir);
3822 f = DotProduct(dir, normal);
3823 f = max(0, f) * 255.0f;
3824 VectorScale(sample, f, color);
3825 //VectorCopy(normal, dir);
3826 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3827 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3828 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3829 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3831 lm_dir[0] = (unsigned char)dir[2];
3832 lm_dir[1] = (unsigned char)dir[1];
3833 lm_dir[2] = (unsigned char)dir[0];
3837 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3840 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3841 VectorCopy(sample, vertex_color);
3844 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3850 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3851 // calculate the direction we'll use to reduce the sample to a directional light source
3852 VectorCopy(sample + 12, dir);
3853 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3854 VectorNormalize(dir);
3855 // extract the diffuse color along the chosen direction and scale it
3856 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3857 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3858 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3859 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3860 VectorScale(sample, 127.5f, ambient);
3861 VectorMA(ambient, -0.333f, diffuse, ambient);
3862 // encode to the grid format
3863 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3864 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3865 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3866 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3867 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3868 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3869 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3870 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3871 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));}
3874 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3879 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3880 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3881 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3882 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3883 radius[0] = mod_generatelightmaps_lightmapradius.value;
3884 radius[1] = mod_generatelightmaps_vertexradius.value;
3885 radius[2] = mod_generatelightmaps_gridradius.value;
3886 for (i = 0;i < 3;i++)
3888 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3891 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3896 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3898 msurface_t *surface;
3901 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3903 surface = model->data_surfaces + surfaceindex;
3904 surface->lightmaptexture = NULL;
3905 surface->deluxemaptexture = NULL;
3907 if (model->brushq3.data_lightmaps)
3909 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3910 if (model->brushq3.data_lightmaps[i])
3911 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3912 Mem_Free(model->brushq3.data_lightmaps);
3913 model->brushq3.data_lightmaps = NULL;
3915 if (model->brushq3.data_deluxemaps)
3917 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3918 if (model->brushq3.data_deluxemaps[i])
3919 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3920 Mem_Free(model->brushq3.data_deluxemaps);
3921 model->brushq3.data_deluxemaps = NULL;
3925 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3927 msurface_t *surface;
3933 surfmesh_t oldsurfmesh;
3935 unsigned char *data;
3936 oldsurfmesh = model->surfmesh;
3937 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3938 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3940 size += model->surfmesh.num_vertices * sizeof(float[3]);
3941 size += model->surfmesh.num_vertices * sizeof(float[3]);
3942 size += model->surfmesh.num_vertices * sizeof(float[3]);
3943 size += model->surfmesh.num_vertices * sizeof(float[3]);
3944 size += model->surfmesh.num_vertices * sizeof(float[2]);
3945 size += model->surfmesh.num_vertices * sizeof(float[2]);
3946 size += model->surfmesh.num_vertices * sizeof(float[4]);
3947 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3948 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3949 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3950 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3951 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3952 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3953 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3954 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3955 if (model->surfmesh.num_vertices > 65536)
3956 model->surfmesh.data_element3s = NULL;
3958 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3959 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3960 model->surfmesh.data_element3i_indexbuffer = NULL;
3961 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3962 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3963 model->surfmesh.data_element3s_indexbuffer = NULL;
3964 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3965 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3966 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3967 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3968 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3969 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3970 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3971 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3972 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3973 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3974 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
3976 // convert all triangles to unique vertex data
3978 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3980 surface = model->data_surfaces + surfaceindex;
3981 surface->num_firstvertex = outvertexindex;
3982 surface->num_vertices = surface->num_triangles*3;
3983 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3984 for (i = 0;i < surface->num_triangles*3;i++)
3987 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3988 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3989 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3990 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3991 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3992 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3993 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3994 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3995 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3996 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3997 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3998 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3999 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4000 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4001 if (oldsurfmesh.data_texcoordlightmap2f)
4003 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4004 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4006 if (oldsurfmesh.data_lightmapcolor4f)
4008 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4009 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4010 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4011 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4014 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4015 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4019 if (model->surfmesh.data_element3s)
4020 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4021 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4023 // find and update all submodels to use this new surfmesh data
4024 for (i = 0;i < model->brush.numsubmodels;i++)
4025 model->brush.submodels[i]->surfmesh = model->surfmesh;
4028 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
4030 msurface_t *surface;
4036 lightmaptriangle_t *triangle;
4037 // generate lightmap triangle structs
4038 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4039 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4041 surface = model->data_surfaces + surfaceindex;
4042 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4043 for (i = 0;i < surface->num_triangles;i++)
4045 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4046 triangle->triangleindex = surface->num_firsttriangle+i;
4047 triangle->surfaceindex = surfaceindex;
4048 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4049 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4050 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4051 // calculate bounds of triangle
4052 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4053 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4054 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4055 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4056 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4057 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4058 // pick an axial projection based on the triangle normal
4059 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4061 if (fabs(normal[1]) > fabs(normal[axis]))
4063 if (fabs(normal[2]) > fabs(normal[axis]))
4065 triangle->axis = axis;
4070 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4072 if (mod_generatelightmaps_lightmaptriangles)
4073 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4074 mod_generatelightmaps_lightmaptriangles = NULL;
4077 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4079 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4081 msurface_t *surface;
4095 float trianglenormal[3];
4096 float samplecenter[3];
4097 float samplenormal[3];
4103 float lmscalepixels;
4106 float lm_basescalepixels;
4107 int lm_borderpixels;
4111 lightmaptriangle_t *triangle;
4112 unsigned char *lightmappixels;
4113 unsigned char *deluxemappixels;
4114 mod_alloclightmap_state_t lmstate;
4117 // generate lightmap projection information for all triangles
4118 if (model->texturepool == NULL)
4119 model->texturepool = R_AllocTexturePool();
4120 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4121 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4122 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4123 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4124 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4126 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4128 surface = model->data_surfaces + surfaceindex;
4129 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4130 lmscalepixels = lm_basescalepixels;
4131 for (retry = 0;retry < 30;retry++)
4133 // after a couple failed attempts, degrade quality to make it fit
4135 lmscalepixels *= 0.5f;
4136 for (i = 0;i < surface->num_triangles;i++)
4138 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4139 triangle->lightmapindex = lightmapnumber;
4140 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4141 // pick two planar axes for projection
4142 // lightmap coordinates here are in pixels
4143 // lightmap projections are snapped to pixel grid explicitly, such
4144 // that two neighboring triangles sharing an edge and projection
4145 // axis will have identical sample spacing along their shared edge
4147 for (j = 0;j < 3;j++)
4149 if (j == triangle->axis)
4151 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4152 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4153 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4154 triangle->lmbase[k] = lmmins/lmscalepixels;
4155 triangle->lmscale[k] = lmscalepixels;
4158 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4161 // if all fit in this texture, we're done with this surface
4162 if (i == surface->num_triangles)
4164 // if we haven't maxed out the lightmap size yet, we retry the
4165 // entire surface batch...
4166 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4168 lm_texturesize *= 2;
4171 Mod_AllocLightmap_Free(&lmstate);
4172 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4175 // if we have maxed out the lightmap size, and this triangle does
4176 // not fit in the same texture as the rest of the surface, we have
4177 // to retry the entire surface in a new texture (can only use one)
4178 // with multiple retries, the lightmap quality degrades until it
4179 // fits (or gives up)
4180 if (surfaceindex > 0)
4182 Mod_AllocLightmap_Reset(&lmstate);
4186 Mod_AllocLightmap_Free(&lmstate);
4188 // now put triangles together into lightmap textures, and do not allow
4189 // triangles of a surface to go into different textures (as that would
4190 // require rewriting the surface list)
4191 model->brushq3.deluxemapping_modelspace = true;
4192 model->brushq3.deluxemapping = true;
4193 model->brushq3.num_mergedlightmaps = lightmapnumber;
4194 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4195 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4196 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4197 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4198 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4200 surface = model->data_surfaces + surfaceindex;
4201 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4202 for (i = 0;i < surface->num_triangles;i++)
4204 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4205 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4206 VectorNormalize(trianglenormal);
4207 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4208 axis = triangle->axis;
4209 axis1 = axis == 0 ? 1 : 0;
4210 axis2 = axis == 2 ? 1 : 2;
4211 lmiscale[0] = 1.0f / triangle->lmscale[0];
4212 lmiscale[1] = 1.0f / triangle->lmscale[1];
4213 if (trianglenormal[axis] < 0)
4214 VectorNegate(trianglenormal, trianglenormal);
4215 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4216 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4217 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4218 for (j = 0;j < 3;j++)
4220 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4221 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4222 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4224 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4225 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4226 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4227 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]);
4237 forward[1] = 1.0f / triangle->lmscale[0];
4241 left[2] = 1.0f / triangle->lmscale[1];
4246 origin[1] = triangle->lmbase[0];
4247 origin[2] = triangle->lmbase[1];
4250 forward[0] = 1.0f / triangle->lmscale[0];
4255 left[2] = 1.0f / triangle->lmscale[1];
4259 origin[0] = triangle->lmbase[0];
4261 origin[2] = triangle->lmbase[1];
4264 forward[0] = 1.0f / triangle->lmscale[0];
4268 left[1] = 1.0f / triangle->lmscale[1];
4273 origin[0] = triangle->lmbase[0];
4274 origin[1] = triangle->lmbase[1];
4278 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4280 #define LM_DIST_EPSILON (1.0f / 32.0f)
4281 for (y = 0;y < triangle->lmsize[1];y++)
4283 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4284 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4286 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4287 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4288 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4289 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4290 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4296 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4298 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);
4299 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);
4303 Mem_Free(lightmappixels);
4304 if (deluxemappixels)
4305 Mem_Free(deluxemappixels);
4307 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4309 surface = model->data_surfaces + surfaceindex;
4310 if (!surface->num_triangles)
4312 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4313 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4314 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4315 surface->lightmapinfo = NULL;
4318 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4319 model->brushq1.lightdata = NULL;
4320 model->brushq1.lightmapupdateflags = NULL;
4321 model->brushq1.firstrender = false;
4322 model->brushq1.num_lightstyles = 0;
4323 model->brushq1.data_lightstyleinfo = NULL;
4324 for (i = 0;i < model->brush.numsubmodels;i++)
4326 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4327 model->brush.submodels[i]->brushq1.firstrender = false;
4328 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4329 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4333 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4336 for (i = 0;i < model->surfmesh.num_vertices;i++)
4337 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4340 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4347 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4349 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4350 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4352 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4353 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4355 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4356 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4362 extern cvar_t mod_q3bsp_nolightmaps;
4363 static void Mod_GenerateLightmaps(dp_model_t *model)
4365 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4366 dp_model_t *oldloadmodel = loadmodel;
4369 Mod_GenerateLightmaps_InitSampleOffsets(model);
4370 Mod_GenerateLightmaps_DestroyLightmaps(model);
4371 Mod_GenerateLightmaps_UnweldTriangles(model);
4372 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4373 Mod_GenerateLightmaps_CreateLights(model);
4374 if(!mod_q3bsp_nolightmaps.integer)
4375 Mod_GenerateLightmaps_CreateLightmaps(model);
4376 Mod_GenerateLightmaps_UpdateVertexColors(model);
4377 Mod_GenerateLightmaps_UpdateLightGrid(model);
4378 Mod_GenerateLightmaps_DestroyLights(model);
4379 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4381 loadmodel = oldloadmodel;
4384 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4386 if (Cmd_Argc(cmd) != 1)
4388 Con_Printf("usage: mod_generatelightmaps\n");
4393 Con_Printf("no worldmodel loaded\n");
4396 Mod_GenerateLightmaps(cl.worldmodel);
4399 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4401 memset(mod, 0, sizeof(*mod));
4402 strlcpy(mod->name, name, sizeof(mod->name));
4403 mod->mempool = Mem_AllocPool(name, 0, NULL);
4404 mod->texturepool = R_AllocTexturePool();
4405 mod->Draw = R_Mod_Draw;
4406 mod->DrawDepth = R_Mod_DrawDepth;
4407 mod->DrawDebug = R_Mod_DrawDebug;
4408 mod->DrawPrepass = R_Mod_DrawPrepass;
4409 mod->GetLightInfo = R_Mod_GetLightInfo;
4410 mod->DrawShadowMap = R_Mod_DrawShadowMap;
4411 mod->DrawLight = R_Mod_DrawLight;
4414 void Mod_Mesh_Destroy(dp_model_t *mod)
4416 Mod_UnloadModel(mod);
4419 // resets the mesh model to have no geometry to render, ready for a new frame -
4420 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4421 void Mod_Mesh_Reset(dp_model_t *mod)
4423 mod->num_surfaces = 0;
4424 mod->surfmesh.num_vertices = 0;
4425 mod->surfmesh.num_triangles = 0;
4426 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4427 mod->DrawSky = NULL; // will be set if a texture needs it
4428 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4431 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4435 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4436 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4437 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4439 if (mod->max_textures <= mod->num_textures)
4441 texture_t *oldtextures = mod->data_textures;
4442 mod->max_textures = max(mod->max_textures * 2, 1024);
4443 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4444 // update the pointers
4445 for (i = 0; i < mod->num_surfaces; i++)
4446 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4448 t = &mod->data_textures[mod->num_textures++];
4449 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4450 t->mesh_drawflag = drawflag;
4451 t->mesh_defaulttexflags = defaulttexflags;
4452 t->mesh_defaultmaterialflags = defaultmaterialflags;
4453 switch (defaultdrawflags & DRAWFLAG_MASK)
4455 case DRAWFLAG_ADDITIVE:
4456 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4457 t->currentmaterialflags = t->basematerialflags;
4459 case DRAWFLAG_MODULATE:
4460 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4461 t->currentmaterialflags = t->basematerialflags;
4462 t->customblendfunc[0] = GL_DST_COLOR;
4463 t->customblendfunc[1] = GL_ZERO;
4465 case DRAWFLAG_2XMODULATE:
4466 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4467 t->currentmaterialflags = t->basematerialflags;
4468 t->customblendfunc[0] = GL_DST_COLOR;
4469 t->customblendfunc[1] = GL_SRC_COLOR;
4471 case DRAWFLAG_SCREEN:
4472 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4473 t->currentmaterialflags = t->basematerialflags;
4474 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4475 t->customblendfunc[1] = GL_ONE;
4483 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qbool batchwithprevioussurface)
4486 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4487 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4488 return mod->data_surfaces + mod->num_surfaces - 1;
4489 // create new surface
4490 if (mod->max_surfaces == mod->num_surfaces)
4492 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4493 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4494 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4496 surf = mod->data_surfaces + mod->num_surfaces;
4497 mod->num_surfaces++;
4498 memset(surf, 0, sizeof(*surf));
4499 surf->texture = tex;
4500 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4501 surf->num_firstvertex = mod->surfmesh.num_vertices;
4502 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4503 mod->DrawSky = R_Mod_DrawSky;
4504 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4505 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
4509 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)
4511 int hashindex, h, vnum, mask;
4512 surfmesh_t *mesh = &mod->surfmesh;
4513 if (mesh->max_vertices == mesh->num_vertices)
4515 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4516 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4517 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4518 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4519 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4520 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4521 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4522 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4523 // rebuild the hash table
4524 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4525 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4526 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4527 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4528 mask = mod->surfmesh.num_vertexhashsize - 1;
4529 // no need to hash the vertices for the entire model, the latest surface will suffice.
4530 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4532 // this uses prime numbers intentionally for computing the hash
4533 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;
4534 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4535 ; // just iterate until we find the terminator
4536 mesh->data_vertexhash[h] = vnum;
4539 mask = mod->surfmesh.num_vertexhashsize - 1;
4540 // this uses prime numbers intentionally for computing the hash
4541 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4542 // when possible find an identical vertex within the same surface and return it
4543 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4545 if (vnum >= surf->num_firstvertex
4546 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4547 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4548 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4549 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4550 && 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)
4553 // add the new vertex
4554 vnum = mesh->num_vertices++;
4555 if (surf->num_vertices > 0)
4557 if (surf->mins[0] > x) surf->mins[0] = x;
4558 if (surf->mins[1] > y) surf->mins[1] = y;
4559 if (surf->mins[2] > z) surf->mins[2] = z;
4560 if (surf->maxs[0] < x) surf->maxs[0] = x;
4561 if (surf->maxs[1] < y) surf->maxs[1] = y;
4562 if (surf->maxs[2] < z) surf->maxs[2] = z;
4566 VectorSet(surf->mins, x, y, z);
4567 VectorSet(surf->maxs, x, y, z);
4569 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4570 mesh->data_vertexhash[h] = vnum;
4571 mesh->data_vertex3f[vnum * 3 + 0] = x;
4572 mesh->data_vertex3f[vnum * 3 + 1] = y;
4573 mesh->data_vertex3f[vnum * 3 + 2] = z;
4574 mesh->data_normal3f[vnum * 3 + 0] = nx;
4575 mesh->data_normal3f[vnum * 3 + 1] = ny;
4576 mesh->data_normal3f[vnum * 3 + 2] = nz;
4577 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4578 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4579 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4580 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4581 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4582 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4583 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4584 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4588 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4590 surfmesh_t *mesh = &mod->surfmesh;
4591 if (mesh->max_triangles == mesh->num_triangles)
4593 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4594 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4595 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4597 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4598 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4599 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4600 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4601 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4602 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4603 mesh->num_triangles++;
4604 surf->num_triangles++;
4607 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4611 msurface_t *surf, *surf2;
4613 // build the sorted surfaces list properly to reduce material setup
4614 // this is easy because we're just sorting on texture and don't care about the order of textures
4615 mod->nummodelsurfaces = 0;
4616 for (i = 0; i < mod->num_surfaces; i++)
4617 mod->data_surfaces[i].included = false;
4618 for (i = 0; i < mod->num_surfaces; i++)
4620 surf = mod->data_surfaces + i;
4623 tex = surf->texture;
4624 // j = i is intentional
4625 for (j = i; j < mod->num_surfaces; j++)
4627 surf2 = mod->data_surfaces + j;
4628 if (surf2->included)
4630 if (surf2->texture == tex)
4632 surf2->included = true;
4633 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4639 static void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4642 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4644 if (mod->surfmesh.num_vertices > 0)
4646 // calculate normalmins/normalmaxs
4647 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4648 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4649 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4651 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4652 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4653 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4654 // expand bounds to include this vertex
4655 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4656 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4657 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4658 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4659 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4660 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4662 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4663 // (fast but less accurate than doing it per vertex)
4664 x2a = mod->normalmins[0] * mod->normalmins[0];
4665 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4666 y2a = mod->normalmins[1] * mod->normalmins[1];
4667 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4668 z2a = mod->normalmins[2] * mod->normalmins[2];
4669 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4673 yawradius = sqrt(x2 + y2);
4674 rotatedradius = sqrt(x2 + y2 + z2);
4675 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4676 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4677 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4678 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4679 mod->radius = rotatedradius;
4680 mod->radius2 = x2 + y2 + z2;
4684 VectorClear(mod->normalmins);
4685 VectorClear(mod->normalmaxs);
4686 VectorClear(mod->yawmins);
4687 VectorClear(mod->yawmaxs);
4688 VectorClear(mod->rotatedmins);
4689 VectorClear(mod->rotatedmaxs);
4695 void Mod_Mesh_Validate(dp_model_t *mod)
4698 qbool warned = false;
4699 for (i = 0; i < mod->num_surfaces; i++)
4701 msurface_t *surf = mod->data_surfaces + i;
4702 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4703 int first = surf->num_firstvertex;
4704 int end = surf->num_firstvertex + surf->num_vertices;
4706 for (j = 0;j < surf->num_triangles * 3;j++)
4708 if (e[j] < first || e[j] >= end)
4711 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4719 static void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
4721 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;
4722 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;
4723 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;
4724 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;
4725 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;
4726 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;
4727 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;
4728 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;
4729 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;
4730 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;
4731 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;
4734 void Mod_Mesh_Finalize(dp_model_t *mod)
4736 if (gl_paranoid.integer)
4737 Mod_Mesh_Validate(mod);
4738 Mod_Mesh_ComputeBounds(mod);
4739 Mod_Mesh_MakeSortedSurfaces(mod);
4740 if(!r_refdef.draw2dstage)
4741 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);
4742 Mod_Mesh_UploadDynamicBuffers(mod);