return success;
}
+void Mod_LoadCustomMaterial(texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe)
+{
+ if (!(materialflags & (MATERIALFLAG_WALL | MATERIALFLAG_SKY)))
+ Con_DPrintf("^1%s:^7 Custom texture ^3\"%s\" does not have MATERIALFLAG_WALL set\n", loadmodel->name, texture->name);
+ strlcpy(texture->name, name, sizeof(texture->name));
+ texture->basealpha = 1.0f;
+ texture->basematerialflags = texture->currentmaterialflags = materialflags;
+ texture->supercontents = supercontents;
+
+ texture->offsetmapping = (mod_noshader_default_offsetmapping.value) ? OFFSETMAPPING_DEFAULT : OFFSETMAPPING_OFF;
+ texture->offsetscale = 1;
+ texture->offsetbias = 0;
+ texture->specularscalemod = 1;
+ texture->specularpowermod = 1;
+ texture->rtlightambient = 0;
+ texture->transparentsort = TRANSPARENTSORT_DISTANCE;
+ // WHEN ADDING DEFAULTS HERE, REMEMBER TO PUT DEFAULTS IN ALL LOADERS
+ // JUST GREP FOR "specularscalemod = 1".
+
+ if (developer_extra.integer)
+ Con_DPrintf("^1%s:^7 Custom texture ^3\"%s\"\n", loadmodel->name, texture->name);
+ texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(skinframe);
+
+ // init the animation variables
+ texture->currentframe = texture;
+ if (!texture->materialshaderpass)
+ texture->materialshaderpass = texture->shaderpasses[0] = Mod_CreateShaderPass(R_SkinFrame_LoadMissing());
+ if (!texture->materialshaderpass->skinframes[0])
+ texture->materialshaderpass->skinframes[0] = R_SkinFrame_LoadMissing();
+ texture->currentskinframe = texture->materialshaderpass ? texture->materialshaderpass->skinframes[0] : NULL;
+ texture->backgroundcurrentskinframe = texture->backgroundshaderpass ? texture->backgroundshaderpass->skinframes[0] : NULL;
+}
+
skinfile_t *Mod_LoadSkinFiles(void)
{
int i, words, line, wordsoverflow;
}
Mod_GenerateLightmaps(cl.worldmodel);
}
+
+void Mod_Mesh_Create(dp_model_t *mod, const char *name)
+{
+ memset(mod, 0, sizeof(*mod));
+ strlcpy(mod->name, name, sizeof(mod->name));
+ mod->mempool = Mem_AllocPool(name, 0, NULL);
+ mod->texturepool = R_AllocTexturePool();
+ mod->Draw = R_Q1BSP_Draw;
+ mod->DrawDepth = R_Q1BSP_DrawDepth;
+ mod->DrawDebug = R_Q1BSP_DrawDebug;
+ mod->DrawPrepass = R_Q1BSP_DrawPrepass;
+ mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+ mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
+ mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
+ mod->DrawLight = R_Q1BSP_DrawLight;
+}
+
+void Mod_Mesh_Destroy(dp_model_t *mod)
+{
+ Mod_UnloadModel(mod);
+}
+
+// resets the mesh model to have no geometry to render, ready for a new frame -
+// the mesh will be prepared for rendering later using Mod_Mesh_Finalize
+void Mod_Mesh_Reset(dp_model_t *mod)
+{
+ mod->num_surfaces = 0;
+ mod->surfmesh.num_vertices = 0;
+ mod->surfmesh.num_triangles = 0;
+ memset(mod->surfmesh.data_vertexhash, -1, mod->surfmesh.num_vertexhashsize * sizeof(*mod->surfmesh.data_vertexhash));
+ mod->DrawSky = NULL; // will be set if a texture needs it
+ mod->DrawAddWaterPlanes = NULL; // will be set if a texture needs it
+}
+
+texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name)
+{
+ int i;
+ texture_t *t;
+ for (i = 0; i < mod->num_textures; i++)
+ if (!strcmp(mod->data_textures[i].name, name))
+ return mod->data_textures + i;
+ if (mod->max_textures <= mod->num_textures)
+ {
+ texture_t *oldtextures = mod->data_textures;
+ mod->max_textures = max(mod->max_textures * 2, 1024);
+ mod->data_textures = (texture_t *)Mem_Realloc(mod->mempool, mod->data_textures, mod->max_textures * sizeof(*mod->data_textures));
+ // update the pointers
+ for (i = 0; i < mod->num_surfaces; i++)
+ mod->data_surfaces[i].texture = mod->data_textures + (mod->data_surfaces[i].texture - oldtextures);
+ }
+ t = &mod->data_textures[mod->num_textures++];
+ Mod_LoadTextureFromQ3Shader(t, name, false, true, 0);
+ return t;
+}
+
+msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex)
+{
+ msurface_t *surf;
+ // check if the proposed surface matches the last one we created
+ if (mod->num_surfaces == 0 || mod->data_surfaces[mod->num_surfaces - 1].texture != tex)
+ {
+ if (mod->max_surfaces == mod->num_surfaces)
+ {
+ mod->max_surfaces = 2 * max(mod->num_surfaces, 64);
+ mod->data_surfaces = (msurface_t *)Mem_Realloc(mod->mempool, mod->data_surfaces, mod->max_surfaces * sizeof(*mod->data_surfaces));
+ mod->sortedmodelsurfaces = (int *)Mem_Realloc(mod->mempool, mod->sortedmodelsurfaces, mod->max_surfaces * sizeof(*mod->sortedmodelsurfaces));
+ }
+ surf = mod->data_surfaces + mod->num_surfaces;
+ mod->num_surfaces++;
+ memset(surf, 0, sizeof(*surf));
+ surf->texture = tex;
+ surf->num_firsttriangle = mod->surfmesh.num_triangles;
+ surf->num_firstvertex = mod->surfmesh.num_vertices;
+ if (tex->basematerialflags & (MATERIALFLAG_SKY))
+ mod->DrawSky = R_Q1BSP_DrawSky;
+ if (tex->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+ mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+ return surf;
+ }
+ return mod->data_surfaces + mod->num_surfaces - 1;
+}
+
+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)
+{
+ int hashindex, h, vnum, mask;
+ surfmesh_t *mesh = &mod->surfmesh;
+ if (mesh->max_vertices == mesh->num_vertices)
+ {
+ mesh->max_vertices = max(mesh->num_vertices * 2, 256);
+ mesh->data_vertex3f = (float *)Mem_Realloc(mod->mempool, mesh->data_vertex3f, mesh->max_vertices * sizeof(float[3]));
+ mesh->data_svector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_svector3f, mesh->max_vertices * sizeof(float[3]));
+ mesh->data_tvector3f = (float *)Mem_Realloc(mod->mempool, mesh->data_tvector3f, mesh->max_vertices * sizeof(float[3]));
+ mesh->data_normal3f = (float *)Mem_Realloc(mod->mempool, mesh->data_normal3f, mesh->max_vertices * sizeof(float[3]));
+ mesh->data_texcoordtexture2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordtexture2f, mesh->max_vertices * sizeof(float[2]));
+ mesh->data_texcoordlightmap2f = (float *)Mem_Realloc(mod->mempool, mesh->data_texcoordlightmap2f, mesh->max_vertices * sizeof(float[2]));
+ mesh->data_lightmapcolor4f = (float *)Mem_Realloc(mod->mempool, mesh->data_lightmapcolor4f, mesh->max_vertices * sizeof(float[4]));
+ // rebuild the hash table
+ mesh->num_vertexhashsize = 4 * mesh->max_vertices;
+ mesh->num_vertexhashsize &= ~(mesh->num_vertexhashsize - 1); // round down to pow2
+ mesh->data_vertexhash = (int *)Mem_Realloc(mod->mempool, mesh->data_vertexhash, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
+ memset(mesh->data_vertexhash, -1, mesh->num_vertexhashsize * sizeof(*mesh->data_vertexhash));
+ mask = mod->surfmesh.num_vertexhashsize - 1;
+ // no need to hash the vertices for the entire model, the latest surface will suffice.
+ for (vnum = surf ? surf->num_firstvertex : 0; vnum < mesh->num_vertices; vnum++)
+ {
+ // this uses prime numbers intentionally for computing the hash
+ 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;
+ for (h = hashindex; mesh->data_vertexhash[h] >= 0; h = (h + 1) & mask)
+ ; // just iterate until we find the terminator
+ mesh->data_vertexhash[h] = vnum;
+ }
+ }
+ mask = mod->surfmesh.num_vertexhashsize - 1;
+ // this uses prime numbers intentionally for computing the hash
+ hashindex = (unsigned int)(x * 2003 + y * 4001 + z * 7919 + nx * 4097 + ny * 257 + nz * 17) & mask;
+ // when possible find an identical vertex within the same surface and return it
+ for(h = hashindex;(vnum = mesh->data_vertexhash[h]) >= 0;h = (h + 1) & mask)
+ {
+ if (vnum >= surf->num_firstvertex
+ && mesh->data_vertex3f[vnum * 3 + 0] == x && mesh->data_vertex3f[vnum * 3 + 1] == y && mesh->data_vertex3f[vnum * 3 + 2] == z
+ && mesh->data_normal3f[vnum * 3 + 0] == nx && mesh->data_normal3f[vnum * 3 + 1] == ny && mesh->data_normal3f[vnum * 3 + 2] == nz
+ && mesh->data_texcoordtexture2f[vnum * 2 + 0] == s && mesh->data_texcoordtexture2f[vnum * 2 + 1] == t
+ && mesh->data_texcoordlightmap2f[vnum * 2 + 0] == u && mesh->data_texcoordlightmap2f[vnum * 2 + 1] == v
+ && 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)
+ return vnum;
+ }
+ // add the new vertex
+ vnum = mesh->num_vertices++;
+ if (surf->num_vertices > 0)
+ {
+ if (surf->mins[0] > x) surf->mins[0] = x;
+ if (surf->mins[1] > y) surf->mins[1] = y;
+ if (surf->mins[2] > z) surf->mins[2] = z;
+ if (surf->maxs[0] < x) surf->maxs[0] = x;
+ if (surf->maxs[1] < y) surf->maxs[1] = y;
+ if (surf->maxs[2] < z) surf->maxs[2] = z;
+ }
+ else
+ {
+ VectorSet(surf->mins, x, y, z);
+ VectorSet(surf->maxs, x, y, z);
+ }
+ surf->num_vertices = mesh->num_vertices - surf->num_firstvertex;
+ mesh->data_vertexhash[h] = vnum;
+ mesh->data_vertex3f[vnum * 3 + 0] = x;
+ mesh->data_vertex3f[vnum * 3 + 1] = y;
+ mesh->data_vertex3f[vnum * 3 + 2] = z;
+ mesh->data_normal3f[vnum * 3 + 0] = nx;
+ mesh->data_normal3f[vnum * 3 + 1] = ny;
+ mesh->data_normal3f[vnum * 3 + 2] = nz;
+ mesh->data_texcoordtexture2f[vnum * 2 + 0] = s;
+ mesh->data_texcoordtexture2f[vnum * 2 + 1] = t;
+ mesh->data_texcoordlightmap2f[vnum * 2 + 0] = u;
+ mesh->data_texcoordlightmap2f[vnum * 2 + 1] = v;
+ 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;
+ return vnum;
+}
+
+void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2)
+{
+ surfmesh_t *mesh = &mod->surfmesh;
+ if (mesh->max_triangles == mesh->num_triangles)
+ {
+ mesh->max_triangles = 2 * max(mesh->num_triangles, 128);
+ mesh->data_element3s = (unsigned short *)Mem_Realloc(mod->mempool, mesh->data_element3s, mesh->max_triangles * sizeof(unsigned short[3]));
+ mesh->data_element3i = (int *)Mem_Realloc(mod->mempool, mesh->data_element3i, mesh->max_triangles * sizeof(int[3]));
+ }
+ mesh->data_element3s[mesh->num_triangles * 3 + 0] = e0;
+ mesh->data_element3s[mesh->num_triangles * 3 + 1] = e1;
+ mesh->data_element3s[mesh->num_triangles * 3 + 2] = e2;
+ mesh->data_element3i[mesh->num_triangles * 3 + 0] = e0;
+ mesh->data_element3i[mesh->num_triangles * 3 + 1] = e1;
+ mesh->data_element3i[mesh->num_triangles * 3 + 2] = e2;
+ mesh->num_triangles++;
+ surf->num_triangles++;
+}
+
+static void Mod_Mesh_MakeSortedSurfaces(dp_model_t *mod)
+{
+ int i, j;
+ texture_t *tex;
+ msurface_t *surf, *surf2;
+
+ // build the sorted surfaces list properly to reduce material setup
+ // this is easy because we're just sorting on texture and don't care about the order of textures
+ mod->nummodelsurfaces = 0;
+ for (i = 0; i < mod->num_surfaces; i++)
+ mod->data_surfaces[i].included = false;
+ for (i = 0; i < mod->num_surfaces; i++)
+ {
+ surf = mod->data_surfaces + i;
+ if (surf->included)
+ continue;
+ tex = surf->texture;
+ // j = i is intentional
+ for (j = i; j < mod->num_surfaces; j++)
+ {
+ surf2 = mod->data_surfaces + j;
+ if (surf2->included)
+ continue;
+ if (surf2->texture == tex)
+ {
+ surf2->included = true;
+ mod->sortedmodelsurfaces[mod->nummodelsurfaces++] = j;
+ }
+ }
+ }
+}
+
+void Mod_Mesh_ComputeBounds(dp_model_t *mod)
+{
+ int i;
+ vec_t x2a, x2b, y2a, y2b, z2a, z2b, x2, y2, z2, yawradius, rotatedradius;
+
+ if (mod->surfmesh.num_vertices > 0)
+ {
+ // calculate normalmins/normalmaxs
+ VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmins);
+ VectorCopy(mod->surfmesh.data_vertex3f, mod->normalmaxs);
+ for (i = 1; i < mod->surfmesh.num_vertices; i++)
+ {
+ float x = mod->surfmesh.data_vertex3f[i * 3 + 0];
+ float y = mod->surfmesh.data_vertex3f[i * 3 + 1];
+ float z = mod->surfmesh.data_vertex3f[i * 3 + 2];
+ // expand bounds to include this vertex
+ if (mod->normalmins[0] > x) mod->normalmins[0] = x;
+ if (mod->normalmins[1] > y) mod->normalmins[1] = y;
+ if (mod->normalmins[2] > z) mod->normalmins[2] = z;
+ if (mod->normalmaxs[0] < x) mod->normalmaxs[0] = x;
+ if (mod->normalmaxs[1] < y) mod->normalmaxs[1] = y;
+ if (mod->normalmaxs[2] < z) mod->normalmaxs[2] = z;
+ }
+ // calculate yawmins/yawmaxs, rotatedmins/maxs from normalmins/maxs
+ // (fast but less accurate than doing it per vertex)
+ x2a = mod->normalmins[0] * mod->normalmins[0];
+ x2b = mod->normalmaxs[0] * mod->normalmaxs[0];
+ y2a = mod->normalmins[1] * mod->normalmins[1];
+ y2b = mod->normalmaxs[1] * mod->normalmaxs[1];
+ z2a = mod->normalmins[2] * mod->normalmins[2];
+ z2b = mod->normalmaxs[2] * mod->normalmaxs[2];
+ x2 = max(x2a, x2b);
+ y2 = max(y2a, y2b);
+ z2 = max(z2a, z2b);
+ yawradius = sqrt(x2 + y2);
+ rotatedradius = sqrt(x2 + y2 + z2);
+ VectorSet(mod->yawmins, -yawradius, -yawradius, mod->normalmins[2]);
+ VectorSet(mod->yawmaxs, yawradius, yawradius, mod->normalmaxs[2]);
+ VectorSet(mod->rotatedmins, -rotatedradius, -rotatedradius, -rotatedradius);
+ VectorSet(mod->rotatedmaxs, rotatedradius, rotatedradius, rotatedradius);
+ mod->radius = rotatedradius;
+ mod->radius2 = x2 + y2 + z2;
+ }
+ else
+ {
+ VectorClear(mod->normalmins);
+ VectorClear(mod->normalmaxs);
+ VectorClear(mod->yawmins);
+ VectorClear(mod->yawmaxs);
+ VectorClear(mod->rotatedmins);
+ VectorClear(mod->rotatedmaxs);
+ mod->radius = 0;
+ mod->radius2 = 0;
+ }
+}
+
+void Mod_Mesh_Finalize(dp_model_t *mod)
+{
+ Mod_Mesh_ComputeBounds(mod);
+ Mod_Mesh_MakeSortedSurfaces(mod);
+ 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);
+}
// vertex and index buffers for rendering
r_meshbuffer_t *vertexmesh_vertexbuffer;
+
+ // dynamic mesh building support (Mod_Mesh_*)
+ int num_vertexhashsize; // always pow2 for simple masking
+ int *data_vertexhash; // hash table - wrapping buffer for storing index of similar vertex with -1 as terminator
+ int max_vertices; // preallocated size of data_vertex3f and friends (always >= num_vertices)
+ int max_triangles; // preallocated size of data_element3i
}
surfmesh_t;
int deprecatedq3num_bboxstride;
// FIXME: collisionmarkframe should be kept in a separate array
int deprecatedq3collisionmarkframe; // q3bsp // don't collide twice in one trace
+
+ // used by Mod_Mesh_Finalize when building sortedmodelsurfaces
+ qboolean included;
}
msurface_t;
float *data_baseboneposeinverse;
// textures of this model
int num_textures;
+ int max_textures; // preallocated for expansion (Mod_Mesh_*)
int num_texturesperskin;
texture_t *data_textures;
qboolean wantnormals;
qboolean wanttangents;
// surfaces of this model
int num_surfaces;
+ int max_surfaces; // preallocated for expansion (Mod_Mesh_*)
msurface_t *data_surfaces;
// optional lightmapinfo data for surface lightmap updates
msurface_lightmapinfo_t *data_surfaces_lightmapinfo;
qboolean Mod_LoadTextureFromQ3Shader(texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags);
texture_shaderpass_t *Mod_CreateShaderPass(skinframe_t *skinframe);
texture_shaderpass_t *Mod_CreateShaderPassFromQ3ShaderLayer(q3shaderinfo_layer_t *layer, int layerindex, int texflags, const char *texturename);
+/// Sets up a material to render the provided skinframe. See also R_SkinFrame_LoadInternalBGRA.
+void Mod_LoadCustomMaterial(texture_t *texture, const char *name, int supercontents, int materialflags, skinframe_t *skinframe);
extern cvar_t r_mipskins;
extern cvar_t r_mipnormalmaps;
void R_Q1BSP_DrawShadowVolume(struct entity_render_s *ent, const vec3_t relativelightorigin, const vec3_t relativelightdirection, float lightradius, int numsurfaces, const int *surfacelist, const vec3_t lightmins, const vec3_t lightmaxs);
void R_Q1BSP_DrawLight(struct entity_render_s *ent, int numsurfaces, const int *surfacelist, const unsigned char *trispvs);
+// dynamic mesh building (every frame) for debugging and other uses
+void Mod_Mesh_Create(dp_model_t *mod, const char *name);
+void Mod_Mesh_Destroy(dp_model_t *mod);
+void Mod_Mesh_Reset(dp_model_t *mod);
+texture_t *Mod_Mesh_GetTexture(dp_model_t *mod, const char *name);
+msurface_t *Mod_Mesh_AddSurface(dp_model_t *mod, texture_t *tex);
+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);
+void Mod_Mesh_AddTriangle(dp_model_t *mod, msurface_t *surf, int e0, int e1, int e2);
+void Mod_Mesh_Finalize(dp_model_t *mod);
+
// Collision optimization using Bounding Interval Hierarchy
void Mod_CollisionBIH_TracePoint(dp_model_t *model, const struct frameblend_s *frameblend, const skeleton_t *skeleton, struct trace_s *trace, const vec3_t start, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask);
void Mod_CollisionBIH_TraceLine(dp_model_t *model, const struct frameblend_s *frameblend, const skeleton_t *skeleton, struct trace_s *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask);