2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // models.c -- model loading and caching
22 // models are the only shared resource between a client and server running
23 // on the same machine.
30 cvar_t r_mipskins = {CF_CLIENT | CF_ARCHIVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
31 cvar_t r_mipnormalmaps = {CF_CLIENT | CF_ARCHIVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
44 // Supported model formats
45 static modloader_t loader[] =
47 {"obj", NULL, 0, Mod_OBJ_Load},
48 {NULL, "IDPO", 4, Mod_IDP0_Load},
49 {NULL, "IDP2", 4, Mod_IDP2_Load},
50 {NULL, "IDP3", 4, Mod_IDP3_Load},
51 {NULL, "IDSP", 4, Mod_IDSP_Load},
52 {NULL, "IDS2", 4, Mod_IDS2_Load},
53 {NULL, "\035", 1, Mod_Q1BSP_Load},
54 {NULL, "\036", 1, Mod_HLBSP_Load},
55 {NULL, "BSP2", 4, Mod_BSP2_Load},
56 {NULL, "2PSB", 4, Mod_2PSB_Load},
57 {NULL, "IBSP", 4, Mod_IBSP_Load},
58 {NULL, "VBSP", 4, Mod_VBSP_Load},
59 {NULL, "ZYMOTICMODEL", 13, Mod_ZYMOTICMODEL_Load},
60 {NULL, "DARKPLACESMODEL", 16, Mod_DARKPLACESMODEL_Load},
61 {NULL, "PSKMODEL", 9, Mod_PSKMODEL_Load},
62 {NULL, "INTERQUAKEMODEL", 16, Mod_INTERQUAKEMODEL_Load},
63 {"map", NULL, 0, Mod_MAP_Load},
67 static mempool_t *mod_mempool;
68 static memexpandablearray_t models;
70 static mempool_t* q3shaders_mem;
71 typedef struct q3shader_hash_entry_s
74 struct q3shader_hash_entry_s* chain;
75 } q3shader_hash_entry_t;
76 #define Q3SHADER_HASH_SIZE 1021
77 typedef struct q3shader_data_s
79 memexpandablearray_t hash_entries;
80 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
81 memexpandablearray_t char_ptrs;
83 static q3shader_data_t* q3shader_data;
85 static void mod_start(void)
88 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
91 SCR_PushLoadingScreen("Loading models", 1.0);
93 for (i = 0;i < nummodels;i++)
94 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
97 for (i = 0;i < nummodels;i++)
98 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
101 SCR_PushLoadingScreen(mod->name, 1.0 / count);
102 Mod_LoadModel(mod, true, false);
103 SCR_PopLoadingScreen(false);
105 SCR_PopLoadingScreen(false);
108 static void mod_shutdown(void)
111 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
114 for (i = 0;i < nummodels;i++)
115 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
116 Mod_UnloadModel(mod);
119 Mod_Skeletal_FreeBuffers();
122 static void mod_newmap(void)
125 int i, j, k, l, surfacenum, ssize, tsize;
126 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
129 for (i = 0;i < nummodels;i++)
131 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
133 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
135 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
136 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
137 if (mod->data_textures[j].shaderpasses[l])
138 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
139 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
141 if (mod->brush.solidskyskinframe)
142 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
143 if (mod->brush.alphaskyskinframe)
144 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
148 if (!cl_stainmaps_clearonload.integer)
151 for (i = 0;i < nummodels;i++)
153 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
155 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
157 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
159 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
160 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
161 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
162 mod->brushq1.lightmapupdateflags[surfacenum] = true;
174 static void Mod_Print_f(cmd_state_t *cmd);
175 static void Mod_Precache_f(cmd_state_t *cmd);
176 static void Mod_Decompile_f(cmd_state_t *cmd);
177 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
180 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
181 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(model_t), 16);
187 Cvar_RegisterVariable(&r_mipskins);
188 Cvar_RegisterVariable(&r_mipnormalmaps);
189 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
190 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
191 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
193 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
194 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
195 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
196 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
197 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
198 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
200 Cmd_AddCommand(CF_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
201 Cmd_AddCommand(CF_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
202 Cmd_AddCommand(CF_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
203 Cmd_AddCommand(CF_CLIENT, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
206 void Mod_RenderInit(void)
208 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
211 void Mod_UnloadModel (model_t *mod)
213 char name[MAX_QPATH];
215 model_t *parentmodel;
217 if (developer_loading.integer)
218 Con_Printf("unloading model %s\n", mod->name);
220 strlcpy(name, mod->name, sizeof(name));
221 parentmodel = mod->brush.parentmodel;
225 if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
226 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
227 mod->surfmesh.data_element3i_indexbuffer = NULL;
228 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
229 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
230 mod->surfmesh.data_element3s_indexbuffer = NULL;
231 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
232 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
233 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
234 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
235 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
236 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
237 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
238 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
239 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
240 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
241 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
243 // free textures/memory attached to the model
244 R_FreeTexturePool(&mod->texturepool);
245 Mem_FreePool(&mod->mempool);
246 // clear the struct to make it available
247 memset(mod, 0, sizeof(model_t));
248 // restore the fields we want to preserve
249 strlcpy(mod->name, name, sizeof(mod->name));
250 mod->brush.parentmodel = parentmodel;
255 static void R_Model_Null_Draw(entity_render_t *ent)
261 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass);
263 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
278 // REQUIRED: fetch start
279 COM_ParseToken_Simple(&bufptr, true, false, true);
281 break; // end of file
282 if (!strcmp(com_token, "\n"))
283 continue; // empty line
284 start = atoi(com_token);
286 // REQUIRED: fetch length
287 COM_ParseToken_Simple(&bufptr, true, false, true);
288 if (!bufptr || !strcmp(com_token, "\n"))
290 Con_Printf("framegroups file: missing number of frames\n");
293 len = atoi(com_token);
295 // OPTIONAL args start
296 COM_ParseToken_Simple(&bufptr, true, false, true);
298 // OPTIONAL: fetch fps
300 if (bufptr && strcmp(com_token, "\n"))
302 fps = atof(com_token);
303 COM_ParseToken_Simple(&bufptr, true, false, true);
306 // OPTIONAL: fetch loopflag
308 if (bufptr && strcmp(com_token, "\n"))
310 loop = (atoi(com_token) != 0);
311 COM_ParseToken_Simple(&bufptr, true, false, true);
314 // OPTIONAL: fetch name
316 if (bufptr && strcmp(com_token, "\n"))
318 strlcpy(name, com_token, sizeof(name));
319 COM_ParseToken_Simple(&bufptr, true, false, true);
322 // OPTIONAL: remaining unsupported tokens (eat them)
323 while (bufptr && strcmp(com_token, "\n"))
324 COM_ParseToken_Simple(&bufptr, true, false, true);
326 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
329 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
336 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass)
338 model_t *mod = (model_t *) pass;
339 animscene_t *anim = &mod->animscenes[i];
341 strlcpy(anim->name, name, sizeof(anim[i].name));
343 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
344 anim->firstframe = bound(0, start, mod->num_poses - 1);
345 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
346 anim->framerate = max(1, fps);
348 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
351 static void Mod_FrameGroupify(model_t *mod, const char *buf)
356 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
359 Con_Printf("no scene found in framegroups file, aborting\n");
362 mod->numframes = cnt;
365 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
366 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
369 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
372 static void Mod_FindPotentialDeforms(model_t *mod)
376 mod->wantnormals = false;
377 mod->wanttangents = false;
378 for (i = 0;i < mod->num_textures;i++)
380 texture = mod->data_textures + i;
381 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
382 mod->wantnormals = true;
383 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
384 mod->wantnormals = true;
385 for (j = 0;j < Q3MAXDEFORMS;j++)
387 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
389 mod->wanttangents = true;
390 mod->wantnormals = true;
393 if (texture->deforms[j].deform != Q3DEFORM_NONE)
394 mod->wantnormals = true;
406 model_t *Mod_LoadModel(model_t *mod, qbool crash, qbool checkdisk)
410 fs_offset_t filesize = 0;
415 if (mod->name[0] == '*') // submodel
418 if (!strcmp(mod->name, "null"))
423 if (mod->loaded || mod->mempool)
424 Mod_UnloadModel(mod);
426 if (developer_loading.integer)
427 Con_Printf("loading model %s\n", mod->name);
430 mod->crc = (unsigned int)-1;
433 VectorClear(mod->normalmins);
434 VectorClear(mod->normalmaxs);
435 VectorClear(mod->yawmins);
436 VectorClear(mod->yawmaxs);
437 VectorClear(mod->rotatedmins);
438 VectorClear(mod->rotatedmaxs);
440 mod->modeldatatypestring = "null";
441 mod->type = mod_null;
442 mod->Draw = R_Model_Null_Draw;
446 // no fatal errors occurred, so this model is ready to use.
455 // even if the model is loaded it still may need reloading...
457 // if it is not loaded or checkdisk is true we need to calculate the crc
458 if (!mod->loaded || checkdisk)
460 if (checkdisk && mod->loaded)
461 Con_DPrintf("checking model %s\n", mod->name);
462 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
465 crc = CRC_Block((unsigned char *)buf, filesize);
466 // we need to reload the model if the crc does not match
472 // if the model is already loaded and checks passed, just return
480 if (developer_loading.integer)
481 Con_Printf("loading model %s\n", mod->name);
483 SCR_PushLoadingScreen(mod->name, 1);
485 // LadyHavoc: unload the existing model in this slot (if there is one)
486 if (mod->loaded || mod->mempool)
487 Mod_UnloadModel(mod);
492 // errors can prevent the corresponding mod->loaded = true;
495 // default lightmap scale
496 mod->lightmapscale = 1;
498 // default model radius and bounding box (mainly for missing models)
500 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
501 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
502 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
503 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
504 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
505 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
509 // load q3 shaders for the first time, or after a level change
516 const char *ext = FS_FileExtension(mod->name);
517 char *bufend = (char *)buf + filesize;
518 // all models use memory, so allocate a memory pool
519 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
521 // We need to have a reference to the base model in case we're parsing submodels
524 // Call the appropriate loader. Try matching magic bytes.
525 for (i = 0; loader[i].Load; i++)
527 // Headerless formats can just load based on extension. Otherwise match the magic string.
528 if((loader[i].extension && !strcasecmp(ext, loader[i].extension) && !loader[i].header) ||
529 (loader[i].header && !memcmp(buf, loader[i].header, loader[i].headersize)))
532 loader[i].Load(mod, buf, bufend);
535 Mod_FindPotentialDeforms(mod);
537 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
540 Mod_FrameGroupify(mod, (const char *)buf);
544 Mod_SetDrawSkyAndWater(mod);
550 Con_Printf(CON_ERROR "Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
553 // LadyHavoc: Sys_Error was *ANNOYING*
554 Con_Printf (CON_ERROR "Mod_LoadModel: %s not found\n", mod->name);
556 // no fatal errors occurred, so this model is ready to use.
559 SCR_PopLoadingScreen(false);
564 void Mod_ClearUsed(void)
567 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
569 for (i = 0;i < nummodels;i++)
570 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
574 void Mod_PurgeUnused(void)
577 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
579 for (i = 0;i < nummodels;i++)
581 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
583 Mod_UnloadModel(mod);
584 Mem_ExpandableArray_FreeRecord(&models, mod);
595 model_t *Mod_FindName(const char *name, const char *parentname)
604 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
607 Host_Error ("Mod_ForName: empty name");
609 // search the currently loaded models
610 for (i = 0;i < nummodels;i++)
612 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name) && ((!mod->brush.parentmodel && !parentname[0]) || (mod->brush.parentmodel && parentname[0] && !strcmp(mod->brush.parentmodel->name, parentname))))
619 // no match found, create a new one
620 mod = (model_t *) Mem_ExpandableArray_AllocRecord(&models);
621 strlcpy(mod->name, name, sizeof(mod->name));
623 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
625 mod->brush.parentmodel = NULL;
631 extern qbool vid_opened;
637 Loads in a model for the given name
640 model_t *Mod_ForName(const char *name, qbool crash, qbool checkdisk, const char *parentname)
644 // FIXME: So we don't crash if a server is started early.
648 model = Mod_FindName(name, parentname);
649 if (!model->loaded || checkdisk)
650 Mod_LoadModel(model, crash, checkdisk);
658 Reloads all models if they have changed
661 void Mod_Reload(void)
664 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
667 SCR_PushLoadingScreen("Reloading models", 1.0);
669 for (i = 0;i < nummodels;i++)
670 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
672 for (i = 0;i < nummodels;i++)
673 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
675 SCR_PushLoadingScreen(mod->name, 1.0 / count);
676 Mod_LoadModel(mod, true, true);
677 SCR_PopLoadingScreen(false);
679 SCR_PopLoadingScreen(false);
682 unsigned char *mod_base;
685 //=============================================================================
692 static void Mod_Print_f(cmd_state_t *cmd)
695 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
698 Con_Print("Loaded models:\n");
699 for (i = 0;i < nummodels;i++)
701 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
703 if (mod->brush.numsubmodels)
704 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
706 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
716 static void Mod_Precache_f(cmd_state_t *cmd)
718 if (Cmd_Argc(cmd) == 2)
719 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
721 Con_Print("usage: modelprecache <filename>\n");
724 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
728 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
729 memset(used, 0, numvertices);
730 for (i = 0;i < numelements;i++)
731 used[elements[i]] = 1;
732 for (i = 0, count = 0;i < numvertices;i++)
733 remapvertices[i] = used[i] ? count++ : -1;
738 qbool Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
740 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
742 int invalidintcount = 0, invalidintexample = 0;
743 int invalidshortcount = 0, invalidshortexample = 0;
744 int invalidmismatchcount = 0, invalidmismatchexample = 0;
747 for (i = 0; i < numelements; i++)
749 if (element3i[i] < first || element3i[i] > last)
752 invalidintexample = i;
758 for (i = 0; i < numelements; i++)
760 if (element3s[i] < first || element3s[i] > last)
763 invalidintexample = i;
767 if (element3i && element3s)
769 for (i = 0; i < numelements; i++)
771 if (element3s[i] != element3i[i])
773 invalidmismatchcount++;
774 invalidmismatchexample = i;
778 if (invalidintcount || invalidshortcount || invalidmismatchcount)
780 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, (void *)element3i, (void *)element3s, filename, fileline);
781 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
782 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
783 Con_Printf(", %i elements mismatch between element3i and element3s (example: element3s[%i] is %i and element3i[%i] is %i)", invalidmismatchcount, invalidmismatchexample, element3s ? element3s[invalidmismatchexample] : 0, invalidmismatchexample, element3i ? element3i[invalidmismatchexample] : 0);
784 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
786 // edit the elements to make them safer, as the driver will crash otherwise
788 for (i = 0; i < numelements; i++)
789 if (element3i[i] < first || element3i[i] > last)
790 element3i[i] = first;
792 for (i = 0; i < numelements; i++)
793 if (element3s[i] < first || element3s[i] > last)
794 element3s[i] = first;
795 if (element3i && element3s)
796 for (i = 0; i < numelements; i++)
797 if (element3s[i] != element3i[i])
798 element3s[i] = element3i[i];
805 // warning: this is an expensive function!
806 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qbool areaweighting)
813 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
814 // process each vertex of each triangle and accumulate the results
815 // use area-averaging, to make triangles with a big area have a bigger
816 // weighting on the vertex normal than triangles with a small area
817 // to do so, just add the 'normals' together (the bigger the area
818 // the greater the length of the normal is
820 for (i = 0; i < numtriangles; i++, element += 3)
823 vertex3f + element[0] * 3,
824 vertex3f + element[1] * 3,
825 vertex3f + element[2] * 3,
830 VectorNormalize(areaNormal);
832 for (j = 0;j < 3;j++)
834 vectorNormal = normal3f + element[j] * 3;
835 vectorNormal[0] += areaNormal[0];
836 vectorNormal[1] += areaNormal[1];
837 vectorNormal[2] += areaNormal[2];
840 // and just normalize the accumulated vertex normal in the end
841 vectorNormal = normal3f + 3 * firstvertex;
842 for (i = 0; i < numvertices; i++, vectorNormal += 3)
843 VectorNormalize(vectorNormal);
847 static void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, const float *tc0, const float *tc1, const float *tc2, float *svector3f, float *tvector3f, float *normal3f)
849 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
850 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
851 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
853 // 6 multiply, 9 subtract
854 VectorSubtract(v1, v0, v10);
855 VectorSubtract(v2, v0, v20);
856 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
857 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
858 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
859 // 12 multiply, 10 subtract
860 tc10[1] = tc1[1] - tc0[1];
861 tc20[1] = tc2[1] - tc0[1];
862 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
863 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
864 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
865 tc10[0] = tc1[0] - tc0[0];
866 tc20[0] = tc2[0] - tc0[0];
867 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
868 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
869 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
870 // 12 multiply, 4 add, 6 subtract
871 f = DotProduct(svector3f, normal3f);
872 svector3f[0] -= f * normal3f[0];
873 svector3f[1] -= f * normal3f[1];
874 svector3f[2] -= f * normal3f[2];
875 f = DotProduct(tvector3f, normal3f);
876 tvector3f[0] -= f * normal3f[0];
877 tvector3f[1] -= f * normal3f[1];
878 tvector3f[2] -= f * normal3f[2];
879 // if texture is mapped the wrong way (counterclockwise), the tangents
880 // have to be flipped, this is detected by calculating a normal from the
881 // two tangents, and seeing if it is opposite the surface normal
882 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
883 CrossProduct(tvector3f, svector3f, tangentcross);
884 if (DotProduct(tangentcross, normal3f) < 0)
886 VectorNegate(svector3f, svector3f);
887 VectorNegate(tvector3f, tvector3f);
892 // warning: this is a very expensive function!
893 void Mod_BuildTextureVectorsFromNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const float *texcoord2f, const float *normal3f, const int *elements, float *svector3f, float *tvector3f, qbool areaweighting)
896 float sdir[3], tdir[3], normal[3], *svec, *tvec;
897 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
898 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
901 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
902 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
903 // process each vertex of each triangle and accumulate the results
904 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
906 v0 = vertex3f + e[0] * 3;
907 v1 = vertex3f + e[1] * 3;
908 v2 = vertex3f + e[2] * 3;
909 tc0 = texcoord2f + e[0] * 2;
910 tc1 = texcoord2f + e[1] * 2;
911 tc2 = texcoord2f + e[2] * 2;
913 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
914 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
916 // calculate the edge directions and surface normal
917 // 6 multiply, 9 subtract
918 VectorSubtract(v1, v0, v10);
919 VectorSubtract(v2, v0, v20);
920 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
921 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
922 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
924 // calculate the tangents
925 // 12 multiply, 10 subtract
926 tc10[1] = tc1[1] - tc0[1];
927 tc20[1] = tc2[1] - tc0[1];
928 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
929 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
930 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
931 tc10[0] = tc1[0] - tc0[0];
932 tc20[0] = tc2[0] - tc0[0];
933 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
934 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
935 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
937 // if texture is mapped the wrong way (counterclockwise), the tangents
938 // have to be flipped, this is detected by calculating a normal from the
939 // two tangents, and seeing if it is opposite the surface normal
940 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
941 CrossProduct(tdir, sdir, tangentcross);
942 if (DotProduct(tangentcross, normal) < 0)
944 VectorNegate(sdir, sdir);
945 VectorNegate(tdir, tdir);
950 VectorNormalize(sdir);
951 VectorNormalize(tdir);
953 for (i = 0;i < 3;i++)
955 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
956 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
959 // make the tangents completely perpendicular to the surface normal, and
960 // then normalize them
961 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
962 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
964 f = -DotProduct(svec, n);
965 VectorMA(svec, f, n, svec);
966 VectorNormalize(svec);
967 f = -DotProduct(tvec, n);
968 VectorMA(tvec, f, n, tvec);
969 VectorNormalize(tvec);
973 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qbool lightmapoffsets, qbool vertexcolors)
976 data = (unsigned char *)Mem_Alloc(mempool, numvertices * (3 + 3 + 3 + 3 + 2 + 2 + (vertexcolors ? 4 : 0)) * sizeof(float) + numvertices * (lightmapoffsets ? 1 : 0) * sizeof(int) + numtriangles * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0));
977 loadmodel->surfmesh.num_vertices = numvertices;
978 loadmodel->surfmesh.num_triangles = numtriangles;
979 if (loadmodel->surfmesh.num_vertices)
981 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
982 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
983 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
984 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
985 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
986 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
988 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
990 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
992 if (loadmodel->surfmesh.num_triangles)
994 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
995 if (loadmodel->surfmesh.num_vertices <= 65536)
996 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
1000 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
1002 shadowmesh_t *newmesh;
1003 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
1004 newmesh->mempool = mempool;
1005 newmesh->maxverts = maxverts;
1006 newmesh->maxtriangles = maxtriangles;
1007 newmesh->numverts = 0;
1008 newmesh->numtriangles = 0;
1009 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1010 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1012 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
1013 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
1014 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
1015 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
1019 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
1021 int hashindex, vnum;
1022 shadowmeshvertexhash_t *hash;
1023 // this uses prime numbers intentionally
1024 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
1025 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1027 vnum = (hash - mesh->vertexhashentries);
1028 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1029 return hash - mesh->vertexhashentries;
1031 vnum = mesh->numverts++;
1032 hash = mesh->vertexhashentries + vnum;
1033 hash->next = mesh->vertexhashtable[hashindex];
1034 mesh->vertexhashtable[hashindex] = hash;
1035 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1036 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1037 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1041 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1045 for (i = 0;i < numtris;i++)
1047 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1048 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1049 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1050 mesh->numtriangles++;
1053 // the triangle calculation can take a while, so let's do a keepalive here
1054 CL_KeepaliveMessage(false);
1057 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1059 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1060 CL_KeepaliveMessage(false);
1062 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1065 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1067 if (!mesh->numverts)
1070 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1071 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1073 // upload short indices as a buffer
1074 if (mesh->element3s && !mesh->element3s_indexbuffer)
1075 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1077 // upload int indices as a buffer
1078 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1079 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1081 // vertex buffer is several arrays and we put them in the same buffer
1083 // is this wise? the texcoordtexture2f array is used with dynamic
1084 // vertex/svector/tvector/normal when rendering animated models, on the
1085 // other hand animated models don't use a lot of vertices anyway...
1086 if (!mesh->vbo_vertexbuffer)
1088 mesh->vbooffset_vertex3f = 0;
1089 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1093 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qbool createvbo)
1095 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1097 if (mesh->vertexhashentries)
1098 Mem_Free(mesh->vertexhashentries);
1099 mesh->vertexhashentries = NULL;
1100 if (mesh->vertexhashtable)
1101 Mem_Free(mesh->vertexhashtable);
1102 mesh->vertexhashtable = NULL;
1103 if (mesh->maxverts > mesh->numverts)
1105 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1106 mesh->maxverts = mesh->numverts;
1108 if (mesh->maxtriangles > mesh->numtriangles)
1110 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1111 mesh->maxtriangles = mesh->numtriangles;
1113 if (mesh->numverts <= 65536)
1116 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1117 for (i = 0;i < mesh->numtriangles*3;i++)
1118 mesh->element3s[i] = mesh->element3i[i];
1121 Mod_ShadowMesh_CreateVBOs(mesh);
1124 // this can take a while, so let's do a keepalive here
1125 CL_KeepaliveMessage(false);
1130 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1133 vec3_t nmins, nmaxs, ncenter, temp;
1134 float nradius2, dist2, *v;
1138 VectorCopy(mesh->vertex3f, nmins);
1139 VectorCopy(mesh->vertex3f, nmaxs);
1140 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1142 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1143 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1144 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1146 // calculate center and radius
1147 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1148 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1149 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1151 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1153 VectorSubtract(v, ncenter, temp);
1154 dist2 = DotProduct(temp, temp);
1155 if (nradius2 < dist2)
1160 VectorCopy(nmins, mins);
1162 VectorCopy(nmaxs, maxs);
1164 VectorCopy(ncenter, center);
1166 *radius = sqrt(nradius2);
1169 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1171 if (mesh->element3i_indexbuffer)
1172 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1173 if (mesh->element3s_indexbuffer)
1174 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1175 if (mesh->vbo_vertexbuffer)
1176 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1178 Mem_Free(mesh->vertex3f);
1179 if (mesh->element3i)
1180 Mem_Free(mesh->element3i);
1181 if (mesh->element3s)
1182 Mem_Free(mesh->element3s);
1183 if (mesh->vertexhashentries)
1184 Mem_Free(mesh->vertexhashentries);
1185 if (mesh->vertexhashtable)
1186 Mem_Free(mesh->vertexhashtable);
1190 void Mod_CreateCollisionMesh(model_t *mod)
1192 int k, numcollisionmeshtriangles;
1193 qbool usesinglecollisionmesh = false;
1194 const msurface_t *surface = NULL;
1196 mempool_t *mempool = mod->mempool;
1197 if (!mempool && mod->brush.parentmodel)
1198 mempool = mod->brush.parentmodel->mempool;
1199 // make a single combined collision mesh for physics engine use
1200 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1201 numcollisionmeshtriangles = 0;
1202 for (k = mod->submodelsurfaces_start;k < mod->submodelsurfaces_end;k++)
1204 surface = mod->data_surfaces + k;
1205 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1207 usesinglecollisionmesh = true;
1208 numcollisionmeshtriangles = surface->num_triangles;
1211 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1213 numcollisionmeshtriangles += surface->num_triangles;
1215 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1216 if (usesinglecollisionmesh)
1217 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1220 for (k = mod->submodelsurfaces_start; k < mod->submodelsurfaces_end; k++)
1222 surface = mod->data_surfaces + k;
1223 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1225 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1228 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1232 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)
1237 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1238 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1241 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1242 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1243 texcoord2f[0] = tc[0];
1244 texcoord2f[1] = tc[1];
1247 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)
1249 float vup[3], vdown[3], vleft[3], vright[3];
1250 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1251 float sv[3], tv[3], nl[3];
1252 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1253 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1254 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1255 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1256 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1257 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1258 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1259 VectorAdd(svector3f, sv, svector3f);
1260 VectorAdd(tvector3f, tv, tvector3f);
1261 VectorAdd(normal3f, nl, normal3f);
1262 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1263 VectorAdd(svector3f, sv, svector3f);
1264 VectorAdd(tvector3f, tv, tvector3f);
1265 VectorAdd(normal3f, nl, normal3f);
1266 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1267 VectorAdd(svector3f, sv, svector3f);
1268 VectorAdd(tvector3f, tv, tvector3f);
1269 VectorAdd(normal3f, nl, normal3f);
1272 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)
1274 int x, y, ix, iy, *e;
1276 for (y = 0;y < height;y++)
1278 for (x = 0;x < width;x++)
1280 e[0] = (y + 1) * (width + 1) + (x + 0);
1281 e[1] = (y + 0) * (width + 1) + (x + 0);
1282 e[2] = (y + 1) * (width + 1) + (x + 1);
1283 e[3] = (y + 0) * (width + 1) + (x + 0);
1284 e[4] = (y + 0) * (width + 1) + (x + 1);
1285 e[5] = (y + 1) * (width + 1) + (x + 1);
1289 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1290 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1291 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1296 void Mod_Terrain_SurfaceRecurseChunk(model_t *model, int stepsize, int x, int y)
1300 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1301 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1302 float viewvector[3];
1303 unsigned int firstvertex;
1306 if (chunkwidth < 2 || chunkheight < 2)
1308 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]);
1309 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]);
1310 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1311 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1312 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1313 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1315 // too close for this stepsize, emit as 4 chunks instead
1317 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1318 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1319 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1320 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1323 // emit the geometry at stepsize into our vertex buffer / index buffer
1324 // we add two columns and two rows for skirt
1325 outwidth = chunkwidth+2;
1326 outheight = chunkheight+2;
1327 outwidth2 = outwidth-1;
1328 outheight2 = outheight-1;
1329 outwidth3 = outwidth+1;
1330 outheight3 = outheight+1;
1331 firstvertex = numvertices;
1332 e = model->terrain.element3i + numtriangles;
1333 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1334 v = model->terrain.vertex3f + numvertices;
1335 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1336 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1337 for (ty = 0;ty < outheight;ty++)
1339 for (tx = 0;tx < outwidth;tx++)
1341 *e++ = firstvertex + (ty )*outwidth3+(tx );
1342 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1343 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1344 *e++ = firstvertex + (ty )*outwidth3+(tx );
1345 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1346 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1349 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1350 for (ty = 0;ty <= outheight;ty++)
1352 skirtrow = ty == 0 || ty == outheight;
1353 ry = y+bound(1, ty, outheight)*stepsize;
1354 for (tx = 0;tx <= outwidth;tx++)
1356 skirt = skirtrow || tx == 0 || tx == outwidth;
1357 rx = x+bound(1, tx, outwidth)*stepsize;
1360 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1364 // TODO: emit skirt vertices
1367 void Mod_Terrain_UpdateSurfacesForViewOrigin(model_t *model)
1369 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1370 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1371 Mod_Terrain_BuildChunk(model,
1375 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1378 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1380 offset = bound(0, s[4] - '0', 9);
1381 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1386 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1387 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1388 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1389 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1390 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1391 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1392 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1393 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1394 return offset | Q3WAVEFUNC_NONE;
1397 void Mod_FreeQ3Shaders(void)
1399 Mem_FreePool(&q3shaders_mem);
1402 static void Q3Shader_AddToHash (shader_t* shader)
1404 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1405 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1406 q3shader_hash_entry_t* lastEntry = NULL;
1409 if (strcasecmp (entry->shader.name, shader->name) == 0)
1412 if(shader->dpshaderkill)
1414 // killed shader is a redeclarion? we can safely ignore it
1417 else if(entry->shader.dpshaderkill)
1419 // replace the old shader!
1420 // this will skip the entry allocating part
1421 // below and just replace the shader
1426 unsigned char *start, *end, *start2;
1427 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1428 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1429 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1430 if(memcmp(start, start2, end - start))
1431 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1433 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1438 entry = entry->chain;
1440 while (entry != NULL);
1443 if (lastEntry->shader.name[0] != 0)
1446 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1447 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1449 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1450 lastEntry->chain = newEntry;
1451 newEntry->chain = NULL;
1452 lastEntry = newEntry;
1454 /* else: head of chain, in hash entry array */
1457 memcpy (&entry->shader, shader, sizeof (shader_t));
1460 void Mod_LoadQ3Shaders(void)
1468 q3shaderinfo_layer_t *layer;
1470 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1471 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1472 unsigned long custsurfaceflags[256];
1473 int numcustsurfaceflags;
1476 Mod_FreeQ3Shaders();
1478 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1479 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1480 sizeof (q3shader_data_t));
1481 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1482 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1483 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1484 q3shaders_mem, sizeof (char**), 256);
1486 // parse custinfoparms.txt
1487 numcustsurfaceflags = 0;
1488 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1490 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1491 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1494 while (COM_ParseToken_QuakeC(&text, false))
1495 if (!strcasecmp(com_token, "}"))
1497 // custom surfaceflags section
1498 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1499 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1502 while(COM_ParseToken_QuakeC(&text, false))
1504 if (!strcasecmp(com_token, "}"))
1506 // register surfaceflag
1507 if (numcustsurfaceflags >= 256)
1509 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1513 j = (int)strlen(com_token)+1;
1514 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1515 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1517 if (COM_ParseToken_QuakeC(&text, false))
1518 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1520 custsurfaceflags[numcustsurfaceflags] = 0;
1521 numcustsurfaceflags++;
1529 search = FS_Search("scripts/*.shader", true, false, NULL);
1532 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1534 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1537 while (COM_ParseToken_QuakeC(&text, false))
1539 memset (&shader, 0, sizeof(shader));
1541 shader.surfaceparms = 0;
1542 shader.surfaceflags = 0;
1543 shader.textureflags = 0;
1544 shader.numlayers = 0;
1545 shader.lighting = false;
1546 shader.vertexalpha = false;
1547 shader.textureblendalpha = false;
1548 shader.skyboxname[0] = 0;
1549 shader.deforms[0].deform = Q3DEFORM_NONE;
1550 shader.dpnortlight = false;
1551 shader.dpshadow = false;
1552 shader.dpnoshadow = false;
1553 shader.dpmeshcollisions = false;
1554 shader.dpshaderkill = false;
1555 shader.dpreflectcube[0] = 0;
1556 shader.reflectmin = 0;
1557 shader.reflectmax = 1;
1558 shader.refractfactor = 1;
1559 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1560 shader.reflectfactor = 1;
1561 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1562 shader.r_water_wateralpha = 1;
1563 shader.r_water_waterscroll[0] = 0;
1564 shader.r_water_waterscroll[1] = 0;
1565 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1566 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1567 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1568 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1569 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1570 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1571 shader.specularscalemod = 1;
1572 shader.specularpowermod = 1;
1573 shader.rtlightambient = 0;
1574 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1575 // JUST GREP FOR "specularscalemod = 1".
1577 strlcpy(shader.name, com_token, sizeof(shader.name));
1578 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1580 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1583 while (COM_ParseToken_QuakeC(&text, false))
1585 if (!strcasecmp(com_token, "}"))
1587 if (!strcasecmp(com_token, "{"))
1589 static q3shaderinfo_layer_t dummy;
1590 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1592 layer = shader.layers + shader.numlayers++;
1596 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1597 memset(&dummy, 0, sizeof(dummy));
1600 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1601 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1602 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1603 layer->blendfunc[0] = GL_ONE;
1604 layer->blendfunc[1] = GL_ZERO;
1605 while (COM_ParseToken_QuakeC(&text, false))
1607 if (!strcasecmp(com_token, "}"))
1609 if (!strcasecmp(com_token, "\n"))
1612 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1614 if (j < TEXTURE_MAXFRAMES + 4)
1616 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1617 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1618 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1620 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1621 numparameters = j + 1;
1623 if (!COM_ParseToken_QuakeC(&text, true))
1626 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1627 // parameter[j][0] = 0;
1628 if (developer_insane.integer)
1630 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1631 for (j = 0;j < numparameters;j++)
1632 Con_DPrintf(" %s", parameter[j]);
1635 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1637 if (numparameters == 2)
1639 if (!strcasecmp(parameter[1], "add"))
1641 layer->blendfunc[0] = GL_ONE;
1642 layer->blendfunc[1] = GL_ONE;
1644 else if (!strcasecmp(parameter[1], "addalpha"))
1646 layer->blendfunc[0] = GL_SRC_ALPHA;
1647 layer->blendfunc[1] = GL_ONE;
1649 else if (!strcasecmp(parameter[1], "filter"))
1651 layer->blendfunc[0] = GL_DST_COLOR;
1652 layer->blendfunc[1] = GL_ZERO;
1654 else if (!strcasecmp(parameter[1], "blend"))
1656 layer->blendfunc[0] = GL_SRC_ALPHA;
1657 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1660 else if (numparameters == 3)
1663 for (k = 0;k < 2;k++)
1665 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1666 layer->blendfunc[k] = GL_ONE;
1667 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1668 layer->blendfunc[k] = GL_ZERO;
1669 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1670 layer->blendfunc[k] = GL_SRC_COLOR;
1671 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1672 layer->blendfunc[k] = GL_SRC_ALPHA;
1673 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1674 layer->blendfunc[k] = GL_DST_COLOR;
1675 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1676 layer->blendfunc[k] = GL_DST_ALPHA;
1677 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1678 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1679 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1680 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1681 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1682 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1683 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1684 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1686 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1690 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1691 layer->alphatest = true;
1692 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1694 if (!strcasecmp(parameter[0], "clampmap"))
1695 layer->clampmap = true;
1696 layer->numframes = 1;
1697 layer->framerate = 1;
1698 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1699 &q3shader_data->char_ptrs);
1700 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1701 if (!strcasecmp(parameter[1], "$lightmap"))
1702 shader.lighting = true;
1704 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1707 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1708 layer->framerate = atof(parameter[1]);
1709 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1710 for (i = 0;i < layer->numframes;i++)
1711 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1713 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1716 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1717 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1718 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1719 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1720 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1721 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1722 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1723 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1724 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1725 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1726 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1727 else if (!strcasecmp(parameter[1], "wave"))
1729 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1730 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1731 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1732 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1734 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1736 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1739 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1740 layer->alphagen.parms[i] = atof(parameter[i+2]);
1741 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1742 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1743 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1744 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1745 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1746 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1747 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1748 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1749 else if (!strcasecmp(parameter[1], "wave"))
1751 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1752 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1753 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1754 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1756 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1758 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1761 // observed values: tcgen environment
1762 // no other values have been observed in real shaders
1763 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1764 layer->tcgen.parms[i] = atof(parameter[i+2]);
1765 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1766 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1767 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1768 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1769 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1770 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1772 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1779 // tcmod stretch sin # # # #
1780 // tcmod stretch triangle # # # #
1781 // tcmod transform # # # # # #
1782 // tcmod turb # # # #
1783 // tcmod turb sin # # # # (this is bogus)
1784 // no other values have been observed in real shaders
1785 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1786 if (!layer->tcmods[tcmodindex].tcmod)
1788 if (tcmodindex < Q3MAXTCMODS)
1790 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1791 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1792 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1793 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1794 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1795 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1796 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1797 else if (!strcasecmp(parameter[1], "stretch"))
1799 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1800 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1801 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1802 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1804 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1805 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1806 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1809 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1811 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1812 if (!strcasecmp(com_token, "}"))
1815 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1816 shader.lighting = true;
1817 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1819 if (layer == shader.layers + 0)
1821 // vertex controlled transparency
1822 shader.vertexalpha = true;
1826 // multilayer terrain shader or similar
1827 shader.textureblendalpha = true;
1828 if (mod_q3shader_force_terrain_alphaflag.integer)
1829 shader.layers[0].dptexflags |= TEXF_ALPHA;
1833 if(mod_q3shader_force_addalpha.integer)
1835 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1836 // this cvar brings back this behaviour
1837 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1838 layer->blendfunc[0] = GL_SRC_ALPHA;
1841 layer->dptexflags = 0;
1842 if (layer->alphatest)
1843 layer->dptexflags |= TEXF_ALPHA;
1844 switch(layer->blendfunc[0])
1847 case GL_ONE_MINUS_SRC_ALPHA:
1848 layer->dptexflags |= TEXF_ALPHA;
1851 switch(layer->blendfunc[1])
1854 case GL_ONE_MINUS_SRC_ALPHA:
1855 layer->dptexflags |= TEXF_ALPHA;
1858 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1859 layer->dptexflags |= TEXF_MIPMAP;
1860 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1861 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1862 if (layer->clampmap)
1863 layer->dptexflags |= TEXF_CLAMP;
1867 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1869 if (j < TEXTURE_MAXFRAMES + 4)
1871 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1872 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1873 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1875 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1876 numparameters = j + 1;
1878 if (!COM_ParseToken_QuakeC(&text, true))
1881 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1882 // parameter[j][0] = 0;
1883 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1885 if (developer_insane.integer)
1887 Con_DPrintf("%s: ", shader.name);
1888 for (j = 0;j < numparameters;j++)
1889 Con_DPrintf(" %s", parameter[j]);
1892 if (numparameters < 1)
1894 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1896 if (!strcasecmp(parameter[1], "alphashadow"))
1897 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1898 else if (!strcasecmp(parameter[1], "areaportal"))
1899 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1900 else if (!strcasecmp(parameter[1], "botclip"))
1901 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1902 else if (!strcasecmp(parameter[1], "clusterportal"))
1903 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1904 else if (!strcasecmp(parameter[1], "detail"))
1905 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1906 else if (!strcasecmp(parameter[1], "donotenter"))
1907 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1908 else if (!strcasecmp(parameter[1], "dust"))
1909 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1910 else if (!strcasecmp(parameter[1], "hint"))
1911 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1912 else if (!strcasecmp(parameter[1], "fog"))
1913 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1914 else if (!strcasecmp(parameter[1], "lava"))
1915 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1916 else if (!strcasecmp(parameter[1], "lightfilter"))
1917 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1918 else if (!strcasecmp(parameter[1], "lightgrid"))
1919 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1920 else if (!strcasecmp(parameter[1], "metalsteps"))
1921 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1922 else if (!strcasecmp(parameter[1], "nodamage"))
1923 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1924 else if (!strcasecmp(parameter[1], "nodlight"))
1925 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1926 else if (!strcasecmp(parameter[1], "nodraw"))
1927 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1928 else if (!strcasecmp(parameter[1], "nodrop"))
1929 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1930 else if (!strcasecmp(parameter[1], "noimpact"))
1931 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1932 else if (!strcasecmp(parameter[1], "nolightmap"))
1933 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1934 else if (!strcasecmp(parameter[1], "nomarks"))
1935 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1936 else if (!strcasecmp(parameter[1], "nomipmaps"))
1937 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1938 else if (!strcasecmp(parameter[1], "nonsolid"))
1939 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1940 else if (!strcasecmp(parameter[1], "origin"))
1941 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1942 else if (!strcasecmp(parameter[1], "playerclip"))
1943 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1944 else if (!strcasecmp(parameter[1], "sky"))
1945 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1946 else if (!strcasecmp(parameter[1], "slick"))
1947 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1948 else if (!strcasecmp(parameter[1], "slime"))
1949 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1950 else if (!strcasecmp(parameter[1], "structural"))
1951 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1952 else if (!strcasecmp(parameter[1], "trans"))
1953 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1954 else if (!strcasecmp(parameter[1], "water"))
1955 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1956 else if (!strcasecmp(parameter[1], "pointlight"))
1957 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1958 else if (!strcasecmp(parameter[1], "antiportal"))
1959 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1960 else if (!strcasecmp(parameter[1], "skip"))
1961 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1964 // try custom surfaceparms
1965 for (j = 0; j < numcustsurfaceflags; j++)
1967 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1969 shader.surfaceflags |= custsurfaceflags[j];
1974 if (j == numcustsurfaceflags)
1975 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1978 else if (!strcasecmp(parameter[0], "dpshadow"))
1979 shader.dpshadow = true;
1980 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1981 shader.dpnoshadow = true;
1982 else if (!strcasecmp(parameter[0], "dpnortlight"))
1983 shader.dpnortlight = true;
1984 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1985 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1986 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1987 shader.dpmeshcollisions = true;
1988 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1989 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1991 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1992 shader.dpshaderkill = dpshaderkill;
1994 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1995 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1997 const char *op = NULL;
1998 if (numparameters >= 3)
2002 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
2003 shader.dpshaderkill = dpshaderkill;
2005 else if (numparameters >= 4 && !strcmp(op, "=="))
2007 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
2008 shader.dpshaderkill = dpshaderkill;
2010 else if (numparameters >= 4 && !strcmp(op, "!="))
2012 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
2013 shader.dpshaderkill = dpshaderkill;
2015 else if (numparameters >= 4 && !strcmp(op, ">"))
2017 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
2018 shader.dpshaderkill = dpshaderkill;
2020 else if (numparameters >= 4 && !strcmp(op, "<"))
2022 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
2023 shader.dpshaderkill = dpshaderkill;
2025 else if (numparameters >= 4 && !strcmp(op, ">="))
2027 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
2028 shader.dpshaderkill = dpshaderkill;
2030 else if (numparameters >= 4 && !strcmp(op, "<="))
2032 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
2033 shader.dpshaderkill = dpshaderkill;
2037 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2040 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2042 // some q3 skies don't have the sky parm set
2043 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2044 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2046 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2048 // some q3 skies don't have the sky parm set
2049 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2050 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2051 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2053 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2055 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2056 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2058 else if (!strcasecmp(parameter[0], "nomipmaps"))
2059 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2060 else if (!strcasecmp(parameter[0], "nopicmip"))
2061 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2062 else if (!strcasecmp(parameter[0], "polygonoffset"))
2063 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2064 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2066 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2067 if(numparameters >= 2)
2069 shader.biaspolygonfactor = atof(parameter[1]);
2070 if(numparameters >= 3)
2071 shader.biaspolygonoffset = atof(parameter[2]);
2073 shader.biaspolygonoffset = 0;
2076 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2078 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2079 if (!strcasecmp(parameter[1], "sky"))
2080 shader.transparentsort = TRANSPARENTSORT_SKY;
2081 else if (!strcasecmp(parameter[1], "distance"))
2082 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2083 else if (!strcasecmp(parameter[1], "hud"))
2084 shader.transparentsort = TRANSPARENTSORT_HUD;
2086 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2088 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2090 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2091 shader.refractfactor = atof(parameter[1]);
2092 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2094 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2096 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2097 shader.reflectfactor = atof(parameter[1]);
2098 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2100 else if (!strcasecmp(parameter[0], "dpcamera"))
2102 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2104 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2106 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2107 shader.reflectmin = atof(parameter[1]);
2108 shader.reflectmax = atof(parameter[2]);
2109 shader.refractfactor = atof(parameter[3]);
2110 shader.reflectfactor = atof(parameter[4]);
2111 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2112 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2113 shader.r_water_wateralpha = atof(parameter[11]);
2115 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2117 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2118 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2120 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2122 shader.specularscalemod = atof(parameter[1]);
2124 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2126 shader.specularpowermod = atof(parameter[1]);
2128 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2130 shader.rtlightambient = atof(parameter[1]);
2132 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2134 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2135 shader.offsetmapping = OFFSETMAPPING_OFF;
2136 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2137 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2138 else if (!strcasecmp(parameter[1], "linear"))
2139 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2140 else if (!strcasecmp(parameter[1], "relief"))
2141 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2142 if (numparameters >= 3)
2143 shader.offsetscale = atof(parameter[2]);
2144 if (numparameters >= 5)
2146 if(!strcasecmp(parameter[3], "bias"))
2147 shader.offsetbias = atof(parameter[4]);
2148 else if(!strcasecmp(parameter[3], "match"))
2149 shader.offsetbias = 1.0f - atof(parameter[4]);
2150 else if(!strcasecmp(parameter[3], "match8"))
2151 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2152 else if(!strcasecmp(parameter[3], "match16"))
2153 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2156 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2159 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2160 if (!shader.deforms[deformindex].deform)
2162 if (deformindex < Q3MAXDEFORMS)
2164 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2165 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2166 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2167 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2168 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2169 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2170 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2171 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2172 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2173 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2174 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2175 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2176 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2177 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2178 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2179 else if (!strcasecmp(parameter[1], "wave" ))
2181 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2182 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2183 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2184 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2186 else if (!strcasecmp(parameter[1], "move" ))
2188 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2189 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2190 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2191 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2196 // hide this shader if a cvar said it should be killed
2197 if (shader.dpshaderkill)
2198 shader.numlayers = 0;
2199 // fix up multiple reflection types
2200 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2201 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2203 Q3Shader_AddToHash (&shader);
2207 FS_FreeSearch(search);
2208 // free custinfoparm values
2209 for (j = 0; j < numcustsurfaceflags; j++)
2210 Mem_Free(custsurfaceparmnames[j]);
2213 shader_t *Mod_LookupQ3Shader(const char *name)
2215 unsigned short hash;
2216 q3shader_hash_entry_t* entry;
2218 Mod_LoadQ3Shaders();
2219 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2220 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2221 while (entry != NULL)
2223 if (strcasecmp (entry->shader.name, name) == 0)
2224 return &entry->shader;
2225 entry = entry->chain;
2230 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2232 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2233 shaderpass->framerate = 0.0f;
2234 shaderpass->numframes = 1;
2235 shaderpass->blendfunc[0] = GL_ONE;
2236 shaderpass->blendfunc[1] = GL_ZERO;
2237 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2238 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2239 shaderpass->alphatest = false;
2240 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2241 shaderpass->skinframes[0] = skinframe;
2245 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2248 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2249 shaderpass->alphatest = layer->alphatest != 0;
2250 shaderpass->framerate = layer->framerate;
2251 shaderpass->numframes = layer->numframes;
2252 shaderpass->blendfunc[0] = layer->blendfunc[0];
2253 shaderpass->blendfunc[1] = layer->blendfunc[1];
2254 shaderpass->rgbgen = layer->rgbgen;
2255 shaderpass->alphagen = layer->alphagen;
2256 shaderpass->tcgen = layer->tcgen;
2257 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2258 shaderpass->tcmods[j] = layer->tcmods[j];
2259 for (j = 0; j < layer->numframes; j++)
2261 for (int i = 0; layer->texturename[j][i]; i++)
2262 if(layer->texturename[j][i] == '\\')
2263 layer->texturename[j][i] = '/';
2264 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2269 qbool Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qbool warnmissing, qbool fallback, int defaulttexflags, int defaultmaterialflags)
2271 int texflagsmask, texflagsor;
2272 qbool success = true;
2276 strlcpy(texture->name, name, sizeof(texture->name));
2277 texture->basealpha = 1.0f;
2278 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2280 // allow disabling of picmip or compression by defaulttexflags
2282 if(!(defaulttexflags & TEXF_PICMIP))
2283 texflagsmask &= ~TEXF_PICMIP;
2284 if(!(defaulttexflags & TEXF_COMPRESS))
2285 texflagsmask &= ~TEXF_COMPRESS;
2287 if(defaulttexflags & TEXF_ISWORLD)
2288 texflagsor |= TEXF_ISWORLD;
2289 if(defaulttexflags & TEXF_ISSPRITE)
2290 texflagsor |= TEXF_ISSPRITE;
2291 // unless later loaded from the shader
2292 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2293 texture->offsetscale = 1;
2294 texture->offsetbias = 0;
2295 texture->specularscalemod = 1;
2296 texture->specularpowermod = 1;
2297 texture->rtlightambient = 0;
2298 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2299 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2300 // JUST GREP FOR "specularscalemod = 1".
2304 if (developer_loading.integer)
2305 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2307 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2309 texture->basematerialflags = MATERIALFLAG_SKY;
2310 if (shader->skyboxname[0] && loadmodel)
2312 // quake3 seems to append a _ to the skybox name, so this must do so as well
2313 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2316 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2317 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2319 texture->basematerialflags = MATERIALFLAG_WALL;
2321 if (shader->layers[0].alphatest)
2322 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2323 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2324 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2325 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2327 texture->biaspolygonoffset += shader->biaspolygonoffset;
2328 texture->biaspolygonfactor += shader->biaspolygonfactor;
2330 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2331 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2332 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2333 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2334 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2335 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2336 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2337 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2338 texture->customblendfunc[0] = GL_ONE;
2339 texture->customblendfunc[1] = GL_ZERO;
2340 texture->transparentsort = shader->transparentsort;
2341 if (shader->numlayers > 0)
2343 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2344 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2346 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2347 * additive GL_ONE GL_ONE
2348 additive weird GL_ONE GL_SRC_ALPHA
2349 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2350 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2351 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2352 brighten GL_DST_COLOR GL_ONE
2353 brighten GL_ONE GL_SRC_COLOR
2354 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2355 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2356 * modulate GL_DST_COLOR GL_ZERO
2357 * modulate GL_ZERO GL_SRC_COLOR
2358 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2359 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2360 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2361 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2362 * no blend GL_ONE GL_ZERO
2363 nothing GL_ZERO GL_ONE
2365 // if not opaque, figure out what blendfunc to use
2366 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2368 if (shader->layers[0].blendfunc[0] == GL_ONE && 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)
2371 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2372 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2373 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2375 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2378 if (!shader->lighting)
2379 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2381 // here be dragons: convert quake3 shaders to material
2382 if (shader->numlayers > 0)
2385 int terrainbackgroundlayer = -1;
2386 int lightmaplayer = -1;
2387 int alphagenspecularlayer = -1;
2388 int rgbgenvertexlayer = -1;
2389 int rgbgendiffuselayer = -1;
2390 int materiallayer = -1;
2391 int endofprelayers = 0;
2392 int firstpostlayer = 0;
2393 int shaderpassindex = 0;
2394 for (i = 0; i < shader->numlayers; i++)
2396 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2398 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2399 rgbgenvertexlayer = i;
2400 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2401 rgbgendiffuselayer = i;
2402 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2403 alphagenspecularlayer = i;
2405 if (shader->numlayers >= 2
2406 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2407 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2408 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2409 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2411 // terrain blend or certain other effects involving alphatest over a regular layer
2412 terrainbackgroundlayer = 0;
2414 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2415 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2417 else if (lightmaplayer == 0)
2419 // ordinary texture but with $lightmap before diffuse
2421 firstpostlayer = lightmaplayer + 2;
2423 else if (lightmaplayer >= 1)
2425 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2426 endofprelayers = lightmaplayer - 1;
2427 materiallayer = lightmaplayer - 1;
2428 firstpostlayer = lightmaplayer + 1;
2430 else if (rgbgenvertexlayer >= 0)
2432 // map models with baked lighting
2433 materiallayer = rgbgenvertexlayer;
2434 endofprelayers = rgbgenvertexlayer;
2435 firstpostlayer = rgbgenvertexlayer + 1;
2436 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2437 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2438 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2440 else if (rgbgendiffuselayer >= 0)
2442 // entity models with dynamic lighting
2443 materiallayer = rgbgendiffuselayer;
2444 endofprelayers = rgbgendiffuselayer;
2445 firstpostlayer = rgbgendiffuselayer + 1;
2446 // 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)...
2447 if (alphagenspecularlayer >= 0)
2448 firstpostlayer = alphagenspecularlayer + 1;
2452 // special effects shaders - treat first as primary layer and do everything else as post
2457 // convert the main material layer
2458 // 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
2459 if (materiallayer >= 0)
2460 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2461 // convert the terrain background blend layer (if any)
2462 if (terrainbackgroundlayer >= 0)
2463 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2464 // convert the prepass layers (if any)
2465 texture->startpreshaderpass = shaderpassindex;
2466 for (i = 0; i < endofprelayers; i++)
2467 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2468 texture->endpreshaderpass = shaderpassindex;
2469 texture->startpostshaderpass = shaderpassindex;
2470 // convert the postpass layers (if any)
2471 for (i = firstpostlayer; i < shader->numlayers; i++)
2472 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2473 texture->startpostshaderpass = shaderpassindex;
2476 if (shader->dpshadow)
2477 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2478 if (shader->dpnoshadow)
2479 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2480 if (shader->dpnortlight)
2481 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2482 if (shader->vertexalpha)
2483 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2484 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2485 texture->reflectmin = shader->reflectmin;
2486 texture->reflectmax = shader->reflectmax;
2487 texture->refractfactor = shader->refractfactor;
2488 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2489 texture->reflectfactor = shader->reflectfactor;
2490 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2491 texture->r_water_wateralpha = shader->r_water_wateralpha;
2492 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2493 texture->offsetmapping = shader->offsetmapping;
2494 texture->offsetscale = shader->offsetscale;
2495 texture->offsetbias = shader->offsetbias;
2496 texture->specularscalemod = shader->specularscalemod;
2497 texture->specularpowermod = shader->specularpowermod;
2498 texture->rtlightambient = shader->rtlightambient;
2499 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2500 if (shader->dpreflectcube[0])
2501 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2503 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2504 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2505 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2506 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2507 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2508 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2509 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2510 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2511 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2513 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2514 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2515 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2516 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2517 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2519 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2520 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2523 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2524 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2525 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2526 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2527 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2528 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2529 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2530 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2531 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2532 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2534 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2535 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2536 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2537 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2538 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2539 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2540 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2541 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2542 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2543 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2544 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2546 texture->surfaceflags = shader->surfaceflags;
2547 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2548 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2549 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2550 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2551 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2552 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2553 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2554 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2555 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2556 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2557 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2558 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2559 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2560 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2561 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2562 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2563 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2564 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2565 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2566 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2567 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2568 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2569 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2570 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2571 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2572 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2573 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2574 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2575 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2576 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2577 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2578 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2580 if (shader->dpmeshcollisions)
2581 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2582 if (shader->dpshaderkill && developer_extra.integer)
2583 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2585 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2587 if (developer_extra.integer)
2588 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2589 texture->basematerialflags = defaultmaterialflags;
2590 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2592 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2594 if (developer_extra.integer)
2595 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2596 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2597 texture->supercontents = SUPERCONTENTS_SOLID;
2601 if (developer_extra.integer)
2602 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2603 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2605 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2606 texture->supercontents = SUPERCONTENTS_SOLID;
2608 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2610 texture->basematerialflags = MATERIALFLAG_SKY;
2611 texture->supercontents = SUPERCONTENTS_SKY;
2615 texture->basematerialflags = defaultmaterialflags;
2616 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2618 if(cls.state == ca_dedicated)
2620 texture->materialshaderpass = NULL;
2625 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2628 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2629 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2630 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2631 if (texture->q2contents)
2632 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2636 if (!success && warnmissing)
2637 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2640 // init the animation variables
2641 texture->currentframe = texture;
2642 texture->currentmaterialflags = texture->basematerialflags;
2643 if (!texture->materialshaderpass)
2644 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2645 if (!texture->materialshaderpass->skinframes[0])
2646 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2647 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2648 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2652 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2654 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2655 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2657 strlcpy(texture->name, name, sizeof(texture->name));
2658 texture->basealpha = 1.0f;
2659 texture->basematerialflags = materialflags;
2660 texture->supercontents = supercontents;
2662 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2663 texture->offsetscale = 1;
2664 texture->offsetbias = 0;
2665 texture->specularscalemod = 1;
2666 texture->specularpowermod = 1;
2667 texture->rtlightambient = 0;
2668 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2669 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2670 // JUST GREP FOR "specularscalemod = 1".
2672 if (developer_extra.integer)
2673 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2675 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2677 // init the animation variables
2678 texture->currentmaterialflags = texture->basematerialflags;
2679 texture->currentframe = texture;
2680 texture->currentskinframe = skinframe;
2681 texture->backgroundcurrentskinframe = NULL;
2684 void Mod_UnloadCustomMaterial(texture_t *texture, qbool purgeskins)
2686 long unsigned int i, j;
2687 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2689 if (texture->shaderpasses[i])
2692 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2693 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2694 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2695 Mem_Free(texture->shaderpasses[i]);
2696 texture->shaderpasses[i] = NULL;
2699 texture->materialshaderpass = NULL;
2700 texture->currentskinframe = NULL;
2701 texture->backgroundcurrentskinframe = NULL;
2704 skinfile_t *Mod_LoadSkinFiles(void)
2706 int i, words, line, wordsoverflow;
2709 skinfile_t *skinfile = NULL, *first = NULL;
2710 skinfileitem_t *skinfileitem;
2711 char word[10][MAX_QPATH];
2716 U_bodyBox,models/players/Legoman/BikerA2.tga
2717 U_RArm,models/players/Legoman/BikerA1.tga
2718 U_LArm,models/players/Legoman/BikerA1.tga
2719 U_armor,common/nodraw
2720 U_sword,common/nodraw
2721 U_shield,common/nodraw
2722 U_homb,common/nodraw
2723 U_backpack,common/nodraw
2724 U_colcha,common/nodraw
2729 memset(word, 0, sizeof(word));
2730 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2732 // If it's the first file we parse
2733 if (skinfile == NULL)
2735 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2740 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2741 skinfile = skinfile->next;
2743 skinfile->next = NULL;
2745 for(line = 0;;line++)
2748 if (!COM_ParseToken_QuakeC(&data, true))
2750 if (!strcmp(com_token, "\n"))
2753 wordsoverflow = false;
2757 strlcpy(word[words++], com_token, sizeof (word[0]));
2759 wordsoverflow = true;
2761 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2764 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);
2767 // words is always >= 1
2768 if (!strcmp(word[0], "replace"))
2772 if (developer_loading.integer)
2773 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2774 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2775 skinfileitem->next = skinfile->items;
2776 skinfile->items = skinfileitem;
2777 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2778 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2781 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]);
2783 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2785 // tag name, like "tag_weapon,"
2786 // not used for anything (not even in Quake3)
2788 else if (words >= 2 && !strcmp(word[1], ","))
2790 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2791 if (developer_loading.integer)
2792 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2793 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2794 skinfileitem->next = skinfile->items;
2795 skinfile->items = skinfileitem;
2796 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2797 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2800 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);
2805 loadmodel->numskins = i;
2809 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2812 skinfileitem_t *skinfileitem, *nextitem;
2813 for (;skinfile;skinfile = next)
2815 next = skinfile->next;
2816 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2818 nextitem = skinfileitem->next;
2819 Mem_Free(skinfileitem);
2825 int Mod_CountSkinFiles(skinfile_t *skinfile)
2828 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2832 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2835 double isnap = 1.0 / snap;
2836 for (i = 0;i < numvertices*numcomponents;i++)
2837 vertices[i] = floor(vertices[i]*isnap)*snap;
2840 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2842 int i, outtriangles;
2843 float edgedir1[3], edgedir2[3], temp[3];
2844 // a degenerate triangle is one with no width (thickness, surface area)
2845 // these are characterized by having all 3 points colinear (along a line)
2846 // or having two points identical
2847 // the simplest check is to calculate the triangle's area
2848 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2850 // calculate first edge
2851 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2852 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2853 CrossProduct(edgedir1, edgedir2, temp);
2854 if (VectorLength2(temp) < 0.001f)
2855 continue; // degenerate triangle (no area)
2856 // valid triangle (has area)
2857 VectorCopy(inelement3i, outelement3i);
2861 return outtriangles;
2864 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2867 int firstvertex, lastvertex;
2868 if (numelements > 0 && elements)
2870 firstvertex = lastvertex = elements[0];
2871 for (i = 1;i < numelements;i++)
2874 firstvertex = min(firstvertex, e);
2875 lastvertex = max(lastvertex, e);
2879 firstvertex = lastvertex = 0;
2880 if (firstvertexpointer)
2881 *firstvertexpointer = firstvertex;
2882 if (lastvertexpointer)
2883 *lastvertexpointer = lastvertex;
2886 void Mod_SetDrawSkyAndWater(model_t* mod)
2889 uint64_t basematerialflags = 0;
2890 // by default assume there is no sky or water used in this model
2891 mod->DrawSky = NULL;
2892 mod->DrawAddWaterPlanes = NULL;
2893 // combine all basematerialflags observed in the submodelsurfaces range, then check for special flags
2894 for (j = mod->submodelsurfaces_start; j < mod->submodelsurfaces_end; j++)
2895 if (mod->data_surfaces[j].texture)
2896 basematerialflags |= mod->data_surfaces[j].texture->basematerialflags;
2897 if (basematerialflags & MATERIALFLAG_SKY)
2898 mod->DrawSky = R_Mod_DrawSky;
2899 if (basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
2900 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
2903 typedef struct Mod_MakeSortedSurfaces_qsortsurface_s
2906 q3deffect_t* effect;
2908 rtexture_t* lightmaptexture;
2910 Mod_MakeSortedSurfaces_qsortsurface_t;
2912 static int Mod_MakeSortedSurfaces_qsortfunc(const void *a, const void *b)
2914 const Mod_MakeSortedSurfaces_qsortsurface_t* l = (Mod_MakeSortedSurfaces_qsortsurface_t*)a;
2915 const Mod_MakeSortedSurfaces_qsortsurface_t* r = (Mod_MakeSortedSurfaces_qsortsurface_t*)b;
2916 if (l->effect < r->effect)
2918 if (l->effect > r->effect)
2920 if (l->texture < r->texture)
2922 if (l->texture > r->texture)
2924 if (l->lightmaptexture < r->lightmaptexture)
2926 if (l->lightmaptexture > r->lightmaptexture)
2928 if (l->surfaceindex < r->surfaceindex)
2930 if (l->surfaceindex > r->surfaceindex)
2935 void Mod_MakeSortedSurfaces(model_t *mod)
2937 // make an optimal set of texture-sorted batches to draw...
2939 Mod_MakeSortedSurfaces_qsortsurface_t *info;
2941 if(cls.state == ca_dedicated)
2944 info = (Mod_MakeSortedSurfaces_qsortsurface_t*)R_FrameData_Alloc(mod->num_surfaces * sizeof(*info));
2945 if (!mod->modelsurfaces_sorted)
2946 mod->modelsurfaces_sorted = (int *) Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*mod->modelsurfaces_sorted));
2947 // the goal is to sort by submodel (can't change which submodel a surface belongs to), and then by effects and textures
2948 for (j = 0; j < mod->num_surfaces; j++)
2950 info[j].surfaceindex = j;
2951 info[j].effect = mod->data_surfaces[j].effect;
2952 info[j].texture = mod->data_surfaces[j].texture;
2953 info[j].lightmaptexture = mod->data_surfaces[j].lightmaptexture;
2955 for (k = 0; k < mod->brush.numsubmodels; k++)
2956 if (mod->brush.submodels[k]->submodelsurfaces_end > mod->brush.submodels[k]->submodelsurfaces_start + 1)
2957 qsort(info + mod->brush.submodels[k]->submodelsurfaces_start, (size_t)mod->brush.submodels[k]->submodelsurfaces_end - mod->brush.submodels[k]->submodelsurfaces_start, sizeof(*info), Mod_MakeSortedSurfaces_qsortfunc);
2958 for (j = 0; j < mod->num_surfaces; j++)
2959 mod->modelsurfaces_sorted[j] = info[j].surfaceindex;
2962 void Mod_BuildVBOs(void)
2964 if(cls.state == ca_dedicated)
2967 if (!loadmodel->surfmesh.num_vertices)
2970 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2973 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2975 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2977 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2978 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2983 // upload short indices as a buffer
2984 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2985 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);
2987 // upload int indices as a buffer
2988 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2989 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);
2991 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2992 // we put several vertex data streams in the same buffer
2993 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2998 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2999 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3000 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3001 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3002 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3003 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3004 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
3005 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3006 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3007 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
3008 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3009 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3010 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3011 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3012 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3013 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3014 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
3015 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3016 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3017 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3018 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3019 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3020 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3021 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3022 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3023 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3024 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3025 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3030 extern cvar_t mod_obj_orientation;
3031 static void Mod_Decompile_OBJ(model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3033 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3035 const char *texname;
3037 const float *v, *vn, *vt;
3039 size_t outbufferpos = 0;
3040 size_t outbuffermax = 0x100000;
3041 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3042 const msurface_t *surface;
3043 const int maxtextures = 256;
3044 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3047 // construct the mtllib file
3048 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3051 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3054 countvertices += surface->num_vertices;
3055 countfaces += surface->num_triangles;
3056 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3057 for (textureindex = 0;textureindex < counttextures;textureindex++)
3058 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3060 if (textureindex < counttextures)
3061 continue; // already wrote this material entry
3062 if (textureindex >= maxtextures)
3063 continue; // just a precaution
3064 textureindex = counttextures++;
3065 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3066 if (outbufferpos >= outbuffermax >> 1)
3069 oldbuffer = outbuffer;
3070 outbuffer = (char *) Z_Malloc(outbuffermax);
3071 memcpy(outbuffer, oldbuffer, outbufferpos);
3074 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");
3079 // write the mtllib file
3080 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3082 // construct the obj file
3084 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);
3088 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)
3090 if (outbufferpos >= outbuffermax >> 1)
3093 oldbuffer = outbuffer;
3094 outbuffer = (char *) Z_Malloc(outbuffermax);
3095 memcpy(outbuffer, oldbuffer, outbufferpos);
3098 if(mod_obj_orientation.integer)
3099 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]);
3101 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]);
3106 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3108 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3111 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3112 for (surfaceindex = submodel->submodelsurfaces_start;surfaceindex < submodel->submodelsurfaces_end;surfaceindex++)
3114 surface = model->data_surfaces + surfaceindex;
3115 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3118 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3120 if (outbufferpos >= outbuffermax >> 1)
3123 oldbuffer = outbuffer;
3124 outbuffer = (char *) Z_Malloc(outbuffermax);
3125 memcpy(outbuffer, oldbuffer, outbufferpos);
3131 if(mod_obj_orientation.integer)
3132 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);
3134 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);
3141 // write the obj file
3142 FS_WriteFile(filename, outbuffer, outbufferpos);
3146 Z_Free(texturenames);
3149 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3152 static void Mod_Decompile_SMD(model_t *model, const char *filename, int firstpose, int numposes, qbool writetriangles)
3154 int countnodes = 0, counttriangles = 0, countframes = 0;
3162 size_t outbufferpos = 0;
3163 size_t outbuffermax = 0x100000;
3164 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3165 const msurface_t *surface;
3166 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3169 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3171 if (outbufferpos >= outbuffermax >> 1)
3174 oldbuffer = outbuffer;
3175 outbuffer = (char *) Z_Malloc(outbuffermax);
3176 memcpy(outbuffer, oldbuffer, outbufferpos);
3180 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3184 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3187 for (poseindex = 0;poseindex < numposes;poseindex++)
3190 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3193 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3197 matrix4x4_t posematrix;
3198 if (outbufferpos >= outbuffermax >> 1)
3201 oldbuffer = outbuffer;
3202 outbuffer = (char *) Z_Malloc(outbuffermax);
3203 memcpy(outbuffer, oldbuffer, outbufferpos);
3207 // strangely the smd angles are for a transposed matrix, so we
3208 // have to generate a transposed matrix, then convert that...
3209 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3210 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3211 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3212 if (angles[0] >= 180) angles[0] -= 360;
3213 if (angles[1] >= 180) angles[1] -= 360;
3214 if (angles[2] >= 180) angles[2] -= 360;
3218 float a = DEG2RAD(angles[ROLL]);
3219 float b = DEG2RAD(angles[PITCH]);
3220 float c = DEG2RAD(angles[YAW]);
3221 float cy, sy, cp, sp, cr, sr;
3223 // smd matrix construction, for comparing
3234 test[1][0] = sr*sp*cy+cr*-sy;
3235 test[1][1] = sr*sp*sy+cr*cy;
3237 test[2][0] = (cr*sp*cy+-sr*-sy);
3238 test[2][1] = (cr*sp*sy+-sr*cy);
3240 test[3][0] = pose[9];
3241 test[3][1] = pose[10];
3242 test[3][2] = pose[11];
3245 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]));
3250 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3255 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3258 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3260 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3263 if (outbufferpos >= outbuffermax >> 1)
3266 oldbuffer = outbuffer;
3267 outbuffer = (char *) Z_Malloc(outbuffermax);
3268 memcpy(outbuffer, oldbuffer, outbufferpos);
3271 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3274 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3276 const int index = e[2-cornerindex];
3277 const float *v = model->surfmesh.data_vertex3f + index * 3;
3278 const float *vn = model->surfmesh.data_normal3f + index * 3;
3279 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3280 const int b = model->surfmesh.blends[index];
3281 if (b < model->num_bones)
3282 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]);
3285 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3286 const unsigned char *wi = w->index;
3287 const unsigned char *wf = w->influence;
3288 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);
3289 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);
3290 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);
3291 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]);
3298 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3303 FS_WriteFile(filename, outbuffer, outbufferpos);
3306 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3313 decompiles a model to editable files
3316 static void Mod_Decompile_f(cmd_state_t *cmd)
3318 int i, j, k, l, first, count;
3320 char inname[MAX_QPATH];
3321 char outname[MAX_QPATH];
3322 char mtlname[MAX_QPATH];
3323 char basename[MAX_QPATH];
3324 char animname[MAX_QPATH];
3325 char animname2[MAX_QPATH];
3326 char zymtextbuffer[16384];
3327 char dpmtextbuffer[16384];
3328 char framegroupstextbuffer[16384];
3329 int zymtextsize = 0;
3330 int dpmtextsize = 0;
3331 int framegroupstextsize = 0;
3334 if (Cmd_Argc(cmd) != 2)
3336 Con_Print("usage: modeldecompile <filename>\n");
3340 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3341 FS_StripExtension(inname, basename, sizeof(basename));
3343 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3346 Con_Print("No such model\n");
3349 if (mod->brush.submodel)
3351 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3352 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3353 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3356 if (!mod->surfmesh.num_triangles)
3358 Con_Print("Empty model (or sprite)\n");
3362 // export OBJ if possible (not on sprites)
3363 if (mod->surfmesh.num_triangles)
3365 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3366 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3367 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3370 // export SMD if possible (only for skeletal models)
3371 if (mod->surfmesh.num_triangles && mod->num_bones)
3373 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3374 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3375 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3376 if (l > 0) zymtextsize += l;
3377 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3378 if (l > 0) dpmtextsize += l;
3379 for (i = 0;i < mod->numframes;i = j)
3381 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3382 first = mod->animscenes[i].firstframe;
3383 if (mod->animscenes[i].framecount > 1)
3386 count = mod->animscenes[i].framecount;
3392 // check for additional frames with same name
3393 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3394 if(animname[l] < '0' || animname[l] > '9')
3396 if(k > 0 && animname[k-1] == '_')
3399 count = mod->num_poses - first;
3400 for (j = i + 1;j < mod->numframes;j++)
3402 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3403 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3404 if(animname2[l] < '0' || animname2[l] > '9')
3406 if(k > 0 && animname[k-1] == '_')
3409 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3411 count = mod->animscenes[j].firstframe - first;
3415 // if it's only one frame, use the original frame name
3417 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3420 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3421 Mod_Decompile_SMD(mod, outname, first, count, false);
3422 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3424 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3425 if (l > 0) zymtextsize += l;
3427 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3429 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3430 if (l > 0) dpmtextsize += l;
3432 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3434 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3435 if (l > 0) framegroupstextsize += l;
3439 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3441 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3442 if (framegroupstextsize)
3443 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3447 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3450 memset(state, 0, sizeof(*state));
3451 state->width = width;
3452 state->height = height;
3453 state->currentY = 0;
3454 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3455 for (y = 0;y < state->height;y++)
3457 state->rows[y].currentX = 0;
3458 state->rows[y].rowY = -1;
3462 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3465 state->currentY = 0;
3466 for (y = 0;y < state->height;y++)
3468 state->rows[y].currentX = 0;
3469 state->rows[y].rowY = -1;
3473 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3476 Mem_Free(state->rows);
3477 memset(state, 0, sizeof(*state));
3480 qbool Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3482 mod_alloclightmap_row_t *row;
3485 row = state->rows + blockheight;
3486 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3488 if (state->currentY + blockheight <= state->height)
3490 // use the current allocation position
3491 row->rowY = state->currentY;
3493 state->currentY += blockheight;
3497 // find another position
3498 for (y = blockheight;y < state->height;y++)
3500 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3502 row = state->rows + y;
3506 if (y == state->height)
3511 *outx = row->currentX;
3512 row->currentX += blockwidth;
3517 typedef struct lightmapsample_s
3521 float *vertex_color;
3522 unsigned char *lm_bgr;
3523 unsigned char *lm_dir;
3527 typedef struct lightmapvertex_s
3532 float texcoordbase[2];
3533 float texcoordlightmap[2];
3534 float lightcolor[4];
3538 typedef struct lightmaptriangle_s
3546 // 2D modelspace coordinates of min corner
3547 // snapped to lightmap grid but not in grid coordinates
3549 // 2D modelspace to lightmap coordinate scale
3557 typedef struct lightmaplight_s
3568 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3570 #define MAX_LIGHTMAPSAMPLES 64
3571 static int mod_generatelightmaps_numoffsets[3];
3572 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3574 static int mod_generatelightmaps_numlights;
3575 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3577 extern cvar_t r_shadow_lightattenuationdividebias;
3578 extern cvar_t r_shadow_lightattenuationlinearscale;
3580 static void Mod_GenerateLightmaps_LightPoint(model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3585 float relativepoint[3];
3592 float lightorigin[3];
3596 float lightcolor[3];
3598 for (i = 0;i < 5*3;i++)
3600 for (index = 0;;index++)
3602 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3607 lightradius2 = lightradius * lightradius;
3608 VectorSubtract(lightorigin, pos, relativepoint);
3609 dist2 = VectorLength2(relativepoint);
3610 if (dist2 >= lightradius2)
3612 lightiradius = 1.0f / lightradius;
3613 dist = sqrt(dist2) * lightiradius;
3614 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3615 if (intensity <= 0.0f)
3617 if (model && model->TraceLine)
3619 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3620 if (trace.fraction < 1)
3623 // scale down intensity to add to both ambient and diffuse
3624 //intensity *= 0.5f;
3625 VectorNormalize(relativepoint);
3626 VectorScale(lightcolor, intensity, color);
3627 VectorMA(sample , 0.5f , color, sample );
3628 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3629 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3630 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3631 // calculate a weighted average light direction as well
3632 intensity *= VectorLength(color);
3633 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3635 // calculate the direction we'll use to reduce the sample to a directional light source
3636 VectorCopy(sample + 12, dir);
3637 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3638 VectorNormalize(dir);
3639 // extract the diffuse color along the chosen direction and scale it
3640 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3641 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3642 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3643 // subtract some of diffuse from ambient
3644 VectorMA(sample, -0.333f, diffuse, ambient);
3645 // store the normalized lightdir
3646 VectorCopy(dir, lightdir);
3649 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3653 const msurface_t *surface;
3654 const float *vertex3f = model->surfmesh.data_vertex3f;
3655 const int *element3i = model->surfmesh.data_element3i;
3658 for (surfaceindex = model->submodelsurfaces_start;surfaceindex < model->submodelsurfaces_end;surfaceindex++)
3660 surface = model->data_surfaces + surfaceindex;
3661 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3663 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3665 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3667 VectorCopy(vertex3f + 3*e[0], v2[0]);
3668 VectorCopy(vertex3f + 3*e[1], v2[1]);
3669 VectorCopy(vertex3f + 3*e[2], v2[2]);
3670 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3675 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model_t *model, lightmaplight_t *lightinfo)
3677 int maxnodes = 1<<14;
3678 svbsp_node_t *nodes;
3683 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3684 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3685 VectorCopy(lightinfo->origin, origin);
3686 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3689 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3690 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3691 if (svbsp.ranoutofnodes)
3694 if (maxnodes > 1<<22)
3700 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3705 if (svbsp.numnodes > 0)
3707 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3708 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3709 lightinfo->svbsp = svbsp;
3714 static void Mod_GenerateLightmaps_CreateLights(model_t *model)
3718 lightmaplight_t *lightinfo;
3722 mod_generatelightmaps_numlights = 0;
3723 for (index = 0;;index++)
3725 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3729 mod_generatelightmaps_numlights++;
3731 if (mod_generatelightmaps_numlights > 0)
3733 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3734 lightinfo = mod_generatelightmaps_lightinfo;
3735 for (index = 0;;index++)
3737 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3744 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3746 lightinfo->iradius = 1.0f / lightinfo->radius;
3747 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3748 // TODO: compute svbsp
3749 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3753 static void Mod_GenerateLightmaps_DestroyLights(model_t *model)
3756 if (mod_generatelightmaps_lightinfo)
3758 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3759 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3760 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3761 Mem_Free(mod_generatelightmaps_lightinfo);
3763 mod_generatelightmaps_lightinfo = NULL;
3764 mod_generatelightmaps_numlights = 0;
3767 static qbool Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3769 const svbsp_node_t *node;
3770 const svbsp_node_t *nodes = svbsp->nodes;
3775 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3777 return num == -1; // true if empty, false if solid (shadowed)
3780 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3783 float relativepoint[3];
3792 const lightmaplight_t *lightinfo;
3794 for (i = 0;i < 5*3;i++)
3796 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3798 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3799 VectorSubtract(lightinfo->origin, pos, relativepoint);
3800 // don't accept light from behind a surface, it causes bad shading
3801 if (normal && DotProduct(relativepoint, normal) <= 0)
3803 dist2 = VectorLength2(relativepoint);
3804 if (dist2 >= lightinfo->radius2)
3806 dist = sqrt(dist2) * lightinfo->iradius;
3807 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3810 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3814 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3816 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3818 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3821 // for light grid we'd better check visibility of the offset point
3822 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3823 if (trace.fraction < 1)
3824 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3827 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3832 // scale intensity according to how many rays succeeded
3833 // we know one test is valid, half of the rest will fail...
3834 //if (normal && tests > 1)
3835 // intensity *= (tests - 1.0f) / tests;
3836 intensity *= (float)hits / tests;
3838 // scale down intensity to add to both ambient and diffuse
3839 //intensity *= 0.5f;
3840 VectorNormalize(relativepoint);
3841 VectorScale(lightinfo->color, intensity, color);
3842 VectorMA(sample , 0.5f , color, sample );
3843 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3844 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3845 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3846 // calculate a weighted average light direction as well
3847 intensity *= VectorLength(color);
3848 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3852 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3858 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3859 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3860 VectorCopy(sample + 12, dir);
3861 VectorNormalize(dir);
3862 //VectorAdd(dir, normal, dir);
3863 //VectorNormalize(dir);
3864 f = DotProduct(dir, normal);
3865 f = max(0, f) * 255.0f;
3866 VectorScale(sample, f, color);
3867 //VectorCopy(normal, dir);
3868 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3869 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3870 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3871 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3873 lm_dir[0] = (unsigned char)dir[2];
3874 lm_dir[1] = (unsigned char)dir[1];
3875 lm_dir[2] = (unsigned char)dir[0];
3879 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3882 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3883 VectorCopy(sample, vertex_color);
3886 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3892 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3893 // calculate the direction we'll use to reduce the sample to a directional light source
3894 VectorCopy(sample + 12, dir);
3895 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3896 VectorNormalize(dir);
3897 // extract the diffuse color along the chosen direction and scale it
3898 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3899 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3900 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3901 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3902 VectorScale(sample, 127.5f, ambient);
3903 VectorMA(ambient, -0.333f, diffuse, ambient);
3904 // encode to the grid format
3905 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3906 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3907 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3908 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3909 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3910 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3911 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3912 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3913 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));}
3916 static void Mod_GenerateLightmaps_InitSampleOffsets(model_t *model)
3921 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3922 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3923 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3924 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3925 radius[0] = mod_generatelightmaps_lightmapradius.value;
3926 radius[1] = mod_generatelightmaps_vertexradius.value;
3927 radius[2] = mod_generatelightmaps_gridradius.value;
3928 for (i = 0;i < 3;i++)
3930 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3933 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3938 static void Mod_GenerateLightmaps_DestroyLightmaps(model_t *model)
3940 msurface_t *surface;
3943 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3945 surface = model->data_surfaces + surfaceindex;
3946 surface->lightmaptexture = NULL;
3947 surface->deluxemaptexture = NULL;
3949 if (model->brushq3.data_lightmaps)
3951 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3952 if (model->brushq3.data_lightmaps[i])
3953 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3954 Mem_Free(model->brushq3.data_lightmaps);
3955 model->brushq3.data_lightmaps = NULL;
3957 if (model->brushq3.data_deluxemaps)
3959 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3960 if (model->brushq3.data_deluxemaps[i])
3961 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3962 Mem_Free(model->brushq3.data_deluxemaps);
3963 model->brushq3.data_deluxemaps = NULL;
3967 static void Mod_GenerateLightmaps_UnweldTriangles(model_t *model)
3969 msurface_t *surface;
3975 surfmesh_t oldsurfmesh;
3977 unsigned char *data;
3978 oldsurfmesh = model->surfmesh;
3979 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3980 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3982 size += model->surfmesh.num_vertices * sizeof(float[3]);
3983 size += model->surfmesh.num_vertices * sizeof(float[3]);
3984 size += model->surfmesh.num_vertices * sizeof(float[3]);
3985 size += model->surfmesh.num_vertices * sizeof(float[3]);
3986 size += model->surfmesh.num_vertices * sizeof(float[2]);
3987 size += model->surfmesh.num_vertices * sizeof(float[2]);
3988 size += model->surfmesh.num_vertices * sizeof(float[4]);
3989 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3990 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3991 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3992 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3993 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3994 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3995 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3996 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3997 if (model->surfmesh.num_vertices > 65536)
3998 model->surfmesh.data_element3s = NULL;
4000 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
4001 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
4002 model->surfmesh.data_element3i_indexbuffer = NULL;
4003 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
4004 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
4005 model->surfmesh.data_element3s_indexbuffer = NULL;
4006 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
4007 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
4008 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
4009 model->surfmesh.data_svector3f_vertexbuffer = NULL;
4010 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
4011 model->surfmesh.data_normal3f_vertexbuffer = NULL;
4012 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
4013 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
4014 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
4015 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
4016 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
4018 // convert all triangles to unique vertex data
4020 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4022 surface = model->data_surfaces + surfaceindex;
4023 surface->num_firstvertex = outvertexindex;
4024 surface->num_vertices = surface->num_triangles*3;
4025 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4026 for (i = 0;i < surface->num_triangles*3;i++)
4029 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4030 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4031 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4032 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4033 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4034 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4035 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4036 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4037 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4038 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4039 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4040 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4041 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4042 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4043 if (oldsurfmesh.data_texcoordlightmap2f)
4045 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4046 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4048 if (oldsurfmesh.data_lightmapcolor4f)
4050 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4051 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4052 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4053 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4056 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4057 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4061 if (model->surfmesh.data_element3s)
4062 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4063 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4065 // find and update all submodels to use this new surfmesh data
4066 for (i = 0;i < model->brush.numsubmodels;i++)
4067 model->brush.submodels[i]->surfmesh = model->surfmesh;
4070 static void Mod_GenerateLightmaps_CreateTriangleInformation(model_t *model)
4072 msurface_t *surface;
4078 lightmaptriangle_t *triangle;
4079 // generate lightmap triangle structs
4080 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4081 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4083 surface = model->data_surfaces + surfaceindex;
4084 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4085 for (i = 0;i < surface->num_triangles;i++)
4087 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4088 triangle->triangleindex = surface->num_firsttriangle+i;
4089 triangle->surfaceindex = surfaceindex;
4090 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4091 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4092 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4093 // calculate bounds of triangle
4094 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4095 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4096 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4097 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4098 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4099 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4100 // pick an axial projection based on the triangle normal
4101 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4103 if (fabs(normal[1]) > fabs(normal[axis]))
4105 if (fabs(normal[2]) > fabs(normal[axis]))
4107 triangle->axis = axis;
4112 static void Mod_GenerateLightmaps_DestroyTriangleInformation(model_t *model)
4114 if (mod_generatelightmaps_lightmaptriangles)
4115 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4116 mod_generatelightmaps_lightmaptriangles = NULL;
4119 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4121 static void Mod_GenerateLightmaps_CreateLightmaps(model_t *model)
4123 msurface_t *surface;
4137 float trianglenormal[3];
4138 float samplecenter[3];
4139 float samplenormal[3];
4145 float lmscalepixels;
4148 float lm_basescalepixels;
4149 int lm_borderpixels;
4153 lightmaptriangle_t *triangle;
4154 unsigned char *lightmappixels;
4155 unsigned char *deluxemappixels;
4156 mod_alloclightmap_state_t lmstate;
4159 // generate lightmap projection information for all triangles
4160 if (model->texturepool == NULL)
4161 model->texturepool = R_AllocTexturePool();
4162 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4163 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4164 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4165 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4166 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4168 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4170 surface = model->data_surfaces + surfaceindex;
4171 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4172 lmscalepixels = lm_basescalepixels;
4173 for (retry = 0;retry < 30;retry++)
4175 // after a couple failed attempts, degrade quality to make it fit
4177 lmscalepixels *= 0.5f;
4178 for (i = 0;i < surface->num_triangles;i++)
4180 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4181 triangle->lightmapindex = lightmapnumber;
4182 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4183 // pick two planar axes for projection
4184 // lightmap coordinates here are in pixels
4185 // lightmap projections are snapped to pixel grid explicitly, such
4186 // that two neighboring triangles sharing an edge and projection
4187 // axis will have identical sample spacing along their shared edge
4189 for (j = 0;j < 3;j++)
4191 if (j == triangle->axis)
4193 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4194 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4195 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4196 triangle->lmbase[k] = lmmins/lmscalepixels;
4197 triangle->lmscale[k] = lmscalepixels;
4200 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4203 // if all fit in this texture, we're done with this surface
4204 if (i == surface->num_triangles)
4206 // if we haven't maxed out the lightmap size yet, we retry the
4207 // entire surface batch...
4208 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4210 lm_texturesize *= 2;
4213 Mod_AllocLightmap_Free(&lmstate);
4214 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4217 // if we have maxed out the lightmap size, and this triangle does
4218 // not fit in the same texture as the rest of the surface, we have
4219 // to retry the entire surface in a new texture (can only use one)
4220 // with multiple retries, the lightmap quality degrades until it
4221 // fits (or gives up)
4222 if (surfaceindex > 0)
4224 Mod_AllocLightmap_Reset(&lmstate);
4228 Mod_AllocLightmap_Free(&lmstate);
4230 // now put triangles together into lightmap textures, and do not allow
4231 // triangles of a surface to go into different textures (as that would
4232 // require rewriting the surface list)
4233 model->brushq3.deluxemapping_modelspace = true;
4234 model->brushq3.deluxemapping = true;
4235 model->brushq3.num_mergedlightmaps = lightmapnumber;
4236 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4237 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4238 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4239 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4240 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4242 surface = model->data_surfaces + surfaceindex;
4243 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4244 for (i = 0;i < surface->num_triangles;i++)
4246 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4247 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4248 VectorNormalize(trianglenormal);
4249 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4250 axis = triangle->axis;
4251 axis1 = axis == 0 ? 1 : 0;
4252 axis2 = axis == 2 ? 1 : 2;
4253 lmiscale[0] = 1.0f / triangle->lmscale[0];
4254 lmiscale[1] = 1.0f / triangle->lmscale[1];
4255 if (trianglenormal[axis] < 0)
4256 VectorNegate(trianglenormal, trianglenormal);
4257 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4258 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4259 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4260 for (j = 0;j < 3;j++)
4262 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4263 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4264 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4266 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4267 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4268 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4269 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]);
4279 forward[1] = 1.0f / triangle->lmscale[0];
4283 left[2] = 1.0f / triangle->lmscale[1];
4288 origin[1] = triangle->lmbase[0];
4289 origin[2] = triangle->lmbase[1];
4292 forward[0] = 1.0f / triangle->lmscale[0];
4297 left[2] = 1.0f / triangle->lmscale[1];
4301 origin[0] = triangle->lmbase[0];
4303 origin[2] = triangle->lmbase[1];
4306 forward[0] = 1.0f / triangle->lmscale[0];
4310 left[1] = 1.0f / triangle->lmscale[1];
4315 origin[0] = triangle->lmbase[0];
4316 origin[1] = triangle->lmbase[1];
4320 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4322 #define LM_DIST_EPSILON (1.0f / 32.0f)
4323 for (y = 0;y < triangle->lmsize[1];y++)
4325 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4326 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4328 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4329 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4330 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4331 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4332 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4338 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4340 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);
4341 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);
4345 Mem_Free(lightmappixels);
4346 if (deluxemappixels)
4347 Mem_Free(deluxemappixels);
4349 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4351 surface = model->data_surfaces + surfaceindex;
4352 if (!surface->num_triangles)
4354 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4355 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4356 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4357 surface->lightmapinfo = NULL;
4360 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4361 model->brushq1.lightdata = NULL;
4362 model->brushq1.lightmapupdateflags = NULL;
4363 model->brushq1.firstrender = false;
4364 model->brushq1.num_lightstyles = 0;
4365 model->brushq1.data_lightstyleinfo = NULL;
4366 for (i = 0;i < model->brush.numsubmodels;i++)
4368 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4369 model->brush.submodels[i]->brushq1.firstrender = false;
4370 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4371 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4375 static void Mod_GenerateLightmaps_UpdateVertexColors(model_t *model)
4378 for (i = 0;i < model->surfmesh.num_vertices;i++)
4379 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4382 static void Mod_GenerateLightmaps_UpdateLightGrid(model_t *model)
4389 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4391 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4392 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4394 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4395 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4397 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4398 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4404 extern cvar_t mod_q3bsp_nolightmaps;
4405 static void Mod_GenerateLightmaps(model_t *model)
4407 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4408 model_t *oldloadmodel = loadmodel;
4411 Mod_GenerateLightmaps_InitSampleOffsets(model);
4412 Mod_GenerateLightmaps_DestroyLightmaps(model);
4413 Mod_GenerateLightmaps_UnweldTriangles(model);
4414 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4415 Mod_GenerateLightmaps_CreateLights(model);
4416 if(!mod_q3bsp_nolightmaps.integer)
4417 Mod_GenerateLightmaps_CreateLightmaps(model);
4418 Mod_GenerateLightmaps_UpdateVertexColors(model);
4419 Mod_GenerateLightmaps_UpdateLightGrid(model);
4420 Mod_GenerateLightmaps_DestroyLights(model);
4421 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4423 loadmodel = oldloadmodel;
4426 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4428 if (Cmd_Argc(cmd) != 1)
4430 Con_Printf("usage: mod_generatelightmaps\n");
4435 Con_Printf("no worldmodel loaded\n");
4438 Mod_GenerateLightmaps(cl.worldmodel);
4441 void Mod_Mesh_Create(model_t *mod, const char *name)
4443 memset(mod, 0, sizeof(*mod));
4444 strlcpy(mod->name, name, sizeof(mod->name));
4445 mod->mempool = Mem_AllocPool(name, 0, NULL);
4446 mod->texturepool = R_AllocTexturePool();
4447 mod->Draw = R_Mod_Draw;
4448 mod->DrawDepth = R_Mod_DrawDepth;
4449 mod->DrawDebug = R_Mod_DrawDebug;
4450 mod->DrawPrepass = R_Mod_DrawPrepass;
4451 mod->GetLightInfo = R_Mod_GetLightInfo;
4452 mod->DrawShadowMap = R_Mod_DrawShadowMap;
4453 mod->DrawLight = R_Mod_DrawLight;
4456 void Mod_Mesh_Destroy(model_t *mod)
4458 Mod_UnloadModel(mod);
4461 // resets the mesh model to have no geometry to render, ready for a new frame -
4462 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4463 void Mod_Mesh_Reset(model_t *mod)
4465 mod->num_surfaces = 0;
4466 mod->surfmesh.num_vertices = 0;
4467 mod->surfmesh.num_triangles = 0;
4468 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4469 mod->DrawSky = NULL; // will be set if a texture needs it
4470 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4473 texture_t *Mod_Mesh_GetTexture(model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4477 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4478 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4479 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4481 if (mod->max_textures <= mod->num_textures)
4483 texture_t *oldtextures = mod->data_textures;
4484 mod->max_textures = max(mod->max_textures * 2, 1024);
4485 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4486 // update the pointers
4487 for (i = 0; i < mod->num_surfaces; i++)
4488 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4490 t = &mod->data_textures[mod->num_textures++];
4491 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4492 t->mesh_drawflag = drawflag;
4493 t->mesh_defaulttexflags = defaulttexflags;
4494 t->mesh_defaultmaterialflags = defaultmaterialflags;
4495 switch (defaultdrawflags & DRAWFLAG_MASK)
4497 case DRAWFLAG_ADDITIVE:
4498 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4499 t->currentmaterialflags = t->basematerialflags;
4501 case DRAWFLAG_MODULATE:
4502 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4503 t->currentmaterialflags = t->basematerialflags;
4504 t->customblendfunc[0] = GL_DST_COLOR;
4505 t->customblendfunc[1] = GL_ZERO;
4507 case DRAWFLAG_2XMODULATE:
4508 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4509 t->currentmaterialflags = t->basematerialflags;
4510 t->customblendfunc[0] = GL_DST_COLOR;
4511 t->customblendfunc[1] = GL_SRC_COLOR;
4513 case DRAWFLAG_SCREEN:
4514 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4515 t->currentmaterialflags = t->basematerialflags;
4516 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4517 t->customblendfunc[1] = GL_ONE;
4525 msurface_t *Mod_Mesh_AddSurface(model_t *mod, texture_t *tex, qbool batchwithprevioussurface)
4528 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4529 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4530 return mod->data_surfaces + mod->num_surfaces - 1;
4531 // create new surface
4532 if (mod->max_surfaces == mod->num_surfaces)
4534 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4535 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4536 mod->modelsurfaces_sorted = (int *)Mem_Realloc(mod->mempool, mod->modelsurfaces_sorted, mod->max_surfaces * sizeof(*mod->modelsurfaces_sorted));
4538 surf = mod->data_surfaces + mod->num_surfaces;
4539 mod->num_surfaces++;
4540 memset(surf, 0, sizeof(*surf));
4541 surf->texture = tex;
4542 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4543 surf->num_firstvertex = mod->surfmesh.num_vertices;
4544 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4545 mod->DrawSky = R_Mod_DrawSky;
4546 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4547 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
4551 static void Mod_Mesh_RebuildHashTable(model_t *mod, msurface_t *surf)
4553 int hashindex, h, vnum, mask;
4554 surfmesh_t *mesh = &mod->surfmesh;
4556 // rebuild the hash table
4557 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4558 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4559 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4560 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4561 mask = mod->surfmesh.num_vertexhashsize - 1;
4562 // no need to hash the vertices for the entire model, the latest surface will suffice.
4563 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4565 // this uses prime numbers intentionally for computing the hash
4566 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;
4567 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4568 ; // just iterate until we find the terminator
4569 mesh->data_vertexhash[h] = vnum;
4573 void Mod_Mesh_CheckResize_Vertex(model_t *mod, msurface_t *surf)
4575 surfmesh_t *mesh = &mod->surfmesh;
4576 if (mesh->max_vertices == mesh->num_vertices)
4578 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4579 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4580 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4581 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4582 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4583 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4584 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4585 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4586 Mod_Mesh_RebuildHashTable(mod, surf);
4590 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)
4593 surfmesh_t *mesh = &mod->surfmesh;
4595 // add the new vertex
4596 vnum = mesh->num_vertices++;
4597 if (surf->num_vertices > 0)
4599 if (surf->mins[0] > x) surf->mins[0] = x;
4600 if (surf->mins[1] > y) surf->mins[1] = y;
4601 if (surf->mins[2] > z) surf->mins[2] = z;
4602 if (surf->maxs[0] < x) surf->maxs[0] = x;
4603 if (surf->maxs[1] < y) surf->maxs[1] = y;
4604 if (surf->maxs[2] < z) surf->maxs[2] = z;
4608 VectorSet(surf->mins, x, y, z);
4609 VectorSet(surf->maxs, x, y, z);
4611 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4612 mesh->data_vertex3f[vnum * 3 + 0] = x;
4613 mesh->data_vertex3f[vnum * 3 + 1] = y;
4614 mesh->data_vertex3f[vnum * 3 + 2] = z;
4615 mesh->data_normal3f[vnum * 3 + 0] = nx;
4616 mesh->data_normal3f[vnum * 3 + 1] = ny;
4617 mesh->data_normal3f[vnum * 3 + 2] = nz;
4618 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4619 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4620 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4621 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4622 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4623 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4624 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4625 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4629 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)
4631 int hashindex, h, vnum, mask;
4632 surfmesh_t *mesh = &mod->surfmesh;
4634 Mod_Mesh_CheckResize_Vertex(mod, surf);
4636 mask = mod->surfmesh.num_vertexhashsize - 1;
4637 // this uses prime numbers intentionally for computing the hash
4638 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4639 // when possible find an identical vertex within the same surface and return it
4640 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4642 if (vnum >= surf->num_firstvertex
4643 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4644 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4645 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4646 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4647 && 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)
4650 vnum = Mod_Mesh_AddVertex(mod, surf, x, y, z, nx, ny, nz, s, t, u, v, r, g, b, a);
4651 mesh->data_vertexhash[h] = vnum;
4655 void Mod_Mesh_AddTriangle(model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4657 surfmesh_t *mesh = &mod->surfmesh;
4658 if (mesh->max_triangles == mesh->num_triangles)
4660 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4661 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4662 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4664 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4665 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4666 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4667 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4668 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4669 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4670 mesh->num_triangles++;
4671 surf->num_triangles++;
4674 static void Mod_Mesh_MakeSortedSurfaces(model_t *mod)
4678 unsigned char* included = (unsigned char *)R_FrameData_Alloc(mod->num_surfaces * sizeof(unsigned char));
4680 // build the sorted surfaces list properly to reduce material setup
4681 // this is easy because we're just sorting on texture and don't care about the order of textures
4682 mod->submodelsurfaces_start = 0;
4683 mod->submodelsurfaces_end = 0;
4684 for (i = 0; i < mod->num_surfaces; i++)
4686 for (i = 0; i < mod->num_surfaces; i++)
4690 tex = mod->data_surfaces[i].texture;
4691 // j = i is intentional
4692 for (j = i; j < mod->num_surfaces; j++)
4694 if (!included[j] && mod->data_surfaces[j].texture == tex)
4697 mod->modelsurfaces_sorted[mod->submodelsurfaces_end++] = j;
4703 static void Mod_Mesh_ComputeBounds(model_t *mod)
4706 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4708 if (mod->surfmesh.num_vertices > 0)
4710 // calculate normalmins/normalmaxs
4711 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4712 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4713 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4715 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4716 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4717 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4718 // expand bounds to include this vertex
4719 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4720 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4721 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4722 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4723 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4724 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4726 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4727 // (fast but less accurate than doing it per vertex)
4728 x2a = mod->normalmins[0] * mod->normalmins[0];
4729 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4730 y2a = mod->normalmins[1] * mod->normalmins[1];
4731 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4732 z2a = mod->normalmins[2] * mod->normalmins[2];
4733 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4737 yawradius = sqrt(x2 + y2);
4738 rotatedradius = sqrt(x2 + y2 + z2);
4739 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4740 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4741 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4742 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4743 mod->radius = rotatedradius;
4744 mod->radius2 = x2 + y2 + z2;
4748 VectorClear(mod->normalmins);
4749 VectorClear(mod->normalmaxs);
4750 VectorClear(mod->yawmins);
4751 VectorClear(mod->yawmaxs);
4752 VectorClear(mod->rotatedmins);
4753 VectorClear(mod->rotatedmaxs);
4759 void Mod_Mesh_Validate(model_t *mod)
4762 qbool warned = false;
4763 for (i = 0; i < mod->num_surfaces; i++)
4765 msurface_t *surf = mod->data_surfaces + i;
4766 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4767 int first = surf->num_firstvertex;
4768 int end = surf->num_firstvertex + surf->num_vertices;
4770 for (j = 0;j < surf->num_triangles * 3;j++)
4772 if (e[j] < first || e[j] >= end)
4775 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4783 static void Mod_Mesh_UploadDynamicBuffers(model_t *mod)
4785 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;
4786 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;
4787 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;
4788 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;
4789 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;
4790 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;
4791 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;
4792 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;
4793 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;
4794 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;
4795 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;
4798 void Mod_Mesh_Finalize(model_t *mod)
4800 if (gl_paranoid.integer)
4801 Mod_Mesh_Validate(mod);
4802 Mod_Mesh_ComputeBounds(mod);
4803 Mod_Mesh_MakeSortedSurfaces(mod);
4804 if(!r_refdef.draw2dstage)
4805 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);
4806 Mod_Mesh_UploadDynamicBuffers(mod);