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 // if we're not dedicatd, the renderer calls will crash without video
585 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
588 Host_Error ("Mod_ForName: empty name");
590 // search the currently loaded models
591 for (i = 0;i < nummodels;i++)
593 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))))
600 // no match found, create a new one
601 mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
602 strlcpy(mod->name, name, sizeof(mod->name));
604 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
606 mod->brush.parentmodel = NULL;
616 Loads in a model for the given name
619 dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
622 model = Mod_FindName(name, parentname);
623 if (!model->loaded || checkdisk)
624 Mod_LoadModel(model, crash, checkdisk);
632 Reloads all models if they have changed
635 void Mod_Reload(void)
638 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
641 SCR_PushLoadingScreen("Reloading models", 1.0);
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 for (i = 0;i < nummodels;i++)
647 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
649 SCR_PushLoadingScreen(mod->name, 1.0 / count);
650 Mod_LoadModel(mod, true, true);
651 SCR_PopLoadingScreen(false);
653 SCR_PopLoadingScreen(false);
656 unsigned char *mod_base;
659 //=============================================================================
666 static void Mod_Print_f(cmd_state_t *cmd)
669 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
672 Con_Print("Loaded models:\n");
673 for (i = 0;i < nummodels;i++)
675 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
677 if (mod->brush.numsubmodels)
678 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
680 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
690 static void Mod_Precache_f(cmd_state_t *cmd)
692 if (Cmd_Argc(cmd) == 2)
693 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
695 Con_Print("usage: modelprecache <filename>\n");
698 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
702 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
703 memset(used, 0, numvertices);
704 for (i = 0;i < numelements;i++)
705 used[elements[i]] = 1;
706 for (i = 0, count = 0;i < numvertices;i++)
707 remapvertices[i] = used[i] ? count++ : -1;
712 qboolean Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
714 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
716 int invalidintcount = 0, invalidintexample = 0;
717 int invalidshortcount = 0, invalidshortexample = 0;
718 int invalidmismatchcount = 0, invalidmismatchexample = 0;
721 for (i = 0; i < numelements; i++)
723 if (element3i[i] < first || element3i[i] > last)
726 invalidintexample = i;
732 for (i = 0; i < numelements; i++)
734 if (element3s[i] < first || element3s[i] > last)
737 invalidintexample = i;
741 if (element3i && element3s)
743 for (i = 0; i < numelements; i++)
745 if (element3s[i] != element3i[i])
747 invalidmismatchcount++;
748 invalidmismatchexample = i;
752 if (invalidintcount || invalidshortcount || invalidmismatchcount)
754 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, element3i, element3s, filename, fileline);
755 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
756 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
757 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);
758 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
760 // edit the elements to make them safer, as the driver will crash otherwise
762 for (i = 0; i < numelements; i++)
763 if (element3i[i] < first || element3i[i] > last)
764 element3i[i] = first;
766 for (i = 0; i < numelements; i++)
767 if (element3s[i] < first || element3s[i] > last)
768 element3s[i] = first;
769 if (element3i && element3s)
770 for (i = 0; i < numelements; i++)
771 if (element3s[i] != element3i[i])
772 element3s[i] = element3i[i];
779 // warning: this is an expensive function!
780 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
787 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
788 // process each vertex of each triangle and accumulate the results
789 // use area-averaging, to make triangles with a big area have a bigger
790 // weighting on the vertex normal than triangles with a small area
791 // to do so, just add the 'normals' together (the bigger the area
792 // the greater the length of the normal is
794 for (i = 0; i < numtriangles; i++, element += 3)
797 vertex3f + element[0] * 3,
798 vertex3f + element[1] * 3,
799 vertex3f + element[2] * 3,
804 VectorNormalize(areaNormal);
806 for (j = 0;j < 3;j++)
808 vectorNormal = normal3f + element[j] * 3;
809 vectorNormal[0] += areaNormal[0];
810 vectorNormal[1] += areaNormal[1];
811 vectorNormal[2] += areaNormal[2];
814 // and just normalize the accumulated vertex normal in the end
815 vectorNormal = normal3f + 3 * firstvertex;
816 for (i = 0; i < numvertices; i++, vectorNormal += 3)
817 VectorNormalize(vectorNormal);
821 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)
823 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
824 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
825 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
827 // 6 multiply, 9 subtract
828 VectorSubtract(v1, v0, v10);
829 VectorSubtract(v2, v0, v20);
830 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
831 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
832 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
833 // 12 multiply, 10 subtract
834 tc10[1] = tc1[1] - tc0[1];
835 tc20[1] = tc2[1] - tc0[1];
836 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
837 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
838 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
839 tc10[0] = tc1[0] - tc0[0];
840 tc20[0] = tc2[0] - tc0[0];
841 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
842 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
843 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
844 // 12 multiply, 4 add, 6 subtract
845 f = DotProduct(svector3f, normal3f);
846 svector3f[0] -= f * normal3f[0];
847 svector3f[1] -= f * normal3f[1];
848 svector3f[2] -= f * normal3f[2];
849 f = DotProduct(tvector3f, normal3f);
850 tvector3f[0] -= f * normal3f[0];
851 tvector3f[1] -= f * normal3f[1];
852 tvector3f[2] -= f * normal3f[2];
853 // if texture is mapped the wrong way (counterclockwise), the tangents
854 // have to be flipped, this is detected by calculating a normal from the
855 // two tangents, and seeing if it is opposite the surface normal
856 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
857 CrossProduct(tvector3f, svector3f, tangentcross);
858 if (DotProduct(tangentcross, normal3f) < 0)
860 VectorNegate(svector3f, svector3f);
861 VectorNegate(tvector3f, tvector3f);
866 // warning: this is a very expensive function!
867 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)
870 float sdir[3], tdir[3], normal[3], *svec, *tvec;
871 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
872 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
875 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
876 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
877 // process each vertex of each triangle and accumulate the results
878 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
880 v0 = vertex3f + e[0] * 3;
881 v1 = vertex3f + e[1] * 3;
882 v2 = vertex3f + e[2] * 3;
883 tc0 = texcoord2f + e[0] * 2;
884 tc1 = texcoord2f + e[1] * 2;
885 tc2 = texcoord2f + e[2] * 2;
887 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
888 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
890 // calculate the edge directions and surface normal
891 // 6 multiply, 9 subtract
892 VectorSubtract(v1, v0, v10);
893 VectorSubtract(v2, v0, v20);
894 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
895 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
896 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
898 // calculate the tangents
899 // 12 multiply, 10 subtract
900 tc10[1] = tc1[1] - tc0[1];
901 tc20[1] = tc2[1] - tc0[1];
902 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
903 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
904 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
905 tc10[0] = tc1[0] - tc0[0];
906 tc20[0] = tc2[0] - tc0[0];
907 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
908 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
909 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
911 // if texture is mapped the wrong way (counterclockwise), the tangents
912 // have to be flipped, this is detected by calculating a normal from the
913 // two tangents, and seeing if it is opposite the surface normal
914 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
915 CrossProduct(tdir, sdir, tangentcross);
916 if (DotProduct(tangentcross, normal) < 0)
918 VectorNegate(sdir, sdir);
919 VectorNegate(tdir, tdir);
924 VectorNormalize(sdir);
925 VectorNormalize(tdir);
927 for (i = 0;i < 3;i++)
929 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
930 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
933 // make the tangents completely perpendicular to the surface normal, and
934 // then normalize them
935 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
936 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
938 f = -DotProduct(svec, n);
939 VectorMA(svec, f, n, svec);
940 VectorNormalize(svec);
941 f = -DotProduct(tvec, n);
942 VectorMA(tvec, f, n, tvec);
943 VectorNormalize(tvec);
947 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors)
950 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));
951 loadmodel->surfmesh.num_vertices = numvertices;
952 loadmodel->surfmesh.num_triangles = numtriangles;
953 if (loadmodel->surfmesh.num_vertices)
955 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
956 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
957 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
958 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
959 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
960 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
962 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
964 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
966 if (loadmodel->surfmesh.num_triangles)
968 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
969 if (loadmodel->surfmesh.num_vertices <= 65536)
970 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
974 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
976 shadowmesh_t *newmesh;
977 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
978 newmesh->mempool = mempool;
979 newmesh->maxverts = maxverts;
980 newmesh->maxtriangles = maxtriangles;
981 newmesh->numverts = 0;
982 newmesh->numtriangles = 0;
983 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
984 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
986 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
987 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
988 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
989 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
993 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
996 shadowmeshvertexhash_t *hash;
997 // this uses prime numbers intentionally
998 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
999 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1001 vnum = (hash - mesh->vertexhashentries);
1002 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1003 return hash - mesh->vertexhashentries;
1005 vnum = mesh->numverts++;
1006 hash = mesh->vertexhashentries + vnum;
1007 hash->next = mesh->vertexhashtable[hashindex];
1008 mesh->vertexhashtable[hashindex] = hash;
1009 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1010 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1011 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1015 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1019 for (i = 0;i < numtris;i++)
1021 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1022 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1023 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1024 mesh->numtriangles++;
1027 // the triangle calculation can take a while, so let's do a keepalive here
1028 CL_KeepaliveMessage(false);
1031 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1033 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1034 CL_KeepaliveMessage(false);
1036 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1039 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1041 if (!mesh->numverts)
1044 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1045 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1047 // upload short indices as a buffer
1048 if (mesh->element3s && !mesh->element3s_indexbuffer)
1049 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1051 // upload int indices as a buffer
1052 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1053 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1055 // vertex buffer is several arrays and we put them in the same buffer
1057 // is this wise? the texcoordtexture2f array is used with dynamic
1058 // vertex/svector/tvector/normal when rendering animated models, on the
1059 // other hand animated models don't use a lot of vertices anyway...
1060 if (!mesh->vbo_vertexbuffer)
1062 mesh->vbooffset_vertex3f = 0;
1063 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1067 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qboolean createvbo)
1069 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1071 if (mesh->vertexhashentries)
1072 Mem_Free(mesh->vertexhashentries);
1073 mesh->vertexhashentries = NULL;
1074 if (mesh->vertexhashtable)
1075 Mem_Free(mesh->vertexhashtable);
1076 mesh->vertexhashtable = NULL;
1077 if (mesh->maxverts > mesh->numverts)
1079 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1080 mesh->maxverts = mesh->numverts;
1082 if (mesh->maxtriangles > mesh->numtriangles)
1084 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1085 mesh->maxtriangles = mesh->numtriangles;
1087 if (mesh->numverts <= 65536)
1090 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1091 for (i = 0;i < mesh->numtriangles*3;i++)
1092 mesh->element3s[i] = mesh->element3i[i];
1095 Mod_ShadowMesh_CreateVBOs(mesh);
1098 // this can take a while, so let's do a keepalive here
1099 CL_KeepaliveMessage(false);
1104 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1107 vec3_t nmins, nmaxs, ncenter, temp;
1108 float nradius2, dist2, *v;
1112 VectorCopy(mesh->vertex3f, nmins);
1113 VectorCopy(mesh->vertex3f, nmaxs);
1114 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1116 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1117 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1118 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1120 // calculate center and radius
1121 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1122 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1123 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1125 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1127 VectorSubtract(v, ncenter, temp);
1128 dist2 = DotProduct(temp, temp);
1129 if (nradius2 < dist2)
1134 VectorCopy(nmins, mins);
1136 VectorCopy(nmaxs, maxs);
1138 VectorCopy(ncenter, center);
1140 *radius = sqrt(nradius2);
1143 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1145 if (mesh->element3i_indexbuffer)
1146 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1147 if (mesh->element3s_indexbuffer)
1148 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1149 if (mesh->vbo_vertexbuffer)
1150 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1152 Mem_Free(mesh->vertex3f);
1153 if (mesh->element3i)
1154 Mem_Free(mesh->element3i);
1155 if (mesh->element3s)
1156 Mem_Free(mesh->element3s);
1157 if (mesh->vertexhashentries)
1158 Mem_Free(mesh->vertexhashentries);
1159 if (mesh->vertexhashtable)
1160 Mem_Free(mesh->vertexhashtable);
1164 void Mod_CreateCollisionMesh(dp_model_t *mod)
1166 int k, numcollisionmeshtriangles;
1167 qboolean usesinglecollisionmesh = false;
1168 const msurface_t *surface = NULL;
1170 mempool_t *mempool = mod->mempool;
1171 if (!mempool && mod->brush.parentmodel)
1172 mempool = mod->brush.parentmodel->mempool;
1173 // make a single combined collision mesh for physics engine use
1174 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1175 numcollisionmeshtriangles = 0;
1176 for (k = 0;k < mod->nummodelsurfaces;k++)
1178 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1179 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1181 usesinglecollisionmesh = true;
1182 numcollisionmeshtriangles = surface->num_triangles;
1185 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1187 numcollisionmeshtriangles += surface->num_triangles;
1189 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1190 if (usesinglecollisionmesh)
1191 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1194 for (k = 0;k < mod->nummodelsurfaces;k++)
1196 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1197 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1199 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1202 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1206 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)
1211 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1212 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1215 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1216 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1217 texcoord2f[0] = tc[0];
1218 texcoord2f[1] = tc[1];
1221 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)
1223 float vup[3], vdown[3], vleft[3], vright[3];
1224 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1225 float sv[3], tv[3], nl[3];
1226 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1227 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1228 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1229 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1230 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1231 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1232 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1233 VectorAdd(svector3f, sv, svector3f);
1234 VectorAdd(tvector3f, tv, tvector3f);
1235 VectorAdd(normal3f, nl, normal3f);
1236 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1237 VectorAdd(svector3f, sv, svector3f);
1238 VectorAdd(tvector3f, tv, tvector3f);
1239 VectorAdd(normal3f, nl, normal3f);
1240 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1241 VectorAdd(svector3f, sv, svector3f);
1242 VectorAdd(tvector3f, tv, tvector3f);
1243 VectorAdd(normal3f, nl, normal3f);
1246 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)
1248 int x, y, ix, iy, *e;
1250 for (y = 0;y < height;y++)
1252 for (x = 0;x < width;x++)
1254 e[0] = (y + 1) * (width + 1) + (x + 0);
1255 e[1] = (y + 0) * (width + 1) + (x + 0);
1256 e[2] = (y + 1) * (width + 1) + (x + 1);
1257 e[3] = (y + 0) * (width + 1) + (x + 0);
1258 e[4] = (y + 0) * (width + 1) + (x + 1);
1259 e[5] = (y + 1) * (width + 1) + (x + 1);
1263 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1264 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1265 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1270 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1274 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1275 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1276 float viewvector[3];
1277 unsigned int firstvertex;
1280 if (chunkwidth < 2 || chunkheight < 2)
1282 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]);
1283 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]);
1284 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1285 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1286 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1287 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1289 // too close for this stepsize, emit as 4 chunks instead
1291 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1292 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1293 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1294 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1297 // emit the geometry at stepsize into our vertex buffer / index buffer
1298 // we add two columns and two rows for skirt
1299 outwidth = chunkwidth+2;
1300 outheight = chunkheight+2;
1301 outwidth2 = outwidth-1;
1302 outheight2 = outheight-1;
1303 outwidth3 = outwidth+1;
1304 outheight3 = outheight+1;
1305 firstvertex = numvertices;
1306 e = model->terrain.element3i + numtriangles;
1307 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1308 v = model->terrain.vertex3f + numvertices;
1309 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1310 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1311 for (ty = 0;ty < outheight;ty++)
1313 for (tx = 0;tx < outwidth;tx++)
1315 *e++ = firstvertex + (ty )*outwidth3+(tx );
1316 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1317 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1318 *e++ = firstvertex + (ty )*outwidth3+(tx );
1319 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1320 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1323 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1324 for (ty = 0;ty <= outheight;ty++)
1326 skirtrow = ty == 0 || ty == outheight;
1327 ry = y+bound(1, ty, outheight)*stepsize;
1328 for (tx = 0;tx <= outwidth;tx++)
1330 skirt = skirtrow || tx == 0 || tx == outwidth;
1331 rx = x+bound(1, tx, outwidth)*stepsize;
1334 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1338 // TODO: emit skirt vertices
1341 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1343 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1344 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1345 Mod_Terrain_BuildChunk(model,
1349 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1352 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1354 offset = bound(0, s[4] - '0', 9);
1355 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1360 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1361 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1362 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1363 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1364 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1365 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1366 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1367 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1368 return offset | Q3WAVEFUNC_NONE;
1371 void Mod_FreeQ3Shaders(void)
1373 Mem_FreePool(&q3shaders_mem);
1376 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
1378 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1379 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1380 q3shader_hash_entry_t* lastEntry = NULL;
1383 if (strcasecmp (entry->shader.name, shader->name) == 0)
1386 if(shader->dpshaderkill)
1388 // killed shader is a redeclarion? we can safely ignore it
1391 else if(entry->shader.dpshaderkill)
1393 // replace the old shader!
1394 // this will skip the entry allocating part
1395 // below and just replace the shader
1400 unsigned char *start, *end, *start2;
1401 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1402 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1403 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1404 if(memcmp(start, start2, end - start))
1405 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1407 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1412 entry = entry->chain;
1414 while (entry != NULL);
1417 if (lastEntry->shader.name[0] != 0)
1420 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1421 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1423 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1424 lastEntry->chain = newEntry;
1425 newEntry->chain = NULL;
1426 lastEntry = newEntry;
1428 /* else: head of chain, in hash entry array */
1431 memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
1434 void Mod_LoadQ3Shaders(void)
1441 q3shaderinfo_t shader;
1442 q3shaderinfo_layer_t *layer;
1444 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1445 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1446 unsigned long custsurfaceflags[256];
1447 int numcustsurfaceflags;
1448 qboolean dpshaderkill;
1450 Mod_FreeQ3Shaders();
1452 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1453 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1454 sizeof (q3shader_data_t));
1455 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1456 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1457 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1458 q3shaders_mem, sizeof (char**), 256);
1460 // parse custinfoparms.txt
1461 numcustsurfaceflags = 0;
1462 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1464 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1465 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1468 while (COM_ParseToken_QuakeC(&text, false))
1469 if (!strcasecmp(com_token, "}"))
1471 // custom surfaceflags section
1472 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1473 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1476 while(COM_ParseToken_QuakeC(&text, false))
1478 if (!strcasecmp(com_token, "}"))
1480 // register surfaceflag
1481 if (numcustsurfaceflags >= 256)
1483 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1487 j = (int)strlen(com_token)+1;
1488 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1489 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1491 if (COM_ParseToken_QuakeC(&text, false))
1492 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1494 custsurfaceflags[numcustsurfaceflags] = 0;
1495 numcustsurfaceflags++;
1503 search = FS_Search("scripts/*.shader", true, false);
1506 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1508 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1511 while (COM_ParseToken_QuakeC(&text, false))
1513 memset (&shader, 0, sizeof(shader));
1515 shader.surfaceparms = 0;
1516 shader.surfaceflags = 0;
1517 shader.textureflags = 0;
1518 shader.numlayers = 0;
1519 shader.lighting = false;
1520 shader.vertexalpha = false;
1521 shader.textureblendalpha = false;
1522 shader.skyboxname[0] = 0;
1523 shader.deforms[0].deform = Q3DEFORM_NONE;
1524 shader.dpnortlight = false;
1525 shader.dpshadow = false;
1526 shader.dpnoshadow = false;
1527 shader.dpmeshcollisions = false;
1528 shader.dpshaderkill = false;
1529 shader.dpreflectcube[0] = 0;
1530 shader.reflectmin = 0;
1531 shader.reflectmax = 1;
1532 shader.refractfactor = 1;
1533 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1534 shader.reflectfactor = 1;
1535 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1536 shader.r_water_wateralpha = 1;
1537 shader.r_water_waterscroll[0] = 0;
1538 shader.r_water_waterscroll[1] = 0;
1539 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1540 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1541 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1542 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1543 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1544 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1545 shader.specularscalemod = 1;
1546 shader.specularpowermod = 1;
1547 shader.rtlightambient = 0;
1548 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1549 // JUST GREP FOR "specularscalemod = 1".
1551 strlcpy(shader.name, com_token, sizeof(shader.name));
1552 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1554 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1557 while (COM_ParseToken_QuakeC(&text, false))
1559 if (!strcasecmp(com_token, "}"))
1561 if (!strcasecmp(com_token, "{"))
1563 static q3shaderinfo_layer_t dummy;
1564 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1566 layer = shader.layers + shader.numlayers++;
1570 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1571 memset(&dummy, 0, sizeof(dummy));
1574 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1575 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1576 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1577 layer->blendfunc[0] = GL_ONE;
1578 layer->blendfunc[1] = GL_ZERO;
1579 while (COM_ParseToken_QuakeC(&text, false))
1581 if (!strcasecmp(com_token, "}"))
1583 if (!strcasecmp(com_token, "\n"))
1586 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1588 if (j < TEXTURE_MAXFRAMES + 4)
1590 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1591 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1592 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1594 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1595 numparameters = j + 1;
1597 if (!COM_ParseToken_QuakeC(&text, true))
1600 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1601 // parameter[j][0] = 0;
1602 if (developer_insane.integer)
1604 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1605 for (j = 0;j < numparameters;j++)
1606 Con_DPrintf(" %s", parameter[j]);
1609 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1611 if (numparameters == 2)
1613 if (!strcasecmp(parameter[1], "add"))
1615 layer->blendfunc[0] = GL_ONE;
1616 layer->blendfunc[1] = GL_ONE;
1618 else if (!strcasecmp(parameter[1], "addalpha"))
1620 layer->blendfunc[0] = GL_SRC_ALPHA;
1621 layer->blendfunc[1] = GL_ONE;
1623 else if (!strcasecmp(parameter[1], "filter"))
1625 layer->blendfunc[0] = GL_DST_COLOR;
1626 layer->blendfunc[1] = GL_ZERO;
1628 else if (!strcasecmp(parameter[1], "blend"))
1630 layer->blendfunc[0] = GL_SRC_ALPHA;
1631 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1634 else if (numparameters == 3)
1637 for (k = 0;k < 2;k++)
1639 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1640 layer->blendfunc[k] = GL_ONE;
1641 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1642 layer->blendfunc[k] = GL_ZERO;
1643 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1644 layer->blendfunc[k] = GL_SRC_COLOR;
1645 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1646 layer->blendfunc[k] = GL_SRC_ALPHA;
1647 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1648 layer->blendfunc[k] = GL_DST_COLOR;
1649 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1650 layer->blendfunc[k] = GL_DST_ALPHA;
1651 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1652 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1653 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1654 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1655 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1656 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1657 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1658 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1660 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1664 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1665 layer->alphatest = true;
1666 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1668 if (!strcasecmp(parameter[0], "clampmap"))
1669 layer->clampmap = true;
1670 layer->numframes = 1;
1671 layer->framerate = 1;
1672 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1673 &q3shader_data->char_ptrs);
1674 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1675 if (!strcasecmp(parameter[1], "$lightmap"))
1676 shader.lighting = true;
1678 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1681 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1682 layer->framerate = atof(parameter[1]);
1683 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1684 for (i = 0;i < layer->numframes;i++)
1685 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1687 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1690 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1691 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1692 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1693 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1694 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1695 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1696 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1697 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1698 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1699 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1700 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1701 else if (!strcasecmp(parameter[1], "wave"))
1703 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1704 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1705 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1706 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1708 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1710 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1713 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1714 layer->alphagen.parms[i] = atof(parameter[i+2]);
1715 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1716 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1717 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1718 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1719 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1720 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1721 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1722 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1723 else if (!strcasecmp(parameter[1], "wave"))
1725 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1726 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1727 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1728 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1730 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1732 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1735 // observed values: tcgen environment
1736 // no other values have been observed in real shaders
1737 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1738 layer->tcgen.parms[i] = atof(parameter[i+2]);
1739 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1740 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1741 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1742 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1743 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1744 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1746 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1753 // tcmod stretch sin # # # #
1754 // tcmod stretch triangle # # # #
1755 // tcmod transform # # # # # #
1756 // tcmod turb # # # #
1757 // tcmod turb sin # # # # (this is bogus)
1758 // no other values have been observed in real shaders
1759 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1760 if (!layer->tcmods[tcmodindex].tcmod)
1762 if (tcmodindex < Q3MAXTCMODS)
1764 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1765 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1766 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1767 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1768 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1769 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1770 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1771 else if (!strcasecmp(parameter[1], "stretch"))
1773 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1774 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1775 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1776 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1778 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1779 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1780 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1783 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1785 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1786 if (!strcasecmp(com_token, "}"))
1789 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1790 shader.lighting = true;
1791 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1793 if (layer == shader.layers + 0)
1795 // vertex controlled transparency
1796 shader.vertexalpha = true;
1800 // multilayer terrain shader or similar
1801 shader.textureblendalpha = true;
1802 if (mod_q3shader_force_terrain_alphaflag.integer)
1803 shader.layers[0].dptexflags |= TEXF_ALPHA;
1807 if(mod_q3shader_force_addalpha.integer)
1809 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1810 // this cvar brings back this behaviour
1811 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1812 layer->blendfunc[0] = GL_SRC_ALPHA;
1815 layer->dptexflags = 0;
1816 if (layer->alphatest)
1817 layer->dptexflags |= TEXF_ALPHA;
1818 switch(layer->blendfunc[0])
1821 case GL_ONE_MINUS_SRC_ALPHA:
1822 layer->dptexflags |= TEXF_ALPHA;
1825 switch(layer->blendfunc[1])
1828 case GL_ONE_MINUS_SRC_ALPHA:
1829 layer->dptexflags |= TEXF_ALPHA;
1832 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1833 layer->dptexflags |= TEXF_MIPMAP;
1834 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1835 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1836 if (layer->clampmap)
1837 layer->dptexflags |= TEXF_CLAMP;
1841 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1843 if (j < TEXTURE_MAXFRAMES + 4)
1845 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1846 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1847 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1849 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1850 numparameters = j + 1;
1852 if (!COM_ParseToken_QuakeC(&text, true))
1855 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1856 // parameter[j][0] = 0;
1857 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1859 if (developer_insane.integer)
1861 Con_DPrintf("%s: ", shader.name);
1862 for (j = 0;j < numparameters;j++)
1863 Con_DPrintf(" %s", parameter[j]);
1866 if (numparameters < 1)
1868 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1870 if (!strcasecmp(parameter[1], "alphashadow"))
1871 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1872 else if (!strcasecmp(parameter[1], "areaportal"))
1873 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1874 else if (!strcasecmp(parameter[1], "botclip"))
1875 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1876 else if (!strcasecmp(parameter[1], "clusterportal"))
1877 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1878 else if (!strcasecmp(parameter[1], "detail"))
1879 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1880 else if (!strcasecmp(parameter[1], "donotenter"))
1881 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1882 else if (!strcasecmp(parameter[1], "dust"))
1883 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1884 else if (!strcasecmp(parameter[1], "hint"))
1885 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1886 else if (!strcasecmp(parameter[1], "fog"))
1887 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1888 else if (!strcasecmp(parameter[1], "lava"))
1889 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1890 else if (!strcasecmp(parameter[1], "lightfilter"))
1891 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1892 else if (!strcasecmp(parameter[1], "lightgrid"))
1893 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1894 else if (!strcasecmp(parameter[1], "metalsteps"))
1895 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1896 else if (!strcasecmp(parameter[1], "nodamage"))
1897 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1898 else if (!strcasecmp(parameter[1], "nodlight"))
1899 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1900 else if (!strcasecmp(parameter[1], "nodraw"))
1901 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1902 else if (!strcasecmp(parameter[1], "nodrop"))
1903 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1904 else if (!strcasecmp(parameter[1], "noimpact"))
1905 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1906 else if (!strcasecmp(parameter[1], "nolightmap"))
1907 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1908 else if (!strcasecmp(parameter[1], "nomarks"))
1909 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1910 else if (!strcasecmp(parameter[1], "nomipmaps"))
1911 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1912 else if (!strcasecmp(parameter[1], "nonsolid"))
1913 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1914 else if (!strcasecmp(parameter[1], "origin"))
1915 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1916 else if (!strcasecmp(parameter[1], "playerclip"))
1917 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1918 else if (!strcasecmp(parameter[1], "sky"))
1919 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1920 else if (!strcasecmp(parameter[1], "slick"))
1921 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1922 else if (!strcasecmp(parameter[1], "slime"))
1923 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1924 else if (!strcasecmp(parameter[1], "structural"))
1925 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1926 else if (!strcasecmp(parameter[1], "trans"))
1927 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1928 else if (!strcasecmp(parameter[1], "water"))
1929 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1930 else if (!strcasecmp(parameter[1], "pointlight"))
1931 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1932 else if (!strcasecmp(parameter[1], "antiportal"))
1933 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1934 else if (!strcasecmp(parameter[1], "skip"))
1935 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1938 // try custom surfaceparms
1939 for (j = 0; j < numcustsurfaceflags; j++)
1941 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1943 shader.surfaceflags |= custsurfaceflags[j];
1948 if (j == numcustsurfaceflags)
1949 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1952 else if (!strcasecmp(parameter[0], "dpshadow"))
1953 shader.dpshadow = true;
1954 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1955 shader.dpnoshadow = true;
1956 else if (!strcasecmp(parameter[0], "dpnortlight"))
1957 shader.dpnortlight = true;
1958 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1959 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1960 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1961 shader.dpmeshcollisions = true;
1962 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1963 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1965 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1966 shader.dpshaderkill = dpshaderkill;
1968 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1969 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1971 const char *op = NULL;
1972 if (numparameters >= 3)
1976 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
1977 shader.dpshaderkill = dpshaderkill;
1979 else if (numparameters >= 4 && !strcmp(op, "=="))
1981 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
1982 shader.dpshaderkill = dpshaderkill;
1984 else if (numparameters >= 4 && !strcmp(op, "!="))
1986 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
1987 shader.dpshaderkill = dpshaderkill;
1989 else if (numparameters >= 4 && !strcmp(op, ">"))
1991 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
1992 shader.dpshaderkill = dpshaderkill;
1994 else if (numparameters >= 4 && !strcmp(op, "<"))
1996 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
1997 shader.dpshaderkill = dpshaderkill;
1999 else if (numparameters >= 4 && !strcmp(op, ">="))
2001 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
2002 shader.dpshaderkill = dpshaderkill;
2004 else if (numparameters >= 4 && !strcmp(op, "<="))
2006 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
2007 shader.dpshaderkill = dpshaderkill;
2011 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2014 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2016 // some q3 skies don't have the sky parm set
2017 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2018 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2020 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2022 // some q3 skies don't have the sky parm set
2023 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2024 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2025 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2027 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2029 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2030 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2032 else if (!strcasecmp(parameter[0], "nomipmaps"))
2033 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2034 else if (!strcasecmp(parameter[0], "nopicmip"))
2035 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2036 else if (!strcasecmp(parameter[0], "polygonoffset"))
2037 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2038 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2040 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2041 if(numparameters >= 2)
2043 shader.biaspolygonfactor = atof(parameter[1]);
2044 if(numparameters >= 3)
2045 shader.biaspolygonoffset = atof(parameter[2]);
2047 shader.biaspolygonoffset = 0;
2050 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2052 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2053 if (!strcasecmp(parameter[1], "sky"))
2054 shader.transparentsort = TRANSPARENTSORT_SKY;
2055 else if (!strcasecmp(parameter[1], "distance"))
2056 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2057 else if (!strcasecmp(parameter[1], "hud"))
2058 shader.transparentsort = TRANSPARENTSORT_HUD;
2060 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2062 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2064 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2065 shader.refractfactor = atof(parameter[1]);
2066 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2068 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2070 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2071 shader.reflectfactor = atof(parameter[1]);
2072 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2074 else if (!strcasecmp(parameter[0], "dpcamera"))
2076 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2078 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2080 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2081 shader.reflectmin = atof(parameter[1]);
2082 shader.reflectmax = atof(parameter[2]);
2083 shader.refractfactor = atof(parameter[3]);
2084 shader.reflectfactor = atof(parameter[4]);
2085 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2086 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2087 shader.r_water_wateralpha = atof(parameter[11]);
2089 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2091 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2092 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2094 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2096 shader.specularscalemod = atof(parameter[1]);
2098 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2100 shader.specularpowermod = atof(parameter[1]);
2102 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2104 shader.rtlightambient = atof(parameter[1]);
2106 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2108 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2109 shader.offsetmapping = OFFSETMAPPING_OFF;
2110 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2111 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2112 else if (!strcasecmp(parameter[1], "linear"))
2113 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2114 else if (!strcasecmp(parameter[1], "relief"))
2115 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2116 if (numparameters >= 3)
2117 shader.offsetscale = atof(parameter[2]);
2118 if (numparameters >= 5)
2120 if(!strcasecmp(parameter[3], "bias"))
2121 shader.offsetbias = atof(parameter[4]);
2122 else if(!strcasecmp(parameter[3], "match"))
2123 shader.offsetbias = 1.0f - atof(parameter[4]);
2124 else if(!strcasecmp(parameter[3], "match8"))
2125 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2126 else if(!strcasecmp(parameter[3], "match16"))
2127 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2130 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2133 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2134 if (!shader.deforms[deformindex].deform)
2136 if (deformindex < Q3MAXDEFORMS)
2138 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2139 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2140 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2141 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2142 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2143 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2144 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2145 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2146 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2147 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2148 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2149 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2150 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2151 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2152 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2153 else if (!strcasecmp(parameter[1], "wave" ))
2155 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2156 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2157 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2158 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2160 else if (!strcasecmp(parameter[1], "move" ))
2162 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2163 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2164 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2165 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2170 // hide this shader if a cvar said it should be killed
2171 if (shader.dpshaderkill)
2172 shader.numlayers = 0;
2173 // fix up multiple reflection types
2174 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2175 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2177 Q3Shader_AddToHash (&shader);
2181 FS_FreeSearch(search);
2182 // free custinfoparm values
2183 for (j = 0; j < numcustsurfaceflags; j++)
2184 Mem_Free(custsurfaceparmnames[j]);
2187 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
2189 unsigned short hash;
2190 q3shader_hash_entry_t* entry;
2192 Mod_LoadQ3Shaders();
2193 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2194 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2195 while (entry != NULL)
2197 if (strcasecmp (entry->shader.name, name) == 0)
2198 return &entry->shader;
2199 entry = entry->chain;
2204 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2206 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2207 shaderpass->framerate = 0.0f;
2208 shaderpass->numframes = 1;
2209 shaderpass->blendfunc[0] = GL_ONE;
2210 shaderpass->blendfunc[1] = GL_ZERO;
2211 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2212 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2213 shaderpass->alphatest = false;
2214 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2215 shaderpass->skinframes[0] = skinframe;
2219 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2222 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2223 shaderpass->alphatest = layer->alphatest != 0;
2224 shaderpass->framerate = layer->framerate;
2225 shaderpass->numframes = layer->numframes;
2226 shaderpass->blendfunc[0] = layer->blendfunc[0];
2227 shaderpass->blendfunc[1] = layer->blendfunc[1];
2228 shaderpass->rgbgen = layer->rgbgen;
2229 shaderpass->alphagen = layer->alphagen;
2230 shaderpass->tcgen = layer->tcgen;
2231 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2232 shaderpass->tcmods[j] = layer->tcmods[j];
2233 for (j = 0; j < layer->numframes; j++)
2234 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2238 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2240 int texflagsmask, texflagsor;
2241 qboolean success = true;
2242 q3shaderinfo_t *shader;
2245 strlcpy(texture->name, name, sizeof(texture->name));
2246 texture->basealpha = 1.0f;
2247 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2249 // allow disabling of picmip or compression by defaulttexflags
2251 if(!(defaulttexflags & TEXF_PICMIP))
2252 texflagsmask &= ~TEXF_PICMIP;
2253 if(!(defaulttexflags & TEXF_COMPRESS))
2254 texflagsmask &= ~TEXF_COMPRESS;
2256 if(defaulttexflags & TEXF_ISWORLD)
2257 texflagsor |= TEXF_ISWORLD;
2258 if(defaulttexflags & TEXF_ISSPRITE)
2259 texflagsor |= TEXF_ISSPRITE;
2260 // unless later loaded from the shader
2261 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2262 texture->offsetscale = 1;
2263 texture->offsetbias = 0;
2264 texture->specularscalemod = 1;
2265 texture->specularpowermod = 1;
2266 texture->rtlightambient = 0;
2267 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2268 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2269 // JUST GREP FOR "specularscalemod = 1".
2273 if (developer_loading.integer)
2274 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2276 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2278 texture->basematerialflags = MATERIALFLAG_SKY;
2279 if (shader->skyboxname[0] && loadmodel)
2281 // quake3 seems to append a _ to the skybox name, so this must do so as well
2282 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2285 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2286 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2288 texture->basematerialflags = MATERIALFLAG_WALL;
2290 if (shader->layers[0].alphatest)
2291 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2292 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2293 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2294 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2296 texture->biaspolygonoffset += shader->biaspolygonoffset;
2297 texture->biaspolygonfactor += shader->biaspolygonfactor;
2299 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2300 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2301 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2302 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2303 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2304 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2305 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2306 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2307 texture->customblendfunc[0] = GL_ONE;
2308 texture->customblendfunc[1] = GL_ZERO;
2309 texture->transparentsort = shader->transparentsort;
2310 if (shader->numlayers > 0)
2312 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2313 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2315 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2316 * additive GL_ONE GL_ONE
2317 additive weird GL_ONE GL_SRC_ALPHA
2318 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2319 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2320 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2321 brighten GL_DST_COLOR GL_ONE
2322 brighten GL_ONE GL_SRC_COLOR
2323 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2324 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2325 * modulate GL_DST_COLOR GL_ZERO
2326 * modulate GL_ZERO GL_SRC_COLOR
2327 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2328 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2329 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2330 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2331 * no blend GL_ONE GL_ZERO
2332 nothing GL_ZERO GL_ONE
2334 // if not opaque, figure out what blendfunc to use
2335 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2337 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2338 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2339 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2340 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2341 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2342 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2344 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2347 if (!shader->lighting)
2348 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2350 // here be dragons: convert quake3 shaders to material
2351 if (shader->numlayers > 0)
2354 int terrainbackgroundlayer = -1;
2355 int lightmaplayer = -1;
2356 int alphagenspecularlayer = -1;
2357 int rgbgenvertexlayer = -1;
2358 int rgbgendiffuselayer = -1;
2359 int materiallayer = -1;
2360 int endofprelayers = 0;
2361 int firstpostlayer = 0;
2362 int shaderpassindex = 0;
2363 for (i = 0; i < shader->numlayers; i++)
2365 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2367 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2368 rgbgenvertexlayer = i;
2369 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2370 rgbgendiffuselayer = i;
2371 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2372 alphagenspecularlayer = i;
2374 if (shader->numlayers >= 2
2375 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2376 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2377 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2378 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2380 // terrain blend or certain other effects involving alphatest over a regular layer
2381 terrainbackgroundlayer = 0;
2383 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2384 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2386 else if (lightmaplayer == 0)
2388 // ordinary texture but with $lightmap before diffuse
2390 firstpostlayer = lightmaplayer + 2;
2392 else if (lightmaplayer >= 1)
2394 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2395 endofprelayers = lightmaplayer - 1;
2396 materiallayer = lightmaplayer - 1;
2397 firstpostlayer = lightmaplayer + 1;
2399 else if (rgbgenvertexlayer >= 0)
2401 // map models with baked lighting
2402 materiallayer = rgbgenvertexlayer;
2403 endofprelayers = rgbgenvertexlayer;
2404 firstpostlayer = rgbgenvertexlayer + 1;
2405 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2406 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2407 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2409 else if (rgbgendiffuselayer >= 0)
2411 // entity models with dynamic lighting
2412 materiallayer = rgbgendiffuselayer;
2413 endofprelayers = rgbgendiffuselayer;
2414 firstpostlayer = rgbgendiffuselayer + 1;
2415 // 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)...
2416 if (alphagenspecularlayer >= 0)
2417 firstpostlayer = alphagenspecularlayer + 1;
2421 // special effects shaders - treat first as primary layer and do everything else as post
2426 // convert the main material layer
2427 // 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
2428 if (materiallayer >= 0)
2429 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2430 // convert the terrain background blend layer (if any)
2431 if (terrainbackgroundlayer >= 0)
2432 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2433 // convert the prepass layers (if any)
2434 texture->startpreshaderpass = shaderpassindex;
2435 for (i = 0; i < endofprelayers; i++)
2436 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2437 texture->endpreshaderpass = shaderpassindex;
2438 texture->startpostshaderpass = shaderpassindex;
2439 // convert the postpass layers (if any)
2440 for (i = firstpostlayer; i < shader->numlayers; i++)
2441 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2442 texture->startpostshaderpass = shaderpassindex;
2445 if (shader->dpshadow)
2446 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2447 if (shader->dpnoshadow)
2448 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2449 if (shader->dpnortlight)
2450 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2451 if (shader->vertexalpha)
2452 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2453 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2454 texture->reflectmin = shader->reflectmin;
2455 texture->reflectmax = shader->reflectmax;
2456 texture->refractfactor = shader->refractfactor;
2457 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2458 texture->reflectfactor = shader->reflectfactor;
2459 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2460 texture->r_water_wateralpha = shader->r_water_wateralpha;
2461 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2462 texture->offsetmapping = shader->offsetmapping;
2463 texture->offsetscale = shader->offsetscale;
2464 texture->offsetbias = shader->offsetbias;
2465 texture->specularscalemod = shader->specularscalemod;
2466 texture->specularpowermod = shader->specularpowermod;
2467 texture->rtlightambient = shader->rtlightambient;
2468 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2469 if (shader->dpreflectcube[0])
2470 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2472 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2473 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2474 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2475 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2476 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2477 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2478 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2479 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2480 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2482 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2483 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2484 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2485 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2486 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2487 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2488 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2489 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2490 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2491 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2492 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2493 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2494 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2495 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2496 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2497 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2498 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2499 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2500 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2501 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2502 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2503 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2504 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2505 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2506 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2507 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2508 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2509 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2510 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2511 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2512 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2513 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2515 texture->surfaceflags = shader->surfaceflags;
2516 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2520 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2523 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2524 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2525 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2526 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2527 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2528 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2529 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2530 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2531 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2532 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2534 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2536 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2537 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2539 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2540 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2541 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2542 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2543 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2544 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2545 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2546 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2547 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2549 if (shader->dpmeshcollisions)
2550 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2551 if (shader->dpshaderkill && developer_extra.integer)
2552 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2554 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2556 if (developer_extra.integer)
2557 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2558 texture->basematerialflags = defaultmaterialflags;
2559 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2561 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2563 if (developer_extra.integer)
2564 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2565 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2566 texture->supercontents = SUPERCONTENTS_SOLID;
2570 if (developer_extra.integer)
2571 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2572 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2574 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2575 texture->supercontents = SUPERCONTENTS_SOLID;
2577 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2579 texture->basematerialflags = MATERIALFLAG_SKY;
2580 texture->supercontents = SUPERCONTENTS_SKY;
2584 texture->basematerialflags = defaultmaterialflags;
2585 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2587 if(cls.state == ca_dedicated)
2589 texture->materialshaderpass = NULL;
2594 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2597 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2598 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2599 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2600 if (texture->q2contents)
2601 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2605 if (!success && warnmissing)
2606 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2609 // init the animation variables
2610 texture->currentframe = texture;
2611 texture->currentmaterialflags = texture->basematerialflags;
2612 if (!texture->materialshaderpass)
2613 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2614 if (!texture->materialshaderpass->skinframes[0])
2615 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2616 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2617 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2621 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2623 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2624 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2626 strlcpy(texture->name, name, sizeof(texture->name));
2627 texture->basealpha = 1.0f;
2628 texture->basematerialflags = materialflags;
2629 texture->supercontents = supercontents;
2631 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2632 texture->offsetscale = 1;
2633 texture->offsetbias = 0;
2634 texture->specularscalemod = 1;
2635 texture->specularpowermod = 1;
2636 texture->rtlightambient = 0;
2637 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2638 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2639 // JUST GREP FOR "specularscalemod = 1".
2641 if (developer_extra.integer)
2642 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2644 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2646 // init the animation variables
2647 texture->currentmaterialflags = texture->basematerialflags;
2648 texture->currentframe = texture;
2649 texture->currentskinframe = skinframe;
2650 texture->backgroundcurrentskinframe = NULL;
2653 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2656 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2658 if (texture->shaderpasses[i])
2661 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2662 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2663 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2664 Mem_Free(texture->shaderpasses[i]);
2665 texture->shaderpasses[i] = NULL;
2668 texture->materialshaderpass = NULL;
2669 texture->currentskinframe = NULL;
2670 texture->backgroundcurrentskinframe = NULL;
2673 skinfile_t *Mod_LoadSkinFiles(void)
2675 int i, words, line, wordsoverflow;
2678 skinfile_t *skinfile = NULL, *first = NULL;
2679 skinfileitem_t *skinfileitem;
2680 char word[10][MAX_QPATH];
2685 U_bodyBox,models/players/Legoman/BikerA2.tga
2686 U_RArm,models/players/Legoman/BikerA1.tga
2687 U_LArm,models/players/Legoman/BikerA1.tga
2688 U_armor,common/nodraw
2689 U_sword,common/nodraw
2690 U_shield,common/nodraw
2691 U_homb,common/nodraw
2692 U_backpack,common/nodraw
2693 U_colcha,common/nodraw
2698 memset(word, 0, sizeof(word));
2699 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2701 // If it's the first file we parse
2702 if (skinfile == NULL)
2704 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2709 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2710 skinfile = skinfile->next;
2712 skinfile->next = NULL;
2714 for(line = 0;;line++)
2717 if (!COM_ParseToken_QuakeC(&data, true))
2719 if (!strcmp(com_token, "\n"))
2722 wordsoverflow = false;
2726 strlcpy(word[words++], com_token, sizeof (word[0]));
2728 wordsoverflow = true;
2730 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2733 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);
2736 // words is always >= 1
2737 if (!strcmp(word[0], "replace"))
2741 if (developer_loading.integer)
2742 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2743 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2744 skinfileitem->next = skinfile->items;
2745 skinfile->items = skinfileitem;
2746 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2747 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2750 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]);
2752 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2754 // tag name, like "tag_weapon,"
2755 // not used for anything (not even in Quake3)
2757 else if (words >= 2 && !strcmp(word[1], ","))
2759 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2760 if (developer_loading.integer)
2761 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2762 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2763 skinfileitem->next = skinfile->items;
2764 skinfile->items = skinfileitem;
2765 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2766 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2769 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);
2774 loadmodel->numskins = i;
2778 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2781 skinfileitem_t *skinfileitem, *nextitem;
2782 for (;skinfile;skinfile = next)
2784 next = skinfile->next;
2785 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2787 nextitem = skinfileitem->next;
2788 Mem_Free(skinfileitem);
2794 int Mod_CountSkinFiles(skinfile_t *skinfile)
2797 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2801 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2804 double isnap = 1.0 / snap;
2805 for (i = 0;i < numvertices*numcomponents;i++)
2806 vertices[i] = floor(vertices[i]*isnap)*snap;
2809 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2811 int i, outtriangles;
2812 float edgedir1[3], edgedir2[3], temp[3];
2813 // a degenerate triangle is one with no width (thickness, surface area)
2814 // these are characterized by having all 3 points colinear (along a line)
2815 // or having two points identical
2816 // the simplest check is to calculate the triangle's area
2817 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2819 // calculate first edge
2820 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2821 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2822 CrossProduct(edgedir1, edgedir2, temp);
2823 if (VectorLength2(temp) < 0.001f)
2824 continue; // degenerate triangle (no area)
2825 // valid triangle (has area)
2826 VectorCopy(inelement3i, outelement3i);
2830 return outtriangles;
2833 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2836 int firstvertex, lastvertex;
2837 if (numelements > 0 && elements)
2839 firstvertex = lastvertex = elements[0];
2840 for (i = 1;i < numelements;i++)
2843 firstvertex = min(firstvertex, e);
2844 lastvertex = max(lastvertex, e);
2848 firstvertex = lastvertex = 0;
2849 if (firstvertexpointer)
2850 *firstvertexpointer = firstvertex;
2851 if (lastvertexpointer)
2852 *lastvertexpointer = lastvertex;
2855 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2857 // make an optimal set of texture-sorted batches to draw...
2859 int *firstsurfacefortexture;
2860 int *numsurfacesfortexture;
2861 if (!mod->sortedmodelsurfaces)
2862 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2863 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2864 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2865 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2866 for (j = 0;j < mod->nummodelsurfaces;j++)
2868 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2869 t = (int)(surface->texture - mod->data_textures);
2870 numsurfacesfortexture[t]++;
2873 for (t = 0;t < mod->num_textures;t++)
2875 firstsurfacefortexture[t] = j;
2876 j += numsurfacesfortexture[t];
2878 for (j = 0;j < mod->nummodelsurfaces;j++)
2880 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2881 t = (int)(surface->texture - mod->data_textures);
2882 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2884 Mem_Free(firstsurfacefortexture);
2885 Mem_Free(numsurfacesfortexture);
2888 void Mod_BuildVBOs(void)
2890 if(cls.state == ca_dedicated)
2893 if (!loadmodel->surfmesh.num_vertices)
2896 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2899 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2901 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2903 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2904 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2909 // upload short indices as a buffer
2910 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2911 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);
2913 // upload int indices as a buffer
2914 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2915 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);
2917 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2918 // we put several vertex data streams in the same buffer
2919 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2924 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2925 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2926 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2927 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2928 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2929 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2930 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2931 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2932 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2933 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2934 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2935 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2936 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2937 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2938 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2939 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2940 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2941 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2942 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2943 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2944 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2945 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2946 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2947 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2948 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2949 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2950 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2951 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2956 extern cvar_t mod_obj_orientation;
2957 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2959 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2961 const char *texname;
2963 const float *v, *vn, *vt;
2965 size_t outbufferpos = 0;
2966 size_t outbuffermax = 0x100000;
2967 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
2968 const msurface_t *surface;
2969 const int maxtextures = 256;
2970 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
2971 dp_model_t *submodel;
2973 // construct the mtllib file
2974 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
2977 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
2980 countvertices += surface->num_vertices;
2981 countfaces += surface->num_triangles;
2982 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
2983 for (textureindex = 0;textureindex < counttextures;textureindex++)
2984 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
2986 if (textureindex < counttextures)
2987 continue; // already wrote this material entry
2988 if (textureindex >= maxtextures)
2989 continue; // just a precaution
2990 textureindex = counttextures++;
2991 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
2992 if (outbufferpos >= outbuffermax >> 1)
2995 oldbuffer = outbuffer;
2996 outbuffer = (char *) Z_Malloc(outbuffermax);
2997 memcpy(outbuffer, oldbuffer, outbufferpos);
3000 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");
3005 // write the mtllib file
3006 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3008 // construct the obj file
3010 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);
3014 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)
3016 if (outbufferpos >= outbuffermax >> 1)
3019 oldbuffer = outbuffer;
3020 outbuffer = (char *) Z_Malloc(outbuffermax);
3021 memcpy(outbuffer, oldbuffer, outbufferpos);
3024 if(mod_obj_orientation.integer)
3025 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]);
3027 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]);
3032 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3034 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3037 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3038 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3040 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3041 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3044 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3046 if (outbufferpos >= outbuffermax >> 1)
3049 oldbuffer = outbuffer;
3050 outbuffer = (char *) Z_Malloc(outbuffermax);
3051 memcpy(outbuffer, oldbuffer, outbufferpos);
3057 if(mod_obj_orientation.integer)
3058 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);
3060 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);
3067 // write the obj file
3068 FS_WriteFile(filename, outbuffer, outbufferpos);
3072 Z_Free(texturenames);
3075 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3078 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3080 int countnodes = 0, counttriangles = 0, countframes = 0;
3088 size_t outbufferpos = 0;
3089 size_t outbuffermax = 0x100000;
3090 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3091 const msurface_t *surface;
3092 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3095 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3097 if (outbufferpos >= outbuffermax >> 1)
3100 oldbuffer = outbuffer;
3101 outbuffer = (char *) Z_Malloc(outbuffermax);
3102 memcpy(outbuffer, oldbuffer, outbufferpos);
3106 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3110 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3113 for (poseindex = 0;poseindex < numposes;poseindex++)
3116 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3119 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3123 matrix4x4_t posematrix;
3124 if (outbufferpos >= outbuffermax >> 1)
3127 oldbuffer = outbuffer;
3128 outbuffer = (char *) Z_Malloc(outbuffermax);
3129 memcpy(outbuffer, oldbuffer, outbufferpos);
3133 // strangely the smd angles are for a transposed matrix, so we
3134 // have to generate a transposed matrix, then convert that...
3135 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3136 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3137 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3138 if (angles[0] >= 180) angles[0] -= 360;
3139 if (angles[1] >= 180) angles[1] -= 360;
3140 if (angles[2] >= 180) angles[2] -= 360;
3144 float a = DEG2RAD(angles[ROLL]);
3145 float b = DEG2RAD(angles[PITCH]);
3146 float c = DEG2RAD(angles[YAW]);
3147 float cy, sy, cp, sp, cr, sr;
3149 // smd matrix construction, for comparing
3160 test[1][0] = sr*sp*cy+cr*-sy;
3161 test[1][1] = sr*sp*sy+cr*cy;
3163 test[2][0] = (cr*sp*cy+-sr*-sy);
3164 test[2][1] = (cr*sp*sy+-sr*cy);
3166 test[3][0] = pose[9];
3167 test[3][1] = pose[10];
3168 test[3][2] = pose[11];
3171 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]));
3176 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3181 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3184 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3186 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3189 if (outbufferpos >= outbuffermax >> 1)
3192 oldbuffer = outbuffer;
3193 outbuffer = (char *) Z_Malloc(outbuffermax);
3194 memcpy(outbuffer, oldbuffer, outbufferpos);
3197 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3200 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3202 const int index = e[2-cornerindex];
3203 const float *v = model->surfmesh.data_vertex3f + index * 3;
3204 const float *vn = model->surfmesh.data_normal3f + index * 3;
3205 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3206 const int b = model->surfmesh.blends[index];
3207 if (b < model->num_bones)
3208 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]);
3211 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3212 const unsigned char *wi = w->index;
3213 const unsigned char *wf = w->influence;
3214 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);
3215 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);
3216 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);
3217 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]);
3224 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3229 FS_WriteFile(filename, outbuffer, outbufferpos);
3232 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3239 decompiles a model to editable files
3242 static void Mod_Decompile_f(cmd_state_t *cmd)
3244 int i, j, k, l, first, count;
3246 char inname[MAX_QPATH];
3247 char outname[MAX_QPATH];
3248 char mtlname[MAX_QPATH];
3249 char basename[MAX_QPATH];
3250 char animname[MAX_QPATH];
3251 char animname2[MAX_QPATH];
3252 char zymtextbuffer[16384];
3253 char dpmtextbuffer[16384];
3254 char framegroupstextbuffer[16384];
3255 int zymtextsize = 0;
3256 int dpmtextsize = 0;
3257 int framegroupstextsize = 0;
3260 if (Cmd_Argc(cmd) != 2)
3262 Con_Print("usage: modeldecompile <filename>\n");
3266 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3267 FS_StripExtension(inname, basename, sizeof(basename));
3269 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3272 Con_Print("No such model\n");
3275 if (mod->brush.submodel)
3277 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3278 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3279 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3282 if (!mod->surfmesh.num_triangles)
3284 Con_Print("Empty model (or sprite)\n");
3288 // export OBJ if possible (not on sprites)
3289 if (mod->surfmesh.num_triangles)
3291 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3292 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3293 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3296 // export SMD if possible (only for skeletal models)
3297 if (mod->surfmesh.num_triangles && mod->num_bones)
3299 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3300 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3301 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3302 if (l > 0) zymtextsize += l;
3303 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3304 if (l > 0) dpmtextsize += l;
3305 for (i = 0;i < mod->numframes;i = j)
3307 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3308 first = mod->animscenes[i].firstframe;
3309 if (mod->animscenes[i].framecount > 1)
3312 count = mod->animscenes[i].framecount;
3318 // check for additional frames with same name
3319 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3320 if(animname[l] < '0' || animname[l] > '9')
3322 if(k > 0 && animname[k-1] == '_')
3325 count = mod->num_poses - first;
3326 for (j = i + 1;j < mod->numframes;j++)
3328 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3329 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3330 if(animname2[l] < '0' || animname2[l] > '9')
3332 if(k > 0 && animname[k-1] == '_')
3335 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3337 count = mod->animscenes[j].firstframe - first;
3341 // if it's only one frame, use the original frame name
3343 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3346 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3347 Mod_Decompile_SMD(mod, outname, first, count, false);
3348 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3350 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3351 if (l > 0) zymtextsize += l;
3353 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3355 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3356 if (l > 0) dpmtextsize += l;
3358 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3360 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3361 if (l > 0) framegroupstextsize += l;
3365 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3367 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3368 if (framegroupstextsize)
3369 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3373 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3376 memset(state, 0, sizeof(*state));
3377 state->width = width;
3378 state->height = height;
3379 state->currentY = 0;
3380 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3381 for (y = 0;y < state->height;y++)
3383 state->rows[y].currentX = 0;
3384 state->rows[y].rowY = -1;
3388 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3391 state->currentY = 0;
3392 for (y = 0;y < state->height;y++)
3394 state->rows[y].currentX = 0;
3395 state->rows[y].rowY = -1;
3399 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3402 Mem_Free(state->rows);
3403 memset(state, 0, sizeof(*state));
3406 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3408 mod_alloclightmap_row_t *row;
3411 row = state->rows + blockheight;
3412 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3414 if (state->currentY + blockheight <= state->height)
3416 // use the current allocation position
3417 row->rowY = state->currentY;
3419 state->currentY += blockheight;
3423 // find another position
3424 for (y = blockheight;y < state->height;y++)
3426 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3428 row = state->rows + y;
3432 if (y == state->height)
3437 *outx = row->currentX;
3438 row->currentX += blockwidth;
3443 typedef struct lightmapsample_s
3447 float *vertex_color;
3448 unsigned char *lm_bgr;
3449 unsigned char *lm_dir;
3453 typedef struct lightmapvertex_s
3458 float texcoordbase[2];
3459 float texcoordlightmap[2];
3460 float lightcolor[4];
3464 typedef struct lightmaptriangle_s
3472 // 2D modelspace coordinates of min corner
3473 // snapped to lightmap grid but not in grid coordinates
3475 // 2D modelspace to lightmap coordinate scale
3483 typedef struct lightmaplight_s
3494 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3496 #define MAX_LIGHTMAPSAMPLES 64
3497 static int mod_generatelightmaps_numoffsets[3];
3498 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3500 static int mod_generatelightmaps_numlights;
3501 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3503 extern cvar_t r_shadow_lightattenuationdividebias;
3504 extern cvar_t r_shadow_lightattenuationlinearscale;
3506 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3511 float relativepoint[3];
3518 float lightorigin[3];
3522 float lightcolor[3];
3524 for (i = 0;i < 5*3;i++)
3526 for (index = 0;;index++)
3528 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3533 lightradius2 = lightradius * lightradius;
3534 VectorSubtract(lightorigin, pos, relativepoint);
3535 dist2 = VectorLength2(relativepoint);
3536 if (dist2 >= lightradius2)
3538 lightiradius = 1.0f / lightradius;
3539 dist = sqrt(dist2) * lightiradius;
3540 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3541 if (intensity <= 0.0f)
3543 if (model && model->TraceLine)
3545 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3546 if (trace.fraction < 1)
3549 // scale down intensity to add to both ambient and diffuse
3550 //intensity *= 0.5f;
3551 VectorNormalize(relativepoint);
3552 VectorScale(lightcolor, intensity, color);
3553 VectorMA(sample , 0.5f , color, sample );
3554 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3555 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3556 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3557 // calculate a weighted average light direction as well
3558 intensity *= VectorLength(color);
3559 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3561 // calculate the direction we'll use to reduce the sample to a directional light source
3562 VectorCopy(sample + 12, dir);
3563 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3564 VectorNormalize(dir);
3565 // extract the diffuse color along the chosen direction and scale it
3566 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3567 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3568 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3569 // subtract some of diffuse from ambient
3570 VectorMA(sample, -0.333f, diffuse, ambient);
3571 // store the normalized lightdir
3572 VectorCopy(dir, lightdir);
3575 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3579 const msurface_t *surface;
3580 const float *vertex3f = model->surfmesh.data_vertex3f;
3581 const int *element3i = model->surfmesh.data_element3i;
3584 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3586 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3588 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3590 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3592 VectorCopy(vertex3f + 3*e[0], v2[0]);
3593 VectorCopy(vertex3f + 3*e[1], v2[1]);
3594 VectorCopy(vertex3f + 3*e[2], v2[2]);
3595 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3600 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3602 int maxnodes = 1<<14;
3603 svbsp_node_t *nodes;
3608 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3609 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3610 VectorCopy(lightinfo->origin, origin);
3611 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3614 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3615 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3616 if (svbsp.ranoutofnodes)
3619 if (maxnodes > 1<<22)
3625 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3630 if (svbsp.numnodes > 0)
3632 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3633 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3634 lightinfo->svbsp = svbsp;
3639 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3643 lightmaplight_t *lightinfo;
3647 mod_generatelightmaps_numlights = 0;
3648 for (index = 0;;index++)
3650 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3654 mod_generatelightmaps_numlights++;
3656 if (mod_generatelightmaps_numlights > 0)
3658 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3659 lightinfo = mod_generatelightmaps_lightinfo;
3660 for (index = 0;;index++)
3662 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3669 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3671 lightinfo->iradius = 1.0f / lightinfo->radius;
3672 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3673 // TODO: compute svbsp
3674 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3678 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3681 if (mod_generatelightmaps_lightinfo)
3683 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3684 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3685 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3686 Mem_Free(mod_generatelightmaps_lightinfo);
3688 mod_generatelightmaps_lightinfo = NULL;
3689 mod_generatelightmaps_numlights = 0;
3692 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3694 const svbsp_node_t *node;
3695 const svbsp_node_t *nodes = svbsp->nodes;
3700 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3702 return num == -1; // true if empty, false if solid (shadowed)
3705 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3708 float relativepoint[3];
3717 const lightmaplight_t *lightinfo;
3719 for (i = 0;i < 5*3;i++)
3721 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3723 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3724 VectorSubtract(lightinfo->origin, pos, relativepoint);
3725 // don't accept light from behind a surface, it causes bad shading
3726 if (normal && DotProduct(relativepoint, normal) <= 0)
3728 dist2 = VectorLength2(relativepoint);
3729 if (dist2 >= lightinfo->radius2)
3731 dist = sqrt(dist2) * lightinfo->iradius;
3732 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3735 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3739 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3741 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3743 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3746 // for light grid we'd better check visibility of the offset point
3747 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3748 if (trace.fraction < 1)
3749 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3752 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3757 // scale intensity according to how many rays succeeded
3758 // we know one test is valid, half of the rest will fail...
3759 //if (normal && tests > 1)
3760 // intensity *= (tests - 1.0f) / tests;
3761 intensity *= (float)hits / tests;
3763 // scale down intensity to add to both ambient and diffuse
3764 //intensity *= 0.5f;
3765 VectorNormalize(relativepoint);
3766 VectorScale(lightinfo->color, intensity, color);
3767 VectorMA(sample , 0.5f , color, sample );
3768 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3769 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3770 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3771 // calculate a weighted average light direction as well
3772 intensity *= VectorLength(color);
3773 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3777 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3783 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3784 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3785 VectorCopy(sample + 12, dir);
3786 VectorNormalize(dir);
3787 //VectorAdd(dir, normal, dir);
3788 //VectorNormalize(dir);
3789 f = DotProduct(dir, normal);
3790 f = max(0, f) * 255.0f;
3791 VectorScale(sample, f, color);
3792 //VectorCopy(normal, dir);
3793 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3794 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3795 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3796 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3798 lm_dir[0] = (unsigned char)dir[2];
3799 lm_dir[1] = (unsigned char)dir[1];
3800 lm_dir[2] = (unsigned char)dir[0];
3804 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3807 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3808 VectorCopy(sample, vertex_color);
3811 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3817 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3818 // calculate the direction we'll use to reduce the sample to a directional light source
3819 VectorCopy(sample + 12, dir);
3820 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3821 VectorNormalize(dir);
3822 // extract the diffuse color along the chosen direction and scale it
3823 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3824 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3825 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3826 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3827 VectorScale(sample, 127.5f, ambient);
3828 VectorMA(ambient, -0.333f, diffuse, ambient);
3829 // encode to the grid format
3830 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3831 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3832 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3833 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3834 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3835 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3836 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3837 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3838 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));}
3841 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3846 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3847 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3848 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3849 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3850 radius[0] = mod_generatelightmaps_lightmapradius.value;
3851 radius[1] = mod_generatelightmaps_vertexradius.value;
3852 radius[2] = mod_generatelightmaps_gridradius.value;
3853 for (i = 0;i < 3;i++)
3855 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3858 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3863 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3865 msurface_t *surface;
3868 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3870 surface = model->data_surfaces + surfaceindex;
3871 surface->lightmaptexture = NULL;
3872 surface->deluxemaptexture = NULL;
3874 if (model->brushq3.data_lightmaps)
3876 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3877 if (model->brushq3.data_lightmaps[i])
3878 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3879 Mem_Free(model->brushq3.data_lightmaps);
3880 model->brushq3.data_lightmaps = NULL;
3882 if (model->brushq3.data_deluxemaps)
3884 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3885 if (model->brushq3.data_deluxemaps[i])
3886 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3887 Mem_Free(model->brushq3.data_deluxemaps);
3888 model->brushq3.data_deluxemaps = NULL;
3892 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3894 msurface_t *surface;
3900 surfmesh_t oldsurfmesh;
3902 unsigned char *data;
3903 oldsurfmesh = model->surfmesh;
3904 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3905 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3907 size += model->surfmesh.num_vertices * sizeof(float[3]);
3908 size += model->surfmesh.num_vertices * sizeof(float[3]);
3909 size += model->surfmesh.num_vertices * sizeof(float[3]);
3910 size += model->surfmesh.num_vertices * sizeof(float[3]);
3911 size += model->surfmesh.num_vertices * sizeof(float[2]);
3912 size += model->surfmesh.num_vertices * sizeof(float[2]);
3913 size += model->surfmesh.num_vertices * sizeof(float[4]);
3914 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3915 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3916 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3917 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3918 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3919 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3920 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3921 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3922 if (model->surfmesh.num_vertices > 65536)
3923 model->surfmesh.data_element3s = NULL;
3925 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3926 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3927 model->surfmesh.data_element3i_indexbuffer = NULL;
3928 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3929 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3930 model->surfmesh.data_element3s_indexbuffer = NULL;
3931 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3932 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3933 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3934 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3935 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3936 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3937 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3938 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3939 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3940 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3941 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
3943 // convert all triangles to unique vertex data
3945 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3947 surface = model->data_surfaces + surfaceindex;
3948 surface->num_firstvertex = outvertexindex;
3949 surface->num_vertices = surface->num_triangles*3;
3950 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3951 for (i = 0;i < surface->num_triangles*3;i++)
3954 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3955 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3956 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3957 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3958 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3959 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3960 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3961 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3962 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3963 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3964 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3965 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3966 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
3967 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
3968 if (oldsurfmesh.data_texcoordlightmap2f)
3970 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
3971 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
3973 if (oldsurfmesh.data_lightmapcolor4f)
3975 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
3976 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
3977 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
3978 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
3981 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
3982 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
3986 if (model->surfmesh.data_element3s)
3987 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
3988 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
3990 // find and update all submodels to use this new surfmesh data
3991 for (i = 0;i < model->brush.numsubmodels;i++)
3992 model->brush.submodels[i]->surfmesh = model->surfmesh;
3995 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
3997 msurface_t *surface;
4003 lightmaptriangle_t *triangle;
4004 // generate lightmap triangle structs
4005 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4006 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4008 surface = model->data_surfaces + surfaceindex;
4009 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4010 for (i = 0;i < surface->num_triangles;i++)
4012 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4013 triangle->triangleindex = surface->num_firsttriangle+i;
4014 triangle->surfaceindex = surfaceindex;
4015 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4016 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4017 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4018 // calculate bounds of triangle
4019 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4020 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4021 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4022 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4023 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4024 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4025 // pick an axial projection based on the triangle normal
4026 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4028 if (fabs(normal[1]) > fabs(normal[axis]))
4030 if (fabs(normal[2]) > fabs(normal[axis]))
4032 triangle->axis = axis;
4037 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4039 if (mod_generatelightmaps_lightmaptriangles)
4040 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4041 mod_generatelightmaps_lightmaptriangles = NULL;
4044 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4046 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4048 msurface_t *surface;
4062 float trianglenormal[3];
4063 float samplecenter[3];
4064 float samplenormal[3];
4070 float lmscalepixels;
4073 float lm_basescalepixels;
4074 int lm_borderpixels;
4078 lightmaptriangle_t *triangle;
4079 unsigned char *lightmappixels;
4080 unsigned char *deluxemappixels;
4081 mod_alloclightmap_state_t lmstate;
4084 // generate lightmap projection information for all triangles
4085 if (model->texturepool == NULL)
4086 model->texturepool = R_AllocTexturePool();
4087 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4088 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4089 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4090 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4091 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4093 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4095 surface = model->data_surfaces + surfaceindex;
4096 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4097 lmscalepixels = lm_basescalepixels;
4098 for (retry = 0;retry < 30;retry++)
4100 // after a couple failed attempts, degrade quality to make it fit
4102 lmscalepixels *= 0.5f;
4103 for (i = 0;i < surface->num_triangles;i++)
4105 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4106 triangle->lightmapindex = lightmapnumber;
4107 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4108 // pick two planar axes for projection
4109 // lightmap coordinates here are in pixels
4110 // lightmap projections are snapped to pixel grid explicitly, such
4111 // that two neighboring triangles sharing an edge and projection
4112 // axis will have identical sample spacing along their shared edge
4114 for (j = 0;j < 3;j++)
4116 if (j == triangle->axis)
4118 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4119 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4120 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4121 triangle->lmbase[k] = lmmins/lmscalepixels;
4122 triangle->lmscale[k] = lmscalepixels;
4125 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4128 // if all fit in this texture, we're done with this surface
4129 if (i == surface->num_triangles)
4131 // if we haven't maxed out the lightmap size yet, we retry the
4132 // entire surface batch...
4133 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4135 lm_texturesize *= 2;
4138 Mod_AllocLightmap_Free(&lmstate);
4139 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4142 // if we have maxed out the lightmap size, and this triangle does
4143 // not fit in the same texture as the rest of the surface, we have
4144 // to retry the entire surface in a new texture (can only use one)
4145 // with multiple retries, the lightmap quality degrades until it
4146 // fits (or gives up)
4147 if (surfaceindex > 0)
4149 Mod_AllocLightmap_Reset(&lmstate);
4153 Mod_AllocLightmap_Free(&lmstate);
4155 // now put triangles together into lightmap textures, and do not allow
4156 // triangles of a surface to go into different textures (as that would
4157 // require rewriting the surface list)
4158 model->brushq3.deluxemapping_modelspace = true;
4159 model->brushq3.deluxemapping = true;
4160 model->brushq3.num_mergedlightmaps = lightmapnumber;
4161 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4162 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4163 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4164 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4165 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4167 surface = model->data_surfaces + surfaceindex;
4168 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4169 for (i = 0;i < surface->num_triangles;i++)
4171 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4172 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4173 VectorNormalize(trianglenormal);
4174 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4175 axis = triangle->axis;
4176 axis1 = axis == 0 ? 1 : 0;
4177 axis2 = axis == 2 ? 1 : 2;
4178 lmiscale[0] = 1.0f / triangle->lmscale[0];
4179 lmiscale[1] = 1.0f / triangle->lmscale[1];
4180 if (trianglenormal[axis] < 0)
4181 VectorNegate(trianglenormal, trianglenormal);
4182 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4183 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4184 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4185 for (j = 0;j < 3;j++)
4187 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4188 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4189 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4191 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4192 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4193 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4194 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]);
4204 forward[1] = 1.0f / triangle->lmscale[0];
4208 left[2] = 1.0f / triangle->lmscale[1];
4213 origin[1] = triangle->lmbase[0];
4214 origin[2] = triangle->lmbase[1];
4217 forward[0] = 1.0f / triangle->lmscale[0];
4222 left[2] = 1.0f / triangle->lmscale[1];
4226 origin[0] = triangle->lmbase[0];
4228 origin[2] = triangle->lmbase[1];
4231 forward[0] = 1.0f / triangle->lmscale[0];
4235 left[1] = 1.0f / triangle->lmscale[1];
4240 origin[0] = triangle->lmbase[0];
4241 origin[1] = triangle->lmbase[1];
4245 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4247 #define LM_DIST_EPSILON (1.0f / 32.0f)
4248 for (y = 0;y < triangle->lmsize[1];y++)
4250 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4251 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4253 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4254 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4255 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4256 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4257 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4263 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4265 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);
4266 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);
4270 Mem_Free(lightmappixels);
4271 if (deluxemappixels)
4272 Mem_Free(deluxemappixels);
4274 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4276 surface = model->data_surfaces + surfaceindex;
4277 if (!surface->num_triangles)
4279 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4280 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4281 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4282 surface->lightmapinfo = NULL;
4285 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4286 model->brushq1.lightdata = NULL;
4287 model->brushq1.lightmapupdateflags = NULL;
4288 model->brushq1.firstrender = false;
4289 model->brushq1.num_lightstyles = 0;
4290 model->brushq1.data_lightstyleinfo = NULL;
4291 for (i = 0;i < model->brush.numsubmodels;i++)
4293 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4294 model->brush.submodels[i]->brushq1.firstrender = false;
4295 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4296 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4300 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4303 for (i = 0;i < model->surfmesh.num_vertices;i++)
4304 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4307 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4314 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4316 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4317 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4319 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4320 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4322 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4323 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4329 extern cvar_t mod_q3bsp_nolightmaps;
4330 static void Mod_GenerateLightmaps(dp_model_t *model)
4332 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4333 dp_model_t *oldloadmodel = loadmodel;
4336 Mod_GenerateLightmaps_InitSampleOffsets(model);
4337 Mod_GenerateLightmaps_DestroyLightmaps(model);
4338 Mod_GenerateLightmaps_UnweldTriangles(model);
4339 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4340 Mod_GenerateLightmaps_CreateLights(model);
4341 if(!mod_q3bsp_nolightmaps.integer)
4342 Mod_GenerateLightmaps_CreateLightmaps(model);
4343 Mod_GenerateLightmaps_UpdateVertexColors(model);
4344 Mod_GenerateLightmaps_UpdateLightGrid(model);
4345 Mod_GenerateLightmaps_DestroyLights(model);
4346 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4348 loadmodel = oldloadmodel;
4351 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4353 if (Cmd_Argc(cmd) != 1)
4355 Con_Printf("usage: mod_generatelightmaps\n");
4360 Con_Printf("no worldmodel loaded\n");
4363 Mod_GenerateLightmaps(cl.worldmodel);
4366 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4368 memset(mod, 0, sizeof(*mod));
4369 strlcpy(mod->name, name, sizeof(mod->name));
4370 mod->mempool = Mem_AllocPool(name, 0, NULL);
4371 mod->texturepool = R_AllocTexturePool();
4372 mod->Draw = R_Q1BSP_Draw;
4373 mod->DrawDepth = R_Q1BSP_DrawDepth;
4374 mod->DrawDebug = R_Q1BSP_DrawDebug;
4375 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4376 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4377 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4378 mod->DrawLight = R_Q1BSP_DrawLight;
4381 void Mod_Mesh_Destroy(dp_model_t *mod)
4383 Mod_UnloadModel(mod);
4386 // resets the mesh model to have no geometry to render, ready for a new frame -
4387 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4388 void Mod_Mesh_Reset(dp_model_t *mod)
4390 mod->num_surfaces = 0;
4391 mod->surfmesh.num_vertices = 0;
4392 mod->surfmesh.num_triangles = 0;
4393 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4394 mod->DrawSky = NULL; // will be set if a texture needs it
4395 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4398 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4402 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4403 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4404 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4406 if (mod->max_textures <= mod->num_textures)
4408 texture_t *oldtextures = mod->data_textures;
4409 mod->max_textures = max(mod->max_textures * 2, 1024);
4410 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4411 // update the pointers
4412 for (i = 0; i < mod->num_surfaces; i++)
4413 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4415 t = &mod->data_textures[mod->num_textures++];
4416 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
4417 t->mesh_drawflag = drawflag;
4418 t->mesh_defaulttexflags = defaulttexflags;
4419 t->mesh_defaultmaterialflags = defaultmaterialflags;
4420 switch (defaultdrawflags & DRAWFLAG_MASK)
4422 case DRAWFLAG_ADDITIVE:
4423 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4424 t->currentmaterialflags = t->basematerialflags;
4426 case DRAWFLAG_MODULATE:
4427 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4428 t->currentmaterialflags = t->basematerialflags;
4429 t->customblendfunc[0] = GL_DST_COLOR;
4430 t->customblendfunc[1] = GL_ZERO;
4432 case DRAWFLAG_2XMODULATE:
4433 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4434 t->currentmaterialflags = t->basematerialflags;
4435 t->customblendfunc[0] = GL_DST_COLOR;
4436 t->customblendfunc[1] = GL_SRC_COLOR;
4438 case DRAWFLAG_SCREEN:
4439 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4440 t->currentmaterialflags = t->basematerialflags;
4441 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4442 t->customblendfunc[1] = GL_ONE;
4450 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4453 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4454 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4455 return mod->data_surfaces + mod->num_surfaces - 1;
4456 // create new surface
4457 if (mod->max_surfaces == mod->num_surfaces)
4459 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4460 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4461 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4463 surf = mod->data_surfaces + mod->num_surfaces;
4464 mod->num_surfaces++;
4465 memset(surf, 0, sizeof(*surf));
4466 surf->texture = tex;
4467 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4468 surf->num_firstvertex = mod->surfmesh.num_vertices;
4469 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4470 mod->DrawSky = R_Q1BSP_DrawSky;
4471 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4472 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4476 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)
4478 int hashindex, h, vnum, mask;
4479 surfmesh_t *mesh = &mod->surfmesh;
4480 if (mesh->max_vertices == mesh->num_vertices)
4482 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4483 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4484 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4485 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4486 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4487 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4488 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4489 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4490 // rebuild the hash table
4491 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4492 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4493 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4494 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4495 mask = mod->surfmesh.num_vertexhashsize - 1;
4496 // no need to hash the vertices for the entire model, the latest surface will suffice.
4497 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4499 // this uses prime numbers intentionally for computing the hash
4500 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;
4501 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4502 ; // just iterate until we find the terminator
4503 mesh->data_vertexhash[h] = vnum;
4506 mask = mod->surfmesh.num_vertexhashsize - 1;
4507 // this uses prime numbers intentionally for computing the hash
4508 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4509 // when possible find an identical vertex within the same surface and return it
4510 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4512 if (vnum >= surf->num_firstvertex
4513 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4514 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4515 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4516 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4517 && 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)
4520 // add the new vertex
4521 vnum = mesh->num_vertices++;
4522 if (surf->num_vertices > 0)
4524 if (surf->mins[0] > x) surf->mins[0] = x;
4525 if (surf->mins[1] > y) surf->mins[1] = y;
4526 if (surf->mins[2] > z) surf->mins[2] = z;
4527 if (surf->maxs[0] < x) surf->maxs[0] = x;
4528 if (surf->maxs[1] < y) surf->maxs[1] = y;
4529 if (surf->maxs[2] < z) surf->maxs[2] = z;
4533 VectorSet(surf->mins, x, y, z);
4534 VectorSet(surf->maxs, x, y, z);
4536 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4537 mesh->data_vertexhash[h] = vnum;
4538 mesh->data_vertex3f[vnum * 3 + 0] = x;
4539 mesh->data_vertex3f[vnum * 3 + 1] = y;
4540 mesh->data_vertex3f[vnum * 3 + 2] = z;
4541 mesh->data_normal3f[vnum * 3 + 0] = nx;
4542 mesh->data_normal3f[vnum * 3 + 1] = ny;
4543 mesh->data_normal3f[vnum * 3 + 2] = nz;
4544 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4545 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4546 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4547 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4548 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4549 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4550 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4551 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4555 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4557 surfmesh_t *mesh = &mod->surfmesh;
4558 if (mesh->max_triangles == mesh->num_triangles)
4560 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4561 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4562 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4564 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4565 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4566 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4567 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4568 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4569 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4570 mesh->num_triangles++;
4571 surf->num_triangles++;
4574 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4578 msurface_t *surf, *surf2;
4580 // build the sorted surfaces list properly to reduce material setup
4581 // this is easy because we're just sorting on texture and don't care about the order of textures
4582 mod->nummodelsurfaces = 0;
4583 for (i = 0; i < mod->num_surfaces; i++)
4584 mod->data_surfaces[i].included = false;
4585 for (i = 0; i < mod->num_surfaces; i++)
4587 surf = mod->data_surfaces + i;
4590 tex = surf->texture;
4591 // j = i is intentional
4592 for (j = i; j < mod->num_surfaces; j++)
4594 surf2 = mod->data_surfaces + j;
4595 if (surf2->included)
4597 if (surf2->texture == tex)
4599 surf2->included = true;
4600 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4606 void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4609 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4611 if (mod->surfmesh.num_vertices > 0)
4613 // calculate normalmins/normalmaxs
4614 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4615 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4616 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4618 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4619 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4620 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4621 // expand bounds to include this vertex
4622 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4623 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4624 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4625 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4626 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4627 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4629 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4630 // (fast but less accurate than doing it per vertex)
4631 x2a = mod->normalmins[0] * mod->normalmins[0];
4632 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4633 y2a = mod->normalmins[1] * mod->normalmins[1];
4634 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4635 z2a = mod->normalmins[2] * mod->normalmins[2];
4636 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4640 yawradius = sqrt(x2 + y2);
4641 rotatedradius = sqrt(x2 + y2 + z2);
4642 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4643 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4644 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4645 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4646 mod->radius = rotatedradius;
4647 mod->radius2 = x2 + y2 + z2;
4651 VectorClear(mod->normalmins);
4652 VectorClear(mod->normalmaxs);
4653 VectorClear(mod->yawmins);
4654 VectorClear(mod->yawmaxs);
4655 VectorClear(mod->rotatedmins);
4656 VectorClear(mod->rotatedmaxs);
4662 void Mod_Mesh_Validate(dp_model_t *mod)
4665 qboolean warned = false;
4666 for (i = 0; i < mod->num_surfaces; i++)
4668 msurface_t *surf = mod->data_surfaces + i;
4669 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4670 int first = surf->num_firstvertex;
4671 int end = surf->num_firstvertex + surf->num_vertices;
4673 for (j = 0;j < surf->num_triangles * 3;j++)
4675 if (e[j] < first || e[j] >= end)
4678 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4686 void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
4688 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;
4689 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;
4690 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;
4691 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;
4692 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;
4693 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;
4694 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;
4695 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;
4696 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;
4697 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;
4698 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;
4701 void Mod_Mesh_Finalize(dp_model_t *mod)
4703 if (gl_paranoid.integer)
4704 Mod_Mesh_Validate(mod);
4705 Mod_Mesh_ComputeBounds(mod);
4706 Mod_Mesh_MakeSortedSurfaces(mod);
4707 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);
4708 Mod_Mesh_UploadDynamicBuffers(mod);