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->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2560 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2562 if (developer_extra.integer)
2563 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2564 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2565 texture->supercontents = SUPERCONTENTS_SOLID;
2569 if (developer_extra.integer)
2570 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2571 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2573 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2574 texture->supercontents = SUPERCONTENTS_SOLID;
2576 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2578 texture->basematerialflags = MATERIALFLAG_SKY;
2579 texture->supercontents = SUPERCONTENTS_SKY;
2583 texture->basematerialflags = defaultmaterialflags;
2584 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2586 if(cls.state == ca_dedicated)
2588 texture->materialshaderpass = NULL;
2593 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2596 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2597 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2598 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2599 if (texture->q2contents)
2600 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2604 if (!success && warnmissing)
2605 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2608 // init the animation variables
2609 texture->currentframe = texture;
2610 texture->currentmaterialflags = texture->basematerialflags;
2611 if (!texture->materialshaderpass)
2612 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2613 if (!texture->materialshaderpass->skinframes[0])
2614 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2615 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2616 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2620 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2622 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2623 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2625 strlcpy(texture->name, name, sizeof(texture->name));
2626 texture->basealpha = 1.0f;
2627 texture->basematerialflags = materialflags;
2628 texture->supercontents = supercontents;
2630 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2631 texture->offsetscale = 1;
2632 texture->offsetbias = 0;
2633 texture->specularscalemod = 1;
2634 texture->specularpowermod = 1;
2635 texture->rtlightambient = 0;
2636 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2637 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2638 // JUST GREP FOR "specularscalemod = 1".
2640 if (developer_extra.integer)
2641 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2643 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2645 // init the animation variables
2646 texture->currentmaterialflags = texture->basematerialflags;
2647 texture->currentframe = texture;
2648 texture->currentskinframe = skinframe;
2649 texture->backgroundcurrentskinframe = NULL;
2652 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2655 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2657 if (texture->shaderpasses[i])
2660 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2661 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2662 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2663 Mem_Free(texture->shaderpasses[i]);
2664 texture->shaderpasses[i] = NULL;
2667 texture->materialshaderpass = NULL;
2668 texture->currentskinframe = NULL;
2669 texture->backgroundcurrentskinframe = NULL;
2672 skinfile_t *Mod_LoadSkinFiles(void)
2674 int i, words, line, wordsoverflow;
2677 skinfile_t *skinfile = NULL, *first = NULL;
2678 skinfileitem_t *skinfileitem;
2679 char word[10][MAX_QPATH];
2684 U_bodyBox,models/players/Legoman/BikerA2.tga
2685 U_RArm,models/players/Legoman/BikerA1.tga
2686 U_LArm,models/players/Legoman/BikerA1.tga
2687 U_armor,common/nodraw
2688 U_sword,common/nodraw
2689 U_shield,common/nodraw
2690 U_homb,common/nodraw
2691 U_backpack,common/nodraw
2692 U_colcha,common/nodraw
2697 memset(word, 0, sizeof(word));
2698 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2700 // If it's the first file we parse
2701 if (skinfile == NULL)
2703 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2708 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2709 skinfile = skinfile->next;
2711 skinfile->next = NULL;
2713 for(line = 0;;line++)
2716 if (!COM_ParseToken_QuakeC(&data, true))
2718 if (!strcmp(com_token, "\n"))
2721 wordsoverflow = false;
2725 strlcpy(word[words++], com_token, sizeof (word[0]));
2727 wordsoverflow = true;
2729 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2732 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);
2735 // words is always >= 1
2736 if (!strcmp(word[0], "replace"))
2740 if (developer_loading.integer)
2741 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2742 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2743 skinfileitem->next = skinfile->items;
2744 skinfile->items = skinfileitem;
2745 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2746 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2749 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]);
2751 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2753 // tag name, like "tag_weapon,"
2754 // not used for anything (not even in Quake3)
2756 else if (words >= 2 && !strcmp(word[1], ","))
2758 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2759 if (developer_loading.integer)
2760 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2761 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2762 skinfileitem->next = skinfile->items;
2763 skinfile->items = skinfileitem;
2764 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2765 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2768 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);
2773 loadmodel->numskins = i;
2777 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2780 skinfileitem_t *skinfileitem, *nextitem;
2781 for (;skinfile;skinfile = next)
2783 next = skinfile->next;
2784 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2786 nextitem = skinfileitem->next;
2787 Mem_Free(skinfileitem);
2793 int Mod_CountSkinFiles(skinfile_t *skinfile)
2796 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2800 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2803 double isnap = 1.0 / snap;
2804 for (i = 0;i < numvertices*numcomponents;i++)
2805 vertices[i] = floor(vertices[i]*isnap)*snap;
2808 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2810 int i, outtriangles;
2811 float edgedir1[3], edgedir2[3], temp[3];
2812 // a degenerate triangle is one with no width (thickness, surface area)
2813 // these are characterized by having all 3 points colinear (along a line)
2814 // or having two points identical
2815 // the simplest check is to calculate the triangle's area
2816 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2818 // calculate first edge
2819 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2820 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2821 CrossProduct(edgedir1, edgedir2, temp);
2822 if (VectorLength2(temp) < 0.001f)
2823 continue; // degenerate triangle (no area)
2824 // valid triangle (has area)
2825 VectorCopy(inelement3i, outelement3i);
2829 return outtriangles;
2832 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2835 int firstvertex, lastvertex;
2836 if (numelements > 0 && elements)
2838 firstvertex = lastvertex = elements[0];
2839 for (i = 1;i < numelements;i++)
2842 firstvertex = min(firstvertex, e);
2843 lastvertex = max(lastvertex, e);
2847 firstvertex = lastvertex = 0;
2848 if (firstvertexpointer)
2849 *firstvertexpointer = firstvertex;
2850 if (lastvertexpointer)
2851 *lastvertexpointer = lastvertex;
2854 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2856 // make an optimal set of texture-sorted batches to draw...
2858 int *firstsurfacefortexture;
2859 int *numsurfacesfortexture;
2860 if (!mod->sortedmodelsurfaces)
2861 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2862 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2863 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2864 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2865 for (j = 0;j < mod->nummodelsurfaces;j++)
2867 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2868 t = (int)(surface->texture - mod->data_textures);
2869 numsurfacesfortexture[t]++;
2872 for (t = 0;t < mod->num_textures;t++)
2874 firstsurfacefortexture[t] = j;
2875 j += numsurfacesfortexture[t];
2877 for (j = 0;j < mod->nummodelsurfaces;j++)
2879 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2880 t = (int)(surface->texture - mod->data_textures);
2881 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2883 Mem_Free(firstsurfacefortexture);
2884 Mem_Free(numsurfacesfortexture);
2887 void Mod_BuildVBOs(void)
2889 if(cls.state == ca_dedicated)
2892 if (!loadmodel->surfmesh.num_vertices)
2895 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2898 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2900 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2902 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2903 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2908 // upload short indices as a buffer
2909 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2910 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);
2912 // upload int indices as a buffer
2913 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2914 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);
2916 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2917 // we put several vertex data streams in the same buffer
2918 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2923 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2924 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2925 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2926 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2927 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2928 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2929 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2930 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2931 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2932 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2933 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2934 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2935 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2936 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2937 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2938 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2939 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2940 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2941 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2942 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2943 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2944 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2945 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2946 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2947 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2948 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2949 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2950 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2955 extern cvar_t mod_obj_orientation;
2956 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2958 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2960 const char *texname;
2962 const float *v, *vn, *vt;
2964 size_t outbufferpos = 0;
2965 size_t outbuffermax = 0x100000;
2966 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
2967 const msurface_t *surface;
2968 const int maxtextures = 256;
2969 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
2970 dp_model_t *submodel;
2972 // construct the mtllib file
2973 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
2976 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
2979 countvertices += surface->num_vertices;
2980 countfaces += surface->num_triangles;
2981 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
2982 for (textureindex = 0;textureindex < counttextures;textureindex++)
2983 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
2985 if (textureindex < counttextures)
2986 continue; // already wrote this material entry
2987 if (textureindex >= maxtextures)
2988 continue; // just a precaution
2989 textureindex = counttextures++;
2990 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
2991 if (outbufferpos >= outbuffermax >> 1)
2994 oldbuffer = outbuffer;
2995 outbuffer = (char *) Z_Malloc(outbuffermax);
2996 memcpy(outbuffer, oldbuffer, outbufferpos);
2999 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");
3004 // write the mtllib file
3005 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3007 // construct the obj file
3009 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);
3013 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)
3015 if (outbufferpos >= outbuffermax >> 1)
3018 oldbuffer = outbuffer;
3019 outbuffer = (char *) Z_Malloc(outbuffermax);
3020 memcpy(outbuffer, oldbuffer, outbufferpos);
3023 if(mod_obj_orientation.integer)
3024 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]);
3026 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]);
3031 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3033 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3036 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3037 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3039 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3040 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3043 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3045 if (outbufferpos >= outbuffermax >> 1)
3048 oldbuffer = outbuffer;
3049 outbuffer = (char *) Z_Malloc(outbuffermax);
3050 memcpy(outbuffer, oldbuffer, outbufferpos);
3056 if(mod_obj_orientation.integer)
3057 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);
3059 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);
3066 // write the obj file
3067 FS_WriteFile(filename, outbuffer, outbufferpos);
3071 Z_Free(texturenames);
3074 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3077 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3079 int countnodes = 0, counttriangles = 0, countframes = 0;
3087 size_t outbufferpos = 0;
3088 size_t outbuffermax = 0x100000;
3089 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3090 const msurface_t *surface;
3091 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3094 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3096 if (outbufferpos >= outbuffermax >> 1)
3099 oldbuffer = outbuffer;
3100 outbuffer = (char *) Z_Malloc(outbuffermax);
3101 memcpy(outbuffer, oldbuffer, outbufferpos);
3105 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3109 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3112 for (poseindex = 0;poseindex < numposes;poseindex++)
3115 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3118 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3122 matrix4x4_t posematrix;
3123 if (outbufferpos >= outbuffermax >> 1)
3126 oldbuffer = outbuffer;
3127 outbuffer = (char *) Z_Malloc(outbuffermax);
3128 memcpy(outbuffer, oldbuffer, outbufferpos);
3132 // strangely the smd angles are for a transposed matrix, so we
3133 // have to generate a transposed matrix, then convert that...
3134 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3135 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3136 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3137 if (angles[0] >= 180) angles[0] -= 360;
3138 if (angles[1] >= 180) angles[1] -= 360;
3139 if (angles[2] >= 180) angles[2] -= 360;
3143 float a = DEG2RAD(angles[ROLL]);
3144 float b = DEG2RAD(angles[PITCH]);
3145 float c = DEG2RAD(angles[YAW]);
3146 float cy, sy, cp, sp, cr, sr;
3148 // smd matrix construction, for comparing
3159 test[1][0] = sr*sp*cy+cr*-sy;
3160 test[1][1] = sr*sp*sy+cr*cy;
3162 test[2][0] = (cr*sp*cy+-sr*-sy);
3163 test[2][1] = (cr*sp*sy+-sr*cy);
3165 test[3][0] = pose[9];
3166 test[3][1] = pose[10];
3167 test[3][2] = pose[11];
3170 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]));
3175 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3180 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3183 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3185 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3188 if (outbufferpos >= outbuffermax >> 1)
3191 oldbuffer = outbuffer;
3192 outbuffer = (char *) Z_Malloc(outbuffermax);
3193 memcpy(outbuffer, oldbuffer, outbufferpos);
3196 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3199 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3201 const int index = e[2-cornerindex];
3202 const float *v = model->surfmesh.data_vertex3f + index * 3;
3203 const float *vn = model->surfmesh.data_normal3f + index * 3;
3204 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3205 const int b = model->surfmesh.blends[index];
3206 if (b < model->num_bones)
3207 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]);
3210 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3211 const unsigned char *wi = w->index;
3212 const unsigned char *wf = w->influence;
3213 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);
3214 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);
3215 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);
3216 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]);
3223 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3228 FS_WriteFile(filename, outbuffer, outbufferpos);
3231 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3238 decompiles a model to editable files
3241 static void Mod_Decompile_f(cmd_state_t *cmd)
3243 int i, j, k, l, first, count;
3245 char inname[MAX_QPATH];
3246 char outname[MAX_QPATH];
3247 char mtlname[MAX_QPATH];
3248 char basename[MAX_QPATH];
3249 char animname[MAX_QPATH];
3250 char animname2[MAX_QPATH];
3251 char zymtextbuffer[16384];
3252 char dpmtextbuffer[16384];
3253 char framegroupstextbuffer[16384];
3254 int zymtextsize = 0;
3255 int dpmtextsize = 0;
3256 int framegroupstextsize = 0;
3259 if (Cmd_Argc(cmd) != 2)
3261 Con_Print("usage: modeldecompile <filename>\n");
3265 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3266 FS_StripExtension(inname, basename, sizeof(basename));
3268 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3271 Con_Print("No such model\n");
3274 if (mod->brush.submodel)
3276 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3277 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3278 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3281 if (!mod->surfmesh.num_triangles)
3283 Con_Print("Empty model (or sprite)\n");
3287 // export OBJ if possible (not on sprites)
3288 if (mod->surfmesh.num_triangles)
3290 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3291 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3292 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3295 // export SMD if possible (only for skeletal models)
3296 if (mod->surfmesh.num_triangles && mod->num_bones)
3298 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3299 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3300 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3301 if (l > 0) zymtextsize += l;
3302 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3303 if (l > 0) dpmtextsize += l;
3304 for (i = 0;i < mod->numframes;i = j)
3306 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3307 first = mod->animscenes[i].firstframe;
3308 if (mod->animscenes[i].framecount > 1)
3311 count = mod->animscenes[i].framecount;
3317 // check for additional frames with same name
3318 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3319 if(animname[l] < '0' || animname[l] > '9')
3321 if(k > 0 && animname[k-1] == '_')
3324 count = mod->num_poses - first;
3325 for (j = i + 1;j < mod->numframes;j++)
3327 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3328 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3329 if(animname2[l] < '0' || animname2[l] > '9')
3331 if(k > 0 && animname[k-1] == '_')
3334 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3336 count = mod->animscenes[j].firstframe - first;
3340 // if it's only one frame, use the original frame name
3342 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3345 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3346 Mod_Decompile_SMD(mod, outname, first, count, false);
3347 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3349 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3350 if (l > 0) zymtextsize += l;
3352 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3354 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3355 if (l > 0) dpmtextsize += l;
3357 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3359 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3360 if (l > 0) framegroupstextsize += l;
3364 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3366 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3367 if (framegroupstextsize)
3368 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3372 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3375 memset(state, 0, sizeof(*state));
3376 state->width = width;
3377 state->height = height;
3378 state->currentY = 0;
3379 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3380 for (y = 0;y < state->height;y++)
3382 state->rows[y].currentX = 0;
3383 state->rows[y].rowY = -1;
3387 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3390 state->currentY = 0;
3391 for (y = 0;y < state->height;y++)
3393 state->rows[y].currentX = 0;
3394 state->rows[y].rowY = -1;
3398 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3401 Mem_Free(state->rows);
3402 memset(state, 0, sizeof(*state));
3405 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3407 mod_alloclightmap_row_t *row;
3410 row = state->rows + blockheight;
3411 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3413 if (state->currentY + blockheight <= state->height)
3415 // use the current allocation position
3416 row->rowY = state->currentY;
3418 state->currentY += blockheight;
3422 // find another position
3423 for (y = blockheight;y < state->height;y++)
3425 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3427 row = state->rows + y;
3431 if (y == state->height)
3436 *outx = row->currentX;
3437 row->currentX += blockwidth;
3442 typedef struct lightmapsample_s
3446 float *vertex_color;
3447 unsigned char *lm_bgr;
3448 unsigned char *lm_dir;
3452 typedef struct lightmapvertex_s
3457 float texcoordbase[2];
3458 float texcoordlightmap[2];
3459 float lightcolor[4];
3463 typedef struct lightmaptriangle_s
3471 // 2D modelspace coordinates of min corner
3472 // snapped to lightmap grid but not in grid coordinates
3474 // 2D modelspace to lightmap coordinate scale
3482 typedef struct lightmaplight_s
3493 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3495 #define MAX_LIGHTMAPSAMPLES 64
3496 static int mod_generatelightmaps_numoffsets[3];
3497 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3499 static int mod_generatelightmaps_numlights;
3500 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3502 extern cvar_t r_shadow_lightattenuationdividebias;
3503 extern cvar_t r_shadow_lightattenuationlinearscale;
3505 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3510 float relativepoint[3];
3517 float lightorigin[3];
3521 float lightcolor[3];
3523 for (i = 0;i < 5*3;i++)
3525 for (index = 0;;index++)
3527 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3532 lightradius2 = lightradius * lightradius;
3533 VectorSubtract(lightorigin, pos, relativepoint);
3534 dist2 = VectorLength2(relativepoint);
3535 if (dist2 >= lightradius2)
3537 lightiradius = 1.0f / lightradius;
3538 dist = sqrt(dist2) * lightiradius;
3539 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3540 if (intensity <= 0.0f)
3542 if (model && model->TraceLine)
3544 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3545 if (trace.fraction < 1)
3548 // scale down intensity to add to both ambient and diffuse
3549 //intensity *= 0.5f;
3550 VectorNormalize(relativepoint);
3551 VectorScale(lightcolor, intensity, color);
3552 VectorMA(sample , 0.5f , color, sample );
3553 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3554 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3555 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3556 // calculate a weighted average light direction as well
3557 intensity *= VectorLength(color);
3558 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3560 // calculate the direction we'll use to reduce the sample to a directional light source
3561 VectorCopy(sample + 12, dir);
3562 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3563 VectorNormalize(dir);
3564 // extract the diffuse color along the chosen direction and scale it
3565 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3566 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3567 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3568 // subtract some of diffuse from ambient
3569 VectorMA(sample, -0.333f, diffuse, ambient);
3570 // store the normalized lightdir
3571 VectorCopy(dir, lightdir);
3574 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3578 const msurface_t *surface;
3579 const float *vertex3f = model->surfmesh.data_vertex3f;
3580 const int *element3i = model->surfmesh.data_element3i;
3583 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3585 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3587 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3589 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3591 VectorCopy(vertex3f + 3*e[0], v2[0]);
3592 VectorCopy(vertex3f + 3*e[1], v2[1]);
3593 VectorCopy(vertex3f + 3*e[2], v2[2]);
3594 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3599 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3601 int maxnodes = 1<<14;
3602 svbsp_node_t *nodes;
3607 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3608 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3609 VectorCopy(lightinfo->origin, origin);
3610 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3613 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3614 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3615 if (svbsp.ranoutofnodes)
3618 if (maxnodes > 1<<22)
3624 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3629 if (svbsp.numnodes > 0)
3631 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3632 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3633 lightinfo->svbsp = svbsp;
3638 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3642 lightmaplight_t *lightinfo;
3646 mod_generatelightmaps_numlights = 0;
3647 for (index = 0;;index++)
3649 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3653 mod_generatelightmaps_numlights++;
3655 if (mod_generatelightmaps_numlights > 0)
3657 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3658 lightinfo = mod_generatelightmaps_lightinfo;
3659 for (index = 0;;index++)
3661 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3668 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3670 lightinfo->iradius = 1.0f / lightinfo->radius;
3671 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3672 // TODO: compute svbsp
3673 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3677 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3680 if (mod_generatelightmaps_lightinfo)
3682 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3683 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3684 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3685 Mem_Free(mod_generatelightmaps_lightinfo);
3687 mod_generatelightmaps_lightinfo = NULL;
3688 mod_generatelightmaps_numlights = 0;
3691 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3693 const svbsp_node_t *node;
3694 const svbsp_node_t *nodes = svbsp->nodes;
3699 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3701 return num == -1; // true if empty, false if solid (shadowed)
3704 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3707 float relativepoint[3];
3716 const lightmaplight_t *lightinfo;
3718 for (i = 0;i < 5*3;i++)
3720 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3722 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3723 VectorSubtract(lightinfo->origin, pos, relativepoint);
3724 // don't accept light from behind a surface, it causes bad shading
3725 if (normal && DotProduct(relativepoint, normal) <= 0)
3727 dist2 = VectorLength2(relativepoint);
3728 if (dist2 >= lightinfo->radius2)
3730 dist = sqrt(dist2) * lightinfo->iradius;
3731 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3734 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3738 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3740 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3742 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3745 // for light grid we'd better check visibility of the offset point
3746 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3747 if (trace.fraction < 1)
3748 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3751 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3756 // scale intensity according to how many rays succeeded
3757 // we know one test is valid, half of the rest will fail...
3758 //if (normal && tests > 1)
3759 // intensity *= (tests - 1.0f) / tests;
3760 intensity *= (float)hits / tests;
3762 // scale down intensity to add to both ambient and diffuse
3763 //intensity *= 0.5f;
3764 VectorNormalize(relativepoint);
3765 VectorScale(lightinfo->color, intensity, color);
3766 VectorMA(sample , 0.5f , color, sample );
3767 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3768 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3769 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3770 // calculate a weighted average light direction as well
3771 intensity *= VectorLength(color);
3772 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3776 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3782 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3783 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3784 VectorCopy(sample + 12, dir);
3785 VectorNormalize(dir);
3786 //VectorAdd(dir, normal, dir);
3787 //VectorNormalize(dir);
3788 f = DotProduct(dir, normal);
3789 f = max(0, f) * 255.0f;
3790 VectorScale(sample, f, color);
3791 //VectorCopy(normal, dir);
3792 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3793 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3794 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3795 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3797 lm_dir[0] = (unsigned char)dir[2];
3798 lm_dir[1] = (unsigned char)dir[1];
3799 lm_dir[2] = (unsigned char)dir[0];
3803 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3806 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3807 VectorCopy(sample, vertex_color);
3810 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3816 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3817 // calculate the direction we'll use to reduce the sample to a directional light source
3818 VectorCopy(sample + 12, dir);
3819 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3820 VectorNormalize(dir);
3821 // extract the diffuse color along the chosen direction and scale it
3822 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3823 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3824 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3825 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3826 VectorScale(sample, 127.5f, ambient);
3827 VectorMA(ambient, -0.333f, diffuse, ambient);
3828 // encode to the grid format
3829 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3830 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3831 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3832 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3833 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3834 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3835 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3836 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3837 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));}
3840 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3845 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3846 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3847 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3848 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3849 radius[0] = mod_generatelightmaps_lightmapradius.value;
3850 radius[1] = mod_generatelightmaps_vertexradius.value;
3851 radius[2] = mod_generatelightmaps_gridradius.value;
3852 for (i = 0;i < 3;i++)
3854 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3857 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3862 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3864 msurface_t *surface;
3867 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3869 surface = model->data_surfaces + surfaceindex;
3870 surface->lightmaptexture = NULL;
3871 surface->deluxemaptexture = NULL;
3873 if (model->brushq3.data_lightmaps)
3875 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3876 if (model->brushq3.data_lightmaps[i])
3877 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3878 Mem_Free(model->brushq3.data_lightmaps);
3879 model->brushq3.data_lightmaps = NULL;
3881 if (model->brushq3.data_deluxemaps)
3883 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3884 if (model->brushq3.data_deluxemaps[i])
3885 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3886 Mem_Free(model->brushq3.data_deluxemaps);
3887 model->brushq3.data_deluxemaps = NULL;
3891 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3893 msurface_t *surface;
3899 surfmesh_t oldsurfmesh;
3901 unsigned char *data;
3902 oldsurfmesh = model->surfmesh;
3903 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3904 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3906 size += model->surfmesh.num_vertices * sizeof(float[3]);
3907 size += model->surfmesh.num_vertices * sizeof(float[3]);
3908 size += model->surfmesh.num_vertices * sizeof(float[3]);
3909 size += model->surfmesh.num_vertices * sizeof(float[3]);
3910 size += model->surfmesh.num_vertices * sizeof(float[2]);
3911 size += model->surfmesh.num_vertices * sizeof(float[2]);
3912 size += model->surfmesh.num_vertices * sizeof(float[4]);
3913 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3914 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3915 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3916 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3917 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3918 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3919 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3920 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3921 if (model->surfmesh.num_vertices > 65536)
3922 model->surfmesh.data_element3s = NULL;
3924 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3925 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3926 model->surfmesh.data_element3i_indexbuffer = NULL;
3927 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3928 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3929 model->surfmesh.data_element3s_indexbuffer = NULL;
3930 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3931 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3932 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3933 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3934 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3935 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3936 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3937 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3938 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3939 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3940 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
3942 // convert all triangles to unique vertex data
3944 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3946 surface = model->data_surfaces + surfaceindex;
3947 surface->num_firstvertex = outvertexindex;
3948 surface->num_vertices = surface->num_triangles*3;
3949 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3950 for (i = 0;i < surface->num_triangles*3;i++)
3953 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3954 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3955 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3956 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3957 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3958 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3959 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3960 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3961 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3962 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3963 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3964 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3965 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
3966 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
3967 if (oldsurfmesh.data_texcoordlightmap2f)
3969 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
3970 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
3972 if (oldsurfmesh.data_lightmapcolor4f)
3974 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
3975 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
3976 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
3977 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
3980 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
3981 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
3985 if (model->surfmesh.data_element3s)
3986 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
3987 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
3989 // find and update all submodels to use this new surfmesh data
3990 for (i = 0;i < model->brush.numsubmodels;i++)
3991 model->brush.submodels[i]->surfmesh = model->surfmesh;
3994 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
3996 msurface_t *surface;
4002 lightmaptriangle_t *triangle;
4003 // generate lightmap triangle structs
4004 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4005 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4007 surface = model->data_surfaces + surfaceindex;
4008 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4009 for (i = 0;i < surface->num_triangles;i++)
4011 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4012 triangle->triangleindex = surface->num_firsttriangle+i;
4013 triangle->surfaceindex = surfaceindex;
4014 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4015 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4016 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4017 // calculate bounds of triangle
4018 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4019 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4020 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4021 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4022 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4023 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4024 // pick an axial projection based on the triangle normal
4025 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4027 if (fabs(normal[1]) > fabs(normal[axis]))
4029 if (fabs(normal[2]) > fabs(normal[axis]))
4031 triangle->axis = axis;
4036 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4038 if (mod_generatelightmaps_lightmaptriangles)
4039 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4040 mod_generatelightmaps_lightmaptriangles = NULL;
4043 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4045 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4047 msurface_t *surface;
4061 float trianglenormal[3];
4062 float samplecenter[3];
4063 float samplenormal[3];
4069 float lmscalepixels;
4072 float lm_basescalepixels;
4073 int lm_borderpixels;
4077 lightmaptriangle_t *triangle;
4078 unsigned char *lightmappixels;
4079 unsigned char *deluxemappixels;
4080 mod_alloclightmap_state_t lmstate;
4083 // generate lightmap projection information for all triangles
4084 if (model->texturepool == NULL)
4085 model->texturepool = R_AllocTexturePool();
4086 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4087 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4088 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4089 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4090 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4092 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4094 surface = model->data_surfaces + surfaceindex;
4095 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4096 lmscalepixels = lm_basescalepixels;
4097 for (retry = 0;retry < 30;retry++)
4099 // after a couple failed attempts, degrade quality to make it fit
4101 lmscalepixels *= 0.5f;
4102 for (i = 0;i < surface->num_triangles;i++)
4104 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4105 triangle->lightmapindex = lightmapnumber;
4106 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4107 // pick two planar axes for projection
4108 // lightmap coordinates here are in pixels
4109 // lightmap projections are snapped to pixel grid explicitly, such
4110 // that two neighboring triangles sharing an edge and projection
4111 // axis will have identical sample spacing along their shared edge
4113 for (j = 0;j < 3;j++)
4115 if (j == triangle->axis)
4117 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4118 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4119 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4120 triangle->lmbase[k] = lmmins/lmscalepixels;
4121 triangle->lmscale[k] = lmscalepixels;
4124 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4127 // if all fit in this texture, we're done with this surface
4128 if (i == surface->num_triangles)
4130 // if we haven't maxed out the lightmap size yet, we retry the
4131 // entire surface batch...
4132 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4134 lm_texturesize *= 2;
4137 Mod_AllocLightmap_Free(&lmstate);
4138 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4141 // if we have maxed out the lightmap size, and this triangle does
4142 // not fit in the same texture as the rest of the surface, we have
4143 // to retry the entire surface in a new texture (can only use one)
4144 // with multiple retries, the lightmap quality degrades until it
4145 // fits (or gives up)
4146 if (surfaceindex > 0)
4148 Mod_AllocLightmap_Reset(&lmstate);
4152 Mod_AllocLightmap_Free(&lmstate);
4154 // now put triangles together into lightmap textures, and do not allow
4155 // triangles of a surface to go into different textures (as that would
4156 // require rewriting the surface list)
4157 model->brushq3.deluxemapping_modelspace = true;
4158 model->brushq3.deluxemapping = true;
4159 model->brushq3.num_mergedlightmaps = lightmapnumber;
4160 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4161 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4162 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4163 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4164 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4166 surface = model->data_surfaces + surfaceindex;
4167 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4168 for (i = 0;i < surface->num_triangles;i++)
4170 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4171 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4172 VectorNormalize(trianglenormal);
4173 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4174 axis = triangle->axis;
4175 axis1 = axis == 0 ? 1 : 0;
4176 axis2 = axis == 2 ? 1 : 2;
4177 lmiscale[0] = 1.0f / triangle->lmscale[0];
4178 lmiscale[1] = 1.0f / triangle->lmscale[1];
4179 if (trianglenormal[axis] < 0)
4180 VectorNegate(trianglenormal, trianglenormal);
4181 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4182 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4183 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4184 for (j = 0;j < 3;j++)
4186 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4187 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4188 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4190 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4191 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4192 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4193 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]);
4203 forward[1] = 1.0f / triangle->lmscale[0];
4207 left[2] = 1.0f / triangle->lmscale[1];
4212 origin[1] = triangle->lmbase[0];
4213 origin[2] = triangle->lmbase[1];
4216 forward[0] = 1.0f / triangle->lmscale[0];
4221 left[2] = 1.0f / triangle->lmscale[1];
4225 origin[0] = triangle->lmbase[0];
4227 origin[2] = triangle->lmbase[1];
4230 forward[0] = 1.0f / triangle->lmscale[0];
4234 left[1] = 1.0f / triangle->lmscale[1];
4239 origin[0] = triangle->lmbase[0];
4240 origin[1] = triangle->lmbase[1];
4244 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4246 #define LM_DIST_EPSILON (1.0f / 32.0f)
4247 for (y = 0;y < triangle->lmsize[1];y++)
4249 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4250 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4252 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4253 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4254 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4255 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4256 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4262 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4264 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);
4265 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);
4269 Mem_Free(lightmappixels);
4270 if (deluxemappixels)
4271 Mem_Free(deluxemappixels);
4273 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4275 surface = model->data_surfaces + surfaceindex;
4276 if (!surface->num_triangles)
4278 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4279 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4280 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4281 surface->lightmapinfo = NULL;
4284 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4285 model->brushq1.lightdata = NULL;
4286 model->brushq1.lightmapupdateflags = NULL;
4287 model->brushq1.firstrender = false;
4288 model->brushq1.num_lightstyles = 0;
4289 model->brushq1.data_lightstyleinfo = NULL;
4290 for (i = 0;i < model->brush.numsubmodels;i++)
4292 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4293 model->brush.submodels[i]->brushq1.firstrender = false;
4294 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4295 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4299 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4302 for (i = 0;i < model->surfmesh.num_vertices;i++)
4303 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4306 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4313 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4315 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4316 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4318 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4319 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4321 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4322 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4328 extern cvar_t mod_q3bsp_nolightmaps;
4329 static void Mod_GenerateLightmaps(dp_model_t *model)
4331 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4332 dp_model_t *oldloadmodel = loadmodel;
4335 Mod_GenerateLightmaps_InitSampleOffsets(model);
4336 Mod_GenerateLightmaps_DestroyLightmaps(model);
4337 Mod_GenerateLightmaps_UnweldTriangles(model);
4338 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4339 Mod_GenerateLightmaps_CreateLights(model);
4340 if(!mod_q3bsp_nolightmaps.integer)
4341 Mod_GenerateLightmaps_CreateLightmaps(model);
4342 Mod_GenerateLightmaps_UpdateVertexColors(model);
4343 Mod_GenerateLightmaps_UpdateLightGrid(model);
4344 Mod_GenerateLightmaps_DestroyLights(model);
4345 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4347 loadmodel = oldloadmodel;
4350 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4352 if (Cmd_Argc(cmd) != 1)
4354 Con_Printf("usage: mod_generatelightmaps\n");
4359 Con_Printf("no worldmodel loaded\n");
4362 Mod_GenerateLightmaps(cl.worldmodel);
4365 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4367 memset(mod, 0, sizeof(*mod));
4368 strlcpy(mod->name, name, sizeof(mod->name));
4369 mod->mempool = Mem_AllocPool(name, 0, NULL);
4370 mod->texturepool = R_AllocTexturePool();
4371 mod->Draw = R_Q1BSP_Draw;
4372 mod->DrawDepth = R_Q1BSP_DrawDepth;
4373 mod->DrawDebug = R_Q1BSP_DrawDebug;
4374 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4375 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4376 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4377 mod->DrawLight = R_Q1BSP_DrawLight;
4380 void Mod_Mesh_Destroy(dp_model_t *mod)
4382 Mod_UnloadModel(mod);
4385 // resets the mesh model to have no geometry to render, ready for a new frame -
4386 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4387 void Mod_Mesh_Reset(dp_model_t *mod)
4389 mod->num_surfaces = 0;
4390 mod->surfmesh.num_vertices = 0;
4391 mod->surfmesh.num_triangles = 0;
4392 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4393 mod->DrawSky = NULL; // will be set if a texture needs it
4394 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4397 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4401 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4402 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4403 if (!strcmp(t->name, name) && t->drawflag == drawflag)
4405 if (mod->max_textures <= mod->num_textures)
4407 texture_t *oldtextures = mod->data_textures;
4408 mod->max_textures = max(mod->max_textures * 2, 1024);
4409 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4410 // update the pointers
4411 for (i = 0; i < mod->num_surfaces; i++)
4412 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4414 t = &mod->data_textures[mod->num_textures++];
4415 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags, defaultmaterialflags);
4416 t->drawflag = drawflag;
4417 switch (defaultdrawflags & DRAWFLAG_MASK)
4419 case DRAWFLAG_ADDITIVE:
4420 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4421 t->currentmaterialflags = t->basematerialflags;
4423 case DRAWFLAG_MODULATE:
4424 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4425 t->currentmaterialflags = t->basematerialflags;
4426 t->customblendfunc[0] = GL_DST_COLOR;
4427 t->customblendfunc[1] = GL_ZERO;
4429 case DRAWFLAG_2XMODULATE:
4430 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4431 t->currentmaterialflags = t->basematerialflags;
4432 t->customblendfunc[0] = GL_DST_COLOR;
4433 t->customblendfunc[1] = GL_SRC_COLOR;
4435 case DRAWFLAG_SCREEN:
4436 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4437 t->currentmaterialflags = t->basematerialflags;
4438 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4439 t->customblendfunc[1] = GL_ONE;
4447 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4450 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4451 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4452 return mod->data_surfaces + mod->num_surfaces - 1;
4453 // create new surface
4454 if (mod->max_surfaces == mod->num_surfaces)
4456 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4457 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4458 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4460 surf = mod->data_surfaces + mod->num_surfaces;
4461 mod->num_surfaces++;
4462 memset(surf, 0, sizeof(*surf));
4463 surf->texture = tex;
4464 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4465 surf->num_firstvertex = mod->surfmesh.num_vertices;
4466 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4467 mod->DrawSky = R_Q1BSP_DrawSky;
4468 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4469 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4473 int Mod_Mesh_IndexForVertex(dp_model_t *mod, msurface_t *surf, float x, float y, float z, float nx, float ny, float nz, float s, float t, float u, float v, float r, float g, float b, float a)
4475 int hashindex, h, vnum, mask;
4476 surfmesh_t *mesh = &mod->surfmesh;
4477 if (mesh->max_vertices == mesh->num_vertices)
4479 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4480 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4481 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4482 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4483 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4484 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4485 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4486 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4487 // rebuild the hash table
4488 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4489 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4490 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4491 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4492 mask = mod->surfmesh.num_vertexhashsize - 1;
4493 // no need to hash the vertices for the entire model, the latest surface will suffice.
4494 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4496 // this uses prime numbers intentionally for computing the hash
4497 hashindex = (unsigned int)(mesh->data_vertex3f[vnum * 3 + 0] * 2003 + mesh->data_vertex3f[vnum * 3 + 1] * 4001 + mesh->data_vertex3f[vnum * 3 + 2] * 7919 + mesh->data_normal3f[vnum * 3 + 0] * 4097 + mesh->data_normal3f[vnum * 3 + 1] * 257 + mesh->data_normal3f[vnum * 3 + 2] * 17) & mask;
4498 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4499 ; // just iterate until we find the terminator
4500 mesh->data_vertexhash[h] = vnum;
4503 mask = mod->surfmesh.num_vertexhashsize - 1;
4504 // this uses prime numbers intentionally for computing the hash
4505 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4506 // when possible find an identical vertex within the same surface and return it
4507 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4509 if (vnum >= surf->num_firstvertex
4510 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4511 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4512 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4513 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4514 && mesh->data_lightmapcolor4f[vnum * 4 + 0] == r && mesh->data_lightmapcolor4f[vnum * 4 + 1] == g && mesh->data_lightmapcolor4f[vnum * 4 + 2] == b && mesh->data_lightmapcolor4f[vnum * 4 + 3] == a)
4517 // add the new vertex
4518 vnum = mesh->num_vertices++;
4519 if (surf->num_vertices > 0)
4521 if (surf->mins[0] > x) surf->mins[0] = x;
4522 if (surf->mins[1] > y) surf->mins[1] = y;
4523 if (surf->mins[2] > z) surf->mins[2] = z;
4524 if (surf->maxs[0] < x) surf->maxs[0] = x;
4525 if (surf->maxs[1] < y) surf->maxs[1] = y;
4526 if (surf->maxs[2] < z) surf->maxs[2] = z;
4530 VectorSet(surf->mins, x, y, z);
4531 VectorSet(surf->maxs, x, y, z);
4533 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4534 mesh->data_vertexhash[h] = vnum;
4535 mesh->data_vertex3f[vnum * 3 + 0] = x;
4536 mesh->data_vertex3f[vnum * 3 + 1] = y;
4537 mesh->data_vertex3f[vnum * 3 + 2] = z;
4538 mesh->data_normal3f[vnum * 3 + 0] = nx;
4539 mesh->data_normal3f[vnum * 3 + 1] = ny;
4540 mesh->data_normal3f[vnum * 3 + 2] = nz;
4541 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4542 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4543 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4544 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4545 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4546 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4547 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4548 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4552 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4554 surfmesh_t *mesh = &mod->surfmesh;
4555 if (mesh->max_triangles == mesh->num_triangles)
4557 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4558 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4559 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4561 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4562 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4563 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4564 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4565 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4566 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4567 mesh->num_triangles++;
4568 surf->num_triangles++;
4571 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4575 msurface_t *surf, *surf2;
4577 // build the sorted surfaces list properly to reduce material setup
4578 // this is easy because we're just sorting on texture and don't care about the order of textures
4579 mod->nummodelsurfaces = 0;
4580 for (i = 0; i < mod->num_surfaces; i++)
4581 mod->data_surfaces[i].included = false;
4582 for (i = 0; i < mod->num_surfaces; i++)
4584 surf = mod->data_surfaces + i;
4587 tex = surf->texture;
4588 // j = i is intentional
4589 for (j = i; j < mod->num_surfaces; j++)
4591 surf2 = mod->data_surfaces + j;
4592 if (surf2->included)
4594 if (surf2->texture == tex)
4596 surf2->included = true;
4597 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4603 void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4606 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4608 if (mod->surfmesh.num_vertices > 0)
4610 // calculate normalmins/normalmaxs
4611 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4612 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4613 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4615 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4616 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4617 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4618 // expand bounds to include this vertex
4619 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4620 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4621 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4622 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4623 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4624 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4626 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4627 // (fast but less accurate than doing it per vertex)
4628 x2a = mod->normalmins[0] * mod->normalmins[0];
4629 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4630 y2a = mod->normalmins[1] * mod->normalmins[1];
4631 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4632 z2a = mod->normalmins[2] * mod->normalmins[2];
4633 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4637 yawradius = sqrt(x2 + y2);
4638 rotatedradius = sqrt(x2 + y2 + z2);
4639 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4640 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4641 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4642 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4643 mod->radius = rotatedradius;
4644 mod->radius2 = x2 + y2 + z2;
4648 VectorClear(mod->normalmins);
4649 VectorClear(mod->normalmaxs);
4650 VectorClear(mod->yawmins);
4651 VectorClear(mod->yawmaxs);
4652 VectorClear(mod->rotatedmins);
4653 VectorClear(mod->rotatedmaxs);
4659 void Mod_Mesh_Validate(dp_model_t *mod)
4662 qboolean warned = false;
4663 for (i = 0; i < mod->num_surfaces; i++)
4665 msurface_t *surf = mod->data_surfaces + i;
4666 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4667 int first = surf->num_firstvertex;
4668 int end = surf->num_firstvertex + surf->num_vertices;
4670 for (j = 0;j < surf->num_triangles * 3;j++)
4672 if (e[j] < first || e[j] >= end)
4675 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4683 void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
4685 mod->surfmesh.data_element3s_indexbuffer = mod->surfmesh.data_element3s ? R_BufferData_Store(mod->surfmesh.num_triangles * sizeof(short[3]), mod->surfmesh.data_element3s, R_BUFFERDATA_INDEX16, &mod->surfmesh.data_element3s_bufferoffset) : NULL;
4686 mod->surfmesh.data_element3i_indexbuffer = mod->surfmesh.data_element3i ? R_BufferData_Store(mod->surfmesh.num_triangles * sizeof(int[3]), mod->surfmesh.data_element3i, R_BUFFERDATA_INDEX32, &mod->surfmesh.data_element3i_bufferoffset) : NULL;
4687 mod->surfmesh.data_vertex3f_vertexbuffer = mod->surfmesh.data_vertex3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_vertex3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_vertex3f_bufferoffset) : NULL;
4688 mod->surfmesh.data_svector3f_vertexbuffer = mod->surfmesh.data_svector3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_svector3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_svector3f_bufferoffset) : NULL;
4689 mod->surfmesh.data_tvector3f_vertexbuffer = mod->surfmesh.data_tvector3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_tvector3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_tvector3f_bufferoffset) : NULL;
4690 mod->surfmesh.data_normal3f_vertexbuffer = mod->surfmesh.data_normal3f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[3]), mod->surfmesh.data_normal3f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_normal3f_bufferoffset) : NULL;
4691 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = mod->surfmesh.data_texcoordtexture2f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[2]), mod->surfmesh.data_texcoordtexture2f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_texcoordtexture2f_bufferoffset) : NULL;
4692 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = mod->surfmesh.data_texcoordlightmap2f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[2]), mod->surfmesh.data_texcoordlightmap2f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_texcoordlightmap2f_bufferoffset) : NULL;
4693 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = mod->surfmesh.data_lightmapcolor4f ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(float[4]), mod->surfmesh.data_lightmapcolor4f, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_lightmapcolor4f_bufferoffset) : NULL;
4694 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = mod->surfmesh.data_skeletalindex4ub ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(unsigned char[4]), mod->surfmesh.data_skeletalindex4ub, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_skeletalindex4ub_bufferoffset) : NULL;
4695 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = mod->surfmesh.data_skeletalweight4ub ? R_BufferData_Store(mod->surfmesh.num_vertices * sizeof(unsigned char[4]), mod->surfmesh.data_skeletalweight4ub, R_BUFFERDATA_VERTEX, &mod->surfmesh.data_skeletalweight4ub_bufferoffset) : NULL;
4698 void Mod_Mesh_Finalize(dp_model_t *mod)
4700 if (gl_paranoid.integer)
4701 Mod_Mesh_Validate(mod);
4702 Mod_Mesh_ComputeBounds(mod);
4703 Mod_Mesh_MakeSortedSurfaces(mod);
4704 Mod_BuildTextureVectorsFromNormals(0, mod->surfmesh.num_vertices, mod->surfmesh.num_triangles, mod->surfmesh.data_vertex3f, mod->surfmesh.data_texcoordtexture2f, mod->surfmesh.data_normal3f, mod->surfmesh.data_element3i, mod->surfmesh.data_svector3f, mod->surfmesh.data_tvector3f, true);
4705 Mod_Mesh_UploadDynamicBuffers(mod);