2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // models.c -- model loading and caching
22 // models are the only shared resource between a client and server running
23 // on the same machine.
30 cvar_t r_mipskins = {CF_CLIENT | CF_ARCHIVE, "r_mipskins", "0", "mipmaps model skins so they render faster in the distance and do not display noise artifacts, can cause discoloration of skins if they contain undesirable border colors"};
31 cvar_t r_mipnormalmaps = {CF_CLIENT | CF_ARCHIVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CF_CLIENT | CF_ARCHIVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
44 // Supported model formats
45 static modloader_t loader[] =
47 {"obj", NULL, 0, Mod_OBJ_Load},
48 {NULL, "IDPO", 4, Mod_IDP0_Load},
49 {NULL, "IDP2", 4, Mod_IDP2_Load},
50 {NULL, "IDP3", 4, Mod_IDP3_Load},
51 {NULL, "IDSP", 4, Mod_IDSP_Load},
52 {NULL, "IDS2", 4, Mod_IDS2_Load},
53 {NULL, "\035", 1, Mod_Q1BSP_Load},
54 {NULL, "\036", 1, Mod_HLBSP_Load},
55 {NULL, "BSP2", 4, Mod_BSP2_Load},
56 {NULL, "2PSB", 4, Mod_2PSB_Load},
57 {NULL, "IBSP", 4, Mod_IBSP_Load},
58 {NULL, "VBSP", 4, Mod_VBSP_Load},
59 {NULL, "ZYMOTICMODEL", 13, Mod_ZYMOTICMODEL_Load},
60 {NULL, "DARKPLACESMODEL", 16, Mod_DARKPLACESMODEL_Load},
61 {NULL, "PSKMODEL", 9, Mod_PSKMODEL_Load},
62 {NULL, "INTERQUAKEMODEL", 16, Mod_INTERQUAKEMODEL_Load},
63 {"map", NULL, 0, Mod_MAP_Load},
67 static mempool_t *mod_mempool;
68 static memexpandablearray_t models;
70 static mempool_t* q3shaders_mem;
71 typedef struct q3shader_hash_entry_s
74 struct q3shader_hash_entry_s* chain;
75 } q3shader_hash_entry_t;
76 #define Q3SHADER_HASH_SIZE 1021
77 typedef struct q3shader_data_s
79 memexpandablearray_t hash_entries;
80 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
81 memexpandablearray_t char_ptrs;
83 static q3shader_data_t* q3shader_data;
85 static void mod_start(void)
88 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
91 SCR_PushLoadingScreen("Loading models", 1.0);
93 for (i = 0;i < nummodels;i++)
94 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
97 for (i = 0;i < nummodels;i++)
98 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
101 SCR_PushLoadingScreen(mod->name, 1.0 / count);
102 Mod_LoadModel(mod, true, false);
103 SCR_PopLoadingScreen(false);
105 SCR_PopLoadingScreen(false);
108 static void mod_shutdown(void)
111 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
114 for (i = 0;i < nummodels;i++)
115 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
116 Mod_UnloadModel(mod);
119 Mod_Skeletal_FreeBuffers();
122 static void mod_newmap(void)
125 int i, j, k, l, surfacenum, ssize, tsize;
126 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
129 for (i = 0;i < nummodels;i++)
131 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
133 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
135 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
136 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
137 if (mod->data_textures[j].shaderpasses[l])
138 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
139 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
141 if (mod->brush.solidskyskinframe)
142 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
143 if (mod->brush.alphaskyskinframe)
144 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
148 if (!cl_stainmaps_clearonload.integer)
151 for (i = 0;i < nummodels;i++)
153 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
155 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
157 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
159 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
160 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
161 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
162 mod->brushq1.lightmapupdateflags[surfacenum] = true;
174 static void Mod_Print_f(cmd_state_t *cmd);
175 static void Mod_Precache_f(cmd_state_t *cmd);
176 static void Mod_Decompile_f(cmd_state_t *cmd);
177 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
180 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
181 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(model_t), 16);
187 Cvar_RegisterVariable(&r_mipskins);
188 Cvar_RegisterVariable(&r_mipnormalmaps);
189 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
190 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
191 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
193 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
194 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
195 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
196 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
197 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
198 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
200 Cmd_AddCommand(CF_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
201 Cmd_AddCommand(CF_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
202 Cmd_AddCommand(CF_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
203 Cmd_AddCommand(CF_CLIENT, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
206 void Mod_RenderInit(void)
208 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
211 void Mod_UnloadModel (model_t *mod)
213 char name[MAX_QPATH];
215 model_t *parentmodel;
217 if (developer_loading.integer)
218 Con_Printf("unloading model %s\n", mod->name);
220 dp_strlcpy(name, mod->name, sizeof(name));
221 parentmodel = mod->brush.parentmodel;
225 if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
226 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
227 mod->surfmesh.data_element3i_indexbuffer = NULL;
228 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
229 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
230 mod->surfmesh.data_element3s_indexbuffer = NULL;
231 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
232 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
233 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
234 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
235 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
236 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
237 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
238 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
239 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
240 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
241 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
243 // free textures/memory attached to the model
244 R_FreeTexturePool(&mod->texturepool);
245 Mem_FreePool(&mod->mempool);
246 // clear the struct to make it available
247 memset(mod, 0, sizeof(model_t));
248 // restore the fields we want to preserve
249 dp_strlcpy(mod->name, name, sizeof(mod->name));
250 mod->brush.parentmodel = parentmodel;
255 static void R_Model_Null_Draw(entity_render_t *ent)
261 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass);
263 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
278 // REQUIRED: fetch start
279 COM_ParseToken_Simple(&bufptr, true, false, true);
281 break; // end of file
282 if (!strcmp(com_token, "\n"))
283 continue; // empty line
284 start = atoi(com_token);
286 // REQUIRED: fetch length
287 COM_ParseToken_Simple(&bufptr, true, false, true);
288 if (!bufptr || !strcmp(com_token, "\n"))
290 Con_Printf("framegroups file: missing number of frames\n");
293 len = atoi(com_token);
295 // OPTIONAL args start
296 COM_ParseToken_Simple(&bufptr, true, false, true);
298 // OPTIONAL: fetch fps
300 if (bufptr && strcmp(com_token, "\n"))
302 fps = atof(com_token);
303 COM_ParseToken_Simple(&bufptr, true, false, true);
306 // OPTIONAL: fetch loopflag
308 if (bufptr && strcmp(com_token, "\n"))
310 loop = (atoi(com_token) != 0);
311 COM_ParseToken_Simple(&bufptr, true, false, true);
314 // OPTIONAL: fetch name
316 if (bufptr && strcmp(com_token, "\n"))
318 dp_strlcpy(name, com_token, sizeof(name));
319 COM_ParseToken_Simple(&bufptr, true, false, true);
322 // OPTIONAL: remaining unsupported tokens (eat them)
323 while (bufptr && strcmp(com_token, "\n"))
324 COM_ParseToken_Simple(&bufptr, true, false, true);
326 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
329 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
336 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qbool loop, const char *name, void *pass)
338 model_t *mod = (model_t *) pass;
339 animscene_t *anim = &mod->animscenes[i];
341 dp_strlcpy(anim->name, name, sizeof(anim[i].name));
343 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
344 anim->firstframe = bound(0, start, mod->num_poses - 1);
345 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
346 anim->framerate = max(1, fps);
348 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
351 static void Mod_FrameGroupify(model_t *mod, const char *buf)
356 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
359 Con_Printf("no scene found in framegroups file, aborting\n");
362 mod->numframes = cnt;
365 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
366 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
369 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
372 static void Mod_FindPotentialDeforms(model_t *mod)
376 mod->wantnormals = false;
377 mod->wanttangents = false;
378 for (i = 0;i < mod->num_textures;i++)
380 texture = mod->data_textures + i;
381 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
382 mod->wantnormals = true;
383 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
384 mod->wantnormals = true;
385 for (j = 0;j < Q3MAXDEFORMS;j++)
387 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
389 mod->wanttangents = true;
390 mod->wantnormals = true;
393 if (texture->deforms[j].deform != Q3DEFORM_NONE)
394 mod->wantnormals = true;
406 model_t *Mod_LoadModel(model_t *mod, qbool crash, qbool checkdisk)
410 fs_offset_t filesize = 0;
415 if (mod->name[0] == '*') // submodel
418 if (!strcmp(mod->name, "null"))
423 if (mod->loaded || mod->mempool)
424 Mod_UnloadModel(mod);
426 if (developer_loading.integer)
427 Con_Printf("loading model %s\n", mod->name);
430 mod->crc = (unsigned int)-1;
433 VectorClear(mod->normalmins);
434 VectorClear(mod->normalmaxs);
435 VectorClear(mod->yawmins);
436 VectorClear(mod->yawmaxs);
437 VectorClear(mod->rotatedmins);
438 VectorClear(mod->rotatedmaxs);
440 mod->modeldatatypestring = "null";
441 mod->type = mod_null;
442 mod->Draw = R_Model_Null_Draw;
446 // no fatal errors occurred, so this model is ready to use.
455 // even if the model is loaded it still may need reloading...
457 // if it is not loaded or checkdisk is true we need to calculate the crc
458 if (!mod->loaded || checkdisk)
460 if (checkdisk && mod->loaded)
461 Con_DPrintf("checking model %s\n", mod->name);
462 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
465 crc = CRC_Block((unsigned char *)buf, filesize);
466 // we need to reload the model if the crc does not match
472 // if the model is already loaded and checks passed, just return
480 if (developer_loading.integer)
481 Con_Printf("loading model %s\n", mod->name);
483 SCR_PushLoadingScreen(mod->name, 1);
485 // LadyHavoc: unload the existing model in this slot (if there is one)
486 if (mod->loaded || mod->mempool)
487 Mod_UnloadModel(mod);
492 // errors can prevent the corresponding mod->loaded = true;
495 // default lightmap scale
496 mod->lightmapscale = 1;
498 // default model radius and bounding box (mainly for missing models)
500 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
501 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
502 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
503 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
504 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
505 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
509 // load q3 shaders for the first time, or after a level change
516 const char *ext = FS_FileExtension(mod->name);
517 char *bufend = (char *)buf + filesize;
518 // all models use memory, so allocate a memory pool
519 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
521 // We need to have a reference to the base model in case we're parsing submodels
524 // Call the appropriate loader. Try matching magic bytes.
525 for (i = 0; loader[i].Load; i++)
527 // Headerless formats can just load based on extension. Otherwise match the magic string.
528 if((loader[i].extension && !strcasecmp(ext, loader[i].extension) && !loader[i].header) ||
529 (loader[i].header && !memcmp(buf, loader[i].header, loader[i].headersize)))
532 loader[i].Load(mod, buf, bufend);
535 Mod_FindPotentialDeforms(mod);
537 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
540 Mod_FrameGroupify(mod, (const char *)buf);
544 Mod_SetDrawSkyAndWater(mod);
550 Con_Printf(CON_ERROR "Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
553 // LadyHavoc: Sys_Error was *ANNOYING*
554 Con_Printf (CON_ERROR "Mod_LoadModel: %s not found\n", mod->name);
556 // no fatal errors occurred, so this model is ready to use.
559 SCR_PopLoadingScreen(false);
564 void Mod_ClearUsed(void)
567 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
569 for (i = 0;i < nummodels;i++)
570 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
574 void Mod_PurgeUnused(void)
577 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
579 for (i = 0;i < nummodels;i++)
581 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
583 Mod_UnloadModel(mod);
584 Mem_ExpandableArray_FreeRecord(&models, mod);
595 model_t *Mod_FindName(const char *name, const char *parentname)
604 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
607 Host_Error ("Mod_ForName: empty name");
609 // search the currently loaded models
610 for (i = 0;i < nummodels;i++)
612 if ((mod = (model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name) && ((!mod->brush.parentmodel && !parentname[0]) || (mod->brush.parentmodel && parentname[0] && !strcmp(mod->brush.parentmodel->name, parentname))))
619 // no match found, create a new one
620 mod = (model_t *) Mem_ExpandableArray_AllocRecord(&models);
621 dp_strlcpy(mod->name, name, sizeof(mod->name));
623 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
625 mod->brush.parentmodel = NULL;
631 extern qbool vid_opened;
637 Loads in a model for the given name
640 model_t *Mod_ForName(const char *name, qbool crash, qbool checkdisk, const char *parentname)
644 // FIXME: So we don't crash if a server is started early.
648 model = Mod_FindName(name, parentname);
649 if (!model->loaded || checkdisk)
650 Mod_LoadModel(model, crash, checkdisk);
658 Reloads all models if they have changed
661 void Mod_Reload(void)
664 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
667 SCR_PushLoadingScreen("Reloading models", 1.0);
669 for (i = 0;i < nummodels;i++)
670 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
672 for (i = 0;i < nummodels;i++)
673 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
675 SCR_PushLoadingScreen(mod->name, 1.0 / count);
676 Mod_LoadModel(mod, true, true);
677 SCR_PopLoadingScreen(false);
679 SCR_PopLoadingScreen(false);
682 unsigned char *mod_base;
685 //=============================================================================
692 static void Mod_Print_f(cmd_state_t *cmd)
695 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
698 Con_Print("Loaded models:\n");
699 for (i = 0;i < nummodels;i++)
701 if ((mod = (model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
703 if (mod->brush.numsubmodels)
704 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
706 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
716 static void Mod_Precache_f(cmd_state_t *cmd)
718 if (Cmd_Argc(cmd) == 2)
719 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
721 Con_Print("usage: modelprecache <filename>\n");
724 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
728 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
729 memset(used, 0, numvertices);
730 for (i = 0;i < numelements;i++)
731 used[elements[i]] = 1;
732 for (i = 0, count = 0;i < numvertices;i++)
733 remapvertices[i] = used[i] ? count++ : -1;
738 qbool Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
740 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
742 int invalidintcount = 0, invalidintexample = 0;
743 int invalidshortcount = 0, invalidshortexample = 0;
744 int invalidmismatchcount = 0, invalidmismatchexample = 0;
747 for (i = 0; i < numelements; i++)
749 if (element3i[i] < first || element3i[i] > last)
752 invalidintexample = i;
758 for (i = 0; i < numelements; i++)
760 if (element3s[i] < first || element3s[i] > last)
763 invalidintexample = i;
767 if (element3i && element3s)
769 for (i = 0; i < numelements; i++)
771 if (element3s[i] != element3i[i])
773 invalidmismatchcount++;
774 invalidmismatchexample = i;
778 if (invalidintcount || invalidshortcount || invalidmismatchcount)
780 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, (void *)element3i, (void *)element3s, filename, fileline);
781 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
782 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
783 Con_Printf(", %i elements mismatch between element3i and element3s (example: element3s[%i] is %i and element3i[%i] is %i)", invalidmismatchcount, invalidmismatchexample, element3s ? element3s[invalidmismatchexample] : 0, invalidmismatchexample, element3i ? element3i[invalidmismatchexample] : 0);
784 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
786 // edit the elements to make them safer, as the driver will crash otherwise
788 for (i = 0; i < numelements; i++)
789 if (element3i[i] < first || element3i[i] > last)
790 element3i[i] = first;
792 for (i = 0; i < numelements; i++)
793 if (element3s[i] < first || element3s[i] > last)
794 element3s[i] = first;
795 if (element3i && element3s)
796 for (i = 0; i < numelements; i++)
797 if (element3s[i] != element3i[i])
798 element3s[i] = element3i[i];
805 // warning: this is an expensive function!
806 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qbool areaweighting)
813 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
814 // process each vertex of each triangle and accumulate the results
815 // use area-averaging, to make triangles with a big area have a bigger
816 // weighting on the vertex normal than triangles with a small area
817 // to do so, just add the 'normals' together (the bigger the area
818 // the greater the length of the normal is
820 for (i = 0; i < numtriangles; i++, element += 3)
823 vertex3f + element[0] * 3,
824 vertex3f + element[1] * 3,
825 vertex3f + element[2] * 3,
830 VectorNormalize(areaNormal);
832 for (j = 0;j < 3;j++)
834 vectorNormal = normal3f + element[j] * 3;
835 vectorNormal[0] += areaNormal[0];
836 vectorNormal[1] += areaNormal[1];
837 vectorNormal[2] += areaNormal[2];
840 // and just normalize the accumulated vertex normal in the end
841 vectorNormal = normal3f + 3 * firstvertex;
842 for (i = 0; i < numvertices; i++, vectorNormal += 3)
843 VectorNormalize(vectorNormal);
847 static void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, const float *tc0, const float *tc1, const float *tc2, float *svector3f, float *tvector3f, float *normal3f)
849 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
850 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
851 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
853 // 6 multiply, 9 subtract
854 VectorSubtract(v1, v0, v10);
855 VectorSubtract(v2, v0, v20);
856 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
857 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
858 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
859 // 12 multiply, 10 subtract
860 tc10[1] = tc1[1] - tc0[1];
861 tc20[1] = tc2[1] - tc0[1];
862 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
863 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
864 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
865 tc10[0] = tc1[0] - tc0[0];
866 tc20[0] = tc2[0] - tc0[0];
867 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
868 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
869 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
870 // 12 multiply, 4 add, 6 subtract
871 f = DotProduct(svector3f, normal3f);
872 svector3f[0] -= f * normal3f[0];
873 svector3f[1] -= f * normal3f[1];
874 svector3f[2] -= f * normal3f[2];
875 f = DotProduct(tvector3f, normal3f);
876 tvector3f[0] -= f * normal3f[0];
877 tvector3f[1] -= f * normal3f[1];
878 tvector3f[2] -= f * normal3f[2];
879 // if texture is mapped the wrong way (counterclockwise), the tangents
880 // have to be flipped, this is detected by calculating a normal from the
881 // two tangents, and seeing if it is opposite the surface normal
882 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
883 CrossProduct(tvector3f, svector3f, tangentcross);
884 if (DotProduct(tangentcross, normal3f) < 0)
886 VectorNegate(svector3f, svector3f);
887 VectorNegate(tvector3f, tvector3f);
892 // warning: this is a very expensive function!
893 void Mod_BuildTextureVectorsFromNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const float *texcoord2f, const float *normal3f, const int *elements, float *svector3f, float *tvector3f, qbool areaweighting)
896 float sdir[3], tdir[3], normal[3], *svec, *tvec;
897 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
898 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
901 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
902 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
903 // process each vertex of each triangle and accumulate the results
904 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
906 v0 = vertex3f + e[0] * 3;
907 v1 = vertex3f + e[1] * 3;
908 v2 = vertex3f + e[2] * 3;
909 tc0 = texcoord2f + e[0] * 2;
910 tc1 = texcoord2f + e[1] * 2;
911 tc2 = texcoord2f + e[2] * 2;
913 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
914 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
916 // calculate the edge directions and surface normal
917 // 6 multiply, 9 subtract
918 VectorSubtract(v1, v0, v10);
919 VectorSubtract(v2, v0, v20);
920 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
921 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
922 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
924 // calculate the tangents
925 // 12 multiply, 10 subtract
926 tc10[1] = tc1[1] - tc0[1];
927 tc20[1] = tc2[1] - tc0[1];
928 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
929 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
930 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
931 tc10[0] = tc1[0] - tc0[0];
932 tc20[0] = tc2[0] - tc0[0];
933 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
934 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
935 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
937 // if texture is mapped the wrong way (counterclockwise), the tangents
938 // have to be flipped, this is detected by calculating a normal from the
939 // two tangents, and seeing if it is opposite the surface normal
940 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
941 CrossProduct(tdir, sdir, tangentcross);
942 if (DotProduct(tangentcross, normal) < 0)
944 VectorNegate(sdir, sdir);
945 VectorNegate(tdir, tdir);
950 VectorNormalize(sdir);
951 VectorNormalize(tdir);
953 for (i = 0;i < 3;i++)
955 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
956 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
959 // make the tangents completely perpendicular to the surface normal, and
960 // then normalize them
961 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
962 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
964 f = -DotProduct(svec, n);
965 VectorMA(svec, f, n, svec);
966 VectorNormalize(svec);
967 f = -DotProduct(tvec, n);
968 VectorMA(tvec, f, n, tvec);
969 VectorNormalize(tvec);
973 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qbool lightmapoffsets, qbool vertexcolors)
976 data = (unsigned char *)Mem_Alloc(mempool, numvertices * (3 + 3 + 3 + 3 + 2 + 2 + (vertexcolors ? 4 : 0)) * sizeof(float) + numvertices * (lightmapoffsets ? 1 : 0) * sizeof(int) + numtriangles * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0));
977 loadmodel->surfmesh.num_vertices = numvertices;
978 loadmodel->surfmesh.num_triangles = numtriangles;
979 if (loadmodel->surfmesh.num_vertices)
981 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
982 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
983 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
984 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
985 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
986 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
988 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
990 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
992 if (loadmodel->surfmesh.num_triangles)
994 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
995 if (loadmodel->surfmesh.num_vertices <= 65536)
996 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
1000 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
1002 shadowmesh_t *newmesh;
1003 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
1004 newmesh->mempool = mempool;
1005 newmesh->maxverts = maxverts;
1006 newmesh->maxtriangles = maxtriangles;
1007 newmesh->numverts = 0;
1008 newmesh->numtriangles = 0;
1009 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1010 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1012 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
1013 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
1014 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
1015 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
1019 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
1021 int hashindex, vnum;
1022 shadowmeshvertexhash_t *hash;
1023 // this uses prime numbers intentionally
1024 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
1025 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1027 vnum = (hash - mesh->vertexhashentries);
1028 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1029 return hash - mesh->vertexhashentries;
1031 vnum = mesh->numverts++;
1032 hash = mesh->vertexhashentries + vnum;
1033 hash->next = mesh->vertexhashtable[hashindex];
1034 mesh->vertexhashtable[hashindex] = hash;
1035 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1036 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1037 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1041 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1045 for (i = 0;i < numtris;i++)
1047 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 dp_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 dp_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 dp_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 dp_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 dp_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 dp_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 dp_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++)
2260 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2264 qbool Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qbool warnmissing, qbool fallback, int defaulttexflags, int defaultmaterialflags)
2266 int texflagsmask, texflagsor;
2267 qbool success = true;
2271 dp_strlcpy(texture->name, name, sizeof(texture->name));
2272 texture->basealpha = 1.0f;
2273 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2275 // allow disabling of picmip or compression by defaulttexflags
2277 if(!(defaulttexflags & TEXF_PICMIP))
2278 texflagsmask &= ~TEXF_PICMIP;
2279 if(!(defaulttexflags & TEXF_COMPRESS))
2280 texflagsmask &= ~TEXF_COMPRESS;
2282 if(defaulttexflags & TEXF_ISWORLD)
2283 texflagsor |= TEXF_ISWORLD;
2284 if(defaulttexflags & TEXF_ISSPRITE)
2285 texflagsor |= TEXF_ISSPRITE;
2286 // unless later loaded from the shader
2287 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2288 texture->offsetscale = 1;
2289 texture->offsetbias = 0;
2290 texture->specularscalemod = 1;
2291 texture->specularpowermod = 1;
2292 texture->rtlightambient = 0;
2293 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2294 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2295 // JUST GREP FOR "specularscalemod = 1".
2299 if (developer_loading.integer)
2300 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2302 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2304 texture->basematerialflags = MATERIALFLAG_SKY;
2305 if (shader->skyboxname[0] && loadmodel)
2307 // quake3 seems to append a _ to the skybox name, so this must do so as well
2308 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2311 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2312 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2314 texture->basematerialflags = MATERIALFLAG_WALL;
2316 if (shader->layers[0].alphatest)
2317 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2318 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2319 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2320 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2322 texture->biaspolygonoffset += shader->biaspolygonoffset;
2323 texture->biaspolygonfactor += shader->biaspolygonfactor;
2325 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2326 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2327 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2328 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2329 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2330 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2331 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2332 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2333 texture->customblendfunc[0] = GL_ONE;
2334 texture->customblendfunc[1] = GL_ZERO;
2335 texture->transparentsort = shader->transparentsort;
2336 if (shader->numlayers > 0)
2338 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2339 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2341 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2342 * additive GL_ONE GL_ONE
2343 additive weird GL_ONE GL_SRC_ALPHA
2344 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2345 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2346 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2347 brighten GL_DST_COLOR GL_ONE
2348 brighten GL_ONE GL_SRC_COLOR
2349 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2350 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2351 * modulate GL_DST_COLOR GL_ZERO
2352 * modulate GL_ZERO GL_SRC_COLOR
2353 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2354 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2355 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2356 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2357 * no blend GL_ONE GL_ZERO
2358 nothing GL_ZERO GL_ONE
2360 // if not opaque, figure out what blendfunc to use
2361 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2363 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2364 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2365 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2366 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2367 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2368 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2370 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2373 if (!shader->lighting)
2374 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2376 // here be dragons: convert quake3 shaders to material
2377 if (shader->numlayers > 0)
2380 int terrainbackgroundlayer = -1;
2381 int lightmaplayer = -1;
2382 int alphagenspecularlayer = -1;
2383 int rgbgenvertexlayer = -1;
2384 int rgbgendiffuselayer = -1;
2385 int materiallayer = -1;
2386 int endofprelayers = 0;
2387 int firstpostlayer = 0;
2388 int shaderpassindex = 0;
2389 for (i = 0; i < shader->numlayers; i++)
2391 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2393 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2394 rgbgenvertexlayer = i;
2395 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2396 rgbgendiffuselayer = i;
2397 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2398 alphagenspecularlayer = i;
2400 if (shader->numlayers >= 2
2401 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2402 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2403 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2404 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2406 // terrain blend or certain other effects involving alphatest over a regular layer
2407 terrainbackgroundlayer = 0;
2409 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2410 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2412 else if (lightmaplayer == 0)
2414 // ordinary texture but with $lightmap before diffuse
2416 firstpostlayer = lightmaplayer + 2;
2418 else if (lightmaplayer >= 1)
2420 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2421 endofprelayers = lightmaplayer - 1;
2422 materiallayer = lightmaplayer - 1;
2423 firstpostlayer = lightmaplayer + 1;
2425 else if (rgbgenvertexlayer >= 0)
2427 // map models with baked lighting
2428 materiallayer = rgbgenvertexlayer;
2429 endofprelayers = rgbgenvertexlayer;
2430 firstpostlayer = rgbgenvertexlayer + 1;
2431 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2432 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2433 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2435 else if (rgbgendiffuselayer >= 0)
2437 // entity models with dynamic lighting
2438 materiallayer = rgbgendiffuselayer;
2439 endofprelayers = rgbgendiffuselayer;
2440 firstpostlayer = rgbgendiffuselayer + 1;
2441 // 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)...
2442 if (alphagenspecularlayer >= 0)
2443 firstpostlayer = alphagenspecularlayer + 1;
2447 // special effects shaders - treat first as primary layer and do everything else as post
2452 // convert the main material layer
2453 // 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
2454 if (materiallayer >= 0)
2455 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2456 // convert the terrain background blend layer (if any)
2457 if (terrainbackgroundlayer >= 0)
2458 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2459 // convert the prepass layers (if any)
2460 texture->startpreshaderpass = shaderpassindex;
2461 for (i = 0; i < endofprelayers; i++)
2462 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2463 texture->endpreshaderpass = shaderpassindex;
2464 texture->startpostshaderpass = shaderpassindex;
2465 // convert the postpass layers (if any)
2466 for (i = firstpostlayer; i < shader->numlayers; i++)
2467 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2468 texture->startpostshaderpass = shaderpassindex;
2471 if (shader->dpshadow)
2472 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2473 if (shader->dpnoshadow)
2474 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2475 if (shader->dpnortlight)
2476 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2477 if (shader->vertexalpha)
2478 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2479 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2480 texture->reflectmin = shader->reflectmin;
2481 texture->reflectmax = shader->reflectmax;
2482 texture->refractfactor = shader->refractfactor;
2483 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2484 texture->reflectfactor = shader->reflectfactor;
2485 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2486 texture->r_water_wateralpha = shader->r_water_wateralpha;
2487 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2488 texture->offsetmapping = shader->offsetmapping;
2489 texture->offsetscale = shader->offsetscale;
2490 texture->offsetbias = shader->offsetbias;
2491 texture->specularscalemod = shader->specularscalemod;
2492 texture->specularpowermod = shader->specularpowermod;
2493 texture->rtlightambient = shader->rtlightambient;
2494 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2495 if (shader->dpreflectcube[0])
2496 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2498 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2499 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2500 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2501 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2502 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2503 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2504 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2505 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2506 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2508 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2509 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2510 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2511 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2512 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2513 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2514 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2515 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2516 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2520 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2523 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2524 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2525 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2526 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2527 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2528 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2529 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2530 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2531 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2532 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2534 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2536 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2537 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2539 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2541 texture->surfaceflags = shader->surfaceflags;
2542 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2543 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2544 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2545 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2546 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2547 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2548 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2549 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2550 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2551 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2552 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2553 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2554 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2555 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2556 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2557 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2558 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2559 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2560 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2561 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2562 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2563 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2564 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2565 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2566 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2567 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2568 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2569 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2570 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2571 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2572 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2573 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2575 if (shader->dpmeshcollisions)
2576 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2577 if (shader->dpshaderkill && developer_extra.integer)
2578 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2580 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2582 if (developer_extra.integer)
2583 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2584 texture->basematerialflags = defaultmaterialflags;
2585 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2587 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2589 if (developer_extra.integer)
2590 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2591 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2592 texture->supercontents = SUPERCONTENTS_SOLID;
2596 if (developer_extra.integer)
2597 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2598 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2600 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2601 texture->supercontents = SUPERCONTENTS_SOLID;
2603 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2605 texture->basematerialflags = MATERIALFLAG_SKY;
2606 texture->supercontents = SUPERCONTENTS_SKY;
2610 texture->basematerialflags = defaultmaterialflags;
2611 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2613 if(cls.state == ca_dedicated)
2615 texture->materialshaderpass = NULL;
2620 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2623 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2624 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2625 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2626 if (texture->q2contents)
2627 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2631 if (!success && warnmissing)
2632 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2635 // init the animation variables
2636 texture->currentframe = texture;
2637 texture->currentmaterialflags = texture->basematerialflags;
2638 if (!texture->materialshaderpass)
2639 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2640 if (!texture->materialshaderpass->skinframes[0])
2641 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2642 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2643 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2647 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2649 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2650 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2652 dp_strlcpy(texture->name, name, sizeof(texture->name));
2653 texture->basealpha = 1.0f;
2654 texture->basematerialflags = materialflags;
2655 texture->supercontents = supercontents;
2657 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2658 texture->offsetscale = 1;
2659 texture->offsetbias = 0;
2660 texture->specularscalemod = 1;
2661 texture->specularpowermod = 1;
2662 texture->rtlightambient = 0;
2663 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2664 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2665 // JUST GREP FOR "specularscalemod = 1".
2667 if (developer_extra.integer)
2668 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2670 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2672 // init the animation variables
2673 texture->currentmaterialflags = texture->basematerialflags;
2674 texture->currentframe = texture;
2675 texture->currentskinframe = skinframe;
2676 texture->backgroundcurrentskinframe = NULL;
2679 void Mod_UnloadCustomMaterial(texture_t *texture, qbool purgeskins)
2681 long unsigned int i, j;
2682 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2684 if (texture->shaderpasses[i])
2687 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2688 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2689 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2690 Mem_Free(texture->shaderpasses[i]);
2691 texture->shaderpasses[i] = NULL;
2694 texture->materialshaderpass = NULL;
2695 texture->currentskinframe = NULL;
2696 texture->backgroundcurrentskinframe = NULL;
2699 skinfile_t *Mod_LoadSkinFiles(void)
2701 int i, words, line, wordsoverflow;
2704 skinfile_t *skinfile = NULL, *first = NULL;
2705 skinfileitem_t *skinfileitem;
2706 char word[10][MAX_QPATH];
2711 U_bodyBox,models/players/Legoman/BikerA2.tga
2712 U_RArm,models/players/Legoman/BikerA1.tga
2713 U_LArm,models/players/Legoman/BikerA1.tga
2714 U_armor,common/nodraw
2715 U_sword,common/nodraw
2716 U_shield,common/nodraw
2717 U_homb,common/nodraw
2718 U_backpack,common/nodraw
2719 U_colcha,common/nodraw
2724 memset(word, 0, sizeof(word));
2725 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2727 // If it's the first file we parse
2728 if (skinfile == NULL)
2730 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2735 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2736 skinfile = skinfile->next;
2738 skinfile->next = NULL;
2740 for(line = 0;;line++)
2743 if (!COM_ParseToken_QuakeC(&data, true))
2745 if (!strcmp(com_token, "\n"))
2748 wordsoverflow = false;
2752 dp_strlcpy(word[words++], com_token, sizeof (word[0]));
2754 wordsoverflow = true;
2756 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2759 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);
2762 // words is always >= 1
2763 if (!strcmp(word[0], "replace"))
2767 if (developer_loading.integer)
2768 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2769 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2770 skinfileitem->next = skinfile->items;
2771 skinfile->items = skinfileitem;
2772 dp_strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2773 dp_strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2776 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]);
2778 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2780 // tag name, like "tag_weapon,"
2781 // not used for anything (not even in Quake3)
2783 else if (words >= 2 && !strcmp(word[1], ","))
2785 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2786 if (developer_loading.integer)
2787 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2788 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2789 skinfileitem->next = skinfile->items;
2790 skinfile->items = skinfileitem;
2791 dp_strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2792 dp_strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2795 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);
2800 loadmodel->numskins = i;
2804 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2807 skinfileitem_t *skinfileitem, *nextitem;
2808 for (;skinfile;skinfile = next)
2810 next = skinfile->next;
2811 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2813 nextitem = skinfileitem->next;
2814 Mem_Free(skinfileitem);
2820 int Mod_CountSkinFiles(skinfile_t *skinfile)
2823 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2827 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2830 double isnap = 1.0 / snap;
2831 for (i = 0;i < numvertices*numcomponents;i++)
2832 vertices[i] = floor(vertices[i]*isnap)*snap;
2835 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2837 int i, outtriangles;
2838 float edgedir1[3], edgedir2[3], temp[3];
2839 // a degenerate triangle is one with no width (thickness, surface area)
2840 // these are characterized by having all 3 points colinear (along a line)
2841 // or having two points identical
2842 // the simplest check is to calculate the triangle's area
2843 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2845 // calculate first edge
2846 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2847 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2848 CrossProduct(edgedir1, edgedir2, temp);
2849 if (VectorLength2(temp) < 0.001f)
2850 continue; // degenerate triangle (no area)
2851 // valid triangle (has area)
2852 VectorCopy(inelement3i, outelement3i);
2856 return outtriangles;
2859 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2862 int firstvertex, lastvertex;
2863 if (numelements > 0 && elements)
2865 firstvertex = lastvertex = elements[0];
2866 for (i = 1;i < numelements;i++)
2869 firstvertex = min(firstvertex, e);
2870 lastvertex = max(lastvertex, e);
2874 firstvertex = lastvertex = 0;
2875 if (firstvertexpointer)
2876 *firstvertexpointer = firstvertex;
2877 if (lastvertexpointer)
2878 *lastvertexpointer = lastvertex;
2881 void Mod_SetDrawSkyAndWater(model_t* mod)
2884 uint64_t basematerialflags = 0;
2885 // by default assume there is no sky or water used in this model
2886 mod->DrawSky = NULL;
2887 mod->DrawAddWaterPlanes = NULL;
2888 // combine all basematerialflags observed in the submodelsurfaces range, then check for special flags
2889 for (j = mod->submodelsurfaces_start; j < mod->submodelsurfaces_end; j++)
2890 if (mod->data_surfaces[j].texture)
2891 basematerialflags |= mod->data_surfaces[j].texture->basematerialflags;
2892 if (basematerialflags & MATERIALFLAG_SKY)
2893 mod->DrawSky = R_Mod_DrawSky;
2894 if (basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
2895 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
2898 typedef struct Mod_MakeSortedSurfaces_qsortsurface_s
2901 q3deffect_t* effect;
2903 rtexture_t* lightmaptexture;
2905 Mod_MakeSortedSurfaces_qsortsurface_t;
2907 static int Mod_MakeSortedSurfaces_qsortfunc(const void *a, const void *b)
2909 const Mod_MakeSortedSurfaces_qsortsurface_t* l = (Mod_MakeSortedSurfaces_qsortsurface_t*)a;
2910 const Mod_MakeSortedSurfaces_qsortsurface_t* r = (Mod_MakeSortedSurfaces_qsortsurface_t*)b;
2911 if (l->effect < r->effect)
2913 if (l->effect > r->effect)
2915 if (l->texture < r->texture)
2917 if (l->texture > r->texture)
2919 if (l->lightmaptexture < r->lightmaptexture)
2921 if (l->lightmaptexture > r->lightmaptexture)
2923 if (l->surfaceindex < r->surfaceindex)
2925 if (l->surfaceindex > r->surfaceindex)
2930 void Mod_MakeSortedSurfaces(model_t *mod)
2932 // make an optimal set of texture-sorted batches to draw...
2934 Mod_MakeSortedSurfaces_qsortsurface_t *info;
2936 if(cls.state == ca_dedicated)
2939 info = (Mod_MakeSortedSurfaces_qsortsurface_t*)Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*info));
2940 if (!mod->modelsurfaces_sorted)
2941 mod->modelsurfaces_sorted = (int *) Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*mod->modelsurfaces_sorted));
2942 // the goal is to sort by submodel (can't change which submodel a surface belongs to), and then by effects and textures
2943 for (j = 0; j < mod->num_surfaces; j++)
2945 info[j].surfaceindex = j;
2946 info[j].effect = mod->data_surfaces[j].effect;
2947 info[j].texture = mod->data_surfaces[j].texture;
2948 info[j].lightmaptexture = mod->data_surfaces[j].lightmaptexture;
2950 for (k = 0; k < mod->brush.numsubmodels; k++)
2951 if (mod->brush.submodels[k]->submodelsurfaces_end > mod->brush.submodels[k]->submodelsurfaces_start + 1)
2952 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);
2953 for (j = 0; j < mod->num_surfaces; j++)
2954 mod->modelsurfaces_sorted[j] = info[j].surfaceindex;
2958 void Mod_BuildVBOs(void)
2960 if(cls.state == ca_dedicated)
2963 if (!loadmodel->surfmesh.num_vertices)
2966 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2969 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2971 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2973 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2974 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2979 // upload short indices as a buffer
2980 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2981 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);
2983 // upload int indices as a buffer
2984 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2985 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);
2987 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2988 // we put several vertex data streams in the same buffer
2989 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2994 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2995 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2996 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2997 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2998 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2999 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3000 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
3001 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3002 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3003 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
3004 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3005 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3006 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3007 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3008 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3009 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3010 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
3011 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3012 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3013 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3014 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3015 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3016 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3017 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3018 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3019 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3020 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3021 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
3026 extern cvar_t mod_obj_orientation;
3027 static void Mod_Decompile_OBJ(model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3029 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3031 const char *texname;
3033 const float *v, *vn, *vt;
3035 size_t outbufferpos = 0;
3036 size_t outbuffermax = 0x100000;
3037 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3038 const msurface_t *surface;
3039 const int maxtextures = 256;
3040 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3043 // construct the mtllib file
3044 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3047 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3050 countvertices += surface->num_vertices;
3051 countfaces += surface->num_triangles;
3052 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3053 for (textureindex = 0;textureindex < counttextures;textureindex++)
3054 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3056 if (textureindex < counttextures)
3057 continue; // already wrote this material entry
3058 if (textureindex >= maxtextures)
3059 continue; // just a precaution
3060 textureindex = counttextures++;
3061 dp_strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3062 if (outbufferpos >= outbuffermax >> 1)
3065 oldbuffer = outbuffer;
3066 outbuffer = (char *) Z_Malloc(outbuffermax);
3067 memcpy(outbuffer, oldbuffer, outbufferpos);
3070 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");
3075 // write the mtllib file
3076 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3078 // construct the obj file
3080 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);
3084 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)
3086 if (outbufferpos >= outbuffermax >> 1)
3089 oldbuffer = outbuffer;
3090 outbuffer = (char *) Z_Malloc(outbuffermax);
3091 memcpy(outbuffer, oldbuffer, outbufferpos);
3094 if(mod_obj_orientation.integer)
3095 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]);
3097 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]);
3102 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3104 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3107 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3108 for (surfaceindex = submodel->submodelsurfaces_start;surfaceindex < submodel->submodelsurfaces_end;surfaceindex++)
3110 surface = model->data_surfaces + surfaceindex;
3111 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3114 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3116 if (outbufferpos >= outbuffermax >> 1)
3119 oldbuffer = outbuffer;
3120 outbuffer = (char *) Z_Malloc(outbuffermax);
3121 memcpy(outbuffer, oldbuffer, outbufferpos);
3127 if(mod_obj_orientation.integer)
3128 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);
3130 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);
3137 // write the obj file
3138 FS_WriteFile(filename, outbuffer, outbufferpos);
3142 Z_Free(texturenames);
3145 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3148 static void Mod_Decompile_SMD(model_t *model, const char *filename, int firstpose, int numposes, qbool writetriangles)
3150 int countnodes = 0, counttriangles = 0, countframes = 0;
3158 size_t outbufferpos = 0;
3159 size_t outbuffermax = 0x100000;
3160 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3161 const msurface_t *surface;
3162 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3165 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3167 if (outbufferpos >= outbuffermax >> 1)
3170 oldbuffer = outbuffer;
3171 outbuffer = (char *) Z_Malloc(outbuffermax);
3172 memcpy(outbuffer, oldbuffer, outbufferpos);
3176 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3180 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3183 for (poseindex = 0;poseindex < numposes;poseindex++)
3186 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3189 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3193 matrix4x4_t posematrix;
3194 if (outbufferpos >= outbuffermax >> 1)
3197 oldbuffer = outbuffer;
3198 outbuffer = (char *) Z_Malloc(outbuffermax);
3199 memcpy(outbuffer, oldbuffer, outbufferpos);
3203 // strangely the smd angles are for a transposed matrix, so we
3204 // have to generate a transposed matrix, then convert that...
3205 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3206 Matrix4x4_ToArray12FloatGL(&posematrix, mtest);
3207 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3208 if (angles[0] >= 180) angles[0] -= 360;
3209 if (angles[1] >= 180) angles[1] -= 360;
3210 if (angles[2] >= 180) angles[2] -= 360;
3214 float a = DEG2RAD(angles[ROLL]);
3215 float b = DEG2RAD(angles[PITCH]);
3216 float c = DEG2RAD(angles[YAW]);
3217 float cy, sy, cp, sp, cr, sr;
3219 // smd matrix construction, for comparing
3230 test[1][0] = sr*sp*cy+cr*-sy;
3231 test[1][1] = sr*sp*sy+cr*cy;
3233 test[2][0] = (cr*sp*cy+-sr*-sy);
3234 test[2][1] = (cr*sp*sy+-sr*cy);
3236 test[3][0] = pose[9];
3237 test[3][1] = pose[10];
3238 test[3][2] = pose[11];
3241 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]));
3246 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3251 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3254 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3256 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3259 if (outbufferpos >= outbuffermax >> 1)
3262 oldbuffer = outbuffer;
3263 outbuffer = (char *) Z_Malloc(outbuffermax);
3264 memcpy(outbuffer, oldbuffer, outbufferpos);
3267 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3270 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3272 const int index = e[2-cornerindex];
3273 const float *v = model->surfmesh.data_vertex3f + index * 3;
3274 const float *vn = model->surfmesh.data_normal3f + index * 3;
3275 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3276 const int b = model->surfmesh.blends[index];
3277 if (b < model->num_bones)
3278 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]);
3281 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3282 const unsigned char *wi = w->index;
3283 const unsigned char *wf = w->influence;
3284 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);
3285 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);
3286 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);
3287 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]);
3294 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3299 FS_WriteFile(filename, outbuffer, outbufferpos);
3302 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3309 decompiles a model to editable files
3312 static void Mod_Decompile_f(cmd_state_t *cmd)
3314 int i, j, k, l, first, count;
3316 char inname[MAX_QPATH];
3317 char outname[MAX_QPATH];
3318 char mtlname[MAX_QPATH];
3319 char basename[MAX_QPATH];
3320 char animname[MAX_QPATH];
3321 char animname2[MAX_QPATH];
3322 char zymtextbuffer[16384];
3323 char dpmtextbuffer[16384];
3324 char framegroupstextbuffer[16384];
3325 int zymtextsize = 0;
3326 int dpmtextsize = 0;
3327 int framegroupstextsize = 0;
3330 if (Cmd_Argc(cmd) != 2)
3332 Con_Print("usage: modeldecompile <filename>\n");
3336 dp_strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3337 FS_StripExtension(inname, basename, sizeof(basename));
3339 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3342 Con_Print("No such model\n");
3345 if (mod->brush.submodel)
3347 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3348 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3349 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3352 if (!mod->surfmesh.num_triangles)
3354 Con_Print("Empty model (or sprite)\n");
3358 // export OBJ if possible (not on sprites)
3359 if (mod->surfmesh.num_triangles)
3361 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3362 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3363 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3366 // export SMD if possible (only for skeletal models)
3367 if (mod->surfmesh.num_triangles && mod->num_bones)
3369 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3370 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3371 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3372 if (l > 0) zymtextsize += l;
3373 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3374 if (l > 0) dpmtextsize += l;
3375 for (i = 0;i < mod->numframes;i = j)
3377 dp_strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3378 first = mod->animscenes[i].firstframe;
3379 if (mod->animscenes[i].framecount > 1)
3382 count = mod->animscenes[i].framecount;
3388 // check for additional frames with same name
3389 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3390 if(animname[l] < '0' || animname[l] > '9')
3392 if(k > 0 && animname[k-1] == '_')
3395 count = mod->num_poses - first;
3396 for (j = i + 1;j < mod->numframes;j++)
3398 dp_strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3399 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3400 if(animname2[l] < '0' || animname2[l] > '9')
3402 if(k > 0 && animname[k-1] == '_')
3405 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3407 count = mod->animscenes[j].firstframe - first;
3411 // if it's only one frame, use the original frame name
3413 dp_strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3416 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3417 Mod_Decompile_SMD(mod, outname, first, count, false);
3418 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3420 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3421 if (l > 0) zymtextsize += l;
3423 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3425 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3426 if (l > 0) dpmtextsize += l;
3428 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3430 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3431 if (l > 0) framegroupstextsize += l;
3435 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3437 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3438 if (framegroupstextsize)
3439 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3443 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3446 memset(state, 0, sizeof(*state));
3447 state->width = width;
3448 state->height = height;
3449 state->currentY = 0;
3450 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3451 for (y = 0;y < state->height;y++)
3453 state->rows[y].currentX = 0;
3454 state->rows[y].rowY = -1;
3458 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3461 state->currentY = 0;
3462 for (y = 0;y < state->height;y++)
3464 state->rows[y].currentX = 0;
3465 state->rows[y].rowY = -1;
3469 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3472 Mem_Free(state->rows);
3473 memset(state, 0, sizeof(*state));
3476 qbool Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3478 mod_alloclightmap_row_t *row;
3481 row = state->rows + blockheight;
3482 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3484 if (state->currentY + blockheight <= state->height)
3486 // use the current allocation position
3487 row->rowY = state->currentY;
3489 state->currentY += blockheight;
3493 // find another position
3494 for (y = blockheight;y < state->height;y++)
3496 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3498 row = state->rows + y;
3502 if (y == state->height)
3507 *outx = row->currentX;
3508 row->currentX += blockwidth;
3513 typedef struct lightmapsample_s
3517 float *vertex_color;
3518 unsigned char *lm_bgr;
3519 unsigned char *lm_dir;
3523 typedef struct lightmapvertex_s
3528 float texcoordbase[2];
3529 float texcoordlightmap[2];
3530 float lightcolor[4];
3534 typedef struct lightmaptriangle_s
3542 // 2D modelspace coordinates of min corner
3543 // snapped to lightmap grid but not in grid coordinates
3545 // 2D modelspace to lightmap coordinate scale
3553 typedef struct lightmaplight_s
3564 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3566 #define MAX_LIGHTMAPSAMPLES 64
3567 static int mod_generatelightmaps_numoffsets[3];
3568 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3570 static int mod_generatelightmaps_numlights;
3571 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3573 extern cvar_t r_shadow_lightattenuationdividebias;
3574 extern cvar_t r_shadow_lightattenuationlinearscale;
3576 static void Mod_GenerateLightmaps_LightPoint(model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3581 float relativepoint[3];
3588 float lightorigin[3];
3592 float lightcolor[3];
3594 for (i = 0;i < 5*3;i++)
3596 for (index = 0;;index++)
3598 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3603 lightradius2 = lightradius * lightradius;
3604 VectorSubtract(lightorigin, pos, relativepoint);
3605 dist2 = VectorLength2(relativepoint);
3606 if (dist2 >= lightradius2)
3608 lightiradius = 1.0f / lightradius;
3609 dist = sqrt(dist2) * lightiradius;
3610 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3611 if (intensity <= 0.0f)
3613 if (model && model->TraceLine)
3615 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3616 if (trace.fraction < 1)
3619 // scale down intensity to add to both ambient and diffuse
3620 //intensity *= 0.5f;
3621 VectorNormalize(relativepoint);
3622 VectorScale(lightcolor, intensity, color);
3623 VectorMA(sample , 0.5f , color, sample );
3624 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3625 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3626 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3627 // calculate a weighted average light direction as well
3628 intensity *= VectorLength(color);
3629 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3631 // calculate the direction we'll use to reduce the sample to a directional light source
3632 VectorCopy(sample + 12, dir);
3633 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3634 VectorNormalize(dir);
3635 // extract the diffuse color along the chosen direction and scale it
3636 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3637 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3638 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3639 // subtract some of diffuse from ambient
3640 VectorMA(sample, -0.333f, diffuse, ambient);
3641 // store the normalized lightdir
3642 VectorCopy(dir, lightdir);
3645 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3649 const msurface_t *surface;
3650 const float *vertex3f = model->surfmesh.data_vertex3f;
3651 const int *element3i = model->surfmesh.data_element3i;
3654 for (surfaceindex = model->submodelsurfaces_start;surfaceindex < model->submodelsurfaces_end;surfaceindex++)
3656 surface = model->data_surfaces + surfaceindex;
3657 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3659 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3661 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3663 VectorCopy(vertex3f + 3*e[0], v2[0]);
3664 VectorCopy(vertex3f + 3*e[1], v2[1]);
3665 VectorCopy(vertex3f + 3*e[2], v2[2]);
3666 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3671 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model_t *model, lightmaplight_t *lightinfo)
3673 int maxnodes = 1<<14;
3674 svbsp_node_t *nodes;
3679 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3680 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3681 VectorCopy(lightinfo->origin, origin);
3682 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3685 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3686 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3687 if (svbsp.ranoutofnodes)
3690 if (maxnodes > 1<<22)
3696 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3701 if (svbsp.numnodes > 0)
3703 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3704 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3705 lightinfo->svbsp = svbsp;
3710 static void Mod_GenerateLightmaps_CreateLights(model_t *model)
3714 lightmaplight_t *lightinfo;
3718 mod_generatelightmaps_numlights = 0;
3719 for (index = 0;;index++)
3721 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3725 mod_generatelightmaps_numlights++;
3727 if (mod_generatelightmaps_numlights > 0)
3729 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3730 lightinfo = mod_generatelightmaps_lightinfo;
3731 for (index = 0;;index++)
3733 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3740 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3742 lightinfo->iradius = 1.0f / lightinfo->radius;
3743 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3744 // TODO: compute svbsp
3745 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3749 static void Mod_GenerateLightmaps_DestroyLights(model_t *model)
3752 if (mod_generatelightmaps_lightinfo)
3754 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3755 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3756 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3757 Mem_Free(mod_generatelightmaps_lightinfo);
3759 mod_generatelightmaps_lightinfo = NULL;
3760 mod_generatelightmaps_numlights = 0;
3763 static qbool Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3765 const svbsp_node_t *node;
3766 const svbsp_node_t *nodes = svbsp->nodes;
3771 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3773 return num == -1; // true if empty, false if solid (shadowed)
3776 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3779 float relativepoint[3];
3788 const lightmaplight_t *lightinfo;
3790 for (i = 0;i < 5*3;i++)
3792 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3794 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3795 VectorSubtract(lightinfo->origin, pos, relativepoint);
3796 // don't accept light from behind a surface, it causes bad shading
3797 if (normal && DotProduct(relativepoint, normal) <= 0)
3799 dist2 = VectorLength2(relativepoint);
3800 if (dist2 >= lightinfo->radius2)
3802 dist = sqrt(dist2) * lightinfo->iradius;
3803 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3806 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3810 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3812 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3814 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3817 // for light grid we'd better check visibility of the offset point
3818 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3819 if (trace.fraction < 1)
3820 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3823 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3828 // scale intensity according to how many rays succeeded
3829 // we know one test is valid, half of the rest will fail...
3830 //if (normal && tests > 1)
3831 // intensity *= (tests - 1.0f) / tests;
3832 intensity *= (float)hits / tests;
3834 // scale down intensity to add to both ambient and diffuse
3835 //intensity *= 0.5f;
3836 VectorNormalize(relativepoint);
3837 VectorScale(lightinfo->color, intensity, color);
3838 VectorMA(sample , 0.5f , color, sample );
3839 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3840 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3841 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3842 // calculate a weighted average light direction as well
3843 intensity *= VectorLength(color);
3844 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3848 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3854 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3855 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3856 VectorCopy(sample + 12, dir);
3857 VectorNormalize(dir);
3858 //VectorAdd(dir, normal, dir);
3859 //VectorNormalize(dir);
3860 f = DotProduct(dir, normal);
3861 f = max(0, f) * 255.0f;
3862 VectorScale(sample, f, color);
3863 //VectorCopy(normal, dir);
3864 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3865 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3866 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3867 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3869 lm_dir[0] = (unsigned char)dir[2];
3870 lm_dir[1] = (unsigned char)dir[1];
3871 lm_dir[2] = (unsigned char)dir[0];
3875 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3878 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3879 VectorCopy(sample, vertex_color);
3882 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3888 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3889 // calculate the direction we'll use to reduce the sample to a directional light source
3890 VectorCopy(sample + 12, dir);
3891 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3892 VectorNormalize(dir);
3893 // extract the diffuse color along the chosen direction and scale it
3894 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3895 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3896 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3897 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3898 VectorScale(sample, 127.5f, ambient);
3899 VectorMA(ambient, -0.333f, diffuse, ambient);
3900 // encode to the grid format
3901 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3902 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3903 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3904 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3905 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3906 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3907 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3908 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3909 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));}
3912 static void Mod_GenerateLightmaps_InitSampleOffsets(model_t *model)
3917 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3918 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3919 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3920 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3921 radius[0] = mod_generatelightmaps_lightmapradius.value;
3922 radius[1] = mod_generatelightmaps_vertexradius.value;
3923 radius[2] = mod_generatelightmaps_gridradius.value;
3924 for (i = 0;i < 3;i++)
3926 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3929 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3934 static void Mod_GenerateLightmaps_DestroyLightmaps(model_t *model)
3936 msurface_t *surface;
3939 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3941 surface = model->data_surfaces + surfaceindex;
3942 surface->lightmaptexture = NULL;
3943 surface->deluxemaptexture = NULL;
3945 if (model->brushq3.data_lightmaps)
3947 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3948 if (model->brushq3.data_lightmaps[i])
3949 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3950 Mem_Free(model->brushq3.data_lightmaps);
3951 model->brushq3.data_lightmaps = NULL;
3953 if (model->brushq3.data_deluxemaps)
3955 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3956 if (model->brushq3.data_deluxemaps[i])
3957 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3958 Mem_Free(model->brushq3.data_deluxemaps);
3959 model->brushq3.data_deluxemaps = NULL;
3963 static void Mod_GenerateLightmaps_UnweldTriangles(model_t *model)
3965 msurface_t *surface;
3971 surfmesh_t oldsurfmesh;
3973 unsigned char *data;
3974 oldsurfmesh = model->surfmesh;
3975 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3976 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3978 size += model->surfmesh.num_vertices * sizeof(float[3]);
3979 size += model->surfmesh.num_vertices * sizeof(float[3]);
3980 size += model->surfmesh.num_vertices * sizeof(float[3]);
3981 size += model->surfmesh.num_vertices * sizeof(float[3]);
3982 size += model->surfmesh.num_vertices * sizeof(float[2]);
3983 size += model->surfmesh.num_vertices * sizeof(float[2]);
3984 size += model->surfmesh.num_vertices * sizeof(float[4]);
3985 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3986 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3987 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3988 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3989 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3990 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3991 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3992 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3993 if (model->surfmesh.num_vertices > 65536)
3994 model->surfmesh.data_element3s = NULL;
3996 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3997 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3998 model->surfmesh.data_element3i_indexbuffer = NULL;
3999 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
4000 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
4001 model->surfmesh.data_element3s_indexbuffer = NULL;
4002 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
4003 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
4004 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
4005 model->surfmesh.data_svector3f_vertexbuffer = NULL;
4006 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
4007 model->surfmesh.data_normal3f_vertexbuffer = NULL;
4008 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
4009 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
4010 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
4011 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
4012 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
4014 // convert all triangles to unique vertex data
4016 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4018 surface = model->data_surfaces + surfaceindex;
4019 surface->num_firstvertex = outvertexindex;
4020 surface->num_vertices = surface->num_triangles*3;
4021 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4022 for (i = 0;i < surface->num_triangles*3;i++)
4025 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4026 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4027 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4028 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4029 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4030 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4031 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4032 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4033 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4034 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4035 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4036 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4037 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4038 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4039 if (oldsurfmesh.data_texcoordlightmap2f)
4041 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4042 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4044 if (oldsurfmesh.data_lightmapcolor4f)
4046 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4047 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4048 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4049 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4052 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4053 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4057 if (model->surfmesh.data_element3s)
4058 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4059 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4061 // find and update all submodels to use this new surfmesh data
4062 for (i = 0;i < model->brush.numsubmodels;i++)
4063 model->brush.submodels[i]->surfmesh = model->surfmesh;
4066 static void Mod_GenerateLightmaps_CreateTriangleInformation(model_t *model)
4068 msurface_t *surface;
4074 lightmaptriangle_t *triangle;
4075 // generate lightmap triangle structs
4076 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4077 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4079 surface = model->data_surfaces + surfaceindex;
4080 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4081 for (i = 0;i < surface->num_triangles;i++)
4083 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4084 triangle->triangleindex = surface->num_firsttriangle+i;
4085 triangle->surfaceindex = surfaceindex;
4086 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4087 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4088 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4089 // calculate bounds of triangle
4090 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4091 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4092 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4093 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4094 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4095 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4096 // pick an axial projection based on the triangle normal
4097 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4099 if (fabs(normal[1]) > fabs(normal[axis]))
4101 if (fabs(normal[2]) > fabs(normal[axis]))
4103 triangle->axis = axis;
4108 static void Mod_GenerateLightmaps_DestroyTriangleInformation(model_t *model)
4110 if (mod_generatelightmaps_lightmaptriangles)
4111 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4112 mod_generatelightmaps_lightmaptriangles = NULL;
4115 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4117 static void Mod_GenerateLightmaps_CreateLightmaps(model_t *model)
4119 msurface_t *surface;
4133 float trianglenormal[3];
4134 float samplecenter[3];
4135 float samplenormal[3];
4141 float lmscalepixels;
4144 float lm_basescalepixels;
4145 int lm_borderpixels;
4149 lightmaptriangle_t *triangle;
4150 unsigned char *lightmappixels;
4151 unsigned char *deluxemappixels;
4152 mod_alloclightmap_state_t lmstate;
4155 // generate lightmap projection information for all triangles
4156 if (model->texturepool == NULL)
4157 model->texturepool = R_AllocTexturePool();
4158 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4159 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4160 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4161 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4162 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4164 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4166 surface = model->data_surfaces + surfaceindex;
4167 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4168 lmscalepixels = lm_basescalepixels;
4169 for (retry = 0;retry < 30;retry++)
4171 // after a couple failed attempts, degrade quality to make it fit
4173 lmscalepixels *= 0.5f;
4174 for (i = 0;i < surface->num_triangles;i++)
4176 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4177 triangle->lightmapindex = lightmapnumber;
4178 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4179 // pick two planar axes for projection
4180 // lightmap coordinates here are in pixels
4181 // lightmap projections are snapped to pixel grid explicitly, such
4182 // that two neighboring triangles sharing an edge and projection
4183 // axis will have identical sample spacing along their shared edge
4185 for (j = 0;j < 3;j++)
4187 if (j == triangle->axis)
4189 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4190 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4191 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4192 triangle->lmbase[k] = lmmins/lmscalepixels;
4193 triangle->lmscale[k] = lmscalepixels;
4196 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4199 // if all fit in this texture, we're done with this surface
4200 if (i == surface->num_triangles)
4202 // if we haven't maxed out the lightmap size yet, we retry the
4203 // entire surface batch...
4204 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4206 lm_texturesize *= 2;
4209 Mod_AllocLightmap_Free(&lmstate);
4210 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4213 // if we have maxed out the lightmap size, and this triangle does
4214 // not fit in the same texture as the rest of the surface, we have
4215 // to retry the entire surface in a new texture (can only use one)
4216 // with multiple retries, the lightmap quality degrades until it
4217 // fits (or gives up)
4218 if (surfaceindex > 0)
4220 Mod_AllocLightmap_Reset(&lmstate);
4224 Mod_AllocLightmap_Free(&lmstate);
4226 // now put triangles together into lightmap textures, and do not allow
4227 // triangles of a surface to go into different textures (as that would
4228 // require rewriting the surface list)
4229 model->brushq3.deluxemapping_modelspace = true;
4230 model->brushq3.deluxemapping = true;
4231 model->brushq3.num_mergedlightmaps = lightmapnumber;
4232 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4233 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4234 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4235 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4236 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4238 surface = model->data_surfaces + surfaceindex;
4239 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4240 for (i = 0;i < surface->num_triangles;i++)
4242 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4243 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4244 VectorNormalize(trianglenormal);
4245 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4246 axis = triangle->axis;
4247 axis1 = axis == 0 ? 1 : 0;
4248 axis2 = axis == 2 ? 1 : 2;
4249 lmiscale[0] = 1.0f / triangle->lmscale[0];
4250 lmiscale[1] = 1.0f / triangle->lmscale[1];
4251 if (trianglenormal[axis] < 0)
4252 VectorNegate(trianglenormal, trianglenormal);
4253 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4254 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4255 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4256 for (j = 0;j < 3;j++)
4258 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4259 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4260 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4262 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4263 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4264 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4265 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]);
4275 forward[1] = 1.0f / triangle->lmscale[0];
4279 left[2] = 1.0f / triangle->lmscale[1];
4284 origin[1] = triangle->lmbase[0];
4285 origin[2] = triangle->lmbase[1];
4288 forward[0] = 1.0f / triangle->lmscale[0];
4293 left[2] = 1.0f / triangle->lmscale[1];
4297 origin[0] = triangle->lmbase[0];
4299 origin[2] = triangle->lmbase[1];
4302 forward[0] = 1.0f / triangle->lmscale[0];
4306 left[1] = 1.0f / triangle->lmscale[1];
4311 origin[0] = triangle->lmbase[0];
4312 origin[1] = triangle->lmbase[1];
4316 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4318 #define LM_DIST_EPSILON (1.0f / 32.0f)
4319 for (y = 0;y < triangle->lmsize[1];y++)
4321 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4322 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4324 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4325 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4326 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4327 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4328 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4334 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4336 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);
4337 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);
4341 Mem_Free(lightmappixels);
4342 if (deluxemappixels)
4343 Mem_Free(deluxemappixels);
4345 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4347 surface = model->data_surfaces + surfaceindex;
4348 if (!surface->num_triangles)
4350 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4351 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4352 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4353 surface->lightmapinfo = NULL;
4356 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4357 model->brushq1.lightdata = NULL;
4358 model->brushq1.lightmapupdateflags = NULL;
4359 model->brushq1.firstrender = false;
4360 model->brushq1.num_lightstyles = 0;
4361 model->brushq1.data_lightstyleinfo = NULL;
4362 for (i = 0;i < model->brush.numsubmodels;i++)
4364 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4365 model->brush.submodels[i]->brushq1.firstrender = false;
4366 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4367 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4371 static void Mod_GenerateLightmaps_UpdateVertexColors(model_t *model)
4374 for (i = 0;i < model->surfmesh.num_vertices;i++)
4375 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4378 static void Mod_GenerateLightmaps_UpdateLightGrid(model_t *model)
4385 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4387 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4388 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4390 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4391 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4393 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4394 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4400 extern cvar_t mod_q3bsp_nolightmaps;
4401 static void Mod_GenerateLightmaps(model_t *model)
4403 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4404 model_t *oldloadmodel = loadmodel;
4407 Mod_GenerateLightmaps_InitSampleOffsets(model);
4408 Mod_GenerateLightmaps_DestroyLightmaps(model);
4409 Mod_GenerateLightmaps_UnweldTriangles(model);
4410 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4411 Mod_GenerateLightmaps_CreateLights(model);
4412 if(!mod_q3bsp_nolightmaps.integer)
4413 Mod_GenerateLightmaps_CreateLightmaps(model);
4414 Mod_GenerateLightmaps_UpdateVertexColors(model);
4415 Mod_GenerateLightmaps_UpdateLightGrid(model);
4416 Mod_GenerateLightmaps_DestroyLights(model);
4417 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4419 loadmodel = oldloadmodel;
4422 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4424 if (Cmd_Argc(cmd) != 1)
4426 Con_Printf("usage: mod_generatelightmaps\n");
4431 Con_Printf("no worldmodel loaded\n");
4434 Mod_GenerateLightmaps(cl.worldmodel);
4437 void Mod_Mesh_Create(model_t *mod, const char *name)
4439 memset(mod, 0, sizeof(*mod));
4440 dp_strlcpy(mod->name, name, sizeof(mod->name));
4441 mod->mempool = Mem_AllocPool(name, 0, NULL);
4442 mod->texturepool = R_AllocTexturePool();
4443 mod->Draw = R_Mod_Draw;
4444 mod->DrawDepth = R_Mod_DrawDepth;
4445 mod->DrawDebug = R_Mod_DrawDebug;
4446 mod->DrawPrepass = R_Mod_DrawPrepass;
4447 mod->GetLightInfo = R_Mod_GetLightInfo;
4448 mod->DrawShadowMap = R_Mod_DrawShadowMap;
4449 mod->DrawLight = R_Mod_DrawLight;
4452 void Mod_Mesh_Destroy(model_t *mod)
4454 Mod_UnloadModel(mod);
4457 // resets the mesh model to have no geometry to render, ready for a new frame -
4458 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4459 void Mod_Mesh_Reset(model_t *mod)
4461 mod->num_surfaces = 0;
4462 mod->surfmesh.num_vertices = 0;
4463 mod->surfmesh.num_triangles = 0;
4464 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4465 mod->DrawSky = NULL; // will be set if a texture needs it
4466 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4469 texture_t *Mod_Mesh_GetTexture(model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4473 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4474 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4475 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4477 if (mod->max_textures <= mod->num_textures)
4479 texture_t *oldtextures = mod->data_textures;
4480 mod->max_textures = max(mod->max_textures * 2, 1024);
4481 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4482 // update the pointers
4483 for (i = 0; i < mod->num_surfaces; i++)
4484 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4486 t = &mod->data_textures[mod->num_textures++];
4487 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4488 t->mesh_drawflag = drawflag;
4489 t->mesh_defaulttexflags = defaulttexflags;
4490 t->mesh_defaultmaterialflags = defaultmaterialflags;
4491 switch (defaultdrawflags & DRAWFLAG_MASK)
4493 case DRAWFLAG_ADDITIVE:
4494 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4495 t->currentmaterialflags = t->basematerialflags;
4497 case DRAWFLAG_MODULATE:
4498 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4499 t->currentmaterialflags = t->basematerialflags;
4500 t->customblendfunc[0] = GL_DST_COLOR;
4501 t->customblendfunc[1] = GL_ZERO;
4503 case DRAWFLAG_2XMODULATE:
4504 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4505 t->currentmaterialflags = t->basematerialflags;
4506 t->customblendfunc[0] = GL_DST_COLOR;
4507 t->customblendfunc[1] = GL_SRC_COLOR;
4509 case DRAWFLAG_SCREEN:
4510 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4511 t->currentmaterialflags = t->basematerialflags;
4512 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4513 t->customblendfunc[1] = GL_ONE;
4521 msurface_t *Mod_Mesh_AddSurface(model_t *mod, texture_t *tex, qbool batchwithprevioussurface)
4524 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4525 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4526 return mod->data_surfaces + mod->num_surfaces - 1;
4527 // create new surface
4528 if (mod->max_surfaces == mod->num_surfaces)
4530 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4531 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4532 mod->modelsurfaces_sorted = (int *)Mem_Realloc(mod->mempool, mod->modelsurfaces_sorted, mod->max_surfaces * sizeof(*mod->modelsurfaces_sorted));
4534 surf = mod->data_surfaces + mod->num_surfaces;
4535 mod->num_surfaces++;
4536 memset(surf, 0, sizeof(*surf));
4537 surf->texture = tex;
4538 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4539 surf->num_firstvertex = mod->surfmesh.num_vertices;
4540 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4541 mod->DrawSky = R_Mod_DrawSky;
4542 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4543 mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes;
4547 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)
4549 int hashindex, h, vnum, mask;
4550 surfmesh_t *mesh = &mod->surfmesh;
4551 if (mesh->max_vertices == mesh->num_vertices)
4553 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4554 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4555 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4556 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4557 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4558 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4559 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4560 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4561 // rebuild the hash table
4562 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4563 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4564 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4565 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4566 mask = mod->surfmesh.num_vertexhashsize - 1;
4567 // no need to hash the vertices for the entire model, the latest surface will suffice.
4568 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4570 // this uses prime numbers intentionally for computing the hash
4571 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;
4572 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4573 ; // just iterate until we find the terminator
4574 mesh->data_vertexhash[h] = vnum;
4577 mask = mod->surfmesh.num_vertexhashsize - 1;
4578 // this uses prime numbers intentionally for computing the hash
4579 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4580 // when possible find an identical vertex within the same surface and return it
4581 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4583 if (vnum >= surf->num_firstvertex
4584 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4585 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4586 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4587 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4588 && 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)
4591 // add the new vertex
4592 vnum = mesh->num_vertices++;
4593 if (surf->num_vertices > 0)
4595 if (surf->mins[0] > x) surf->mins[0] = x;
4596 if (surf->mins[1] > y) surf->mins[1] = y;
4597 if (surf->mins[2] > z) surf->mins[2] = z;
4598 if (surf->maxs[0] < x) surf->maxs[0] = x;
4599 if (surf->maxs[1] < y) surf->maxs[1] = y;
4600 if (surf->maxs[2] < z) surf->maxs[2] = z;
4604 VectorSet(surf->mins, x, y, z);
4605 VectorSet(surf->maxs, x, y, z);
4607 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4608 mesh->data_vertexhash[h] = vnum;
4609 mesh->data_vertex3f[vnum * 3 + 0] = x;
4610 mesh->data_vertex3f[vnum * 3 + 1] = y;
4611 mesh->data_vertex3f[vnum * 3 + 2] = z;
4612 mesh->data_normal3f[vnum * 3 + 0] = nx;
4613 mesh->data_normal3f[vnum * 3 + 1] = ny;
4614 mesh->data_normal3f[vnum * 3 + 2] = nz;
4615 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4616 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4617 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4618 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4619 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4620 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4621 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4622 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4626 void Mod_Mesh_AddTriangle(model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4628 surfmesh_t *mesh = &mod->surfmesh;
4629 if (mesh->max_triangles == mesh->num_triangles)
4631 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4632 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4633 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4635 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4636 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4637 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4638 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4639 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4640 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4641 mesh->num_triangles++;
4642 surf->num_triangles++;
4645 static void Mod_Mesh_MakeSortedSurfaces(model_t *mod)
4650 // build the sorted surfaces list properly to reduce material setup
4651 // this is easy because we're just sorting on texture and don't care about the order of textures
4652 mod->submodelsurfaces_start = 0;
4653 mod->submodelsurfaces_end = 0;
4654 for (i = 0; i < mod->num_surfaces; i++)
4655 mod->data_surfaces[i].included = false;
4656 for (i = 0; i < mod->num_surfaces; i++)
4658 if (mod->data_surfaces[i].included)
4660 tex = mod->data_surfaces[i].texture;
4661 // j = i is intentional
4662 for (j = i; j < mod->num_surfaces; j++)
4664 if (!mod->data_surfaces[j].included && mod->data_surfaces[j].texture == tex)
4666 mod->data_surfaces[j].included = 1;
4667 mod->modelsurfaces_sorted[mod->submodelsurfaces_end++] = j;
4673 static void Mod_Mesh_ComputeBounds(model_t *mod)
4676 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4678 if (mod->surfmesh.num_vertices > 0)
4680 // calculate normalmins/normalmaxs
4681 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4682 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4683 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4685 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4686 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4687 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4688 // expand bounds to include this vertex
4689 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4690 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4691 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4692 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4693 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4694 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4696 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4697 // (fast but less accurate than doing it per vertex)
4698 x2a = mod->normalmins[0] * mod->normalmins[0];
4699 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4700 y2a = mod->normalmins[1] * mod->normalmins[1];
4701 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4702 z2a = mod->normalmins[2] * mod->normalmins[2];
4703 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4707 yawradius = sqrt(x2 + y2);
4708 rotatedradius = sqrt(x2 + y2 + z2);
4709 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4710 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4711 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4712 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4713 mod->radius = rotatedradius;
4714 mod->radius2 = x2 + y2 + z2;
4718 VectorClear(mod->normalmins);
4719 VectorClear(mod->normalmaxs);
4720 VectorClear(mod->yawmins);
4721 VectorClear(mod->yawmaxs);
4722 VectorClear(mod->rotatedmins);
4723 VectorClear(mod->rotatedmaxs);
4729 void Mod_Mesh_Validate(model_t *mod)
4732 qbool warned = false;
4733 for (i = 0; i < mod->num_surfaces; i++)
4735 msurface_t *surf = mod->data_surfaces + i;
4736 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4737 int first = surf->num_firstvertex;
4738 int end = surf->num_firstvertex + surf->num_vertices;
4740 for (j = 0;j < surf->num_triangles * 3;j++)
4742 if (e[j] < first || e[j] >= end)
4745 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4753 static void Mod_Mesh_UploadDynamicBuffers(model_t *mod)
4755 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;
4756 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;
4757 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;
4758 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;
4759 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;
4760 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;
4761 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;
4762 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;
4763 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;
4764 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;
4765 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;
4768 void Mod_Mesh_Finalize(model_t *mod)
4770 if (gl_paranoid.integer)
4771 Mod_Mesh_Validate(mod);
4772 Mod_Mesh_ComputeBounds(mod);
4773 Mod_Mesh_MakeSortedSurfaces(mod);
4774 if(!r_refdef.draw2dstage)
4775 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);
4776 Mod_Mesh_UploadDynamicBuffers(mod);