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(mempool_t *mempool, skinframe_t *skinframe)
2417 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(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(mempool_t *mempool, const char *modelname, q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename)
2433 texture_shaderpass_t *shaderpass = (texture_shaderpass_t *)Mem_Alloc(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++)
2445 shaderpass->skinframes[j] = R_SkinFrame_LoadExternal(layer->texturename[j], texflags, false, true);
2449 qboolean Mod_LoadTextureFromQ3Shader(mempool_t *mempool, const char *modelname, texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags)
2451 int texflagsmask, texflagsor;
2452 qboolean success = true;
2453 q3shaderinfo_t *shader;
2456 strlcpy(texture->name, name, sizeof(texture->name));
2457 texture->basealpha = 1.0f;
2458 shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
2460 // allow disabling of picmip or compression by defaulttexflags
2462 if(!(defaulttexflags & TEXF_PICMIP))
2463 texflagsmask &= ~TEXF_PICMIP;
2464 if(!(defaulttexflags & TEXF_COMPRESS))
2465 texflagsmask &= ~TEXF_COMPRESS;
2467 if(defaulttexflags & TEXF_ISWORLD)
2468 texflagsor |= TEXF_ISWORLD;
2469 if(defaulttexflags & TEXF_ISSPRITE)
2470 texflagsor |= TEXF_ISSPRITE;
2471 // unless later loaded from the shader
2472 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2473 texture->offsetscale = 1;
2474 texture->offsetbias = 0;
2475 texture->specularscalemod = 1;
2476 texture->specularpowermod = 1;
2477 texture->rtlightambient = 0;
2478 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2479 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2480 // JUST GREP FOR "specularscalemod = 1".
2484 if (developer_loading.integer)
2485 Con_Printf("%s: loaded shader for %s\n", modelname, name);
2487 if (shader->surfaceparms & Q3SURFACEPARM_SKY)
2489 texture->basematerialflags = MATERIALFLAG_SKY;
2490 if (shader->skyboxname[0] && loadmodel)
2492 // quake3 seems to append a _ to the skybox name, so this must do so as well
2493 dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
2496 else if ((texture->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
2497 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2499 texture->basematerialflags = MATERIALFLAG_WALL;
2501 if (shader->layers[0].alphatest)
2502 texture->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
2503 if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
2504 texture->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
2505 if (shader->textureflags & Q3TEXTUREFLAG_POLYGONOFFSET)
2507 texture->biaspolygonoffset += shader->biaspolygonoffset;
2508 texture->biaspolygonfactor += shader->biaspolygonfactor;
2510 if (shader->textureflags & Q3TEXTUREFLAG_REFRACTION)
2511 texture->basematerialflags |= MATERIALFLAG_REFRACTION;
2512 if (shader->textureflags & Q3TEXTUREFLAG_REFLECTION)
2513 texture->basematerialflags |= MATERIALFLAG_REFLECTION;
2514 if (shader->textureflags & Q3TEXTUREFLAG_WATERSHADER)
2515 texture->basematerialflags |= MATERIALFLAG_WATERSHADER;
2516 if (shader->textureflags & Q3TEXTUREFLAG_CAMERA)
2517 texture->basematerialflags |= MATERIALFLAG_CAMERA;
2518 texture->customblendfunc[0] = GL_ONE;
2519 texture->customblendfunc[1] = GL_ZERO;
2520 texture->transparentsort = shader->transparentsort;
2521 if (shader->numlayers > 0)
2523 texture->customblendfunc[0] = shader->layers[0].blendfunc[0];
2524 texture->customblendfunc[1] = shader->layers[0].blendfunc[1];
2526 Q3 shader blendfuncs actually used in the game (* = supported by DP)
2527 * additive GL_ONE GL_ONE
2528 additive weird GL_ONE GL_SRC_ALPHA
2529 additive weird 2 GL_ONE GL_ONE_MINUS_SRC_ALPHA
2530 * alpha GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
2531 alpha inverse GL_ONE_MINUS_SRC_ALPHA GL_SRC_ALPHA
2532 brighten GL_DST_COLOR GL_ONE
2533 brighten GL_ONE GL_SRC_COLOR
2534 brighten weird GL_DST_COLOR GL_ONE_MINUS_DST_ALPHA
2535 brighten weird 2 GL_DST_COLOR GL_SRC_ALPHA
2536 * modulate GL_DST_COLOR GL_ZERO
2537 * modulate GL_ZERO GL_SRC_COLOR
2538 modulate inverse GL_ZERO GL_ONE_MINUS_SRC_COLOR
2539 modulate inverse alpha GL_ZERO GL_SRC_ALPHA
2540 modulate weird inverse GL_ONE_MINUS_DST_COLOR GL_ZERO
2541 * modulate x2 GL_DST_COLOR GL_SRC_COLOR
2542 * no blend GL_ONE GL_ZERO
2543 nothing GL_ZERO GL_ONE
2545 // if not opaque, figure out what blendfunc to use
2546 if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
2548 if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
2549 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2550 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
2551 texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2552 else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2553 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2555 texture->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2558 if (!shader->lighting)
2559 texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
2561 // here be dragons: convert quake3 shaders to material
2562 if (shader->numlayers > 0)
2565 int terrainbackgroundlayer = -1;
2566 int lightmaplayer = -1;
2567 int alphagenspecularlayer = -1;
2568 int rgbgenvertexlayer = -1;
2569 int rgbgendiffuselayer = -1;
2570 int materiallayer = -1;
2571 int endofprelayers = 0;
2572 int firstpostlayer = 0;
2573 int shaderpassindex = 0;
2574 for (i = 0; i < shader->numlayers; i++)
2576 if (shader->layers[i].texturename != NULL && !strcasecmp(shader->layers[i].texturename[0], "$lightmap"))
2578 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_VERTEX)
2579 rgbgenvertexlayer = i;
2580 if (shader->layers[i].rgbgen.rgbgen == Q3RGBGEN_LIGHTINGDIFFUSE)
2581 rgbgendiffuselayer = i;
2582 if (shader->layers[i].alphagen.alphagen == Q3ALPHAGEN_LIGHTINGSPECULAR)
2583 alphagenspecularlayer = i;
2585 if (shader->numlayers >= 2
2586 && shader->layers[1].alphagen.alphagen == Q3ALPHAGEN_VERTEX
2587 && (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
2588 && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
2589 || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
2591 // terrain blend or certain other effects involving alphatest over a regular layer
2592 terrainbackgroundlayer = 0;
2594 // terrain may be vertex lit (in which case both layers are rgbGen vertex) or lightmapped (in which ase the third layer is lightmap)
2595 firstpostlayer = lightmaplayer >= 0 ? lightmaplayer + 1 : materiallayer + 1;
2597 else if (lightmaplayer == 0)
2599 // ordinary texture but with $lightmap before diffuse
2601 firstpostlayer = lightmaplayer + 2;
2603 else if (lightmaplayer >= 1)
2605 // ordinary texture - we don't properly apply lighting to the prelayers, but oh well...
2606 endofprelayers = lightmaplayer - 1;
2607 materiallayer = lightmaplayer - 1;
2608 firstpostlayer = lightmaplayer + 1;
2610 else if (rgbgenvertexlayer >= 0)
2612 // map models with baked lighting
2613 materiallayer = rgbgenvertexlayer;
2614 endofprelayers = rgbgenvertexlayer;
2615 firstpostlayer = rgbgenvertexlayer + 1;
2617 else if (rgbgendiffuselayer >= 0)
2619 // entity models with dynamic lighting
2620 materiallayer = rgbgendiffuselayer;
2621 endofprelayers = rgbgendiffuselayer;
2622 firstpostlayer = rgbgendiffuselayer + 1;
2623 // 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)...
2624 if (alphagenspecularlayer >= 0)
2625 firstpostlayer = alphagenspecularlayer + 1;
2629 // special effects shaders - treat first as primary layer and do everything else as post
2634 // convert the main material layer
2635 // 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
2636 if (materiallayer >= 0)
2637 texture->materialshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[materiallayer], materiallayer, (shader->layers[materiallayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2638 // convert the terrain background blend layer (if any)
2639 if (terrainbackgroundlayer >= 0)
2640 texture->backgroundshaderpass = texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[terrainbackgroundlayer], terrainbackgroundlayer, (shader->layers[terrainbackgroundlayer].dptexflags & texflagsmask) | texflagsor, texture->name);
2641 // convert the prepass layers (if any)
2642 texture->startpreshaderpass = shaderpassindex;
2643 for (i = 0; i < endofprelayers; i++)
2644 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2645 texture->endpreshaderpass = shaderpassindex;
2646 texture->startpostshaderpass = shaderpassindex;
2647 // convert the postpass layers (if any)
2648 for (i = firstpostlayer; i < shader->numlayers; i++)
2649 texture->shaderpasses[shaderpassindex++] = Mod_CreateShaderPassFromQ3ShaderLayer(mempool, modelname, &shader->layers[i], i, (shader->layers[i].dptexflags & texflagsmask) | texflagsor, texture->name);
2650 texture->startpostshaderpass = shaderpassindex;
2653 if (shader->dpshadow)
2654 texture->basematerialflags &= ~MATERIALFLAG_NOSHADOW;
2655 if (shader->dpnoshadow)
2656 texture->basematerialflags |= MATERIALFLAG_NOSHADOW;
2657 if (shader->dpnortlight)
2658 texture->basematerialflags |= MATERIALFLAG_NORTLIGHT;
2659 if (shader->vertexalpha)
2660 texture->basematerialflags |= MATERIALFLAG_ALPHAGEN_VERTEX;
2661 memcpy(texture->deforms, shader->deforms, sizeof(texture->deforms));
2662 texture->reflectmin = shader->reflectmin;
2663 texture->reflectmax = shader->reflectmax;
2664 texture->refractfactor = shader->refractfactor;
2665 Vector4Copy(shader->refractcolor4f, texture->refractcolor4f);
2666 texture->reflectfactor = shader->reflectfactor;
2667 Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
2668 texture->r_water_wateralpha = shader->r_water_wateralpha;
2669 Vector2Copy(shader->r_water_waterscroll, texture->r_water_waterscroll);
2670 texture->offsetmapping = shader->offsetmapping;
2671 texture->offsetscale = shader->offsetscale;
2672 texture->offsetbias = shader->offsetbias;
2673 texture->specularscalemod = shader->specularscalemod;
2674 texture->specularpowermod = shader->specularpowermod;
2675 texture->rtlightambient = shader->rtlightambient;
2676 if (shader->dpreflectcube[0])
2677 texture->reflectcubetexture = R_GetCubemap(shader->dpreflectcube);
2679 // set up default supercontents (on q3bsp this is overridden by the q3bsp loader)
2680 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2681 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents = SUPERCONTENTS_LAVA ;
2682 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents = SUPERCONTENTS_SLIME ;
2683 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents = SUPERCONTENTS_WATER ;
2684 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents = 0 ;
2685 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents = SUPERCONTENTS_PLAYERCLIP ;
2686 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents = SUPERCONTENTS_MONSTERCLIP ;
2687 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents = SUPERCONTENTS_SKY ;
2689 // if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->supercontents |= SUPERCONTENTS_ALPHASHADOW ;
2690 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->supercontents |= SUPERCONTENTS_AREAPORTAL ;
2691 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->supercontents |= SUPERCONTENTS_CLUSTERPORTAL;
2692 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->supercontents |= SUPERCONTENTS_DETAIL ;
2693 if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->supercontents |= SUPERCONTENTS_DONOTENTER ;
2694 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->supercontents |= SUPERCONTENTS_FOG ;
2695 if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->supercontents |= SUPERCONTENTS_LAVA ;
2696 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->supercontents |= SUPERCONTENTS_LIGHTFILTER ;
2697 // if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->supercontents |= SUPERCONTENTS_METALSTEPS ;
2698 // if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->supercontents |= SUPERCONTENTS_NODAMAGE ;
2699 // if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->supercontents |= SUPERCONTENTS_NODLIGHT ;
2700 // if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->supercontents |= SUPERCONTENTS_NODRAW ;
2701 if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->supercontents |= SUPERCONTENTS_NODROP ;
2702 // if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->supercontents |= SUPERCONTENTS_NOIMPACT ;
2703 // if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->supercontents |= SUPERCONTENTS_NOLIGHTMAP ;
2704 // if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->supercontents |= SUPERCONTENTS_NOMARKS ;
2705 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->supercontents |= SUPERCONTENTS_NOMIPMAPS ;
2706 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->supercontents &=~SUPERCONTENTS_SOLID ;
2707 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->supercontents |= SUPERCONTENTS_ORIGIN ;
2708 if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->supercontents |= SUPERCONTENTS_PLAYERCLIP ;
2709 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->supercontents |= SUPERCONTENTS_SKY ;
2710 // if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->supercontents |= SUPERCONTENTS_SLICK ;
2711 if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->supercontents |= SUPERCONTENTS_SLIME ;
2712 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->supercontents |= SUPERCONTENTS_STRUCTURAL ;
2713 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->supercontents |= SUPERCONTENTS_TRANS ;
2714 if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->supercontents |= SUPERCONTENTS_WATER ;
2715 // if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->supercontents |= SUPERCONTENTS_POINTLIGHT ;
2716 // if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->supercontents |= SUPERCONTENTS_HINT ;
2717 // if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->supercontents |= SUPERCONTENTS_DUST ;
2718 if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->supercontents |= SUPERCONTENTS_BOTCLIP | SUPERCONTENTS_MONSTERCLIP;
2719 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->supercontents |= SUPERCONTENTS_LIGHTGRID ;
2720 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->supercontents |= SUPERCONTENTS_ANTIPORTAL ;
2722 texture->surfaceflags = shader->surfaceflags;
2723 if (shader->surfaceparms & Q3SURFACEPARM_ALPHASHADOW ) texture->surfaceflags |= Q3SURFACEFLAG_ALPHASHADOW ;
2724 // if (shader->surfaceparms & Q3SURFACEPARM_AREAPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_AREAPORTAL ;
2725 // if (shader->surfaceparms & Q3SURFACEPARM_CLUSTERPORTAL) texture->surfaceflags |= Q3SURFACEFLAG_CLUSTERPORTAL;
2726 // if (shader->surfaceparms & Q3SURFACEPARM_DETAIL ) texture->surfaceflags |= Q3SURFACEFLAG_DETAIL ;
2727 // if (shader->surfaceparms & Q3SURFACEPARM_DONOTENTER ) texture->surfaceflags |= Q3SURFACEFLAG_DONOTENTER ;
2728 // if (shader->surfaceparms & Q3SURFACEPARM_FOG ) texture->surfaceflags |= Q3SURFACEFLAG_FOG ;
2729 // if (shader->surfaceparms & Q3SURFACEPARM_LAVA ) texture->surfaceflags |= Q3SURFACEFLAG_LAVA ;
2730 if (shader->surfaceparms & Q3SURFACEPARM_LIGHTFILTER ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTFILTER ;
2731 if (shader->surfaceparms & Q3SURFACEPARM_METALSTEPS ) texture->surfaceflags |= Q3SURFACEFLAG_METALSTEPS ;
2732 if (shader->surfaceparms & Q3SURFACEPARM_NODAMAGE ) texture->surfaceflags |= Q3SURFACEFLAG_NODAMAGE ;
2733 if (shader->surfaceparms & Q3SURFACEPARM_NODLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_NODLIGHT ;
2734 if (shader->surfaceparms & Q3SURFACEPARM_NODRAW ) texture->surfaceflags |= Q3SURFACEFLAG_NODRAW ;
2735 // if (shader->surfaceparms & Q3SURFACEPARM_NODROP ) texture->surfaceflags |= Q3SURFACEFLAG_NODROP ;
2736 if (shader->surfaceparms & Q3SURFACEPARM_NOIMPACT ) texture->surfaceflags |= Q3SURFACEFLAG_NOIMPACT ;
2737 if (shader->surfaceparms & Q3SURFACEPARM_NOLIGHTMAP ) texture->surfaceflags |= Q3SURFACEFLAG_NOLIGHTMAP ;
2738 if (shader->surfaceparms & Q3SURFACEPARM_NOMARKS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMARKS ;
2739 // if (shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS ) texture->surfaceflags |= Q3SURFACEFLAG_NOMIPMAPS ;
2740 if (shader->surfaceparms & Q3SURFACEPARM_NONSOLID ) texture->surfaceflags |= Q3SURFACEFLAG_NONSOLID ;
2741 // if (shader->surfaceparms & Q3SURFACEPARM_ORIGIN ) texture->surfaceflags |= Q3SURFACEFLAG_ORIGIN ;
2742 // if (shader->surfaceparms & Q3SURFACEPARM_PLAYERCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_PLAYERCLIP ;
2743 if (shader->surfaceparms & Q3SURFACEPARM_SKY ) texture->surfaceflags |= Q3SURFACEFLAG_SKY ;
2744 if (shader->surfaceparms & Q3SURFACEPARM_SLICK ) texture->surfaceflags |= Q3SURFACEFLAG_SLICK ;
2745 // if (shader->surfaceparms & Q3SURFACEPARM_SLIME ) texture->surfaceflags |= Q3SURFACEFLAG_SLIME ;
2746 // if (shader->surfaceparms & Q3SURFACEPARM_STRUCTURAL ) texture->surfaceflags |= Q3SURFACEFLAG_STRUCTURAL ;
2747 // if (shader->surfaceparms & Q3SURFACEPARM_TRANS ) texture->surfaceflags |= Q3SURFACEFLAG_TRANS ;
2748 // if (shader->surfaceparms & Q3SURFACEPARM_WATER ) texture->surfaceflags |= Q3SURFACEFLAG_WATER ;
2749 if (shader->surfaceparms & Q3SURFACEPARM_POINTLIGHT ) texture->surfaceflags |= Q3SURFACEFLAG_POINTLIGHT ;
2750 if (shader->surfaceparms & Q3SURFACEPARM_HINT ) texture->surfaceflags |= Q3SURFACEFLAG_HINT ;
2751 if (shader->surfaceparms & Q3SURFACEPARM_DUST ) texture->surfaceflags |= Q3SURFACEFLAG_DUST ;
2752 // if (shader->surfaceparms & Q3SURFACEPARM_BOTCLIP ) texture->surfaceflags |= Q3SURFACEFLAG_BOTCLIP ;
2753 // if (shader->surfaceparms & Q3SURFACEPARM_LIGHTGRID ) texture->surfaceflags |= Q3SURFACEFLAG_LIGHTGRID ;
2754 // if (shader->surfaceparms & Q3SURFACEPARM_ANTIPORTAL ) texture->surfaceflags |= Q3SURFACEFLAG_ANTIPORTAL ;
2756 if (shader->dpmeshcollisions)
2757 texture->basematerialflags |= MATERIALFLAG_MESHCOLLISIONS;
2758 if (shader->dpshaderkill && developer_extra.integer)
2759 Con_DPrintf("^1%s:^7 killing shader ^3\"%s\" because of cvar\n", modelname, name);
2761 else if (!strcmp(texture->name, "noshader") || !texture->name[0])
2763 if (developer_extra.integer)
2764 Con_DPrintf("^1%s:^7 using fallback noshader material for ^3\"%s\"\n", modelname, name);
2765 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2767 else if (!strcmp(texture->name, "common/nodraw") || !strcmp(texture->name, "textures/common/nodraw"))
2769 if (developer_extra.integer)
2770 Con_DPrintf("^1%s:^7 using fallback nodraw material for ^3\"%s\"\n", modelname, name);
2771 texture->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2772 texture->supercontents = SUPERCONTENTS_SOLID;
2776 if (developer_extra.integer)
2777 Con_DPrintf("^1%s:^7 No shader found for texture ^3\"%s\"\n", modelname, texture->name);
2778 if (texture->surfaceflags & Q3SURFACEFLAG_NODRAW)
2780 texture->basematerialflags |= MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
2781 texture->supercontents = SUPERCONTENTS_SOLID;
2783 else if (texture->surfaceflags & Q3SURFACEFLAG_SKY)
2785 texture->basematerialflags |= MATERIALFLAG_SKY;
2786 texture->supercontents = SUPERCONTENTS_SKY;
2790 texture->basematerialflags |= MATERIALFLAG_WALL;
2791 texture->supercontents = SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
2793 if(cls.state == ca_dedicated)
2795 texture->materialshaderpass = NULL;
2802 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadExternal(texture->name, defaulttexflags, false, true));
2803 if (texture->materialshaderpass->skinframes[0]->hasalpha)
2804 texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
2805 if (texture->q2contents)
2806 texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(texture->q2contents);
2810 if (!success && warnmissing)
2811 Con_Printf("^1%s:^7 could not load texture ^3\"%s\"\n", modelname, texture->name);
2814 // init the animation variables
2815 texture->currentframe = texture;
2816 texture->currentmaterialflags = texture->basematerialflags;
2817 if (!texture->materialshaderpass)
2818 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, R_SkinFrame_LoadMissing());
2819 if (!texture->materialshaderpass->skinframes[0])
2820 texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
2821 texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
2822 texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
2826 void Mod_LoadCustomMaterial(mempool_t *mempool, texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
2828 if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
2829 Con_DPrintf("^1Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", texture->name);
2831 strlcpy(texture->name, name, sizeof(texture->name));
2832 texture->basealpha = 1.0f;
2833 texture->basematerialflags = materialflags;
2834 texture->supercontents = supercontents;
2836 texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
2837 texture->offsetscale = 1;
2838 texture->offsetbias = 0;
2839 texture->specularscalemod = 1;
2840 texture->specularpowermod = 1;
2841 texture->rtlightambient = 0;
2842 texture->transparentsort = TRANSPARENTSORT_DISTANCE;
2843 // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
2844 // JUST GREP FOR "specularscalemod = 1".
2846 if (developer_extra.integer)
2847 Con_DPrintf("^1Custom texture ^3\"%s\"\n", texture->name);
2849 texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(mempool, skinframe);
2851 // init the animation variables
2852 texture->currentmaterialflags = texture->basematerialflags;
2853 texture->currentframe = texture;
2854 texture->currentskinframe = skinframe;
2855 texture->backgroundcurrentskinframe = NULL;
2858 void Mod_UnloadCustomMaterial(texture_t *texture, qboolean purgeskins)
2861 for (i = 0; i < sizeof(texture->shaderpasses) / sizeof(texture->shaderpasses[0]); i++)
2863 if (texture->shaderpasses[i])
2866 for (j = 0; j < sizeof(texture->shaderpasses[i]->skinframes) / sizeof(skinframe_t *);j++)
2867 if (texture->shaderpasses[i]->skinframes[j] && texture->shaderpasses[i]->skinframes[j]->base)
2868 R_SkinFrame_PurgeSkinFrame(texture->shaderpasses[i]->skinframes[j]);
2869 Mem_Free(texture->shaderpasses[i]);
2870 texture->shaderpasses[i] = NULL;
2873 texture->materialshaderpass = NULL;
2874 texture->currentskinframe = NULL;
2875 texture->backgroundcurrentskinframe = NULL;
2878 skinfile_t *Mod_LoadSkinFiles(void)
2880 int i, words, line, wordsoverflow;
2883 skinfile_t *skinfile = NULL, *first = NULL;
2884 skinfileitem_t *skinfileitem;
2885 char word[10][MAX_QPATH];
2890 U_bodyBox,models/players/Legoman/BikerA2.tga
2891 U_RArm,models/players/Legoman/BikerA1.tga
2892 U_LArm,models/players/Legoman/BikerA1.tga
2893 U_armor,common/nodraw
2894 U_sword,common/nodraw
2895 U_shield,common/nodraw
2896 U_homb,common/nodraw
2897 U_backpack,common/nodraw
2898 U_colcha,common/nodraw
2903 memset(word, 0, sizeof(word));
2904 for (i = 0;i < 256 && (data = text = (char *)FS_LoadFile(va(vabuf, sizeof(vabuf), "%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
2906 // If it's the first file we parse
2907 if (skinfile == NULL)
2909 skinfile = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2914 skinfile->next = (skinfile_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfile_t));
2915 skinfile = skinfile->next;
2917 skinfile->next = NULL;
2919 for(line = 0;;line++)
2922 if (!COM_ParseToken_QuakeC(&data, true))
2924 if (!strcmp(com_token, "\n"))
2927 wordsoverflow = false;
2931 strlcpy(word[words++], com_token, sizeof (word[0]));
2933 wordsoverflow = true;
2935 while (COM_ParseToken_QuakeC(&data, true) && strcmp(com_token, "\n"));
2938 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);
2941 // words is always >= 1
2942 if (!strcmp(word[0], "replace"))
2946 if (developer_loading.integer)
2947 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[1], word[2]);
2948 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2949 skinfileitem->next = skinfile->items;
2950 skinfile->items = skinfileitem;
2951 strlcpy (skinfileitem->name, word[1], sizeof (skinfileitem->name));
2952 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2955 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]);
2957 else if (words >= 2 && !strncmp(word[0], "tag_", 4))
2959 // tag name, like "tag_weapon,"
2960 // not used for anything (not even in Quake3)
2962 else if (words >= 2 && !strcmp(word[1], ","))
2964 // mesh shader name, like "U_RArm,models/players/Legoman/BikerA1.tga"
2965 if (developer_loading.integer)
2966 Con_Printf("Mod_LoadSkinFiles: parsed mesh \"%s\" shader replacement \"%s\"\n", word[0], word[2]);
2967 skinfileitem = (skinfileitem_t *)Mem_Alloc(loadmodel->mempool, sizeof(skinfileitem_t));
2968 skinfileitem->next = skinfile->items;
2969 skinfile->items = skinfileitem;
2970 strlcpy (skinfileitem->name, word[0], sizeof (skinfileitem->name));
2971 strlcpy (skinfileitem->replacement, word[2], sizeof (skinfileitem->replacement));
2974 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);
2979 loadmodel->numskins = i;
2983 void Mod_FreeSkinFiles(skinfile_t *skinfile)
2986 skinfileitem_t *skinfileitem, *nextitem;
2987 for (;skinfile;skinfile = next)
2989 next = skinfile->next;
2990 for (skinfileitem = skinfile->items;skinfileitem;skinfileitem = nextitem)
2992 nextitem = skinfileitem->next;
2993 Mem_Free(skinfileitem);
2999 int Mod_CountSkinFiles(skinfile_t *skinfile)
3002 for (i = 0;skinfile;skinfile = skinfile->next, i++);
3006 void Mod_SnapVertices(int numcomponents, int numvertices, float *vertices, float snap)
3009 double isnap = 1.0 / snap;
3010 for (i = 0;i < numvertices*numcomponents;i++)
3011 vertices[i] = floor(vertices[i]*isnap)*snap;
3014 int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f)
3016 int i, outtriangles;
3017 float edgedir1[3], edgedir2[3], temp[3];
3018 // a degenerate triangle is one with no width (thickness, surface area)
3019 // these are characterized by having all 3 points colinear (along a line)
3020 // or having two points identical
3021 // the simplest check is to calculate the triangle's area
3022 for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3)
3024 // calculate first edge
3025 VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir1);
3026 VectorSubtract(vertex3f + inelement3i[2] * 3, vertex3f + inelement3i[0] * 3, edgedir2);
3027 CrossProduct(edgedir1, edgedir2, temp);
3028 if (VectorLength2(temp) < 0.001f)
3029 continue; // degenerate triangle (no area)
3030 // valid triangle (has area)
3031 VectorCopy(inelement3i, outelement3i);
3035 return outtriangles;
3038 void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
3041 int firstvertex, lastvertex;
3042 if (numelements > 0 && elements)
3044 firstvertex = lastvertex = elements[0];
3045 for (i = 1;i < numelements;i++)
3048 firstvertex = min(firstvertex, e);
3049 lastvertex = max(lastvertex, e);
3053 firstvertex = lastvertex = 0;
3054 if (firstvertexpointer)
3055 *firstvertexpointer = firstvertex;
3056 if (lastvertexpointer)
3057 *lastvertexpointer = lastvertex;
3060 void Mod_MakeSortedSurfaces(dp_model_t *mod)
3062 // make an optimal set of texture-sorted batches to draw...
3064 int *firstsurfacefortexture;
3065 int *numsurfacesfortexture;
3066 if (!mod->sortedmodelsurfaces)
3067 mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
3068 firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
3069 numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
3070 memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
3071 for (j = 0;j < mod->nummodelsurfaces;j++)
3073 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
3074 t = (int)(surface->texture - mod->data_textures);
3075 numsurfacesfortexture[t]++;
3078 for (t = 0;t < mod->num_textures;t++)
3080 firstsurfacefortexture[t] = j;
3081 j += numsurfacesfortexture[t];
3083 for (j = 0;j < mod->nummodelsurfaces;j++)
3085 const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
3086 t = (int)(surface->texture - mod->data_textures);
3087 mod->sortedmodelsurfaces[firstsurfacefortexture[t]++] = j + mod->firstmodelsurface;
3089 Mem_Free(firstsurfacefortexture);
3090 Mem_Free(numsurfacesfortexture);
3093 void Mod_BuildVBOs(void)
3095 if (!loadmodel->surfmesh.num_vertices)
3098 if (gl_paranoid.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
3101 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
3103 if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
3105 Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
3106 loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
3111 // build r_vertexmesh_t array
3112 // (compressed interleaved array for D3D)
3113 if (!loadmodel->surfmesh.data_vertexmesh && vid.useinterleavedarrays)
3116 int numvertices = loadmodel->surfmesh.num_vertices;
3117 r_vertexmesh_t *vertexmesh;
3118 loadmodel->surfmesh.data_vertexmesh = vertexmesh = (r_vertexmesh_t*)Mem_Alloc(loadmodel->mempool, numvertices * sizeof(r_vertexmesh_t));
3119 for (vertexindex = 0;vertexindex < numvertices;vertexindex++, vertexmesh++)
3121 VectorCopy(loadmodel->surfmesh.data_vertex3f + 3*vertexindex, vertexmesh->vertex3f);
3122 VectorScale(loadmodel->surfmesh.data_svector3f + 3*vertexindex, 1.0f, vertexmesh->svector3f);
3123 VectorScale(loadmodel->surfmesh.data_tvector3f + 3*vertexindex, 1.0f, vertexmesh->tvector3f);
3124 VectorScale(loadmodel->surfmesh.data_normal3f + 3*vertexindex, 1.0f, vertexmesh->normal3f);
3125 if (loadmodel->surfmesh.data_lightmapcolor4f)
3126 Vector4Copy(loadmodel->surfmesh.data_lightmapcolor4f + 4*vertexindex, vertexmesh->color4f);
3127 Vector2Copy(loadmodel->surfmesh.data_texcoordtexture2f + 2*vertexindex, vertexmesh->texcoordtexture2f);
3128 if (loadmodel->surfmesh.data_texcoordlightmap2f)
3129 Vector2Scale(loadmodel->surfmesh.data_texcoordlightmap2f + 2*vertexindex, 1.0f, vertexmesh->texcoordlightmap2f);
3130 if (loadmodel->surfmesh.data_skeletalindex4ub)
3131 Vector4Copy(loadmodel->surfmesh.data_skeletalindex4ub + 4*vertexindex, vertexmesh->skeletalindex4ub);
3132 if (loadmodel->surfmesh.data_skeletalweight4ub)
3133 Vector4Copy(loadmodel->surfmesh.data_skeletalweight4ub + 4*vertexindex, vertexmesh->skeletalweight4ub);
3137 // upload short indices as a buffer
3138 if (loadmodel->surfmesh.data_element3s && !loadmodel->surfmesh.data_element3s_indexbuffer)
3139 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);
3141 // upload int indices as a buffer
3142 if (loadmodel->surfmesh.data_element3i && !loadmodel->surfmesh.data_element3i_indexbuffer && !loadmodel->surfmesh.data_element3s)
3143 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);
3145 // only build a vbo if one has not already been created (this is important for brush models which load specially)
3146 // vertex buffer is several arrays and we put them in the same buffer
3148 // is this wise? the texcoordtexture2f array is used with dynamic
3149 // vertex/svector/tvector/normal when rendering animated models, on the
3150 // other hand animated models don't use a lot of vertices anyway...
3151 if (!loadmodel->surfmesh.vbo_vertexbuffer && !vid.useinterleavedarrays)
3156 loadmodel->surfmesh.vbooffset_vertexmesh = size;if (loadmodel->surfmesh.data_vertexmesh ) size += loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t);
3157 loadmodel->surfmesh.vbooffset_vertex3f = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3158 loadmodel->surfmesh.vbooffset_svector3f = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3159 loadmodel->surfmesh.vbooffset_tvector3f = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3160 loadmodel->surfmesh.vbooffset_normal3f = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
3161 loadmodel->surfmesh.vbooffset_texcoordtexture2f = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3162 loadmodel->surfmesh.vbooffset_texcoordlightmap2f = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
3163 loadmodel->surfmesh.vbooffset_lightmapcolor4f = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
3164 loadmodel->surfmesh.vbooffset_skeletalindex4ub = size;if (loadmodel->surfmesh.data_skeletalindex4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3165 loadmodel->surfmesh.vbooffset_skeletalweight4ub = size;if (loadmodel->surfmesh.data_skeletalweight4ub ) size += loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]);
3166 mem = (unsigned char *)Mem_Alloc(tempmempool, size);
3167 if (loadmodel->surfmesh.data_vertexmesh ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertexmesh , loadmodel->surfmesh.data_vertexmesh , loadmodel->surfmesh.num_vertices * sizeof(r_vertexmesh_t));
3168 if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertex3f , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3169 if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_svector3f , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3170 if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_tvector3f , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3171 if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_normal3f , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
3172 if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordtexture2f , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3173 if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordlightmap2f, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
3174 if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.vbooffset_lightmapcolor4f , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
3175 if (loadmodel->surfmesh.data_skeletalindex4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalindex4ub , loadmodel->surfmesh.data_skeletalindex4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3176 if (loadmodel->surfmesh.data_skeletalweight4ub ) memcpy(mem + loadmodel->surfmesh.vbooffset_skeletalweight4ub , loadmodel->surfmesh.data_skeletalweight4ub , loadmodel->surfmesh.num_vertices * sizeof(unsigned char[4]));
3177 loadmodel->surfmesh.vbo_vertexbuffer = R_Mesh_CreateMeshBuffer(mem, size, loadmodel->name, false, false, false, false);
3182 extern cvar_t mod_obj_orientation;
3183 static void Mod_Decompile_OBJ(dp_model_t *model, const char *filename, const char *mtlfilename, const char *originalfilename)
3185 int submodelindex, vertexindex, surfaceindex, triangleindex, textureindex, countvertices = 0, countsurfaces = 0, countfaces = 0, counttextures = 0;
3187 const char *texname;
3189 const float *v, *vn, *vt;
3191 size_t outbufferpos = 0;
3192 size_t outbuffermax = 0x100000;
3193 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3194 const msurface_t *surface;
3195 const int maxtextures = 256;
3196 char *texturenames = (char *) Z_Malloc(maxtextures * MAX_QPATH);
3197 dp_model_t *submodel;
3199 // construct the mtllib file
3200 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "# mtllib for %s exported by darkplaces engine\n", originalfilename);
3203 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3206 countvertices += surface->num_vertices;
3207 countfaces += surface->num_triangles;
3208 texname = (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default";
3209 for (textureindex = 0;textureindex < counttextures;textureindex++)
3210 if (!strcmp(texturenames + textureindex * MAX_QPATH, texname))
3212 if (textureindex < counttextures)
3213 continue; // already wrote this material entry
3214 if (textureindex >= maxtextures)
3215 continue; // just a precaution
3216 textureindex = counttextures++;
3217 strlcpy(texturenames + textureindex * MAX_QPATH, texname, MAX_QPATH);
3218 if (outbufferpos >= outbuffermax >> 1)
3221 oldbuffer = outbuffer;
3222 outbuffer = (char *) Z_Malloc(outbuffermax);
3223 memcpy(outbuffer, oldbuffer, outbufferpos);
3226 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");
3231 // write the mtllib file
3232 FS_WriteFile(mtlfilename, outbuffer, outbufferpos);
3234 // construct the obj file
3236 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);
3240 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)
3242 if (outbufferpos >= outbuffermax >> 1)
3245 oldbuffer = outbuffer;
3246 outbuffer = (char *) Z_Malloc(outbuffermax);
3247 memcpy(outbuffer, oldbuffer, outbufferpos);
3250 if(mod_obj_orientation.integer)
3251 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]);
3253 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]);
3258 for (submodelindex = 0;submodelindex < max(1, model->brush.numsubmodels);submodelindex++)
3260 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "o %i\n", submodelindex);
3263 submodel = model->brush.numsubmodels ? model->brush.submodels[submodelindex] : model;
3264 for (surfaceindex = 0;surfaceindex < submodel->nummodelsurfaces;surfaceindex++)
3266 surface = model->data_surfaces + submodel->sortedmodelsurfaces[surfaceindex];
3267 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "usemtl %s\n", (surface->texture && surface->texture->name[0]) ? surface->texture->name : "default");
3270 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3272 if (outbufferpos >= outbuffermax >> 1)
3275 oldbuffer = outbuffer;
3276 outbuffer = (char *) Z_Malloc(outbuffermax);
3277 memcpy(outbuffer, oldbuffer, outbufferpos);
3283 if(mod_obj_orientation.integer)
3284 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);
3286 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);
3293 // write the obj file
3294 FS_WriteFile(filename, outbuffer, outbufferpos);
3298 Z_Free(texturenames);
3301 Con_Printf("Wrote %s (%i bytes, %i vertices, %i faces, %i surfaces with %i distinct textures)\n", filename, (int)outbufferpos, countvertices, countfaces, countsurfaces, counttextures);
3304 static void Mod_Decompile_SMD(dp_model_t *model, const char *filename, int firstpose, int numposes, qboolean writetriangles)
3306 int countnodes = 0, counttriangles = 0, countframes = 0;
3314 size_t outbufferpos = 0;
3315 size_t outbuffermax = 0x100000;
3316 char *outbuffer = (char *) Z_Malloc(outbuffermax), *oldbuffer;
3317 const msurface_t *surface;
3318 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "version 1\nnodes\n");
3321 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3323 if (outbufferpos >= outbuffermax >> 1)
3326 oldbuffer = outbuffer;
3327 outbuffer = (char *) Z_Malloc(outbuffermax);
3328 memcpy(outbuffer, oldbuffer, outbufferpos);
3332 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%3i \"%s\" %3i\n", transformindex, model->data_bones[transformindex].name, model->data_bones[transformindex].parent);
3336 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\nskeleton\n");
3339 for (poseindex = 0;poseindex < numposes;poseindex++)
3342 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "time %i\n", poseindex);
3345 for (transformindex = 0;transformindex < model->num_bones;transformindex++)
3349 matrix4x4_t posematrix;
3350 if (outbufferpos >= outbuffermax >> 1)
3353 oldbuffer = outbuffer;
3354 outbuffer = (char *) Z_Malloc(outbuffermax);
3355 memcpy(outbuffer, oldbuffer, outbufferpos);
3359 // strangely the smd angles are for a transposed matrix, so we
3360 // have to generate a transposed matrix, then convert that...
3361 Matrix4x4_FromBonePose7s(&posematrix, model->num_posescale, model->data_poses7s + 7*(model->num_bones * poseindex + transformindex));
3362 Matrix4x4_ToArray12FloatGL(&posematrix, mtest[0]);
3363 AnglesFromVectors(angles, mtest[0], mtest[2], false);
3364 if (angles[0] >= 180) angles[0] -= 360;
3365 if (angles[1] >= 180) angles[1] -= 360;
3366 if (angles[2] >= 180) angles[2] -= 360;
3370 float a = DEG2RAD(angles[ROLL]);
3371 float b = DEG2RAD(angles[PITCH]);
3372 float c = DEG2RAD(angles[YAW]);
3373 float cy, sy, cp, sp, cr, sr;
3375 // smd matrix construction, for comparing
3386 test[1][0] = sr*sp*cy+cr*-sy;
3387 test[1][1] = sr*sp*sy+cr*cy;
3389 test[2][0] = (cr*sp*cy+-sr*-sy);
3390 test[2][1] = (cr*sp*sy+-sr*cy);
3392 test[3][0] = pose[9];
3393 test[3][1] = pose[10];
3394 test[3][2] = pose[11];
3397 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]));
3402 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3407 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "triangles\n");
3410 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->num_surfaces;surfaceindex++, surface++)
3412 for (triangleindex = 0, e = model->surfmesh.data_element3i + surface->num_firsttriangle * 3;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3415 if (outbufferpos >= outbuffermax >> 1)
3418 oldbuffer = outbuffer;
3419 outbuffer = (char *) Z_Malloc(outbuffermax);
3420 memcpy(outbuffer, oldbuffer, outbufferpos);
3423 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "%s\n", surface->texture && surface->texture->name[0] ? surface->texture->name : "default.bmp");
3426 for (cornerindex = 0;cornerindex < 3;cornerindex++)
3428 const int index = e[2-cornerindex];
3429 const float *v = model->surfmesh.data_vertex3f + index * 3;
3430 const float *vn = model->surfmesh.data_normal3f + index * 3;
3431 const float *vt = model->surfmesh.data_texcoordtexture2f + index * 2;
3432 const int b = model->surfmesh.blends[index];
3433 if (b < model->num_bones)
3434 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]);
3437 const blendweights_t *w = model->surfmesh.data_blendweights + b - model->num_bones;
3438 const unsigned char *wi = w->index;
3439 const unsigned char *wf = w->influence;
3440 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);
3441 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);
3442 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);
3443 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]);
3450 l = dpsnprintf(outbuffer + outbufferpos, outbuffermax - outbufferpos, "end\n");
3455 FS_WriteFile(filename, outbuffer, outbufferpos);
3458 Con_Printf("Wrote %s (%i bytes, %i nodes, %i frames, %i triangles)\n", filename, (int)outbufferpos, countnodes, countframes, counttriangles);
3465 decompiles a model to editable files
3468 static void Mod_Decompile_f(void)
3470 int i, j, k, l, first, count;
3472 char inname[MAX_QPATH];
3473 char outname[MAX_QPATH];
3474 char mtlname[MAX_QPATH];
3475 char basename[MAX_QPATH];
3476 char animname[MAX_QPATH];
3477 char animname2[MAX_QPATH];
3478 char zymtextbuffer[16384];
3479 char dpmtextbuffer[16384];
3480 char framegroupstextbuffer[16384];
3481 int zymtextsize = 0;
3482 int dpmtextsize = 0;
3483 int framegroupstextsize = 0;
3486 if (Cmd_Argc() != 2)
3488 Con_Print("usage: modeldecompile <filename>\n");
3492 strlcpy(inname, Cmd_Argv(1), sizeof(inname));
3493 FS_StripExtension(inname, basename, sizeof(basename));
3495 mod = Mod_ForName(inname, false, true, inname[0] == '*' ? cl.model_name[1] : NULL);
3498 Con_Print("No such model\n");
3501 if (mod->brush.submodel)
3503 // if we're decompiling a submodel, be sure to give it a proper name based on its parent
3504 FS_StripExtension(cl.model_name[1], outname, sizeof(outname));
3505 dpsnprintf(basename, sizeof(basename), "%s/%s", outname, mod->name);
3508 if (!mod->surfmesh.num_triangles)
3510 Con_Print("Empty model (or sprite)\n");
3514 // export OBJ if possible (not on sprites)
3515 if (mod->surfmesh.num_triangles)
3517 dpsnprintf(outname, sizeof(outname), "%s_decompiled.obj", basename);
3518 dpsnprintf(mtlname, sizeof(mtlname), "%s_decompiled.mtl", basename);
3519 Mod_Decompile_OBJ(mod, outname, mtlname, inname);
3522 // export SMD if possible (only for skeletal models)
3523 if (mod->surfmesh.num_triangles && mod->num_bones)
3525 dpsnprintf(outname, sizeof(outname), "%s_decompiled/ref1.smd", basename);
3526 Mod_Decompile_SMD(mod, outname, 0, 1, true);
3527 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "output out.zym\nscale 1\norigin 0 0 0\nmesh ref1.smd\n");
3528 if (l > 0) zymtextsize += l;
3529 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "outputdir .\nmodel out\nscale 1\norigin 0 0 0\nscene ref1.smd\n");
3530 if (l > 0) dpmtextsize += l;
3531 for (i = 0;i < mod->numframes;i = j)
3533 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3534 first = mod->animscenes[i].firstframe;
3535 if (mod->animscenes[i].framecount > 1)
3538 count = mod->animscenes[i].framecount;
3544 // check for additional frames with same name
3545 for (l = 0, k = (int)strlen(animname);animname[l];l++)
3546 if(animname[l] < '0' || animname[l] > '9')
3548 if(k > 0 && animname[k-1] == '_')
3551 count = mod->num_poses - first;
3552 for (j = i + 1;j < mod->numframes;j++)
3554 strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));
3555 for (l = 0, k = (int)strlen(animname2);animname2[l];l++)
3556 if(animname2[l] < '0' || animname2[l] > '9')
3558 if(k > 0 && animname[k-1] == '_')
3561 if (strcmp(animname2, animname) || mod->animscenes[j].framecount > 1)
3563 count = mod->animscenes[j].firstframe - first;
3567 // if it's only one frame, use the original frame name
3569 strlcpy(animname, mod->animscenes[i].name, sizeof(animname));
3572 dpsnprintf(outname, sizeof(outname), "%s_decompiled/%s.smd", basename, animname);
3573 Mod_Decompile_SMD(mod, outname, first, count, false);
3574 if (zymtextsize < (int)sizeof(zymtextbuffer) - 100)
3576 l = dpsnprintf(zymtextbuffer + zymtextsize, sizeof(zymtextbuffer) - zymtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3577 if (l > 0) zymtextsize += l;
3579 if (dpmtextsize < (int)sizeof(dpmtextbuffer) - 100)
3581 l = dpsnprintf(dpmtextbuffer + dpmtextsize, sizeof(dpmtextbuffer) - dpmtextsize, "scene %s.smd fps %g %s\n", animname, mod->animscenes[i].framerate, mod->animscenes[i].loop ? "" : " noloop");
3582 if (l > 0) dpmtextsize += l;
3584 if (framegroupstextsize < (int)sizeof(framegroupstextbuffer) - 100)
3586 l = dpsnprintf(framegroupstextbuffer + framegroupstextsize, sizeof(framegroupstextbuffer) - framegroupstextsize, "%d %d %f %d // %s\n", first, count, mod->animscenes[i].framerate, mod->animscenes[i].loop, animname);
3587 if (l > 0) framegroupstextsize += l;
3591 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_zym.txt", basename), zymtextbuffer, (fs_offset_t)zymtextsize);
3593 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled/out_dpm.txt", basename), dpmtextbuffer, (fs_offset_t)dpmtextsize);
3594 if (framegroupstextsize)
3595 FS_WriteFile(va(vabuf, sizeof(vabuf), "%s_decompiled.framegroups", basename), framegroupstextbuffer, (fs_offset_t)framegroupstextsize);
3599 void Mod_AllocLightmap_Init(mod_alloclightmap_state_t *state, mempool_t *mempool, int width, int height)
3602 memset(state, 0, sizeof(*state));
3603 state->width = width;
3604 state->height = height;
3605 state->currentY = 0;
3606 state->rows = (mod_alloclightmap_row_t *)Mem_Alloc(mempool, state->height * sizeof(*state->rows));
3607 for (y = 0;y < state->height;y++)
3609 state->rows[y].currentX = 0;
3610 state->rows[y].rowY = -1;
3614 void Mod_AllocLightmap_Reset(mod_alloclightmap_state_t *state)
3617 state->currentY = 0;
3618 for (y = 0;y < state->height;y++)
3620 state->rows[y].currentX = 0;
3621 state->rows[y].rowY = -1;
3625 void Mod_AllocLightmap_Free(mod_alloclightmap_state_t *state)
3628 Mem_Free(state->rows);
3629 memset(state, 0, sizeof(*state));
3632 qboolean Mod_AllocLightmap_Block(mod_alloclightmap_state_t *state, int blockwidth, int blockheight, int *outx, int *outy)
3634 mod_alloclightmap_row_t *row;
3637 row = state->rows + blockheight;
3638 if ((row->rowY < 0) || (row->currentX + blockwidth > state->width))
3640 if (state->currentY + blockheight <= state->height)
3642 // use the current allocation position
3643 row->rowY = state->currentY;
3645 state->currentY += blockheight;
3649 // find another position
3650 for (y = blockheight;y < state->height;y++)
3652 if ((state->rows[y].rowY >= 0) && (state->rows[y].currentX + blockwidth <= state->width))
3654 row = state->rows + y;
3658 if (y == state->height)
3663 *outx = row->currentX;
3664 row->currentX += blockwidth;
3669 typedef struct lightmapsample_s
3673 float *vertex_color;
3674 unsigned char *lm_bgr;
3675 unsigned char *lm_dir;
3679 typedef struct lightmapvertex_s
3684 float texcoordbase[2];
3685 float texcoordlightmap[2];
3686 float lightcolor[4];
3690 typedef struct lightmaptriangle_s
3698 // 2D modelspace coordinates of min corner
3699 // snapped to lightmap grid but not in grid coordinates
3701 // 2D modelspace to lightmap coordinate scale
3709 typedef struct lightmaplight_s
3720 lightmaptriangle_t *mod_generatelightmaps_lightmaptriangles;
3722 #define MAX_LIGHTMAPSAMPLES 64
3723 static int mod_generatelightmaps_numoffsets[3];
3724 static float mod_generatelightmaps_offsets[3][MAX_LIGHTMAPSAMPLES][3];
3726 static int mod_generatelightmaps_numlights;
3727 static lightmaplight_t *mod_generatelightmaps_lightinfo;
3729 extern cvar_t r_shadow_lightattenuationdividebias;
3730 extern cvar_t r_shadow_lightattenuationlinearscale;
3732 static void Mod_GenerateLightmaps_LightPoint(dp_model_t *model, const vec3_t pos, vec3_t ambient, vec3_t diffuse, vec3_t lightdir)
3737 float relativepoint[3];
3744 float lightorigin[3];
3748 float lightcolor[3];
3750 for (i = 0;i < 5*3;i++)
3752 for (index = 0;;index++)
3754 result = R_Shadow_GetRTLightInfo(index, lightorigin, &lightradius, lightcolor);
3759 lightradius2 = lightradius * lightradius;
3760 VectorSubtract(lightorigin, pos, relativepoint);
3761 dist2 = VectorLength2(relativepoint);
3762 if (dist2 >= lightradius2)
3764 lightiradius = 1.0f / lightradius;
3765 dist = sqrt(dist2) * lightiradius;
3766 intensity = (1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
3767 if (intensity <= 0.0f)
3769 if (model && model->TraceLine)
3771 model->TraceLine(model, NULL, NULL, &trace, pos, lightorigin, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3772 if (trace.fraction < 1)
3775 // scale down intensity to add to both ambient and diffuse
3776 //intensity *= 0.5f;
3777 VectorNormalize(relativepoint);
3778 VectorScale(lightcolor, intensity, color);
3779 VectorMA(sample , 0.5f , color, sample );
3780 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3781 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3782 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3783 // calculate a weighted average light direction as well
3784 intensity *= VectorLength(color);
3785 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
3787 // calculate the direction we'll use to reduce the sample to a directional light source
3788 VectorCopy(sample + 12, dir);
3789 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
3790 VectorNormalize(dir);
3791 // extract the diffuse color along the chosen direction and scale it
3792 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]);
3793 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]);
3794 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]);
3795 // subtract some of diffuse from ambient
3796 VectorMA(sample, -0.333f, diffuse, ambient);
3797 // store the normalized lightdir
3798 VectorCopy(dir, lightdir);
3801 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(const dp_model_t *model, svbsp_t *svbsp, const float *mins, const float *maxs)
3805 const msurface_t *surface;
3806 const float *vertex3f = model->surfmesh.data_vertex3f;
3807 const int *element3i = model->surfmesh.data_element3i;
3810 for (surfaceindex = 0, surface = model->data_surfaces;surfaceindex < model->nummodelsurfaces;surfaceindex++, surface++)
3812 if (!BoxesOverlap(surface->mins, surface->maxs, mins, maxs))
3814 if (surface->texture->basematerialflags & MATERIALFLAG_NOSHADOW)
3816 for (triangleindex = 0, e = element3i + 3*surface->num_firsttriangle;triangleindex < surface->num_triangles;triangleindex++, e += 3)
3818 VectorCopy(vertex3f + 3*e[0], v2[0]);
3819 VectorCopy(vertex3f + 3*e[1], v2[1]);
3820 VectorCopy(vertex3f + 3*e[2], v2[2]);
3821 SVBSP_AddPolygon(svbsp, 3, v2[0], true, NULL, NULL, 0);
3826 static void Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(dp_model_t *model, lightmaplight_t *lightinfo)
3828 int maxnodes = 1<<14;
3829 svbsp_node_t *nodes;
3834 VectorSet(mins, lightinfo->origin[0] - lightinfo->radius, lightinfo->origin[1] - lightinfo->radius, lightinfo->origin[2] - lightinfo->radius);
3835 VectorSet(maxs, lightinfo->origin[0] + lightinfo->radius, lightinfo->origin[1] + lightinfo->radius, lightinfo->origin[2] + lightinfo->radius);
3836 VectorCopy(lightinfo->origin, origin);
3837 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3840 SVBSP_Init(&svbsp, origin, maxnodes, nodes);
3841 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP_InsertSurfaces(model, &svbsp, mins, maxs);
3842 if (svbsp.ranoutofnodes)
3845 if (maxnodes > 1<<22)
3851 nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, maxnodes * sizeof(*nodes));
3856 if (svbsp.numnodes > 0)
3858 svbsp.nodes = (svbsp_node_t *)Mem_Alloc(tempmempool, svbsp.numnodes * sizeof(*nodes));
3859 memcpy(svbsp.nodes, nodes, svbsp.numnodes * sizeof(*nodes));
3860 lightinfo->svbsp = svbsp;
3865 static void Mod_GenerateLightmaps_CreateLights(dp_model_t *model)
3869 lightmaplight_t *lightinfo;
3873 mod_generatelightmaps_numlights = 0;
3874 for (index = 0;;index++)
3876 result = R_Shadow_GetRTLightInfo(index, origin, &radius, color);
3880 mod_generatelightmaps_numlights++;
3882 if (mod_generatelightmaps_numlights > 0)
3884 mod_generatelightmaps_lightinfo = (lightmaplight_t *)Mem_Alloc(tempmempool, mod_generatelightmaps_numlights * sizeof(*mod_generatelightmaps_lightinfo));
3885 lightinfo = mod_generatelightmaps_lightinfo;
3886 for (index = 0;;index++)
3888 result = R_Shadow_GetRTLightInfo(index, lightinfo->origin, &lightinfo->radius, lightinfo->color);
3895 for (index = 0, lightinfo = mod_generatelightmaps_lightinfo;index < mod_generatelightmaps_numlights;index++, lightinfo++)
3897 lightinfo->iradius = 1.0f / lightinfo->radius;
3898 lightinfo->radius2 = lightinfo->radius * lightinfo->radius;
3899 // TODO: compute svbsp
3900 Mod_GenerateLightmaps_CreateLights_ComputeSVBSP(model, lightinfo);
3904 static void Mod_GenerateLightmaps_DestroyLights(dp_model_t *model)
3907 if (mod_generatelightmaps_lightinfo)
3909 for (i = 0;i < mod_generatelightmaps_numlights;i++)
3910 if (mod_generatelightmaps_lightinfo[i].svbsp.nodes)
3911 Mem_Free(mod_generatelightmaps_lightinfo[i].svbsp.nodes);
3912 Mem_Free(mod_generatelightmaps_lightinfo);
3914 mod_generatelightmaps_lightinfo = NULL;
3915 mod_generatelightmaps_numlights = 0;
3918 static qboolean Mod_GenerateLightmaps_SamplePoint_SVBSP(const svbsp_t *svbsp, const float *pos)
3920 const svbsp_node_t *node;
3921 const svbsp_node_t *nodes = svbsp->nodes;
3926 num = node->children[DotProduct(node->plane, pos) < node->plane[3]];
3928 return num == -1; // true if empty, false if solid (shadowed)
3931 static void Mod_GenerateLightmaps_SamplePoint(const float *pos, const float *normal, float *sample, int numoffsets, const float *offsets)
3934 float relativepoint[3];
3943 const lightmaplight_t *lightinfo;
3945 for (i = 0;i < 5*3;i++)
3947 for (i = 0, lightinfo = mod_generatelightmaps_lightinfo;i < mod_generatelightmaps_numlights;i++, lightinfo++)
3949 //R_SampleRTLights(pos, sample, numoffsets, offsets);
3950 VectorSubtract(lightinfo->origin, pos, relativepoint);
3951 // don't accept light from behind a surface, it causes bad shading
3952 if (normal && DotProduct(relativepoint, normal) <= 0)
3954 dist2 = VectorLength2(relativepoint);
3955 if (dist2 >= lightinfo->radius2)
3957 dist = sqrt(dist2) * lightinfo->iradius;
3958 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
3961 if (cl.worldmodel && cl.worldmodel->TraceLine && numoffsets > 0)
3965 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, pos))
3967 for (offsetindex = 1;offsetindex < numoffsets;offsetindex++)
3969 VectorAdd(pos, offsets + 3*offsetindex, offsetpos);
3972 // for light grid we'd better check visibility of the offset point
3973 cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, pos, offsetpos, SUPERCONTENTS_SOLID, 0, MATERIALFLAGMASK_TRANSLUCENT | MATERIALFLAG_NOSHADOW);
3974 if (trace.fraction < 1)
3975 VectorLerp(pos, trace.fraction, offsetpos, offsetpos);
3978 if (Mod_GenerateLightmaps_SamplePoint_SVBSP(&lightinfo->svbsp, offsetpos))
3983 // scale intensity according to how many rays succeeded
3984 // we know one test is valid, half of the rest will fail...
3985 //if (normal && tests > 1)
3986 // intensity *= (tests - 1.0f) / tests;
3987 intensity *= (float)hits / tests;
3989 // scale down intensity to add to both ambient and diffuse
3990 //intensity *= 0.5f;
3991 VectorNormalize(relativepoint);
3992 VectorScale(lightinfo->color, intensity, color);
3993 VectorMA(sample , 0.5f , color, sample );
3994 VectorMA(sample + 3, relativepoint[0], color, sample + 3);
3995 VectorMA(sample + 6, relativepoint[1], color, sample + 6);
3996 VectorMA(sample + 9, relativepoint[2], color, sample + 9);
3997 // calculate a weighted average light direction as well
3998 intensity *= VectorLength(color);
3999 VectorMA(sample + 12, intensity, relativepoint, sample + 12);
4003 static void Mod_GenerateLightmaps_LightmapSample(const float *pos, const float *normal, unsigned char *lm_bgr, unsigned char *lm_dir)
4009 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[0], mod_generatelightmaps_offsets[0][0]);
4010 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
4011 VectorCopy(sample + 12, dir);
4012 VectorNormalize(dir);
4013 //VectorAdd(dir, normal, dir);
4014 //VectorNormalize(dir);
4015 f = DotProduct(dir, normal);
4016 f = max(0, f) * 255.0f;
4017 VectorScale(sample, f, color);
4018 //VectorCopy(normal, dir);
4019 VectorSet(dir, (dir[0]+1.0f)*127.5f, (dir[1]+1.0f)*127.5f, (dir[2]+1.0f)*127.5f);
4020 lm_bgr[0] = (unsigned char)bound(0.0f, color[2], 255.0f);
4021 lm_bgr[1] = (unsigned char)bound(0.0f, color[1], 255.0f);
4022 lm_bgr[2] = (unsigned char)bound(0.0f, color[0], 255.0f);
4024 lm_dir[0] = (unsigned char)dir[2];
4025 lm_dir[1] = (unsigned char)dir[1];
4026 lm_dir[2] = (unsigned char)dir[0];
4030 static void Mod_GenerateLightmaps_VertexSample(const float *pos, const float *normal, float *vertex_color)
4033 Mod_GenerateLightmaps_SamplePoint(pos, normal, sample, mod_generatelightmaps_numoffsets[1], mod_generatelightmaps_offsets[1][0]);
4034 VectorCopy(sample, vertex_color);
4037 static void Mod_GenerateLightmaps_GridSample(const float *pos, q3dlightgrid_t *s)
4043 Mod_GenerateLightmaps_SamplePoint(pos, NULL, sample, mod_generatelightmaps_numoffsets[2], mod_generatelightmaps_offsets[2][0]);
4044 // calculate the direction we'll use to reduce the sample to a directional light source
4045 VectorCopy(sample + 12, dir);
4046 //VectorSet(dir, sample[3] + sample[4] + sample[5], sample[6] + sample[7] + sample[8], sample[9] + sample[10] + sample[11]);
4047 VectorNormalize(dir);
4048 // extract the diffuse color along the chosen direction and scale it
4049 diffuse[0] = (dir[0]*sample[3] + dir[1]*sample[6] + dir[2]*sample[ 9] + sample[ 0]) * 127.5f;
4050 diffuse[1] = (dir[0]*sample[4] + dir[1]*sample[7] + dir[2]*sample[10] + sample[ 1]) * 127.5f;
4051 diffuse[2] = (dir[0]*sample[5] + dir[1]*sample[8] + dir[2]*sample[11] + sample[ 2]) * 127.5f;
4052 // scale the ambient from 0-2 to 0-255 and subtract some of diffuse
4053 VectorScale(sample, 127.5f, ambient);
4054 VectorMA(ambient, -0.333f, diffuse, ambient);
4055 // encode to the grid format
4056 s->ambientrgb[0] = (unsigned char)bound(0.0f, ambient[0], 255.0f);
4057 s->ambientrgb[1] = (unsigned char)bound(0.0f, ambient[1], 255.0f);
4058 s->ambientrgb[2] = (unsigned char)bound(0.0f, ambient[2], 255.0f);
4059 s->diffusergb[0] = (unsigned char)bound(0.0f, diffuse[0], 255.0f);
4060 s->diffusergb[1] = (unsigned char)bound(0.0f, diffuse[1], 255.0f);
4061 s->diffusergb[2] = (unsigned char)bound(0.0f, diffuse[2], 255.0f);
4062 if (dir[2] >= 0.99f) {s->diffusepitch = 0;s->diffuseyaw = 0;}
4063 else if (dir[2] <= -0.99f) {s->diffusepitch = 128;s->diffuseyaw = 0;}
4064 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));}
4067 static void Mod_GenerateLightmaps_InitSampleOffsets(dp_model_t *model)
4072 memset(mod_generatelightmaps_offsets, 0, sizeof(mod_generatelightmaps_offsets));
4073 mod_generatelightmaps_numoffsets[0] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_lightmapsamples.integer);
4074 mod_generatelightmaps_numoffsets[1] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_vertexsamples.integer);
4075 mod_generatelightmaps_numoffsets[2] = min(MAX_LIGHTMAPSAMPLES, mod_generatelightmaps_gridsamples.integer);
4076 radius[0] = mod_generatelightmaps_lightmapradius.value;
4077 radius[1] = mod_generatelightmaps_vertexradius.value;
4078 radius[2] = mod_generatelightmaps_gridradius.value;
4079 for (i = 0;i < 3;i++)
4081 for (j = 1;j < mod_generatelightmaps_numoffsets[i];j++)
4084 VectorScale(temp, radius[i], mod_generatelightmaps_offsets[i][j]);
4089 static void Mod_GenerateLightmaps_DestroyLightmaps(dp_model_t *model)
4091 msurface_t *surface;
4094 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4096 surface = model->data_surfaces + surfaceindex;
4097 surface->lightmaptexture = NULL;
4098 surface->deluxemaptexture = NULL;
4100 if (model->brushq3.data_lightmaps)
4102 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
4103 if (model->brushq3.data_lightmaps[i])
4104 R_FreeTexture(model->brushq3.data_lightmaps[i]);
4105 Mem_Free(model->brushq3.data_lightmaps);
4106 model->brushq3.data_lightmaps = NULL;
4108 if (model->brushq3.data_deluxemaps)
4110 for (i = 0;i < model->brushq3.num_mergedlightmaps;i++)
4111 if (model->brushq3.data_deluxemaps[i])
4112 R_FreeTexture(model->brushq3.data_deluxemaps[i]);
4113 Mem_Free(model->brushq3.data_deluxemaps);
4114 model->brushq3.data_deluxemaps = NULL;
4118 static void Mod_GenerateLightmaps_UnweldTriangles(dp_model_t *model)
4120 msurface_t *surface;
4126 surfmesh_t oldsurfmesh;
4128 unsigned char *data;
4129 oldsurfmesh = model->surfmesh;
4130 model->surfmesh.num_triangles = oldsurfmesh.num_triangles;
4131 model->surfmesh.num_vertices = oldsurfmesh.num_triangles * 3;
4133 size += model->surfmesh.num_vertices * sizeof(float[3]);
4134 size += model->surfmesh.num_vertices * sizeof(float[3]);
4135 size += model->surfmesh.num_vertices * sizeof(float[3]);
4136 size += model->surfmesh.num_vertices * sizeof(float[3]);
4137 size += model->surfmesh.num_vertices * sizeof(float[2]);
4138 size += model->surfmesh.num_vertices * sizeof(float[2]);
4139 size += model->surfmesh.num_vertices * sizeof(float[4]);
4140 data = (unsigned char *)Mem_Alloc(model->mempool, size);
4141 model->surfmesh.data_vertex3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4142 model->surfmesh.data_normal3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4143 model->surfmesh.data_svector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4144 model->surfmesh.data_tvector3f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[3]);
4145 model->surfmesh.data_texcoordtexture2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
4146 model->surfmesh.data_texcoordlightmap2f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[2]);
4147 model->surfmesh.data_lightmapcolor4f = (float *)data;data += model->surfmesh.num_vertices * sizeof(float[4]);
4148 if (model->surfmesh.num_vertices > 65536)
4149 model->surfmesh.data_element3s = NULL;
4151 if (model->surfmesh.data_element3i_indexbuffer)
4152 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3i_indexbuffer);
4153 model->surfmesh.data_element3i_indexbuffer = NULL;
4154 if (model->surfmesh.data_element3s_indexbuffer)
4155 R_Mesh_DestroyMeshBuffer(model->surfmesh.data_element3s_indexbuffer);
4156 model->surfmesh.data_element3s_indexbuffer = NULL;
4157 if (model->surfmesh.vbo_vertexbuffer)
4158 R_Mesh_DestroyMeshBuffer(model->surfmesh.vbo_vertexbuffer);
4159 model->surfmesh.vbo_vertexbuffer = 0;
4161 // convert all triangles to unique vertex data
4163 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4165 surface = model->data_surfaces + surfaceindex;
4166 surface->num_firstvertex = outvertexindex;
4167 surface->num_vertices = surface->num_triangles*3;
4168 e = oldsurfmesh.data_element3i + surface->num_firsttriangle*3;
4169 for (i = 0;i < surface->num_triangles*3;i++)
4172 model->surfmesh.data_vertex3f[outvertexindex*3+0] = oldsurfmesh.data_vertex3f[vertexindex*3+0];
4173 model->surfmesh.data_vertex3f[outvertexindex*3+1] = oldsurfmesh.data_vertex3f[vertexindex*3+1];
4174 model->surfmesh.data_vertex3f[outvertexindex*3+2] = oldsurfmesh.data_vertex3f[vertexindex*3+2];
4175 model->surfmesh.data_normal3f[outvertexindex*3+0] = oldsurfmesh.data_normal3f[vertexindex*3+0];
4176 model->surfmesh.data_normal3f[outvertexindex*3+1] = oldsurfmesh.data_normal3f[vertexindex*3+1];
4177 model->surfmesh.data_normal3f[outvertexindex*3+2] = oldsurfmesh.data_normal3f[vertexindex*3+2];
4178 model->surfmesh.data_svector3f[outvertexindex*3+0] = oldsurfmesh.data_svector3f[vertexindex*3+0];
4179 model->surfmesh.data_svector3f[outvertexindex*3+1] = oldsurfmesh.data_svector3f[vertexindex*3+1];
4180 model->surfmesh.data_svector3f[outvertexindex*3+2] = oldsurfmesh.data_svector3f[vertexindex*3+2];
4181 model->surfmesh.data_tvector3f[outvertexindex*3+0] = oldsurfmesh.data_tvector3f[vertexindex*3+0];
4182 model->surfmesh.data_tvector3f[outvertexindex*3+1] = oldsurfmesh.data_tvector3f[vertexindex*3+1];
4183 model->surfmesh.data_tvector3f[outvertexindex*3+2] = oldsurfmesh.data_tvector3f[vertexindex*3+2];
4184 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+0];
4185 model->surfmesh.data_texcoordtexture2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordtexture2f[vertexindex*2+1];
4186 if (oldsurfmesh.data_texcoordlightmap2f)
4188 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+0] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+0];
4189 model->surfmesh.data_texcoordlightmap2f[outvertexindex*2+1] = oldsurfmesh.data_texcoordlightmap2f[vertexindex*2+1];
4191 if (oldsurfmesh.data_lightmapcolor4f)
4193 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+0] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+0];
4194 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+1] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+1];
4195 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+2] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+2];
4196 model->surfmesh.data_lightmapcolor4f[outvertexindex*4+3] = oldsurfmesh.data_lightmapcolor4f[vertexindex*4+3];
4199 Vector4Set(model->surfmesh.data_lightmapcolor4f + 4*outvertexindex, 1, 1, 1, 1);
4200 model->surfmesh.data_element3i[surface->num_firsttriangle*3+i] = outvertexindex;
4204 if (model->surfmesh.data_element3s)
4205 for (i = 0;i < model->surfmesh.num_triangles*3;i++)
4206 model->surfmesh.data_element3s[i] = model->surfmesh.data_element3i[i];
4208 // find and update all submodels to use this new surfmesh data
4209 for (i = 0;i < model->brush.numsubmodels;i++)
4210 model->brush.submodels[i]->surfmesh = model->surfmesh;
4213 static void Mod_GenerateLightmaps_CreateTriangleInformation(dp_model_t *model)
4215 msurface_t *surface;
4221 lightmaptriangle_t *triangle;
4222 // generate lightmap triangle structs
4223 mod_generatelightmaps_lightmaptriangles = (lightmaptriangle_t *)Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4224 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4226 surface = model->data_surfaces + surfaceindex;
4227 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4228 for (i = 0;i < surface->num_triangles;i++)
4230 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4231 triangle->triangleindex = surface->num_firsttriangle+i;
4232 triangle->surfaceindex = surfaceindex;
4233 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+0], triangle->vertex[0]);
4234 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+1], triangle->vertex[1]);
4235 VectorCopy(model->surfmesh.data_vertex3f + 3*e[i*3+2], triangle->vertex[2]);
4236 // calculate bounds of triangle
4237 triangle->mins[0] = min(triangle->vertex[0][0], min(triangle->vertex[1][0], triangle->vertex[2][0]));
4238 triangle->mins[1] = min(triangle->vertex[0][1], min(triangle->vertex[1][1], triangle->vertex[2][1]));
4239 triangle->mins[2] = min(triangle->vertex[0][2], min(triangle->vertex[1][2], triangle->vertex[2][2]));
4240 triangle->maxs[0] = max(triangle->vertex[0][0], max(triangle->vertex[1][0], triangle->vertex[2][0]));
4241 triangle->maxs[1] = max(triangle->vertex[0][1], max(triangle->vertex[1][1], triangle->vertex[2][1]));
4242 triangle->maxs[2] = max(triangle->vertex[0][2], max(triangle->vertex[1][2], triangle->vertex[2][2]));
4243 // pick an axial projection based on the triangle normal
4244 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], normal);
4246 if (fabs(normal[1]) > fabs(normal[axis]))
4248 if (fabs(normal[2]) > fabs(normal[axis]))
4250 triangle->axis = axis;
4255 static void Mod_GenerateLightmaps_DestroyTriangleInformation(dp_model_t *model)
4257 if (mod_generatelightmaps_lightmaptriangles)
4258 Mem_Free(mod_generatelightmaps_lightmaptriangles);
4259 mod_generatelightmaps_lightmaptriangles = NULL;
4262 float lmaxis[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
4264 static void Mod_GenerateLightmaps_CreateLightmaps(dp_model_t *model)
4266 msurface_t *surface;
4280 float trianglenormal[3];
4281 float samplecenter[3];
4282 float samplenormal[3];
4288 float lmscalepixels;
4291 float lm_basescalepixels;
4292 int lm_borderpixels;
4296 lightmaptriangle_t *triangle;
4297 unsigned char *lightmappixels;
4298 unsigned char *deluxemappixels;
4299 mod_alloclightmap_state_t lmstate;
4302 // generate lightmap projection information for all triangles
4303 if (model->texturepool == NULL)
4304 model->texturepool = R_AllocTexturePool();
4305 lm_basescalepixels = 1.0f / max(0.0001f, mod_generatelightmaps_unitspersample.value);
4306 lm_borderpixels = mod_generatelightmaps_borderpixels.integer;
4307 lm_texturesize = bound(lm_borderpixels*2+1, 64, (int)vid.maxtexturesize_2d);
4308 //lm_maxpixels = lm_texturesize-(lm_borderpixels*2+1);
4309 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4311 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4313 surface = model->data_surfaces + surfaceindex;
4314 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4315 lmscalepixels = lm_basescalepixels;
4316 for (retry = 0;retry < 30;retry++)
4318 // after a couple failed attempts, degrade quality to make it fit
4320 lmscalepixels *= 0.5f;
4321 for (i = 0;i < surface->num_triangles;i++)
4323 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4324 triangle->lightmapindex = lightmapnumber;
4325 // calculate lightmap bounds in 3D pixel coordinates, limit size,
4326 // pick two planar axes for projection
4327 // lightmap coordinates here are in pixels
4328 // lightmap projections are snapped to pixel grid explicitly, such
4329 // that two neighboring triangles sharing an edge and projection
4330 // axis will have identical sampl espacing along their shared edge
4332 for (j = 0;j < 3;j++)
4334 if (j == triangle->axis)
4336 lmmins = floor(triangle->mins[j]*lmscalepixels)-lm_borderpixels;
4337 lmmaxs = floor(triangle->maxs[j]*lmscalepixels)+lm_borderpixels;
4338 triangle->lmsize[k] = (int)(lmmaxs-lmmins);
4339 triangle->lmbase[k] = lmmins/lmscalepixels;
4340 triangle->lmscale[k] = lmscalepixels;
4343 if (!Mod_AllocLightmap_Block(&lmstate, triangle->lmsize[0], triangle->lmsize[1], &triangle->lmoffset[0], &triangle->lmoffset[1]))
4346 // if all fit in this texture, we're done with this surface
4347 if (i == surface->num_triangles)
4349 // if we haven't maxed out the lightmap size yet, we retry the
4350 // entire surface batch...
4351 if (lm_texturesize * 2 <= min(mod_generatelightmaps_texturesize.integer, (int)vid.maxtexturesize_2d))
4353 lm_texturesize *= 2;
4356 Mod_AllocLightmap_Free(&lmstate);
4357 Mod_AllocLightmap_Init(&lmstate, loadmodel->mempool, lm_texturesize, lm_texturesize);
4360 // if we have maxed out the lightmap size, and this triangle does
4361 // not fit in the same texture as the rest of the surface, we have
4362 // to retry the entire surface in a new texture (can only use one)
4363 // with multiple retries, the lightmap quality degrades until it
4364 // fits (or gives up)
4365 if (surfaceindex > 0)
4367 Mod_AllocLightmap_Reset(&lmstate);
4371 Mod_AllocLightmap_Free(&lmstate);
4373 // now put triangles together into lightmap textures, and do not allow
4374 // triangles of a surface to go into different textures (as that would
4375 // require rewriting the surface list)
4376 model->brushq3.deluxemapping_modelspace = true;
4377 model->brushq3.deluxemapping = true;
4378 model->brushq3.num_mergedlightmaps = lightmapnumber;
4379 model->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4380 model->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(model->mempool, model->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4381 lightmappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4382 deluxemappixels = (unsigned char *)Mem_Alloc(tempmempool, model->brushq3.num_mergedlightmaps * lm_texturesize * lm_texturesize * 4);
4383 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4385 surface = model->data_surfaces + surfaceindex;
4386 e = model->surfmesh.data_element3i + surface->num_firsttriangle*3;
4387 for (i = 0;i < surface->num_triangles;i++)
4389 triangle = &mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle+i];
4390 TriangleNormal(triangle->vertex[0], triangle->vertex[1], triangle->vertex[2], trianglenormal);
4391 VectorNormalize(trianglenormal);
4392 VectorCopy(trianglenormal, samplenormal); // FIXME: this is supposed to be interpolated per pixel from vertices
4393 axis = triangle->axis;
4394 axis1 = axis == 0 ? 1 : 0;
4395 axis2 = axis == 2 ? 1 : 2;
4396 lmiscale[0] = 1.0f / triangle->lmscale[0];
4397 lmiscale[1] = 1.0f / triangle->lmscale[1];
4398 if (trianglenormal[axis] < 0)
4399 VectorNegate(trianglenormal, trianglenormal);
4400 CrossProduct(lmaxis[axis2], trianglenormal, temp);slopex = temp[axis] / temp[axis1];
4401 CrossProduct(lmaxis[axis1], trianglenormal, temp);slopey = temp[axis] / temp[axis2];
4402 slopebase = triangle->vertex[0][axis] - triangle->vertex[0][axis1]*slopex - triangle->vertex[0][axis2]*slopey;
4403 for (j = 0;j < 3;j++)
4405 float *t2f = model->surfmesh.data_texcoordlightmap2f + e[i*3+j]*2;
4406 t2f[0] = ((triangle->vertex[j][axis1] - triangle->lmbase[0]) * triangle->lmscale[0] + triangle->lmoffset[0]) / lm_texturesize;
4407 t2f[1] = ((triangle->vertex[j][axis2] - triangle->lmbase[1]) * triangle->lmscale[1] + triangle->lmoffset[1]) / lm_texturesize;
4409 samplecenter[axis1] = (t2f[0]*lm_texturesize-triangle->lmoffset[0])*lmiscale[0] + triangle->lmbase[0];
4410 samplecenter[axis2] = (t2f[1]*lm_texturesize-triangle->lmoffset[1])*lmiscale[1] + triangle->lmbase[1];
4411 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4412 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]);
4422 forward[1] = 1.0f / triangle->lmscale[0];
4426 left[2] = 1.0f / triangle->lmscale[1];
4431 origin[1] = triangle->lmbase[0];
4432 origin[2] = triangle->lmbase[1];
4435 forward[0] = 1.0f / triangle->lmscale[0];
4440 left[2] = 1.0f / triangle->lmscale[1];
4444 origin[0] = triangle->lmbase[0];
4446 origin[2] = triangle->lmbase[1];
4449 forward[0] = 1.0f / triangle->lmscale[0];
4453 left[1] = 1.0f / triangle->lmscale[1];
4458 origin[0] = triangle->lmbase[0];
4459 origin[1] = triangle->lmbase[1];
4463 Matrix4x4_FromVectors(&backmatrix, forward, left, up, origin);
4465 #define LM_DIST_EPSILON (1.0f / 32.0f)
4466 for (y = 0;y < triangle->lmsize[1];y++)
4468 pixeloffset = ((triangle->lightmapindex * lm_texturesize + y + triangle->lmoffset[1]) * lm_texturesize + triangle->lmoffset[0]) * 4;
4469 for (x = 0;x < triangle->lmsize[0];x++, pixeloffset += 4)
4471 samplecenter[axis1] = (x+0.5f)*lmiscale[0] + triangle->lmbase[0];
4472 samplecenter[axis2] = (y+0.5f)*lmiscale[1] + triangle->lmbase[1];
4473 samplecenter[axis] = samplecenter[axis1]*slopex + samplecenter[axis2]*slopey + slopebase;
4474 VectorMA(samplecenter, 0.125f, samplenormal, samplecenter);
4475 Mod_GenerateLightmaps_LightmapSample(samplecenter, samplenormal, lightmappixels + pixeloffset, deluxemappixels + pixeloffset);
4481 for (lightmapindex = 0;lightmapindex < model->brushq3.num_mergedlightmaps;lightmapindex++)
4483 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);
4484 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);
4488 Mem_Free(lightmappixels);
4489 if (deluxemappixels)
4490 Mem_Free(deluxemappixels);
4492 for (surfaceindex = 0;surfaceindex < model->num_surfaces;surfaceindex++)
4494 surface = model->data_surfaces + surfaceindex;
4495 if (!surface->num_triangles)
4497 lightmapindex = mod_generatelightmaps_lightmaptriangles[surface->num_firsttriangle].lightmapindex;
4498 surface->lightmaptexture = model->brushq3.data_lightmaps[lightmapindex];
4499 surface->deluxemaptexture = model->brushq3.data_deluxemaps[lightmapindex];
4500 surface->lightmapinfo = NULL;
4503 model->brush.LightPoint = Mod_GenerateLightmaps_LightPoint;
4504 model->brushq1.lightdata = NULL;
4505 model->brushq1.lightmapupdateflags = NULL;
4506 model->brushq1.firstrender = false;
4507 model->brushq1.num_lightstyles = 0;
4508 model->brushq1.data_lightstyleinfo = NULL;
4509 for (i = 0;i < model->brush.numsubmodels;i++)
4511 model->brush.submodels[i]->brushq1.lightmapupdateflags = NULL;
4512 model->brush.submodels[i]->brushq1.firstrender = false;
4513 model->brush.submodels[i]->brushq1.num_lightstyles = 0;
4514 model->brush.submodels[i]->brushq1.data_lightstyleinfo = NULL;
4518 static void Mod_GenerateLightmaps_UpdateVertexColors(dp_model_t *model)
4521 for (i = 0;i < model->surfmesh.num_vertices;i++)
4522 Mod_GenerateLightmaps_VertexSample(model->surfmesh.data_vertex3f + 3*i, model->surfmesh.data_normal3f + 3*i, model->surfmesh.data_lightmapcolor4f + 4*i);
4525 static void Mod_GenerateLightmaps_UpdateLightGrid(dp_model_t *model)
4532 for (z = 0;z < model->brushq3.num_lightgrid_isize[2];z++)
4534 pos[2] = (model->brushq3.num_lightgrid_imins[2] + z + 0.5f) * model->brushq3.num_lightgrid_cellsize[2];
4535 for (y = 0;y < model->brushq3.num_lightgrid_isize[1];y++)
4537 pos[1] = (model->brushq3.num_lightgrid_imins[1] + y + 0.5f) * model->brushq3.num_lightgrid_cellsize[1];
4538 for (x = 0;x < model->brushq3.num_lightgrid_isize[0];x++, index++)
4540 pos[0] = (model->brushq3.num_lightgrid_imins[0] + x + 0.5f) * model->brushq3.num_lightgrid_cellsize[0];
4541 Mod_GenerateLightmaps_GridSample(pos, model->brushq3.data_lightgrid + index);
4547 extern cvar_t mod_q3bsp_nolightmaps;
4548 static void Mod_GenerateLightmaps(dp_model_t *model)
4550 //lightmaptriangle_t *lightmaptriangles = Mem_Alloc(model->mempool, model->surfmesh.num_triangles * sizeof(lightmaptriangle_t));
4551 dp_model_t *oldloadmodel = loadmodel;
4554 Mod_GenerateLightmaps_InitSampleOffsets(model);
4555 Mod_GenerateLightmaps_DestroyLightmaps(model);
4556 Mod_GenerateLightmaps_UnweldTriangles(model);
4557 Mod_GenerateLightmaps_CreateTriangleInformation(model);
4558 Mod_GenerateLightmaps_CreateLights(model);
4559 if(!mod_q3bsp_nolightmaps.integer)
4560 Mod_GenerateLightmaps_CreateLightmaps(model);
4561 Mod_GenerateLightmaps_UpdateVertexColors(model);
4562 Mod_GenerateLightmaps_UpdateLightGrid(model);
4563 Mod_GenerateLightmaps_DestroyLights(model);
4564 Mod_GenerateLightmaps_DestroyTriangleInformation(model);
4566 loadmodel = oldloadmodel;
4569 static void Mod_GenerateLightmaps_f(void)
4571 if (Cmd_Argc() != 1)
4573 Con_Printf("usage: mod_generatelightmaps\n");
4578 Con_Printf("no worldmodel loaded\n");
4581 Mod_GenerateLightmaps(cl.worldmodel);
4584 void Mod_Mesh_Create(dp_model_t *mod, const char *name)
4586 memset(mod, 0, sizeof(*mod));
4587 strlcpy(mod->name, name, sizeof(mod->name));
4588 mod->mempool = Mem_AllocPool(name, 0, NULL);
4589 mod->texturepool = R_AllocTexturePool();
4590 mod->Draw = R_Q1BSP_Draw;
4591 mod->DrawDepth = R_Q1BSP_DrawDepth;
4592 mod->DrawDebug = R_Q1BSP_DrawDebug;
4593 mod->DrawPrepass = R_Q1BSP_DrawPrepass;
4594 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
4595 mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
4596 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
4597 mod->DrawLight = R_Q1BSP_DrawLight;
4600 void Mod_Mesh_Destroy(dp_model_t *mod)
4602 Mod_UnloadModel(mod);
4605 // resets the mesh model to have no geometry to render, ready for a new frame -
4606 // the mesh will be prepared for rendering later using Mod_Mesh_Finalize
4607 void Mod_Mesh_Reset(dp_model_t *mod)
4609 mod->num_surfaces = 0;
4610 mod->surfmesh.num_vertices = 0;
4611 mod->surfmesh.num_triangles = 0;
4612 memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
4613 mod->DrawSky = NULL; // will be set if a texture needs it
4614 mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
4617 texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name, int defaultdrawflags, int defaulttexflags, int addmaterialflags)
4621 for (i = 0; i < mod->num_textures; i++)
4622 if (!strcmp(mod->data_textures[i].name, name))
4623 return mod->data_textures + i;
4624 if (mod->max_textures <= mod->num_textures)
4626 texture_t *oldtextures = mod->data_textures;
4627 mod->max_textures = max(mod->max_textures * 2, 1024);
4628 mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
4629 // update the pointers
4630 for (i = 0; i < mod->num_surfaces; i++)
4631 mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
4633 t = &mod->data_textures[mod->num_textures++];
4634 Mod_LoadTextureFromQ3Shader(mod->mempool, mod->name, t, name, false, true, defaulttexflags);
4635 t->basematerialflags |= addmaterialflags;
4636 switch (defaultdrawflags & DRAWFLAG_MASK)
4638 case DRAWFLAG_ADDITIVE:
4639 t->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED;
4640 t->currentmaterialflags = t->basematerialflags;
4642 case DRAWFLAG_MODULATE:
4643 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4644 t->currentmaterialflags = t->basematerialflags;
4645 t->customblendfunc[0] = GL_DST_COLOR;
4646 t->customblendfunc[1] = GL_ZERO;
4648 case DRAWFLAG_2XMODULATE:
4649 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4650 t->currentmaterialflags = t->basematerialflags;
4651 t->customblendfunc[0] = GL_DST_COLOR;
4652 t->customblendfunc[1] = GL_SRC_COLOR;
4654 case DRAWFLAG_SCREEN:
4655 t->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_BLENDED;
4656 t->currentmaterialflags = t->basematerialflags;
4657 t->customblendfunc[0] = GL_ONE_MINUS_DST_COLOR;
4658 t->customblendfunc[1] = GL_ONE;
4666 msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex, qboolean batchwithprevioussurface)
4669 // batch if possible; primarily useful for UI rendering where bounding boxes don't matter
4670 if (batchwithprevioussurface && mod->num_surfaces > 0 && mod->data_surfaces[mod->num_surfaces - 1].texture == tex)
4671 return mod->data_surfaces + mod->num_surfaces - 1;
4672 // create new surface
4673 if (mod->max_surfaces == mod->num_surfaces)
4675 mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
4676 mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
4677 mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
4679 surf = mod->data_surfaces + mod->num_surfaces;
4680 mod->num_surfaces++;
4681 memset(surf, 0, sizeof(*surf));
4682 surf->texture = tex;
4683 surf->num_firsttriangle = mod->surfmesh.num_triangles;
4684 surf->num_firstvertex = mod->surfmesh.num_vertices;
4685 if (tex->basematerialflags & (MATERIALFLAG_SKY))
4686 mod->DrawSky = R_Q1BSP_DrawSky;
4687 if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
4688 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
4692 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)
4694 int hashindex, h, vnum, mask;
4695 surfmesh_t *mesh = &mod->surfmesh;
4696 if (mesh->max_vertices == mesh->num_vertices)
4698 mesh->max_vertices = max(mesh->num_vertices * 2, 256);
4699 mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
4700 mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
4701 mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
4702 mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
4703 mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
4704 mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
4705 mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
4706 // rebuild the hash table
4707 mesh->num_vertexhashsize = 4 * mesh->max_vertices;
4708 mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
4709 mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4710 memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
4711 mask = mod->surfmesh.num_vertexhashsize - 1;
4712 // no need to hash the vertices for the entire model, the latest surface will suffice.
4713 for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
4715 // this uses prime numbers intentionally for computing the hash
4716 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;
4717 for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
4718 ; // just iterate until we find the terminator
4719 mesh->data_vertexhash[h] = vnum;
4722 mask = mod->surfmesh.num_vertexhashsize - 1;
4723 // this uses prime numbers intentionally for computing the hash
4724 hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
4725 // when possible find an identical vertex within the same surface and return it
4726 for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
4728 if (vnum >= surf->num_firstvertex
4729 && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
4730 && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
4731 && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
4732 && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
4733 && 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)
4736 // add the new vertex
4737 vnum = mesh->num_vertices++;
4738 if (surf->num_vertices > 0)
4740 if (surf->mins[0] > x) surf->mins[0] = x;
4741 if (surf->mins[1] > y) surf->mins[1] = y;
4742 if (surf->mins[2] > z) surf->mins[2] = z;
4743 if (surf->maxs[0] < x) surf->maxs[0] = x;
4744 if (surf->maxs[1] < y) surf->maxs[1] = y;
4745 if (surf->maxs[2] < z) surf->maxs[2] = z;
4749 VectorSet(surf->mins, x, y, z);
4750 VectorSet(surf->maxs, x, y, z);
4752 surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
4753 mesh->data_vertexhash[h] = vnum;
4754 mesh->data_vertex3f[vnum * 3 + 0] = x;
4755 mesh->data_vertex3f[vnum * 3 + 1] = y;
4756 mesh->data_vertex3f[vnum * 3 + 2] = z;
4757 mesh->data_normal3f[vnum * 3 + 0] = nx;
4758 mesh->data_normal3f[vnum * 3 + 1] = ny;
4759 mesh->data_normal3f[vnum * 3 + 2] = nz;
4760 mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
4761 mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
4762 mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
4763 mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
4764 mesh->data_lightmapcolor4f[vnum * 4 + 0] = r;
4765 mesh->data_lightmapcolor4f[vnum * 4 + 1] = g;
4766 mesh->data_lightmapcolor4f[vnum * 4 + 2] = b;
4767 mesh->data_lightmapcolor4f[vnum * 4 + 3] = a;
4771 void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
4773 surfmesh_t *mesh = &mod->surfmesh;
4774 if (mesh->max_triangles == mesh->num_triangles)
4776 mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
4777 mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
4778 mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
4780 mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
4781 mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
4782 mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
4783 mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
4784 mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
4785 mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
4786 mesh->num_triangles++;
4787 surf->num_triangles++;
4790 static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
4794 msurface_t *surf, *surf2;
4796 // build the sorted surfaces list properly to reduce material setup
4797 // this is easy because we're just sorting on texture and don't care about the order of textures
4798 mod->nummodelsurfaces = 0;
4799 for (i = 0; i < mod->num_surfaces; i++)
4800 mod->data_surfaces[i].included = false;
4801 for (i = 0; i < mod->num_surfaces; i++)
4803 surf = mod->data_surfaces + i;
4806 tex = surf->texture;
4807 // j = i is intentional
4808 for (j = i; j < mod->num_surfaces; j++)
4810 surf2 = mod->data_surfaces + j;
4811 if (surf2->included)
4813 if (surf2->texture == tex)
4815 surf2->included = true;
4816 mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
4822 void Mod_Mesh_ComputeBounds(dp_model_t *mod)
4825 vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
4827 if (mod->surfmesh.num_vertices > 0)
4829 // calculate normalmins/normalmaxs
4830 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
4831 VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
4832 for (i = 1; i < mod->surfmesh.num_vertices; i++)
4834 float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
4835 float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
4836 float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
4837 // expand bounds to include this vertex
4838 if (mod->normalmins[0] > x) mod->normalmins[0] = x;
4839 if (mod->normalmins[1] > y) mod->normalmins[1] = y;
4840 if (mod->normalmins[2] > z) mod->normalmins[2] = z;
4841 if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
4842 if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
4843 if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
4845 // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
4846 // (fast but less accurate than doing it per vertex)
4847 x2a = mod->normalmins[0] * mod->normalmins[0];
4848 x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
4849 y2a = mod->normalmins[1] * mod->normalmins[1];
4850 y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
4851 z2a = mod->normalmins[2] * mod->normalmins[2];
4852 z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
4856 yawradius = sqrt(x2 + y2);
4857 rotatedradius = sqrt(x2 + y2 + z2);
4858 VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
4859 VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
4860 VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
4861 VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
4862 mod->radius = rotatedradius;
4863 mod->radius2 = x2 + y2 + z2;
4867 VectorClear(mod->normalmins);
4868 VectorClear(mod->normalmaxs);
4869 VectorClear(mod->yawmins);
4870 VectorClear(mod->yawmaxs);
4871 VectorClear(mod->rotatedmins);
4872 VectorClear(mod->rotatedmaxs);
4878 void Mod_Mesh_Finalize(dp_model_t *mod)
4880 Mod_Mesh_ComputeBounds(mod);
4881 Mod_Mesh_MakeSortedSurfaces(mod);
4882 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);