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_enableshadowvolumes = {CVAR_SAVE, "r_enableshadowvolumes", "1", "Enables use of Stencil Shadow Volume shadowing methods, saves some memory if turned off"};
31 cvar_t r_mipskins = {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"};
32 cvar_t r_mipnormalmaps = {CVAR_SAVE, "r_mipnormalmaps", "1", "mipmaps normalmaps (turning it off looks sharper but may have aliasing)"};
33 cvar_t mod_generatelightmaps_unitspersample = {CVAR_SAVE, "mod_generatelightmaps_unitspersample", "8", "lightmap resolution"};
34 cvar_t mod_generatelightmaps_borderpixels = {CVAR_SAVE, "mod_generatelightmaps_borderpixels", "2", "extra space around polygons to prevent sampling artifacts"};
35 cvar_t mod_generatelightmaps_texturesize = {CVAR_SAVE, "mod_generatelightmaps_texturesize", "1024", "size of lightmap textures"};
36 cvar_t mod_generatelightmaps_lightmapsamples = {CVAR_SAVE, "mod_generatelightmaps_lightmapsamples", "16", "number of shadow tests done per lightmap pixel"};
37 cvar_t mod_generatelightmaps_vertexsamples = {CVAR_SAVE, "mod_generatelightmaps_vertexsamples", "16", "number of shadow tests done per vertex"};
38 cvar_t mod_generatelightmaps_gridsamples = {CVAR_SAVE, "mod_generatelightmaps_gridsamples", "64", "number of shadow tests done per lightgrid cell"};
39 cvar_t mod_generatelightmaps_lightmapradius = {CVAR_SAVE, "mod_generatelightmaps_lightmapradius", "16", "sampling area around each lightmap pixel"};
40 cvar_t mod_generatelightmaps_vertexradius = {CVAR_SAVE, "mod_generatelightmaps_vertexradius", "16", "sampling area around each vertex"};
41 cvar_t mod_generatelightmaps_gridradius = {CVAR_SAVE, "mod_generatelightmaps_gridradius", "64", "sampling area around each lightgrid cell center"};
43 dp_model_t *loadmodel;
45 static mempool_t *mod_mempool;
46 static memexpandablearray_t models;
48 static mempool_t* q3shaders_mem;
49 typedef struct q3shader_hash_entry_s
51 q3shaderinfo_t shader;
52 struct q3shader_hash_entry_s* chain;
53 } q3shader_hash_entry_t;
54 #define Q3SHADER_HASH_SIZE 1021
55 typedef struct q3shader_data_s
57 memexpandablearray_t hash_entries;
58 q3shader_hash_entry_t hash[Q3SHADER_HASH_SIZE];
59 memexpandablearray_t char_ptrs;
61 static q3shader_data_t* q3shader_data;
63 static void mod_start(void)
66 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
69 SCR_PushLoadingScreen(false, "Loading models", 1.0);
71 for (i = 0;i < nummodels;i++)
72 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
75 for (i = 0;i < nummodels;i++)
76 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
79 SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
80 Mod_LoadModel(mod, true, false);
81 SCR_PopLoadingScreen(false);
83 SCR_PopLoadingScreen(false);
86 static void mod_shutdown(void)
89 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
92 for (i = 0;i < nummodels;i++)
93 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && (mod->loaded || mod->mempool))
97 Mod_Skeletal_FreeBuffers();
100 static void mod_newmap(void)
103 int i, j, k, l, surfacenum, ssize, tsize;
104 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
107 for (i = 0;i < nummodels;i++)
109 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool)
111 for (j = 0;j < mod->num_textures && mod->data_textures;j++)
113 // note that materialshaderpass and backgroundshaderpass point to shaderpasses[] and so do the pre/post shader ranges, so this catches all of them...
114 for (l = 0; l < Q3SHADER_MAXLAYERS; l++)
115 if (mod->data_textures[j].shaderpasses[l])
116 for (k = 0; k < mod->data_textures[j].shaderpasses[l]->numframes; k++)
117 R_SkinFrame_MarkUsed(mod->data_textures[j].shaderpasses[l]->skinframes[k]);
119 if (mod->brush.solidskyskinframe)
120 R_SkinFrame_MarkUsed(mod->brush.solidskyskinframe);
121 if (mod->brush.alphaskyskinframe)
122 R_SkinFrame_MarkUsed(mod->brush.alphaskyskinframe);
126 if (!cl_stainmaps_clearonload.integer)
129 for (i = 0;i < nummodels;i++)
131 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->mempool && mod->data_surfaces)
133 for (surfacenum = 0, surface = mod->data_surfaces;surfacenum < mod->num_surfaces;surfacenum++, surface++)
135 if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
137 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
138 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
139 memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
140 mod->brushq1.lightmapupdateflags[surfacenum] = true;
152 static void Mod_Print(void);
153 static void Mod_Precache (void);
154 static void Mod_Decompile_f(void);
155 static void Mod_GenerateLightmaps_f(void);
158 mod_mempool = Mem_AllocPool("modelinfo", 0, NULL);
159 Mem_ExpandableArray_NewArray(&models, mod_mempool, sizeof(dp_model_t), 16);
165 Cvar_RegisterVariable(&r_enableshadowvolumes);
166 Cvar_RegisterVariable(&r_mipskins);
167 Cvar_RegisterVariable(&r_mipnormalmaps);
168 Cvar_RegisterVariable(&mod_generatelightmaps_unitspersample);
169 Cvar_RegisterVariable(&mod_generatelightmaps_borderpixels);
170 Cvar_RegisterVariable(&mod_generatelightmaps_texturesize);
172 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapsamples);
173 Cvar_RegisterVariable(&mod_generatelightmaps_vertexsamples);
174 Cvar_RegisterVariable(&mod_generatelightmaps_gridsamples);
175 Cvar_RegisterVariable(&mod_generatelightmaps_lightmapradius);
176 Cvar_RegisterVariable(&mod_generatelightmaps_vertexradius);
177 Cvar_RegisterVariable(&mod_generatelightmaps_gridradius);
179 Cmd_AddCommand ("modellist", Mod_Print, "prints a list of loaded models");
180 Cmd_AddCommand ("modelprecache", Mod_Precache, "load a model");
181 Cmd_AddCommand ("modeldecompile", Mod_Decompile_f, "exports a model in several formats for editing purposes");
182 Cmd_AddCommand ("mod_generatelightmaps", Mod_GenerateLightmaps_f, "rebuilds lighting on current worldmodel");
185 void Mod_RenderInit(void)
187 R_RegisterModule("Models", mod_start, mod_shutdown, mod_newmap, NULL, NULL);
190 void Mod_UnloadModel (dp_model_t *mod)
192 char name[MAX_QPATH];
194 dp_model_t *parentmodel;
196 if (developer_loading.integer)
197 Con_Printf("unloading model %s\n", mod->name);
199 strlcpy(name, mod->name, sizeof(name));
200 parentmodel = mod->brush.parentmodel;
204 if (mod->surfmesh.data_element3i_indexbuffer)
205 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3i_indexbuffer);
206 mod->surfmesh.data_element3i_indexbuffer = NULL;
207 if (mod->surfmesh.data_element3s_indexbuffer)
208 R_Mesh_DestroyMeshBuffer(mod->surfmesh.data_element3s_indexbuffer);
209 mod->surfmesh.data_element3s_indexbuffer = NULL;
210 if (mod->surfmesh.vbo_vertexbuffer)
211 R_Mesh_DestroyMeshBuffer(mod->surfmesh.vbo_vertexbuffer);
212 mod->surfmesh.vbo_vertexbuffer = NULL;
214 // free textures/memory attached to the model
215 R_FreeTexturePool(&mod->texturepool);
216 Mem_FreePool(&mod->mempool);
217 // clear the struct to make it available
218 memset(mod, 0, sizeof(dp_model_t));
219 // restore the fields we want to preserve
220 strlcpy(mod->name, name, sizeof(mod->name));
221 mod->brush.parentmodel = parentmodel;
226 static void R_Model_Null_Draw(entity_render_t *ent)
232 typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass);
234 static int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
249 // REQUIRED: fetch start
250 COM_ParseToken_Simple(&bufptr, true, false, true);
252 break; // end of file
253 if (!strcmp(com_token, "\n"))
254 continue; // empty line
255 start = atoi(com_token);
257 // REQUIRED: fetch length
258 COM_ParseToken_Simple(&bufptr, true, false, true);
259 if (!bufptr || !strcmp(com_token, "\n"))
261 Con_Printf("framegroups file: missing number of frames\n");
264 len = atoi(com_token);
266 // OPTIONAL args start
267 COM_ParseToken_Simple(&bufptr, true, false, true);
269 // OPTIONAL: fetch fps
271 if (bufptr && strcmp(com_token, "\n"))
273 fps = atof(com_token);
274 COM_ParseToken_Simple(&bufptr, true, false, true);
277 // OPTIONAL: fetch loopflag
279 if (bufptr && strcmp(com_token, "\n"))
281 loop = (atoi(com_token) != 0);
282 COM_ParseToken_Simple(&bufptr, true, false, true);
285 // OPTIONAL: fetch name
287 if (bufptr && strcmp(com_token, "\n"))
289 strlcpy(name, com_token, sizeof(name));
290 COM_ParseToken_Simple(&bufptr, true, false, true);
293 // OPTIONAL: remaining unsupported tokens (eat them)
294 while (bufptr && strcmp(com_token, "\n"))
295 COM_ParseToken_Simple(&bufptr, true, false, true);
297 //Con_Printf("data: %d %d %d %f %d (%s)\n", i, start, len, fps, loop, name);
300 cb(i, start, len, fps, loop, (name[0] ? name : NULL), pass);
307 static void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, const char *name, void *pass)
309 dp_model_t *mod = (dp_model_t *) pass;
310 animscene_t *anim = &mod->animscenes[i];
312 strlcpy(anim->name, name, sizeof(anim[i].name));
314 dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d_anim", i);
315 anim->firstframe = bound(0, start, mod->num_poses - 1);
316 anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
317 anim->framerate = max(1, fps);
319 //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
322 static void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
327 cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
330 Con_Printf("no scene found in framegroups file, aborting\n");
333 mod->numframes = cnt;
336 // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
337 mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
340 Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
343 static void Mod_FindPotentialDeforms(dp_model_t *mod)
347 mod->wantnormals = false;
348 mod->wanttangents = false;
349 for (i = 0;i < mod->num_textures;i++)
351 texture = mod->data_textures + i;
352 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
353 mod->wantnormals = true;
354 if (texture->materialshaderpass && texture->materialshaderpass->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
355 mod->wantnormals = true;
356 for (j = 0;j < Q3MAXDEFORMS;j++)
358 if (texture->deforms[j].deform == Q3DEFORM_AUTOSPRITE)
360 mod->wanttangents = true;
361 mod->wantnormals = true;
364 if (texture->deforms[j].deform != Q3DEFORM_NONE)
365 mod->wantnormals = true;
377 dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
382 fs_offset_t filesize = 0;
387 if (mod->name[0] == '*') // submodel
390 if (!strcmp(mod->name, "null"))
395 if (mod->loaded || mod->mempool)
396 Mod_UnloadModel(mod);
398 if (developer_loading.integer)
399 Con_Printf("loading model %s\n", mod->name);
402 mod->crc = (unsigned int)-1;
405 VectorClear(mod->normalmins);
406 VectorClear(mod->normalmaxs);
407 VectorClear(mod->yawmins);
408 VectorClear(mod->yawmaxs);
409 VectorClear(mod->rotatedmins);
410 VectorClear(mod->rotatedmaxs);
412 mod->modeldatatypestring = "null";
413 mod->type = mod_null;
414 mod->Draw = R_Model_Null_Draw;
418 // no fatal errors occurred, so this model is ready to use.
427 // even if the model is loaded it still may need reloading...
429 // if it is not loaded or checkdisk is true we need to calculate the crc
430 if (!mod->loaded || checkdisk)
432 if (checkdisk && mod->loaded)
433 Con_DPrintf("checking model %s\n", mod->name);
434 buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
437 crc = CRC_Block((unsigned char *)buf, filesize);
438 // we need to reload the model if the crc does not match
444 // if the model is already loaded and checks passed, just return
452 if (developer_loading.integer)
453 Con_Printf("loading model %s\n", mod->name);
455 SCR_PushLoadingScreen(true, mod->name, 1);
457 // LordHavoc: unload the existing model in this slot (if there is one)
458 if (mod->loaded || mod->mempool)
459 Mod_UnloadModel(mod);
464 // errors can prevent the corresponding mod->loaded = true;
467 // default lightmap scale
468 mod->lightmapscale = 1;
470 // default model radius and bounding box (mainly for missing models)
472 VectorSet(mod->normalmins, -mod->radius, -mod->radius, -mod->radius);
473 VectorSet(mod->normalmaxs, mod->radius, mod->radius, mod->radius);
474 VectorSet(mod->yawmins, -mod->radius, -mod->radius, -mod->radius);
475 VectorSet(mod->yawmaxs, mod->radius, mod->radius, mod->radius);
476 VectorSet(mod->rotatedmins, -mod->radius, -mod->radius, -mod->radius);
477 VectorSet(mod->rotatedmaxs, mod->radius, mod->radius, mod->radius);
481 // load q3 shaders for the first time, or after a level change
487 char *bufend = (char *)buf + filesize;
489 // all models use memory, so allocate a memory pool
490 mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
492 num = LittleLong(*((int *)buf));
493 // call the apropriate loader
495 if (!strcasecmp(FS_FileExtension(mod->name), "obj")) Mod_OBJ_Load(mod, buf, bufend);
496 else if (!memcmp(buf, "IDPO", 4)) Mod_IDP0_Load(mod, buf, bufend);
497 else if (!memcmp(buf, "IDP2", 4)) Mod_IDP2_Load(mod, buf, bufend);
498 else if (!memcmp(buf, "IDP3", 4)) Mod_IDP3_Load(mod, buf, bufend);
499 else if (!memcmp(buf, "IDSP", 4)) Mod_IDSP_Load(mod, buf, bufend);
500 else if (!memcmp(buf, "IDS2", 4)) Mod_IDS2_Load(mod, buf, bufend);
501 else if (!memcmp(buf, "IBSP", 4)) Mod_IBSP_Load(mod, buf, bufend);
502 else if (!memcmp(buf, "ZYMOTICMODEL", 12)) Mod_ZYMOTICMODEL_Load(mod, buf, bufend);
503 else if (!memcmp(buf, "DARKPLACESMODEL", 16)) Mod_DARKPLACESMODEL_Load(mod, buf, bufend);
504 else if (!memcmp(buf, "ACTRHEAD", 8)) Mod_PSKMODEL_Load(mod, buf, bufend);
505 else if (!memcmp(buf, "INTERQUAKEMODEL", 16)) Mod_INTERQUAKEMODEL_Load(mod, buf, bufend);
506 else if (strlen(mod->name) >= 4 && !strcmp(mod->name + strlen(mod->name) - 4, ".map")) Mod_MAP_Load(mod, buf, bufend);
507 else if (num == BSPVERSION || num == 30 || !memcmp(buf, "BSP2", 4) || !memcmp(buf, "2PSB", 4)) Mod_Q1BSP_Load(mod, buf, bufend);
508 else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
511 Mod_FindPotentialDeforms(mod);
513 buf = FS_LoadFile(va(vabuf, sizeof(vabuf), "%s.framegroups", mod->name), tempmempool, false, &filesize);
516 Mod_FrameGroupify(mod, (const char *)buf);
524 // LordHavoc: Sys_Error was *ANNOYING*
525 Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
528 // no fatal errors occurred, so this model is ready to use.
531 SCR_PopLoadingScreen(false);
536 void Mod_ClearUsed(void)
539 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
541 for (i = 0;i < nummodels;i++)
542 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0])
546 void Mod_PurgeUnused(void)
549 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
551 for (i = 0;i < nummodels;i++)
553 if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && !mod->used)
555 Mod_UnloadModel(mod);
556 Mem_ExpandableArray_FreeRecord(&models, mod);
567 dp_model_t *Mod_FindName(const char *name, const char *parentname)
576 // if we're not dedicatd, the renderer calls will crash without video
579 nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
582 Host_Error ("Mod_ForName: empty name");
584 // search the currently loaded models
585 for (i = 0;i < nummodels;i++)
587 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))))
594 // no match found, create a new one
595 mod = (dp_model_t *) Mem_ExpandableArray_AllocRecord(&models);
596 strlcpy(mod->name, name, sizeof(mod->name));
598 mod->brush.parentmodel = Mod_FindName(parentname, NULL);
600 mod->brush.parentmodel = NULL;
610 Loads in a model for the given name
613 dp_model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, const char *parentname)
616 model = Mod_FindName(name, parentname);
617 if (!model->loaded || checkdisk)
618 Mod_LoadModel(model, crash, checkdisk);
626 Reloads all models if they have changed
629 void Mod_Reload(void)
632 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
635 SCR_PushLoadingScreen(false, "Reloading models", 1.0);
637 for (i = 0;i < nummodels;i++)
638 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
640 for (i = 0;i < nummodels;i++)
641 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
643 SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
644 Mod_LoadModel(mod, true, true);
645 SCR_PopLoadingScreen(false);
647 SCR_PopLoadingScreen(false);
650 unsigned char *mod_base;
653 //=============================================================================
660 static void Mod_Print(void)
663 int nummodels = (int)Mem_ExpandableArray_IndexRange(&models);
666 Con_Print("Loaded models:\n");
667 for (i = 0;i < nummodels;i++)
669 if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
671 if (mod->brush.numsubmodels)
672 Con_Printf("%4iK %s (%i submodels)\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name, mod->brush.numsubmodels);
674 Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
684 static void Mod_Precache(void)
687 Mod_ForName(Cmd_Argv(1), false, true, Cmd_Argv(1)[0] == '*' ? cl.model_name[1] : NULL);
689 Con_Print("usage: modelprecache <filename>\n");
692 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
696 used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
697 memset(used, 0, numvertices);
698 for (i = 0;i < numelements;i++)
699 used[elements[i]] = 1;
700 for (i = 0, count = 0;i < numvertices;i++)
701 remapvertices[i] = used[i] ? count++ : -1;
707 // fast way, using an edge hash
708 #define TRIANGLEEDGEHASH 8192
709 void Mod_BuildTriangleNeighbors(int *neighbors, const int *elements, int numtriangles)
711 int i, j, p, e1, e2, *n, hashindex, count, match;
713 typedef struct edgehashentry_s
715 struct edgehashentry_s *next;
720 static edgehashentry_t **edgehash;
721 edgehashentry_t *edgehashentries, *hash;
724 edgehash = (edgehashentry_t **)Mem_Alloc(tempmempool, TRIANGLEEDGEHASH * sizeof(*edgehash));
725 // if there are too many triangles for the stack array, allocate larger buffer
726 edgehashentries = (edgehashentry_t *)Mem_Alloc(tempmempool, numtriangles * 3 * sizeof(edgehashentry_t));
727 // find neighboring triangles
728 for (i = 0, e = elements, n = neighbors;i < numtriangles;i++, e += 3, n += 3)
730 for (j = 0, p = 2;j < 3;p = j, j++)
734 // this hash index works for both forward and backward edges
735 hashindex = (unsigned int)(e1 + e2) % TRIANGLEEDGEHASH;
736 hash = edgehashentries + i * 3 + j;
737 hash->next = edgehash[hashindex];
738 edgehash[hashindex] = hash;
740 hash->element[0] = e1;
741 hash->element[1] = e2;
744 for (i = 0, e = elements, n = neighbors;i < numtriangles;i++, e += 3, n += 3)
746 for (j = 0, p = 2;j < 3;p = j, j++)
750 // this hash index works for both forward and backward edges
751 hashindex = (unsigned int)(e1 + e2) % TRIANGLEEDGEHASH;
754 for (hash = edgehash[hashindex];hash;hash = hash->next)
756 if (hash->element[0] == e2 && hash->element[1] == e1)
758 if (hash->triangle != i)
759 match = hash->triangle;
762 else if ((hash->element[0] == e1 && hash->element[1] == e2))
765 // detect edges shared by three triangles and make them seams
771 // also send a keepalive here (this can take a while too!)
772 CL_KeepaliveMessage(false);
774 // free the allocated buffer
775 Mem_Free(edgehashentries);
779 // very slow but simple way
780 static int Mod_FindTriangleWithEdge(const int *elements, int numtriangles, int start, int end, int ignore)
785 for (i = 0;i < numtriangles;i++, elements += 3)
787 if ((elements[0] == start && elements[1] == end)
788 || (elements[1] == start && elements[2] == end)
789 || (elements[2] == start && elements[0] == end))
795 else if ((elements[1] == start && elements[0] == end)
796 || (elements[2] == start && elements[1] == end)
797 || (elements[0] == start && elements[2] == end))
800 // detect edges shared by three triangles and make them seams
806 void Mod_BuildTriangleNeighbors(int *neighbors, const int *elements, int numtriangles)
810 for (i = 0, e = elements, n = neighbors;i < numtriangles;i++, e += 3, n += 3)
812 n[0] = Mod_FindTriangleWithEdge(elements, numtriangles, e[1], e[0], i);
813 n[1] = Mod_FindTriangleWithEdge(elements, numtriangles, e[2], e[1], i);
814 n[2] = Mod_FindTriangleWithEdge(elements, numtriangles, e[0], e[2], i);
819 void Mod_ValidateElements(int *elements, int numtriangles, int firstvertex, int numverts, const char *filename, int fileline)
821 int i, warned = false, endvertex = firstvertex + numverts;
822 for (i = 0;i < numtriangles * 3;i++)
824 if (elements[i] < firstvertex || elements[i] >= endvertex)
829 Con_Printf("Mod_ValidateElements: out of bounds elements detected at %s:%d\n", filename, fileline);
831 elements[i] = firstvertex;
836 // warning: this is an expensive function!
837 void Mod_BuildNormals(int firstvertex, int numvertices, int numtriangles, const float *vertex3f, const int *elements, float *normal3f, qboolean areaweighting)
844 memset(normal3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
845 // process each vertex of each triangle and accumulate the results
846 // use area-averaging, to make triangles with a big area have a bigger
847 // weighting on the vertex normal than triangles with a small area
848 // to do so, just add the 'normals' together (the bigger the area
849 // the greater the length of the normal is
851 for (i = 0; i < numtriangles; i++, element += 3)
854 vertex3f + element[0] * 3,
855 vertex3f + element[1] * 3,
856 vertex3f + element[2] * 3,
861 VectorNormalize(areaNormal);
863 for (j = 0;j < 3;j++)
865 vectorNormal = normal3f + element[j] * 3;
866 vectorNormal[0] += areaNormal[0];
867 vectorNormal[1] += areaNormal[1];
868 vectorNormal[2] += areaNormal[2];
871 // and just normalize the accumulated vertex normal in the end
872 vectorNormal = normal3f + 3 * firstvertex;
873 for (i = 0; i < numvertices; i++, vectorNormal += 3)
874 VectorNormalize(vectorNormal);
878 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)
880 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
881 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
882 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
884 // 6 multiply, 9 subtract
885 VectorSubtract(v1, v0, v10);
886 VectorSubtract(v2, v0, v20);
887 normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
888 normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
889 normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
890 // 12 multiply, 10 subtract
891 tc10[1] = tc1[1] - tc0[1];
892 tc20[1] = tc2[1] - tc0[1];
893 svector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
894 svector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
895 svector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
896 tc10[0] = tc1[0] - tc0[0];
897 tc20[0] = tc2[0] - tc0[0];
898 tvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
899 tvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
900 tvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
901 // 12 multiply, 4 add, 6 subtract
902 f = DotProduct(svector3f, normal3f);
903 svector3f[0] -= f * normal3f[0];
904 svector3f[1] -= f * normal3f[1];
905 svector3f[2] -= f * normal3f[2];
906 f = DotProduct(tvector3f, normal3f);
907 tvector3f[0] -= f * normal3f[0];
908 tvector3f[1] -= f * normal3f[1];
909 tvector3f[2] -= f * normal3f[2];
910 // if texture is mapped the wrong way (counterclockwise), the tangents
911 // have to be flipped, this is detected by calculating a normal from the
912 // two tangents, and seeing if it is opposite the surface normal
913 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
914 CrossProduct(tvector3f, svector3f, tangentcross);
915 if (DotProduct(tangentcross, normal3f) < 0)
917 VectorNegate(svector3f, svector3f);
918 VectorNegate(tvector3f, tvector3f);
923 // warning: this is a very expensive function!
924 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)
927 float sdir[3], tdir[3], normal[3], *svec, *tvec;
928 const float *v0, *v1, *v2, *tc0, *tc1, *tc2, *n;
929 float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
932 memset(svector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
933 memset(tvector3f + 3 * firstvertex, 0, numvertices * sizeof(float[3]));
934 // process each vertex of each triangle and accumulate the results
935 for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
937 v0 = vertex3f + e[0] * 3;
938 v1 = vertex3f + e[1] * 3;
939 v2 = vertex3f + e[2] * 3;
940 tc0 = texcoord2f + e[0] * 2;
941 tc1 = texcoord2f + e[1] * 2;
942 tc2 = texcoord2f + e[2] * 2;
944 // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
945 // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
947 // calculate the edge directions and surface normal
948 // 6 multiply, 9 subtract
949 VectorSubtract(v1, v0, v10);
950 VectorSubtract(v2, v0, v20);
951 normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
952 normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
953 normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
955 // calculate the tangents
956 // 12 multiply, 10 subtract
957 tc10[1] = tc1[1] - tc0[1];
958 tc20[1] = tc2[1] - tc0[1];
959 sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
960 sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
961 sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
962 tc10[0] = tc1[0] - tc0[0];
963 tc20[0] = tc2[0] - tc0[0];
964 tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
965 tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
966 tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
968 // if texture is mapped the wrong way (counterclockwise), the tangents
969 // have to be flipped, this is detected by calculating a normal from the
970 // two tangents, and seeing if it is opposite the surface normal
971 // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
972 CrossProduct(tdir, sdir, tangentcross);
973 if (DotProduct(tangentcross, normal) < 0)
975 VectorNegate(sdir, sdir);
976 VectorNegate(tdir, tdir);
981 VectorNormalize(sdir);
982 VectorNormalize(tdir);
984 for (i = 0;i < 3;i++)
986 VectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);
987 VectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);
990 // make the tangents completely perpendicular to the surface normal, and
991 // then normalize them
992 // 16 assignments, 2 divide, 2 sqrt, 2 negates, 14 adds, 24 multiplies
993 for (i = 0, svec = svector3f + 3 * firstvertex, tvec = tvector3f + 3 * firstvertex, n = normal3f + 3 * firstvertex;i < numvertices;i++, svec += 3, tvec += 3, n += 3)
995 f = -DotProduct(svec, n);
996 VectorMA(svec, f, n, svec);
997 VectorNormalize(svec);
998 f = -DotProduct(tvec, n);
999 VectorMA(tvec, f, n, tvec);
1000 VectorNormalize(tvec);
1004 void Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors, qboolean neighbors)
1006 unsigned char *data;
1007 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 * (3 + (neighbors ? 3 : 0)) * sizeof(int) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0));
1008 loadmodel->surfmesh.num_vertices = numvertices;
1009 loadmodel->surfmesh.num_triangles = numtriangles;
1010 if (loadmodel->surfmesh.num_vertices)
1012 loadmodel->surfmesh.data_vertex3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1013 loadmodel->surfmesh.data_svector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1014 loadmodel->surfmesh.data_tvector3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1015 loadmodel->surfmesh.data_normal3f = (float *)data, data += sizeof(float[3]) * loadmodel->surfmesh.num_vertices;
1016 loadmodel->surfmesh.data_texcoordtexture2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
1017 loadmodel->surfmesh.data_texcoordlightmap2f = (float *)data, data += sizeof(float[2]) * loadmodel->surfmesh.num_vertices;
1019 loadmodel->surfmesh.data_lightmapcolor4f = (float *)data, data += sizeof(float[4]) * loadmodel->surfmesh.num_vertices;
1020 if (lightmapoffsets)
1021 loadmodel->surfmesh.data_lightmapoffsets = (int *)data, data += sizeof(int) * loadmodel->surfmesh.num_vertices;
1023 if (loadmodel->surfmesh.num_triangles)
1025 loadmodel->surfmesh.data_element3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
1027 loadmodel->surfmesh.data_neighbor3i = (int *)data, data += sizeof(int[3]) * loadmodel->surfmesh.num_triangles;
1028 if (loadmodel->surfmesh.num_vertices <= 65536)
1029 loadmodel->surfmesh.data_element3s = (unsigned short *)data, data += sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles;
1033 shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtriangles, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, int light, int neighbors, int expandable)
1035 shadowmesh_t *newmesh;
1036 unsigned char *data;
1038 size = sizeof(shadowmesh_t);
1039 size += maxverts * sizeof(float[3]);
1041 size += maxverts * sizeof(float[11]);
1042 size += maxtriangles * sizeof(int[3]);
1043 if (maxverts <= 65536)
1044 size += maxtriangles * sizeof(unsigned short[3]);
1046 size += maxtriangles * sizeof(int[3]);
1048 size += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *) + maxverts * sizeof(shadowmeshvertexhash_t);
1049 data = (unsigned char *)Mem_Alloc(mempool, size);
1050 newmesh = (shadowmesh_t *)data;data += sizeof(*newmesh);
1051 newmesh->map_diffuse = map_diffuse;
1052 newmesh->map_specular = map_specular;
1053 newmesh->map_normal = map_normal;
1054 newmesh->maxverts = maxverts;
1055 newmesh->maxtriangles = maxtriangles;
1056 newmesh->numverts = 0;
1057 newmesh->numtriangles = 0;
1058 memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
1059 memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
1061 newmesh->vertex3f = (float *)data;data += maxverts * sizeof(float[3]);
1064 newmesh->svector3f = (float *)data;data += maxverts * sizeof(float[3]);
1065 newmesh->tvector3f = (float *)data;data += maxverts * sizeof(float[3]);
1066 newmesh->normal3f = (float *)data;data += maxverts * sizeof(float[3]);
1067 newmesh->texcoord2f = (float *)data;data += maxverts * sizeof(float[2]);
1069 newmesh->element3i = (int *)data;data += maxtriangles * sizeof(int[3]);
1072 newmesh->neighbor3i = (int *)data;data += maxtriangles * sizeof(int[3]);
1076 newmesh->vertexhashtable = (shadowmeshvertexhash_t **)data;data += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *);
1077 newmesh->vertexhashentries = (shadowmeshvertexhash_t *)data;data += maxverts * sizeof(shadowmeshvertexhash_t);
1079 if (maxverts <= 65536)
1080 newmesh->element3s = (unsigned short *)data;data += maxtriangles * sizeof(unsigned short[3]);
1084 shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh, int light, int neighbors)
1086 shadowmesh_t *newmesh;
1087 newmesh = Mod_ShadowMesh_Alloc(mempool, oldmesh->numverts, oldmesh->numtriangles, oldmesh->map_diffuse, oldmesh->map_specular, oldmesh->map_normal, light, neighbors, false);
1088 newmesh->numverts = oldmesh->numverts;
1089 newmesh->numtriangles = oldmesh->numtriangles;
1090 memcpy(newmesh->sideoffsets, oldmesh->sideoffsets, sizeof(oldmesh->sideoffsets));
1091 memcpy(newmesh->sidetotals, oldmesh->sidetotals, sizeof(oldmesh->sidetotals));
1093 memcpy(newmesh->vertex3f, oldmesh->vertex3f, oldmesh->numverts * sizeof(float[3]));
1094 if (newmesh->svector3f && oldmesh->svector3f)
1096 memcpy(newmesh->svector3f, oldmesh->svector3f, oldmesh->numverts * sizeof(float[3]));
1097 memcpy(newmesh->tvector3f, oldmesh->tvector3f, oldmesh->numverts * sizeof(float[3]));
1098 memcpy(newmesh->normal3f, oldmesh->normal3f, oldmesh->numverts * sizeof(float[3]));
1099 memcpy(newmesh->texcoord2f, oldmesh->texcoord2f, oldmesh->numverts * sizeof(float[2]));
1101 memcpy(newmesh->element3i, oldmesh->element3i, oldmesh->numtriangles * sizeof(int[3]));
1102 if (newmesh->neighbor3i && oldmesh->neighbor3i)
1103 memcpy(newmesh->neighbor3i, oldmesh->neighbor3i, oldmesh->numtriangles * sizeof(int[3]));
1107 int Mod_ShadowMesh_AddVertex(shadowmesh_t *mesh, float *vertex14f)
1109 int hashindex, vnum;
1110 shadowmeshvertexhash_t *hash;
1111 // this uses prime numbers intentionally
1112 hashindex = (unsigned int) (vertex14f[0] * 2003 + vertex14f[1] * 4001 + vertex14f[2] * 7919) % SHADOWMESHVERTEXHASH;
1113 for (hash = mesh->vertexhashtable[hashindex];hash;hash = hash->next)
1115 vnum = (hash - mesh->vertexhashentries);
1116 if ((mesh->vertex3f == NULL || (mesh->vertex3f[vnum * 3 + 0] == vertex14f[0] && mesh->vertex3f[vnum * 3 + 1] == vertex14f[1] && mesh->vertex3f[vnum * 3 + 2] == vertex14f[2]))
1117 && (mesh->svector3f == NULL || (mesh->svector3f[vnum * 3 + 0] == vertex14f[3] && mesh->svector3f[vnum * 3 + 1] == vertex14f[4] && mesh->svector3f[vnum * 3 + 2] == vertex14f[5]))
1118 && (mesh->tvector3f == NULL || (mesh->tvector3f[vnum * 3 + 0] == vertex14f[6] && mesh->tvector3f[vnum * 3 + 1] == vertex14f[7] && mesh->tvector3f[vnum * 3 + 2] == vertex14f[8]))
1119 && (mesh->normal3f == NULL || (mesh->normal3f[vnum * 3 + 0] == vertex14f[9] && mesh->normal3f[vnum * 3 + 1] == vertex14f[10] && mesh->normal3f[vnum * 3 + 2] == vertex14f[11]))
1120 && (mesh->texcoord2f == NULL || (mesh->texcoord2f[vnum * 2 + 0] == vertex14f[12] && mesh->texcoord2f[vnum * 2 + 1] == vertex14f[13])))
1121 return hash - mesh->vertexhashentries;
1123 vnum = mesh->numverts++;
1124 hash = mesh->vertexhashentries + vnum;
1125 hash->next = mesh->vertexhashtable[hashindex];
1126 mesh->vertexhashtable[hashindex] = hash;
1127 if (mesh->vertex3f) {mesh->vertex3f[vnum * 3 + 0] = vertex14f[0];mesh->vertex3f[vnum * 3 + 1] = vertex14f[1];mesh->vertex3f[vnum * 3 + 2] = vertex14f[2];}
1128 if (mesh->svector3f) {mesh->svector3f[vnum * 3 + 0] = vertex14f[3];mesh->svector3f[vnum * 3 + 1] = vertex14f[4];mesh->svector3f[vnum * 3 + 2] = vertex14f[5];}
1129 if (mesh->tvector3f) {mesh->tvector3f[vnum * 3 + 0] = vertex14f[6];mesh->tvector3f[vnum * 3 + 1] = vertex14f[7];mesh->tvector3f[vnum * 3 + 2] = vertex14f[8];}
1130 if (mesh->normal3f) {mesh->normal3f[vnum * 3 + 0] = vertex14f[9];mesh->normal3f[vnum * 3 + 1] = vertex14f[10];mesh->normal3f[vnum * 3 + 2] = vertex14f[11];}
1131 if (mesh->texcoord2f) {mesh->texcoord2f[vnum * 2 + 0] = vertex14f[12];mesh->texcoord2f[vnum * 2 + 1] = vertex14f[13];}
1135 void Mod_ShadowMesh_AddTriangle(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, float *vertex14f)
1137 if (mesh->numtriangles == 0)
1139 // set the properties on this empty mesh to be more favorable...
1140 // (note: this case only occurs for the first triangle added to a new mesh chain)
1141 mesh->map_diffuse = map_diffuse;
1142 mesh->map_specular = map_specular;
1143 mesh->map_normal = map_normal;
1145 while (mesh->map_diffuse != map_diffuse || mesh->map_specular != map_specular || mesh->map_normal != map_normal || mesh->numverts + 3 > mesh->maxverts || mesh->numtriangles + 1 > mesh->maxtriangles)
1147 if (mesh->next == NULL)
1148 mesh->next = Mod_ShadowMesh_Alloc(mempool, max(mesh->maxverts, 300), max(mesh->maxtriangles, 100), map_diffuse, map_specular, map_normal, mesh->svector3f != NULL, mesh->neighbor3i != NULL, true);
1151 mesh->element3i[mesh->numtriangles * 3 + 0] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 0);
1152 mesh->element3i[mesh->numtriangles * 3 + 1] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 1);
1153 mesh->element3i[mesh->numtriangles * 3 + 2] = Mod_ShadowMesh_AddVertex(mesh, vertex14f + 14 * 2);
1154 mesh->numtriangles++;
1157 void Mod_ShadowMesh_AddMesh(mempool_t *mempool, shadowmesh_t *mesh, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, int numtris, const int *element3i)
1160 float vbuf[3*14], *v;
1161 memset(vbuf, 0, sizeof(vbuf));
1162 for (i = 0;i < numtris;i++)
1164 for (j = 0, v = vbuf;j < 3;j++, v += 14)
1169 v[0] = vertex3f[e * 3 + 0];
1170 v[1] = vertex3f[e * 3 + 1];
1171 v[2] = vertex3f[e * 3 + 2];
1175 v[3] = svector3f[e * 3 + 0];
1176 v[4] = svector3f[e * 3 + 1];
1177 v[5] = svector3f[e * 3 + 2];
1181 v[6] = tvector3f[e * 3 + 0];
1182 v[7] = tvector3f[e * 3 + 1];
1183 v[8] = tvector3f[e * 3 + 2];
1187 v[9] = normal3f[e * 3 + 0];
1188 v[10] = normal3f[e * 3 + 1];
1189 v[11] = normal3f[e * 3 + 2];
1193 v[12] = texcoord2f[e * 2 + 0];
1194 v[13] = texcoord2f[e * 2 + 1];
1197 Mod_ShadowMesh_AddTriangle(mempool, mesh, map_diffuse, map_specular, map_normal, vbuf);
1200 // the triangle calculation can take a while, so let's do a keepalive here
1201 CL_KeepaliveMessage(false);
1204 shadowmesh_t *Mod_ShadowMesh_Begin(mempool_t *mempool, int maxverts, int maxtriangles, rtexture_t *map_diffuse, rtexture_t *map_specular, rtexture_t *map_normal, int light, int neighbors, int expandable)
1206 // the preparation before shadow mesh initialization can take a while, so let's do a keepalive here
1207 CL_KeepaliveMessage(false);
1209 return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles, map_diffuse, map_specular, map_normal, light, neighbors, expandable);
1212 static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh, mempool_t *mempool)
1214 if (!mesh->numverts)
1217 // build r_vertexmesh_t array
1218 // (compressed interleaved array for D3D)
1219 if (!mesh->vertexmesh && mesh->texcoord2f && vid.useinterleavedarrays)
1222 int numvertices = mesh->numverts;
1223 r_vertexmesh_t *vertexmesh;
1224 mesh->vertexmesh = vertexmesh = (r_vertexmesh_t*)Mem_Alloc(mempool, numvertices * sizeof(*mesh->vertexmesh));
1225 for (vertexindex = 0;vertexindex < numvertices;vertexindex++, vertexmesh++)
1227 VectorCopy(mesh->vertex3f + 3*vertexindex, vertexmesh->vertex3f);
1228 VectorScale(mesh->svector3f + 3*vertexindex, 1.0f, vertexmesh->svector3f);
1229 VectorScale(mesh->tvector3f + 3*vertexindex, 1.0f, vertexmesh->tvector3f);
1230 VectorScale(mesh->normal3f + 3*vertexindex, 1.0f, vertexmesh->normal3f);
1231 Vector2Copy(mesh->texcoord2f + 2*vertexindex, vertexmesh->texcoordtexture2f);
1235 // upload short indices as a buffer
1236 if (mesh->element3s && !mesh->element3s_indexbuffer)
1237 mesh->element3s_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3s, mesh->numtriangles * sizeof(short[3]), loadmodel->name, true, false, false, true);
1239 // upload int indices as a buffer
1240 if (mesh->element3i && !mesh->element3i_indexbuffer && !mesh->element3s)
1241 mesh->element3i_indexbuffer = R_Mesh_CreateMeshBuffer(mesh->element3i, mesh->numtriangles * sizeof(int[3]), loadmodel->name, true, false, false, false);
1243 // vertex buffer is several arrays and we put them in the same buffer
1245 // is this wise? the texcoordtexture2f array is used with dynamic
1246 // vertex/svector/tvector/normal when rendering animated models, on the
1247 // other hand animated models don't use a lot of vertices anyway...
1248 if (!mesh->vbo_vertexbuffer && !vid.useinterleavedarrays)
1253 mesh->vbooffset_vertexmesh = size;if (mesh->vertexmesh ) size += mesh->numverts * sizeof(r_vertexmesh_t);
1254 mesh->vbooffset_vertex3f = size;if (mesh->vertex3f ) size += mesh->numverts * sizeof(float[3]);
1255 mesh->vbooffset_svector3f = size;if (mesh->svector3f ) size += mesh->numverts * sizeof(float[3]);
1256 mesh->vbooffset_tvector3f = size;if (mesh->tvector3f ) size += mesh->numverts * sizeof(float[3]);
1257 mesh->vbooffset_normal3f = size;if (mesh->normal3f ) size += mesh->numverts * sizeof(float[3]);
1258 mesh->vbooffset_texcoord2f = size;if (mesh->texcoord2f ) size += mesh->numverts * sizeof(float[2]);
1259 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
1260 if (mesh->vertexmesh ) memcpy(mem + mesh->vbooffset_vertexmesh , mesh->vertexmesh , mesh->numverts * sizeof(r_vertexmesh_t));
1261 if (mesh->vertex3f ) memcpy(mem + mesh->vbooffset_vertex3f , mesh->vertex3f , mesh->numverts * sizeof(float[3]));
1262 if (mesh->svector3f ) memcpy(mem + mesh->vbooffset_svector3f , mesh->svector3f , mesh->numverts * sizeof(float[3]));
1263 if (mesh->tvector3f ) memcpy(mem + mesh->vbooffset_tvector3f , mesh->tvector3f , mesh->numverts * sizeof(float[3]));
1264 if (mesh->normal3f ) memcpy(mem + mesh->vbooffset_normal3f , mesh->normal3f , mesh->numverts * sizeof(float[3]));
1265 if (mesh->texcoord2f ) memcpy(mem + mesh->vbooffset_texcoord2f , mesh->texcoord2f , mesh->numverts * sizeof(float[2]));
1266 mesh->vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, "shadowmesh", false, false, false, false);
1271 shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, qboolean light, qboolean neighbors, qboolean createvbo)
1273 shadowmesh_t *mesh, *newmesh, *nextmesh;
1274 // reallocate meshs to conserve space
1275 for (mesh = firstmesh, firstmesh = NULL;mesh;mesh = nextmesh)
1277 nextmesh = mesh->next;
1278 if (mesh->numverts >= 3 && mesh->numtriangles >= 1)
1280 newmesh = Mod_ShadowMesh_ReAlloc(mempool, mesh, light, neighbors);
1281 newmesh->next = firstmesh;
1282 firstmesh = newmesh;
1283 if (newmesh->element3s)
1286 for (i = 0;i < newmesh->numtriangles*3;i++)
1287 newmesh->element3s[i] = newmesh->element3i[i];
1290 Mod_ShadowMesh_CreateVBOs(newmesh, mempool);
1295 // this can take a while, so let's do a keepalive here
1296 CL_KeepaliveMessage(false);
1301 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *firstmesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius)
1305 vec3_t nmins, nmaxs, ncenter, temp;
1306 float nradius2, dist2, *v;
1310 for (mesh = firstmesh;mesh;mesh = mesh->next)
1312 if (mesh == firstmesh)
1314 VectorCopy(mesh->vertex3f, nmins);
1315 VectorCopy(mesh->vertex3f, nmaxs);
1317 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1319 if (nmins[0] > v[0]) nmins[0] = v[0];if (nmaxs[0] < v[0]) nmaxs[0] = v[0];
1320 if (nmins[1] > v[1]) nmins[1] = v[1];if (nmaxs[1] < v[1]) nmaxs[1] = v[1];
1321 if (nmins[2] > v[2]) nmins[2] = v[2];if (nmaxs[2] < v[2]) nmaxs[2] = v[2];
1324 // calculate center and radius
1325 ncenter[0] = (nmins[0] + nmaxs[0]) * 0.5f;
1326 ncenter[1] = (nmins[1] + nmaxs[1]) * 0.5f;
1327 ncenter[2] = (nmins[2] + nmaxs[2]) * 0.5f;
1329 for (mesh = firstmesh;mesh;mesh = mesh->next)
1331 for (i = 0, v = mesh->vertex3f;i < mesh->numverts;i++, v += 3)
1333 VectorSubtract(v, ncenter, temp);
1334 dist2 = DotProduct(temp, temp);
1335 if (nradius2 < dist2)
1341 VectorCopy(nmins, mins);
1343 VectorCopy(nmaxs, maxs);
1345 VectorCopy(ncenter, center);
1347 *radius = sqrt(nradius2);
1350 void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
1352 shadowmesh_t *nextmesh;
1353 for (;mesh;mesh = nextmesh)
1355 if (mesh->element3i_indexbuffer)
1356 R_Mesh_DestroyMeshBuffer(mesh->element3i_indexbuffer);
1357 if (mesh->element3s_indexbuffer)
1358 R_Mesh_DestroyMeshBuffer(mesh->element3s_indexbuffer);
1359 if (mesh->vbo_vertexbuffer)
1360 R_Mesh_DestroyMeshBuffer(mesh->vbo_vertexbuffer);
1361 nextmesh = mesh->next;
1366 void Mod_CreateCollisionMesh(dp_model_t *mod)
1368 int k, numcollisionmeshtriangles;
1369 qboolean usesinglecollisionmesh = false;
1370 const msurface_t *surface = NULL;
1372 mempool_t *mempool = mod->mempool;
1373 if (!mempool && mod->brush.parentmodel)
1374 mempool = mod->brush.parentmodel->mempool;
1375 // make a single combined collision mesh for physics engine use
1376 // TODO rewrite this to use the collision brushes as source, to fix issues with e.g. common/caulk which creates no drawsurface
1377 numcollisionmeshtriangles = 0;
1378 for (k = 0;k < mod->nummodelsurfaces;k++)
1380 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1381 if (!strcmp(surface->texture->name, "collision") || !strcmp(surface->texture->name, "collisionconvex")) // found collision mesh
1383 usesinglecollisionmesh = true;
1384 numcollisionmeshtriangles = surface->num_triangles;
1387 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1389 numcollisionmeshtriangles += surface->num_triangles;
1391 mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles, NULL, NULL, NULL, false, false, true);
1392 if (usesinglecollisionmesh)
1393 Mod_ShadowMesh_AddMesh(mempool, mod->brush.collisionmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1396 for (k = 0;k < mod->nummodelsurfaces;k++)
1398 surface = mod->data_surfaces + mod->firstmodelsurface + k;
1399 if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
1401 Mod_ShadowMesh_AddMesh(mempool, mod->brush.collisionmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
1404 mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mempool, mod->brush.collisionmesh, false, false, false);
1408 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)
1413 if (ix >= 0 && iy >= 0 && ix < imagewidth && iy < imageheight)
1414 v[2] = (imagepixels[((iy*imagewidth)+ix)*4+0] + imagepixels[((iy*imagewidth)+ix)*4+1] + imagepixels[((iy*imagewidth)+ix)*4+2]) * (1.0f / 765.0f);
1417 Matrix4x4_Transform(pixelstepmatrix, v, vertex3f);
1418 Matrix4x4_Transform(pixeltexturestepmatrix, v, tc);
1419 texcoord2f[0] = tc[0];
1420 texcoord2f[1] = tc[1];
1423 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)
1425 float vup[3], vdown[3], vleft[3], vright[3];
1426 float tcup[3], tcdown[3], tcleft[3], tcright[3];
1427 float sv[3], tv[3], nl[3];
1428 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, pixelstepmatrix, pixeltexturestepmatrix);
1429 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy - 1, vup, tcup, pixelstepmatrix, pixeltexturestepmatrix);
1430 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix, iy + 1, vdown, tcdown, pixelstepmatrix, pixeltexturestepmatrix);
1431 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix - 1, iy, vleft, tcleft, pixelstepmatrix, pixeltexturestepmatrix);
1432 Mod_GetTerrainVertex3fTexCoord2fFromBGRA(imagepixels, imagewidth, imageheight, ix + 1, iy, vright, tcright, pixelstepmatrix, pixeltexturestepmatrix);
1433 Mod_BuildBumpVectors(vertex3f, vup, vright, texcoord2f, tcup, tcright, svector3f, tvector3f, normal3f);
1434 Mod_BuildBumpVectors(vertex3f, vright, vdown, texcoord2f, tcright, tcdown, sv, tv, nl);
1435 VectorAdd(svector3f, sv, svector3f);
1436 VectorAdd(tvector3f, tv, tvector3f);
1437 VectorAdd(normal3f, nl, normal3f);
1438 Mod_BuildBumpVectors(vertex3f, vdown, vleft, texcoord2f, tcdown, tcleft, sv, tv, nl);
1439 VectorAdd(svector3f, sv, svector3f);
1440 VectorAdd(tvector3f, tv, tvector3f);
1441 VectorAdd(normal3f, nl, normal3f);
1442 Mod_BuildBumpVectors(vertex3f, vleft, vup, texcoord2f, tcleft, tcup, sv, tv, nl);
1443 VectorAdd(svector3f, sv, svector3f);
1444 VectorAdd(tvector3f, tv, tvector3f);
1445 VectorAdd(normal3f, nl, normal3f);
1448 static void Mod_ConstructTerrainPatchFromBGRA(const unsigned char *imagepixels, int imagewidth, int imageheight, int x1, int y1, int width, int height, int *element3i, int *neighbor3i, float *vertex3f, float *svector3f, float *tvector3f, float *normal3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
1450 int x, y, ix, iy, *e;
1452 for (y = 0;y < height;y++)
1454 for (x = 0;x < width;x++)
1456 e[0] = (y + 1) * (width + 1) + (x + 0);
1457 e[1] = (y + 0) * (width + 1) + (x + 0);
1458 e[2] = (y + 1) * (width + 1) + (x + 1);
1459 e[3] = (y + 0) * (width + 1) + (x + 0);
1460 e[4] = (y + 0) * (width + 1) + (x + 1);
1461 e[5] = (y + 1) * (width + 1) + (x + 1);
1465 Mod_BuildTriangleNeighbors(neighbor3i, element3i, width*height*2);
1466 for (y = 0, iy = y1;y < height + 1;y++, iy++)
1467 for (x = 0, ix = x1;x < width + 1;x++, ix++, vertex3f += 3, texcoord2f += 2, svector3f += 3, tvector3f += 3, normal3f += 3)
1468 Mod_GetTerrainVertexFromBGRA(imagepixels, imagewidth, imageheight, ix, iy, vertex3f, texcoord2f, svector3f, tvector3f, normal3f, pixelstepmatrix, pixeltexturestepmatrix);
1473 void Mod_Terrain_SurfaceRecurseChunk(dp_model_t *model, int stepsize, int x, int y)
1477 float chunkwidth = min(stepsize, model->terrain.width - 1 - x);
1478 float chunkheight = min(stepsize, model->terrain.height - 1 - y);
1479 float viewvector[3];
1480 unsigned int firstvertex;
1483 if (chunkwidth < 2 || chunkheight < 2)
1485 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]);
1486 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]);
1487 viewvector[0] = bound(mins[0], localvieworigin, maxs[0]) - model->terrain.vieworigin[0];
1488 viewvector[1] = bound(mins[1], localvieworigin, maxs[1]) - model->terrain.vieworigin[1];
1489 viewvector[2] = bound(mins[2], localvieworigin, maxs[2]) - model->terrain.vieworigin[2];
1490 if (stepsize > 1 && VectorLength(viewvector) < stepsize*model->terrain.scale[0]*r_terrain_lodscale.value)
1492 // too close for this stepsize, emit as 4 chunks instead
1494 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y);
1495 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y);
1496 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x, y+stepsize);
1497 Mod_Terrain_SurfaceRecurseChunk(model, stepsize, x+stepsize, y+stepsize);
1500 // emit the geometry at stepsize into our vertex buffer / index buffer
1501 // we add two columns and two rows for skirt
1502 outwidth = chunkwidth+2;
1503 outheight = chunkheight+2;
1504 outwidth2 = outwidth-1;
1505 outheight2 = outheight-1;
1506 outwidth3 = outwidth+1;
1507 outheight3 = outheight+1;
1508 firstvertex = numvertices;
1509 e = model->terrain.element3i + numtriangles;
1510 numtriangles += chunkwidth*chunkheight*2+chunkwidth*2*2+chunkheight*2*2;
1511 v = model->terrain.vertex3f + numvertices;
1512 numvertices += (chunkwidth+1)*(chunkheight+1)+(chunkwidth+1)*2+(chunkheight+1)*2;
1513 // emit the triangles (note: the skirt is treated as two extra rows and two extra columns)
1514 for (ty = 0;ty < outheight;ty++)
1516 for (tx = 0;tx < outwidth;tx++)
1518 *e++ = firstvertex + (ty )*outwidth3+(tx );
1519 *e++ = firstvertex + (ty )*outwidth3+(tx+1);
1520 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1521 *e++ = firstvertex + (ty )*outwidth3+(tx );
1522 *e++ = firstvertex + (ty+1)*outwidth3+(tx+1);
1523 *e++ = firstvertex + (ty+1)*outwidth3+(tx );
1526 // TODO: emit surface vertices (x+tx*stepsize, y+ty*stepsize)
1527 for (ty = 0;ty <= outheight;ty++)
1529 skirtrow = ty == 0 || ty == outheight;
1530 ry = y+bound(1, ty, outheight)*stepsize;
1531 for (tx = 0;tx <= outwidth;tx++)
1533 skirt = skirtrow || tx == 0 || tx == outwidth;
1534 rx = x+bound(1, tx, outwidth)*stepsize;
1537 v[2] = heightmap[ry*terrainwidth+rx]*scale[2];
1541 // TODO: emit skirt vertices
1544 void Mod_Terrain_UpdateSurfacesForViewOrigin(dp_model_t *model)
1546 for (y = 0;y < model->terrain.size[1];y += model->terrain.
1547 Mod_Terrain_SurfaceRecurseChunk(model, model->terrain.maxstepsize, x, y);
1548 Mod_Terrain_BuildChunk(model,
1552 static int Mod_LoadQ3Shaders_EnumerateWaveFunc(const char *s)
1555 if (!strncasecmp(s, "user", 4)) // parse stuff like "user1sin", always user<n>func
1557 offset = bound(0, s[4] - '0', 9);
1558 offset = (offset + 1) << Q3WAVEFUNC_USER_SHIFT;
1563 if (!strcasecmp(s, "sin")) return offset | Q3WAVEFUNC_SIN;
1564 if (!strcasecmp(s, "square")) return offset | Q3WAVEFUNC_SQUARE;
1565 if (!strcasecmp(s, "triangle")) return offset | Q3WAVEFUNC_TRIANGLE;
1566 if (!strcasecmp(s, "sawtooth")) return offset | Q3WAVEFUNC_SAWTOOTH;
1567 if (!strcasecmp(s, "inversesawtooth")) return offset | Q3WAVEFUNC_INVERSESAWTOOTH;
1568 if (!strcasecmp(s, "noise")) return offset | Q3WAVEFUNC_NOISE;
1569 if (!strcasecmp(s, "none")) return offset | Q3WAVEFUNC_NONE;
1570 Con_DPrintf("Mod_LoadQ3Shaders: unknown wavefunc %s\n", s);
1571 return offset | Q3WAVEFUNC_NONE;
1574 void Mod_FreeQ3Shaders(void)
1576 Mem_FreePool(&q3shaders_mem);
1579 static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
1581 unsigned short hash = CRC_Block_CaseInsensitive ((const unsigned char *)shader->name, strlen (shader->name));
1582 q3shader_hash_entry_t* entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
1583 q3shader_hash_entry_t* lastEntry = NULL;
1586 if (strcasecmp (entry->shader.name, shader->name) == 0)
1589 if(shader->dpshaderkill)
1591 // killed shader is a redeclarion? we can safely ignore it
1594 else if(entry->shader.dpshaderkill)
1596 // replace the old shader!
1597 // this will skip the entry allocating part
1598 // below and just replace the shader
1603 unsigned char *start, *end, *start2;
1604 start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
1605 end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
1606 start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
1607 if(memcmp(start, start2, end - start))
1608 Con_DPrintf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
1610 Con_DPrintf("Shader '%s' already defined\n", shader->name);
1615 entry = entry->chain;
1617 while (entry != NULL);
1620 if (lastEntry->shader.name[0] != 0)
1623 q3shader_hash_entry_t* newEntry = (q3shader_hash_entry_t*)
1624 Mem_ExpandableArray_AllocRecord (&q3shader_data->hash_entries);
1626 while (lastEntry->chain != NULL) lastEntry = lastEntry->chain;
1627 lastEntry->chain = newEntry;
1628 newEntry->chain = NULL;
1629 lastEntry = newEntry;
1631 /* else: head of chain, in hash entry array */
1634 memcpy (&entry->shader, shader, sizeof (q3shaderinfo_t));
1637 extern cvar_t mod_noshader_default_offsetmapping;
1638 extern cvar_t mod_q3shader_default_offsetmapping;
1639 extern cvar_t mod_q3shader_default_offsetmapping_scale;
1640 extern cvar_t mod_q3shader_default_offsetmapping_bias;
1641 extern cvar_t mod_q3shader_default_polygonoffset;
1642 extern cvar_t mod_q3shader_default_polygonfactor;
1643 extern cvar_t mod_q3shader_force_addalpha;
1644 extern cvar_t mod_q3shader_force_terrain_alphaflag;
1645 void Mod_LoadQ3Shaders(void)
1652 q3shaderinfo_t shader;
1653 q3shaderinfo_layer_t *layer;
1655 char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
1656 char *custsurfaceparmnames[256]; // VorteX: q3map2 has 64 but well, someone will need more
1657 unsigned long custsurfaceflags[256];
1658 int numcustsurfaceflags;
1659 qboolean dpshaderkill;
1661 Mod_FreeQ3Shaders();
1663 q3shaders_mem = Mem_AllocPool("q3shaders", 0, NULL);
1664 q3shader_data = (q3shader_data_t*)Mem_Alloc (q3shaders_mem,
1665 sizeof (q3shader_data_t));
1666 Mem_ExpandableArray_NewArray (&q3shader_data->hash_entries,
1667 q3shaders_mem, sizeof (q3shader_hash_entry_t), 256);
1668 Mem_ExpandableArray_NewArray (&q3shader_data->char_ptrs,
1669 q3shaders_mem, sizeof (char**), 256);
1671 // parse custinfoparms.txt
1672 numcustsurfaceflags = 0;
1673 if ((text = f = (char *)FS_LoadFile("scripts/custinfoparms.txt", tempmempool, false, NULL)) != NULL)
1675 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1676 Con_DPrintf("scripts/custinfoparms.txt: contentflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1679 while (COM_ParseToken_QuakeC(&text, false))
1680 if (!strcasecmp(com_token, "}"))
1682 // custom surfaceflags section
1683 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1684 Con_DPrintf("scripts/custinfoparms.txt: surfaceflags section parsing error - expected \"{\", found \"%s\"\n", com_token);
1687 while(COM_ParseToken_QuakeC(&text, false))
1689 if (!strcasecmp(com_token, "}"))
1691 // register surfaceflag
1692 if (numcustsurfaceflags >= 256)
1694 Con_Printf("scripts/custinfoparms.txt: surfaceflags section parsing error - max 256 surfaceflags exceeded\n");
1698 j = (int)strlen(com_token)+1;
1699 custsurfaceparmnames[numcustsurfaceflags] = (char *)Mem_Alloc(tempmempool, j);
1700 strlcpy(custsurfaceparmnames[numcustsurfaceflags], com_token, j+1);
1702 if (COM_ParseToken_QuakeC(&text, false))
1703 custsurfaceflags[numcustsurfaceflags] = strtol(com_token, NULL, 0);
1705 custsurfaceflags[numcustsurfaceflags] = 0;
1706 numcustsurfaceflags++;
1714 search = FS_Search("scripts/*.shader", true, false);
1717 for (fileindex = 0;fileindex < search->numfilenames;fileindex++)
1719 text = f = (char *)FS_LoadFile(search->filenames[fileindex], tempmempool, false, NULL);
1722 while (COM_ParseToken_QuakeC(&text, false))
1724 memset (&shader, 0, sizeof(shader));
1726 shader.surfaceparms = 0;
1727 shader.surfaceflags = 0;
1728 shader.textureflags = 0;
1729 shader.numlayers = 0;
1730 shader.lighting = false;
1731 shader.vertexalpha = false;
1732 shader.textureblendalpha = false;
1733 shader.skyboxname[0] = 0;
1734 shader.deforms[0].deform = Q3DEFORM_NONE;
1735 shader.dpnortlight = false;
1736 shader.dpshadow = false;
1737 shader.dpnoshadow = false;
1738 shader.dpmeshcollisions = false;
1739 shader.dpshaderkill = false;
1740 shader.dpreflectcube[0] = 0;
1741 shader.reflectmin = 0;
1742 shader.reflectmax = 1;
1743 shader.refractfactor = 1;
1744 Vector4Set(shader.refractcolor4f, 1, 1, 1, 1);
1745 shader.reflectfactor = 1;
1746 Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
1747 shader.r_water_wateralpha = 1;
1748 shader.r_water_waterscroll[0] = 0;
1749 shader.r_water_waterscroll[1] = 0;
1750 shader.offsetmapping = (mod_q3shader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
1751 shader.offsetscale = mod_q3shader_default_offsetmapping_scale.value;
1752 shader.offsetbias = mod_q3shader_default_offsetmapping_bias.value;
1753 shader.biaspolygonoffset = mod_q3shader_default_polygonoffset.value;
1754 shader.biaspolygonfactor = mod_q3shader_default_polygonfactor.value;
1755 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
1756 shader.specularscalemod = 1;
1757 shader.specularpowermod = 1;
1758 shader.rtlightambient = 0;
1759 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
1760 // JUST GREP FOR "specularscalemod = 1".
1762 strlcpy(shader.name, com_token, sizeof(shader.name));
1763 if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
1765 Con_DPrintf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[fileindex], com_token);
1768 while (COM_ParseToken_QuakeC(&text, false))
1770 if (!strcasecmp(com_token, "}"))
1772 if (!strcasecmp(com_token, "{"))
1774 static q3shaderinfo_layer_t dummy;
1775 if (shader.numlayers < Q3SHADER_MAXLAYERS)
1777 layer = shader.layers + shader.numlayers++;
1781 // parse and process it anyway, just don't store it (so a map $lightmap or such stuff still is found)
1782 memset(&dummy, 0, sizeof(dummy));
1785 layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1786 layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1787 layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1788 layer->blendfunc[0] = GL_ONE;
1789 layer->blendfunc[1] = GL_ZERO;
1790 while (COM_ParseToken_QuakeC(&text, false))
1792 if (!strcasecmp(com_token, "}"))
1794 if (!strcasecmp(com_token, "\n"))
1797 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
1799 if (j < TEXTURE_MAXFRAMES + 4)
1801 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
1802 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
1803 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
1805 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
1806 numparameters = j + 1;
1808 if (!COM_ParseToken_QuakeC(&text, true))
1811 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
1812 // parameter[j][0] = 0;
1813 if (developer_insane.integer)
1815 Con_DPrintf("%s %i: ", shader.name, shader.numlayers - 1);
1816 for (j = 0;j < numparameters;j++)
1817 Con_DPrintf(" %s", parameter[j]);
1820 if (numparameters >= 2 && !strcasecmp(parameter[0], "blendfunc"))
1822 if (numparameters == 2)
1824 if (!strcasecmp(parameter[1], "add"))
1826 layer->blendfunc[0] = GL_ONE;
1827 layer->blendfunc[1] = GL_ONE;
1829 else if (!strcasecmp(parameter[1], "addalpha"))
1831 layer->blendfunc[0] = GL_SRC_ALPHA;
1832 layer->blendfunc[1] = GL_ONE;
1834 else if (!strcasecmp(parameter[1], "filter"))
1836 layer->blendfunc[0] = GL_DST_COLOR;
1837 layer->blendfunc[1] = GL_ZERO;
1839 else if (!strcasecmp(parameter[1], "blend"))
1841 layer->blendfunc[0] = GL_SRC_ALPHA;
1842 layer->blendfunc[1] = GL_ONE_MINUS_SRC_ALPHA;
1845 else if (numparameters == 3)
1848 for (k = 0;k < 2;k++)
1850 if (!strcasecmp(parameter[k+1], "GL_ONE"))
1851 layer->blendfunc[k] = GL_ONE;
1852 else if (!strcasecmp(parameter[k+1], "GL_ZERO"))
1853 layer->blendfunc[k] = GL_ZERO;
1854 else if (!strcasecmp(parameter[k+1], "GL_SRC_COLOR"))
1855 layer->blendfunc[k] = GL_SRC_COLOR;
1856 else if (!strcasecmp(parameter[k+1], "GL_SRC_ALPHA"))
1857 layer->blendfunc[k] = GL_SRC_ALPHA;
1858 else if (!strcasecmp(parameter[k+1], "GL_DST_COLOR"))
1859 layer->blendfunc[k] = GL_DST_COLOR;
1860 else if (!strcasecmp(parameter[k+1], "GL_DST_ALPHA"))
1861 layer->blendfunc[k] = GL_DST_ALPHA;
1862 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_COLOR"))
1863 layer->blendfunc[k] = GL_ONE_MINUS_SRC_COLOR;
1864 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_SRC_ALPHA"))
1865 layer->blendfunc[k] = GL_ONE_MINUS_SRC_ALPHA;
1866 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_COLOR"))
1867 layer->blendfunc[k] = GL_ONE_MINUS_DST_COLOR;
1868 else if (!strcasecmp(parameter[k+1], "GL_ONE_MINUS_DST_ALPHA"))
1869 layer->blendfunc[k] = GL_ONE_MINUS_DST_ALPHA;
1871 layer->blendfunc[k] = GL_ONE; // default in case of parsing error
1875 if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
1876 layer->alphatest = true;
1877 if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
1879 if (!strcasecmp(parameter[0], "clampmap"))
1880 layer->clampmap = true;
1881 layer->numframes = 1;
1882 layer->framerate = 1;
1883 layer->texturename = (char**)Mem_ExpandableArray_AllocRecord (
1884 &q3shader_data->char_ptrs);
1885 layer->texturename[0] = Mem_strdup (q3shaders_mem, parameter[1]);
1886 if (!strcasecmp(parameter[1], "$lightmap"))
1887 shader.lighting = true;
1889 else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
1892 layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
1893 layer->framerate = atof(parameter[1]);
1894 layer->texturename = (char **) Mem_Alloc (q3shaders_mem, sizeof (char*) * layer->numframes);
1895 for (i = 0;i < layer->numframes;i++)
1896 layer->texturename[i] = Mem_strdup (q3shaders_mem, parameter[i + 2]);
1898 else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen"))
1901 for (i = 0;i < numparameters - 2 && i < Q3RGBGEN_MAXPARMS;i++)
1902 layer->rgbgen.parms[i] = atof(parameter[i+2]);
1903 if (!strcasecmp(parameter[1], "identity")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
1904 else if (!strcasecmp(parameter[1], "const")) layer->rgbgen.rgbgen = Q3RGBGEN_CONST;
1905 else if (!strcasecmp(parameter[1], "entity")) layer->rgbgen.rgbgen = Q3RGBGEN_ENTITY;
1906 else if (!strcasecmp(parameter[1], "exactvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_EXACTVERTEX;
1907 else if (!strcasecmp(parameter[1], "identitylighting")) layer->rgbgen.rgbgen = Q3RGBGEN_IDENTITYLIGHTING;
1908 else if (!strcasecmp(parameter[1], "lightingdiffuse")) layer->rgbgen.rgbgen = Q3RGBGEN_LIGHTINGDIFFUSE;
1909 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSENTITY;
1910 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->rgbgen.rgbgen = Q3RGBGEN_ONEMINUSVERTEX;
1911 else if (!strcasecmp(parameter[1], "vertex")) layer->rgbgen.rgbgen = Q3RGBGEN_VERTEX;
1912 else if (!strcasecmp(parameter[1], "wave"))
1914 layer->rgbgen.rgbgen = Q3RGBGEN_WAVE;
1915 layer->rgbgen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1916 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1917 layer->rgbgen.waveparms[i] = atof(parameter[i+3]);
1919 else Con_DPrintf("%s parsing warning: unknown rgbgen %s\n", search->filenames[fileindex], parameter[1]);
1921 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen"))
1924 for (i = 0;i < numparameters - 2 && i < Q3ALPHAGEN_MAXPARMS;i++)
1925 layer->alphagen.parms[i] = atof(parameter[i+2]);
1926 if (!strcasecmp(parameter[1], "identity")) layer->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
1927 else if (!strcasecmp(parameter[1], "const")) layer->alphagen.alphagen = Q3ALPHAGEN_CONST;
1928 else if (!strcasecmp(parameter[1], "entity")) layer->alphagen.alphagen = Q3ALPHAGEN_ENTITY;
1929 else if (!strcasecmp(parameter[1], "lightingspecular")) layer->alphagen.alphagen = Q3ALPHAGEN_LIGHTINGSPECULAR;
1930 else if (!strcasecmp(parameter[1], "oneminusentity")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSENTITY;
1931 else if (!strcasecmp(parameter[1], "oneminusvertex")) layer->alphagen.alphagen = Q3ALPHAGEN_ONEMINUSVERTEX;
1932 else if (!strcasecmp(parameter[1], "portal")) layer->alphagen.alphagen = Q3ALPHAGEN_PORTAL;
1933 else if (!strcasecmp(parameter[1], "vertex")) layer->alphagen.alphagen = Q3ALPHAGEN_VERTEX;
1934 else if (!strcasecmp(parameter[1], "wave"))
1936 layer->alphagen.alphagen = Q3ALPHAGEN_WAVE;
1937 layer->alphagen.wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1938 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1939 layer->alphagen.waveparms[i] = atof(parameter[i+3]);
1941 else Con_DPrintf("%s parsing warning: unknown alphagen %s\n", search->filenames[fileindex], parameter[1]);
1943 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "texgen") || !strcasecmp(parameter[0], "tcgen")))
1946 // observed values: tcgen environment
1947 // no other values have been observed in real shaders
1948 for (i = 0;i < numparameters - 2 && i < Q3TCGEN_MAXPARMS;i++)
1949 layer->tcgen.parms[i] = atof(parameter[i+2]);
1950 if (!strcasecmp(parameter[1], "base")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1951 else if (!strcasecmp(parameter[1], "texture")) layer->tcgen.tcgen = Q3TCGEN_TEXTURE;
1952 else if (!strcasecmp(parameter[1], "environment")) layer->tcgen.tcgen = Q3TCGEN_ENVIRONMENT;
1953 else if (!strcasecmp(parameter[1], "lightmap")) layer->tcgen.tcgen = Q3TCGEN_LIGHTMAP;
1954 else if (!strcasecmp(parameter[1], "vector")) layer->tcgen.tcgen = Q3TCGEN_VECTOR;
1955 else Con_DPrintf("%s parsing warning: unknown tcgen mode %s\n", search->filenames[fileindex], parameter[1]);
1957 else if (numparameters >= 2 && !strcasecmp(parameter[0], "tcmod"))
1964 // tcmod stretch sin # # # #
1965 // tcmod stretch triangle # # # #
1966 // tcmod transform # # # # # #
1967 // tcmod turb # # # #
1968 // tcmod turb sin # # # # (this is bogus)
1969 // no other values have been observed in real shaders
1970 for (tcmodindex = 0;tcmodindex < Q3MAXTCMODS;tcmodindex++)
1971 if (!layer->tcmods[tcmodindex].tcmod)
1973 if (tcmodindex < Q3MAXTCMODS)
1975 for (i = 0;i < numparameters - 2 && i < Q3TCMOD_MAXPARMS;i++)
1976 layer->tcmods[tcmodindex].parms[i] = atof(parameter[i+2]);
1977 if (!strcasecmp(parameter[1], "entitytranslate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ENTITYTRANSLATE;
1978 else if (!strcasecmp(parameter[1], "rotate")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_ROTATE;
1979 else if (!strcasecmp(parameter[1], "scale")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCALE;
1980 else if (!strcasecmp(parameter[1], "scroll")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_SCROLL;
1981 else if (!strcasecmp(parameter[1], "page")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_PAGE;
1982 else if (!strcasecmp(parameter[1], "stretch"))
1984 layer->tcmods[tcmodindex].tcmod = Q3TCMOD_STRETCH;
1985 layer->tcmods[tcmodindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[2]);
1986 for (i = 0;i < numparameters - 3 && i < Q3WAVEPARMS;i++)
1987 layer->tcmods[tcmodindex].waveparms[i] = atof(parameter[i+3]);
1989 else if (!strcasecmp(parameter[1], "transform")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TRANSFORM;
1990 else if (!strcasecmp(parameter[1], "turb")) layer->tcmods[tcmodindex].tcmod = Q3TCMOD_TURBULENT;
1991 else Con_DPrintf("%s parsing warning: unknown tcmod mode %s\n", search->filenames[fileindex], parameter[1]);
1994 Con_DPrintf("%s parsing warning: too many tcmods on one layer\n", search->filenames[fileindex]);
1996 // break out a level if it was a closing brace (not using the character here to not confuse vim)
1997 if (!strcasecmp(com_token, "}"))
2000 if (layer->rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE || layer->rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2001 shader.lighting = true;
2002 if (layer->alphagen.alphagen == Q3ALPHAGEN_VERTEX)
2004 if (layer == shader.layers + 0)
2006 // vertex controlled transparency
2007 shader.vertexalpha = true;
2011 // multilayer terrain shader or similar
2012 shader.textureblendalpha = true;
2013 if (mod_q3shader_force_terrain_alphaflag.integer)
2014 shader.layers[0].dptexflags |= TEXF_ALPHA;
2018 if(mod_q3shader_force_addalpha.integer)
2020 // for a long while, DP treated GL_ONE GL_ONE as GL_SRC_ALPHA GL_ONE
2021 // this cvar brings back this behaviour
2022 if(layer->blendfunc[0] == GL_ONE && layer->blendfunc[1] == GL_ONE)
2023 layer->blendfunc[0] = GL_SRC_ALPHA;
2026 layer->dptexflags = 0;
2027 if (layer->alphatest)
2028 layer->dptexflags |= TEXF_ALPHA;
2029 switch(layer->blendfunc[0])
2032 case GL_ONE_MINUS_SRC_ALPHA:
2033 layer->dptexflags |= TEXF_ALPHA;
2036 switch(layer->blendfunc[1])
2039 case GL_ONE_MINUS_SRC_ALPHA:
2040 layer->dptexflags |= TEXF_ALPHA;
2043 if (!(shader.surfaceparms & Q3SURFACEPARM_NOMIPMAPS))
2044 layer->dptexflags |= TEXF_MIPMAP;
2045 if (!(shader.textureflags & Q3TEXTUREFLAG_NOPICMIP))
2046 layer->dptexflags |= TEXF_PICMIP | TEXF_COMPRESS;
2047 if (layer->clampmap)
2048 layer->dptexflags |= TEXF_CLAMP;
2052 for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
2054 if (j < TEXTURE_MAXFRAMES + 4)
2056 // remap dp_water to dpwater, dp_reflect to dpreflect, etc.
2057 if(j == 0 && !strncasecmp(com_token, "dp_", 3))
2058 dpsnprintf(parameter[j], sizeof(parameter[j]), "dp%s", &com_token[3]);
2060 strlcpy(parameter[j], com_token, sizeof(parameter[j]));
2061 numparameters = j + 1;
2063 if (!COM_ParseToken_QuakeC(&text, true))
2066 //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
2067 // parameter[j][0] = 0;
2068 if (fileindex == 0 && !strcasecmp(com_token, "}"))
2070 if (developer_insane.integer)
2072 Con_DPrintf("%s: ", shader.name);
2073 for (j = 0;j < numparameters;j++)
2074 Con_DPrintf(" %s", parameter[j]);
2077 if (numparameters < 1)
2079 if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
2081 if (!strcasecmp(parameter[1], "alphashadow"))
2082 shader.surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
2083 else if (!strcasecmp(parameter[1], "areaportal"))
2084 shader.surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
2085 else if (!strcasecmp(parameter[1], "botclip"))
2086 shader.surfaceparms |= Q3SURFACEPARM_BOTCLIP;
2087 else if (!strcasecmp(parameter[1], "clusterportal"))
2088 shader.surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
2089 else if (!strcasecmp(parameter[1], "detail"))
2090 shader.surfaceparms |= Q3SURFACEPARM_DETAIL;
2091 else if (!strcasecmp(parameter[1], "donotenter"))
2092 shader.surfaceparms |= Q3SURFACEPARM_DONOTENTER;
2093 else if (!strcasecmp(parameter[1], "dust"))
2094 shader.surfaceparms |= Q3SURFACEPARM_DUST;
2095 else if (!strcasecmp(parameter[1], "hint"))
2096 shader.surfaceparms |= Q3SURFACEPARM_HINT;
2097 else if (!strcasecmp(parameter[1], "fog"))
2098 shader.surfaceparms |= Q3SURFACEPARM_FOG;
2099 else if (!strcasecmp(parameter[1], "lava"))
2100 shader.surfaceparms |= Q3SURFACEPARM_LAVA;
2101 else if (!strcasecmp(parameter[1], "lightfilter"))
2102 shader.surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
2103 else if (!strcasecmp(parameter[1], "lightgrid"))
2104 shader.surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
2105 else if (!strcasecmp(parameter[1], "metalsteps"))
2106 shader.surfaceparms |= Q3SURFACEPARM_METALSTEPS;
2107 else if (!strcasecmp(parameter[1], "nodamage"))
2108 shader.surfaceparms |= Q3SURFACEPARM_NODAMAGE;
2109 else if (!strcasecmp(parameter[1], "nodlight"))
2110 shader.surfaceparms |= Q3SURFACEPARM_NODLIGHT;
2111 else if (!strcasecmp(parameter[1], "nodraw"))
2112 shader.surfaceparms |= Q3SURFACEPARM_NODRAW;
2113 else if (!strcasecmp(parameter[1], "nodrop"))
2114 shader.surfaceparms |= Q3SURFACEPARM_NODROP;
2115 else if (!strcasecmp(parameter[1], "noimpact"))
2116 shader.surfaceparms |= Q3SURFACEPARM_NOIMPACT;
2117 else if (!strcasecmp(parameter[1], "nolightmap"))
2118 shader.surfaceparms |= Q3SURFACEPARM_NOLIGHTMAP;
2119 else if (!strcasecmp(parameter[1], "nomarks"))
2120 shader.surfaceparms |= Q3SURFACEPARM_NOMARKS;
2121 else if (!strcasecmp(parameter[1], "nomipmaps"))
2122 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2123 else if (!strcasecmp(parameter[1], "nonsolid"))
2124 shader.surfaceparms |= Q3SURFACEPARM_NONSOLID;
2125 else if (!strcasecmp(parameter[1], "origin"))
2126 shader.surfaceparms |= Q3SURFACEPARM_ORIGIN;
2127 else if (!strcasecmp(parameter[1], "playerclip"))
2128 shader.surfaceparms |= Q3SURFACEPARM_PLAYERCLIP;
2129 else if (!strcasecmp(parameter[1], "sky"))
2130 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2131 else if (!strcasecmp(parameter[1], "slick"))
2132 shader.surfaceparms |= Q3SURFACEPARM_SLICK;
2133 else if (!strcasecmp(parameter[1], "slime"))
2134 shader.surfaceparms |= Q3SURFACEPARM_SLIME;
2135 else if (!strcasecmp(parameter[1], "structural"))
2136 shader.surfaceparms |= Q3SURFACEPARM_STRUCTURAL;
2137 else if (!strcasecmp(parameter[1], "trans"))
2138 shader.surfaceparms |= Q3SURFACEPARM_TRANS;
2139 else if (!strcasecmp(parameter[1], "water"))
2140 shader.surfaceparms |= Q3SURFACEPARM_WATER;
2141 else if (!strcasecmp(parameter[1], "pointlight"))
2142 shader.surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
2143 else if (!strcasecmp(parameter[1], "antiportal"))
2144 shader.surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
2145 else if (!strcasecmp(parameter[1], "skip"))
2146 ; // shader.surfaceparms |= Q3SURFACEPARM_SKIP; FIXME we don't have enough #defines for this any more, and the engine doesn't need this one anyway
2149 // try custom surfaceparms
2150 for (j = 0; j < numcustsurfaceflags; j++)
2152 if (!strcasecmp(custsurfaceparmnames[j], parameter[1]))
2154 shader.surfaceflags |= custsurfaceflags[j];
2159 if (j == numcustsurfaceflags)
2160 Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
2163 else if (!strcasecmp(parameter[0], "dpshadow"))
2164 shader.dpshadow = true;
2165 else if (!strcasecmp(parameter[0], "dpnoshadow"))
2166 shader.dpnoshadow = true;
2167 else if (!strcasecmp(parameter[0], "dpnortlight"))
2168 shader.dpnortlight = true;
2169 else if (!strcasecmp(parameter[0], "dpreflectcube"))
2170 strlcpy(shader.dpreflectcube, parameter[1], sizeof(shader.dpreflectcube));
2171 else if (!strcasecmp(parameter[0], "dpmeshcollisions"))
2172 shader.dpmeshcollisions = true;
2173 // this sets dpshaderkill to true if dpshaderkillifcvarzero was used, and to false if dpnoshaderkillifcvarzero was used
2174 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvarzero")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvarzero")) && numparameters >= 2)
2176 if (Cvar_VariableValue(parameter[1]) == 0.0f)
2177 shader.dpshaderkill = dpshaderkill;
2179 // this sets dpshaderkill to true if dpshaderkillifcvar was used, and to false if dpnoshaderkillifcvar was used
2180 else if (((dpshaderkill = !strcasecmp(parameter[0], "dpshaderkillifcvar")) || !strcasecmp(parameter[0], "dpnoshaderkillifcvar")) && numparameters >= 2)
2182 const char *op = NULL;
2183 if (numparameters >= 3)
2187 if (Cvar_VariableValue(parameter[1]) != 0.0f)
2188 shader.dpshaderkill = dpshaderkill;
2190 else if (numparameters >= 4 && !strcmp(op, "=="))
2192 if (Cvar_VariableValue(parameter[1]) == atof(parameter[3]))
2193 shader.dpshaderkill = dpshaderkill;
2195 else if (numparameters >= 4 && !strcmp(op, "!="))
2197 if (Cvar_VariableValue(parameter[1]) != atof(parameter[3]))
2198 shader.dpshaderkill = dpshaderkill;
2200 else if (numparameters >= 4 && !strcmp(op, ">"))
2202 if (Cvar_VariableValue(parameter[1]) > atof(parameter[3]))
2203 shader.dpshaderkill = dpshaderkill;
2205 else if (numparameters >= 4 && !strcmp(op, "<"))
2207 if (Cvar_VariableValue(parameter[1]) < atof(parameter[3]))
2208 shader.dpshaderkill = dpshaderkill;
2210 else if (numparameters >= 4 && !strcmp(op, ">="))
2212 if (Cvar_VariableValue(parameter[1]) >= atof(parameter[3]))
2213 shader.dpshaderkill = dpshaderkill;
2215 else if (numparameters >= 4 && !strcmp(op, "<="))
2217 if (Cvar_VariableValue(parameter[1]) <= atof(parameter[3]))
2218 shader.dpshaderkill = dpshaderkill;
2222 Con_DPrintf("%s parsing warning: unknown dpshaderkillifcvar op \"%s\", or not enough arguments\n", search->filenames[fileindex], op);
2225 else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
2227 // some q3 skies don't have the sky parm set
2228 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2229 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2231 else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
2233 // some q3 skies don't have the sky parm set
2234 shader.surfaceparms |= Q3SURFACEPARM_SKY;
2235 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
2236 strlcpy(shader.skyboxname, parameter[1], sizeof(shader.skyboxname));
2238 else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
2240 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
2241 shader.textureflags |= Q3TEXTUREFLAG_TWOSIDED;
2243 else if (!strcasecmp(parameter[0], "nomipmaps"))
2244 shader.surfaceparms |= Q3SURFACEPARM_NOMIPMAPS;
2245 else if (!strcasecmp(parameter[0], "nopicmip"))
2246 shader.textureflags |= Q3TEXTUREFLAG_NOPICMIP;
2247 else if (!strcasecmp(parameter[0], "polygonoffset"))
2248 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2249 else if (!strcasecmp(parameter[0], "dppolygonoffset"))
2251 shader.textureflags |= Q3TEXTUREFLAG_POLYGONOFFSET;
2252 if(numparameters >= 2)
2254 shader.biaspolygonfactor = atof(parameter[1]);
2255 if(numparameters >= 3)
2256 shader.biaspolygonoffset = atof(parameter[2]);
2258 shader.biaspolygonoffset = 0;
2261 else if (!strcasecmp(parameter[0], "dptransparentsort") && numparameters >= 2)
2263 shader.textureflags |= Q3TEXTUREFLAG_TRANSPARENTSORT;
2264 if (!strcasecmp(parameter[1], "sky"))
2265 shader.transparentsort = TRANSPARENTSORT_SKY;
2266 else if (!strcasecmp(parameter[1], "distance"))
2267 shader.transparentsort = TRANSPARENTSORT_DISTANCE;
2268 else if (!strcasecmp(parameter[1], "hud"))
2269 shader.transparentsort = TRANSPARENTSORT_HUD;
2271 Con_DPrintf("%s parsing warning: unknown dptransparentsort category \"%s\", or not enough arguments\n", search->filenames[fileindex], parameter[1]);
2273 else if (!strcasecmp(parameter[0], "dprefract") && numparameters >= 5)
2275 shader.textureflags |= Q3TEXTUREFLAG_REFRACTION;
2276 shader.refractfactor = atof(parameter[1]);
2277 Vector4Set(shader.refractcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), 1);
2279 else if (!strcasecmp(parameter[0], "dpreflect") && numparameters >= 6)
2281 shader.textureflags |= Q3TEXTUREFLAG_REFLECTION;
2282 shader.reflectfactor = atof(parameter[1]);
2283 Vector4Set(shader.reflectcolor4f, atof(parameter[2]), atof(parameter[3]), atof(parameter[4]), atof(parameter[5]));
2285 else if (!strcasecmp(parameter[0], "dpcamera"))
2287 shader.textureflags |= Q3TEXTUREFLAG_CAMERA;
2289 else if (!strcasecmp(parameter[0], "dpwater") && numparameters >= 12)
2291 shader.textureflags |= Q3TEXTUREFLAG_WATERSHADER;
2292 shader.reflectmin = atof(parameter[1]);
2293 shader.reflectmax = atof(parameter[2]);
2294 shader.refractfactor = atof(parameter[3]);
2295 shader.reflectfactor = atof(parameter[4]);
2296 Vector4Set(shader.refractcolor4f, atof(parameter[5]), atof(parameter[6]), atof(parameter[7]), 1);
2297 Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
2298 shader.r_water_wateralpha = atof(parameter[11]);
2300 else if (!strcasecmp(parameter[0], "dpwaterscroll") && numparameters >= 3)
2302 shader.r_water_waterscroll[0] = 1/atof(parameter[1]);
2303 shader.r_water_waterscroll[1] = 1/atof(parameter[2]);
2305 else if (!strcasecmp(parameter[0], "dpglossintensitymod") && numparameters >= 2)
2307 shader.specularscalemod = atof(parameter[1]);
2309 else if (!strcasecmp(parameter[0], "dpglossexponentmod") && numparameters >= 2)
2311 shader.specularpowermod = atof(parameter[1]);
2313 else if (!strcasecmp(parameter[0], "dprtlightambient") && numparameters >= 2)
2315 shader.rtlightambient = atof(parameter[1]);
2317 else if (!strcasecmp(parameter[0], "dpoffsetmapping") && numparameters >= 2)
2319 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "off"))
2320 shader.offsetmapping = OFFSETMAPPING_OFF;
2321 else if (!strcasecmp(parameter[1], "default") || !strcasecmp(parameter[1], "normal"))
2322 shader.offsetmapping = OFFSETMAPPING_DEFAULT;
2323 else if (!strcasecmp(parameter[1], "linear"))
2324 shader.offsetmapping = OFFSETMAPPING_LINEAR;
2325 else if (!strcasecmp(parameter[1], "relief"))
2326 shader.offsetmapping = OFFSETMAPPING_RELIEF;
2327 if (numparameters >= 3)
2328 shader.offsetscale = atof(parameter[2]);
2329 if (numparameters >= 5)
2331 if(!strcasecmp(parameter[3], "bias"))
2332 shader.offsetbias = atof(parameter[4]);
2333 else if(!strcasecmp(parameter[3], "match"))
2334 shader.offsetbias = 1.0f - atof(parameter[4]);
2335 else if(!strcasecmp(parameter[3], "match8"))
2336 shader.offsetbias = 1.0f - atof(parameter[4]) / 255.0f;
2337 else if(!strcasecmp(parameter[3], "match16"))
2338 shader.offsetbias = 1.0f - atof(parameter[4]) / 65535.0f;
2341 else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
2344 for (deformindex = 0;deformindex < Q3MAXDEFORMS;deformindex++)
2345 if (!shader.deforms[deformindex].deform)
2347 if (deformindex < Q3MAXDEFORMS)
2349 for (i = 0;i < numparameters - 2 && i < Q3DEFORM_MAXPARMS;i++)
2350 shader.deforms[deformindex].parms[i] = atof(parameter[i+2]);
2351 if (!strcasecmp(parameter[1], "projectionshadow")) shader.deforms[deformindex].deform = Q3DEFORM_PROJECTIONSHADOW;
2352 else if (!strcasecmp(parameter[1], "autosprite" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE;
2353 else if (!strcasecmp(parameter[1], "autosprite2" )) shader.deforms[deformindex].deform = Q3DEFORM_AUTOSPRITE2;
2354 else if (!strcasecmp(parameter[1], "text0" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT0;
2355 else if (!strcasecmp(parameter[1], "text1" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT1;
2356 else if (!strcasecmp(parameter[1], "text2" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT2;
2357 else if (!strcasecmp(parameter[1], "text3" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT3;
2358 else if (!strcasecmp(parameter[1], "text4" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT4;
2359 else if (!strcasecmp(parameter[1], "text5" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT5;
2360 else if (!strcasecmp(parameter[1], "text6" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT6;
2361 else if (!strcasecmp(parameter[1], "text7" )) shader.deforms[deformindex].deform = Q3DEFORM_TEXT7;
2362 else if (!strcasecmp(parameter[1], "bulge" )) shader.deforms[deformindex].deform = Q3DEFORM_BULGE;
2363 else if (!strcasecmp(parameter[1], "normal" )) shader.deforms[deformindex].deform = Q3DEFORM_NORMAL;
2364 else if (!strcasecmp(parameter[1], "wave" ))
2366 shader.deforms[deformindex].deform = Q3DEFORM_WAVE;
2367 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[3]);
2368 for (i = 0;i < numparameters - 4 && i < Q3WAVEPARMS;i++)
2369 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+4]);
2371 else if (!strcasecmp(parameter[1], "move" ))
2373 shader.deforms[deformindex].deform = Q3DEFORM_MOVE;
2374 shader.deforms[deformindex].wavefunc = Mod_LoadQ3Shaders_EnumerateWaveFunc(parameter[5]);
2375 for (i = 0;i < numparameters - 6 && i < Q3WAVEPARMS;i++)
2376 shader.deforms[deformindex].waveparms[i] = atof(parameter[i+6]);
2381 // hide this shader if a cvar said it should be killed
2382 if (shader.dpshaderkill)
2383 shader.numlayers = 0;
2384 // fix up multiple reflection types
2385 if(shader.textureflags & Q3TEXTUREFLAG_WATERSHADER)
2386 shader.textureflags &= ~(Q3TEXTUREFLAG_REFRACTION | Q3TEXTUREFLAG_REFLECTION | Q3TEXTUREFLAG_CAMERA);
2388 Q3Shader_AddToHash (&shader);
2392 FS_FreeSearch(search);
2393 // free custinfoparm values
2394 for (j = 0; j < numcustsurfaceflags; j++)
2395 Mem_Free(custsurfaceparmnames[j]);
2398 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
2400 unsigned short hash;
2401 q3shader_hash_entry_t* entry;
2403 Mod_LoadQ3Shaders();
2404 hash = CRC_Block_CaseInsensitive ((const unsigned char *)name, strlen (name));
2405 entry = q3shader_data->hash + (hash % Q3SHADER_HASH_SIZE);
2406 while (entry != NULL)
2408 if (strcasecmp (entry->shader.name, name) == 0)
2409 return &entry->shader;
2410 entry = entry->chain;
2415 texture_shaderpass_t *Mod_CreateShaderPass(skinframe_t *skinframe)
2417 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(loadmodel->mempool, sizeof(*shaderpass));
2418 shaderpass->framerate = 0.0f;
2419 shaderpass->numframes = 1;
2420 shaderpass->blendfunc[0] = GL_ONE;
2421 shaderpass->blendfunc[1] = GL_ZERO;
2422 shaderpass->rgbgen.rgbgen = Q3RGBGEN_IDENTITY;
2423 shaderpass->alphagen.alphagen = Q3ALPHAGEN_IDENTITY;
2424 shaderpass->alphatest = false;
2425 shaderpass->tcgen.tcgen = Q3TCGEN_TEXTURE;
2426 shaderpass->skinframes[0] = skinframe;
2430 texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2433 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(loadmodel->mempool, sizeof(*shaderpass));
2434 shaderpass->alphatest = layer->alphatest != 0;
2435 shaderpass->framerate = layer->framerate;
2436 shaderpass->numframes = layer->numframes;
2437 shaderpass->blendfunc[0] = layer->blendfunc[0];
2438 shaderpass->blendfunc[1] = layer->blendfunc[1];
2439 shaderpass->rgbgen = layer->rgbgen;
2440 shaderpass->alphagen = layer->alphagen;
2441 shaderpass->tcgen = layer->tcgen;
2442 for (j = 0; j < Q3MAXTCMODS && layer->tcmods[j].tcmod != Q3TCMOD_NONE; j++)
2443 shaderpass->tcmods[j] = layer->tcmods[j];
2444 for (j = 0; j < layer->numframes; j++)
2446 if (cls.state == ca_dedicated)
2448 shaderpass->skinframes[j] = NULL;
2450 else if (!(shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false)))
2452 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"^7 (frame %i) for layer %i of shader ^2\"%s\"\n", loadmodel->name, layer->texturename[j], j, layerindex, texturename);
2453 shaderpass->skinframes[j] = R_SkinFrame_LoadMissing();
2459 qboolean Mod_LoadTextureFromQ3Shader(texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags)
2461 int texflagsmask, texflagsor;
2462 qboolean success = true;
2463 q3shaderinfo_t *shader;
2466 strlcpy(texture->name, name, sizeof(texture->name));
2467 texture->basealpha = 1.0f;
2468 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2470 // allow disabling of picmip or compression by defaulttexflags
2472 if(!(defaulttexflags & TEXF_PICMIP))
2473 texflagsmask &= ~TEXF_PICMIP;
2474 if(!(defaulttexflags & TEXF_COMPRESS))
2475 texflagsmask &= ~TEXF_COMPRESS;
2477 if(defaulttexflags & TEXF_ISWORLD)
2478 texflagsor |= TEXF_ISWORLD;
2479 if(defaulttexflags & TEXF_ISSPRITE)
2480 texflagsor |= TEXF_ISSPRITE;
2481 // unless later loaded from the shader
2482 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2483 texture->offsetscale = 1;
2484 texture->offsetbias = 0;
2485 texture->specularscalemod = 1;
2486 texture->specularpowermod = 1;
2487 texture->rtlightambient = 0;
2488 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2489 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2490 // JUST GREP FOR "specularscalemod = 1".
2494 if (developer_loading.integer)
2495 Con_Printf("%s: loaded shader for %s\n", loadmodel->name, name);
2497 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2499 texture->basematerialflags = MATERIALFLAG_SKY;
2500 if (shader->skyboxname[0])
2502 // quake3 seems to append a _ to the skybox name, so this must do so as well
2503 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2506 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2507 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2509 texture->basematerialflags = MATERIALFLAG_WALL;
2511 if (shader->layers[0].alphatest)
2512 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2513 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2514 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2515 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2517 texture->biaspolygonoffset += shader->biaspolygonoffset;
2518 texture->biaspolygonfactor += shader->biaspolygonfactor;
2520 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2521 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2522 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2523 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2524 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2525 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2526 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2527 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2528 texture->customblendfunc[0] = GL_ONE;
2529 texture->customblendfunc[1] = GL_ZERO;
2530 texture->transparentsort = shader->transparentsort;
2531 if (shader->numlayers > 0)
2533 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2534 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2536 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2537 * additive GL_ONE GL_ONE
2538 additive weird GL_ONE GL_SRC_ALPHA
2539 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2540 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2541 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2542 brighten GL_DST_COLOR GL_ONE
2543 brighten GL_ONE GL_SRC_COLOR
2544 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2545 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2546 * modulate GL_DST_COLOR GL_ZERO
2547 * modulate GL_ZERO GL_SRC_COLOR
2548 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2549 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2550 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2551 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2552 * no blend GL_ONE GL_ZERO
2553 nothing GL_ZERO GL_ONE
2555 // if not opaque, figure out what blendfunc to use
2556 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2558 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2559 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2560 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2561 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2562 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2563 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2565 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2568 if (!shader->lighting)
2569 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2571 // here be dragons: convert quake3 shaders to material
2572 if (shader->numlayers > 0)
2575 int terrainbackgroundlayer = -1;
2576 int lightmaplayer = -1;
2577 int alphagenspecularlayer = -1;
2578 int rgbgenvertexlayer = -1;
2579 int rgbgendiffuselayer = -1;
2580 int materiallayer = -1;
2581 int endofprelayers = 0;
2582 int firstpostlayer = 0;
2583 int shaderpassindex = 0;
2584 for (i = 0; i < shader->numlayers; i++)
2586 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2588 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2589 rgbgenvertexlayer = i;
2590 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2591 rgbgendiffuselayer = i;
2592 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2593 alphagenspecularlayer = i;
2595 if (shader->numlayers >= 2
2596 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2597 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2598 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2599 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2601 // terrain blend or certain other effects involving alphatest over a regular layer
2602 terrainbackgroundlayer = 0;
2604 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2605 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2607 else if (lightmaplayer == 0)
2609 // ordinary texture but with $lightmap before diffuse
2611 firstpostlayer = lightmaplayer + 2;
2613 else if (lightmaplayer >= 1)
2615 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2616 endofprelayers = lightmaplayer - 1;
2617 materiallayer = lightmaplayer - 1;
2618 firstpostlayer = lightmaplayer + 1;
2620 else if (rgbgenvertexlayer >= 0)
2622 // map models with baked lighting
2623 materiallayer = rgbgenvertexlayer;
2624 endofprelayers = rgbgenvertexlayer;
2625 firstpostlayer = rgbgenvertexlayer + 1;
2627 else if (rgbgendiffuselayer >= 0)
2629 // entity models with dynamic lighting
2630 materiallayer = rgbgendiffuselayer;
2631 endofprelayers = rgbgendiffuselayer;
2632 firstpostlayer = rgbgendiffuselayer + 1;
2633 // 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)...
2634 if (alphagenspecularlayer >= 0)
2635 firstpostlayer = alphagenspecularlayer + 1;
2639 // special effects shaders - treat first as primary layer and do everything else as post
2644 // convert the main material layer
2645 // 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
2646 if (materiallayer >= 0)
2647 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(&shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2648 // convert the terrain background blend layer (if any)
2649 if (terrainbackgroundlayer >= 0)
2650 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(&shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2651 // convert the prepass layers (if any)
2652 texture->startpreshaderpass = shaderpassindex;
2653 for (i = 0; i < endofprelayers; i++)
2654 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(&shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2655 texture->endpreshaderpass = shaderpassindex;
2656 texture->startpostshaderpass = shaderpassindex;
2657 // convert the postpass layers (if any)
2658 for (i = firstpostlayer; i < shader->numlayers; i++)
2659 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(&shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2660 texture->startpostshaderpass = shaderpassindex;
2663 if (shader->dpshadow)
2664 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2665 if (shader->dpnoshadow)
2666 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2667 if (shader->dpnortlight)
2668 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2669 if (shader->vertexalpha)
2670 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2671 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2672 texture->reflectmin = shader->reflectmin;
2673 texture->reflectmax = shader->reflectmax;
2674 texture->refractfactor = shader->refractfactor;
2675 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2676 texture->reflectfactor = shader->reflectfactor;
2677 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2678 texture->r_water_wateralpha = shader->r_water_wateralpha;
2679 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2680 texture->offsetmapping = shader->offsetmapping;
2681 texture->offsetscale = shader->offsetscale;
2682 texture->offsetbias = shader->offsetbias;
2683 texture->specularscalemod = shader->specularscalemod;
2684 texture->specularpowermod = shader->specularpowermod;
2685 texture->rtlightambient = shader->rtlightambient;
2686 if (shader->dpreflectcube[0])
2687 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2689 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2690 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2691 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2692 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2693 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2694 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2695 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2696 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2697 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2699 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2700 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2701 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2702 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2703 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2704 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2705 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2706 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2707 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2708 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2709 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2710 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2711 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2712 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2713 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2714 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2715 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2716 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2717 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2718 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2719 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2720 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2721 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2722 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2723 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2724 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2725 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2726 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2727 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2728 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2729 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2730 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2732 texture->surfaceflags = shader->surfaceflags;
2733 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2734 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2735 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2736 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2737 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2738 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2739 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2740 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2741 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2742 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2743 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2744 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2745 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2746 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2747 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2748 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2749 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2750 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2751 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2752 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2753 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2754 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2755 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2756 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2757 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2758 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2759 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2760 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2761 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2762 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2763 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2764 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2766 if (shader->dpmeshcollisions)
2767 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2768 if (shader->dpshaderkill && developer_extra.integer)
2769 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", loadmodel->name, name);
2771 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2773 if (developer_extra.integer)
2774 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", loadmodel->name, name);
2775 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2777 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2779 if (developer_extra.integer)
2780 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", loadmodel->name, name);
2781 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2782 texture->supercontents = SUPERCONTENTS_SOLID;
2786 if (developer_extra.integer)
2787 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", loadmodel->name, texture->name);
2788 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2790 texture->basematerialflags |= MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2791 texture->supercontents = SUPERCONTENTS_SOLID;
2793 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2795 texture->basematerialflags |= MATERIALFLAG_SKY;
2796 texture->supercontents = SUPERCONTENTS_SKY;
2800 texture->basematerialflags |= MATERIALFLAG_WALL;
2801 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2803 if(cls.state == ca_dedicated)
2805 texture->materialshaderpass = NULL;
2812 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false));
2813 if (texture->materialshaderpass->skinframes[0])
2815 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2816 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2817 if (texture->q2contents)
2818 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(loadmodel, texture->q2contents);
2825 if (!success && warnmissing)
2826 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", loadmodel->name, texture->name);
2829 // init the animation variables
2830 texture->currentframe = texture;
2831 if (!texture->materialshaderpass)
2832 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(R_SkinFrame_LoadMissing());
2833 if (!texture->materialshaderpass->skinframes[0])
2834 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2835 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2836 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2840 void Mod_LoadCustomMaterial(texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2842 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2843 Con_DPrintf("^1%s:^7 Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", loadmodel->name, texture->name);
2844 strlcpy(texture->name, name, sizeof(texture->name));
2845 texture->basealpha = 1.0f;
2846 texture->basematerialflags = texture->currentmaterialflags = materialflags;
2847 texture->supercontents = supercontents;
2849 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2850 texture->offsetscale = 1;
2851 texture->offsetbias = 0;
2852 texture->specularscalemod = 1;
2853 texture->specularpowermod = 1;
2854 texture->rtlightambient = 0;
2855 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2856 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2857 // JUST GREP FOR "specularscalemod = 1".
2859 if (developer_extra.integer)
2860 Con_DPrintf("^1%s:^7 Custom texture ^3\"%s\"\n", loadmodel->name, texture->name);
2861 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(skinframe);
2863 // init the animation variables
2864 texture->currentframe = texture;
2865 if (!texture->materialshaderpass)
2866 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(R_SkinFrame_LoadMissing());
2867 if (!texture->materialshaderpass->skinframes[0])
2868 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2869 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2870 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2873 skinfile_t *Mod_LoadSkinFiles(void)
2875 int i, words, line, wordsoverflow;
2878 skinfile_t *skinfile = NULL, *first = NULL;
2879 skinfileitem_t *skinfileitem;
2880 char word[10][MAX_QPATH];
2885 U_bodyBox,models/players/Legoman/BikerA2.tga
2886 U_RArm,models/players/Legoman/BikerA1.tga
2887 U_LArm,models/players/Legoman/BikerA1.tga
2888 U_armor,common/nodraw
2889 U_sword,common/nodraw
2890 U_shield,common/nodraw
2891 U_homb,common/nodraw
2892 U_backpack,common/nodraw
2893 U_colcha,common/nodraw
2898 memset(word, 0, sizeof(word));
2899 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2901 // If it's the first file we parse
2902 if (skinfile == NULL)
2904 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2909 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2910 skinfile = skinfile->next;
2912 skinfile->next = NULL;
2914 for(line = 0;;line++)
2917 if (!COM_ParseToken_QuakeC(&data, true))
2919 if (!strcmp(com_token, "\n"))
2922 wordsoverflow = false;
2926 strlcpy(word[words++], com_token, sizeof (word[0]));
2928 wordsoverflow = true;
2930 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2933 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);
2936 // words is always >= 1
2937 if (!strcmp(word[0], "replace"))
2941 if (developer_loading.integer)
2942 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2943 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2944 skinfileitem->next = skinfile->items;
2945 skinfile->items = skinfileitem;
2946 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2947 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2950 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]);
2952 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2954 // tag name, like "tag_weapon,"
2955 // not used for anything (not even in Quake3)
2957 else if (words >= 2 && !strcmp(word[1], ","))
2959 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2960 if (developer_loading.integer)
2961 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2962 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2963 skinfileitem->next = skinfile->items;
2964 skinfile->items = skinfileitem;
2965 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2966 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2969 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);
2974 loadmodel->numskins = i;
2978 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2981 skinfileitem_t *skinfileitem, *nextitem;
2982 for (;skinfile;skinfile = next)
2984 next = skinfile->next;
2985 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2987 nextitem = skinfileitem->next;
2988 Mem_Free(skinfileitem);
2994 int Mod_CountSkinFiles(skinfile_t *skinfile)
2997 for (i = 0;skinfile;skinfile = skinfile->next, i++);
3001 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
3004 double isnap = 1.0 / snap;
3005 for (i = 0;i < numvertices*numcomponents;i++)
3006 vertices[i] = floor(vertices[i]*isnap)*snap;
3009 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
3011 int i, outtriangles;
3012 float edgedir1[3], edgedir2[3], temp[3];
3013 // a degenerate triangle is one with no width (thickness, surface area)
3014 // these are characterized by having all 3 points colinear (along a line)
3015 // or having two points identical
3016 // the simplest check is to calculate the triangle's area
3017 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
3019 // calculate first edge
3020 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
3021 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
3022 CrossProduct(edgedir1, edgedir2, temp);
3023 if (VectorLength2(temp) < 0.001f)
3024 continue; // degenerate triangle (no area)
3025 // valid triangle (has area)
3026 VectorCopy(inelement3i, outelement3i);
3030 return outtriangles;
3033 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
3036 int firstvertex, lastvertex;
3037 if (numelements > 0 && elements)
3039 firstvertex = lastvertex = elements[0];
3040 for (i = 1;i < numelements;i++)
3043 firstvertex = min(firstvertex, e);
3044 lastvertex = max(lastvertex, e);
3048 firstvertex = lastvertex = 0;
3049 if (firstvertexpointer)
3050 *firstvertexpointer = firstvertex;
3051 if (lastvertexpointer)
3052 *lastvertexpointer = lastvertex;
3055 void Mod_MakeSortedSurfaces(dp_model_t *mod)
3057 // make an optimal set of texture-sorted batches to draw...
3059 int *firstsurfacefortexture;
3060 int *numsurfacesfortexture;
3061 if (!mod->sortedmodelsurfaces)
3062 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
3063 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
3064 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
3065 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
3066 for (j = 0;j < mod->nummodelsurfaces;j++)
3068 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
3069 t = (int)(surface->texture - mod->data_textures);
3070 numsurfacesfortexture[t]++;
3073 for (t = 0;t < mod->num_textures;t++)
3075 firstsurfacefortexture[t] = j;
3076 j += numsurfacesfortexture[t];
3078 for (j = 0;j < mod->nummodelsurfaces;j++)
3080 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
3081 t = (int)(surface->texture - mod->data_textures);
3082 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
3084 Mem_Free(firstsurfacefortexture);
3085 Mem_Free(numsurfacesfortexture);
3088 void Mod_BuildVBOs(void)
3090 if (!loadmodel->surfmesh.num_vertices)
3093 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
3096 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
3098 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
3100 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
3101 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
3106 // build r_vertexmesh_t array
3107 // (compressed interleaved array for D3D)
3108 if (!loadmodel->surfmesh.data_vertexmesh && vid.useinterleavedarrays)
3111 int numvertices = loadmodel->surfmesh.num_vertices;
3112 r_vertexmesh_t *vertexmesh;
3113 loadmodel->surfmesh.data_vertexmesh = vertexmesh = (r_vertexmesh_t*)Mem_Alloc(loadmodel->mempool, numvertices * sizeof(r_vertexmesh_t));
3114 for (vertexindex = 0;vertexindex < numvertices;vertexindex++, vertexmesh++)
3116 VectorCopy(loadmodel->surfmesh.data_vertex3f + 3*vertexindex, vertexmesh->vertex3f);
3117 VectorScale(loadmodel->surfmesh.data_svector3f + 3*vertexindex, 1.0f, vertexmesh->svector3f);
3118 VectorScale(loadmodel->surfmesh.data_tvector3f + 3*vertexindex, 1.0f, vertexmesh->tvector3f);
3119 VectorScale(loadmodel->surfmesh.data_normal3f + 3*vertexindex, 1.0f, vertexmesh->normal3f);
3120 if (loadmodel->surfmesh.data_lightmapcolor4f)
3121 Vector4Copy(loadmodel->surfmesh.data_lightmapcolor4f + 4*vertexindex, vertexmesh->color4f);
3122 Vector2Copy(loadmodel->surfmesh.data_texcoordtexture2f + 2*vertexindex, vertexmesh->texcoordtexture2f);
3123 if (loadmodel->surfmesh.data_texcoordlightmap2f)
3124 Vector2Scale(loadmodel->surfmesh.data_texcoordlightmap2f + 2*vertexindex, 1.0f, vertexmesh->texcoordlightmap2f);
3125 if (loadmodel->surfmesh.data_skeletalindex4ub)
3126 Vector4Copy(loadmodel->surfmesh.data_skeletalindex4ub + 4*vertexindex, vertexmesh->skeletalindex4ub);
3127 if (loadmodel->surfmesh.data_skeletalweight4ub)
3128 Vector4Copy(loadmodel->surfmesh.data_skeletalweight4ub + 4*vertexindex, vertexmesh->skeletalweight4ub);
3132 // upload short indices as a buffer
3133 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
3134 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);
3136 // upload int indices as a buffer
3137 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
3138 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);
3140 // only build a vbo if one has not already been created (this is important for brush models which load specially)
3141 // vertex buffer is several arrays and we put them in the same buffer
3143 // is this wise? the texcoordtexture2f array is used with dynamic
3144 // vertex/svector/tvector/normal when rendering animated models, on the
3145 // other hand animated models don't use a lot of vertices anyway...
3146 if (!loadmodel->surfmesh.vbo_vertexbuffer && !vid.useinterleavedarrays)
3151 loadmodel->surfmesh.vbooffset_vertexmesh = size;if (loadmodel->surfmesh.data_vertexmesh ) size += loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t);
3152 loadmodel->surfmesh.vbooffset_vertex3f = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3153 loadmodel->surfmesh.vbooffset_svector3f = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3154 loadmodel->surfmesh.vbooffset_tvector3f = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3155 loadmodel->surfmesh.vbooffset_normal3f = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3156 loadmodel->surfmesh.vbooffset_texcoordtexture2f = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3157 loadmodel->surfmesh.vbooffset_texcoordlightmap2f = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3158 loadmodel->surfmesh.vbooffset_lightmapcolor4f = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
3159 loadmodel->surfmesh.vbooffset_skeletalindex4ub = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3160 loadmodel->surfmesh.vbooffset_skeletalweight4ub = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3161 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
3162 if (loadmodel->surfmesh.data_vertexmesh ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertexmesh , loadmodel->surfmesh.data_vertexmesh , loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t));
3163 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertex3f , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3164 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_svector3f , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3165 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_tvector3f , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3166 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_normal3f , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3167 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordtexture2f , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3168 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordlightmap2f, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3169 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.vbooffset_lightmapcolor4f , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
3170 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalindex4ub , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3171 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalweight4ub , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3172 loadmodel->surfmesh.vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3177 extern cvar_t mod_obj_orientation;
3178 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3180 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3182 const char *texname;
3184 const float *v, *vn, *vt;
3186 size_t outbufferpos = 0;
3187 size_t outbuffermax = 0x100000;
3188 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3189 const msurface_t *surface;
3190 const int maxtextures = 256;
3191 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3192 dp_model_t *submodel;
3194 // construct the mtllib file
3195 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3198 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3201 countvertices += surface->num_vertices;
3202 countfaces += surface->num_triangles;
3203 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3204 for (textureindex = 0;textureindex < counttextures;textureindex++)
3205 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3207 if (textureindex < counttextures)
3208 continue; // already wrote this material entry
3209 if (textureindex >= maxtextures)
3210 continue; // just a precaution
3211 textureindex = counttextures++;
3212 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3213 if (outbufferpos >= outbuffermax >> 1)
3216 oldbuffer = outbuffer;
3217 outbuffer = (char *) Z_Malloc(outbuffermax);
3218 memcpy(outbuffer, oldbuffer, outbufferpos);
3221 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");
3226 // write the mtllib file
3227 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3229 // construct the obj file
3231 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);
3235 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)
3237 if (outbufferpos >= outbuffermax >> 1)
3240 oldbuffer = outbuffer;
3241 outbuffer = (char *) Z_Malloc(outbuffermax);
3242 memcpy(outbuffer, oldbuffer, outbufferpos);
3245 if(mod_obj_orientation.integer)
3246 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]);
3248 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]);
3253 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3255 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3258 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3259 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3261 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3262 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3265 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3267 if (outbufferpos >= outbuffermax >> 1)
3270 oldbuffer = outbuffer;
3271 outbuffer = (char *) Z_Malloc(outbuffermax);
3272 memcpy(outbuffer, oldbuffer, outbufferpos);
3278 if(mod_obj_orientation.integer)
3279 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);
3281 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);
3288 // write the obj file
3289 FS_WriteFile(filename, outbuffer, outbufferpos);
3293 Z_Free(texturenames);
3296 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3299 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3301 int countnodes = 0, counttriangles = 0, countframes = 0;
3309 size_t outbufferpos = 0;
3310 size_t outbuffermax = 0x100000;
3311 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3312 const msurface_t *surface;
3313 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3316 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3318 if (outbufferpos >= outbuffermax >> 1)
3321 oldbuffer = outbuffer;
3322 outbuffer = (char *) Z_Malloc(outbuffermax);
3323 memcpy(outbuffer, oldbuffer, outbufferpos);
3327 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3331 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3334 for (poseindex = 0;poseindex < numposes;poseindex++)
3337 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3340 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3344 matrix4x4_t posematrix;
3345 if (outbufferpos >= outbuffermax >> 1)
3348 oldbuffer = outbuffer;
3349 outbuffer = (char *) Z_Malloc(outbuffermax);
3350 memcpy(outbuffer, oldbuffer, outbufferpos);
3354 // strangely the smd angles are for a transposed matrix, so we
3355 // have to generate a transposed matrix, then convert that...
3356 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3357 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3358 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3359 if (angles[0] >= 180) angles[0] -= 360;
3360 if (angles[1] >= 180) angles[1] -= 360;
3361 if (angles[2] >= 180) angles[2] -= 360;
3365 float a = DEG2RAD(angles[ROLL]);
3366 float b = DEG2RAD(angles[PITCH]);
3367 float c = DEG2RAD(angles[YAW]);
3368 float cy, sy, cp, sp, cr, sr;
3370 // smd matrix construction, for comparing
3381 test[1][0] = sr*sp*cy+cr*-sy;
3382 test[1][1] = sr*sp*sy+cr*cy;
3384 test[2][0] = (cr*sp*cy+-sr*-sy);
3385 test[2][1] = (cr*sp*sy+-sr*cy);
3387 test[3][0] = pose[9];
3388 test[3][1] = pose[10];
3389 test[3][2] = pose[11];
3392 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]));
3397 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3402 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3405 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3407 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3410 if (outbufferpos >= outbuffermax >> 1)
3413 oldbuffer = outbuffer;
3414 outbuffer = (char *) Z_Malloc(outbuffermax);
3415 memcpy(outbuffer, oldbuffer, outbufferpos);
3418 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3421 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3423 const int index = e[2-cornerindex];
3424 const float *v = model->surfmesh.data_vertex3f + index * 3;
3425 const float *vn = model->surfmesh.data_normal3f + index * 3;
3426 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3427 const int b = model->surfmesh.blends[index];
3428 if (b < model->num_bones)
3429 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]);
3432 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3433 const unsigned char *wi = w->index;
3434 const unsigned char *wf = w->influence;
3435 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);
3436 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);
3437 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);
3438 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]);
3445 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3450 FS_WriteFile(filename, outbuffer, outbufferpos);
3453 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3460 decompiles a model to editable files
3463 static void Mod_Decompile_f(void)
3465 int i, j, k, l, first, count;
3467 char inname[MAX_QPATH];
3468 char outname[MAX_QPATH];
3469 char mtlname[MAX_QPATH];
3470 char basename[MAX_QPATH];
3471 char animname[MAX_QPATH];
3472 char animname2[MAX_QPATH];
3473 char zymtextbuffer[16384];
3474 char dpmtextbuffer[16384];
3475 char framegroupstextbuffer[16384];
3476 int zymtextsize = 0;
3477 int dpmtextsize = 0;
3478 int framegroupstextsize = 0;
3481 if (Cmd_Argc() != 2)
3483 Con_Print("usage: modeldecompile <filename>\n");
3487 strlcpy(inname, Cmd_Argv(1), sizeof(inname));
3488 FS_StripExtension(inname, basename, sizeof(basename));
3490 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3493 Con_Print("No such model\n");
3496 if (mod->brush.submodel)
3498 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3499 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3500 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3503 if (!mod->surfmesh.num_triangles)
3505 Con_Print("Empty model (or sprite)\n");
3509 // export OBJ if possible (not on sprites)
3510 if (mod->surfmesh.num_triangles)
3512 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3513 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3514 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3517 // export SMD if possible (only for skeletal models)
3518 if (mod->surfmesh.num_triangles && mod->num_bones)
3520 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3521 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3522 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3523 if (l > 0) zymtextsize += l;
3524 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3525 if (l > 0) dpmtextsize += l;
3526 for (i = 0;i < mod->numframes;i = j)
3528 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3529 first = mod->animscenes[i].firstframe;
3530 if (mod->animscenes[i].framecount > 1)
3533 count = mod->animscenes[i].framecount;
3539 // check for additional frames with same name
3540 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3541 if(animname[l] < '0' || animname[l] > '9')
3543 if(k > 0 && animname[k-1] == '_')
3546 count = mod->num_poses - first;
3547 for (j = i + 1;j < mod->numframes;j++)
3549 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3550 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3551 if(animname2[l] < '0' || animname2[l] > '9')
3553 if(k > 0 && animname[k-1] == '_')
3556 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3558 count = mod->animscenes[j].firstframe - first;
3562 // if it's only one frame, use the original frame name
3564 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3567 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3568 Mod_Decompile_SMD(mod, outname, first, count, false);
3569 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3571 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3572 if (l > 0) zymtextsize += l;
3574 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3576 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3577 if (l > 0) dpmtextsize += l;
3579 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3581 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3582 if (l > 0) framegroupstextsize += l;
3586 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3588 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3589 if (framegroupstextsize)
3590 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3594 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3597 memset(state, 0, sizeof(*state));
3598 state->width = width;
3599 state->height = height;
3600 state->currentY = 0;
3601 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3602 for (y = 0;y < state->height;y++)
3604 state->rows[y].currentX = 0;
3605 state->rows[y].rowY = -1;
3609 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3612 state->currentY = 0;
3613 for (y = 0;y < state->height;y++)
3615 state->rows[y].currentX = 0;
3616 state->rows[y].rowY = -1;
3620 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3623 Mem_Free(state->rows);
3624 memset(state, 0, sizeof(*state));
3627 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3629 mod_alloclightmap_row_t *row;
3632 row = state->rows + blockheight;
3633 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3635 if (state->currentY + blockheight <= state->height)
3637 // use the current allocation position
3638 row->rowY = state->currentY;
3640 state->currentY += blockheight;
3644 // find another position
3645 for (y = blockheight;y < state->height;y++)
3647 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3649 row = state->rows + y;
3653 if (y == state->height)
3658 *outx = row->currentX;
3659 row->currentX += blockwidth;
3664 typedef struct lightmapsample_s
3668 float *vertex_color;
3669 unsigned char *lm_bgr;
3670 unsigned char *lm_dir;
3674 typedef struct lightmapvertex_s
3679 float texcoordbase[2];
3680 float texcoordlightmap[2];
3681 float lightcolor[4];
3685 typedef struct lightmaptriangle_s
3693 // 2D modelspace coordinates of min corner
3694 // snapped to lightmap grid but not in grid coordinates
3696 // 2D modelspace to lightmap coordinate scale
3704 typedef struct lightmaplight_s
3715 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3717 #define MAX_LIGHTMAPSAMPLES 64
3718 static int mod_generatelightmaps_numoffsets[3];
3719 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3721 static int mod_generatelightmaps_numlights;
3722 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3724 extern cvar_t r_shadow_lightattenuationdividebias;
3725 extern cvar_t r_shadow_lightattenuationlinearscale;
3727 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3732 float relativepoint[3];
3739 float lightorigin[3];
3743 float lightcolor[3];
3745 for (i = 0;i < 5*3;i++)
3747 for (index = 0;;index++)
3749 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3754 lightradius2 = lightradius * lightradius;
3755 VectorSubtract(lightorigin, pos, relativepoint);
3756 dist2 = VectorLength2(relativepoint);
3757 if (dist2 >= lightradius2)
3759 lightiradius = 1.0f / lightradius;
3760 dist = sqrt(dist2) * lightiradius;
3761 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3762 if (intensity <= 0.0f)
3764 if (model && model->TraceLine)
3766 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3767 if (trace.fraction < 1)
3770 // scale down intensity to add to both ambient and diffuse
3771 //intensity *= 0.5f;
3772 VectorNormalize(relativepoint);
3773 VectorScale(lightcolor, intensity, color);
3774 VectorMA(sample , 0.5f , color, sample );
3775 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3776 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3777 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3778 // calculate a weighted average light direction as well
3779 intensity *= VectorLength(color);
3780 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3782 // calculate the direction we'll use to reduce the sample to a directional light source
3783 VectorCopy(sample + 12, dir);
3784 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3785 VectorNormalize(dir);
3786 // extract the diffuse color along the chosen direction and scale it
3787 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3788 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3789 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3790 // subtract some of diffuse from ambient
3791 VectorMA(sample, -0.333f, diffuse, ambient);
3792 // store the normalized lightdir
3793 VectorCopy(dir, lightdir);
3796 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3800 const msurface_t *surface;
3801 const float *vertex3f = model->surfmesh.data_vertex3f;
3802 const int *element3i = model->surfmesh.data_element3i;
3805 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3807 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3809 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3811 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3813 VectorCopy(vertex3f + 3*e[0], v2[0]);
3814 VectorCopy(vertex3f + 3*e[1], v2[1]);
3815 VectorCopy(vertex3f + 3*e[2], v2[2]);
3816 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3821 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3823 int maxnodes = 1<<14;
3824 svbsp_node_t *nodes;
3829 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3830 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3831 VectorCopy(lightinfo->origin, origin);
3832 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3835 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3836 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3837 if (svbsp.ranoutofnodes)
3840 if (maxnodes > 1<<22)
3846 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3851 if (svbsp.numnodes > 0)
3853 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3854 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3855 lightinfo->svbsp = svbsp;
3860 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3864 lightmaplight_t *lightinfo;
3868 mod_generatelightmaps_numlights = 0;
3869 for (index = 0;;index++)
3871 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3875 mod_generatelightmaps_numlights++;
3877 if (mod_generatelightmaps_numlights > 0)
3879 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3880 lightinfo = mod_generatelightmaps_lightinfo;
3881 for (index = 0;;index++)
3883 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3890 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3892 lightinfo->iradius = 1.0f / lightinfo->radius;
3893 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3894 // TODO: compute svbsp
3895 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3899 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3902 if (mod_generatelightmaps_lightinfo)
3904 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3905 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3906 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3907 Mem_Free(mod_generatelightmaps_lightinfo);
3909 mod_generatelightmaps_lightinfo = NULL;
3910 mod_generatelightmaps_numlights = 0;
3913 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3915 const svbsp_node_t *node;
3916 const svbsp_node_t *nodes = svbsp->nodes;
3921 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3923 return num == -1; // true if empty, false if solid (shadowed)
3926 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3929 float relativepoint[3];
3938 const lightmaplight_t *lightinfo;
3940 for (i = 0;i < 5*3;i++)
3942 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3944 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3945 VectorSubtract(lightinfo->origin, pos, relativepoint);
3946 // don't accept light from behind a surface, it causes bad shading
3947 if (normal && DotProduct(relativepoint, normal) <= 0)
3949 dist2 = VectorLength2(relativepoint);
3950 if (dist2 >= lightinfo->radius2)
3952 dist = sqrt(dist2) * lightinfo->iradius;
3953 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3956 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3960 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3962 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3964 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3967 // for light grid we'd better check visibility of the offset point
3968 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3969 if (trace.fraction < 1)
3970 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3973 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3978 // scale intensity according to how many rays succeeded
3979 // we know one test is valid, half of the rest will fail...
3980 //if (normal && tests > 1)
3981 // intensity *= (tests - 1.0f) / tests;
3982 intensity *= (float)hits / tests;
3984 // scale down intensity to add to both ambient and diffuse
3985 //intensity *= 0.5f;
3986 VectorNormalize(relativepoint);
3987 VectorScale(lightinfo->color, intensity, color);
3988 VectorMA(sample , 0.5f , color, sample );
3989 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3990 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3991 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3992 // calculate a weighted average light direction as well
3993 intensity *= VectorLength(color);
3994 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3998 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
4004 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
4005 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
4006 VectorCopy(sample + 12, dir);
4007 VectorNormalize(dir);
4008 //VectorAdd(dir, normal, dir);
4009 //VectorNormalize(dir);
4010 f = DotProduct(dir, normal);
4011 f = max(0, f) * 255.0f;
4012 VectorScale(sample, f, color);
4013 //VectorCopy(normal, dir);
4014 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
4015 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
4016 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
4017 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
4019 lm_dir[0] = (unsigned char)dir[2];
4020 lm_dir[1] = (unsigned char)dir[1];
4021 lm_dir[2] = (unsigned char)dir[0];
4025 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
4028 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
4029 VectorCopy(sample, vertex_color);
4032 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
4038 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
4039 // calculate the direction we'll use to reduce the sample to a directional light source
4040 VectorCopy(sample + 12, dir);
4041 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
4042 VectorNormalize(dir);
4043 // extract the diffuse color along the chosen direction and scale it
4044 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
4045 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
4046 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
4047 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
4048 VectorScale(sample, 127.5f, ambient);
4049 VectorMA(ambient, -0.333f, diffuse, ambient);
4050 // encode to the grid format
4051 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
4052 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
4053 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
4054 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
4055 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
4056 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
4057 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
4058 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
4059 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));}
4062 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
4067 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
4068 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
4069 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
4070 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
4071 radius[0] = mod_generatelightmaps_lightmapradius.value;
4072 radius[1] = mod_generatelightmaps_vertexradius.value;
4073 radius[2] = mod_generatelightmaps_gridradius.value;
4074 for (i = 0;i < 3;i++)
4076 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
4079 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
4084 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
4086 msurface_t *surface;
4089 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4091 surface = model->data_surfaces + surfaceindex;
4092 surface->lightmaptexture = NULL;
4093 surface->deluxemaptexture = NULL;
4095 if (model->brushq3.data_lightmaps)
4097 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
4098 if (model->brushq3.data_lightmaps[i])
4099 R_FreeTexture(model->brushq3.data_lightmaps[i]);
4100 Mem_Free(model->brushq3.data_lightmaps);
4101 model->brushq3.data_lightmaps = NULL;
4103 if (model->brushq3.data_deluxemaps)
4105 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
4106 if (model->brushq3.data_deluxemaps[i])
4107 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
4108 Mem_Free(model->brushq3.data_deluxemaps);
4109 model->brushq3.data_deluxemaps = NULL;
4113 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
4115 msurface_t *surface;
4121 surfmesh_t oldsurfmesh;
4123 unsigned char *data;
4124 oldsurfmesh = model->surfmesh;
4125 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
4126 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
4128 size += model->surfmesh.num_vertices * sizeof(float[3]);
4129 size += model->surfmesh.num_vertices * sizeof(float[3]);
4130 size += model->surfmesh.num_vertices * sizeof(float[3]);
4131 size += model->surfmesh.num_vertices * sizeof(float[3]);
4132 size += model->surfmesh.num_vertices * sizeof(float[2]);
4133 size += model->surfmesh.num_vertices * sizeof(float[2]);
4134 size += model->surfmesh.num_vertices * sizeof(float[4]);
4135 data = (unsigned char *)Mem_Alloc(model->mempool, size);
4136 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4137 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4138 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4139 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4140 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
4141 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
4142 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
4143 if (model->surfmesh.num_vertices > 65536)
4144 model->surfmesh.data_element3s = NULL;
4146 if (model->surfmesh.data_element3i_indexbuffer)
4147 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
4148 model->surfmesh.data_element3i_indexbuffer = NULL;
4149 if (model->surfmesh.data_element3s_indexbuffer)
4150 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
4151 model->surfmesh.data_element3s_indexbuffer = NULL;
4152 if (model->surfmesh.vbo_vertexbuffer)
4153 R_Mesh_DestroyMeshBuffer(model->surfmesh.vbo_vertexbuffer);
4154 model->surfmesh.vbo_vertexbuffer = 0;
4156 // convert all triangles to unique vertex data
4158 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4160 surface = model->data_surfaces + surfaceindex;
4161 surface->num_firstvertex = outvertexindex;
4162 surface->num_vertices = surface->num_triangles*3;
4163 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4164 for (i = 0;i < surface->num_triangles*3;i++)
4167 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4168 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4169 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4170 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4171 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4172 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4173 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4174 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4175 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4176 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4177 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4178 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4179 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4180 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4181 if (oldsurfmesh.data_texcoordlightmap2f)
4183 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4184 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4186 if (oldsurfmesh.data_lightmapcolor4f)
4188 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4189 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4190 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4191 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4194 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4195 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4199 if (model->surfmesh.data_element3s)
4200 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4201 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4203 // find and update all submodels to use this new surfmesh data
4204 for (i = 0;i < model->brush.numsubmodels;i++)
4205 model->brush.submodels[i]->surfmesh = model->surfmesh;
4208 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
4210 msurface_t *surface;
4216 lightmaptriangle_t *triangle;
4217 // generate lightmap triangle structs
4218 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4219 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4221 surface = model->data_surfaces + surfaceindex;
4222 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4223 for (i = 0;i < surface->num_triangles;i++)
4225 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4226 triangle->triangleindex = surface->num_firsttriangle+i;
4227 triangle->surfaceindex = surfaceindex;
4228 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4229 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4230 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4231 // calculate bounds of triangle
4232 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4233 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4234 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4235 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4236 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4237 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4238 // pick an axial projection based on the triangle normal
4239 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4241 if (fabs(normal[1]) > fabs(normal[axis]))
4243 if (fabs(normal[2]) > fabs(normal[axis]))
4245 triangle->axis = axis;
4250 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4252 if (mod_generatelightmaps_lightmaptriangles)
4253 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4254 mod_generatelightmaps_lightmaptriangles = NULL;
4257 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4259 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4261 msurface_t *surface;
4275 float trianglenormal[3];
4276 float samplecenter[3];
4277 float samplenormal[3];
4283 float lmscalepixels;
4286 float lm_basescalepixels;
4287 int lm_borderpixels;
4291 lightmaptriangle_t *triangle;
4292 unsigned char *lightmappixels;
4293 unsigned char *deluxemappixels;
4294 mod_alloclightmap_state_t lmstate;
4297 // generate lightmap projection information for all triangles
4298 if (model->texturepool == NULL)
4299 model->texturepool = R_AllocTexturePool();
4300 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4301 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4302 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4303 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4304 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4306 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4308 surface = model->data_surfaces + surfaceindex;
4309 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4310 lmscalepixels = lm_basescalepixels;
4311 for (retry = 0;retry < 30;retry++)
4313 // after a couple failed attempts, degrade quality to make it fit
4315 lmscalepixels *= 0.5f;
4316 for (i = 0;i < surface->num_triangles;i++)
4318 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4319 triangle->lightmapindex = lightmapnumber;
4320 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4321 // pick two planar axes for projection
4322 // lightmap coordinates here are in pixels
4323 // lightmap projections are snapped to pixel grid explicitly, such
4324 // that two neighboring triangles sharing an edge and projection
4325 // axis will have identical sampl espacing along their shared edge
4327 for (j = 0;j < 3;j++)
4329 if (j == triangle->axis)
4331 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4332 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4333 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4334 triangle->lmbase[k] = lmmins/lmscalepixels;
4335 triangle->lmscale[k] = lmscalepixels;
4338 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4341 // if all fit in this texture, we're done with this surface
4342 if (i == surface->num_triangles)
4344 // if we haven't maxed out the lightmap size yet, we retry the
4345 // entire surface batch...
4346 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4348 lm_texturesize *= 2;
4351 Mod_AllocLightmap_Free(&lmstate);
4352 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4355 // if we have maxed out the lightmap size, and this triangle does
4356 // not fit in the same texture as the rest of the surface, we have
4357 // to retry the entire surface in a new texture (can only use one)
4358 // with multiple retries, the lightmap quality degrades until it
4359 // fits (or gives up)
4360 if (surfaceindex > 0)
4362 Mod_AllocLightmap_Reset(&lmstate);
4366 Mod_AllocLightmap_Free(&lmstate);
4368 // now put triangles together into lightmap textures, and do not allow
4369 // triangles of a surface to go into different textures (as that would
4370 // require rewriting the surface list)
4371 model->brushq3.deluxemapping_modelspace = true;
4372 model->brushq3.deluxemapping = true;
4373 model->brushq3.num_mergedlightmaps = lightmapnumber;
4374 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4375 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4376 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4377 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4378 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4380 surface = model->data_surfaces + surfaceindex;
4381 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4382 for (i = 0;i < surface->num_triangles;i++)
4384 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4385 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4386 VectorNormalize(trianglenormal);
4387 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4388 axis = triangle->axis;
4389 axis1 = axis == 0 ? 1 : 0;
4390 axis2 = axis == 2 ? 1 : 2;
4391 lmiscale[0] = 1.0f / triangle->lmscale[0];
4392 lmiscale[1] = 1.0f / triangle->lmscale[1];
4393 if (trianglenormal[axis] < 0)
4394 VectorNegate(trianglenormal, trianglenormal);
4395 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4396 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4397 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4398 for (j = 0;j < 3;j++)
4400 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4401 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4402 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4404 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4405 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4406 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4407 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]);
4417 forward[1] = 1.0f / triangle->lmscale[0];
4421 left[2] = 1.0f / triangle->lmscale[1];
4426 origin[1] = triangle->lmbase[0];
4427 origin[2] = triangle->lmbase[1];
4430 forward[0] = 1.0f / triangle->lmscale[0];
4435 left[2] = 1.0f / triangle->lmscale[1];
4439 origin[0] = triangle->lmbase[0];
4441 origin[2] = triangle->lmbase[1];
4444 forward[0] = 1.0f / triangle->lmscale[0];
4448 left[1] = 1.0f / triangle->lmscale[1];
4453 origin[0] = triangle->lmbase[0];
4454 origin[1] = triangle->lmbase[1];
4458 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4460 #define LM_DIST_EPSILON (1.0f / 32.0f)
4461 for (y = 0;y < triangle->lmsize[1];y++)
4463 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4464 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4466 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4467 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4468 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4469 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4470 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4476 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4478 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);
4479 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);
4483 Mem_Free(lightmappixels);
4484 if (deluxemappixels)
4485 Mem_Free(deluxemappixels);
4487 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4489 surface = model->data_surfaces + surfaceindex;
4490 if (!surface->num_triangles)
4492 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4493 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4494 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4495 surface->lightmapinfo = NULL;
4498 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4499 model->brushq1.lightdata = NULL;
4500 model->brushq1.lightmapupdateflags = NULL;
4501 model->brushq1.firstrender = false;
4502 model->brushq1.num_lightstyles = 0;
4503 model->brushq1.data_lightstyleinfo = NULL;
4504 for (i = 0;i < model->brush.numsubmodels;i++)
4506 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4507 model->brush.submodels[i]->brushq1.firstrender = false;
4508 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4509 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4513 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4516 for (i = 0;i < model->surfmesh.num_vertices;i++)
4517 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4520 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4527 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4529 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4530 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4532 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4533 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4535 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4536 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4542 extern cvar_t mod_q3bsp_nolightmaps;
4543 static void Mod_GenerateLightmaps(dp_model_t *model)
4545 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4546 dp_model_t *oldloadmodel = loadmodel;
4549 Mod_GenerateLightmaps_InitSampleOffsets(model);
4550 Mod_GenerateLightmaps_DestroyLightmaps(model);
4551 Mod_GenerateLightmaps_UnweldTriangles(model);
4552 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4553 Mod_GenerateLightmaps_CreateLights(model);
4554 if(!mod_q3bsp_nolightmaps.integer)
4555 Mod_GenerateLightmaps_CreateLightmaps(model);
4556 Mod_GenerateLightmaps_UpdateVertexColors(model);
4557 Mod_GenerateLightmaps_UpdateLightGrid(model);
4558 Mod_GenerateLightmaps_DestroyLights(model);
4559 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4561 loadmodel = oldloadmodel;
4564 static void Mod_GenerateLightmaps_f(void)
4566 if (Cmd_Argc() != 1)
4568 Con_Printf("usage: mod_generatelightmaps\n");
4573 Con_Printf("no worldmodel loaded\n");
4576 Mod_GenerateLightmaps(cl.worldmodel);
4579 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4581 memset(mod, 0, sizeof(*mod));
4582 strlcpy(mod->name, name, sizeof(mod->name));
4583 mod->mempool = Mem_AllocPool(name, 0, NULL);
4584 mod->texturepool = R_AllocTexturePool();
4585 mod->Draw = R_Q1BSP_Draw;
4586 mod->DrawDepth = R_Q1BSP_DrawDepth;
4587 mod->DrawDebug = R_Q1BSP_DrawDebug;
4588 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4589 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4590 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4591 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
4592 mod->DrawLight = R_Q1BSP_DrawLight;
4595 void Mod_Mesh_Destroy(dp_model_t *mod)
4597 Mod_UnloadModel(mod);
4600 // resets the mesh model to have no geometry to render, ready for a new frame -
4601 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4602 void Mod_Mesh_Reset(dp_model_t *mod)
4604 mod->num_surfaces = 0;
4605 mod->surfmesh.num_vertices = 0;
4606 mod->surfmesh.num_triangles = 0;
4607 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4608 mod->DrawSky = NULL; // will be set if a texture needs it
4609 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4612 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name)
4616 for (i = 0; i < mod->num_textures; i++)
4617 if (!strcmp(mod->data_textures[i].name, name))
4618 return mod->data_textures + i;
4619 if (mod->max_textures <= mod->num_textures)
4621 texture_t *oldtextures = mod->data_textures;
4622 mod->max_textures = max(mod->max_textures * 2, 1024);
4623 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4624 // update the pointers
4625 for (i = 0; i < mod->num_surfaces; i++)
4626 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4628 t = &mod->data_textures[mod->num_textures++];
4629 Mod_LoadTextureFromQ3Shader(t, name, false, true, 0);
4633 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex)
4636 // check if the proposed surface matches the last one we created
4637 if (mod->num_surfaces == 0 || mod->data_surfaces[mod->num_surfaces - 1].texture != tex)
4639 if (mod->max_surfaces == mod->num_surfaces)
4641 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4642 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4643 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4645 surf = mod->data_surfaces + mod->num_surfaces;
4646 mod->num_surfaces++;
4647 memset(surf, 0, sizeof(*surf));
4648 surf->texture = tex;
4649 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4650 surf->num_firstvertex = mod->surfmesh.num_vertices;
4651 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4652 mod->DrawSky = R_Q1BSP_DrawSky;
4653 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4654 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4657 return mod->data_surfaces + mod->num_surfaces - 1;
4660 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)
4662 int hashindex, h, vnum, mask;
4663 surfmesh_t *mesh = &mod->surfmesh;
4664 if (mesh->max_vertices == mesh->num_vertices)
4666 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4667 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4668 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4669 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4670 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4671 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4672 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4673 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4674 // rebuild the hash table
4675 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4676 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4677 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4678 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4679 mask = mod->surfmesh.num_vertexhashsize - 1;
4680 // no need to hash the vertices for the entire model, the latest surface will suffice.
4681 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4683 // this uses prime numbers intentionally for computing the hash
4684 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;
4685 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4686 ; // just iterate until we find the terminator
4687 mesh->data_vertexhash[h] = vnum;
4690 mask = mod->surfmesh.num_vertexhashsize - 1;
4691 // this uses prime numbers intentionally for computing the hash
4692 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4693 // when possible find an identical vertex within the same surface and return it
4694 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4696 if (vnum >= surf->num_firstvertex
4697 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4698 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4699 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4700 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4701 && 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)
4704 // add the new vertex
4705 vnum = mesh->num_vertices++;
4706 if (surf->num_vertices > 0)
4708 if (surf->mins[0] > x) surf->mins[0] = x;
4709 if (surf->mins[1] > y) surf->mins[1] = y;
4710 if (surf->mins[2] > z) surf->mins[2] = z;
4711 if (surf->maxs[0] < x) surf->maxs[0] = x;
4712 if (surf->maxs[1] < y) surf->maxs[1] = y;
4713 if (surf->maxs[2] < z) surf->maxs[2] = z;
4717 VectorSet(surf->mins, x, y, z);
4718 VectorSet(surf->maxs, x, y, z);
4720 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4721 mesh->data_vertexhash[h] = vnum;
4722 mesh->data_vertex3f[vnum * 3 + 0] = x;
4723 mesh->data_vertex3f[vnum * 3 + 1] = y;
4724 mesh->data_vertex3f[vnum * 3 + 2] = z;
4725 mesh->data_normal3f[vnum * 3 + 0] = nx;
4726 mesh->data_normal3f[vnum * 3 + 1] = ny;
4727 mesh->data_normal3f[vnum * 3 + 2] = nz;
4728 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4729 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4730 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4731 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4732 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4733 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4734 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4735 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4739 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4741 surfmesh_t *mesh = &mod->surfmesh;
4742 if (mesh->max_triangles == mesh->num_triangles)
4744 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4745 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4746 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4748 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4749 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4750 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4751 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4752 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4753 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4754 mesh->num_triangles++;
4755 surf->num_triangles++;
4758 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4762 msurface_t *surf, *surf2;
4764 // build the sorted surfaces list properly to reduce material setup
4765 // this is easy because we're just sorting on texture and don't care about the order of textures
4766 mod->nummodelsurfaces = 0;
4767 for (i = 0; i < mod->num_surfaces; i++)
4768 mod->data_surfaces[i].included = false;
4769 for (i = 0; i < mod->num_surfaces; i++)
4771 surf = mod->data_surfaces + i;
4774 tex = surf->texture;
4775 // j = i is intentional
4776 for (j = i; j < mod->num_surfaces; j++)
4778 surf2 = mod->data_surfaces + j;
4779 if (surf2->included)
4781 if (surf2->texture == tex)
4783 surf2->included = true;
4784 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4790 void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4793 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4795 if (mod->surfmesh.num_vertices > 0)
4797 // calculate normalmins/normalmaxs
4798 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4799 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4800 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4802 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4803 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4804 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4805 // expand bounds to include this vertex
4806 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4807 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4808 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4809 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4810 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4811 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4813 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4814 // (fast but less accurate than doing it per vertex)
4815 x2a = mod->normalmins[0] * mod->normalmins[0];
4816 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4817 y2a = mod->normalmins[1] * mod->normalmins[1];
4818 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4819 z2a = mod->normalmins[2] * mod->normalmins[2];
4820 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4824 yawradius = sqrt(x2 + y2);
4825 rotatedradius = sqrt(x2 + y2 + z2);
4826 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4827 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4828 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4829 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4830 mod->radius = rotatedradius;
4831 mod->radius2 = x2 + y2 + z2;
4835 VectorClear(mod->normalmins);
4836 VectorClear(mod->normalmaxs);
4837 VectorClear(mod->yawmins);
4838 VectorClear(mod->yawmaxs);
4839 VectorClear(mod->rotatedmins);
4840 VectorClear(mod->rotatedmaxs);
4846 void Mod_Mesh_Finalize(dp_model_t *mod)
4848 Mod_Mesh_ComputeBounds(mod);
4849 Mod_Mesh_MakeSortedSurfaces(mod);
4850 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);