#include "image.h"
#include "r_shadow.h"
-cvar_t r_mipskins = {CVAR_SAVE, "r_mipskins", "0", "mipmaps skins (so they become blurrier in the distance), disabled by default because it tends to blur with strange border colors from the skin"};
+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"};
model_t *loadmodel;
static void mod_newmap(void)
{
msurface_t *surface;
- int i, surfacenum, ssize, tsize;
+ int i, j, k, surfacenum, ssize, tsize;
+
+ R_SkinFrame_PrepareForPurge();
+ for (i = 0;i < mod_numknown;i++)
+ {
+ if (mod_known[i].mempool && mod_known[i].data_textures)
+ {
+ for (j = 0;j < mod_known[i].num_textures;j++)
+ {
+ for (k = 0;k < mod_known[i].data_textures[j].numskinframes;k++)
+ R_SkinFrame_MarkUsed(mod_known[i].data_textures[j].skinframes[k]);
+ for (k = 0;k < mod_known[i].data_textures[j].backgroundnumskinframes;k++)
+ R_SkinFrame_MarkUsed(mod_known[i].data_textures[j].backgroundskinframes[k]);
+ }
+ }
+ }
+ R_SkinFrame_Purge();
if (!cl_stainmaps_clearonload.integer)
return;
*/
static void Mod_Print(void);
static void Mod_Precache (void);
+static void Mod_BuildVBOs(void);
void Mod_Init (void)
{
Mod_BrushInit();
strlcpy(name, mod->name, sizeof(name));
isworldmodel = mod->isworldmodel;
used = mod->used;
+ if (mod->surfmesh.ebo)
+ R_Mesh_DestroyBufferObject(mod->surfmesh.ebo);
+ if (mod->surfmesh.vbo)
+ R_Mesh_DestroyBufferObject(mod->surfmesh.vbo);
// free textures/memory attached to the model
R_FreeTexturePool(&mod->texturepool);
Mem_FreePool(&mod->mempool);
crc = 0;
buf = NULL;
+
+ // even if the model is loaded it still may need reloading...
+
+ // if the model is a worldmodel and is being referred to as a
+ // non-worldmodel here, then it needs reloading to get rid of the
+ // submodels
if (mod->isworldmodel != isworldmodel)
mod->loaded = false;
+
+ // if it is not loaded or checkdisk is true we need to calculate the crc
if (!mod->loaded || checkdisk)
{
if (checkdisk && mod->loaded)
if (buf)
{
crc = CRC_Block((unsigned char *)buf, filesize);
+ // we need to reload the model if the crc does not match
if (mod->crc != crc)
mod->loaded = false;
}
}
+
+ // if the model is already loaded and checks passed, just return
if (mod->loaded)
{
- // already loaded
if (buf)
Mem_Free(buf);
return mod;
// all models use memory, so allocate a memory pool
mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
- // all models load textures, so allocate a texture pool
- if (cls.state != ca_dedicated)
- mod->texturepool = R_AllocTexturePool();
num = LittleLong(*((int *)buf));
// call the apropriate loader
else if (num == BSPVERSION || num == 30) Mod_Q1BSP_Load(mod, buf, bufend);
else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
Mem_Free(buf);
+
+ Mod_BuildVBOs();
+
// no fatal errors occurred, so this model is ready to use.
mod->loaded = true;
}
Con_Print("Loaded models:\n");
for (i = 0, mod = mod_known;i < mod_numknown;i++, mod++)
if (mod->name[0])
- Con_Printf("%4iK %s\n", mod->mempool ? (mod->mempool->totalsize + 1023) / 1024 : 0, mod->name);
+ Con_Printf("%4iK %s\n", mod->mempool ? (int)((mod->mempool->totalsize + 1023) / 1024) : 0, mod->name);
}
/*
return Mod_ShadowMesh_Alloc(mempool, maxverts, maxtriangles, map_diffuse, map_specular, map_normal, light, neighbors, expandable);
}
-shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, int light, int neighbors)
+static void Mod_ShadowMesh_CreateVBOs(shadowmesh_t *mesh)
+{
+ if (!gl_support_arb_vertex_buffer_object)
+ return;
+
+ // element buffer is easy because it's just one array
+ if (mesh->numtriangles)
+ mesh->ebo = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, mesh->element3i, mesh->numtriangles * sizeof(int[3]), "shadowmesh");
+
+ // vertex buffer is several arrays and we put them in the same buffer
+ //
+ // is this wise? the texcoordtexture2f array is used with dynamic
+ // vertex/svector/tvector/normal when rendering animated models, on the
+ // other hand animated models don't use a lot of vertices anyway...
+ if (mesh->numverts)
+ {
+ size_t size;
+ unsigned char *mem;
+ size = 0;
+ mesh->vbooffset_vertex3f = size;if (mesh->vertex3f ) size += mesh->numverts * sizeof(float[3]);
+ mesh->vbooffset_svector3f = size;if (mesh->svector3f ) size += mesh->numverts * sizeof(float[3]);
+ mesh->vbooffset_tvector3f = size;if (mesh->tvector3f ) size += mesh->numverts * sizeof(float[3]);
+ mesh->vbooffset_normal3f = size;if (mesh->normal3f ) size += mesh->numverts * sizeof(float[3]);
+ mesh->vbooffset_texcoord2f = size;if (mesh->texcoord2f ) size += mesh->numverts * sizeof(float[2]);
+ mem = (unsigned char *)Mem_Alloc(tempmempool, size);
+ if (mesh->vertex3f ) memcpy(mem + mesh->vbooffset_vertex3f , mesh->vertex3f , mesh->numverts * sizeof(float[3]));
+ if (mesh->svector3f ) memcpy(mem + mesh->vbooffset_svector3f , mesh->svector3f , mesh->numverts * sizeof(float[3]));
+ if (mesh->tvector3f ) memcpy(mem + mesh->vbooffset_tvector3f , mesh->tvector3f , mesh->numverts * sizeof(float[3]));
+ if (mesh->normal3f ) memcpy(mem + mesh->vbooffset_normal3f , mesh->normal3f , mesh->numverts * sizeof(float[3]));
+ if (mesh->texcoord2f ) memcpy(mem + mesh->vbooffset_texcoord2f , mesh->texcoord2f , mesh->numverts * sizeof(float[2]));
+ mesh->vbo = R_Mesh_CreateStaticBufferObject(GL_ARRAY_BUFFER_ARB, mem, size, "shadowmesh");
+ Mem_Free(mem);
+ }
+}
+
+shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh, qboolean light, qboolean neighbors, qboolean createvbo)
{
shadowmesh_t *mesh, *newmesh, *nextmesh;
// reallocate meshs to conserve space
newmesh = Mod_ShadowMesh_ReAlloc(mempool, mesh, light, neighbors);
newmesh->next = firstmesh;
firstmesh = newmesh;
+ if (createvbo)
+ Mod_ShadowMesh_CreateVBOs(newmesh);
}
Mem_Free(mesh);
}
shadowmesh_t *nextmesh;
for (;mesh;mesh = nextmesh)
{
+ if (mesh->ebo)
+ R_Mesh_DestroyBufferObject(mesh->ebo);
+ if (mesh->vbo)
+ R_Mesh_DestroyBufferObject(mesh->vbo);
nextmesh = mesh->next;
Mem_Free(mesh);
}
}
-static rtexture_t *GL_TextureForSkinLayer(const unsigned char *in, int width, int height, const char *name, const unsigned int *palette, int textureflags, qboolean force)
-{
- int i;
- if (!force)
- {
- for (i = 0;i < width*height;i++)
- if (((unsigned char *)&palette[in[i]])[3] > 0)
- break;
- if (i == width*height)
- return NULL;
- }
- return R_LoadTexture2D (loadmodel->texturepool, name, width, height, in, TEXTYPE_PALETTE, textureflags, palette);
-}
-
-int Mod_LoadSkinFrame(skinframe_t *skinframe, const char *basename, int textureflags, int loadpantsandshirt, int loadglowtexture)
-{
- imageskin_t s;
- memset(skinframe, 0, sizeof(*skinframe));
- if (cls.state == ca_dedicated)
- return false;
- if (!image_loadskin(&s, basename))
- return false;
- skinframe->base = R_LoadTexture2D (loadmodel->texturepool, basename, s.basepixels_width, s.basepixels_height, s.basepixels, TEXTYPE_RGBA, textureflags, NULL);
- if (s.nmappixels != NULL)
- skinframe->nmap = R_LoadTexture2D (loadmodel->texturepool, va("%s_nmap", basename), s.nmappixels_width, s.nmappixels_height, s.nmappixels, TEXTYPE_RGBA, textureflags, NULL);
- if (s.glosspixels != NULL)
- skinframe->gloss = R_LoadTexture2D (loadmodel->texturepool, va("%s_gloss", basename), s.glosspixels_width, s.glosspixels_height, s.glosspixels, TEXTYPE_RGBA, textureflags, NULL);
- if (s.glowpixels != NULL && loadglowtexture)
- skinframe->glow = R_LoadTexture2D (loadmodel->texturepool, va("%s_glow", basename), s.glowpixels_width, s.glowpixels_height, s.glowpixels, TEXTYPE_RGBA, textureflags, NULL);
- if (s.maskpixels != NULL)
- skinframe->fog = R_LoadTexture2D (loadmodel->texturepool, va("%s_mask", basename), s.maskpixels_width, s.maskpixels_height, s.maskpixels, TEXTYPE_RGBA, textureflags, NULL);
- if (loadpantsandshirt)
- {
- if (s.pantspixels != NULL)
- skinframe->pants = R_LoadTexture2D (loadmodel->texturepool, va("%s_pants", basename), s.pantspixels_width, s.pantspixels_height, s.pantspixels, TEXTYPE_RGBA, textureflags, NULL);
- if (s.shirtpixels != NULL)
- skinframe->shirt = R_LoadTexture2D (loadmodel->texturepool, va("%s_shirt", basename), s.shirtpixels_width, s.shirtpixels_height, s.shirtpixels, TEXTYPE_RGBA, textureflags, NULL);
- }
- if (!skinframe->base)
- skinframe->base = r_texture_notexture;
- if (!skinframe->nmap)
- skinframe->nmap = r_texture_blanknormalmap;
- image_freeskin(&s);
- return true;
-}
-
-int Mod_LoadSkinFrame_Internal(skinframe_t *skinframe, const char *basename, int textureflags, int loadpantsandshirt, int loadglowtexture, const unsigned char *skindata, int width, int height, int bitsperpixel, const unsigned int *palette, const unsigned int *alphapalette)
-{
- int i;
- unsigned char *temp1, *temp2;
- memset(skinframe, 0, sizeof(*skinframe));
- if (cls.state == ca_dedicated)
- return false;
- if (!skindata)
- return false;
- if (bitsperpixel == 32)
- {
- if (r_shadow_bumpscale_basetexture.value > 0)
- {
- temp1 = (unsigned char *)Mem_Alloc(loadmodel->mempool, width * height * 8);
- temp2 = temp1 + width * height * 4;
- Image_HeightmapToNormalmap(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
- skinframe->nmap = R_LoadTexture2D(loadmodel->texturepool, va("%s_nmap", basename), width, height, temp2, TEXTYPE_RGBA, textureflags | TEXF_ALPHA, NULL);
- Mem_Free(temp1);
- }
- skinframe->base = skinframe->merged = R_LoadTexture2D(loadmodel->texturepool, basename, width, height, skindata, TEXTYPE_RGBA, textureflags, NULL);
- if (textureflags & TEXF_ALPHA)
- {
- for (i = 3;i < width * height * 4;i += 4)
- if (skindata[i] < 255)
- break;
- if (i < width * height * 4)
- {
- unsigned char *fogpixels = (unsigned char *)Mem_Alloc(loadmodel->mempool, width * height * 4);
- memcpy(fogpixels, skindata, width * height * 4);
- for (i = 0;i < width * height * 4;i += 4)
- fogpixels[i] = fogpixels[i+1] = fogpixels[i+2] = 255;
- skinframe->fog = R_LoadTexture2D(loadmodel->texturepool, va("%s_fog", basename), width, height, fogpixels, TEXTYPE_RGBA, textureflags, NULL);
- Mem_Free(fogpixels);
- }
- }
- }
- else if (bitsperpixel == 8)
- {
- if (r_shadow_bumpscale_basetexture.value > 0)
- {
- temp1 = (unsigned char *)Mem_Alloc(loadmodel->mempool, width * height * 8);
- temp2 = temp1 + width * height * 4;
- if (bitsperpixel == 32)
- Image_HeightmapToNormalmap(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
- else
- {
- // use either a custom palette or the quake palette
- Image_Copy8bitRGBA(skindata, temp1, width * height, palette ? palette : palette_complete);
- Image_HeightmapToNormalmap(temp1, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
- }
- skinframe->nmap = R_LoadTexture2D(loadmodel->texturepool, va("%s_nmap", basename), width, height, temp2, TEXTYPE_RGBA, textureflags | TEXF_ALPHA, NULL);
- Mem_Free(temp1);
- }
- // use either a custom palette, or the quake palette
- skinframe->base = skinframe->merged = GL_TextureForSkinLayer(skindata, width, height, va("%s_merged", basename), palette ? palette : (loadglowtexture ? palette_nofullbrights : ((textureflags & TEXF_ALPHA) ? palette_transparent : palette_complete)), textureflags, true); // all
- if (!palette && loadglowtexture)
- skinframe->glow = GL_TextureForSkinLayer(skindata, width, height, va("%s_glow", basename), palette_onlyfullbrights, textureflags, false); // glow
- if (!palette && loadpantsandshirt)
- {
- skinframe->pants = GL_TextureForSkinLayer(skindata, width, height, va("%s_pants", basename), palette_pantsaswhite, textureflags, false); // pants
- skinframe->shirt = GL_TextureForSkinLayer(skindata, width, height, va("%s_shirt", basename), palette_shirtaswhite, textureflags, false); // shirt
- }
- if (skinframe->pants || skinframe->shirt)
- skinframe->base = GL_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", basename),loadglowtexture ? palette_nocolormapnofullbrights : palette_nocolormap, textureflags, false); // no special colors
- if (textureflags & TEXF_ALPHA)
- {
- // if not using a custom alphapalette, use the quake one
- if (!alphapalette)
- alphapalette = palette_alpha;
- for (i = 0;i < width * height;i++)
- if (((unsigned char *)alphapalette)[skindata[i]*4+3] < 255)
- break;
- if (i < width * height)
- skinframe->fog = GL_TextureForSkinLayer(skindata, width, height, va("%s_fog", basename), alphapalette, textureflags, true); // fog mask
- }
- }
- else
- return false;
- if (!skinframe->nmap)
- skinframe->nmap = r_texture_blanknormalmap;
- return true;
-}
-
void Mod_GetTerrainVertex3fTexCoord2fFromRGBA(const unsigned char *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
{
float v[3], tc[3];
return outtriangles;
}
+void Mod_VertexRangeFromElements(int numelements, const int *elements, int *firstvertexpointer, int *lastvertexpointer)
+{
+ int i, e;
+ int firstvertex, lastvertex;
+ if (numelements > 0 && elements)
+ {
+ firstvertex = lastvertex = elements[0];
+ for (i = 1;i < numelements;i++)
+ {
+ e = elements[i];
+ firstvertex = min(firstvertex, e);
+ lastvertex = max(lastvertex, e);
+ }
+ }
+ else
+ firstvertex = lastvertex = 0;
+ if (firstvertexpointer)
+ *firstvertexpointer = firstvertex;
+ if (lastvertexpointer)
+ *lastvertexpointer = lastvertex;
+}
+
+static void Mod_BuildVBOs(void)
+{
+ if (!gl_support_arb_vertex_buffer_object)
+ return;
+
+ // element buffer is easy because it's just one array
+ if (loadmodel->surfmesh.num_triangles)
+ loadmodel->surfmesh.ebo = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles * sizeof(int[3]), loadmodel->name);
+
+ // vertex buffer is several arrays and we put them in the same buffer
+ //
+ // is this wise? the texcoordtexture2f array is used with dynamic
+ // vertex/svector/tvector/normal when rendering animated models, on the
+ // other hand animated models don't use a lot of vertices anyway...
+ if (loadmodel->surfmesh.num_vertices)
+ {
+ size_t size;
+ unsigned char *mem;
+ size = 0;
+ loadmodel->surfmesh.vbooffset_vertex3f = size;if (loadmodel->surfmesh.data_vertex3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+ loadmodel->surfmesh.vbooffset_svector3f = size;if (loadmodel->surfmesh.data_svector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+ loadmodel->surfmesh.vbooffset_tvector3f = size;if (loadmodel->surfmesh.data_tvector3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+ loadmodel->surfmesh.vbooffset_normal3f = size;if (loadmodel->surfmesh.data_normal3f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[3]);
+ loadmodel->surfmesh.vbooffset_texcoordtexture2f = size;if (loadmodel->surfmesh.data_texcoordtexture2f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+ loadmodel->surfmesh.vbooffset_texcoordlightmap2f = size;if (loadmodel->surfmesh.data_texcoordlightmap2f) size += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
+ loadmodel->surfmesh.vbooffset_lightmapcolor4f = size;if (loadmodel->surfmesh.data_lightmapcolor4f ) size += loadmodel->surfmesh.num_vertices * sizeof(float[4]);
+ mem = (unsigned char *)Mem_Alloc(tempmempool, size);
+ if (loadmodel->surfmesh.data_vertex3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_vertex3f , loadmodel->surfmesh.data_vertex3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+ if (loadmodel->surfmesh.data_svector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_svector3f , loadmodel->surfmesh.data_svector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+ if (loadmodel->surfmesh.data_tvector3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_tvector3f , loadmodel->surfmesh.data_tvector3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+ if (loadmodel->surfmesh.data_normal3f ) memcpy(mem + loadmodel->surfmesh.vbooffset_normal3f , loadmodel->surfmesh.data_normal3f , loadmodel->surfmesh.num_vertices * sizeof(float[3]));
+ if (loadmodel->surfmesh.data_texcoordtexture2f ) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordtexture2f , loadmodel->surfmesh.data_texcoordtexture2f , loadmodel->surfmesh.num_vertices * sizeof(float[2]));
+ if (loadmodel->surfmesh.data_texcoordlightmap2f) memcpy(mem + loadmodel->surfmesh.vbooffset_texcoordlightmap2f, loadmodel->surfmesh.data_texcoordlightmap2f, loadmodel->surfmesh.num_vertices * sizeof(float[2]));
+ if (loadmodel->surfmesh.data_lightmapcolor4f ) memcpy(mem + loadmodel->surfmesh.vbooffset_lightmapcolor4f , loadmodel->surfmesh.data_lightmapcolor4f , loadmodel->surfmesh.num_vertices * sizeof(float[4]));
+ loadmodel->surfmesh.vbo = R_Mesh_CreateStaticBufferObject(GL_ARRAY_BUFFER_ARB, mem, size, loadmodel->name);
+ Mem_Free(mem);
+ }
+}