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);
155 void Mod_Init_Commands (void)
157 Cvar_RegisterVariable(&r_mipskins);
158 Cvar_RegisterVariable(&r_mipnormalmaps);
159 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
160 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
161 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
163 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
164 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
165 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
166 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
167 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
168 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
170 Cmd_AddCommand(CMD_CLIENT, "modellist", Mod_Print_f, "prints a list of loaded models");
171 Cmd_AddCommand(CMD_CLIENT, "modelprecache", Mod_Precache_f, "load a model");
172 Cmd_AddCommand(CMD_CLIENT, "modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
173 Cmd_AddCommand(CMD_CLIENT, "mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
178 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
179 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(dp_model_t), 16);
186 void Mod_RenderInit(void)
188 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
191 void Mod_UnloadModel (dp_model_t *mod)
193 char name[MAX_QPATH];
195 dp_model_t *parentmodel;
197 if (developer_loading.integer)
198 Con_Printf("unloading model %s\n", mod->name);
200 strlcpy(name, mod->name, sizeof(name));
201 parentmodel = mod->brush.parentmodel;
205 if (mod->surfmesh.data_element3i_indexbuffer && !mod->surfmesh.data_element3i_indexbuffer->isdynamic)
206 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
207 mod->surfmesh.data_element3i_indexbuffer = NULL;
208 if (mod->surfmesh.data_element3s_indexbuffer && !mod->surfmesh.data_element3s_indexbuffer->isdynamic)
209 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
210 mod->surfmesh.data_element3s_indexbuffer = NULL;
211 if (mod->surfmesh.data_vertex3f_vertexbuffer && !mod->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
212 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_vertex3f_vertexbuffer);
213 mod->surfmesh.data_vertex3f_vertexbuffer = NULL;
214 mod->surfmesh.data_svector3f_vertexbuffer = NULL;
215 mod->surfmesh.data_tvector3f_vertexbuffer = NULL;
216 mod->surfmesh.data_normal3f_vertexbuffer = NULL;
217 mod->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
218 mod->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
219 mod->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
220 mod->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
221 mod->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
223 // free textures/memory attached to the model
224 R_FreeTexturePool(&mod->texturepool);
225 Mem_FreePool(&mod->mempool);
226 // clear the struct to make it available
227 memset(mod, 0, sizeof(dp_model_t));
228 // restore the fields we want to preserve
229 strlcpy(mod->name, name, sizeof(mod->name));
230 mod->brush.parentmodel = parentmodel;
235 static void R_Model_Null_Draw(entity_render_t *ent)
241 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass);
243 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
258 // REQUIRED: fetch start
259 COM_ParseToken_Simple(&bufptr, true, false, true);
261 break; // end of file
262 if (!strcmp(com_token, "\n"))
263 continue; // empty line
264 start = atoi(com_token);
266 // REQUIRED: fetch length
267 COM_ParseToken_Simple(&bufptr, true, false, true);
268 if (!bufptr || !strcmp(com_token, "\n"))
270 Con_Printf("framegroups file: missing number of frames\n");
273 len = atoi(com_token);
275 // OPTIONAL args start
276 COM_ParseToken_Simple(&bufptr, true, false, true);
278 // OPTIONAL: fetch fps
280 if (bufptr && strcmp(com_token, "\n"))
282 fps = atof(com_token);
283 COM_ParseToken_Simple(&bufptr, true, false, true);
286 // OPTIONAL: fetch loopflag
288 if (bufptr && strcmp(com_token, "\n"))
290 loop = (atoi(com_token) != 0);
291 COM_ParseToken_Simple(&bufptr, true, false, true);
294 // OPTIONAL: fetch name
296 if (bufptr && strcmp(com_token, "\n"))
298 strlcpy(name, com_token, sizeof(name));
299 COM_ParseToken_Simple(&bufptr, true, false, true);
302 // OPTIONAL: remaining unsupported tokens (eat them)
303 while (bufptr && strcmp(com_token, "\n"))
304 COM_ParseToken_Simple(&bufptr, true, false, true);
306 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
309 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
316 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass)
318 dp_model_t *mod = (dp_model_t *) pass;
319 animscene_t *anim = &mod->animscenes[i];
321 strlcpy(anim->name, name, sizeof(anim[i].name));
323 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
324 anim->firstframe = bound(0, start, mod->num_poses - 1);
325 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
326 anim->framerate = max(1, fps);
328 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
331 static void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
336 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
339 Con_Printf("no scene found in framegroups file, aborting\n");
342 mod->numframes = cnt;
345 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
346 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
349 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
352 static void Mod_FindPotentialDeforms(dp_model_t *mod)
356 mod->wantnormals = false;
357 mod->wanttangents = false;
358 for (i = 0;i < mod->num_textures;i++)
360 texture = mod->data_textures + i;
361 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
362 mod->wantnormals = true;
363 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
364 mod->wantnormals = true;
365 for (j = 0;j < Q3MAXDEFORMS;j++)
367 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
369 mod->wanttangents = true;
370 mod->wantnormals = true;
373 if (texture->deforms[j].deform != Q3DEFORM_NONE)
374 mod->wantnormals = true;
386 dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
391 fs_offset_t filesize = 0;
396 if (mod->name[0] == '*') // submodel
399 if (!strcmp(mod->name, "null"))
404 if (mod->loaded || mod->mempool)
405 Mod_UnloadModel(mod);
407 if (developer_loading.integer)
408 Con_Printf("loading model %s\n", mod->name);
411 mod->crc = (unsigned int)-1;
414 VectorClear(mod->normalmins);
415 VectorClear(mod->normalmaxs);
416 VectorClear(mod->yawmins);
417 VectorClear(mod->yawmaxs);
418 VectorClear(mod->rotatedmins);
419 VectorClear(mod->rotatedmaxs);
421 mod->modeldatatypestring = "null";
422 mod->type = mod_null;
423 mod->Draw = R_Model_Null_Draw;
427 // no fatal errors occurred, so this model is ready to use.
436 // even if the model is loaded it still may need reloading...
438 // if it is not loaded or checkdisk is true we need to calculate the crc
439 if (!mod->loaded || checkdisk)
441 if (checkdisk && mod->loaded)
442 Con_DPrintf("checking model %s\n", mod->name);
443 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
446 crc = CRC_Block((unsigned char *)buf, filesize);
447 // we need to reload the model if the crc does not match
453 // if the model is already loaded and checks passed, just return
461 if (developer_loading.integer)
462 Con_Printf("loading model %s\n", mod->name);
464 SCR_PushLoadingScreen(mod->name, 1);
466 // LadyHavoc: unload the existing model in this slot (if there is one)
467 if (mod->loaded || mod->mempool)
468 Mod_UnloadModel(mod);
473 // errors can prevent the corresponding mod->loaded = true;
476 // default lightmap scale
477 mod->lightmapscale = 1;
479 // default model radius and bounding box (mainly for missing models)
481 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
482 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
483 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
484 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
485 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
486 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
490 // load q3 shaders for the first time, or after a level change
496 char *bufend = (char *)buf + filesize;
498 // all models use memory, so allocate a memory pool
499 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
501 num = LittleLong(*((int *)buf));
502 // call the apropriate loader
504 if (!strcasecmp(FS_FileExtension(mod->name), "obj")) Mod_OBJ_Load(mod, buf, bufend);
505 else if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
506 else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend);
507 else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend);
508 else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend);
509 else if (!memcmp(buf, "IDS2", 4)) Mod_IDS2_Load(mod, buf, bufend);
510 else if (!memcmp(buf, "IBSP", 4)) Mod_IBSP_Load(mod, buf, bufend);
511 else if (!memcmp(buf, "ZYMOTICMODEL", 12)) Mod_ZYMOTICMODEL_Load(mod, buf, bufend);
512 else if (!memcmp(buf, "DARKPLACESMODEL", 16)) Mod_DARKPLACESMODEL_Load(mod, buf, bufend);
513 else if (!memcmp(buf, "ACTRHEAD", 8)) Mod_PSKMODEL_Load(mod, buf, bufend);
514 else if (!memcmp(buf, "INTERQUAKEMODEL", 16)) Mod_INTERQUAKEMODEL_Load(mod, buf, bufend);
515 else if (strlen(mod->name) >= 4 && !strcmp(mod->name + strlen(mod->name) - 4, ".map")) Mod_MAP_Load(mod, buf, bufend);
516 else if (num == BSPVERSION || num == 30 || !memcmp(buf, "BSP2", 4) || !memcmp(buf, "2PSB", 4)) Mod_Q1BSP_Load(mod, buf, bufend);
517 else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
520 Mod_FindPotentialDeforms(mod);
522 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
525 Mod_FrameGroupify(mod, (const char *)buf);
533 // LadyHavoc: Sys_Error was *ANNOYING*
534 Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
537 // no fatal errors occurred, so this model is ready to use.
540 SCR_PopLoadingScreen(false);
545 void Mod_ClearUsed(void)
548 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
550 for (i = 0;i < nummodels;i++)
551 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
555 void Mod_PurgeUnused(void)
558 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
560 for (i = 0;i < nummodels;i++)
562 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
564 Mod_UnloadModel(mod);
565 Mem_ExpandableArray_FreeRecord(&models, mod);
576 dp_model_t *Mod_FindName(const char *name, const char *parentname)
585 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
588 Host_Error ("Mod_ForName: empty name");
590 // search the currently loaded models
591 for (i = 0;i < nummodels;i++)
593 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !strcmp(mod->name, name) && ((!mod->brush.parentmodel && !parentname[0]) || (mod->brush.parentmodel && parentname[0] && !strcmp(mod->brush.parentmodel->name, parentname))))
600 // no match found, create a new one
601 mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
602 strlcpy(mod->name, name, sizeof(mod->name));
604 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
606 mod->brush.parentmodel = NULL;
616 Loads in a model for the given name
619 dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
622 model = Mod_FindName(name, parentname);
623 if (!model->loaded || checkdisk)
624 Mod_LoadModel(model, crash, checkdisk);
632 Reloads all models if they have changed
635 void Mod_Reload(void)
638 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
641 SCR_PushLoadingScreen("Reloading models", 1.0);
643 for (i = 0;i < nummodels;i++)
644 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
646 for (i = 0;i < nummodels;i++)
647 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
649 SCR_PushLoadingScreen(mod->name, 1.0 / count);
650 Mod_LoadModel(mod, true, true);
651 SCR_PopLoadingScreen(false);
653 SCR_PopLoadingScreen(false);
656 unsigned char *mod_base;
659 //=============================================================================
666 static void Mod_Print_f(cmd_state_t *cmd)
669 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
672 Con_Print("Loaded models:\n");
673 for (i = 0;i < nummodels;i++)
675 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
677 if (mod->brush.numsubmodels)
678 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
680 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
690 static void Mod_Precache_f(cmd_state_t *cmd)
692 if (Cmd_Argc(cmd) == 2)
693 Mod_ForName(Cmd_Argv(cmd, 1), false, true, Cmd_Argv(cmd, 1)[0] == '*' ? cl.model_name[1] : NULL);
695 Con_Print("usage: modelprecache <filename>\n");
698 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
702 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
703 memset(used, 0, numvertices);
704 for (i = 0;i < numelements;i++)
705 used[elements[i]] = 1;
706 for (i = 0, count = 0;i < numvertices;i++)
707 remapvertices[i] = used[i] ? count++ : -1;
712 qboolean Mod_ValidateElements(int *element3i, unsigned short *element3s, int numtriangles, int firstvertex, int numvertices, const char *filename, int fileline)
714 int first = firstvertex, last = first + numvertices - 1, numelements = numtriangles * 3;
716 int invalidintcount = 0, invalidintexample = 0;
717 int invalidshortcount = 0, invalidshortexample = 0;
718 int invalidmismatchcount = 0, invalidmismatchexample = 0;
721 for (i = 0; i < numelements; i++)
723 if (element3i[i] < first || element3i[i] > last)
726 invalidintexample = i;
732 for (i = 0; i < numelements; i++)
734 if (element3s[i] < first || element3s[i] > last)
737 invalidintexample = i;
741 if (element3i && element3s)
743 for (i = 0; i < numelements; i++)
745 if (element3s[i] != element3i[i])
747 invalidmismatchcount++;
748 invalidmismatchexample = i;
752 if (invalidintcount || invalidshortcount || invalidmismatchcount)
754 Con_Printf("Mod_ValidateElements(%i, %i, %i, %p, %p) called at %s:%d", numelements, first, last, element3i, element3s, filename, fileline);
755 Con_Printf(", %i elements are invalid in element3i (example: element3i[%i] == %i)", invalidintcount, invalidintexample, element3i ? element3i[invalidintexample] : 0);
756 Con_Printf(", %i elements are invalid in element3s (example: element3s[%i] == %i)", invalidshortcount, invalidshortexample, element3s ? element3s[invalidshortexample] : 0);
757 Con_Printf(", %i elements mismatch between element3i and element3s (example: element3s[%i] is %i and element3i[%i] is %i)", invalidmismatchcount, invalidmismatchexample, element3s ? element3s[invalidmismatchexample] : 0, invalidmismatchexample, element3i ? element3i[invalidmismatchexample] : 0);
758 Con_Print(". Please debug the engine code - these elements have been modified to not crash, but nothing more.\n");
760 // edit the elements to make them safer, as the driver will crash otherwise
762 for (i = 0; i < numelements; i++)
763 if (element3i[i] < first || element3i[i] > last)
764 element3i[i] = first;
766 for (i = 0; i < numelements; i++)
767 if (element3s[i] < first || element3s[i] > last)
768 element3s[i] = first;
769 if (element3i && element3s)
770 for (i = 0; i < numelements; i++)
771 if (element3s[i] != element3i[i])
772 element3s[i] = element3i[i];
779 // warning: this is an expensive function!
780 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
787 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
788 // process each vertex of each triangle and accumulate the results
789 // use area-averaging, to make triangles with a big area have a bigger
790 // weighting on the vertex normal than triangles with a small area
791 // to do so, just add the 'normals' together (the bigger the area
792 // the greater the length of the normal is
794 for (i = 0; i < numtriangles; i++, element += 3)
797 vertex3f + element[0] * 3,
798 vertex3f + element[1] * 3,
799 vertex3f + element[2] * 3,
804 VectorNormalize(areaNormal);
806 for (j = 0;j < 3;j++)
808 vectorNormal = normal3f + element[j] * 3;
809 vectorNormal[0] += areaNormal[0];
810 vectorNormal[1] += areaNormal[1];
811 vectorNormal[2] += areaNormal[2];
814 // and just normalize the accumulated vertex normal in the end
815 vectorNormal = normal3f + 3 * firstvertex;
816 for (i = 0; i < numvertices; i++, vectorNormal += 3)
817 VectorNormalize(vectorNormal);
821 static void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, const float *tc0, const float *tc1, const float *tc2, float *svector3f, float *tvector3f, float *normal3f)
823 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
824 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
825 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
827 // 6 multiply, 9 subtract
828 VectorSubtract(v1, v0, v10);
829 VectorSubtract(v2, v0, v20);
830 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
831 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
832 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
833 // 12 multiply, 10 subtract
834 tc10[1] = tc1[1] - tc0[1];
835 tc20[1] = tc2[1] - tc0[1];
836 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
837 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
838 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
839 tc10[0] = tc1[0] - tc0[0];
840 tc20[0] = tc2[0] - tc0[0];
841 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
842 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
843 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
844 // 12 multiply, 4 add, 6 subtract
845 f = DotProduct(svector3f, normal3f);
846 svector3f[0] -= f * normal3f[0];
847 svector3f[1] -= f * normal3f[1];
848 svector3f[2] -= f * normal3f[2];
849 f = DotProduct(tvector3f, normal3f);
850 tvector3f[0] -= f * normal3f[0];
851 tvector3f[1] -= f * normal3f[1];
852 tvector3f[2] -= f * normal3f[2];
853 // if texture is mapped the wrong way (counterclockwise), the tangents
854 // have to be flipped, this is detected by calculating a normal from the
855 // two tangents, and seeing if it is opposite the surface normal
856 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
857 CrossProduct(tvector3f, svector3f, tangentcross);
858 if (DotProduct(tangentcross, normal3f) < 0)
860 VectorNegate(svector3f, svector3f);
861 VectorNegate(tvector3f, tvector3f);
866 // warning: this is a very expensive function!
867 void Mod_BuildTextureVectorsFromNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const float *texcoord2f, const float *normal3f, const int *elements, float *svector3f, float *tvector3f, qboolean areaweighting)
870 float sdir[3], tdir[3], normal[3], *svec, *tvec;
871 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
872 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
875 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
876 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
877 // process each vertex of each triangle and accumulate the results
878 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
880 v0 = vertex3f + e[0] * 3;
881 v1 = vertex3f + e[1] * 3;
882 v2 = vertex3f + e[2] * 3;
883 tc0 = texcoord2f + e[0] * 2;
884 tc1 = texcoord2f + e[1] * 2;
885 tc2 = texcoord2f + e[2] * 2;
887 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
888 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
890 // calculate the edge directions and surface normal
891 // 6 multiply, 9 subtract
892 VectorSubtract(v1, v0, v10);
893 VectorSubtract(v2, v0, v20);
894 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
895 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
896 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
898 // calculate the tangents
899 // 12 multiply, 10 subtract
900 tc10[1] = tc1[1] - tc0[1];
901 tc20[1] = tc2[1] - tc0[1];
902 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
903 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
904 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
905 tc10[0] = tc1[0] - tc0[0];
906 tc20[0] = tc2[0] - tc0[0];
907 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
908 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
909 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
911 // if texture is mapped the wrong way (counterclockwise), the tangents
912 // have to be flipped, this is detected by calculating a normal from the
913 // two tangents, and seeing if it is opposite the surface normal
914 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
915 CrossProduct(tdir, sdir, tangentcross);
916 if (DotProduct(tangentcross, normal) < 0)
918 VectorNegate(sdir, sdir);
919 VectorNegate(tdir, tdir);
924 VectorNormalize(sdir);
925 VectorNormalize(tdir);
927 for (i = 0;i < 3;i++)
929 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
930 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
933 // make the tangents completely perpendicular to the surface normal, and
934 // then normalize them
935 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
936 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
938 f = -DotProduct(svec, n);
939 VectorMA(svec, f, n, svec);
940 VectorNormalize(svec);
941 f = -DotProduct(tvec, n);
942 VectorMA(tvec, f, n, tvec);
943 VectorNormalize(tvec);
947 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors)
950 data = (unsigned char *)Mem_Alloc(mempool, numvertices * (3 + 3 + 3 + 3 + 2 + 2 + (vertexcolors ? 4 : 0)) * sizeof(float) + numvertices * (lightmapoffsets ? 1 : 0) * sizeof(int) + numtriangles * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0));
951 loadmodel->surfmesh.num_vertices = numvertices;
952 loadmodel->surfmesh.num_triangles = numtriangles;
953 if (loadmodel->surfmesh.num_vertices)
955 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
956 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
957 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
958 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
959 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
960 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
962 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
964 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
966 if (loadmodel->surfmesh.num_triangles)
968 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
969 if (loadmodel->surfmesh.num_vertices <= 65536)
970 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
974 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles)
976 shadowmesh_t *newmesh;
977 newmesh = (shadowmesh_t *)Mem_Alloc(mempool, sizeof(shadowmesh_t));
978 newmesh->mempool = mempool;
979 newmesh->maxverts = maxverts;
980 newmesh->maxtriangles = maxtriangles;
981 newmesh->numverts = 0;
982 newmesh->numtriangles = 0;
983 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
984 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
986 newmesh->vertex3f = (float *)Mem_Alloc(mempool, maxverts * sizeof(float[3]));
987 newmesh->element3i = (int *)Mem_Alloc(mempool, maxtriangles * sizeof(int[3]));
988 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)Mem_Alloc(mempool, SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *));
989 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)Mem_Alloc(mempool, maxverts * sizeof(shadowmeshvertexhash_t));
993 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, const float *vertex3f)
996 shadowmeshvertexhash_t *hash;
997 // this uses prime numbers intentionally
998 hashindex = (unsigned int) (vertex3f[0] * 2003 + vertex3f[1] * 4001 + vertex3f[2] * 7919) % SHADOWMESHVERTEXHASH;
999 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1001 vnum = (hash - mesh->vertexhashentries);
1002 if (mesh->vertex3f[vnum * 3 + 0] == vertex3f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex3f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex3f[2])
1003 return hash - mesh->vertexhashentries;
1005 vnum = mesh->numverts++;
1006 hash = mesh->vertexhashentries + vnum;
1007 hash->next = mesh->vertexhashtable[hashindex];
1008 mesh->vertexhashtable[hashindex] = hash;
1009 mesh->vertex3f[vnum * 3 + 0] = vertex3f[0];
1010 mesh->vertex3f[vnum * 3 + 1] = vertex3f[1];
1011 mesh->vertex3f[vnum * 3 + 2] = vertex3f[2];
1015 void Mod_ShadowMesh_AddMesh(shadowmesh_t *mesh, const float *vertex3f, int numtris, const int *element3i)
1019 for (i = 0;i < numtris;i++)
1021 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 0]);
1022 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 1]);
1023 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex3f + 3 * element3i[i * 3 + 2]);
1024 mesh->numtriangles++;
1027 // the triangle calculation can take a while, so let's do a keepalive here
1028 CL_KeepaliveMessage(false);
1031 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles)
1033 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1034 CL_KeepaliveMessage(false);
1036 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles);
1039 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
1041 if (!mesh->numverts)
1044 // make sure we don't crash inside the driver if something went wrong, as it's annoying to debug
1045 Mod_ValidateElements(mesh->element3i, mesh->element3s, mesh->numtriangles, 0, mesh->numverts, __FILE__, __LINE__);
1047 // upload short indices as a buffer
1048 if (mesh->element3s && !mesh->element3s_indexbuffer)
1049 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), "shadowmesh", true, false, false, true);
1051 // upload int indices as a buffer
1052 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1053 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh", true, false, false, false);
1055 // vertex buffer is several arrays and we put them in the same buffer
1057 // is this wise? the texcoordtexture2f array is used with dynamic
1058 // vertex/svector/tvector/normal when rendering animated models, on the
1059 // other hand animated models don't use a lot of vertices anyway...
1060 if (!mesh->vbo_vertexbuffer)
1062 mesh->vbooffset_vertex3f = 0;
1063 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mesh->vertex3f, mesh->numverts * sizeof(float[3]), "shadowmesh", false, false, false, false);
1067 shadowmesh_t *Mod_ShadowMesh_Finish(shadowmesh_t *mesh, qboolean createvbo)
1069 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1071 if (mesh->vertexhashentries)
1072 Mem_Free(mesh->vertexhashentries);
1073 mesh->vertexhashentries = NULL;
1074 if (mesh->vertexhashtable)
1075 Mem_Free(mesh->vertexhashtable);
1076 mesh->vertexhashtable = NULL;
1077 if (mesh->maxverts > mesh->numverts)
1079 mesh->vertex3f = (float *)Mem_Realloc(mesh->mempool, mesh->vertex3f, mesh->numverts * sizeof(float[3]));
1080 mesh->maxverts = mesh->numverts;
1082 if (mesh->maxtriangles > mesh->numtriangles)
1084 mesh->element3i = (int *)Mem_Realloc(mesh->mempool, mesh->element3i, mesh->numtriangles * sizeof(int[3]));
1085 mesh->maxtriangles = mesh->numtriangles;
1087 if (mesh->numverts <= 65536)
1090 mesh->element3s = (unsigned short *)Mem_Alloc(mesh->mempool, mesh->numtriangles * sizeof(unsigned short[3]));
1091 for (i = 0;i < mesh->numtriangles*3;i++)
1092 mesh->element3s[i] = mesh->element3i[i];
1095 Mod_ShadowMesh_CreateVBOs(mesh);
1098 // this can take a while, so let's do a keepalive here
1099 CL_KeepaliveMessage(false);
1104 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *mesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1107 vec3_t nmins, nmaxs, ncenter, temp;
1108 float nradius2, dist2, *v;
1112 VectorCopy(mesh->vertex3f, nmins);
1113 VectorCopy(mesh->vertex3f, nmaxs);
1114 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1116 if (nmins[0] > v[0]) { nmins[0] = v[0]; } if (nmaxs[0] < v[0]) { nmaxs[0] = v[0]; }
1117 if (nmins[1] > v[1]) { nmins[1] = v[1]; } if (nmaxs[1] < v[1]) { nmaxs[1] = v[1]; }
1118 if (nmins[2] > v[2]) { nmins[2] = v[2]; } if (nmaxs[2] < v[2]) { nmaxs[2] = v[2]; }
1120 // calculate center and radius
1121 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1122 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1123 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1125 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1127 VectorSubtract(v, ncenter, temp);
1128 dist2 = DotProduct(temp, temp);
1129 if (nradius2 < dist2)
1134 VectorCopy(nmins, mins);
1136 VectorCopy(nmaxs, maxs);
1138 VectorCopy(ncenter, center);
1140 *radius = sqrt(nradius2);
1143 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1145 if (mesh->element3i_indexbuffer)
1146 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1147 if (mesh->element3s_indexbuffer)
1148 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1149 if (mesh->vbo_vertexbuffer)
1150 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1152 Mem_Free(mesh->vertex3f);
1153 if (mesh->element3i)
1154 Mem_Free(mesh->element3i);
1155 if (mesh->element3s)
1156 Mem_Free(mesh->element3s);
1157 if (mesh->vertexhashentries)
1158 Mem_Free(mesh->vertexhashentries);
1159 if (mesh->vertexhashtable)
1160 Mem_Free(mesh->vertexhashtable);
1164 void Mod_CreateCollisionMesh(dp_model_t *mod)
1166 int k, numcollisionmeshtriangles;
1167 qboolean usesinglecollisionmesh = false;
1168 const msurface_t *surface = NULL;
1170 mempool_t *mempool = mod->mempool;
1171 if (!mempool && mod->brush.parentmodel)
1172 mempool = mod->brush.parentmodel->mempool;
1173 // make a single combined collision mesh for physics engine use
1174 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1175 numcollisionmeshtriangles = 0;
1176 for (k = 0;k < mod->nummodelsurfaces;k++)
1178 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1179 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1181 usesinglecollisionmesh = true;
1182 numcollisionmeshtriangles = surface->num_triangles;
1185 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1187 numcollisionmeshtriangles += surface->num_triangles;
1189 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles);
1190 if (usesinglecollisionmesh)
1191 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1194 for (k = 0;k < mod->nummodelsurfaces;k++)
1196 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1197 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1199 Mod_ShadowMesh_AddMesh(mod->brush.collisionmesh, mod->surfmesh.data_vertex3f, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1202 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mod->brush.collisionmesh, false);
1206 static void Mod_GetTerrainVertex3fTexCoord2fFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
1211 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1212 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1215 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1216 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1217 texcoord2f[0] = tc[0];
1218 texcoord2f[1] = tc[1];
1221 static void Mod_GetTerrainVertexFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *svector3f, float *tvector3f, float *normal3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
1223 float vup[3], vdown[3], vleft[3], vright[3];
1224 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1225 float sv[3], tv[3], nl[3];
1226 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1227 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1228 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1229 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1230 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1231 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1232 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1233 VectorAdd(svector3f, sv, svector3f);
1234 VectorAdd(tvector3f, tv, tvector3f);
1235 VectorAdd(normal3f, nl, normal3f);
1236 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1237 VectorAdd(svector3f, sv, svector3f);
1238 VectorAdd(tvector3f, tv, tvector3f);
1239 VectorAdd(normal3f, nl, normal3f);
1240 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1241 VectorAdd(svector3f, sv, svector3f);
1242 VectorAdd(tvector3f, tv, tvector3f);
1243 VectorAdd(normal3f, nl, normal3f);
1246 static void Mod_ConstructTerrainPatchFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int x1, int y1, int width, int height, int *element3i, float *vertex3f, float *svector3f, float *tvector3f, float *normal3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
1248 int x, y, ix, iy, *e;
1250 for (y = 0;y < height;y++)
1252 for (x = 0;x < width;x++)
1254 e[0] = (y + 1) * (width + 1) + (x + 0);
1255 e[1] = (y + 0) * (width + 1) + (x + 0);
1256 e[2] = (y + 1) * (width + 1) + (x + 1);
1257 e[3] = (y + 0) * (width + 1) + (x + 0);
1258 e[4] = (y + 0) * (width + 1) + (x + 1);
1259 e[5] = (y + 1) * (width + 1) + (x + 1);
1263 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1264 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1265 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1270 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1274 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1275 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1276 float viewvector[3];
1277 unsigned int firstvertex;
1280 if (chunkwidth < 2 || chunkheight < 2)
1282 VectorSet(mins, model->terrain.mins[0] + x * stepsize * model->terrain.scale[0], model->terrain.mins[1] + y * stepsize * model->terrain.scale[1], model->terrain.mins[2]);
1283 VectorSet(maxs, model->terrain.mins[0] + (x+1) * stepsize * model->terrain.scale[0], model->terrain.mins[1] + (y+1) * stepsize * model->terrain.scale[1], model->terrain.maxs[2]);
1284 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1285 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1286 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1287 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1289 // too close for this stepsize, emit as 4 chunks instead
1291 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1292 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1293 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1294 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1297 // emit the geometry at stepsize into our vertex buffer / index buffer
1298 // we add two columns and two rows for skirt
1299 outwidth = chunkwidth+2;
1300 outheight = chunkheight+2;
1301 outwidth2 = outwidth-1;
1302 outheight2 = outheight-1;
1303 outwidth3 = outwidth+1;
1304 outheight3 = outheight+1;
1305 firstvertex = numvertices;
1306 e = model->terrain.element3i + numtriangles;
1307 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1308 v = model->terrain.vertex3f + numvertices;
1309 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1310 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1311 for (ty = 0;ty < outheight;ty++)
1313 for (tx = 0;tx < outwidth;tx++)
1315 *e++ = firstvertex + (ty )*outwidth3+(tx );
1316 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1317 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1318 *e++ = firstvertex + (ty )*outwidth3+(tx );
1319 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1320 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1323 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1324 for (ty = 0;ty <= outheight;ty++)
1326 skirtrow = ty == 0 || ty == outheight;
1327 ry = y+bound(1, ty, outheight)*stepsize;
1328 for (tx = 0;tx <= outwidth;tx++)
1330 skirt = skirtrow || tx == 0 || tx == outwidth;
1331 rx = x+bound(1, tx, outwidth)*stepsize;
1334 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1338 // TODO: emit skirt vertices
1341 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1343 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1344 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1345 Mod_Terrain_BuildChunk(model,
1349 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1352 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1354 offset = bound(0, s[4] - '0', 9);
1355 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1360 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1361 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1362 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1363 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1364 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1365 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1366 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1367 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1368 return offset | Q3WAVEFUNC_NONE;
1371 void Mod_FreeQ3Shaders(void)
1373 Mem_FreePool(&q3shaders_mem);
1376 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
1378 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1379 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1380 q3shader_hash_entry_t* lastEntry = NULL;
1383 if (strcasecmp (entry->shader.name, shader->name) == 0)
1386 if(shader->dpshaderkill)
1388 // killed shader is a redeclarion? we can safely ignore it
1391 else if(entry->shader.dpshaderkill)
1393 // replace the old shader!
1394 // this will skip the entry allocating part
1395 // below and just replace the shader
1400 unsigned char *start, *end, *start2;
1401 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1402 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1403 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1404 if(memcmp(start, start2, end - start))
1405 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1407 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1412 entry = entry->chain;
1414 while (entry != NULL);
1417 if (lastEntry->shader.name[0] != 0)
1420 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1421 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1423 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1424 lastEntry->chain = newEntry;
1425 newEntry->chain = NULL;
1426 lastEntry = newEntry;
1428 /* else: head of chain, in hash entry array */
1431 memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
1434 void Mod_LoadQ3Shaders(void)
1441 q3shaderinfo_t shader;
1442 q3shaderinfo_layer_t *layer;
1444 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1445 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1446 unsigned long custsurfaceflags[256];
1447 int numcustsurfaceflags;
1448 qboolean dpshaderkill;
1450 Mod_FreeQ3Shaders();
1452 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1453 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1454 sizeof (q3shader_data_t));
1455 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1456 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1457 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1458 q3shaders_mem, sizeof (char**), 256);
1460 // parse custinfoparms.txt
1461 numcustsurfaceflags = 0;
1462 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1464 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1465 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1468 while (COM_ParseToken_QuakeC(&text, false))
1469 if (!strcasecmp(com_token, "}"))
1471 // custom surfaceflags section
1472 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1473 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1476 while(COM_ParseToken_QuakeC(&text, false))
1478 if (!strcasecmp(com_token, "}"))
1480 // register surfaceflag
1481 if (numcustsurfaceflags >= 256)
1483 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1487 j = (int)strlen(com_token)+1;
1488 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1489 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1491 if (COM_ParseToken_QuakeC(&text, false))
1492 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1494 custsurfaceflags[numcustsurfaceflags] = 0;
1495 numcustsurfaceflags++;
1503 search = FS_Search("scripts/*.shader", true, false);
1506 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1508 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1511 while (COM_ParseToken_QuakeC(&text, false))
1513 memset (&shader, 0, sizeof(shader));
1515 shader.surfaceparms = 0;
1516 shader.surfaceflags = 0;
1517 shader.textureflags = 0;
1518 shader.numlayers = 0;
1519 shader.lighting = false;
1520 shader.vertexalpha = false;
1521 shader.textureblendalpha = false;
1522 shader.skyboxname[0] = 0;
1523 shader.deforms[0].deform = Q3DEFORM_NONE;
1524 shader.dpnortlight = false;
1525 shader.dpshadow = false;
1526 shader.dpnoshadow = false;
1527 shader.dpmeshcollisions = false;
1528 shader.dpshaderkill = false;
1529 shader.dpreflectcube[0] = 0;
1530 shader.reflectmin = 0;
1531 shader.reflectmax = 1;
1532 shader.refractfactor = 1;
1533 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1534 shader.reflectfactor = 1;
1535 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1536 shader.r_water_wateralpha = 1;
1537 shader.r_water_waterscroll[0] = 0;
1538 shader.r_water_waterscroll[1] = 0;
1539 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1540 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1541 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1542 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1543 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1544 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1545 shader.specularscalemod = 1;
1546 shader.specularpowermod = 1;
1547 shader.rtlightambient = 0;
1548 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1549 // JUST GREP FOR "specularscalemod = 1".
1551 strlcpy(shader.name, com_token, sizeof(shader.name));
1552 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1554 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1557 while (COM_ParseToken_QuakeC(&text, false))
1559 if (!strcasecmp(com_token, "}"))
1561 if (!strcasecmp(com_token, "{"))
1563 static q3shaderinfo_layer_t dummy;
1564 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1566 layer = shader.layers + shader.numlayers++;
1570 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1571 memset(&dummy, 0, sizeof(dummy));
1574 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1575 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1576 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1577 layer->blendfunc[0] = GL_ONE;
1578 layer->blendfunc[1] = GL_ZERO;
1579 while (COM_ParseToken_QuakeC(&text, false))
1581 if (!strcasecmp(com_token, "}"))
1583 if (!strcasecmp(com_token, "\n"))
1586 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1588 if (j < TEXTURE_MAXFRAMES + 4)
1590 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1591 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1592 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1594 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1595 numparameters = j + 1;
1597 if (!COM_ParseToken_QuakeC(&text, true))
1600 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1601 // parameter[j][0] = 0;
1602 if (developer_insane.integer)
1604 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1605 for (j = 0;j < numparameters;j++)
1606 Con_DPrintf(" %s", parameter[j]);
1609 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1611 if (numparameters == 2)
1613 if (!strcasecmp(parameter[1], "add"))
1615 layer->blendfunc[0] = GL_ONE;
1616 layer->blendfunc[1] = GL_ONE;
1618 else if (!strcasecmp(parameter[1], "addalpha"))
1620 layer->blendfunc[0] = GL_SRC_ALPHA;
1621 layer->blendfunc[1] = GL_ONE;
1623 else if (!strcasecmp(parameter[1], "filter"))
1625 layer->blendfunc[0] = GL_DST_COLOR;
1626 layer->blendfunc[1] = GL_ZERO;
1628 else if (!strcasecmp(parameter[1], "blend"))
1630 layer->blendfunc[0] = GL_SRC_ALPHA;
1631 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1634 else if (numparameters == 3)
1637 for (k = 0;k < 2;k++)
1639 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1640 layer->blendfunc[k] = GL_ONE;
1641 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1642 layer->blendfunc[k] = GL_ZERO;
1643 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1644 layer->blendfunc[k] = GL_SRC_COLOR;
1645 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1646 layer->blendfunc[k] = GL_SRC_ALPHA;
1647 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1648 layer->blendfunc[k] = GL_DST_COLOR;
1649 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1650 layer->blendfunc[k] = GL_DST_ALPHA;
1651 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1652 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1653 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1654 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1655 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1656 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1657 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1658 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1660 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1664 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1665 layer->alphatest = true;
1666 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1668 if (!strcasecmp(parameter[0], "clampmap"))
1669 layer->clampmap = true;
1670 layer->numframes = 1;
1671 layer->framerate = 1;
1672 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1673 &q3shader_data->char_ptrs);
1674 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1675 if (!strcasecmp(parameter[1], "$lightmap"))
1676 shader.lighting = true;
1678 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1681 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1682 layer->framerate = atof(parameter[1]);
1683 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1684 for (i = 0;i < layer->numframes;i++)
1685 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1687 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1690 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1691 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1692 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1693 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1694 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1695 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1696 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1697 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1698 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1699 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1700 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1701 else if (!strcasecmp(parameter[1], "wave"))
1703 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1704 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1705 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1706 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1708 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1710 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1713 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1714 layer->alphagen.parms[i] = atof(parameter[i+2]);
1715 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1716 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1717 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1718 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1719 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1720 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1721 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1722 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1723 else if (!strcasecmp(parameter[1], "wave"))
1725 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1726 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1727 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1728 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1730 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1732 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1735 // observed values: tcgen environment
1736 // no other values have been observed in real shaders
1737 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1738 layer->tcgen.parms[i] = atof(parameter[i+2]);
1739 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1740 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1741 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1742 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1743 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1744 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1746 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1753 // tcmod stretch sin # # # #
1754 // tcmod stretch triangle # # # #
1755 // tcmod transform # # # # # #
1756 // tcmod turb # # # #
1757 // tcmod turb sin # # # # (this is bogus)
1758 // no other values have been observed in real shaders
1759 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1760 if (!layer->tcmods[tcmodindex].tcmod)
1762 if (tcmodindex < Q3MAXTCMODS)
1764 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1765 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1766 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1767 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1768 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1769 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1770 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1771 else if (!strcasecmp(parameter[1], "stretch"))
1773 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1774 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1775 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1776 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1778 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1779 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1780 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1783 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1785 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1786 if (!strcasecmp(com_token, "}"))
1789 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
1790 shader.lighting = true;
1791 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
1793 if (layer == shader.layers + 0)
1795 // vertex controlled transparency
1796 shader.vertexalpha = true;
1800 // multilayer terrain shader or similar
1801 shader.textureblendalpha = true;
1802 if (mod_q3shader_force_terrain_alphaflag.integer)
1803 shader.layers[0].dptexflags |= TEXF_ALPHA;
1807 if(mod_q3shader_force_addalpha.integer)
1809 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
1810 // this cvar brings back this behaviour
1811 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
1812 layer->blendfunc[0] = GL_SRC_ALPHA;
1815 layer->dptexflags = 0;
1816 if (layer->alphatest)
1817 layer->dptexflags |= TEXF_ALPHA;
1818 switch(layer->blendfunc[0])
1821 case GL_ONE_MINUS_SRC_ALPHA:
1822 layer->dptexflags |= TEXF_ALPHA;
1825 switch(layer->blendfunc[1])
1828 case GL_ONE_MINUS_SRC_ALPHA:
1829 layer->dptexflags |= TEXF_ALPHA;
1832 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
1833 layer->dptexflags |= TEXF_MIPMAP;
1834 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
1835 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
1836 if (layer->clampmap)
1837 layer->dptexflags |= TEXF_CLAMP;
1841 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1843 if (j < TEXTURE_MAXFRAMES + 4)
1845 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1846 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1847 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1849 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1850 numparameters = j + 1;
1852 if (!COM_ParseToken_QuakeC(&text, true))
1855 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1856 // parameter[j][0] = 0;
1857 if (fileindex == 0 && !strcasecmp(com_token, "}"))
1859 if (developer_insane.integer)
1861 Con_DPrintf("%s: ", shader.name);
1862 for (j = 0;j < numparameters;j++)
1863 Con_DPrintf(" %s", parameter[j]);
1866 if (numparameters < 1)
1868 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
1870 if (!strcasecmp(parameter[1], "alphashadow"))
1871 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
1872 else if (!strcasecmp(parameter[1], "areaportal"))
1873 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
1874 else if (!strcasecmp(parameter[1], "botclip"))
1875 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
1876 else if (!strcasecmp(parameter[1], "clusterportal"))
1877 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
1878 else if (!strcasecmp(parameter[1], "detail"))
1879 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
1880 else if (!strcasecmp(parameter[1], "donotenter"))
1881 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
1882 else if (!strcasecmp(parameter[1], "dust"))
1883 shader.surfaceparms |= Q3SURFACEPARM_DUST;
1884 else if (!strcasecmp(parameter[1], "hint"))
1885 shader.surfaceparms |= Q3SURFACEPARM_HINT;
1886 else if (!strcasecmp(parameter[1], "fog"))
1887 shader.surfaceparms |= Q3SURFACEPARM_FOG;
1888 else if (!strcasecmp(parameter[1], "lava"))
1889 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
1890 else if (!strcasecmp(parameter[1], "lightfilter"))
1891 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
1892 else if (!strcasecmp(parameter[1], "lightgrid"))
1893 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
1894 else if (!strcasecmp(parameter[1], "metalsteps"))
1895 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
1896 else if (!strcasecmp(parameter[1], "nodamage"))
1897 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
1898 else if (!strcasecmp(parameter[1], "nodlight"))
1899 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
1900 else if (!strcasecmp(parameter[1], "nodraw"))
1901 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
1902 else if (!strcasecmp(parameter[1], "nodrop"))
1903 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
1904 else if (!strcasecmp(parameter[1], "noimpact"))
1905 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
1906 else if (!strcasecmp(parameter[1], "nolightmap"))
1907 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
1908 else if (!strcasecmp(parameter[1], "nomarks"))
1909 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
1910 else if (!strcasecmp(parameter[1], "nomipmaps"))
1911 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
1912 else if (!strcasecmp(parameter[1], "nonsolid"))
1913 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
1914 else if (!strcasecmp(parameter[1], "origin"))
1915 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
1916 else if (!strcasecmp(parameter[1], "playerclip"))
1917 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
1918 else if (!strcasecmp(parameter[1], "sky"))
1919 shader.surfaceparms |= Q3SURFACEPARM_SKY;
1920 else if (!strcasecmp(parameter[1], "slick"))
1921 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
1922 else if (!strcasecmp(parameter[1], "slime"))
1923 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
1924 else if (!strcasecmp(parameter[1], "structural"))
1925 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
1926 else if (!strcasecmp(parameter[1], "trans"))
1927 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
1928 else if (!strcasecmp(parameter[1], "water"))
1929 shader.surfaceparms |= Q3SURFACEPARM_WATER;
1930 else if (!strcasecmp(parameter[1], "pointlight"))
1931 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
1932 else if (!strcasecmp(parameter[1], "antiportal"))
1933 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
1934 else if (!strcasecmp(parameter[1], "skip"))
1935 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
1938 // try custom surfaceparms
1939 for (j = 0; j < numcustsurfaceflags; j++)
1941 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
1943 shader.surfaceflags |= custsurfaceflags[j];
1948 if (j == numcustsurfaceflags)
1949 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
1952 else if (!strcasecmp(parameter[0], "dpshadow"))
1953 shader.dpshadow = true;
1954 else if (!strcasecmp(parameter[0], "dpnoshadow"))
1955 shader.dpnoshadow = true;
1956 else if (!strcasecmp(parameter[0], "dpnortlight"))
1957 shader.dpnortlight = true;
1958 else if (!strcasecmp(parameter[0], "dpreflectcube"))
1959 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
1960 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
1961 shader.dpmeshcollisions = true;
1962 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
1963 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
1965 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == 0.0f)
1966 shader.dpshaderkill = dpshaderkill;
1968 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
1969 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
1971 const char *op = NULL;
1972 if (numparameters >= 3)
1976 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != 0.0f)
1977 shader.dpshaderkill = dpshaderkill;
1979 else if (numparameters >= 4 && !strcmp(op, "=="))
1981 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) == atof(parameter[3]))
1982 shader.dpshaderkill = dpshaderkill;
1984 else if (numparameters >= 4 && !strcmp(op, "!="))
1986 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) != atof(parameter[3]))
1987 shader.dpshaderkill = dpshaderkill;
1989 else if (numparameters >= 4 && !strcmp(op, ">"))
1991 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) > atof(parameter[3]))
1992 shader.dpshaderkill = dpshaderkill;
1994 else if (numparameters >= 4 && !strcmp(op, "<"))
1996 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) < atof(parameter[3]))
1997 shader.dpshaderkill = dpshaderkill;
1999 else if (numparameters >= 4 && !strcmp(op, ">="))
2001 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) >= atof(parameter[3]))
2002 shader.dpshaderkill = dpshaderkill;
2004 else if (numparameters >= 4 && !strcmp(op, "<="))
2006 if (Cvar_VariableValue(&cvars_all, parameter[1], ~0) <= atof(parameter[3]))
2007 shader.dpshaderkill = dpshaderkill;
2011 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2014 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2016 // some q3 skies don't have the sky parm set
2017 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2018 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2020 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2022 // some q3 skies don't have the sky parm set
2023 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2024 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2025 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2027 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2029 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2030 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2032 else if (!strcasecmp(parameter[0], "nomipmaps"))
2033 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2034 else if (!strcasecmp(parameter[0], "nopicmip"))
2035 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2036 else if (!strcasecmp(parameter[0], "polygonoffset"))
2037 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2038 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2040 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2041 if(numparameters >= 2)
2043 shader.biaspolygonfactor = atof(parameter[1]);
2044 if(numparameters >= 3)
2045 shader.biaspolygonoffset = atof(parameter[2]);
2047 shader.biaspolygonoffset = 0;
2050 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2052 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2053 if (!strcasecmp(parameter[1], "sky"))
2054 shader.transparentsort = TRANSPARENTSORT_SKY;
2055 else if (!strcasecmp(parameter[1], "distance"))
2056 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2057 else if (!strcasecmp(parameter[1], "hud"))
2058 shader.transparentsort = TRANSPARENTSORT_HUD;
2060 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2062 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2064 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2065 shader.refractfactor = atof(parameter[1]);
2066 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2068 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2070 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2071 shader.reflectfactor = atof(parameter[1]);
2072 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2074 else if (!strcasecmp(parameter[0], "dpcamera"))
2076 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2078 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2080 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2081 shader.reflectmin = atof(parameter[1]);
2082 shader.reflectmax = atof(parameter[2]);
2083 shader.refractfactor = atof(parameter[3]);
2084 shader.reflectfactor = atof(parameter[4]);
2085 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2086 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2087 shader.r_water_wateralpha = atof(parameter[11]);
2089 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2091 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2092 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2094 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2096 shader.specularscalemod = atof(parameter[1]);
2098 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2100 shader.specularpowermod = atof(parameter[1]);
2102 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2104 shader.rtlightambient = atof(parameter[1]);
2106 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2108 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2109 shader.offsetmapping = OFFSETMAPPING_OFF;
2110 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2111 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2112 else if (!strcasecmp(parameter[1], "linear"))
2113 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2114 else if (!strcasecmp(parameter[1], "relief"))
2115 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2116 if (numparameters >= 3)
2117 shader.offsetscale = atof(parameter[2]);
2118 if (numparameters >= 5)
2120 if(!strcasecmp(parameter[3], "bias"))
2121 shader.offsetbias = atof(parameter[4]);
2122 else if(!strcasecmp(parameter[3], "match"))
2123 shader.offsetbias = 1.0f - atof(parameter[4]);
2124 else if(!strcasecmp(parameter[3], "match8"))
2125 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2126 else if(!strcasecmp(parameter[3], "match16"))
2127 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2130 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2133 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2134 if (!shader.deforms[deformindex].deform)
2136 if (deformindex < Q3MAXDEFORMS)
2138 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2139 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2140 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2141 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2142 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2143 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2144 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2145 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2146 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2147 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2148 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2149 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2150 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2151 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2152 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2153 else if (!strcasecmp(parameter[1], "wave" ))
2155 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2156 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2157 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2158 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2160 else if (!strcasecmp(parameter[1], "move" ))
2162 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2163 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2164 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2165 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2170 // hide this shader if a cvar said it should be killed
2171 if (shader.dpshaderkill)
2172 shader.numlayers = 0;
2173 // fix up multiple reflection types
2174 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2175 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2177 Q3Shader_AddToHash (&shader);
2181 FS_FreeSearch(search);
2182 // free custinfoparm values
2183 for (j = 0; j < numcustsurfaceflags; j++)
2184 Mem_Free(custsurfaceparmnames[j]);
2187 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
2189 unsigned short hash;
2190 q3shader_hash_entry_t* entry;
2192 Mod_LoadQ3Shaders();
2193 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2194 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2195 while (entry != NULL)
2197 if (strcasecmp (entry->shader.name, name) == 0)
2198 return &entry->shader;
2199 entry = entry->chain;
2204 texture_shaderpass_t *Mod_CreateShaderPass(mempool_t *mempool, skinframe_t *skinframe)
2206 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2207 shaderpass->framerate = 0.0f;
2208 shaderpass->numframes = 1;
2209 shaderpass->blendfunc[0] = GL_ONE;
2210 shaderpass->blendfunc[1] = GL_ZERO;
2211 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2212 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2213 shaderpass->alphatest = false;
2214 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2215 shaderpass->skinframes[0] = skinframe;
2219 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2222 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(mempool, sizeof(*shaderpass));
2223 shaderpass->alphatest = layer->alphatest != 0;
2224 shaderpass->framerate = layer->framerate;
2225 shaderpass->numframes = layer->numframes;
2226 shaderpass->blendfunc[0] = layer->blendfunc[0];
2227 shaderpass->blendfunc[1] = layer->blendfunc[1];
2228 shaderpass->rgbgen = layer->rgbgen;
2229 shaderpass->alphagen = layer->alphagen;
2230 shaderpass->tcgen = layer->tcgen;
2231 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2232 shaderpass->tcmods[j] = layer->tcmods[j];
2233 for (j = 0; j < layer->numframes; j++)
2234 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2238 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags, int defaultmaterialflags)
2240 int texflagsmask, texflagsor;
2241 qboolean success = true;
2242 q3shaderinfo_t *shader;
2245 strlcpy(texture->name, name, sizeof(texture->name));
2246 texture->basealpha = 1.0f;
2247 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2249 // allow disabling of picmip or compression by defaulttexflags
2251 if(!(defaulttexflags & TEXF_PICMIP))
2252 texflagsmask &= ~TEXF_PICMIP;
2253 if(!(defaulttexflags & TEXF_COMPRESS))
2254 texflagsmask &= ~TEXF_COMPRESS;
2256 if(defaulttexflags & TEXF_ISWORLD)
2257 texflagsor |= TEXF_ISWORLD;
2258 if(defaulttexflags & TEXF_ISSPRITE)
2259 texflagsor |= TEXF_ISSPRITE;
2260 // unless later loaded from the shader
2261 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2262 texture->offsetscale = 1;
2263 texture->offsetbias = 0;
2264 texture->specularscalemod = 1;
2265 texture->specularpowermod = 1;
2266 texture->rtlightambient = 0;
2267 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2268 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2269 // JUST GREP FOR "specularscalemod = 1".
2273 if (developer_loading.integer)
2274 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2276 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2278 texture->basematerialflags = MATERIALFLAG_SKY;
2279 if (shader->skyboxname[0] && loadmodel)
2281 // quake3 seems to append a _ to the skybox name, so this must do so as well
2282 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2285 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2286 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2288 texture->basematerialflags = MATERIALFLAG_WALL;
2290 if (shader->layers[0].alphatest)
2291 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2292 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2293 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2294 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2296 texture->biaspolygonoffset += shader->biaspolygonoffset;
2297 texture->biaspolygonfactor += shader->biaspolygonfactor;
2299 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2300 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2301 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2302 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2303 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2304 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2305 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2306 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2307 texture->customblendfunc[0] = GL_ONE;
2308 texture->customblendfunc[1] = GL_ZERO;
2309 texture->transparentsort = shader->transparentsort;
2310 if (shader->numlayers > 0)
2312 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2313 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2315 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2316 * additive GL_ONE GL_ONE
2317 additive weird GL_ONE GL_SRC_ALPHA
2318 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2319 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2320 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2321 brighten GL_DST_COLOR GL_ONE
2322 brighten GL_ONE GL_SRC_COLOR
2323 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2324 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2325 * modulate GL_DST_COLOR GL_ZERO
2326 * modulate GL_ZERO GL_SRC_COLOR
2327 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2328 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2329 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2330 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2331 * no blend GL_ONE GL_ZERO
2332 nothing GL_ZERO GL_ONE
2334 // if not opaque, figure out what blendfunc to use
2335 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2337 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2338 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2339 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2340 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2341 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2342 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2344 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2347 if (!shader->lighting)
2348 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2350 // here be dragons: convert quake3 shaders to material
2351 if (shader->numlayers > 0)
2354 int terrainbackgroundlayer = -1;
2355 int lightmaplayer = -1;
2356 int alphagenspecularlayer = -1;
2357 int rgbgenvertexlayer = -1;
2358 int rgbgendiffuselayer = -1;
2359 int materiallayer = -1;
2360 int endofprelayers = 0;
2361 int firstpostlayer = 0;
2362 int shaderpassindex = 0;
2363 for (i = 0; i < shader->numlayers; i++)
2365 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2367 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2368 rgbgenvertexlayer = i;
2369 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2370 rgbgendiffuselayer = i;
2371 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2372 alphagenspecularlayer = i;
2374 if (shader->numlayers >= 2
2375 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2376 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2377 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2378 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2380 // terrain blend or certain other effects involving alphatest over a regular layer
2381 terrainbackgroundlayer = 0;
2383 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2384 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2386 else if (lightmaplayer == 0)
2388 // ordinary texture but with $lightmap before diffuse
2390 firstpostlayer = lightmaplayer + 2;
2392 else if (lightmaplayer >= 1)
2394 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2395 endofprelayers = lightmaplayer - 1;
2396 materiallayer = lightmaplayer - 1;
2397 firstpostlayer = lightmaplayer + 1;
2399 else if (rgbgenvertexlayer >= 0)
2401 // map models with baked lighting
2402 materiallayer = rgbgenvertexlayer;
2403 endofprelayers = rgbgenvertexlayer;
2404 firstpostlayer = rgbgenvertexlayer + 1;
2405 // special case for rgbgen vertex if MATERIALFLAG_VERTEXCOLOR is expected on this material
2406 if (defaultmaterialflags & MATERIALFLAG_VERTEXCOLOR)
2407 texture->basematerialflags |= MATERIALFLAG_VERTEXCOLOR | MATERIALFLAG_ALPHAGEN_VERTEX;
2409 else if (rgbgendiffuselayer >= 0)
2411 // entity models with dynamic lighting
2412 materiallayer = rgbgendiffuselayer;
2413 endofprelayers = rgbgendiffuselayer;
2414 firstpostlayer = rgbgendiffuselayer + 1;
2415 // player models often have specular as a pass after diffuse - we don't currently make use of that specular texture (would need to meld it into the skinframe)...
2416 if (alphagenspecularlayer >= 0)
2417 firstpostlayer = alphagenspecularlayer + 1;
2421 // special effects shaders - treat first as primary layer and do everything else as post
2426 // convert the main material layer
2427 // FIXME: if alphagenspecularlayer is used, we should pass a specular texture name to R_SkinFrame_LoadExternal and have it load that texture instead of the assumed name for _gloss texture
2428 if (materiallayer >= 0)
2429 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2430 // convert the terrain background blend layer (if any)
2431 if (terrainbackgroundlayer >= 0)
2432 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2433 // convert the prepass layers (if any)
2434 texture->startpreshaderpass = shaderpassindex;
2435 for (i = 0; i < endofprelayers; i++)
2436 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2437 texture->endpreshaderpass = shaderpassindex;
2438 texture->startpostshaderpass = shaderpassindex;
2439 // convert the postpass layers (if any)
2440 for (i = firstpostlayer; i < shader->numlayers; i++)
2441 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2442 texture->startpostshaderpass = shaderpassindex;
2445 if (shader->dpshadow)
2446 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2447 if (shader->dpnoshadow)
2448 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2449 if (shader->dpnortlight)
2450 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2451 if (shader->vertexalpha)
2452 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2453 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2454 texture->reflectmin = shader->reflectmin;
2455 texture->reflectmax = shader->reflectmax;
2456 texture->refractfactor = shader->refractfactor;
2457 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2458 texture->reflectfactor = shader->reflectfactor;
2459 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2460 texture->r_water_wateralpha = shader->r_water_wateralpha;
2461 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2462 texture->offsetmapping = shader->offsetmapping;
2463 texture->offsetscale = shader->offsetscale;
2464 texture->offsetbias = shader->offsetbias;
2465 texture->specularscalemod = shader->specularscalemod;
2466 texture->specularpowermod = shader->specularpowermod;
2467 texture->rtlightambient = shader->rtlightambient;
2468 texture->refractive_index = mod_q3shader_default_refractive_index.value;
2469 if (shader->dpreflectcube[0])
2470 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2472 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2473 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2474 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2475 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2476 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2477 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2478 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2479 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2480 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2482 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2483 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2484 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2485 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2486 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2487 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2488 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2489 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2490 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2491 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2492 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2493 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2494 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2495 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2496 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2497 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2498 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2499 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2500 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2501 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2502 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2503 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2504 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2505 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2506 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2507 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2508 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2509 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2510 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2511 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2512 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2513 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2515 texture->surfaceflags = shader->surfaceflags;
2516 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2517 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2518 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2519 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2520 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2521 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2522 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2523 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2524 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2525 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2526 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2527 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2528 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2529 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2530 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2531 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2532 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2533 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2534 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2535 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2536 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2537 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2538 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2539 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2540 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2541 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2542 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2543 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2544 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2545 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2546 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2547 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2549 if (shader->dpmeshcollisions)
2550 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2551 if (shader->dpshaderkill && developer_extra.integer)
2552 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2554 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2556 if (developer_extra.integer)
2557 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2558 texture->basematerialflags = defaultmaterialflags;
2559 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2561 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2563 if (developer_extra.integer)
2564 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2565 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2566 texture->supercontents = SUPERCONTENTS_SOLID;
2570 if (developer_extra.integer)
2571 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2572 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2574 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2575 texture->supercontents = SUPERCONTENTS_SOLID;
2577 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2579 texture->basematerialflags = MATERIALFLAG_SKY;
2580 texture->supercontents = SUPERCONTENTS_SKY;
2584 texture->basematerialflags = defaultmaterialflags;
2585 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2587 if(cls.state == ca_dedicated)
2589 texture->materialshaderpass = NULL;
2594 skinframe_t *skinframe = R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, fallback);
2597 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2598 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2599 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2600 if (texture->q2contents)
2601 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2605 if (!success && warnmissing)
2606 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2609 // init the animation variables
2610 texture->currentframe = texture;
2611 texture->currentmaterialflags = texture->basematerialflags;
2612 if (!texture->materialshaderpass)
2613 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2614 if (!texture->materialshaderpass->skinframes[0])
2615 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2616 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2617 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2621 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2623 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2624 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2626 strlcpy(texture->name, name, sizeof(texture->name));
2627 texture->basealpha = 1.0f;
2628 texture->basematerialflags = materialflags;
2629 texture->supercontents = supercontents;
2631 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2632 texture->offsetscale = 1;
2633 texture->offsetbias = 0;
2634 texture->specularscalemod = 1;
2635 texture->specularpowermod = 1;
2636 texture->rtlightambient = 0;
2637 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2638 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2639 // JUST GREP FOR "specularscalemod = 1".
2641 if (developer_extra.integer)
2642 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2644 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2646 // init the animation variables
2647 texture->currentmaterialflags = texture->basematerialflags;
2648 texture->currentframe = texture;
2649 texture->currentskinframe = skinframe;
2650 texture->backgroundcurrentskinframe = NULL;
2653 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2655 long unsigned int i, j;
2656 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2658 if (texture->shaderpasses[i])
2661 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2662 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2663 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2664 Mem_Free(texture->shaderpasses[i]);
2665 texture->shaderpasses[i] = NULL;
2668 texture->materialshaderpass = NULL;
2669 texture->currentskinframe = NULL;
2670 texture->backgroundcurrentskinframe = NULL;
2673 skinfile_t *Mod_LoadSkinFiles(void)
2675 int i, words, line, wordsoverflow;
2678 skinfile_t *skinfile = NULL, *first = NULL;
2679 skinfileitem_t *skinfileitem;
2680 char word[10][MAX_QPATH];
2685 U_bodyBox,models/players/Legoman/BikerA2.tga
2686 U_RArm,models/players/Legoman/BikerA1.tga
2687 U_LArm,models/players/Legoman/BikerA1.tga
2688 U_armor,common/nodraw
2689 U_sword,common/nodraw
2690 U_shield,common/nodraw
2691 U_homb,common/nodraw
2692 U_backpack,common/nodraw
2693 U_colcha,common/nodraw
2698 memset(word, 0, sizeof(word));
2699 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2701 // If it's the first file we parse
2702 if (skinfile == NULL)
2704 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2709 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2710 skinfile = skinfile->next;
2712 skinfile->next = NULL;
2714 for(line = 0;;line++)
2717 if (!COM_ParseToken_QuakeC(&data, true))
2719 if (!strcmp(com_token, "\n"))
2722 wordsoverflow = false;
2726 strlcpy(word[words++], com_token, sizeof (word[0]));
2728 wordsoverflow = true;
2730 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2733 Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: line with too many statements, skipping\n", loadmodel->name, i, line);
2736 // words is always >= 1
2737 if (!strcmp(word[0], "replace"))
2741 if (developer_loading.integer)
2742 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2743 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2744 skinfileitem->next = skinfile->items;
2745 skinfile->items = skinfileitem;
2746 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2747 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2750 Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: wrong number of parameters to command \"%s\", see documentation in DP_GFX_SKINFILES extension in dpextensions.qc\n", loadmodel->name, i, line, word[0]);
2752 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2754 // tag name, like "tag_weapon,"
2755 // not used for anything (not even in Quake3)
2757 else if (words >= 2 && !strcmp(word[1], ","))
2759 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2760 if (developer_loading.integer)
2761 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2762 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2763 skinfileitem->next = skinfile->items;
2764 skinfile->items = skinfileitem;
2765 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2766 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2769 Con_Printf("Mod_LoadSkinFiles: parsing error in file \"%s_%i.skin\" on line #%i: does not look like tag or mesh specification, or replace command, see documentation in DP_GFX_SKINFILES extension in dpextensions.qc\n", loadmodel->name, i, line);
2774 loadmodel->numskins = i;
2778 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2781 skinfileitem_t *skinfileitem, *nextitem;
2782 for (;skinfile;skinfile = next)
2784 next = skinfile->next;
2785 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2787 nextitem = skinfileitem->next;
2788 Mem_Free(skinfileitem);
2794 int Mod_CountSkinFiles(skinfile_t *skinfile)
2797 for (i = 0;skinfile;skinfile = skinfile->next, i++);
2801 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
2804 double isnap = 1.0 / snap;
2805 for (i = 0;i < numvertices*numcomponents;i++)
2806 vertices[i] = floor(vertices[i]*isnap)*snap;
2809 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
2811 int i, outtriangles;
2812 float edgedir1[3], edgedir2[3], temp[3];
2813 // a degenerate triangle is one with no width (thickness, surface area)
2814 // these are characterized by having all 3 points colinear (along a line)
2815 // or having two points identical
2816 // the simplest check is to calculate the triangle's area
2817 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
2819 // calculate first edge
2820 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
2821 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
2822 CrossProduct(edgedir1, edgedir2, temp);
2823 if (VectorLength2(temp) < 0.001f)
2824 continue; // degenerate triangle (no area)
2825 // valid triangle (has area)
2826 VectorCopy(inelement3i, outelement3i);
2830 return outtriangles;
2833 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
2836 int firstvertex, lastvertex;
2837 if (numelements > 0 && elements)
2839 firstvertex = lastvertex = elements[0];
2840 for (i = 1;i < numelements;i++)
2843 firstvertex = min(firstvertex, e);
2844 lastvertex = max(lastvertex, e);
2848 firstvertex = lastvertex = 0;
2849 if (firstvertexpointer)
2850 *firstvertexpointer = firstvertex;
2851 if (lastvertexpointer)
2852 *lastvertexpointer = lastvertex;
2855 void Mod_MakeSortedSurfaces(dp_model_t *mod)
2857 // make an optimal set of texture-sorted batches to draw...
2859 int *firstsurfacefortexture;
2860 int *numsurfacesfortexture;
2861 if (!mod->sortedmodelsurfaces)
2862 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
2863 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
2864 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
2865 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
2866 for (j = 0;j < mod->nummodelsurfaces;j++)
2868 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2869 if(!surface->texture)
2871 t = (int)(surface->texture - mod->data_textures);
2872 numsurfacesfortexture[t]++;
2875 for (t = 0;t < mod->num_textures;t++)
2877 firstsurfacefortexture[t] = j;
2878 j += numsurfacesfortexture[t];
2880 for (j = 0;j < mod->nummodelsurfaces;j++)
2882 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
2883 if (!surface->texture)
2885 t = (int)(surface->texture - mod->data_textures);
2886 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
2888 Mem_Free(firstsurfacefortexture);
2889 Mem_Free(numsurfacesfortexture);
2892 void Mod_BuildVBOs(void)
2894 if(cls.state == ca_dedicated)
2897 if (!loadmodel->surfmesh.num_vertices)
2900 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
2903 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2905 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
2907 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
2908 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2913 // upload short indices as a buffer
2914 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
2915 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);
2917 // upload int indices as a buffer
2918 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
2919 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);
2921 // only build a vbo if one has not already been created (this is important for brush models which load specially)
2922 // we put several vertex data streams in the same buffer
2923 if (!loadmodel->surfmesh.data_vertex3f_vertexbuffer)
2928 loadmodel->surfmesh.data_vertex3f_bufferoffset = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2929 loadmodel->surfmesh.data_svector3f_bufferoffset = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2930 loadmodel->surfmesh.data_tvector3f_bufferoffset = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2931 loadmodel->surfmesh.data_normal3f_bufferoffset = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
2932 loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2933 loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
2934 loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
2935 loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2936 loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
2937 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
2938 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.data_vertex3f_bufferoffset , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2939 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.data_svector3f_bufferoffset , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2940 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.data_tvector3f_bufferoffset , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2941 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.data_normal3f_bufferoffset , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
2942 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.data_texcoordtexture2f_bufferoffset , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2943 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.data_texcoordlightmap2f_bufferoffset, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
2944 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.data_lightmapcolor4f_bufferoffset , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
2945 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalindex4ub_bufferoffset , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2946 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.data_skeletalweight4ub_bufferoffset , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
2947 loadmodel->surfmesh.data_vertex3f_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
2948 loadmodel->surfmesh.data_svector3f_vertexbuffer = loadmodel->surfmesh.data_svector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2949 loadmodel->surfmesh.data_tvector3f_vertexbuffer = loadmodel->surfmesh.data_tvector3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2950 loadmodel->surfmesh.data_normal3f_vertexbuffer = loadmodel->surfmesh.data_normal3f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2951 loadmodel->surfmesh.data_texcoordtexture2f_vertexbuffer = loadmodel->surfmesh.data_texcoordtexture2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2952 loadmodel->surfmesh.data_texcoordlightmap2f_vertexbuffer = loadmodel->surfmesh.data_texcoordlightmap2f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2953 loadmodel->surfmesh.data_lightmapcolor4f_vertexbuffer = loadmodel->surfmesh.data_lightmapcolor4f ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2954 loadmodel->surfmesh.data_skeletalindex4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalindex4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2955 loadmodel->surfmesh.data_skeletalweight4ub_vertexbuffer = loadmodel->surfmesh.data_skeletalweight4ub ? loadmodel->surfmesh.data_vertex3f_vertexbuffer : NULL;
2960 extern cvar_t mod_obj_orientation;
2961 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
2963 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
2965 const char *texname;
2967 const float *v, *vn, *vt;
2969 size_t outbufferpos = 0;
2970 size_t outbuffermax = 0x100000;
2971 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
2972 const msurface_t *surface;
2973 const int maxtextures = 256;
2974 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
2975 dp_model_t *submodel;
2977 // construct the mtllib file
2978 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
2981 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
2984 countvertices += surface->num_vertices;
2985 countfaces += surface->num_triangles;
2986 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
2987 for (textureindex = 0;textureindex < counttextures;textureindex++)
2988 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
2990 if (textureindex < counttextures)
2991 continue; // already wrote this material entry
2992 if (textureindex >= maxtextures)
2993 continue; // just a precaution
2994 textureindex = counttextures++;
2995 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
2996 if (outbufferpos >= outbuffermax >> 1)
2999 oldbuffer = outbuffer;
3000 outbuffer = (char *) Z_Malloc(outbuffermax);
3001 memcpy(outbuffer, oldbuffer, outbufferpos);
3004 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");
3009 // write the mtllib file
3010 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3012 // construct the obj file
3014 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);
3018 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)
3020 if (outbufferpos >= outbuffermax >> 1)
3023 oldbuffer = outbuffer;
3024 outbuffer = (char *) Z_Malloc(outbuffermax);
3025 memcpy(outbuffer, oldbuffer, outbufferpos);
3028 if(mod_obj_orientation.integer)
3029 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]);
3031 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]);
3036 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3038 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3041 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3042 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3044 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3045 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3048 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3050 if (outbufferpos >= outbuffermax >> 1)
3053 oldbuffer = outbuffer;
3054 outbuffer = (char *) Z_Malloc(outbuffermax);
3055 memcpy(outbuffer, oldbuffer, outbufferpos);
3061 if(mod_obj_orientation.integer)
3062 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);
3064 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);
3071 // write the obj file
3072 FS_WriteFile(filename, outbuffer, outbufferpos);
3076 Z_Free(texturenames);
3079 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3082 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3084 int countnodes = 0, counttriangles = 0, countframes = 0;
3092 size_t outbufferpos = 0;
3093 size_t outbuffermax = 0x100000;
3094 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3095 const msurface_t *surface;
3096 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3099 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3101 if (outbufferpos >= outbuffermax >> 1)
3104 oldbuffer = outbuffer;
3105 outbuffer = (char *) Z_Malloc(outbuffermax);
3106 memcpy(outbuffer, oldbuffer, outbufferpos);
3110 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3114 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3117 for (poseindex = 0;poseindex < numposes;poseindex++)
3120 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3123 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3127 matrix4x4_t posematrix;
3128 if (outbufferpos >= outbuffermax >> 1)
3131 oldbuffer = outbuffer;
3132 outbuffer = (char *) Z_Malloc(outbuffermax);
3133 memcpy(outbuffer, oldbuffer, outbufferpos);
3137 // strangely the smd angles are for a transposed matrix, so we
3138 // have to generate a transposed matrix, then convert that...
3139 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3140 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3141 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3142 if (angles[0] >= 180) angles[0] -= 360;
3143 if (angles[1] >= 180) angles[1] -= 360;
3144 if (angles[2] >= 180) angles[2] -= 360;
3148 float a = DEG2RAD(angles[ROLL]);
3149 float b = DEG2RAD(angles[PITCH]);
3150 float c = DEG2RAD(angles[YAW]);
3151 float cy, sy, cp, sp, cr, sr;
3153 // smd matrix construction, for comparing
3164 test[1][0] = sr*sp*cy+cr*-sy;
3165 test[1][1] = sr*sp*sy+cr*cy;
3167 test[2][0] = (cr*sp*cy+-sr*-sy);
3168 test[2][1] = (cr*sp*sy+-sr*cy);
3170 test[3][0] = pose[9];
3171 test[3][1] = pose[10];
3172 test[3][2] = pose[11];
3175 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]));
3180 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3185 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3188 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3190 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3193 if (outbufferpos >= outbuffermax >> 1)
3196 oldbuffer = outbuffer;
3197 outbuffer = (char *) Z_Malloc(outbuffermax);
3198 memcpy(outbuffer, oldbuffer, outbufferpos);
3201 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3204 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3206 const int index = e[2-cornerindex];
3207 const float *v = model->surfmesh.data_vertex3f + index * 3;
3208 const float *vn = model->surfmesh.data_normal3f + index * 3;
3209 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3210 const int b = model->surfmesh.blends[index];
3211 if (b < model->num_bones)
3212 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]);
3215 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3216 const unsigned char *wi = w->index;
3217 const unsigned char *wf = w->influence;
3218 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);
3219 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);
3220 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);
3221 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]);
3228 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3233 FS_WriteFile(filename, outbuffer, outbufferpos);
3236 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3243 decompiles a model to editable files
3246 static void Mod_Decompile_f(cmd_state_t *cmd)
3248 int i, j, k, l, first, count;
3250 char inname[MAX_QPATH];
3251 char outname[MAX_QPATH];
3252 char mtlname[MAX_QPATH];
3253 char basename[MAX_QPATH];
3254 char animname[MAX_QPATH];
3255 char animname2[MAX_QPATH];
3256 char zymtextbuffer[16384];
3257 char dpmtextbuffer[16384];
3258 char framegroupstextbuffer[16384];
3259 int zymtextsize = 0;
3260 int dpmtextsize = 0;
3261 int framegroupstextsize = 0;
3264 if (Cmd_Argc(cmd) != 2)
3266 Con_Print("usage: modeldecompile <filename>\n");
3270 strlcpy(inname, Cmd_Argv(cmd, 1), sizeof(inname));
3271 FS_StripExtension(inname, basename, sizeof(basename));
3273 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3276 Con_Print("No such model\n");
3279 if (mod->brush.submodel)
3281 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3282 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3283 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3286 if (!mod->surfmesh.num_triangles)
3288 Con_Print("Empty model (or sprite)\n");
3292 // export OBJ if possible (not on sprites)
3293 if (mod->surfmesh.num_triangles)
3295 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3296 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3297 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3300 // export SMD if possible (only for skeletal models)
3301 if (mod->surfmesh.num_triangles && mod->num_bones)
3303 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3304 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3305 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3306 if (l > 0) zymtextsize += l;
3307 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3308 if (l > 0) dpmtextsize += l;
3309 for (i = 0;i < mod->numframes;i = j)
3311 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3312 first = mod->animscenes[i].firstframe;
3313 if (mod->animscenes[i].framecount > 1)
3316 count = mod->animscenes[i].framecount;
3322 // check for additional frames with same name
3323 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3324 if(animname[l] < '0' || animname[l] > '9')
3326 if(k > 0 && animname[k-1] == '_')
3329 count = mod->num_poses - first;
3330 for (j = i + 1;j < mod->numframes;j++)
3332 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3333 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3334 if(animname2[l] < '0' || animname2[l] > '9')
3336 if(k > 0 && animname[k-1] == '_')
3339 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3341 count = mod->animscenes[j].firstframe - first;
3345 // if it's only one frame, use the original frame name
3347 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3350 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3351 Mod_Decompile_SMD(mod, outname, first, count, false);
3352 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3354 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3355 if (l > 0) zymtextsize += l;
3357 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3359 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3360 if (l > 0) dpmtextsize += l;
3362 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3364 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3365 if (l > 0) framegroupstextsize += l;
3369 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3371 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3372 if (framegroupstextsize)
3373 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3377 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3380 memset(state, 0, sizeof(*state));
3381 state->width = width;
3382 state->height = height;
3383 state->currentY = 0;
3384 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3385 for (y = 0;y < state->height;y++)
3387 state->rows[y].currentX = 0;
3388 state->rows[y].rowY = -1;
3392 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3395 state->currentY = 0;
3396 for (y = 0;y < state->height;y++)
3398 state->rows[y].currentX = 0;
3399 state->rows[y].rowY = -1;
3403 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3406 Mem_Free(state->rows);
3407 memset(state, 0, sizeof(*state));
3410 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3412 mod_alloclightmap_row_t *row;
3415 row = state->rows + blockheight;
3416 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3418 if (state->currentY + blockheight <= state->height)
3420 // use the current allocation position
3421 row->rowY = state->currentY;
3423 state->currentY += blockheight;
3427 // find another position
3428 for (y = blockheight;y < state->height;y++)
3430 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3432 row = state->rows + y;
3436 if (y == state->height)
3441 *outx = row->currentX;
3442 row->currentX += blockwidth;
3447 typedef struct lightmapsample_s
3451 float *vertex_color;
3452 unsigned char *lm_bgr;
3453 unsigned char *lm_dir;
3457 typedef struct lightmapvertex_s
3462 float texcoordbase[2];
3463 float texcoordlightmap[2];
3464 float lightcolor[4];
3468 typedef struct lightmaptriangle_s
3476 // 2D modelspace coordinates of min corner
3477 // snapped to lightmap grid but not in grid coordinates
3479 // 2D modelspace to lightmap coordinate scale
3487 typedef struct lightmaplight_s
3498 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3500 #define MAX_LIGHTMAPSAMPLES 64
3501 static int mod_generatelightmaps_numoffsets[3];
3502 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3504 static int mod_generatelightmaps_numlights;
3505 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3507 extern cvar_t r_shadow_lightattenuationdividebias;
3508 extern cvar_t r_shadow_lightattenuationlinearscale;
3510 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3515 float relativepoint[3];
3522 float lightorigin[3];
3526 float lightcolor[3];
3528 for (i = 0;i < 5*3;i++)
3530 for (index = 0;;index++)
3532 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3537 lightradius2 = lightradius * lightradius;
3538 VectorSubtract(lightorigin, pos, relativepoint);
3539 dist2 = VectorLength2(relativepoint);
3540 if (dist2 >= lightradius2)
3542 lightiradius = 1.0f / lightradius;
3543 dist = sqrt(dist2) * lightiradius;
3544 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3545 if (intensity <= 0.0f)
3547 if (model && model->TraceLine)
3549 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3550 if (trace.fraction < 1)
3553 // scale down intensity to add to both ambient and diffuse
3554 //intensity *= 0.5f;
3555 VectorNormalize(relativepoint);
3556 VectorScale(lightcolor, intensity, color);
3557 VectorMA(sample , 0.5f , color, sample );
3558 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3559 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3560 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3561 // calculate a weighted average light direction as well
3562 intensity *= VectorLength(color);
3563 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3565 // calculate the direction we'll use to reduce the sample to a directional light source
3566 VectorCopy(sample + 12, dir);
3567 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3568 VectorNormalize(dir);
3569 // extract the diffuse color along the chosen direction and scale it
3570 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3571 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3572 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3573 // subtract some of diffuse from ambient
3574 VectorMA(sample, -0.333f, diffuse, ambient);
3575 // store the normalized lightdir
3576 VectorCopy(dir, lightdir);
3579 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3583 const msurface_t *surface;
3584 const float *vertex3f = model->surfmesh.data_vertex3f;
3585 const int *element3i = model->surfmesh.data_element3i;
3588 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3590 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3592 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3594 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3596 VectorCopy(vertex3f + 3*e[0], v2[0]);
3597 VectorCopy(vertex3f + 3*e[1], v2[1]);
3598 VectorCopy(vertex3f + 3*e[2], v2[2]);
3599 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3604 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3606 int maxnodes = 1<<14;
3607 svbsp_node_t *nodes;
3612 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3613 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3614 VectorCopy(lightinfo->origin, origin);
3615 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3618 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3619 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3620 if (svbsp.ranoutofnodes)
3623 if (maxnodes > 1<<22)
3629 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3634 if (svbsp.numnodes > 0)
3636 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3637 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3638 lightinfo->svbsp = svbsp;
3643 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3647 lightmaplight_t *lightinfo;
3651 mod_generatelightmaps_numlights = 0;
3652 for (index = 0;;index++)
3654 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3658 mod_generatelightmaps_numlights++;
3660 if (mod_generatelightmaps_numlights > 0)
3662 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3663 lightinfo = mod_generatelightmaps_lightinfo;
3664 for (index = 0;;index++)
3666 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3673 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3675 lightinfo->iradius = 1.0f / lightinfo->radius;
3676 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3677 // TODO: compute svbsp
3678 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3682 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3685 if (mod_generatelightmaps_lightinfo)
3687 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3688 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3689 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3690 Mem_Free(mod_generatelightmaps_lightinfo);
3692 mod_generatelightmaps_lightinfo = NULL;
3693 mod_generatelightmaps_numlights = 0;
3696 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3698 const svbsp_node_t *node;
3699 const svbsp_node_t *nodes = svbsp->nodes;
3704 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3706 return num == -1; // true if empty, false if solid (shadowed)
3709 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3712 float relativepoint[3];
3721 const lightmaplight_t *lightinfo;
3723 for (i = 0;i < 5*3;i++)
3725 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3727 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3728 VectorSubtract(lightinfo->origin, pos, relativepoint);
3729 // don't accept light from behind a surface, it causes bad shading
3730 if (normal && DotProduct(relativepoint, normal) <= 0)
3732 dist2 = VectorLength2(relativepoint);
3733 if (dist2 >= lightinfo->radius2)
3735 dist = sqrt(dist2) * lightinfo->iradius;
3736 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3739 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3743 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3745 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3747 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3750 // for light grid we'd better check visibility of the offset point
3751 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3752 if (trace.fraction < 1)
3753 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3756 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3761 // scale intensity according to how many rays succeeded
3762 // we know one test is valid, half of the rest will fail...
3763 //if (normal && tests > 1)
3764 // intensity *= (tests - 1.0f) / tests;
3765 intensity *= (float)hits / tests;
3767 // scale down intensity to add to both ambient and diffuse
3768 //intensity *= 0.5f;
3769 VectorNormalize(relativepoint);
3770 VectorScale(lightinfo->color, intensity, color);
3771 VectorMA(sample , 0.5f , color, sample );
3772 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3773 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3774 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3775 // calculate a weighted average light direction as well
3776 intensity *= VectorLength(color);
3777 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3781 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
3787 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
3788 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3789 VectorCopy(sample + 12, dir);
3790 VectorNormalize(dir);
3791 //VectorAdd(dir, normal, dir);
3792 //VectorNormalize(dir);
3793 f = DotProduct(dir, normal);
3794 f = max(0, f) * 255.0f;
3795 VectorScale(sample, f, color);
3796 //VectorCopy(normal, dir);
3797 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
3798 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
3799 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
3800 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
3802 lm_dir[0] = (unsigned char)dir[2];
3803 lm_dir[1] = (unsigned char)dir[1];
3804 lm_dir[2] = (unsigned char)dir[0];
3808 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
3811 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
3812 VectorCopy(sample, vertex_color);
3815 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
3821 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
3822 // calculate the direction we'll use to reduce the sample to a directional light source
3823 VectorCopy(sample + 12, dir);
3824 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3825 VectorNormalize(dir);
3826 // extract the diffuse color along the chosen direction and scale it
3827 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
3828 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
3829 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
3830 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
3831 VectorScale(sample, 127.5f, ambient);
3832 VectorMA(ambient, -0.333f, diffuse, ambient);
3833 // encode to the grid format
3834 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
3835 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
3836 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
3837 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
3838 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
3839 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
3840 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
3841 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
3842 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));}
3845 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
3850 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
3851 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
3852 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
3853 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
3854 radius[0] = mod_generatelightmaps_lightmapradius.value;
3855 radius[1] = mod_generatelightmaps_vertexradius.value;
3856 radius[2] = mod_generatelightmaps_gridradius.value;
3857 for (i = 0;i < 3;i++)
3859 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
3862 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
3867 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
3869 msurface_t *surface;
3872 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3874 surface = model->data_surfaces + surfaceindex;
3875 surface->lightmaptexture = NULL;
3876 surface->deluxemaptexture = NULL;
3878 if (model->brushq3.data_lightmaps)
3880 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3881 if (model->brushq3.data_lightmaps[i])
3882 R_FreeTexture(model->brushq3.data_lightmaps[i]);
3883 Mem_Free(model->brushq3.data_lightmaps);
3884 model->brushq3.data_lightmaps = NULL;
3886 if (model->brushq3.data_deluxemaps)
3888 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
3889 if (model->brushq3.data_deluxemaps[i])
3890 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
3891 Mem_Free(model->brushq3.data_deluxemaps);
3892 model->brushq3.data_deluxemaps = NULL;
3896 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
3898 msurface_t *surface;
3904 surfmesh_t oldsurfmesh;
3906 unsigned char *data;
3907 oldsurfmesh = model->surfmesh;
3908 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
3909 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
3911 size += model->surfmesh.num_vertices * sizeof(float[3]);
3912 size += model->surfmesh.num_vertices * sizeof(float[3]);
3913 size += model->surfmesh.num_vertices * sizeof(float[3]);
3914 size += model->surfmesh.num_vertices * sizeof(float[3]);
3915 size += model->surfmesh.num_vertices * sizeof(float[2]);
3916 size += model->surfmesh.num_vertices * sizeof(float[2]);
3917 size += model->surfmesh.num_vertices * sizeof(float[4]);
3918 data = (unsigned char *)Mem_Alloc(model->mempool, size);
3919 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3920 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3921 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3922 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
3923 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3924 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
3925 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
3926 if (model->surfmesh.num_vertices > 65536)
3927 model->surfmesh.data_element3s = NULL;
3929 if (model->surfmesh.data_element3i_indexbuffer && !model->surfmesh.data_element3i_indexbuffer->isdynamic)
3930 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
3931 model->surfmesh.data_element3i_indexbuffer = NULL;
3932 if (model->surfmesh.data_element3s_indexbuffer && !model->surfmesh.data_element3s_indexbuffer->isdynamic)
3933 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
3934 model->surfmesh.data_element3s_indexbuffer = NULL;
3935 if (model->surfmesh.data_vertex3f_vertexbuffer && !model->surfmesh.data_vertex3f_vertexbuffer->isdynamic)
3936 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_vertex3f_vertexbuffer);
3937 model->surfmesh.data_vertex3f_vertexbuffer = NULL;
3938 model->surfmesh.data_svector3f_vertexbuffer = NULL;
3939 model->surfmesh.data_tvector3f_vertexbuffer = NULL;
3940 model->surfmesh.data_normal3f_vertexbuffer = NULL;
3941 model->surfmesh.data_texcoordtexture2f_vertexbuffer = NULL;
3942 model->surfmesh.data_texcoordlightmap2f_vertexbuffer = NULL;
3943 model->surfmesh.data_lightmapcolor4f_vertexbuffer = NULL;
3944 model->surfmesh.data_skeletalindex4ub_vertexbuffer = NULL;
3945 model->surfmesh.data_skeletalweight4ub_vertexbuffer = NULL;
3947 // convert all triangles to unique vertex data
3949 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
3951 surface = model->data_surfaces + surfaceindex;
3952 surface->num_firstvertex = outvertexindex;
3953 surface->num_vertices = surface->num_triangles*3;
3954 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
3955 for (i = 0;i < surface->num_triangles*3;i++)
3958 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
3959 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
3960 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
3961 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
3962 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
3963 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
3964 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
3965 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
3966 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
3967 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
3968 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
3969 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
3970 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
3971 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
3972 if (oldsurfmesh.data_texcoordlightmap2f)
3974 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
3975 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
3977 if (oldsurfmesh.data_lightmapcolor4f)
3979 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
3980 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
3981 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
3982 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
3985 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
3986 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
3990 if (model->surfmesh.data_element3s)
3991 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
3992 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
3994 // find and update all submodels to use this new surfmesh data
3995 for (i = 0;i < model->brush.numsubmodels;i++)
3996 model->brush.submodels[i]->surfmesh = model->surfmesh;
3999 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
4001 msurface_t *surface;
4007 lightmaptriangle_t *triangle;
4008 // generate lightmap triangle structs
4009 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4010 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4012 surface = model->data_surfaces + surfaceindex;
4013 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4014 for (i = 0;i < surface->num_triangles;i++)
4016 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4017 triangle->triangleindex = surface->num_firsttriangle+i;
4018 triangle->surfaceindex = surfaceindex;
4019 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4020 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4021 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4022 // calculate bounds of triangle
4023 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4024 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4025 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4026 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4027 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4028 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4029 // pick an axial projection based on the triangle normal
4030 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4032 if (fabs(normal[1]) > fabs(normal[axis]))
4034 if (fabs(normal[2]) > fabs(normal[axis]))
4036 triangle->axis = axis;
4041 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4043 if (mod_generatelightmaps_lightmaptriangles)
4044 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4045 mod_generatelightmaps_lightmaptriangles = NULL;
4048 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4050 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4052 msurface_t *surface;
4066 float trianglenormal[3];
4067 float samplecenter[3];
4068 float samplenormal[3];
4074 float lmscalepixels;
4077 float lm_basescalepixels;
4078 int lm_borderpixels;
4082 lightmaptriangle_t *triangle;
4083 unsigned char *lightmappixels;
4084 unsigned char *deluxemappixels;
4085 mod_alloclightmap_state_t lmstate;
4088 // generate lightmap projection information for all triangles
4089 if (model->texturepool == NULL)
4090 model->texturepool = R_AllocTexturePool();
4091 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4092 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4093 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4094 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4095 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4097 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4099 surface = model->data_surfaces + surfaceindex;
4100 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4101 lmscalepixels = lm_basescalepixels;
4102 for (retry = 0;retry < 30;retry++)
4104 // after a couple failed attempts, degrade quality to make it fit
4106 lmscalepixels *= 0.5f;
4107 for (i = 0;i < surface->num_triangles;i++)
4109 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4110 triangle->lightmapindex = lightmapnumber;
4111 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4112 // pick two planar axes for projection
4113 // lightmap coordinates here are in pixels
4114 // lightmap projections are snapped to pixel grid explicitly, such
4115 // that two neighboring triangles sharing an edge and projection
4116 // axis will have identical sample spacing along their shared edge
4118 for (j = 0;j < 3;j++)
4120 if (j == triangle->axis)
4122 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4123 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4124 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4125 triangle->lmbase[k] = lmmins/lmscalepixels;
4126 triangle->lmscale[k] = lmscalepixels;
4129 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4132 // if all fit in this texture, we're done with this surface
4133 if (i == surface->num_triangles)
4135 // if we haven't maxed out the lightmap size yet, we retry the
4136 // entire surface batch...
4137 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4139 lm_texturesize *= 2;
4142 Mod_AllocLightmap_Free(&lmstate);
4143 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4146 // if we have maxed out the lightmap size, and this triangle does
4147 // not fit in the same texture as the rest of the surface, we have
4148 // to retry the entire surface in a new texture (can only use one)
4149 // with multiple retries, the lightmap quality degrades until it
4150 // fits (or gives up)
4151 if (surfaceindex > 0)
4153 Mod_AllocLightmap_Reset(&lmstate);
4157 Mod_AllocLightmap_Free(&lmstate);
4159 // now put triangles together into lightmap textures, and do not allow
4160 // triangles of a surface to go into different textures (as that would
4161 // require rewriting the surface list)
4162 model->brushq3.deluxemapping_modelspace = true;
4163 model->brushq3.deluxemapping = true;
4164 model->brushq3.num_mergedlightmaps = lightmapnumber;
4165 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4166 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4167 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4168 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4169 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4171 surface = model->data_surfaces + surfaceindex;
4172 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4173 for (i = 0;i < surface->num_triangles;i++)
4175 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4176 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4177 VectorNormalize(trianglenormal);
4178 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4179 axis = triangle->axis;
4180 axis1 = axis == 0 ? 1 : 0;
4181 axis2 = axis == 2 ? 1 : 2;
4182 lmiscale[0] = 1.0f / triangle->lmscale[0];
4183 lmiscale[1] = 1.0f / triangle->lmscale[1];
4184 if (trianglenormal[axis] < 0)
4185 VectorNegate(trianglenormal, trianglenormal);
4186 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4187 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4188 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4189 for (j = 0;j < 3;j++)
4191 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4192 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4193 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4195 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4196 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4197 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4198 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]);
4208 forward[1] = 1.0f / triangle->lmscale[0];
4212 left[2] = 1.0f / triangle->lmscale[1];
4217 origin[1] = triangle->lmbase[0];
4218 origin[2] = triangle->lmbase[1];
4221 forward[0] = 1.0f / triangle->lmscale[0];
4226 left[2] = 1.0f / triangle->lmscale[1];
4230 origin[0] = triangle->lmbase[0];
4232 origin[2] = triangle->lmbase[1];
4235 forward[0] = 1.0f / triangle->lmscale[0];
4239 left[1] = 1.0f / triangle->lmscale[1];
4244 origin[0] = triangle->lmbase[0];
4245 origin[1] = triangle->lmbase[1];
4249 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4251 #define LM_DIST_EPSILON (1.0f / 32.0f)
4252 for (y = 0;y < triangle->lmsize[1];y++)
4254 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4255 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4257 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4258 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4259 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4260 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4261 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4267 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4269 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);
4270 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);
4274 Mem_Free(lightmappixels);
4275 if (deluxemappixels)
4276 Mem_Free(deluxemappixels);
4278 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4280 surface = model->data_surfaces + surfaceindex;
4281 if (!surface->num_triangles)
4283 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4284 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4285 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4286 surface->lightmapinfo = NULL;
4289 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4290 model->brushq1.lightdata = NULL;
4291 model->brushq1.lightmapupdateflags = NULL;
4292 model->brushq1.firstrender = false;
4293 model->brushq1.num_lightstyles = 0;
4294 model->brushq1.data_lightstyleinfo = NULL;
4295 for (i = 0;i < model->brush.numsubmodels;i++)
4297 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4298 model->brush.submodels[i]->brushq1.firstrender = false;
4299 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4300 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4304 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4307 for (i = 0;i < model->surfmesh.num_vertices;i++)
4308 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4311 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4318 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4320 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4321 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4323 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4324 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4326 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4327 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4333 extern cvar_t mod_q3bsp_nolightmaps;
4334 static void Mod_GenerateLightmaps(dp_model_t *model)
4336 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4337 dp_model_t *oldloadmodel = loadmodel;
4340 Mod_GenerateLightmaps_InitSampleOffsets(model);
4341 Mod_GenerateLightmaps_DestroyLightmaps(model);
4342 Mod_GenerateLightmaps_UnweldTriangles(model);
4343 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4344 Mod_GenerateLightmaps_CreateLights(model);
4345 if(!mod_q3bsp_nolightmaps.integer)
4346 Mod_GenerateLightmaps_CreateLightmaps(model);
4347 Mod_GenerateLightmaps_UpdateVertexColors(model);
4348 Mod_GenerateLightmaps_UpdateLightGrid(model);
4349 Mod_GenerateLightmaps_DestroyLights(model);
4350 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4352 loadmodel = oldloadmodel;
4355 static void Mod_GenerateLightmaps_f(cmd_state_t *cmd)
4357 if (Cmd_Argc(cmd) != 1)
4359 Con_Printf("usage: mod_generatelightmaps\n");
4364 Con_Printf("no worldmodel loaded\n");
4367 Mod_GenerateLightmaps(cl.worldmodel);
4370 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4372 memset(mod, 0, sizeof(*mod));
4373 strlcpy(mod->name, name, sizeof(mod->name));
4374 mod->mempool = Mem_AllocPool(name, 0, NULL);
4375 mod->texturepool = R_AllocTexturePool();
4376 mod->Draw = R_Q1BSP_Draw;
4377 mod->DrawDepth = R_Q1BSP_DrawDepth;
4378 mod->DrawDebug = R_Q1BSP_DrawDebug;
4379 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4380 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4381 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4382 mod->DrawLight = R_Q1BSP_DrawLight;
4385 void Mod_Mesh_Destroy(dp_model_t *mod)
4387 Mod_UnloadModel(mod);
4390 // resets the mesh model to have no geometry to render, ready for a new frame -
4391 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4392 void Mod_Mesh_Reset(dp_model_t *mod)
4394 mod->num_surfaces = 0;
4395 mod->surfmesh.num_vertices = 0;
4396 mod->surfmesh.num_triangles = 0;
4397 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4398 mod->DrawSky = NULL; // will be set if a texture needs it
4399 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4402 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int defaultmaterialflags)
4406 int drawflag = defaultdrawflags & DRAWFLAG_MASK;
4407 for (i = 0, t = mod->data_textures; i < mod->num_textures; i++, t++)
4408 if (!strcmp(t->name, name) && t->mesh_drawflag == drawflag && t->mesh_defaulttexflags == defaulttexflags && t->mesh_defaultmaterialflags == defaultmaterialflags)
4410 if (mod->max_textures <= mod->num_textures)
4412 texture_t *oldtextures = mod->data_textures;
4413 mod->max_textures = max(mod->max_textures * 2, 1024);
4414 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4415 // update the pointers
4416 for (i = 0; i < mod->num_surfaces; i++)
4417 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4419 t = &mod->data_textures[mod->num_textures++];
4420 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, true, true, defaulttexflags, defaultmaterialflags);
4421 t->mesh_drawflag = drawflag;
4422 t->mesh_defaulttexflags = defaulttexflags;
4423 t->mesh_defaultmaterialflags = defaultmaterialflags;
4424 switch (defaultdrawflags & DRAWFLAG_MASK)
4426 case DRAWFLAG_ADDITIVE:
4427 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4428 t->currentmaterialflags = t->basematerialflags;
4430 case DRAWFLAG_MODULATE:
4431 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4432 t->currentmaterialflags = t->basematerialflags;
4433 t->customblendfunc[0] = GL_DST_COLOR;
4434 t->customblendfunc[1] = GL_ZERO;
4436 case DRAWFLAG_2XMODULATE:
4437 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4438 t->currentmaterialflags = t->basematerialflags;
4439 t->customblendfunc[0] = GL_DST_COLOR;
4440 t->customblendfunc[1] = GL_SRC_COLOR;
4442 case DRAWFLAG_SCREEN:
4443 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4444 t->currentmaterialflags = t->basematerialflags;
4445 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4446 t->customblendfunc[1] = GL_ONE;
4454 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4457 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4458 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4459 return mod->data_surfaces + mod->num_surfaces - 1;
4460 // create new surface
4461 if (mod->max_surfaces == mod->num_surfaces)
4463 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4464 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4465 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4467 surf = mod->data_surfaces + mod->num_surfaces;
4468 mod->num_surfaces++;
4469 memset(surf, 0, sizeof(*surf));
4470 surf->texture = tex;
4471 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4472 surf->num_firstvertex = mod->surfmesh.num_vertices;
4473 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4474 mod->DrawSky = R_Q1BSP_DrawSky;
4475 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4476 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4480 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)
4482 int hashindex, h, vnum, mask;
4483 surfmesh_t *mesh = &mod->surfmesh;
4484 if (mesh->max_vertices == mesh->num_vertices)
4486 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4487 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4488 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4489 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4490 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4491 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4492 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4493 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4494 // rebuild the hash table
4495 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4496 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4497 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4498 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4499 mask = mod->surfmesh.num_vertexhashsize - 1;
4500 // no need to hash the vertices for the entire model, the latest surface will suffice.
4501 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4503 // this uses prime numbers intentionally for computing the hash
4504 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;
4505 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4506 ; // just iterate until we find the terminator
4507 mesh->data_vertexhash[h] = vnum;
4510 mask = mod->surfmesh.num_vertexhashsize - 1;
4511 // this uses prime numbers intentionally for computing the hash
4512 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4513 // when possible find an identical vertex within the same surface and return it
4514 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4516 if (vnum >= surf->num_firstvertex
4517 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4518 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4519 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4520 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4521 && 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)
4524 // add the new vertex
4525 vnum = mesh->num_vertices++;
4526 if (surf->num_vertices > 0)
4528 if (surf->mins[0] > x) surf->mins[0] = x;
4529 if (surf->mins[1] > y) surf->mins[1] = y;
4530 if (surf->mins[2] > z) surf->mins[2] = z;
4531 if (surf->maxs[0] < x) surf->maxs[0] = x;
4532 if (surf->maxs[1] < y) surf->maxs[1] = y;
4533 if (surf->maxs[2] < z) surf->maxs[2] = z;
4537 VectorSet(surf->mins, x, y, z);
4538 VectorSet(surf->maxs, x, y, z);
4540 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4541 mesh->data_vertexhash[h] = vnum;
4542 mesh->data_vertex3f[vnum * 3 + 0] = x;
4543 mesh->data_vertex3f[vnum * 3 + 1] = y;
4544 mesh->data_vertex3f[vnum * 3 + 2] = z;
4545 mesh->data_normal3f[vnum * 3 + 0] = nx;
4546 mesh->data_normal3f[vnum * 3 + 1] = ny;
4547 mesh->data_normal3f[vnum * 3 + 2] = nz;
4548 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4549 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4550 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4551 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4552 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4553 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4554 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4555 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4559 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4561 surfmesh_t *mesh = &mod->surfmesh;
4562 if (mesh->max_triangles == mesh->num_triangles)
4564 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4565 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4566 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4568 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4569 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4570 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4571 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4572 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4573 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4574 mesh->num_triangles++;
4575 surf->num_triangles++;
4578 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4582 msurface_t *surf, *surf2;
4584 // build the sorted surfaces list properly to reduce material setup
4585 // this is easy because we're just sorting on texture and don't care about the order of textures
4586 mod->nummodelsurfaces = 0;
4587 for (i = 0; i < mod->num_surfaces; i++)
4588 mod->data_surfaces[i].included = false;
4589 for (i = 0; i < mod->num_surfaces; i++)
4591 surf = mod->data_surfaces + i;
4594 tex = surf->texture;
4595 // j = i is intentional
4596 for (j = i; j < mod->num_surfaces; j++)
4598 surf2 = mod->data_surfaces + j;
4599 if (surf2->included)
4601 if (surf2->texture == tex)
4603 surf2->included = true;
4604 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4610 static void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4613 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4615 if (mod->surfmesh.num_vertices > 0)
4617 // calculate normalmins/normalmaxs
4618 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4619 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4620 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4622 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4623 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4624 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4625 // expand bounds to include this vertex
4626 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4627 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4628 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4629 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4630 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4631 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4633 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4634 // (fast but less accurate than doing it per vertex)
4635 x2a = mod->normalmins[0] * mod->normalmins[0];
4636 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4637 y2a = mod->normalmins[1] * mod->normalmins[1];
4638 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4639 z2a = mod->normalmins[2] * mod->normalmins[2];
4640 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4644 yawradius = sqrt(x2 + y2);
4645 rotatedradius = sqrt(x2 + y2 + z2);
4646 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4647 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4648 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4649 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4650 mod->radius = rotatedradius;
4651 mod->radius2 = x2 + y2 + z2;
4655 VectorClear(mod->normalmins);
4656 VectorClear(mod->normalmaxs);
4657 VectorClear(mod->yawmins);
4658 VectorClear(mod->yawmaxs);
4659 VectorClear(mod->rotatedmins);
4660 VectorClear(mod->rotatedmaxs);
4666 void Mod_Mesh_Validate(dp_model_t *mod)
4669 qboolean warned = false;
4670 for (i = 0; i < mod->num_surfaces; i++)
4672 msurface_t *surf = mod->data_surfaces + i;
4673 int *e = mod->surfmesh.data_element3i + surf->num_firsttriangle * 3;
4674 int first = surf->num_firstvertex;
4675 int end = surf->num_firstvertex + surf->num_vertices;
4677 for (j = 0;j < surf->num_triangles * 3;j++)
4679 if (e[j] < first || e[j] >= end)
4682 Con_DPrintf("Mod_Mesh_Validate: detected corrupt surface - debug me!\n");
4690 static void Mod_Mesh_UploadDynamicBuffers(dp_model_t *mod)
4692 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;
4693 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;
4694 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;
4695 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;
4696 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;
4697 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;
4698 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;
4699 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;
4700 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;
4701 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;
4702 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;
4705 void Mod_Mesh_Finalize(dp_model_t *mod)
4707 if (gl_paranoid.integer)
4708 Mod_Mesh_Validate(mod);
4709 Mod_Mesh_ComputeBounds(mod);
4710 Mod_Mesh_MakeSortedSurfaces(mod);
4711 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);
4712 Mod_Mesh_UploadDynamicBuffers(mod);