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