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 = {CVAR_CLIENT | CVAR_SAVE, "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 = {CVAR_CLIENT | CVAR_SAVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
32 cvar_t mod_generatelightmaps_unitspersample = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
33 cvar_t mod_generatelightmaps_borderpixels = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
34 cvar_t mod_generatelightmaps_texturesize = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
35 cvar_t mod_generatelightmaps_lightmapsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
36 cvar_t mod_generatelightmaps_vertexsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
37 cvar_t mod_generatelightmaps_gridsamples = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
38 cvar_t mod_generatelightmaps_lightmapradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
39 cvar_t mod_generatelightmaps_vertexradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
40 cvar_t mod_generatelightmaps_gridradius = {CVAR_CLIENT | CVAR_SAVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
42 dp_model_t *loadmodel;
44 static mempool_t *mod_mempool;
45 static memexpandablearray_t models;
47 static mempool_t* q3shaders_mem;
48 typedef struct q3shader_hash_entry_s
50 q3shaderinfo_t shader;
51 struct q3shader_hash_entry_s* chain;
52 } q3shader_hash_entry_t;
53 #define Q3SHADER_HASH_SIZE 1021
54 typedef struct q3shader_data_s
56 memexpandablearray_t hash_entries;
57 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
58 memexpandablearray_t char_ptrs;
60 static q3shader_data_t* q3shader_data;
62 static void mod_start(void)
65 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
68 SCR_PushLoadingScreen("Loading models", 1.0);
70 for (i = 0;i < nummodels;i++)
71 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
74 for (i = 0;i < nummodels;i++)
75 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
78 SCR_PushLoadingScreen(mod->name, 1.0 / count);
79 Mod_LoadModel(mod, true, false);
80 SCR_PopLoadingScreen(false);
82 SCR_PopLoadingScreen(false);
85 static void mod_shutdown(void)
88 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
91 for (i = 0;i < nummodels;i++)
92 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
96 Mod_Skeletal_FreeBuffers();
99 static void mod_newmap(void)
102 int i, j, k, l, surfacenum, ssize, tsize;
103 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
106 for (i = 0;i < nummodels;i++)
108 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
110 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
112 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
113 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
114 if (mod->data_textures[j].shaderpasses[l])
115 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
116 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
118 if (mod->brush.solidskyskinframe)
119 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
120 if (mod->brush.alphaskyskinframe)
121 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
125 if (!cl_stainmaps_clearonload.integer)
128 for (i = 0;i < nummodels;i++)
130 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
132 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
134 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
136 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
137 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
138 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
139 mod->brushq1.lightmapupdateflags[surfacenum] = true;
151 static void Mod_Print_f(cmd_state_t *cmd);
152 static void Mod_Precache_f(cmd_state_t *cmd);
153 static void Mod_Decompile_f(cmd_state_t *cmd);
154 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd);
157 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
158 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(dp_model_t), 16);
164 Cvar_RegisterVariable(&r_mipskins);
165 Cvar_RegisterVariable(&r_mipnormalmaps);
166 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
167 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
168 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
170 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
171 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
172 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
173 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
174 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
175 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
177 Cmd_AddCommand(&cmd_client, "modellist", Mod_Print_f, "prints a list of loaded models");
178 Cmd_AddCommand(&cmd_client, "modelprecache", Mod_Precache_f, "load a model");
179 Cmd_AddCommand(&cmd_client, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
180 Cmd_AddCommand(&cmd_client, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
183 void Mod_RenderInit(void)
185 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
188 void Mod_UnloadModel (dp_model_t *mod)
190 char name[MAX_QPATH];
192 dp_model_t *parentmodel;
194 if (developer_loading.integer)
195 Con_Printf("unloading model %s\n", mod->name);
197 strlcpy(name, mod->name, sizeof(name));
198 parentmodel = mod->brush.parentmodel;
202 if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
203 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
204 mod->surfmesh.data_element3i_indexbuffer = NULL;
205 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
206 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
207 mod->surfmesh.data_element3s_indexbuffer = NULL;
208 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
209 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
210 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
211 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
212 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
213 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
214 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
215 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
216 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
217 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
218 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
220 // free textures/memory attached to the model
221 R_FreeTexturePool(&mod->texturepool);
222 Mem_FreePool(&mod->mempool);
223 // clear the struct to make it available
224 memset(mod, 0, sizeof(dp_model_t));
225 // restore the fields we want to preserve
226 strlcpy(mod->name, name, sizeof(mod->name));
227 mod->brush.parentmodel = parentmodel;
232 static void R_Model_Null_Draw(entity_render_t *ent)
238 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass);
240 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
255 // REQUIRED: fetch start
256 COM_ParseToken_Simple(&bufptr, true, false, true);
258 break; // end of file
259 if (!strcmp(com_token, "\n"))
260 continue; // empty line
261 start = atoi(com_token);
263 // REQUIRED: fetch length
264 COM_ParseToken_Simple(&bufptr, true, false, true);
265 if (!bufptr || !strcmp(com_token, "\n"))
267 Con_Printf("framegroups file: missing number of frames\n");
270 len = atoi(com_token);
272 // OPTIONAL args start
273 COM_ParseToken_Simple(&bufptr, true, false, true);
275 // OPTIONAL: fetch fps
277 if (bufptr && strcmp(com_token, "\n"))
279 fps = atof(com_token);
280 COM_ParseToken_Simple(&bufptr, true, false, true);
283 // OPTIONAL: fetch loopflag
285 if (bufptr && strcmp(com_token, "\n"))
287 loop = (atoi(com_token) != 0);
288 COM_ParseToken_Simple(&bufptr, true, false, true);
291 // OPTIONAL: fetch name
293 if (bufptr && strcmp(com_token, "\n"))
295 strlcpy(name, com_token, sizeof(name));
296 COM_ParseToken_Simple(&bufptr, true, false, true);
299 // OPTIONAL: remaining unsupported tokens (eat them)
300 while (bufptr && strcmp(com_token, "\n"))
301 COM_ParseToken_Simple(&bufptr, true, false, true);
303 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
306 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
313 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass)
315 dp_model_t *mod = (dp_model_t *) pass;
316 animscene_t *anim = &mod->animscenes[i];
318 strlcpy(anim->name, name, sizeof(anim[i].name));
320 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
321 anim->firstframe = bound(0, start, mod->num_poses - 1);
322 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
323 anim->framerate = max(1, fps);
325 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
328 static void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
333 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
336 Con_Printf("no scene found in framegroups file, aborting\n");
339 mod->numframes = cnt;
342 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
343 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
346 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
349 static void Mod_FindPotentialDeforms(dp_model_t *mod)
353 mod->wantnormals = false;
354 mod->wanttangents = false;
355 for (i = 0;i < mod->num_textures;i++)
357 texture = mod->data_textures + i;
358 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
359 mod->wantnormals = true;
360 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
361 mod->wantnormals = true;
362 for (j = 0;j < Q3MAXDEFORMS;j++)
364 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
366 mod->wanttangents = true;
367 mod->wantnormals = true;
370 if (texture->deforms[j].deform != Q3DEFORM_NONE)
371 mod->wantnormals = true;
383 dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
388 fs_offset_t filesize = 0;
393 if (mod->name[0] == '*') // submodel
396 if (!strcmp(mod->name, "null"))
401 if (mod->loaded || mod->mempool)
402 Mod_UnloadModel(mod);
404 if (developer_loading.integer)
405 Con_Printf("loading model %s\n", mod->name);
408 mod->crc = (unsigned int)-1;
411 VectorClear(mod->normalmins);
412 VectorClear(mod->normalmaxs);
413 VectorClear(mod->yawmins);
414 VectorClear(mod->yawmaxs);
415 VectorClear(mod->rotatedmins);
416 VectorClear(mod->rotatedmaxs);
418 mod->modeldatatypestring = "null";
419 mod->type = mod_null;
420 mod->Draw = R_Model_Null_Draw;
424 // no fatal errors occurred, so this model is ready to use.
433 // even if the model is loaded it still may need reloading...
435 // if it is not loaded or checkdisk is true we need to calculate the crc
436 if (!mod->loaded || checkdisk)
438 if (checkdisk && mod->loaded)
439 Con_DPrintf("checking model %s\n", mod->name);
440 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
443 crc = CRC_Block((unsigned char *)buf, filesize);
444 // we need to reload the model if the crc does not match
450 // if the model is already loaded and checks passed, just return
458 if (developer_loading.integer)
459 Con_Printf("loading model %s\n", mod->name);
461 SCR_PushLoadingScreen(mod->name, 1);
463 // LadyHavoc: unload the existing model in this slot (if there is one)
464 if (mod->loaded || mod->mempool)
465 Mod_UnloadModel(mod);
470 // errors can prevent the corresponding mod->loaded = true;
473 // default lightmap scale
474 mod->lightmapscale = 1;
476 // default model radius and bounding box (mainly for missing models)
478 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
479 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
480 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
481 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
482 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
483 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
487 // load q3 shaders for the first time, or after a level change
493 char *bufend = (char *)buf + filesize;
495 // all models use memory, so allocate a memory pool
496 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
498 num = LittleLong(*((int *)buf));
499 // call the apropriate loader
501 if (!strcasecmp(FS_FileExtension(mod->name), "obj")) Mod_OBJ_Load(mod, buf, bufend);
502 else if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
503 else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend);
504 else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend);
505 else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend);
506 else if (!memcmp(buf, "IDS2", 4)) Mod_IDS2_Load(mod, buf, bufend);
507 else if (!memcmp(buf, "IBSP", 4)) Mod_IBSP_Load(mod, buf, bufend);
508 else if (!memcmp(buf, "ZYMOTICMODEL", 12)) Mod_ZYMOTICMODEL_Load(mod, buf, bufend);
509 else if (!memcmp(buf, "DARKPLACESMODEL", 16)) Mod_DARKPLACESMODEL_Load(mod, buf, bufend);
510 else if (!memcmp(buf, "ACTRHEAD", 8)) Mod_PSKMODEL_Load(mod, buf, bufend);
511 else if (!memcmp(buf, "INTERQUAKEMODEL", 16)) Mod_INTERQUAKEMODEL_Load(mod, buf, bufend);
512 else if (strlen(mod->name) >= 4 && !strcmp(mod->name + strlen(mod->name) - 4, ".map")) Mod_MAP_Load(mod, buf, bufend);
513 else if (num == BSPVERSION || num == 30 || !memcmp(buf, "BSP2", 4) || !memcmp(buf, "2PSB", 4)) Mod_Q1BSP_Load(mod, buf, bufend);
514 else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
517 Mod_FindPotentialDeforms(mod);
519 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
522 Mod_FrameGroupify(mod, (const char *)buf);
530 // LadyHavoc: Sys_Error was *ANNOYING*
531 Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
534 // no fatal errors occurred, so this model is ready to use.
537 SCR_PopLoadingScreen(false);
542 void Mod_ClearUsed(void)
545 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
547 for (i = 0;i < nummodels;i++)
548 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
552 void Mod_PurgeUnused(void)
555 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
557 for (i = 0;i < nummodels;i++)
559 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
561 Mod_UnloadModel(mod);
562 Mem_ExpandableArray_FreeRecord(&models, mod);
573 dp_model_t *Mod_FindName(const char *name, const char *parentname)
582 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
585 Host_Error ("Mod_ForName: empty name");
587 // search the currently loaded models
588 for (i = 0;i < nummodels;i++)
590 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name) && ((!mod->brush.parentmodel && !parentname[0]) || (mod->brush.parentmodel && parentname[0] && !strcmp(mod->brush.parentmodel->name, parentname))))
597 // no match found, create a new one
598 mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
599 strlcpy(mod->name, name, sizeof(mod->name));
601 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
603 mod->brush.parentmodel = NULL;
613 Loads in a model for the given name
616 dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
619 model = Mod_FindName(name, parentname);
620 if (!model->loaded || checkdisk)
621 Mod_LoadModel(model, crash, checkdisk);
629 Reloads all models if they have changed
632 void Mod_Reload(void)
635 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
638 SCR_PushLoadingScreen("Reloading models", 1.0);
640 for (i = 0;i < nummodels;i++)
641 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
643 for (i = 0;i < nummodels;i++)
644 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
646 SCR_PushLoadingScreen(mod->name, 1.0 / count);
647 Mod_LoadModel(mod, true, true);
648 SCR_PopLoadingScreen(false);
650 SCR_PopLoadingScreen(false);
653 unsigned char *mod_base;
656 //=============================================================================
663 static void Mod_Print_f(cmd_state_t *cmd)
666 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
669 Con_Print("Loaded models:\n");
670 for (i = 0;i < nummodels;i++)
672 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
674 if (mod->brush.numsubmodels)
675 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
677 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
687 static void Mod_Precache_f(cmd_state_t *cmd)
689 if (Cmd_Argc(cmd) == 2)
690 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
692 Con_Print("usage: modelprecache <filename>\n");
695 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
699 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
700 memset(used, 0, numvertices);
701 for (i = 0;i < numelements;i++)
702 used[elements[i]] = 1;
703 for (i = 0, count = 0;i < numvertices;i++)
704 remapvertices[i] = used[i] ? count++ : -1;
709 qboolean Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
711 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
713 int invalidintcount = 0, invalidintexample = 0;
714 int invalidshortcount = 0, invalidshortexample = 0;
715 int invalidmismatchcount = 0, invalidmismatchexample = 0;
718 for (i = 0; i < numelements; i++)
720 if (element3i[i] < first || element3i[i] > last)
723 invalidintexample = i;
729 for (i = 0; i < numelements; i++)
731 if (element3s[i] < first || element3s[i] > last)
734 invalidintexample = i;
738 if (element3i && element3s)
740 for (i = 0; i < numelements; i++)
742 if (element3s[i] != element3i[i])
744 invalidmismatchcount++;
745 invalidmismatchexample = i;
749 if (invalidintcount || invalidshortcount || invalidmismatchcount)
751 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, element3i, element3s, filename, fileline);
752 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
753 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
754 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);
755 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
757 // edit the elements to make them safer, as the driver will crash otherwise
759 for (i = 0; i < numelements; i++)
760 if (element3i[i] < first || element3i[i] > last)
761 element3i[i] = first;
763 for (i = 0; i < numelements; i++)
764 if (element3s[i] < first || element3s[i] > last)
765 element3s[i] = first;
766 if (element3i && element3s)
767 for (i = 0; i < numelements; i++)
768 if (element3s[i] != element3i[i])
769 element3s[i] = element3i[i];
776 // warning: this is an expensive function!
777 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
784 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
785 // process each vertex of each triangle and accumulate the results
786 // use area-averaging, to make triangles with a big area have a bigger
787 // weighting on the vertex normal than triangles with a small area
788 // to do so, just add the 'normals' together (the bigger the area
789 // the greater the length of the normal is
791 for (i = 0; i < numtriangles; i++, element += 3)
794 vertex3f + element[0] * 3,
795 vertex3f + element[1] * 3,
796 vertex3f + element[2] * 3,
801 VectorNormalize(areaNormal);
803 for (j = 0;j < 3;j++)
805 vectorNormal = normal3f + element[j] * 3;
806 vectorNormal[0] += areaNormal[0];
807 vectorNormal[1] += areaNormal[1];
808 vectorNormal[2] += areaNormal[2];
811 // and just normalize the accumulated vertex normal in the end
812 vectorNormal = normal3f + 3 * firstvertex;
813 for (i = 0; i < numvertices; i++, vectorNormal += 3)
814 VectorNormalize(vectorNormal);
818 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)
820 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
821 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
822 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
824 // 6 multiply, 9 subtract
825 VectorSubtract(v1, v0, v10);
826 VectorSubtract(v2, v0, v20);
827 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
828 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
829 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
830 // 12 multiply, 10 subtract
831 tc10[1] = tc1[1] - tc0[1];
832 tc20[1] = tc2[1] - tc0[1];
833 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
834 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
835 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
836 tc10[0] = tc1[0] - tc0[0];
837 tc20[0] = tc2[0] - tc0[0];
838 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
839 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
840 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
841 // 12 multiply, 4 add, 6 subtract
842 f = DotProduct(svector3f, normal3f);
843 svector3f[0] -= f * normal3f[0];
844 svector3f[1] -= f * normal3f[1];
845 svector3f[2] -= f * normal3f[2];
846 f = DotProduct(tvector3f, normal3f);
847 tvector3f[0] -= f * normal3f[0];
848 tvector3f[1] -= f * normal3f[1];
849 tvector3f[2] -= f * normal3f[2];
850 // if texture is mapped the wrong way (counterclockwise), the tangents
851 // have to be flipped, this is detected by calculating a normal from the
852 // two tangents, and seeing if it is opposite the surface normal
853 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
854 CrossProduct(tvector3f, svector3f, tangentcross);
855 if (DotProduct(tangentcross, normal3f) < 0)
857 VectorNegate(svector3f, svector3f);
858 VectorNegate(tvector3f, tvector3f);
863 // warning: this is a very expensive function!
864 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, qboolean areaweighting)
867 float sdir[3], tdir[3], normal[3], *svec, *tvec;
868 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
869 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
872 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
873 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
874 // process each vertex of each triangle and accumulate the results
875 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
877 v0 = vertex3f + e[0] * 3;
878 v1 = vertex3f + e[1] * 3;
879 v2 = vertex3f + e[2] * 3;
880 tc0 = texcoord2f + e[0] * 2;
881 tc1 = texcoord2f + e[1] * 2;
882 tc2 = texcoord2f + e[2] * 2;
884 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
885 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
887 // calculate the edge directions and surface normal
888 // 6 multiply, 9 subtract
889 VectorSubtract(v1, v0, v10);
890 VectorSubtract(v2, v0, v20);
891 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
892 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
893 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
895 // calculate the tangents
896 // 12 multiply, 10 subtract
897 tc10[1] = tc1[1] - tc0[1];
898 tc20[1] = tc2[1] - tc0[1];
899 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
900 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
901 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
902 tc10[0] = tc1[0] - tc0[0];
903 tc20[0] = tc2[0] - tc0[0];
904 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
905 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
906 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
908 // if texture is mapped the wrong way (counterclockwise), the tangents
909 // have to be flipped, this is detected by calculating a normal from the
910 // two tangents, and seeing if it is opposite the surface normal
911 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
912 CrossProduct(tdir, sdir, tangentcross);
913 if (DotProduct(tangentcross, normal) < 0)
915 VectorNegate(sdir, sdir);
916 VectorNegate(tdir, tdir);
921 VectorNormalize(sdir);
922 VectorNormalize(tdir);
924 for (i = 0;i < 3;i++)
926 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
927 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
930 // make the tangents completely perpendicular to the surface normal, and
931 // then normalize them
932 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
933 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
935 f = -DotProduct(svec, n);
936 VectorMA(svec, f, n, svec);
937 VectorNormalize(svec);
938 f = -DotProduct(tvec, n);
939 VectorMA(tvec, f, n, tvec);
940 VectorNormalize(tvec);
944 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors)
947 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));
948 loadmodel->surfmesh.num_vertices = numvertices;
949 loadmodel->surfmesh.num_triangles = numtriangles;
950 if (loadmodel->surfmesh.num_vertices)
952 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
953 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
954 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
955 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
956 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
957 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
959 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
961 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
963 if (loadmodel->surfmesh.num_triangles)
965 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
966 if (loadmodel->surfmesh.num_vertices <= 65536)
967 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
971 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
973 shadowmesh_t *newmesh;
974 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
975 newmesh->mempool = mempool;
976 newmesh->maxverts = maxverts;
977 newmesh->maxtriangles = maxtriangles;
978 newmesh->numverts = 0;
979 newmesh->numtriangles = 0;
980 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
981 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
983 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
984 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
985 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
986 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
990 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
993 shadowmeshvertexhash_t *hash;
994 // this uses prime numbers intentionally
995 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
996 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
998 vnum = (hash - mesh->vertexhashentries);
999 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1000 return hash - mesh->vertexhashentries;
1002 vnum = mesh->numverts++;
1003 hash = mesh->vertexhashentries + vnum;
1004 hash->next = mesh->vertexhashtable[hashindex];
1005 mesh->vertexhashtable[hashindex] = hash;
1006 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1007 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1008 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1012 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1016 for (i = 0;i < numtris;i++)
1018 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1019 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1020 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1021 mesh->numtriangles++;
1024 // the triangle calculation can take a while, so let's do a keepalive here
1025 CL_KeepaliveMessage(false);
1028 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1030 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1031 CL_KeepaliveMessage(false);
1033 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1036 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1038 if (!mesh->numverts)
1041 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1042 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1044 // upload short indices as a buffer
1045 if (mesh->element3s && !mesh->element3s_indexbuffer)
1046 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1048 // upload int indices as a buffer
1049 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1050 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1052 // vertex buffer is several arrays and we put them in the same buffer
1054 // is this wise? the texcoordtexture2f array is used with dynamic
1055 // vertex/svector/tvector/normal when rendering animated models, on the
1056 // other hand animated models don't use a lot of vertices anyway...
1057 if (!mesh->vbo_vertexbuffer)
1059 mesh->vbooffset_vertex3f = 0;
1060 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1064 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qboolean createvbo)
1066 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1068 if (mesh->vertexhashentries)
1069 Mem_Free(mesh->vertexhashentries);
1070 mesh->vertexhashentries = NULL;
1071 if (mesh->vertexhashtable)
1072 Mem_Free(mesh->vertexhashtable);
1073 mesh->vertexhashtable = NULL;
1074 if (mesh->maxverts > mesh->numverts)
1076 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1077 mesh->maxverts = mesh->numverts;
1079 if (mesh->maxtriangles > mesh->numtriangles)
1081 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1082 mesh->maxtriangles = mesh->numtriangles;
1084 if (mesh->numverts <= 65536)
1087 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1088 for (i = 0;i < mesh->numtriangles*3;i++)
1089 mesh->element3s[i] = mesh->element3i[i];
1092 Mod_ShadowMesh_CreateVBOs(mesh);
1095 // this can take a while, so let's do a keepalive here
1096 CL_KeepaliveMessage(false);
1101 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1104 vec3_t nmins, nmaxs, ncenter, temp;
1105 float nradius2, dist2, *v;
1109 VectorCopy(mesh->vertex3f, nmins);
1110 VectorCopy(mesh->vertex3f, nmaxs);
1111 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1113 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1114 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1115 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1117 // calculate center and radius
1118 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1119 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1120 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1122 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1124 VectorSubtract(v, ncenter, temp);
1125 dist2 = DotProduct(temp, temp);
1126 if (nradius2 < dist2)
1131 VectorCopy(nmins, mins);
1133 VectorCopy(nmaxs, maxs);
1135 VectorCopy(ncenter, center);
1137 *radius = sqrt(nradius2);
1140 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1142 if (mesh->element3i_indexbuffer)
1143 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1144 if (mesh->element3s_indexbuffer)
1145 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1146 if (mesh->vbo_vertexbuffer)
1147 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1149 Mem_Free(mesh->vertex3f);
1150 if (mesh->element3i)
1151 Mem_Free(mesh->element3i);
1152 if (mesh->element3s)
1153 Mem_Free(mesh->element3s);
1154 if (mesh->vertexhashentries)
1155 Mem_Free(mesh->vertexhashentries);
1156 if (mesh->vertexhashtable)
1157 Mem_Free(mesh->vertexhashtable);
1161 void Mod_CreateCollisionMesh(dp_model_t *mod)
1163 int k, numcollisionmeshtriangles;
1164 qboolean usesinglecollisionmesh = false;
1165 const msurface_t *surface = NULL;
1167 mempool_t *mempool = mod->mempool;
1168 if (!mempool && mod->brush.parentmodel)
1169 mempool = mod->brush.parentmodel->mempool;
1170 // make a single combined collision mesh for physics engine use
1171 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1172 numcollisionmeshtriangles = 0;
1173 for (k = 0;k < mod->nummodelsurfaces;k++)
1175 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1176 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1178 usesinglecollisionmesh = true;
1179 numcollisionmeshtriangles = surface->num_triangles;
1182 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1184 numcollisionmeshtriangles += surface->num_triangles;
1186 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1187 if (usesinglecollisionmesh)
1188 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1191 for (k = 0;k < mod->nummodelsurfaces;k++)
1193 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1194 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1196 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1199 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1203 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)
1208 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1209 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1212 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1213 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1214 texcoord2f[0] = tc[0];
1215 texcoord2f[1] = tc[1];
1218 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)
1220 float vup[3], vdown[3], vleft[3], vright[3];
1221 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1222 float sv[3], tv[3], nl[3];
1223 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1224 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1225 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1226 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1227 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1228 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1229 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1230 VectorAdd(svector3f, sv, svector3f);
1231 VectorAdd(tvector3f, tv, tvector3f);
1232 VectorAdd(normal3f, nl, normal3f);
1233 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1234 VectorAdd(svector3f, sv, svector3f);
1235 VectorAdd(tvector3f, tv, tvector3f);
1236 VectorAdd(normal3f, nl, normal3f);
1237 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1238 VectorAdd(svector3f, sv, svector3f);
1239 VectorAdd(tvector3f, tv, tvector3f);
1240 VectorAdd(normal3f, nl, normal3f);
1243 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)
1245 int x, y, ix, iy, *e;
1247 for (y = 0;y < height;y++)
1249 for (x = 0;x < width;x++)
1251 e[0] = (y + 1) * (width + 1) + (x + 0);
1252 e[1] = (y + 0) * (width + 1) + (x + 0);
1253 e[2] = (y + 1) * (width + 1) + (x + 1);
1254 e[3] = (y + 0) * (width + 1) + (x + 0);
1255 e[4] = (y + 0) * (width + 1) + (x + 1);
1256 e[5] = (y + 1) * (width + 1) + (x + 1);
1260 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1261 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1262 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1267 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1271 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1272 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1273 float viewvector[3];
1274 unsigned int firstvertex;
1277 if (chunkwidth < 2 || chunkheight < 2)
1279 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]);
1280 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]);
1281 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1282 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1283 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1284 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1286 // too close for this stepsize, emit as 4 chunks instead
1288 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1289 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1290 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1291 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1294 // emit the geometry at stepsize into our vertex buffer / index buffer
1295 // we add two columns and two rows for skirt
1296 outwidth = chunkwidth+2;
1297 outheight = chunkheight+2;
1298 outwidth2 = outwidth-1;
1299 outheight2 = outheight-1;
1300 outwidth3 = outwidth+1;
1301 outheight3 = outheight+1;
1302 firstvertex = numvertices;
1303 e = model->terrain.element3i + numtriangles;
1304 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1305 v = model->terrain.vertex3f + numvertices;
1306 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1307 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1308 for (ty = 0;ty < outheight;ty++)
1310 for (tx = 0;tx < outwidth;tx++)
1312 *e++ = firstvertex + (ty )*outwidth3+(tx );
1313 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1314 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1315 *e++ = firstvertex + (ty )*outwidth3+(tx );
1316 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1317 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1320 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1321 for (ty = 0;ty <= outheight;ty++)
1323 skirtrow = ty == 0 || ty == outheight;
1324 ry = y+bound(1, ty, outheight)*stepsize;
1325 for (tx = 0;tx <= outwidth;tx++)
1327 skirt = skirtrow || tx == 0 || tx == outwidth;
1328 rx = x+bound(1, tx, outwidth)*stepsize;
1331 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1335 // TODO: emit skirt vertices
1338 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1340 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1341 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1342 Mod_Terrain_BuildChunk(model,
1346 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1349 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1351 offset = bound(0, s[4] - '0', 9);
1352 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1357 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1358 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1359 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1360 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1361 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1362 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1363 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1364 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1365 return offset | Q3WAVEFUNC_NONE;
1368 void Mod_FreeQ3Shaders(void)
1370 Mem_FreePool(&q3shaders_mem);
1373 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
1375 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1376 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1377 q3shader_hash_entry_t* lastEntry = NULL;
1380 if (strcasecmp (entry->shader.name, shader->name) == 0)
1383 if(shader->dpshaderkill)
1385 // killed shader is a redeclarion? we can safely ignore it
1388 else if(entry->shader.dpshaderkill)
1390 // replace the old shader!
1391 // this will skip the entry allocating part
1392 // below and just replace the shader
1397 unsigned char *start, *end, *start2;
1398 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1399 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1400 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1401 if(memcmp(start, start2, end - start))
1402 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1404 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1409 entry = entry->chain;
1411 while (entry != NULL);
1414 if (lastEntry->shader.name[0] != 0)
1417 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1418 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1420 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1421 lastEntry->chain = newEntry;
1422 newEntry->chain = NULL;
1423 lastEntry = newEntry;
1425 /* else: head of chain, in hash entry array */
1428 memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
1431 void Mod_LoadQ3Shaders(void)
1438 q3shaderinfo_t shader;
1439 q3shaderinfo_layer_t *layer;
1441 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1442 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1443 unsigned long custsurfaceflags[256];
1444 int numcustsurfaceflags;
1445 qboolean dpshaderkill;
1447 Mod_FreeQ3Shaders();
1449 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1450 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1451 sizeof (q3shader_data_t));
1452 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1453 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1454 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1455 q3shaders_mem, sizeof (char**), 256);
1457 // parse custinfoparms.txt
1458 numcustsurfaceflags = 0;
1459 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1461 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1462 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1465 while (COM_ParseToken_QuakeC(&text, false))
1466 if (!strcasecmp(com_token, "}"))
1468 // custom surfaceflags section
1469 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1470 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1473 while(COM_ParseToken_QuakeC(&text, false))
1475 if (!strcasecmp(com_token, "}"))
1477 // register surfaceflag
1478 if (numcustsurfaceflags >= 256)
1480 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1484 j = (int)strlen(com_token)+1;
1485 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1486 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1488 if (COM_ParseToken_QuakeC(&text, false))
1489 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1491 custsurfaceflags[numcustsurfaceflags] = 0;
1492 numcustsurfaceflags++;
1500 search = FS_Search("scripts/*.shader", true, false);
1503 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1505 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1508 while (COM_ParseToken_QuakeC(&text, false))
1510 memset (&shader, 0, sizeof(shader));
1512 shader.surfaceparms = 0;
1513 shader.surfaceflags = 0;
1514 shader.textureflags = 0;
1515 shader.numlayers = 0;
1516 shader.lighting = false;
1517 shader.vertexalpha = false;
1518 shader.textureblendalpha = false;
1519 shader.skyboxname[0] = 0;
1520 shader.deforms[0].deform = Q3DEFORM_NONE;
1521 shader.dpnortlight = false;
1522 shader.dpshadow = false;
1523 shader.dpnoshadow = false;
1524 shader.dpmeshcollisions = false;
1525 shader.dpshaderkill = false;
1526 shader.dpreflectcube[0] = 0;
1527 shader.reflectmin = 0;
1528 shader.reflectmax = 1;
1529 shader.refractfactor = 1;
1530 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1531 shader.reflectfactor = 1;
1532 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1533 shader.r_water_wateralpha = 1;
1534 shader.r_water_waterscroll[0] = 0;
1535 shader.r_water_waterscroll[1] = 0;
1536 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1537 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1538 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1539 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1540 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1541 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1542 shader.specularscalemod = 1;
1543 shader.specularpowermod = 1;
1544 shader.rtlightambient = 0;
1545 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1546 // JUST GREP FOR "specularscalemod = 1".
1548 strlcpy(shader.name, com_token, sizeof(shader.name));
1549 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1551 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1554 while (COM_ParseToken_QuakeC(&text, false))
1556 if (!strcasecmp(com_token, "}"))
1558 if (!strcasecmp(com_token, "{"))
1560 static q3shaderinfo_layer_t dummy;
1561 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1563 layer = shader.layers + shader.numlayers++;
1567 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1568 memset(&dummy, 0, sizeof(dummy));
1571 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1572 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1573 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1574 layer->blendfunc[0] = GL_ONE;
1575 layer->blendfunc[1] = GL_ZERO;
1576 while (COM_ParseToken_QuakeC(&text, false))
1578 if (!strcasecmp(com_token, "}"))
1580 if (!strcasecmp(com_token, "\n"))
1583 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1585 if (j < TEXTURE_MAXFRAMES + 4)
1587 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1588 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1589 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1591 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1592 numparameters = j + 1;
1594 if (!COM_ParseToken_QuakeC(&text, true))
1597 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1598 // parameter[j][0] = 0;
1599 if (developer_insane.integer)
1601 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1602 for (j = 0;j < numparameters;j++)
1603 Con_DPrintf(" %s", parameter[j]);
1606 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1608 if (numparameters == 2)
1610 if (!strcasecmp(parameter[1], "add"))
1612 layer->blendfunc[0] = GL_ONE;
1613 layer->blendfunc[1] = GL_ONE;
1615 else if (!strcasecmp(parameter[1], "addalpha"))
1617 layer->blendfunc[0] = GL_SRC_ALPHA;
1618 layer->blendfunc[1] = GL_ONE;
1620 else if (!strcasecmp(parameter[1], "filter"))
1622 layer->blendfunc[0] = GL_DST_COLOR;
1623 layer->blendfunc[1] = GL_ZERO;
1625 else if (!strcasecmp(parameter[1], "blend"))
1627 layer->blendfunc[0] = GL_SRC_ALPHA;
1628 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1631 else if (numparameters == 3)
1634 for (k = 0;k < 2;k++)
1636 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1637 layer->blendfunc[k] = GL_ONE;
1638 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1639 layer->blendfunc[k] = GL_ZERO;
1640 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1641 layer->blendfunc[k] = GL_SRC_COLOR;
1642 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1643 layer->blendfunc[k] = GL_SRC_ALPHA;
1644 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1645 layer->blendfunc[k] = GL_DST_COLOR;
1646 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1647 layer->blendfunc[k] = GL_DST_ALPHA;
1648 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1649 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1650 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1651 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1652 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1653 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1654 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1655 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1657 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1661 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1662 layer->alphatest = true;
1663 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1665 if (!strcasecmp(parameter[0], "clampmap"))
1666 layer->clampmap = true;
1667 layer->numframes = 1;
1668 layer->framerate = 1;
1669 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1670 &q3shader_data->char_ptrs);
1671 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1672 if (!strcasecmp(parameter[1], "$lightmap"))
1673 shader.lighting = true;
1675 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1678 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1679 layer->framerate = atof(parameter[1]);
1680 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1681 for (i = 0;i < layer->numframes;i++)
1682 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1684 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1687 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1688 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1689 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1690 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1691 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1692 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1693 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1694 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1695 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1696 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1697 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1698 else if (!strcasecmp(parameter[1], "wave"))
1700 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1701 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1702 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1703 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1705 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1707 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1710 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1711 layer->alphagen.parms[i] = atof(parameter[i+2]);
1712 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1713 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1714 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1715 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1716 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1717 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1718 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1719 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1720 else if (!strcasecmp(parameter[1], "wave"))
1722 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1723 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1724 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1725 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1727 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1729 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1732 // observed values: tcgen environment
1733 // no other values have been observed in real shaders
1734 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1735 layer->tcgen.parms[i] = atof(parameter[i+2]);
1736 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1737 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1738 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1739 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1740 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1741 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1743 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1750 // tcmod stretch sin # # # #
1751 // tcmod stretch triangle # # # #
1752 // tcmod transform # # # # # #
1753 // tcmod turb # # # #
1754 // tcmod turb sin # # # # (this is bogus)
1755 // no other values have been observed in real shaders
1756 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1757 if (!layer->tcmods[tcmodindex].tcmod)
1759 if (tcmodindex < Q3MAXTCMODS)
1761 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1762 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1763 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1764 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1765 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1766 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1767 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1768 else if (!strcasecmp(parameter[1], "stretch"))
1770 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1771 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1772 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1773 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1775 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1776 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1777 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1780 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1782 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1783 if (!strcasecmp(com_token, "}"))
1786 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1787 shader.lighting = true;
1788 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1790 if (layer == shader.layers + 0)
1792 // vertex controlled transparency
1793 shader.vertexalpha = true;
1797 // multilayer terrain shader or similar
1798 shader.textureblendalpha = true;
1799 if (mod_q3shader_force_terrain_alphaflag.integer)
1800 shader.layers[0].dptexflags |= TEXF_ALPHA;
1804 if(mod_q3shader_force_addalpha.integer)
1806 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1807 // this cvar brings back this behaviour
1808 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1809 layer->blendfunc[0] = GL_SRC_ALPHA;
1812 layer->dptexflags = 0;
1813 if (layer->alphatest)
1814 layer->dptexflags |= TEXF_ALPHA;
1815 switch(layer->blendfunc[0])
1818 case GL_ONE_MINUS_SRC_ALPHA:
1819 layer->dptexflags |= TEXF_ALPHA;
1822 switch(layer->blendfunc[1])
1825 case GL_ONE_MINUS_SRC_ALPHA:
1826 layer->dptexflags |= TEXF_ALPHA;
1829 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1830 layer->dptexflags |= TEXF_MIPMAP;
1831 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1832 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1833 if (layer->clampmap)
1834 layer->dptexflags |= TEXF_CLAMP;
1838 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1840 if (j < TEXTURE_MAXFRAMES + 4)
1842 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1843 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1844 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1846 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1847 numparameters = j + 1;
1849 if (!COM_ParseToken_QuakeC(&text, true))
1852 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1853 // parameter[j][0] = 0;
1854 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1856 if (developer_insane.integer)
1858 Con_DPrintf("%s: ", shader.name);
1859 for (j = 0;j < numparameters;j++)
1860 Con_DPrintf(" %s", parameter[j]);
1863 if (numparameters < 1)
1865 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1867 if (!strcasecmp(parameter[1], "alphashadow"))
1868 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1869 else if (!strcasecmp(parameter[1], "areaportal"))
1870 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1871 else if (!strcasecmp(parameter[1], "botclip"))
1872 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1873 else if (!strcasecmp(parameter[1], "clusterportal"))
1874 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1875 else if (!strcasecmp(parameter[1], "detail"))
1876 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1877 else if (!strcasecmp(parameter[1], "donotenter"))
1878 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1879 else if (!strcasecmp(parameter[1], "dust"))
1880 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1881 else if (!strcasecmp(parameter[1], "hint"))
1882 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1883 else if (!strcasecmp(parameter[1], "fog"))
1884 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1885 else if (!strcasecmp(parameter[1], "lava"))
1886 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1887 else if (!strcasecmp(parameter[1], "lightfilter"))
1888 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1889 else if (!strcasecmp(parameter[1], "lightgrid"))
1890 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1891 else if (!strcasecmp(parameter[1], "metalsteps"))
1892 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1893 else if (!strcasecmp(parameter[1], "nodamage"))
1894 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1895 else if (!strcasecmp(parameter[1], "nodlight"))
1896 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1897 else if (!strcasecmp(parameter[1], "nodraw"))
1898 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1899 else if (!strcasecmp(parameter[1], "nodrop"))
1900 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1901 else if (!strcasecmp(parameter[1], "noimpact"))
1902 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1903 else if (!strcasecmp(parameter[1], "nolightmap"))
1904 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1905 else if (!strcasecmp(parameter[1], "nomarks"))
1906 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1907 else if (!strcasecmp(parameter[1], "nomipmaps"))
1908 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1909 else if (!strcasecmp(parameter[1], "nonsolid"))
1910 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1911 else if (!strcasecmp(parameter[1], "origin"))
1912 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1913 else if (!strcasecmp(parameter[1], "playerclip"))
1914 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1915 else if (!strcasecmp(parameter[1], "sky"))
1916 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1917 else if (!strcasecmp(parameter[1], "slick"))
1918 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1919 else if (!strcasecmp(parameter[1], "slime"))
1920 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1921 else if (!strcasecmp(parameter[1], "structural"))
1922 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1923 else if (!strcasecmp(parameter[1], "trans"))
1924 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1925 else if (!strcasecmp(parameter[1], "water"))
1926 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1927 else if (!strcasecmp(parameter[1], "pointlight"))
1928 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1929 else if (!strcasecmp(parameter[1], "antiportal"))
1930 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1931 else if (!strcasecmp(parameter[1], "skip"))
1932 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1935 // try custom surfaceparms
1936 for (j = 0; j < numcustsurfaceflags; j++)
1938 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1940 shader.surfaceflags |= custsurfaceflags[j];
1945 if (j == numcustsurfaceflags)
1946 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1949 else if (!strcasecmp(parameter[0], "dpshadow"))
1950 shader.dpshadow = true;
1951 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1952 shader.dpnoshadow = true;
1953 else if (!strcasecmp(parameter[0], "dpnortlight"))
1954 shader.dpnortlight = true;
1955 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1956 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1957 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1958 shader.dpmeshcollisions = true;
1959 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1960 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1962 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1963 shader.dpshaderkill = dpshaderkill;
1965 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1966 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1968 const char *op = NULL;
1969 if (numparameters >= 3)
1973 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
1974 shader.dpshaderkill = dpshaderkill;
1976 else if (numparameters >= 4 && !strcmp(op, "=="))
1978 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
1979 shader.dpshaderkill = dpshaderkill;
1981 else if (numparameters >= 4 && !strcmp(op, "!="))
1983 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
1984 shader.dpshaderkill = dpshaderkill;
1986 else if (numparameters >= 4 && !strcmp(op, ">"))
1988 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
1989 shader.dpshaderkill = dpshaderkill;
1991 else if (numparameters >= 4 && !strcmp(op, "<"))
1993 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
1994 shader.dpshaderkill = dpshaderkill;
1996 else if (numparameters >= 4 && !strcmp(op, ">="))
1998 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
1999 shader.dpshaderkill = dpshaderkill;
2001 else if (numparameters >= 4 && !strcmp(op, "<="))
2003 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
2004 shader.dpshaderkill = dpshaderkill;
2008 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2011 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2013 // some q3 skies don't have the sky parm set
2014 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2015 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2017 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2019 // some q3 skies don't have the sky parm set
2020 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2021 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2022 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2024 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2026 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2027 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2029 else if (!strcasecmp(parameter[0], "nomipmaps"))
2030 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2031 else if (!strcasecmp(parameter[0], "nopicmip"))
2032 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2033 else if (!strcasecmp(parameter[0], "polygonoffset"))
2034 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2035 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2037 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2038 if(numparameters >= 2)
2040 shader.biaspolygonfactor = atof(parameter[1]);
2041 if(numparameters >= 3)
2042 shader.biaspolygonoffset = atof(parameter[2]);
2044 shader.biaspolygonoffset = 0;
2047 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2049 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2050 if (!strcasecmp(parameter[1], "sky"))
2051 shader.transparentsort = TRANSPARENTSORT_SKY;
2052 else if (!strcasecmp(parameter[1], "distance"))
2053 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2054 else if (!strcasecmp(parameter[1], "hud"))
2055 shader.transparentsort = TRANSPARENTSORT_HUD;
2057 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2059 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2061 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2062 shader.refractfactor = atof(parameter[1]);
2063 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2065 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2067 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2068 shader.reflectfactor = atof(parameter[1]);
2069 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2071 else if (!strcasecmp(parameter[0], "dpcamera"))
2073 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2075 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2077 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2078 shader.reflectmin = atof(parameter[1]);
2079 shader.reflectmax = atof(parameter[2]);
2080 shader.refractfactor = atof(parameter[3]);
2081 shader.reflectfactor = atof(parameter[4]);
2082 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2083 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2084 shader.r_water_wateralpha = atof(parameter[11]);
2086 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2088 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2089 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2091 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2093 shader.specularscalemod = atof(parameter[1]);
2095 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2097 shader.specularpowermod = atof(parameter[1]);
2099 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2101 shader.rtlightambient = atof(parameter[1]);
2103 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2105 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2106 shader.offsetmapping = OFFSETMAPPING_OFF;
2107 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2108 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2109 else if (!strcasecmp(parameter[1], "linear"))
2110 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2111 else if (!strcasecmp(parameter[1], "relief"))
2112 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2113 if (numparameters >= 3)
2114 shader.offsetscale = atof(parameter[2]);
2115 if (numparameters >= 5)
2117 if(!strcasecmp(parameter[3], "bias"))
2118 shader.offsetbias = atof(parameter[4]);
2119 else if(!strcasecmp(parameter[3], "match"))
2120 shader.offsetbias = 1.0f - atof(parameter[4]);
2121 else if(!strcasecmp(parameter[3], "match8"))
2122 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2123 else if(!strcasecmp(parameter[3], "match16"))
2124 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2127 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2130 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2131 if (!shader.deforms[deformindex].deform)
2133 if (deformindex < Q3MAXDEFORMS)
2135 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2136 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2137 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2138 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2139 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2140 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2141 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2142 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2143 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2144 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2145 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2146 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2147 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2148 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2149 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2150 else if (!strcasecmp(parameter[1], "wave" ))
2152 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2153 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2154 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2155 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2157 else if (!strcasecmp(parameter[1], "move" ))
2159 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2160 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2161 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2162 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2167 // hide this shader if a cvar said it should be killed
2168 if (shader.dpshaderkill)
2169 shader.numlayers = 0;
2170 // fix up multiple reflection types
2171 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2172 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2174 Q3Shader_AddToHash (&shader);
2178 FS_FreeSearch(search);
2179 // free custinfoparm values
2180 for (j = 0; j < numcustsurfaceflags; j++)
2181 Mem_Free(custsurfaceparmnames[j]);
2184 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
2186 unsigned short hash;
2187 q3shader_hash_entry_t* entry;
2189 Mod_LoadQ3Shaders();
2190 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2191 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2192 while (entry != NULL)
2194 if (strcasecmp (entry->shader.name, name) == 0)
2195 return &entry->shader;
2196 entry = entry->chain;
2201 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2203 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2204 shaderpass->framerate = 0.0f;
2205 shaderpass->numframes = 1;
2206 shaderpass->blendfunc[0] = GL_ONE;
2207 shaderpass->blendfunc[1] = GL_ZERO;
2208 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2209 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2210 shaderpass->alphatest = false;
2211 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2212 shaderpass->skinframes[0] = skinframe;
2216 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2219 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2220 shaderpass->alphatest = layer->alphatest != 0;
2221 shaderpass->framerate = layer->framerate;
2222 shaderpass->numframes = layer->numframes;
2223 shaderpass->blendfunc[0] = layer->blendfunc[0];
2224 shaderpass->blendfunc[1] = layer->blendfunc[1];
2225 shaderpass->rgbgen = layer->rgbgen;
2226 shaderpass->alphagen = layer->alphagen;
2227 shaderpass->tcgen = layer->tcgen;
2228 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2229 shaderpass->tcmods[j] = layer->tcmods[j];
2230 for (j = 0; j < layer->numframes; j++)
2231 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2235 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2237 int texflagsmask, texflagsor;
2238 qboolean success = true;
2239 q3shaderinfo_t *shader;
2242 strlcpy(texture->name, name, sizeof(texture->name));
2243 texture->basealpha = 1.0f;
2244 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2246 // allow disabling of picmip or compression by defaulttexflags
2248 if(!(defaulttexflags & TEXF_PICMIP))
2249 texflagsmask &= ~TEXF_PICMIP;
2250 if(!(defaulttexflags & TEXF_COMPRESS))
2251 texflagsmask &= ~TEXF_COMPRESS;
2253 if(defaulttexflags & TEXF_ISWORLD)
2254 texflagsor |= TEXF_ISWORLD;
2255 if(defaulttexflags & TEXF_ISSPRITE)
2256 texflagsor |= TEXF_ISSPRITE;
2257 // unless later loaded from the shader
2258 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2259 texture->offsetscale = 1;
2260 texture->offsetbias = 0;
2261 texture->specularscalemod = 1;
2262 texture->specularpowermod = 1;
2263 texture->rtlightambient = 0;
2264 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2265 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2266 // JUST GREP FOR "specularscalemod = 1".
2270 if (developer_loading.integer)
2271 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2273 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2275 texture->basematerialflags = MATERIALFLAG_SKY;
2276 if (shader->skyboxname[0] && loadmodel)
2278 // quake3 seems to append a _ to the skybox name, so this must do so as well
2279 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2282 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2283 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2285 texture->basematerialflags = MATERIALFLAG_WALL;
2287 if (shader->layers[0].alphatest)
2288 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2289 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2290 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2291 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2293 texture->biaspolygonoffset += shader->biaspolygonoffset;
2294 texture->biaspolygonfactor += shader->biaspolygonfactor;
2296 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2297 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2298 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2299 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2300 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2301 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2302 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2303 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2304 texture->customblendfunc[0] = GL_ONE;
2305 texture->customblendfunc[1] = GL_ZERO;
2306 texture->transparentsort = shader->transparentsort;
2307 if (shader->numlayers > 0)
2309 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2310 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2312 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2313 * additive GL_ONE GL_ONE
2314 additive weird GL_ONE GL_SRC_ALPHA
2315 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2316 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2317 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2318 brighten GL_DST_COLOR GL_ONE
2319 brighten GL_ONE GL_SRC_COLOR
2320 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2321 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2322 * modulate GL_DST_COLOR GL_ZERO
2323 * modulate GL_ZERO GL_SRC_COLOR
2324 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2325 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2326 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2327 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2328 * no blend GL_ONE GL_ZERO
2329 nothing GL_ZERO GL_ONE
2331 // if not opaque, figure out what blendfunc to use
2332 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2334 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2335 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2336 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2337 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2338 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2339 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2341 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2344 if (!shader->lighting)
2345 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2347 // here be dragons: convert quake3 shaders to material
2348 if (shader->numlayers > 0)
2351 int terrainbackgroundlayer = -1;
2352 int lightmaplayer = -1;
2353 int alphagenspecularlayer = -1;
2354 int rgbgenvertexlayer = -1;
2355 int rgbgendiffuselayer = -1;
2356 int materiallayer = -1;
2357 int endofprelayers = 0;
2358 int firstpostlayer = 0;
2359 int shaderpassindex = 0;
2360 for (i = 0; i < shader->numlayers; i++)
2362 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2364 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2365 rgbgenvertexlayer = i;
2366 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2367 rgbgendiffuselayer = i;
2368 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2369 alphagenspecularlayer = i;
2371 if (shader->numlayers >= 2
2372 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2373 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2374 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2375 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2377 // terrain blend or certain other effects involving alphatest over a regular layer
2378 terrainbackgroundlayer = 0;
2380 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2381 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2383 else if (lightmaplayer == 0)
2385 // ordinary texture but with $lightmap before diffuse
2387 firstpostlayer = lightmaplayer + 2;
2389 else if (lightmaplayer >= 1)
2391 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2392 endofprelayers = lightmaplayer - 1;
2393 materiallayer = lightmaplayer - 1;
2394 firstpostlayer = lightmaplayer + 1;
2396 else if (rgbgenvertexlayer >= 0)
2398 // map models with baked lighting
2399 materiallayer = rgbgenvertexlayer;
2400 endofprelayers = rgbgenvertexlayer;
2401 firstpostlayer = rgbgenvertexlayer + 1;
2402 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2403 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2404 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2406 else if (rgbgendiffuselayer >= 0)
2408 // entity models with dynamic lighting
2409 materiallayer = rgbgendiffuselayer;
2410 endofprelayers = rgbgendiffuselayer;
2411 firstpostlayer = rgbgendiffuselayer + 1;
2412 // 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)...
2413 if (alphagenspecularlayer >= 0)
2414 firstpostlayer = alphagenspecularlayer + 1;
2418 // special effects shaders - treat first as primary layer and do everything else as post
2423 // convert the main material layer
2424 // 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
2425 if (materiallayer >= 0)
2426 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2427 // convert the terrain background blend layer (if any)
2428 if (terrainbackgroundlayer >= 0)
2429 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2430 // convert the prepass layers (if any)
2431 texture->startpreshaderpass = shaderpassindex;
2432 for (i = 0; i < endofprelayers; i++)
2433 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2434 texture->endpreshaderpass = shaderpassindex;
2435 texture->startpostshaderpass = shaderpassindex;
2436 // convert the postpass layers (if any)
2437 for (i = firstpostlayer; i < shader->numlayers; i++)
2438 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2439 texture->startpostshaderpass = shaderpassindex;
2442 if (shader->dpshadow)
2443 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2444 if (shader->dpnoshadow)
2445 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2446 if (shader->dpnortlight)
2447 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2448 if (shader->vertexalpha)
2449 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2450 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2451 texture->reflectmin = shader->reflectmin;
2452 texture->reflectmax = shader->reflectmax;
2453 texture->refractfactor = shader->refractfactor;
2454 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2455 texture->reflectfactor = shader->reflectfactor;
2456 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2457 texture->r_water_wateralpha = shader->r_water_wateralpha;
2458 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2459 texture->offsetmapping = shader->offsetmapping;
2460 texture->offsetscale = shader->offsetscale;
2461 texture->offsetbias = shader->offsetbias;
2462 texture->specularscalemod = shader->specularscalemod;
2463 texture->specularpowermod = shader->specularpowermod;
2464 texture->rtlightambient = shader->rtlightambient;
2465 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2466 if (shader->dpreflectcube[0])
2467 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2469 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2470 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2471 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2472 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2473 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2474 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2475 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2476 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2477 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2479 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2480 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2481 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2482 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2483 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2484 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2485 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2486 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2487 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2488 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2489 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2490 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2491 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2492 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2493 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2494 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2495 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2496 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2497 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2498 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2499 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2500 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2501 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2502 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2503 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2504 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2505 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2506 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2507 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2508 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2509 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2510 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2512 texture->surfaceflags = shader->surfaceflags;
2513 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2514 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2515 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2516 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2520 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2521 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2522 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2523 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2524 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2525 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2526 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2527 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2528 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2529 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2530 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2531 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2532 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2534 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2536 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2537 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2539 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2540 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2541 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2542 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2543 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2544 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2546 if (shader->dpmeshcollisions)
2547 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2548 if (shader->dpshaderkill && developer_extra.integer)
2549 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2551 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2553 if (developer_extra.integer)
2554 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2555 texture->basematerialflags = defaultmaterialflags;
2556 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2558 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2560 if (developer_extra.integer)
2561 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2562 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2563 texture->supercontents = SUPERCONTENTS_SOLID;
2567 if (developer_extra.integer)
2568 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2569 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2571 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2572 texture->supercontents = SUPERCONTENTS_SOLID;
2574 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2576 texture->basematerialflags = MATERIALFLAG_SKY;
2577 texture->supercontents = SUPERCONTENTS_SKY;
2581 texture->basematerialflags = defaultmaterialflags;
2582 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2584 if(cls.state == ca_dedicated)
2586 texture->materialshaderpass = NULL;
2591 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2594 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2595 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2596 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2597 if (texture->q2contents)
2598 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2602 if (!success && warnmissing)
2603 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2606 // init the animation variables
2607 texture->currentframe = texture;
2608 texture->currentmaterialflags = texture->basematerialflags;
2609 if (!texture->materialshaderpass)
2610 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2611 if (!texture->materialshaderpass->skinframes[0])
2612 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2613 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2614 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2618 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2620 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2621 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2623 strlcpy(texture->name, name, sizeof(texture->name));
2624 texture->basealpha = 1.0f;
2625 texture->basematerialflags = materialflags;
2626 texture->supercontents = supercontents;
2628 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2629 texture->offsetscale = 1;
2630 texture->offsetbias = 0;
2631 texture->specularscalemod = 1;
2632 texture->specularpowermod = 1;
2633 texture->rtlightambient = 0;
2634 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2635 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2636 // JUST GREP FOR "specularscalemod = 1".
2638 if (developer_extra.integer)
2639 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2641 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2643 // init the animation variables
2644 texture->currentmaterialflags = texture->basematerialflags;
2645 texture->currentframe = texture;
2646 texture->currentskinframe = skinframe;
2647 texture->backgroundcurrentskinframe = NULL;
2650 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2652 long unsigned int i, j;
2653 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2655 if (texture->shaderpasses[i])
2658 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2659 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2660 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2661 Mem_Free(texture->shaderpasses[i]);
2662 texture->shaderpasses[i] = NULL;
2665 texture->materialshaderpass = NULL;
2666 texture->currentskinframe = NULL;
2667 texture->backgroundcurrentskinframe = NULL;
2670 skinfile_t *Mod_LoadSkinFiles(void)
2672 int i, words, line, wordsoverflow;
2675 skinfile_t *skinfile = NULL, *first = NULL;
2676 skinfileitem_t *skinfileitem;
2677 char word[10][MAX_QPATH];
2682 U_bodyBox,models/players/Legoman/BikerA2.tga
2683 U_RArm,models/players/Legoman/BikerA1.tga
2684 U_LArm,models/players/Legoman/BikerA1.tga
2685 U_armor,common/nodraw
2686 U_sword,common/nodraw
2687 U_shield,common/nodraw
2688 U_homb,common/nodraw
2689 U_backpack,common/nodraw
2690 U_colcha,common/nodraw
2695 memset(word, 0, sizeof(word));
2696 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2698 // If it's the first file we parse
2699 if (skinfile == NULL)
2701 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2706 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2707 skinfile = skinfile->next;
2709 skinfile->next = NULL;
2711 for(line = 0;;line++)
2714 if (!COM_ParseToken_QuakeC(&data, true))
2716 if (!strcmp(com_token, "\n"))
2719 wordsoverflow = false;
2723 strlcpy(word[words++], com_token, sizeof (word[0]));
2725 wordsoverflow = true;
2727 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2730 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);
2733 // words is always >= 1
2734 if (!strcmp(word[0], "replace"))
2738 if (developer_loading.integer)
2739 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2740 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2741 skinfileitem->next = skinfile->items;
2742 skinfile->items = skinfileitem;
2743 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2744 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2747 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]);
2749 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2751 // tag name, like "tag_weapon,"
2752 // not used for anything (not even in Quake3)
2754 else if (words >= 2 && !strcmp(word[1], ","))
2756 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2757 if (developer_loading.integer)
2758 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2759 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2760 skinfileitem->next = skinfile->items;
2761 skinfile->items = skinfileitem;
2762 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2763 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2766 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);
2771 loadmodel->numskins = i;
2775 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2778 skinfileitem_t *skinfileitem, *nextitem;
2779 for (;skinfile;skinfile = next)
2781 next = skinfile->next;
2782 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2784 nextitem = skinfileitem->next;
2785 Mem_Free(skinfileitem);
2791 int Mod_CountSkinFiles(skinfile_t *skinfile)
2794 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2798 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2801 double isnap = 1.0 / snap;
2802 for (i = 0;i < numvertices*numcomponents;i++)
2803 vertices[i] = floor(vertices[i]*isnap)*snap;
2806 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2808 int i, outtriangles;
2809 float edgedir1[3], edgedir2[3], temp[3];
2810 // a degenerate triangle is one with no width (thickness, surface area)
2811 // these are characterized by having all 3 points colinear (along a line)
2812 // or having two points identical
2813 // the simplest check is to calculate the triangle's area
2814 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2816 // calculate first edge
2817 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2818 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2819 CrossProduct(edgedir1, edgedir2, temp);
2820 if (VectorLength2(temp) < 0.001f)
2821 continue; // degenerate triangle (no area)
2822 // valid triangle (has area)
2823 VectorCopy(inelement3i, outelement3i);
2827 return outtriangles;
2830 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2833 int firstvertex, lastvertex;
2834 if (numelements > 0 && elements)
2836 firstvertex = lastvertex = elements[0];
2837 for (i = 1;i < numelements;i++)
2840 firstvertex = min(firstvertex, e);
2841 lastvertex = max(lastvertex, e);
2845 firstvertex = lastvertex = 0;
2846 if (firstvertexpointer)
2847 *firstvertexpointer = firstvertex;
2848 if (lastvertexpointer)
2849 *lastvertexpointer = lastvertex;
2852 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2854 // make an optimal set of texture-sorted batches to draw...
2856 int *firstsurfacefortexture;
2857 int *numsurfacesfortexture;
2858 if (!mod->sortedmodelsurfaces)
2859 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2860 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2861 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2862 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2863 for (j = 0;j < mod->nummodelsurfaces;j++)
2865 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2866 t = (int)(surface->texture - mod->data_textures);
2867 numsurfacesfortexture[t]++;
2870 for (t = 0;t < mod->num_textures;t++)
2872 firstsurfacefortexture[t] = j;
2873 j += numsurfacesfortexture[t];
2875 for (j = 0;j < mod->nummodelsurfaces;j++)
2877 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2878 t = (int)(surface->texture - mod->data_textures);
2879 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2881 Mem_Free(firstsurfacefortexture);
2882 Mem_Free(numsurfacesfortexture);
2885 void Mod_BuildVBOs(void)
2887 if(cls.state == ca_dedicated)
2890 if (!loadmodel->surfmesh.num_vertices)
2893 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2896 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2898 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2900 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2901 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2906 // upload short indices as a buffer
2907 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2908 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);
2910 // upload int indices as a buffer
2911 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2912 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);
2914 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2915 // we put several vertex data streams in the same buffer
2916 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2921 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2922 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2923 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2924 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2925 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2926 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2927 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2928 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2929 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2930 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2931 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2932 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2933 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2934 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2935 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2936 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2937 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2938 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2939 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2940 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2941 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2942 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2943 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2944 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2945 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2946 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2947 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2948 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2953 extern cvar_t mod_obj_orientation;
2954 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2956 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2958 const char *texname;
2960 const float *v, *vn, *vt;
2962 size_t outbufferpos = 0;
2963 size_t outbuffermax = 0x100000;
2964 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
2965 const msurface_t *surface;
2966 const int maxtextures = 256;
2967 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
2968 dp_model_t *submodel;
2970 // construct the mtllib file
2971 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
2974 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
2977 countvertices += surface->num_vertices;
2978 countfaces += surface->num_triangles;
2979 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
2980 for (textureindex = 0;textureindex < counttextures;textureindex++)
2981 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
2983 if (textureindex < counttextures)
2984 continue; // already wrote this material entry
2985 if (textureindex >= maxtextures)
2986 continue; // just a precaution
2987 textureindex = counttextures++;
2988 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
2989 if (outbufferpos >= outbuffermax >> 1)
2992 oldbuffer = outbuffer;
2993 outbuffer = (char *) Z_Malloc(outbuffermax);
2994 memcpy(outbuffer, oldbuffer, outbufferpos);
2997 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");
3002 // write the mtllib file
3003 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3005 // construct the obj file
3007 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);
3011 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)
3013 if (outbufferpos >= outbuffermax >> 1)
3016 oldbuffer = outbuffer;
3017 outbuffer = (char *) Z_Malloc(outbuffermax);
3018 memcpy(outbuffer, oldbuffer, outbufferpos);
3021 if(mod_obj_orientation.integer)
3022 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]);
3024 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]);
3029 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3031 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3034 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3035 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3037 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3038 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3041 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3043 if (outbufferpos >= outbuffermax >> 1)
3046 oldbuffer = outbuffer;
3047 outbuffer = (char *) Z_Malloc(outbuffermax);
3048 memcpy(outbuffer, oldbuffer, outbufferpos);
3054 if(mod_obj_orientation.integer)
3055 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);
3057 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);
3064 // write the obj file
3065 FS_WriteFile(filename, outbuffer, outbufferpos);
3069 Z_Free(texturenames);
3072 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3075 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3077 int countnodes = 0, counttriangles = 0, countframes = 0;
3085 size_t outbufferpos = 0;
3086 size_t outbuffermax = 0x100000;
3087 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3088 const msurface_t *surface;
3089 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3092 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3094 if (outbufferpos >= outbuffermax >> 1)
3097 oldbuffer = outbuffer;
3098 outbuffer = (char *) Z_Malloc(outbuffermax);
3099 memcpy(outbuffer, oldbuffer, outbufferpos);
3103 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3107 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3110 for (poseindex = 0;poseindex < numposes;poseindex++)
3113 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3116 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3120 matrix4x4_t posematrix;
3121 if (outbufferpos >= outbuffermax >> 1)
3124 oldbuffer = outbuffer;
3125 outbuffer = (char *) Z_Malloc(outbuffermax);
3126 memcpy(outbuffer, oldbuffer, outbufferpos);
3130 // strangely the smd angles are for a transposed matrix, so we
3131 // have to generate a transposed matrix, then convert that...
3132 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3133 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3134 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3135 if (angles[0] >= 180) angles[0] -= 360;
3136 if (angles[1] >= 180) angles[1] -= 360;
3137 if (angles[2] >= 180) angles[2] -= 360;
3141 float a = DEG2RAD(angles[ROLL]);
3142 float b = DEG2RAD(angles[PITCH]);
3143 float c = DEG2RAD(angles[YAW]);
3144 float cy, sy, cp, sp, cr, sr;
3146 // smd matrix construction, for comparing
3157 test[1][0] = sr*sp*cy+cr*-sy;
3158 test[1][1] = sr*sp*sy+cr*cy;
3160 test[2][0] = (cr*sp*cy+-sr*-sy);
3161 test[2][1] = (cr*sp*sy+-sr*cy);
3163 test[3][0] = pose[9];
3164 test[3][1] = pose[10];
3165 test[3][2] = pose[11];
3168 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]));
3173 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3178 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3181 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3183 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3186 if (outbufferpos >= outbuffermax >> 1)
3189 oldbuffer = outbuffer;
3190 outbuffer = (char *) Z_Malloc(outbuffermax);
3191 memcpy(outbuffer, oldbuffer, outbufferpos);
3194 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3197 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3199 const int index = e[2-cornerindex];
3200 const float *v = model->surfmesh.data_vertex3f + index * 3;
3201 const float *vn = model->surfmesh.data_normal3f + index * 3;
3202 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3203 const int b = model->surfmesh.blends[index];
3204 if (b < model->num_bones)
3205 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]);
3208 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3209 const unsigned char *wi = w->index;
3210 const unsigned char *wf = w->influence;
3211 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);
3212 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);
3213 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);
3214 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]);
3221 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3226 FS_WriteFile(filename, outbuffer, outbufferpos);
3229 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3236 decompiles a model to editable files
3239 static void Mod_Decompile_f(cmd_state_t *cmd)
3241 int i, j, k, l, first, count;
3243 char inname[MAX_QPATH];
3244 char outname[MAX_QPATH];
3245 char mtlname[MAX_QPATH];
3246 char basename[MAX_QPATH];
3247 char animname[MAX_QPATH];
3248 char animname2[MAX_QPATH];
3249 char zymtextbuffer[16384];
3250 char dpmtextbuffer[16384];
3251 char framegroupstextbuffer[16384];
3252 int zymtextsize = 0;
3253 int dpmtextsize = 0;
3254 int framegroupstextsize = 0;
3257 if (Cmd_Argc(cmd) != 2)
3259 Con_Print("usage: modeldecompile <filename>\n");
3263 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3264 FS_StripExtension(inname, basename, sizeof(basename));
3266 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3269 Con_Print("No such model\n");
3272 if (mod->brush.submodel)
3274 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3275 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3276 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3279 if (!mod->surfmesh.num_triangles)
3281 Con_Print("Empty model (or sprite)\n");
3285 // export OBJ if possible (not on sprites)
3286 if (mod->surfmesh.num_triangles)
3288 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3289 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3290 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3293 // export SMD if possible (only for skeletal models)
3294 if (mod->surfmesh.num_triangles && mod->num_bones)
3296 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3297 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3298 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3299 if (l > 0) zymtextsize += l;
3300 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3301 if (l > 0) dpmtextsize += l;
3302 for (i = 0;i < mod->numframes;i = j)
3304 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3305 first = mod->animscenes[i].firstframe;
3306 if (mod->animscenes[i].framecount > 1)
3309 count = mod->animscenes[i].framecount;
3315 // check for additional frames with same name
3316 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3317 if(animname[l] < '0' || animname[l] > '9')
3319 if(k > 0 && animname[k-1] == '_')
3322 count = mod->num_poses - first;
3323 for (j = i + 1;j < mod->numframes;j++)
3325 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3326 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3327 if(animname2[l] < '0' || animname2[l] > '9')
3329 if(k > 0 && animname[k-1] == '_')
3332 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3334 count = mod->animscenes[j].firstframe - first;
3338 // if it's only one frame, use the original frame name
3340 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3343 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3344 Mod_Decompile_SMD(mod, outname, first, count, false);
3345 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3347 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3348 if (l > 0) zymtextsize += l;
3350 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3352 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3353 if (l > 0) dpmtextsize += l;
3355 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3357 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3358 if (l > 0) framegroupstextsize += l;
3362 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3364 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3365 if (framegroupstextsize)
3366 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3370 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3373 memset(state, 0, sizeof(*state));
3374 state->width = width;
3375 state->height = height;
3376 state->currentY = 0;
3377 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3378 for (y = 0;y < state->height;y++)
3380 state->rows[y].currentX = 0;
3381 state->rows[y].rowY = -1;
3385 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3388 state->currentY = 0;
3389 for (y = 0;y < state->height;y++)
3391 state->rows[y].currentX = 0;
3392 state->rows[y].rowY = -1;
3396 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3399 Mem_Free(state->rows);
3400 memset(state, 0, sizeof(*state));
3403 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3405 mod_alloclightmap_row_t *row;
3408 row = state->rows + blockheight;
3409 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3411 if (state->currentY + blockheight <= state->height)
3413 // use the current allocation position
3414 row->rowY = state->currentY;
3416 state->currentY += blockheight;
3420 // find another position
3421 for (y = blockheight;y < state->height;y++)
3423 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3425 row = state->rows + y;
3429 if (y == state->height)
3434 *outx = row->currentX;
3435 row->currentX += blockwidth;
3440 typedef struct lightmapsample_s
3444 float *vertex_color;
3445 unsigned char *lm_bgr;
3446 unsigned char *lm_dir;
3450 typedef struct lightmapvertex_s
3455 float texcoordbase[2];
3456 float texcoordlightmap[2];
3457 float lightcolor[4];
3461 typedef struct lightmaptriangle_s
3469 // 2D modelspace coordinates of min corner
3470 // snapped to lightmap grid but not in grid coordinates
3472 // 2D modelspace to lightmap coordinate scale
3480 typedef struct lightmaplight_s
3491 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3493 #define MAX_LIGHTMAPSAMPLES 64
3494 static int mod_generatelightmaps_numoffsets[3];
3495 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3497 static int mod_generatelightmaps_numlights;
3498 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3500 extern cvar_t r_shadow_lightattenuationdividebias;
3501 extern cvar_t r_shadow_lightattenuationlinearscale;
3503 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3508 float relativepoint[3];
3515 float lightorigin[3];
3519 float lightcolor[3];
3521 for (i = 0;i < 5*3;i++)
3523 for (index = 0;;index++)
3525 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3530 lightradius2 = lightradius * lightradius;
3531 VectorSubtract(lightorigin, pos, relativepoint);
3532 dist2 = VectorLength2(relativepoint);
3533 if (dist2 >= lightradius2)
3535 lightiradius = 1.0f / lightradius;
3536 dist = sqrt(dist2) * lightiradius;
3537 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3538 if (intensity <= 0.0f)
3540 if (model && model->TraceLine)
3542 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3543 if (trace.fraction < 1)
3546 // scale down intensity to add to both ambient and diffuse
3547 //intensity *= 0.5f;
3548 VectorNormalize(relativepoint);
3549 VectorScale(lightcolor, intensity, color);
3550 VectorMA(sample , 0.5f , color, sample );
3551 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3552 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3553 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3554 // calculate a weighted average light direction as well
3555 intensity *= VectorLength(color);
3556 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3558 // calculate the direction we'll use to reduce the sample to a directional light source
3559 VectorCopy(sample + 12, dir);
3560 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3561 VectorNormalize(dir);
3562 // extract the diffuse color along the chosen direction and scale it
3563 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3564 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3565 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3566 // subtract some of diffuse from ambient
3567 VectorMA(sample, -0.333f, diffuse, ambient);
3568 // store the normalized lightdir
3569 VectorCopy(dir, lightdir);
3572 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3576 const msurface_t *surface;
3577 const float *vertex3f = model->surfmesh.data_vertex3f;
3578 const int *element3i = model->surfmesh.data_element3i;
3581 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3583 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3585 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3587 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3589 VectorCopy(vertex3f + 3*e[0], v2[0]);
3590 VectorCopy(vertex3f + 3*e[1], v2[1]);
3591 VectorCopy(vertex3f + 3*e[2], v2[2]);
3592 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3597 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3599 int maxnodes = 1<<14;
3600 svbsp_node_t *nodes;
3605 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3606 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3607 VectorCopy(lightinfo->origin, origin);
3608 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3611 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3612 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3613 if (svbsp.ranoutofnodes)
3616 if (maxnodes > 1<<22)
3622 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3627 if (svbsp.numnodes > 0)
3629 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3630 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3631 lightinfo->svbsp = svbsp;
3636 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3640 lightmaplight_t *lightinfo;
3644 mod_generatelightmaps_numlights = 0;
3645 for (index = 0;;index++)
3647 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3651 mod_generatelightmaps_numlights++;
3653 if (mod_generatelightmaps_numlights > 0)
3655 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3656 lightinfo = mod_generatelightmaps_lightinfo;
3657 for (index = 0;;index++)
3659 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3666 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3668 lightinfo->iradius = 1.0f / lightinfo->radius;
3669 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3670 // TODO: compute svbsp
3671 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3675 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3678 if (mod_generatelightmaps_lightinfo)
3680 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3681 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3682 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3683 Mem_Free(mod_generatelightmaps_lightinfo);
3685 mod_generatelightmaps_lightinfo = NULL;
3686 mod_generatelightmaps_numlights = 0;
3689 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3691 const svbsp_node_t *node;
3692 const svbsp_node_t *nodes = svbsp->nodes;
3697 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3699 return num == -1; // true if empty, false if solid (shadowed)
3702 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3705 float relativepoint[3];
3714 const lightmaplight_t *lightinfo;
3716 for (i = 0;i < 5*3;i++)
3718 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3720 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3721 VectorSubtract(lightinfo->origin, pos, relativepoint);
3722 // don't accept light from behind a surface, it causes bad shading
3723 if (normal && DotProduct(relativepoint, normal) <= 0)
3725 dist2 = VectorLength2(relativepoint);
3726 if (dist2 >= lightinfo->radius2)
3728 dist = sqrt(dist2) * lightinfo->iradius;
3729 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3732 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3736 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3738 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3740 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3743 // for light grid we'd better check visibility of the offset point
3744 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3745 if (trace.fraction < 1)
3746 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3749 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3754 // scale intensity according to how many rays succeeded
3755 // we know one test is valid, half of the rest will fail...
3756 //if (normal && tests > 1)
3757 // intensity *= (tests - 1.0f) / tests;
3758 intensity *= (float)hits / tests;
3760 // scale down intensity to add to both ambient and diffuse
3761 //intensity *= 0.5f;
3762 VectorNormalize(relativepoint);
3763 VectorScale(lightinfo->color, intensity, color);
3764 VectorMA(sample , 0.5f , color, sample );
3765 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3766 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3767 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3768 // calculate a weighted average light direction as well
3769 intensity *= VectorLength(color);
3770 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3774 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3780 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3781 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3782 VectorCopy(sample + 12, dir);
3783 VectorNormalize(dir);
3784 //VectorAdd(dir, normal, dir);
3785 //VectorNormalize(dir);
3786 f = DotProduct(dir, normal);
3787 f = max(0, f) * 255.0f;
3788 VectorScale(sample, f, color);
3789 //VectorCopy(normal, dir);
3790 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3791 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3792 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3793 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3795 lm_dir[0] = (unsigned char)dir[2];
3796 lm_dir[1] = (unsigned char)dir[1];
3797 lm_dir[2] = (unsigned char)dir[0];
3801 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3804 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3805 VectorCopy(sample, vertex_color);
3808 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3814 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3815 // calculate the direction we'll use to reduce the sample to a directional light source
3816 VectorCopy(sample + 12, dir);
3817 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3818 VectorNormalize(dir);
3819 // extract the diffuse color along the chosen direction and scale it
3820 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3821 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3822 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3823 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3824 VectorScale(sample, 127.5f, ambient);
3825 VectorMA(ambient, -0.333f, diffuse, ambient);
3826 // encode to the grid format
3827 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3828 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3829 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3830 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3831 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3832 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3833 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3834 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3835 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));}
3838 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3843 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3844 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3845 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3846 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3847 radius[0] = mod_generatelightmaps_lightmapradius.value;
3848 radius[1] = mod_generatelightmaps_vertexradius.value;
3849 radius[2] = mod_generatelightmaps_gridradius.value;
3850 for (i = 0;i < 3;i++)
3852 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3855 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3860 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3862 msurface_t *surface;
3865 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3867 surface = model->data_surfaces + surfaceindex;
3868 surface->lightmaptexture = NULL;
3869 surface->deluxemaptexture = NULL;
3871 if (model->brushq3.data_lightmaps)
3873 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3874 if (model->brushq3.data_lightmaps[i])
3875 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3876 Mem_Free(model->brushq3.data_lightmaps);
3877 model->brushq3.data_lightmaps = NULL;
3879 if (model->brushq3.data_deluxemaps)
3881 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3882 if (model->brushq3.data_deluxemaps[i])
3883 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3884 Mem_Free(model->brushq3.data_deluxemaps);
3885 model->brushq3.data_deluxemaps = NULL;
3889 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3891 msurface_t *surface;
3897 surfmesh_t oldsurfmesh;
3899 unsigned char *data;
3900 oldsurfmesh = model->surfmesh;
3901 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3902 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3904 size += model->surfmesh.num_vertices * sizeof(float[3]);
3905 size += model->surfmesh.num_vertices * sizeof(float[3]);
3906 size += model->surfmesh.num_vertices * sizeof(float[3]);
3907 size += model->surfmesh.num_vertices * sizeof(float[3]);
3908 size += model->surfmesh.num_vertices * sizeof(float[2]);
3909 size += model->surfmesh.num_vertices * sizeof(float[2]);
3910 size += model->surfmesh.num_vertices * sizeof(float[4]);
3911 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3912 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3913 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3914 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3915 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3916 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3917 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3918 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3919 if (model->surfmesh.num_vertices > 65536)
3920 model->surfmesh.data_element3s = NULL;
3922 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3923 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3924 model->surfmesh.data_element3i_indexbuffer = NULL;
3925 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3926 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3927 model->surfmesh.data_element3s_indexbuffer = NULL;
3928 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3929 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3930 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3931 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3932 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3933 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3934 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3935 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3936 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3937 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3938 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
3940 // convert all triangles to unique vertex data
3942 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3944 surface = model->data_surfaces + surfaceindex;
3945 surface->num_firstvertex = outvertexindex;
3946 surface->num_vertices = surface->num_triangles*3;
3947 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3948 for (i = 0;i < surface->num_triangles*3;i++)
3951 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3952 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3953 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3954 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3955 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3956 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3957 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3958 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3959 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3960 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3961 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3962 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3963 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
3964 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
3965 if (oldsurfmesh.data_texcoordlightmap2f)
3967 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
3968 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
3970 if (oldsurfmesh.data_lightmapcolor4f)
3972 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
3973 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
3974 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
3975 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
3978 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
3979 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
3983 if (model->surfmesh.data_element3s)
3984 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
3985 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
3987 // find and update all submodels to use this new surfmesh data
3988 for (i = 0;i < model->brush.numsubmodels;i++)
3989 model->brush.submodels[i]->surfmesh = model->surfmesh;
3992 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
3994 msurface_t *surface;
4000 lightmaptriangle_t *triangle;
4001 // generate lightmap triangle structs
4002 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4003 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4005 surface = model->data_surfaces + surfaceindex;
4006 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4007 for (i = 0;i < surface->num_triangles;i++)
4009 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4010 triangle->triangleindex = surface->num_firsttriangle+i;
4011 triangle->surfaceindex = surfaceindex;
4012 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4013 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4014 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4015 // calculate bounds of triangle
4016 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4017 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4018 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4019 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4020 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4021 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4022 // pick an axial projection based on the triangle normal
4023 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4025 if (fabs(normal[1]) > fabs(normal[axis]))
4027 if (fabs(normal[2]) > fabs(normal[axis]))
4029 triangle->axis = axis;
4034 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4036 if (mod_generatelightmaps_lightmaptriangles)
4037 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4038 mod_generatelightmaps_lightmaptriangles = NULL;
4041 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4043 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4045 msurface_t *surface;
4059 float trianglenormal[3];
4060 float samplecenter[3];
4061 float samplenormal[3];
4067 float lmscalepixels;
4070 float lm_basescalepixels;
4071 int lm_borderpixels;
4075 lightmaptriangle_t *triangle;
4076 unsigned char *lightmappixels;
4077 unsigned char *deluxemappixels;
4078 mod_alloclightmap_state_t lmstate;
4081 // generate lightmap projection information for all triangles
4082 if (model->texturepool == NULL)
4083 model->texturepool = R_AllocTexturePool();
4084 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4085 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4086 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4087 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4088 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4090 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4092 surface = model->data_surfaces + surfaceindex;
4093 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4094 lmscalepixels = lm_basescalepixels;
4095 for (retry = 0;retry < 30;retry++)
4097 // after a couple failed attempts, degrade quality to make it fit
4099 lmscalepixels *= 0.5f;
4100 for (i = 0;i < surface->num_triangles;i++)
4102 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4103 triangle->lightmapindex = lightmapnumber;
4104 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4105 // pick two planar axes for projection
4106 // lightmap coordinates here are in pixels
4107 // lightmap projections are snapped to pixel grid explicitly, such
4108 // that two neighboring triangles sharing an edge and projection
4109 // axis will have identical sample spacing along their shared edge
4111 for (j = 0;j < 3;j++)
4113 if (j == triangle->axis)
4115 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4116 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4117 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4118 triangle->lmbase[k] = lmmins/lmscalepixels;
4119 triangle->lmscale[k] = lmscalepixels;
4122 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4125 // if all fit in this texture, we're done with this surface
4126 if (i == surface->num_triangles)
4128 // if we haven't maxed out the lightmap size yet, we retry the
4129 // entire surface batch...
4130 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4132 lm_texturesize *= 2;
4135 Mod_AllocLightmap_Free(&lmstate);
4136 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4139 // if we have maxed out the lightmap size, and this triangle does
4140 // not fit in the same texture as the rest of the surface, we have
4141 // to retry the entire surface in a new texture (can only use one)
4142 // with multiple retries, the lightmap quality degrades until it
4143 // fits (or gives up)
4144 if (surfaceindex > 0)
4146 Mod_AllocLightmap_Reset(&lmstate);
4150 Mod_AllocLightmap_Free(&lmstate);
4152 // now put triangles together into lightmap textures, and do not allow
4153 // triangles of a surface to go into different textures (as that would
4154 // require rewriting the surface list)
4155 model->brushq3.deluxemapping_modelspace = true;
4156 model->brushq3.deluxemapping = true;
4157 model->brushq3.num_mergedlightmaps = lightmapnumber;
4158 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4159 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4160 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4161 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4162 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4164 surface = model->data_surfaces + surfaceindex;
4165 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4166 for (i = 0;i < surface->num_triangles;i++)
4168 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4169 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4170 VectorNormalize(trianglenormal);
4171 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4172 axis = triangle->axis;
4173 axis1 = axis == 0 ? 1 : 0;
4174 axis2 = axis == 2 ? 1 : 2;
4175 lmiscale[0] = 1.0f / triangle->lmscale[0];
4176 lmiscale[1] = 1.0f / triangle->lmscale[1];
4177 if (trianglenormal[axis] < 0)
4178 VectorNegate(trianglenormal, trianglenormal);
4179 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4180 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4181 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4182 for (j = 0;j < 3;j++)
4184 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4185 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4186 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4188 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4189 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4190 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4191 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]);
4201 forward[1] = 1.0f / triangle->lmscale[0];
4205 left[2] = 1.0f / triangle->lmscale[1];
4210 origin[1] = triangle->lmbase[0];
4211 origin[2] = triangle->lmbase[1];
4214 forward[0] = 1.0f / triangle->lmscale[0];
4219 left[2] = 1.0f / triangle->lmscale[1];
4223 origin[0] = triangle->lmbase[0];
4225 origin[2] = triangle->lmbase[1];
4228 forward[0] = 1.0f / triangle->lmscale[0];
4232 left[1] = 1.0f / triangle->lmscale[1];
4237 origin[0] = triangle->lmbase[0];
4238 origin[1] = triangle->lmbase[1];
4242 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4244 #define LM_DIST_EPSILON (1.0f / 32.0f)
4245 for (y = 0;y < triangle->lmsize[1];y++)
4247 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4248 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4250 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4251 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4252 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4253 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4254 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4260 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4262 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);
4263 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);
4267 Mem_Free(lightmappixels);
4268 if (deluxemappixels)
4269 Mem_Free(deluxemappixels);
4271 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4273 surface = model->data_surfaces + surfaceindex;
4274 if (!surface->num_triangles)
4276 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4277 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4278 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4279 surface->lightmapinfo = NULL;
4282 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4283 model->brushq1.lightdata = NULL;
4284 model->brushq1.lightmapupdateflags = NULL;
4285 model->brushq1.firstrender = false;
4286 model->brushq1.num_lightstyles = 0;
4287 model->brushq1.data_lightstyleinfo = NULL;
4288 for (i = 0;i < model->brush.numsubmodels;i++)
4290 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4291 model->brush.submodels[i]->brushq1.firstrender = false;
4292 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4293 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4297 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4300 for (i = 0;i < model->surfmesh.num_vertices;i++)
4301 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4304 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4311 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4313 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4314 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4316 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4317 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4319 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4320 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4326 extern cvar_t mod_q3bsp_nolightmaps;
4327 static void Mod_GenerateLightmaps(dp_model_t *model)
4329 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4330 dp_model_t *oldloadmodel = loadmodel;
4333 Mod_GenerateLightmaps_InitSampleOffsets(model);
4334 Mod_GenerateLightmaps_DestroyLightmaps(model);
4335 Mod_GenerateLightmaps_UnweldTriangles(model);
4336 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4337 Mod_GenerateLightmaps_CreateLights(model);
4338 if(!mod_q3bsp_nolightmaps.integer)
4339 Mod_GenerateLightmaps_CreateLightmaps(model);
4340 Mod_GenerateLightmaps_UpdateVertexColors(model);
4341 Mod_GenerateLightmaps_UpdateLightGrid(model);
4342 Mod_GenerateLightmaps_DestroyLights(model);
4343 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4345 loadmodel = oldloadmodel;
4348 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4350 if (Cmd_Argc(cmd) != 1)
4352 Con_Printf("usage: mod_generatelightmaps\n");
4357 Con_Printf("no worldmodel loaded\n");
4360 Mod_GenerateLightmaps(cl.worldmodel);
4363 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4365 memset(mod, 0, sizeof(*mod));
4366 strlcpy(mod->name, name, sizeof(mod->name));
4367 mod->mempool = Mem_AllocPool(name, 0, NULL);
4368 mod->texturepool = R_AllocTexturePool();
4369 mod->Draw = R_Q1BSP_Draw;
4370 mod->DrawDepth = R_Q1BSP_DrawDepth;
4371 mod->DrawDebug = R_Q1BSP_DrawDebug;
4372 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4373 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4374 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4375 mod->DrawLight = R_Q1BSP_DrawLight;
4378 void Mod_Mesh_Destroy(dp_model_t *mod)
4380 Mod_UnloadModel(mod);
4383 // resets the mesh model to have no geometry to render, ready for a new frame -
4384 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4385 void Mod_Mesh_Reset(dp_model_t *mod)
4387 mod->num_surfaces = 0;
4388 mod->surfmesh.num_vertices = 0;
4389 mod->surfmesh.num_triangles = 0;
4390 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4391 mod->DrawSky = NULL; // will be set if a texture needs it
4392 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4395 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4399 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4400 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4401 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4403 if (mod->max_textures <= mod->num_textures)
4405 texture_t *oldtextures = mod->data_textures;
4406 mod->max_textures = max(mod->max_textures * 2, 1024);
4407 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4408 // update the pointers
4409 for (i = 0; i < mod->num_surfaces; i++)
4410 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4412 t = &mod->data_textures[mod->num_textures++];
4413 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
4414 t->mesh_drawflag = drawflag;
4415 t->mesh_defaulttexflags = defaulttexflags;
4416 t->mesh_defaultmaterialflags = defaultmaterialflags;
4417 switch (defaultdrawflags & DRAWFLAG_MASK)
4419 case DRAWFLAG_ADDITIVE:
4420 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4421 t->currentmaterialflags = t->basematerialflags;
4423 case DRAWFLAG_MODULATE:
4424 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4425 t->currentmaterialflags = t->basematerialflags;
4426 t->customblendfunc[0] = GL_DST_COLOR;
4427 t->customblendfunc[1] = GL_ZERO;
4429 case DRAWFLAG_2XMODULATE:
4430 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4431 t->currentmaterialflags = t->basematerialflags;
4432 t->customblendfunc[0] = GL_DST_COLOR;
4433 t->customblendfunc[1] = GL_SRC_COLOR;
4435 case DRAWFLAG_SCREEN:
4436 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4437 t->currentmaterialflags = t->basematerialflags;
4438 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4439 t->customblendfunc[1] = GL_ONE;
4447 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4450 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4451 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4452 return mod->data_surfaces + mod->num_surfaces - 1;
4453 // create new surface
4454 if (mod->max_surfaces == mod->num_surfaces)
4456 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4457 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4458 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4460 surf = mod->data_surfaces + mod->num_surfaces;
4461 mod->num_surfaces++;
4462 memset(surf, 0, sizeof(*surf));
4463 surf->texture = tex;
4464 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4465 surf->num_firstvertex = mod->surfmesh.num_vertices;
4466 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4467 mod->DrawSky = R_Q1BSP_DrawSky;
4468 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4469 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4473 int Mod_Mesh_IndexForVertex(dp_model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
4475 int hashindex, h, vnum, mask;
4476 surfmesh_t *mesh = &mod->surfmesh;
4477 if (mesh->max_vertices == mesh->num_vertices)
4479 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4480 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4481 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4482 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4483 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4484 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4485 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4486 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4487 // rebuild the hash table
4488 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4489 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4490 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4491 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4492 mask = mod->surfmesh.num_vertexhashsize - 1;
4493 // no need to hash the vertices for the entire model, the latest surface will suffice.
4494 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4496 // this uses prime numbers intentionally for computing the hash
4497 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;
4498 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4499 ; // just iterate until we find the terminator
4500 mesh->data_vertexhash[h] = vnum;
4503 mask = mod->surfmesh.num_vertexhashsize - 1;
4504 // this uses prime numbers intentionally for computing the hash
4505 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4506 // when possible find an identical vertex within the same surface and return it
4507 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4509 if (vnum >= surf->num_firstvertex
4510 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4511 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4512 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4513 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4514 && 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)
4517 // add the new vertex
4518 vnum = mesh->num_vertices++;
4519 if (surf->num_vertices > 0)
4521 if (surf->mins[0] > x) surf->mins[0] = x;
4522 if (surf->mins[1] > y) surf->mins[1] = y;
4523 if (surf->mins[2] > z) surf->mins[2] = z;
4524 if (surf->maxs[0] < x) surf->maxs[0] = x;
4525 if (surf->maxs[1] < y) surf->maxs[1] = y;
4526 if (surf->maxs[2] < z) surf->maxs[2] = z;
4530 VectorSet(surf->mins, x, y, z);
4531 VectorSet(surf->maxs, x, y, z);
4533 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4534 mesh->data_vertexhash[h] = vnum;
4535 mesh->data_vertex3f[vnum * 3 + 0] = x;
4536 mesh->data_vertex3f[vnum * 3 + 1] = y;
4537 mesh->data_vertex3f[vnum * 3 + 2] = z;
4538 mesh->data_normal3f[vnum * 3 + 0] = nx;
4539 mesh->data_normal3f[vnum * 3 + 1] = ny;
4540 mesh->data_normal3f[vnum * 3 + 2] = nz;
4541 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4542 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4543 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4544 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4545 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4546 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4547 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4548 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4552 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4554 surfmesh_t *mesh = &mod->surfmesh;
4555 if (mesh->max_triangles == mesh->num_triangles)
4557 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4558 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4559 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4561 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4562 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4563 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4564 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4565 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4566 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4567 mesh->num_triangles++;
4568 surf->num_triangles++;
4571 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4575 msurface_t *surf, *surf2;
4577 // build the sorted surfaces list properly to reduce material setup
4578 // this is easy because we're just sorting on texture and don't care about the order of textures
4579 mod->nummodelsurfaces = 0;
4580 for (i = 0; i < mod->num_surfaces; i++)
4581 mod->data_surfaces[i].included = false;
4582 for (i = 0; i < mod->num_surfaces; i++)
4584 surf = mod->data_surfaces + i;
4587 tex = surf->texture;
4588 // j = i is intentional
4589 for (j = i; j < mod->num_surfaces; j++)
4591 surf2 = mod->data_surfaces + j;
4592 if (surf2->included)
4594 if (surf2->texture == tex)
4596 surf2->included = true;
4597 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4603 static void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4606 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4608 if (mod->surfmesh.num_vertices > 0)
4610 // calculate normalmins/normalmaxs
4611 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4612 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4613 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4615 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4616 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4617 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4618 // expand bounds to include this vertex
4619 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4620 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4621 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4622 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4623 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4624 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4626 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4627 // (fast but less accurate than doing it per vertex)
4628 x2a = mod->normalmins[0] * mod->normalmins[0];
4629 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4630 y2a = mod->normalmins[1] * mod->normalmins[1];
4631 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4632 z2a = mod->normalmins[2] * mod->normalmins[2];
4633 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4637 yawradius = sqrt(x2 + y2);
4638 rotatedradius = sqrt(x2 + y2 + z2);
4639 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4640 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4641 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4642 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4643 mod->radius = rotatedradius;
4644 mod->radius2 = x2 + y2 + z2;
4648 VectorClear(mod->normalmins);
4649 VectorClear(mod->normalmaxs);
4650 VectorClear(mod->yawmins);
4651 VectorClear(mod->yawmaxs);
4652 VectorClear(mod->rotatedmins);
4653 VectorClear(mod->rotatedmaxs);
4659 void Mod_Mesh_Validate(dp_model_t *mod)
4662 qboolean warned = false;
4663 for (i = 0; i < mod->num_surfaces; i++)
4665 msurface_t *surf = mod->data_surfaces + i;
4666 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4667 int first = surf->num_firstvertex;
4668 int end = surf->num_firstvertex + surf->num_vertices;
4670 for (j = 0;j < surf->num_triangles * 3;j++)
4672 if (e[j] < first || e[j] >= end)
4675 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4683 static void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
4685 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;
4686 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;
4687 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;
4688 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;
4689 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;
4690 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;
4691 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;
4692 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;
4693 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;
4694 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;
4695 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;
4698 void Mod_Mesh_Finalize(dp_model_t *mod)
4700 if (gl_paranoid.integer)
4701 Mod_Mesh_Validate(mod);
4702 Mod_Mesh_ComputeBounds(mod);
4703 Mod_Mesh_MakeSortedSurfaces(mod);
4704 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);
4705 Mod_Mesh_UploadDynamicBuffers(mod);