2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // models.c -- model loading and caching
22 // models are the only shared resource between a client and server running
23 // on the same machine.
30 cvar_t r_mipskins = {CF_CLIENT | CF_ARCHIVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
31 cvar_t r_mipnormalmaps = {CF_CLIENT | CF_ARCHIVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
44 // Supported model formats
45 static modloader_t loader[] =
47 {"obj", NULL, 0, Mod_OBJ_Load},
48 {NULL, "IDPO", 4, Mod_IDP0_Load},
49 {NULL, "IDP2", 4, Mod_IDP2_Load},
50 {NULL, "IDP3", 4, Mod_IDP3_Load},
51 {NULL, "IDSP", 4, Mod_IDSP_Load},
52 {NULL, "IDS2", 4, Mod_IDS2_Load},
53 {NULL, "\035", 1, Mod_Q1BSP_Load},
54 {NULL, "\036", 1, Mod_HLBSP_Load},
55 {NULL, "BSP2", 4, Mod_BSP2_Load},
56 {NULL, "2PSB", 4, Mod_2PSB_Load},
57 {NULL, "IBSP", 4, Mod_IBSP_Load},
58 {NULL, "ZYMOTICMODEL", 13, Mod_ZYMOTICMODEL_Load},
59 {NULL, "DARKPLACESMODEL", 16, Mod_DARKPLACESMODEL_Load},
60 {NULL, "PSKMODEL", 9, Mod_PSKMODEL_Load},
61 {NULL, "INTERQUAKEMODEL", 16, Mod_INTERQUAKEMODEL_Load},
62 {"map", NULL, 0, Mod_MAP_Load},
66 static mempool_t *mod_mempool;
67 static memexpandablearray_t models;
69 static mempool_t* q3shaders_mem;
70 typedef struct q3shader_hash_entry_s
73 struct q3shader_hash_entry_s* chain;
74 } q3shader_hash_entry_t;
75 #define Q3SHADER_HASH_SIZE 1021
76 typedef struct q3shader_data_s
78 memexpandablearray_t hash_entries;
79 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
80 memexpandablearray_t char_ptrs;
82 static q3shader_data_t* q3shader_data;
84 static void mod_start(void)
87 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
90 SCR_PushLoadingScreen("Loading models", 1.0);
92 for (i = 0;i < nummodels;i++)
93 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
96 for (i = 0;i < nummodels;i++)
97 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
100 SCR_PushLoadingScreen(mod->name, 1.0 / count);
101 Mod_LoadModel(mod, true, false);
102 SCR_PopLoadingScreen(false);
104 SCR_PopLoadingScreen(false);
107 static void mod_shutdown(void)
110 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
113 for (i = 0;i < nummodels;i++)
114 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
115 Mod_UnloadModel(mod);
118 Mod_Skeletal_FreeBuffers();
121 static void mod_newmap(void)
124 int i, j, k, l, surfacenum, ssize, tsize;
125 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
128 for (i = 0;i < nummodels;i++)
130 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
132 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
134 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
135 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
136 if (mod->data_textures[j].shaderpasses[l])
137 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
138 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
140 if (mod->brush.solidskyskinframe)
141 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
142 if (mod->brush.alphaskyskinframe)
143 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
147 if (!cl_stainmaps_clearonload.integer)
150 for (i = 0;i < nummodels;i++)
152 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
154 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
156 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
158 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
159 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
160 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
161 mod->brushq1.lightmapupdateflags[surfacenum] = true;
173 static void Mod_Print_f(cmd_state_t *cmd);
174 static void Mod_Precache_f(cmd_state_t *cmd);
175 static void Mod_Decompile_f(cmd_state_t *cmd);
176 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
179 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
180 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(model_t), 16);
186 Cvar_RegisterVariable(&r_mipskins);
187 Cvar_RegisterVariable(&r_mipnormalmaps);
188 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
189 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
190 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
192 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
193 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
194 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
195 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
196 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
197 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
199 Cmd_AddCommand(CF_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
200 Cmd_AddCommand(CF_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
201 Cmd_AddCommand(CF_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
202 Cmd_AddCommand(CF_CLIENT, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
205 void Mod_RenderInit(void)
207 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
210 void Mod_UnloadModel (model_t *mod)
212 char name[MAX_QPATH];
214 model_t *parentmodel;
216 if (developer_loading.integer)
217 Con_Printf("unloading model %s\n", mod->name);
219 strlcpy(name, mod->name, sizeof(name));
220 parentmodel = mod->brush.parentmodel;
224 if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
225 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
226 mod->surfmesh.data_element3i_indexbuffer = NULL;
227 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
228 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
229 mod->surfmesh.data_element3s_indexbuffer = NULL;
230 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
231 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
232 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
233 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
234 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
235 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
236 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
237 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
238 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
239 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
240 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
242 // free textures/memory attached to the model
243 R_FreeTexturePool(&mod->texturepool);
244 Mem_FreePool(&mod->mempool);
245 // clear the struct to make it available
246 memset(mod, 0, sizeof(model_t));
247 // restore the fields we want to preserve
248 strlcpy(mod->name, name, sizeof(mod->name));
249 mod->brush.parentmodel = parentmodel;
254 static void R_Model_Null_Draw(entity_render_t *ent)
260 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass);
262 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
277 // REQUIRED: fetch start
278 COM_ParseToken_Simple(&bufptr, true, false, true);
280 break; // end of file
281 if (!strcmp(com_token, "\n"))
282 continue; // empty line
283 start = atoi(com_token);
285 // REQUIRED: fetch length
286 COM_ParseToken_Simple(&bufptr, true, false, true);
287 if (!bufptr || !strcmp(com_token, "\n"))
289 Con_Printf("framegroups file: missing number of frames\n");
292 len = atoi(com_token);
294 // OPTIONAL args start
295 COM_ParseToken_Simple(&bufptr, true, false, true);
297 // OPTIONAL: fetch fps
299 if (bufptr && strcmp(com_token, "\n"))
301 fps = atof(com_token);
302 COM_ParseToken_Simple(&bufptr, true, false, true);
305 // OPTIONAL: fetch loopflag
307 if (bufptr && strcmp(com_token, "\n"))
309 loop = (atoi(com_token) != 0);
310 COM_ParseToken_Simple(&bufptr, true, false, true);
313 // OPTIONAL: fetch name
315 if (bufptr && strcmp(com_token, "\n"))
317 strlcpy(name, com_token, sizeof(name));
318 COM_ParseToken_Simple(&bufptr, true, false, true);
321 // OPTIONAL: remaining unsupported tokens (eat them)
322 while (bufptr && strcmp(com_token, "\n"))
323 COM_ParseToken_Simple(&bufptr, true, false, true);
325 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
328 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
335 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass)
337 model_t *mod = (model_t *) pass;
338 animscene_t *anim = &mod->animscenes[i];
340 strlcpy(anim->name, name, sizeof(anim[i].name));
342 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
343 anim->firstframe = bound(0, start, mod->num_poses - 1);
344 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
345 anim->framerate = max(1, fps);
347 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
350 static void Mod_FrameGroupify(model_t *mod, const char *buf)
355 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
358 Con_Printf("no scene found in framegroups file, aborting\n");
361 mod->numframes = cnt;
364 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
365 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
368 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
371 static void Mod_FindPotentialDeforms(model_t *mod)
375 mod->wantnormals = false;
376 mod->wanttangents = false;
377 for (i = 0;i < mod->num_textures;i++)
379 texture = mod->data_textures + i;
380 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
381 mod->wantnormals = true;
382 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
383 mod->wantnormals = true;
384 for (j = 0;j < Q3MAXDEFORMS;j++)
386 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
388 mod->wanttangents = true;
389 mod->wantnormals = true;
392 if (texture->deforms[j].deform != Q3DEFORM_NONE)
393 mod->wantnormals = true;
405 model_t *Mod_LoadModel(model_t *mod, qbool crash, qbool checkdisk)
409 fs_offset_t filesize = 0;
414 if (mod->name[0] == '*') // submodel
417 if (!strcmp(mod->name, "null"))
422 if (mod->loaded || mod->mempool)
423 Mod_UnloadModel(mod);
425 if (developer_loading.integer)
426 Con_Printf("loading model %s\n", mod->name);
429 mod->crc = (unsigned int)-1;
432 VectorClear(mod->normalmins);
433 VectorClear(mod->normalmaxs);
434 VectorClear(mod->yawmins);
435 VectorClear(mod->yawmaxs);
436 VectorClear(mod->rotatedmins);
437 VectorClear(mod->rotatedmaxs);
439 mod->modeldatatypestring = "null";
440 mod->type = mod_null;
441 mod->Draw = R_Model_Null_Draw;
445 // no fatal errors occurred, so this model is ready to use.
454 // even if the model is loaded it still may need reloading...
456 // if it is not loaded or checkdisk is true we need to calculate the crc
457 if (!mod->loaded || checkdisk)
459 if (checkdisk && mod->loaded)
460 Con_DPrintf("checking model %s\n", mod->name);
461 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
464 crc = CRC_Block((unsigned char *)buf, filesize);
465 // we need to reload the model if the crc does not match
471 // if the model is already loaded and checks passed, just return
479 if (developer_loading.integer)
480 Con_Printf("loading model %s\n", mod->name);
482 SCR_PushLoadingScreen(mod->name, 1);
484 // LadyHavoc: unload the existing model in this slot (if there is one)
485 if (mod->loaded || mod->mempool)
486 Mod_UnloadModel(mod);
491 // errors can prevent the corresponding mod->loaded = true;
494 // default lightmap scale
495 mod->lightmapscale = 1;
497 // default model radius and bounding box (mainly for missing models)
499 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
500 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
501 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
502 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
503 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
504 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
508 // load q3 shaders for the first time, or after a level change
515 const char *ext = FS_FileExtension(mod->name);
516 char *bufend = (char *)buf + filesize;
517 // all models use memory, so allocate a memory pool
518 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
520 // call the apropriate loader
523 // Try matching magic bytes.
524 for (i = 0; loader[i].Load; i++)
526 // Headerless formats can just load based on extension. Otherwise match the magic string.
527 if((loader[i].extension && !strcasecmp(ext, loader[i].extension) && !loader[i].header) ||
528 (loader[i].header && !memcmp(buf, loader[i].header, loader[i].headersize)))
531 loader[i].Load(mod, buf, bufend);
534 Mod_FindPotentialDeforms(mod);
536 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
539 Mod_FrameGroupify(mod, (const char *)buf);
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 = (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 = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
581 Mod_UnloadModel(mod);
582 Mem_ExpandableArray_FreeRecord(&models, mod);
593 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 = (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 = (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 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 = (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 = (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 = (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(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 = mod->submodelsurfaces_start;k < mod->submodelsurfaces_end;k++)
1202 surface = mod->data_surfaces + 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 = mod->submodelsurfaces_start; k < mod->submodelsurfaces_end; k++)
1220 surface = mod->data_surfaces + 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(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(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 typedef struct Mod_MakeSortedSurfaces_qsortsurface_s
2888 q3deffect_t* effect;
2890 rtexture_t* lightmaptexture;
2892 Mod_MakeSortedSurfaces_qsortsurface_t;
2894 int Mod_MakeSortedSurfaces_qsortfunc(const void *a, const void *b)
2896 const Mod_MakeSortedSurfaces_qsortsurface_t* l = (Mod_MakeSortedSurfaces_qsortsurface_t*)a;
2897 const Mod_MakeSortedSurfaces_qsortsurface_t* r = (Mod_MakeSortedSurfaces_qsortsurface_t*)b;
2898 if (l->submodel < r->submodel)
2900 if (l->submodel > r->submodel)
2902 if (l->effect < r->effect)
2904 if (l->effect > r->effect)
2906 if (l->texture < r->texture)
2908 if (l->texture > r->texture)
2910 if (l->lightmaptexture < r->lightmaptexture)
2912 if (l->lightmaptexture > r->lightmaptexture)
2914 if (l->surfaceindex < r->surfaceindex)
2916 if (l->surfaceindex > r->surfaceindex)
2921 void Mod_MakeSortedSurfaces(model_t *mod)
2923 // make an optimal set of texture-sorted batches to draw...
2925 Mod_MakeSortedSurfaces_qsortsurface_t *info = (Mod_MakeSortedSurfaces_qsortsurface_t*)R_FrameData_Alloc(mod->num_surfaces * sizeof(*info));
2926 if (!mod->modelsurfaces_sorted)
2927 mod->modelsurfaces_sorted = (int *) Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*mod->modelsurfaces_sorted));
2928 // the goal is to sort by submodel (can't change which submodel a surface belongs to), and then by effects and textures
2929 for (j = 0; j < mod->num_surfaces; j++)
2931 info[j].submodel = 0;
2932 info[j].surfaceindex = j;
2933 info[j].effect = mod->data_surfaces[j].effect;
2934 info[j].texture = mod->data_surfaces[j].texture;
2935 info[j].lightmaptexture = mod->data_surfaces[j].lightmaptexture;
2937 for (k = 0; k < mod->brush.numsubmodels; k++)
2938 for (j = mod->brush.submodels[k]->submodelsurfaces_start; j < mod->brush.submodels[k]->submodelsurfaces_end; j++)
2939 info[j].submodel = k;
2940 qsort(info, mod->num_surfaces, sizeof(*info), Mod_MakeSortedSurfaces_qsortfunc);
2941 for (j = 0; j < mod->num_surfaces; j++)
2942 mod->modelsurfaces_sorted[j] = info[j].surfaceindex;
2945 void Mod_BuildVBOs(void)
2947 if(cls.state == ca_dedicated)
2950 if (!loadmodel->surfmesh.num_vertices)
2953 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2956 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2958 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2960 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2961 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2966 // upload short indices as a buffer
2967 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2968 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);
2970 // upload int indices as a buffer
2971 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2972 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);
2974 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2975 // we put several vertex data streams in the same buffer
2976 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2981 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2982 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2983 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2984 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2985 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2986 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2987 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2988 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2989 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2990 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2991 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2992 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2993 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2994 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2995 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2996 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2997 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2998 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2999 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3000 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3001 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3002 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3003 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3004 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3005 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3006 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3007 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3008 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3013 extern cvar_t mod_obj_orientation;
3014 static void Mod_Decompile_OBJ(model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3016 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3018 const char *texname;
3020 const float *v, *vn, *vt;
3022 size_t outbufferpos = 0;
3023 size_t outbuffermax = 0x100000;
3024 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3025 const msurface_t *surface;
3026 const int maxtextures = 256;
3027 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3030 // construct the mtllib file
3031 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3034 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3037 countvertices += surface->num_vertices;
3038 countfaces += surface->num_triangles;
3039 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3040 for (textureindex = 0;textureindex < counttextures;textureindex++)
3041 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3043 if (textureindex < counttextures)
3044 continue; // already wrote this material entry
3045 if (textureindex >= maxtextures)
3046 continue; // just a precaution
3047 textureindex = counttextures++;
3048 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3049 if (outbufferpos >= outbuffermax >> 1)
3052 oldbuffer = outbuffer;
3053 outbuffer = (char *) Z_Malloc(outbuffermax);
3054 memcpy(outbuffer, oldbuffer, outbufferpos);
3057 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");
3062 // write the mtllib file
3063 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3065 // construct the obj file
3067 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);
3071 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)
3073 if (outbufferpos >= outbuffermax >> 1)
3076 oldbuffer = outbuffer;
3077 outbuffer = (char *) Z_Malloc(outbuffermax);
3078 memcpy(outbuffer, oldbuffer, outbufferpos);
3081 if(mod_obj_orientation.integer)
3082 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]);
3084 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]);
3089 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3091 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3094 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3095 for (surfaceindex = submodel->submodelsurfaces_start;surfaceindex < submodel->submodelsurfaces_end;surfaceindex++)
3097 surface = model->data_surfaces + surfaceindex;
3098 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3101 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3103 if (outbufferpos >= outbuffermax >> 1)
3106 oldbuffer = outbuffer;
3107 outbuffer = (char *) Z_Malloc(outbuffermax);
3108 memcpy(outbuffer, oldbuffer, outbufferpos);
3114 if(mod_obj_orientation.integer)
3115 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);
3117 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);
3124 // write the obj file
3125 FS_WriteFile(filename, outbuffer, outbufferpos);
3129 Z_Free(texturenames);
3132 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3135 static void Mod_Decompile_SMD(model_t *model, const char *filename, int firstpose, int numposes, qbool writetriangles)
3137 int countnodes = 0, counttriangles = 0, countframes = 0;
3145 size_t outbufferpos = 0;
3146 size_t outbuffermax = 0x100000;
3147 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3148 const msurface_t *surface;
3149 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3152 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3154 if (outbufferpos >= outbuffermax >> 1)
3157 oldbuffer = outbuffer;
3158 outbuffer = (char *) Z_Malloc(outbuffermax);
3159 memcpy(outbuffer, oldbuffer, outbufferpos);
3163 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3167 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3170 for (poseindex = 0;poseindex < numposes;poseindex++)
3173 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3176 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3180 matrix4x4_t posematrix;
3181 if (outbufferpos >= outbuffermax >> 1)
3184 oldbuffer = outbuffer;
3185 outbuffer = (char *) Z_Malloc(outbuffermax);
3186 memcpy(outbuffer, oldbuffer, outbufferpos);
3190 // strangely the smd angles are for a transposed matrix, so we
3191 // have to generate a transposed matrix, then convert that...
3192 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3193 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3194 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3195 if (angles[0] >= 180) angles[0] -= 360;
3196 if (angles[1] >= 180) angles[1] -= 360;
3197 if (angles[2] >= 180) angles[2] -= 360;
3201 float a = DEG2RAD(angles[ROLL]);
3202 float b = DEG2RAD(angles[PITCH]);
3203 float c = DEG2RAD(angles[YAW]);
3204 float cy, sy, cp, sp, cr, sr;
3206 // smd matrix construction, for comparing
3217 test[1][0] = sr*sp*cy+cr*-sy;
3218 test[1][1] = sr*sp*sy+cr*cy;
3220 test[2][0] = (cr*sp*cy+-sr*-sy);
3221 test[2][1] = (cr*sp*sy+-sr*cy);
3223 test[3][0] = pose[9];
3224 test[3][1] = pose[10];
3225 test[3][2] = pose[11];
3228 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]));
3233 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3238 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3241 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3243 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3246 if (outbufferpos >= outbuffermax >> 1)
3249 oldbuffer = outbuffer;
3250 outbuffer = (char *) Z_Malloc(outbuffermax);
3251 memcpy(outbuffer, oldbuffer, outbufferpos);
3254 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3257 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3259 const int index = e[2-cornerindex];
3260 const float *v = model->surfmesh.data_vertex3f + index * 3;
3261 const float *vn = model->surfmesh.data_normal3f + index * 3;
3262 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3263 const int b = model->surfmesh.blends[index];
3264 if (b < model->num_bones)
3265 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]);
3268 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3269 const unsigned char *wi = w->index;
3270 const unsigned char *wf = w->influence;
3271 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);
3272 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);
3273 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);
3274 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]);
3281 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3286 FS_WriteFile(filename, outbuffer, outbufferpos);
3289 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3296 decompiles a model to editable files
3299 static void Mod_Decompile_f(cmd_state_t *cmd)
3301 int i, j, k, l, first, count;
3303 char inname[MAX_QPATH];
3304 char outname[MAX_QPATH];
3305 char mtlname[MAX_QPATH];
3306 char basename[MAX_QPATH];
3307 char animname[MAX_QPATH];
3308 char animname2[MAX_QPATH];
3309 char zymtextbuffer[16384];
3310 char dpmtextbuffer[16384];
3311 char framegroupstextbuffer[16384];
3312 int zymtextsize = 0;
3313 int dpmtextsize = 0;
3314 int framegroupstextsize = 0;
3317 if (Cmd_Argc(cmd) != 2)
3319 Con_Print("usage: modeldecompile <filename>\n");
3323 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3324 FS_StripExtension(inname, basename, sizeof(basename));
3326 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3329 Con_Print("No such model\n");
3332 if (mod->brush.submodel)
3334 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3335 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3336 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3339 if (!mod->surfmesh.num_triangles)
3341 Con_Print("Empty model (or sprite)\n");
3345 // export OBJ if possible (not on sprites)
3346 if (mod->surfmesh.num_triangles)
3348 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3349 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3350 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3353 // export SMD if possible (only for skeletal models)
3354 if (mod->surfmesh.num_triangles && mod->num_bones)
3356 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3357 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3358 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3359 if (l > 0) zymtextsize += l;
3360 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3361 if (l > 0) dpmtextsize += l;
3362 for (i = 0;i < mod->numframes;i = j)
3364 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3365 first = mod->animscenes[i].firstframe;
3366 if (mod->animscenes[i].framecount > 1)
3369 count = mod->animscenes[i].framecount;
3375 // check for additional frames with same name
3376 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3377 if(animname[l] < '0' || animname[l] > '9')
3379 if(k > 0 && animname[k-1] == '_')
3382 count = mod->num_poses - first;
3383 for (j = i + 1;j < mod->numframes;j++)
3385 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3386 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3387 if(animname2[l] < '0' || animname2[l] > '9')
3389 if(k > 0 && animname[k-1] == '_')
3392 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3394 count = mod->animscenes[j].firstframe - first;
3398 // if it's only one frame, use the original frame name
3400 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3403 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3404 Mod_Decompile_SMD(mod, outname, first, count, false);
3405 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3407 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3408 if (l > 0) zymtextsize += l;
3410 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3412 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3413 if (l > 0) dpmtextsize += l;
3415 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3417 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3418 if (l > 0) framegroupstextsize += l;
3422 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3424 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3425 if (framegroupstextsize)
3426 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3430 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3433 memset(state, 0, sizeof(*state));
3434 state->width = width;
3435 state->height = height;
3436 state->currentY = 0;
3437 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3438 for (y = 0;y < state->height;y++)
3440 state->rows[y].currentX = 0;
3441 state->rows[y].rowY = -1;
3445 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3448 state->currentY = 0;
3449 for (y = 0;y < state->height;y++)
3451 state->rows[y].currentX = 0;
3452 state->rows[y].rowY = -1;
3456 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3459 Mem_Free(state->rows);
3460 memset(state, 0, sizeof(*state));
3463 qbool Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3465 mod_alloclightmap_row_t *row;
3468 row = state->rows + blockheight;
3469 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3471 if (state->currentY + blockheight <= state->height)
3473 // use the current allocation position
3474 row->rowY = state->currentY;
3476 state->currentY += blockheight;
3480 // find another position
3481 for (y = blockheight;y < state->height;y++)
3483 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3485 row = state->rows + y;
3489 if (y == state->height)
3494 *outx = row->currentX;
3495 row->currentX += blockwidth;
3500 typedef struct lightmapsample_s
3504 float *vertex_color;
3505 unsigned char *lm_bgr;
3506 unsigned char *lm_dir;
3510 typedef struct lightmapvertex_s
3515 float texcoordbase[2];
3516 float texcoordlightmap[2];
3517 float lightcolor[4];
3521 typedef struct lightmaptriangle_s
3529 // 2D modelspace coordinates of min corner
3530 // snapped to lightmap grid but not in grid coordinates
3532 // 2D modelspace to lightmap coordinate scale
3540 typedef struct lightmaplight_s
3551 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3553 #define MAX_LIGHTMAPSAMPLES 64
3554 static int mod_generatelightmaps_numoffsets[3];
3555 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3557 static int mod_generatelightmaps_numlights;
3558 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3560 extern cvar_t r_shadow_lightattenuationdividebias;
3561 extern cvar_t r_shadow_lightattenuationlinearscale;
3563 static void Mod_GenerateLightmaps_LightPoint(model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3568 float relativepoint[3];
3575 float lightorigin[3];
3579 float lightcolor[3];
3581 for (i = 0;i < 5*3;i++)
3583 for (index = 0;;index++)
3585 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3590 lightradius2 = lightradius * lightradius;
3591 VectorSubtract(lightorigin, pos, relativepoint);
3592 dist2 = VectorLength2(relativepoint);
3593 if (dist2 >= lightradius2)
3595 lightiradius = 1.0f / lightradius;
3596 dist = sqrt(dist2) * lightiradius;
3597 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3598 if (intensity <= 0.0f)
3600 if (model && model->TraceLine)
3602 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3603 if (trace.fraction < 1)
3606 // scale down intensity to add to both ambient and diffuse
3607 //intensity *= 0.5f;
3608 VectorNormalize(relativepoint);
3609 VectorScale(lightcolor, intensity, color);
3610 VectorMA(sample , 0.5f , color, sample );
3611 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3612 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3613 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3614 // calculate a weighted average light direction as well
3615 intensity *= VectorLength(color);
3616 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3618 // calculate the direction we'll use to reduce the sample to a directional light source
3619 VectorCopy(sample + 12, dir);
3620 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3621 VectorNormalize(dir);
3622 // extract the diffuse color along the chosen direction and scale it
3623 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3624 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3625 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3626 // subtract some of diffuse from ambient
3627 VectorMA(sample, -0.333f, diffuse, ambient);
3628 // store the normalized lightdir
3629 VectorCopy(dir, lightdir);
3632 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3636 const msurface_t *surface;
3637 const float *vertex3f = model->surfmesh.data_vertex3f;
3638 const int *element3i = model->surfmesh.data_element3i;
3641 for (surfaceindex = model->submodelsurfaces_start;surfaceindex < model->submodelsurfaces_end;surfaceindex++)
3643 surface = model->data_surfaces + surfaceindex;
3644 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3646 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3648 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3650 VectorCopy(vertex3f + 3*e[0], v2[0]);
3651 VectorCopy(vertex3f + 3*e[1], v2[1]);
3652 VectorCopy(vertex3f + 3*e[2], v2[2]);
3653 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3658 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model_t *model, lightmaplight_t *lightinfo)
3660 int maxnodes = 1<<14;
3661 svbsp_node_t *nodes;
3666 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3667 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3668 VectorCopy(lightinfo->origin, origin);
3669 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3672 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3673 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3674 if (svbsp.ranoutofnodes)
3677 if (maxnodes > 1<<22)
3683 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3688 if (svbsp.numnodes > 0)
3690 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3691 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3692 lightinfo->svbsp = svbsp;
3697 static void Mod_GenerateLightmaps_CreateLights(model_t *model)
3701 lightmaplight_t *lightinfo;
3705 mod_generatelightmaps_numlights = 0;
3706 for (index = 0;;index++)
3708 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3712 mod_generatelightmaps_numlights++;
3714 if (mod_generatelightmaps_numlights > 0)
3716 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3717 lightinfo = mod_generatelightmaps_lightinfo;
3718 for (index = 0;;index++)
3720 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3727 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3729 lightinfo->iradius = 1.0f / lightinfo->radius;
3730 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3731 // TODO: compute svbsp
3732 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3736 static void Mod_GenerateLightmaps_DestroyLights(model_t *model)
3739 if (mod_generatelightmaps_lightinfo)
3741 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3742 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3743 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3744 Mem_Free(mod_generatelightmaps_lightinfo);
3746 mod_generatelightmaps_lightinfo = NULL;
3747 mod_generatelightmaps_numlights = 0;
3750 static qbool Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3752 const svbsp_node_t *node;
3753 const svbsp_node_t *nodes = svbsp->nodes;
3758 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3760 return num == -1; // true if empty, false if solid (shadowed)
3763 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3766 float relativepoint[3];
3775 const lightmaplight_t *lightinfo;
3777 for (i = 0;i < 5*3;i++)
3779 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3781 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3782 VectorSubtract(lightinfo->origin, pos, relativepoint);
3783 // don't accept light from behind a surface, it causes bad shading
3784 if (normal && DotProduct(relativepoint, normal) <= 0)
3786 dist2 = VectorLength2(relativepoint);
3787 if (dist2 >= lightinfo->radius2)
3789 dist = sqrt(dist2) * lightinfo->iradius;
3790 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3793 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3797 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3799 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3801 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3804 // for light grid we'd better check visibility of the offset point
3805 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3806 if (trace.fraction < 1)
3807 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3810 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3815 // scale intensity according to how many rays succeeded
3816 // we know one test is valid, half of the rest will fail...
3817 //if (normal && tests > 1)
3818 // intensity *= (tests - 1.0f) / tests;
3819 intensity *= (float)hits / tests;
3821 // scale down intensity to add to both ambient and diffuse
3822 //intensity *= 0.5f;
3823 VectorNormalize(relativepoint);
3824 VectorScale(lightinfo->color, intensity, color);
3825 VectorMA(sample , 0.5f , color, sample );
3826 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3827 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3828 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3829 // calculate a weighted average light direction as well
3830 intensity *= VectorLength(color);
3831 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3835 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3841 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3842 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3843 VectorCopy(sample + 12, dir);
3844 VectorNormalize(dir);
3845 //VectorAdd(dir, normal, dir);
3846 //VectorNormalize(dir);
3847 f = DotProduct(dir, normal);
3848 f = max(0, f) * 255.0f;
3849 VectorScale(sample, f, color);
3850 //VectorCopy(normal, dir);
3851 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3852 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3853 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3854 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3856 lm_dir[0] = (unsigned char)dir[2];
3857 lm_dir[1] = (unsigned char)dir[1];
3858 lm_dir[2] = (unsigned char)dir[0];
3862 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3865 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3866 VectorCopy(sample, vertex_color);
3869 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3875 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3876 // calculate the direction we'll use to reduce the sample to a directional light source
3877 VectorCopy(sample + 12, dir);
3878 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3879 VectorNormalize(dir);
3880 // extract the diffuse color along the chosen direction and scale it
3881 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3882 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3883 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3884 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3885 VectorScale(sample, 127.5f, ambient);
3886 VectorMA(ambient, -0.333f, diffuse, ambient);
3887 // encode to the grid format
3888 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3889 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3890 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3891 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3892 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3893 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3894 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3895 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3896 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));}
3899 static void Mod_GenerateLightmaps_InitSampleOffsets(model_t *model)
3904 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3905 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3906 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3907 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3908 radius[0] = mod_generatelightmaps_lightmapradius.value;
3909 radius[1] = mod_generatelightmaps_vertexradius.value;
3910 radius[2] = mod_generatelightmaps_gridradius.value;
3911 for (i = 0;i < 3;i++)
3913 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3916 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3921 static void Mod_GenerateLightmaps_DestroyLightmaps(model_t *model)
3923 msurface_t *surface;
3926 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3928 surface = model->data_surfaces + surfaceindex;
3929 surface->lightmaptexture = NULL;
3930 surface->deluxemaptexture = NULL;
3932 if (model->brushq3.data_lightmaps)
3934 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3935 if (model->brushq3.data_lightmaps[i])
3936 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3937 Mem_Free(model->brushq3.data_lightmaps);
3938 model->brushq3.data_lightmaps = NULL;
3940 if (model->brushq3.data_deluxemaps)
3942 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3943 if (model->brushq3.data_deluxemaps[i])
3944 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3945 Mem_Free(model->brushq3.data_deluxemaps);
3946 model->brushq3.data_deluxemaps = NULL;
3950 static void Mod_GenerateLightmaps_UnweldTriangles(model_t *model)
3952 msurface_t *surface;
3958 surfmesh_t oldsurfmesh;
3960 unsigned char *data;
3961 oldsurfmesh = model->surfmesh;
3962 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3963 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3965 size += model->surfmesh.num_vertices * sizeof(float[3]);
3966 size += model->surfmesh.num_vertices * sizeof(float[3]);
3967 size += model->surfmesh.num_vertices * sizeof(float[3]);
3968 size += model->surfmesh.num_vertices * sizeof(float[3]);
3969 size += model->surfmesh.num_vertices * sizeof(float[2]);
3970 size += model->surfmesh.num_vertices * sizeof(float[2]);
3971 size += model->surfmesh.num_vertices * sizeof(float[4]);
3972 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3973 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3974 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3975 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3976 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3977 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3978 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3979 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3980 if (model->surfmesh.num_vertices > 65536)
3981 model->surfmesh.data_element3s = NULL;
3983 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3984 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3985 model->surfmesh.data_element3i_indexbuffer = NULL;
3986 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3987 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3988 model->surfmesh.data_element3s_indexbuffer = NULL;
3989 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3990 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3991 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3992 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3993 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3994 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3995 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3996 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3997 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3998 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3999 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
4001 // convert all triangles to unique vertex data
4003 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4005 surface = model->data_surfaces + surfaceindex;
4006 surface->num_firstvertex = outvertexindex;
4007 surface->num_vertices = surface->num_triangles*3;
4008 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4009 for (i = 0;i < surface->num_triangles*3;i++)
4012 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4013 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4014 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4015 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4016 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4017 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4018 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4019 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4020 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4021 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4022 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4023 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4024 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4025 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4026 if (oldsurfmesh.data_texcoordlightmap2f)
4028 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4029 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4031 if (oldsurfmesh.data_lightmapcolor4f)
4033 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4034 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4035 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4036 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4039 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4040 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4044 if (model->surfmesh.data_element3s)
4045 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4046 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4048 // find and update all submodels to use this new surfmesh data
4049 for (i = 0;i < model->brush.numsubmodels;i++)
4050 model->brush.submodels[i]->surfmesh = model->surfmesh;
4053 static void Mod_GenerateLightmaps_CreateTriangleInformation(model_t *model)
4055 msurface_t *surface;
4061 lightmaptriangle_t *triangle;
4062 // generate lightmap triangle structs
4063 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4064 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4066 surface = model->data_surfaces + surfaceindex;
4067 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4068 for (i = 0;i < surface->num_triangles;i++)
4070 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4071 triangle->triangleindex = surface->num_firsttriangle+i;
4072 triangle->surfaceindex = surfaceindex;
4073 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4074 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4075 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4076 // calculate bounds of triangle
4077 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4078 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4079 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4080 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4081 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4082 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4083 // pick an axial projection based on the triangle normal
4084 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4086 if (fabs(normal[1]) > fabs(normal[axis]))
4088 if (fabs(normal[2]) > fabs(normal[axis]))
4090 triangle->axis = axis;
4095 static void Mod_GenerateLightmaps_DestroyTriangleInformation(model_t *model)
4097 if (mod_generatelightmaps_lightmaptriangles)
4098 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4099 mod_generatelightmaps_lightmaptriangles = NULL;
4102 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4104 static void Mod_GenerateLightmaps_CreateLightmaps(model_t *model)
4106 msurface_t *surface;
4120 float trianglenormal[3];
4121 float samplecenter[3];
4122 float samplenormal[3];
4128 float lmscalepixels;
4131 float lm_basescalepixels;
4132 int lm_borderpixels;
4136 lightmaptriangle_t *triangle;
4137 unsigned char *lightmappixels;
4138 unsigned char *deluxemappixels;
4139 mod_alloclightmap_state_t lmstate;
4142 // generate lightmap projection information for all triangles
4143 if (model->texturepool == NULL)
4144 model->texturepool = R_AllocTexturePool();
4145 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4146 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4147 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4148 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4149 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4151 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4153 surface = model->data_surfaces + surfaceindex;
4154 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4155 lmscalepixels = lm_basescalepixels;
4156 for (retry = 0;retry < 30;retry++)
4158 // after a couple failed attempts, degrade quality to make it fit
4160 lmscalepixels *= 0.5f;
4161 for (i = 0;i < surface->num_triangles;i++)
4163 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4164 triangle->lightmapindex = lightmapnumber;
4165 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4166 // pick two planar axes for projection
4167 // lightmap coordinates here are in pixels
4168 // lightmap projections are snapped to pixel grid explicitly, such
4169 // that two neighboring triangles sharing an edge and projection
4170 // axis will have identical sample spacing along their shared edge
4172 for (j = 0;j < 3;j++)
4174 if (j == triangle->axis)
4176 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4177 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4178 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4179 triangle->lmbase[k] = lmmins/lmscalepixels;
4180 triangle->lmscale[k] = lmscalepixels;
4183 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4186 // if all fit in this texture, we're done with this surface
4187 if (i == surface->num_triangles)
4189 // if we haven't maxed out the lightmap size yet, we retry the
4190 // entire surface batch...
4191 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4193 lm_texturesize *= 2;
4196 Mod_AllocLightmap_Free(&lmstate);
4197 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4200 // if we have maxed out the lightmap size, and this triangle does
4201 // not fit in the same texture as the rest of the surface, we have
4202 // to retry the entire surface in a new texture (can only use one)
4203 // with multiple retries, the lightmap quality degrades until it
4204 // fits (or gives up)
4205 if (surfaceindex > 0)
4207 Mod_AllocLightmap_Reset(&lmstate);
4211 Mod_AllocLightmap_Free(&lmstate);
4213 // now put triangles together into lightmap textures, and do not allow
4214 // triangles of a surface to go into different textures (as that would
4215 // require rewriting the surface list)
4216 model->brushq3.deluxemapping_modelspace = true;
4217 model->brushq3.deluxemapping = true;
4218 model->brushq3.num_mergedlightmaps = lightmapnumber;
4219 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4220 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4221 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4222 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4223 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4225 surface = model->data_surfaces + surfaceindex;
4226 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4227 for (i = 0;i < surface->num_triangles;i++)
4229 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4230 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4231 VectorNormalize(trianglenormal);
4232 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4233 axis = triangle->axis;
4234 axis1 = axis == 0 ? 1 : 0;
4235 axis2 = axis == 2 ? 1 : 2;
4236 lmiscale[0] = 1.0f / triangle->lmscale[0];
4237 lmiscale[1] = 1.0f / triangle->lmscale[1];
4238 if (trianglenormal[axis] < 0)
4239 VectorNegate(trianglenormal, trianglenormal);
4240 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4241 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4242 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4243 for (j = 0;j < 3;j++)
4245 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4246 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4247 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4249 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4250 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4251 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4252 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]);
4262 forward[1] = 1.0f / triangle->lmscale[0];
4266 left[2] = 1.0f / triangle->lmscale[1];
4271 origin[1] = triangle->lmbase[0];
4272 origin[2] = triangle->lmbase[1];
4275 forward[0] = 1.0f / triangle->lmscale[0];
4280 left[2] = 1.0f / triangle->lmscale[1];
4284 origin[0] = triangle->lmbase[0];
4286 origin[2] = triangle->lmbase[1];
4289 forward[0] = 1.0f / triangle->lmscale[0];
4293 left[1] = 1.0f / triangle->lmscale[1];
4298 origin[0] = triangle->lmbase[0];
4299 origin[1] = triangle->lmbase[1];
4303 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4305 #define LM_DIST_EPSILON (1.0f / 32.0f)
4306 for (y = 0;y < triangle->lmsize[1];y++)
4308 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4309 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4311 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4312 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4313 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4314 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4315 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4321 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4323 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);
4324 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);
4328 Mem_Free(lightmappixels);
4329 if (deluxemappixels)
4330 Mem_Free(deluxemappixels);
4332 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4334 surface = model->data_surfaces + surfaceindex;
4335 if (!surface->num_triangles)
4337 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4338 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4339 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4340 surface->lightmapinfo = NULL;
4343 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4344 model->brushq1.lightdata = NULL;
4345 model->brushq1.lightmapupdateflags = NULL;
4346 model->brushq1.firstrender = false;
4347 model->brushq1.num_lightstyles = 0;
4348 model->brushq1.data_lightstyleinfo = NULL;
4349 for (i = 0;i < model->brush.numsubmodels;i++)
4351 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4352 model->brush.submodels[i]->brushq1.firstrender = false;
4353 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4354 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4358 static void Mod_GenerateLightmaps_UpdateVertexColors(model_t *model)
4361 for (i = 0;i < model->surfmesh.num_vertices;i++)
4362 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4365 static void Mod_GenerateLightmaps_UpdateLightGrid(model_t *model)
4372 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4374 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4375 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4377 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4378 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4380 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4381 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4387 extern cvar_t mod_q3bsp_nolightmaps;
4388 static void Mod_GenerateLightmaps(model_t *model)
4390 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4391 model_t *oldloadmodel = loadmodel;
4394 Mod_GenerateLightmaps_InitSampleOffsets(model);
4395 Mod_GenerateLightmaps_DestroyLightmaps(model);
4396 Mod_GenerateLightmaps_UnweldTriangles(model);
4397 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4398 Mod_GenerateLightmaps_CreateLights(model);
4399 if(!mod_q3bsp_nolightmaps.integer)
4400 Mod_GenerateLightmaps_CreateLightmaps(model);
4401 Mod_GenerateLightmaps_UpdateVertexColors(model);
4402 Mod_GenerateLightmaps_UpdateLightGrid(model);
4403 Mod_GenerateLightmaps_DestroyLights(model);
4404 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4406 loadmodel = oldloadmodel;
4409 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4411 if (Cmd_Argc(cmd) != 1)
4413 Con_Printf("usage: mod_generatelightmaps\n");
4418 Con_Printf("no worldmodel loaded\n");
4421 Mod_GenerateLightmaps(cl.worldmodel);
4424 void Mod_Mesh_Create(model_t *mod, const char *name)
4426 memset(mod, 0, sizeof(*mod));
4427 strlcpy(mod->name, name, sizeof(mod->name));
4428 mod->mempool = Mem_AllocPool(name, 0, NULL);
4429 mod->texturepool = R_AllocTexturePool();
4430 mod->Draw = R_Mod_Draw;
4431 mod->DrawDepth = R_Mod_DrawDepth;
4432 mod->DrawDebug = R_Mod_DrawDebug;
4433 mod->DrawPrepass = R_Mod_DrawPrepass;
4434 mod->GetLightInfo = R_Mod_GetLightInfo;
4435 mod->DrawShadowMap = R_Mod_DrawShadowMap;
4436 mod->DrawLight = R_Mod_DrawLight;
4439 void Mod_Mesh_Destroy(model_t *mod)
4441 Mod_UnloadModel(mod);
4444 // resets the mesh model to have no geometry to render, ready for a new frame -
4445 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4446 void Mod_Mesh_Reset(model_t *mod)
4448 mod->num_surfaces = 0;
4449 mod->surfmesh.num_vertices = 0;
4450 mod->surfmesh.num_triangles = 0;
4451 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4452 mod->DrawSky = NULL; // will be set if a texture needs it
4453 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4456 texture_t *Mod_Mesh_GetTexture(model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4460 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4461 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4462 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4464 if (mod->max_textures <= mod->num_textures)
4466 texture_t *oldtextures = mod->data_textures;
4467 mod->max_textures = max(mod->max_textures * 2, 1024);
4468 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4469 // update the pointers
4470 for (i = 0; i < mod->num_surfaces; i++)
4471 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4473 t = &mod->data_textures[mod->num_textures++];
4474 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4475 t->mesh_drawflag = drawflag;
4476 t->mesh_defaulttexflags = defaulttexflags;
4477 t->mesh_defaultmaterialflags = defaultmaterialflags;
4478 switch (defaultdrawflags & DRAWFLAG_MASK)
4480 case DRAWFLAG_ADDITIVE:
4481 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4482 t->currentmaterialflags = t->basematerialflags;
4484 case DRAWFLAG_MODULATE:
4485 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4486 t->currentmaterialflags = t->basematerialflags;
4487 t->customblendfunc[0] = GL_DST_COLOR;
4488 t->customblendfunc[1] = GL_ZERO;
4490 case DRAWFLAG_2XMODULATE:
4491 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4492 t->currentmaterialflags = t->basematerialflags;
4493 t->customblendfunc[0] = GL_DST_COLOR;
4494 t->customblendfunc[1] = GL_SRC_COLOR;
4496 case DRAWFLAG_SCREEN:
4497 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4498 t->currentmaterialflags = t->basematerialflags;
4499 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4500 t->customblendfunc[1] = GL_ONE;
4508 msurface_t *Mod_Mesh_AddSurface(model_t *mod, texture_t *tex, qbool batchwithprevioussurface)
4511 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4512 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4513 return mod->data_surfaces + mod->num_surfaces - 1;
4514 // create new surface
4515 if (mod->max_surfaces == mod->num_surfaces)
4517 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4518 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4519 mod->modelsurfaces_sorted = (int *)Mem_Realloc(mod->mempool, mod->modelsurfaces_sorted, mod->max_surfaces * sizeof(*mod->modelsurfaces_sorted));
4521 surf = mod->data_surfaces + mod->num_surfaces;
4522 mod->num_surfaces++;
4523 memset(surf, 0, sizeof(*surf));
4524 surf->texture = tex;
4525 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4526 surf->num_firstvertex = mod->surfmesh.num_vertices;
4527 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4528 mod->DrawSky = R_Mod_DrawSky;
4529 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4530 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
4534 static void Mod_Mesh_RebuildHashTable(model_t *mod, msurface_t *surf)
4536 int hashindex, h, vnum, mask;
4537 surfmesh_t *mesh = &mod->surfmesh;
4539 // rebuild the hash table
4540 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4541 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4542 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4543 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4544 mask = mod->surfmesh.num_vertexhashsize - 1;
4545 // no need to hash the vertices for the entire model, the latest surface will suffice.
4546 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4548 // this uses prime numbers intentionally for computing the hash
4549 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;
4550 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4551 ; // just iterate until we find the terminator
4552 mesh->data_vertexhash[h] = vnum;
4556 void Mod_Mesh_CheckResize_Vertex(model_t *mod, msurface_t *surf)
4558 surfmesh_t *mesh = &mod->surfmesh;
4559 if (mesh->max_vertices == mesh->num_vertices)
4561 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4562 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4563 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4564 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4565 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4566 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4567 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4568 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4569 Mod_Mesh_RebuildHashTable(mod, surf);
4573 int Mod_Mesh_AddVertex(model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
4576 surfmesh_t *mesh = &mod->surfmesh;
4578 // add the new vertex
4579 vnum = mesh->num_vertices++;
4580 if (surf->num_vertices > 0)
4582 if (surf->mins[0] > x) surf->mins[0] = x;
4583 if (surf->mins[1] > y) surf->mins[1] = y;
4584 if (surf->mins[2] > z) surf->mins[2] = z;
4585 if (surf->maxs[0] < x) surf->maxs[0] = x;
4586 if (surf->maxs[1] < y) surf->maxs[1] = y;
4587 if (surf->maxs[2] < z) surf->maxs[2] = z;
4591 VectorSet(surf->mins, x, y, z);
4592 VectorSet(surf->maxs, x, y, z);
4594 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4595 mesh->data_vertex3f[vnum * 3 + 0] = x;
4596 mesh->data_vertex3f[vnum * 3 + 1] = y;
4597 mesh->data_vertex3f[vnum * 3 + 2] = z;
4598 mesh->data_normal3f[vnum * 3 + 0] = nx;
4599 mesh->data_normal3f[vnum * 3 + 1] = ny;
4600 mesh->data_normal3f[vnum * 3 + 2] = nz;
4601 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4602 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4603 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4604 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4605 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4606 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4607 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4608 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4612 int Mod_Mesh_IndexForVertex(model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
4614 int hashindex, h, vnum, mask;
4615 surfmesh_t *mesh = &mod->surfmesh;
4617 Mod_Mesh_CheckResize_Vertex(mod, surf);
4619 mask = mod->surfmesh.num_vertexhashsize - 1;
4620 // this uses prime numbers intentionally for computing the hash
4621 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4622 // when possible find an identical vertex within the same surface and return it
4623 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4625 if (vnum >= surf->num_firstvertex
4626 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4627 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4628 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4629 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4630 && 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)
4633 vnum = Mod_Mesh_AddVertex(mod, surf, x, y, z, nx, ny, nz, s, t, u, v, r, g, b, a);
4634 mesh->data_vertexhash[h] = vnum;
4638 void Mod_Mesh_AddTriangle(model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4640 surfmesh_t *mesh = &mod->surfmesh;
4641 if (mesh->max_triangles == mesh->num_triangles)
4643 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4644 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4645 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4647 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4648 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4649 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4650 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4651 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4652 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4653 mesh->num_triangles++;
4654 surf->num_triangles++;
4657 static void Mod_Mesh_MakeSortedSurfaces(model_t *mod)
4661 unsigned char* included = R_FrameData_Alloc(mod->num_surfaces * sizeof(unsigned char));
4663 // build the sorted surfaces list properly to reduce material setup
4664 // this is easy because we're just sorting on texture and don't care about the order of textures
4665 mod->submodelsurfaces_start = 0;
4666 mod->submodelsurfaces_end = 0;
4667 for (i = 0; i < mod->num_surfaces; i++)
4669 for (i = 0; i < mod->num_surfaces; i++)
4673 tex = mod->data_surfaces[i].texture;
4674 // j = i is intentional
4675 for (j = i; j < mod->num_surfaces; j++)
4677 if (!included[j] && mod->data_surfaces[j].texture == tex)
4680 mod->modelsurfaces_sorted[mod->submodelsurfaces_end++] = j;
4686 static void Mod_Mesh_ComputeBounds(model_t *mod)
4689 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4691 if (mod->surfmesh.num_vertices > 0)
4693 // calculate normalmins/normalmaxs
4694 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4695 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4696 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4698 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4699 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4700 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4701 // expand bounds to include this vertex
4702 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4703 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4704 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4705 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4706 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4707 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4709 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4710 // (fast but less accurate than doing it per vertex)
4711 x2a = mod->normalmins[0] * mod->normalmins[0];
4712 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4713 y2a = mod->normalmins[1] * mod->normalmins[1];
4714 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4715 z2a = mod->normalmins[2] * mod->normalmins[2];
4716 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4720 yawradius = sqrt(x2 + y2);
4721 rotatedradius = sqrt(x2 + y2 + z2);
4722 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4723 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4724 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4725 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4726 mod->radius = rotatedradius;
4727 mod->radius2 = x2 + y2 + z2;
4731 VectorClear(mod->normalmins);
4732 VectorClear(mod->normalmaxs);
4733 VectorClear(mod->yawmins);
4734 VectorClear(mod->yawmaxs);
4735 VectorClear(mod->rotatedmins);
4736 VectorClear(mod->rotatedmaxs);
4742 void Mod_Mesh_Validate(model_t *mod)
4745 qbool warned = false;
4746 for (i = 0; i < mod->num_surfaces; i++)
4748 msurface_t *surf = mod->data_surfaces + i;
4749 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4750 int first = surf->num_firstvertex;
4751 int end = surf->num_firstvertex + surf->num_vertices;
4753 for (j = 0;j < surf->num_triangles * 3;j++)
4755 if (e[j] < first || e[j] >= end)
4758 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4766 static void Mod_Mesh_UploadDynamicBuffers(model_t *mod)
4768 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;
4769 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;
4770 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;
4771 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;
4772 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;
4773 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;
4774 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;
4775 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;
4776 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;
4777 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;
4778 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;
4781 void Mod_Mesh_Finalize(model_t *mod)
4783 if (gl_paranoid.integer)
4784 Mod_Mesh_Validate(mod);
4785 Mod_Mesh_ComputeBounds(mod);
4786 Mod_Mesh_MakeSortedSurfaces(mod);
4787 if(!r_refdef.draw2dstage)
4788 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);
4789 Mod_Mesh_UploadDynamicBuffers(mod);