]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_shared.c
some work on SV_TestEntityPosition and entity unsticking, now only checks against...
[xonotic/darkplaces.git] / model_shared.c
index e100139c2bd743e8c977adac764fe18beefcc694..558e0deaba79b3b3a83f45812b01852acfbcfefa 100644 (file)
@@ -26,7 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "image.h"
 #include "r_shadow.h"
 
-cvar_t r_mipskins = {CVAR_SAVE, "r_mipskins", "0"};
+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"};
 
 model_t *loadmodel;
 
@@ -95,8 +95,8 @@ void Mod_Init (void)
        Mod_SpriteInit();
 
        Cvar_RegisterVariable(&r_mipskins);
-       Cmd_AddCommand ("modellist", Mod_Print);
-       Cmd_AddCommand ("modelprecache", Mod_Precache);
+       Cmd_AddCommand ("modellist", Mod_Print, "prints a list of loaded models");
+       Cmd_AddCommand ("modelprecache", Mod_Precache, "load a model");
 }
 
 void Mod_RenderInit(void)
@@ -136,6 +136,7 @@ model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk, qboolea
        int num;
        unsigned int crc;
        void *buf;
+       fs_offset_t filesize;
 
        mod->used = true;
 
@@ -150,10 +151,10 @@ model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk, qboolea
        {
                if (checkdisk && mod->loaded)
                        Con_DPrintf("checking model %s\n", mod->name);
-               buf = FS_LoadFile (mod->name, tempmempool, false);
+               buf = FS_LoadFile (mod->name, tempmempool, false, &filesize);
                if (buf)
                {
-                       crc = CRC_Block((qbyte *)buf, fs_filesize);
+                       crc = CRC_Block((unsigned char *)buf, filesize);
                        if (mod->crc != crc)
                                mod->loaded = false;
                }
@@ -190,7 +191,7 @@ model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk, qboolea
 
        if (buf)
        {
-               char *bufend = (char *)buf + fs_filesize;
+               char *bufend = (char *)buf + filesize;
                num = LittleLong(*((int *)buf));
                // call the apropriate loader
                loadmodel = mod;
@@ -308,7 +309,7 @@ model_t *Mod_FindName(const char *name)
                return mod;
        }
 
-       Host_Error ("Mod_FindName: ran out of models\n");
+       Host_Error ("Mod_FindName: ran out of models");
        return NULL;
 }
 
@@ -328,7 +329,7 @@ model_t *Mod_ForName(const char *name, qboolean crash, qboolean checkdisk, qbool
        return model;
 }
 
-qbyte *mod_base;
+unsigned char *mod_base;
 
 
 //=============================================================================
@@ -365,8 +366,8 @@ static void Mod_Precache(void)
 int Mod_BuildVertexRemapTableFromElements(int numelements, const int *elements, int numvertices, int *remapvertices)
 {
        int i, count;
-       qbyte *used;
-       used = (qbyte *)Mem_Alloc(tempmempool, numvertices);
+       unsigned char *used;
+       used = (unsigned char *)Mem_Alloc(tempmempool, numvertices);
        memset(used, 0, numvertices);
        for (i = 0;i < numelements;i++)
                used[elements[i]] = 1;
@@ -552,9 +553,9 @@ void Mod_BuildBumpVectors(const float *v0, const float *v1, const float *v2, con
        // 6 multiply, 9 subtract
        VectorSubtract(v1, v0, v10);
        VectorSubtract(v2, v0, v20);
-       normal3f[0] = v10[1] * v20[2] - v10[2] * v20[1];
-       normal3f[1] = v10[2] * v20[0] - v10[0] * v20[2];
-       normal3f[2] = v10[0] * v20[1] - v10[1] * v20[0];
+       normal3f[0] = v20[1] * v10[2] - v20[2] * v10[1];
+       normal3f[1] = v20[2] * v10[0] - v20[0] * v10[2];
+       normal3f[2] = v20[0] * v10[1] - v20[1] * v10[0];
        // 12 multiply, 10 subtract
        tc10[1] = tc1[1] - tc0[1];
        tc20[1] = tc2[1] - tc0[1];
@@ -592,6 +593,8 @@ void Mod_BuildTextureVectorsAndNormals(int firstvertex, int numvertices, int num
 {
        int i, tnum;
        float sdir[3], tdir[3], normal[3], *v;
+       const float *v0, *v1, *v2, *tc0, *tc1, *tc2;
+       float f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];
        const int *e;
        // clear the vectors
        if (svector3f)
@@ -603,7 +606,59 @@ void Mod_BuildTextureVectorsAndNormals(int firstvertex, int numvertices, int num
        // process each vertex of each triangle and accumulate the results
        for (tnum = 0, e = elements;tnum < numtriangles;tnum++, e += 3)
        {
-               Mod_BuildBumpVectors(vertex3f + e[0] * 3, vertex3f + e[1] * 3, vertex3f + e[2] * 3, texcoord2f + e[0] * 2, texcoord2f + e[1] * 2, texcoord2f + e[2] * 2, sdir, tdir, normal);
+               v0 = vertex3f + e[0] * 3;
+               v1 = vertex3f + e[1] * 3;
+               v2 = vertex3f + e[2] * 3;
+               tc0 = texcoord2f + e[0] * 2;
+               tc1 = texcoord2f + e[1] * 2;
+               tc2 = texcoord2f + e[2] * 2;
+
+               // 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles
+               // 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates
+
+               // calculate the edge directions and surface normal
+               // 6 multiply, 9 subtract
+               VectorSubtract(v1, v0, v10);
+               VectorSubtract(v2, v0, v20);
+               normal[0] = v20[1] * v10[2] - v20[2] * v10[1];
+               normal[1] = v20[2] * v10[0] - v20[0] * v10[2];
+               normal[2] = v20[0] * v10[1] - v20[1] * v10[0];
+
+               // calculate the tangents
+               // 12 multiply, 10 subtract
+               tc10[1] = tc1[1] - tc0[1];
+               tc20[1] = tc2[1] - tc0[1];
+               sdir[0] = tc10[1] * v20[0] - tc20[1] * v10[0];
+               sdir[1] = tc10[1] * v20[1] - tc20[1] * v10[1];
+               sdir[2] = tc10[1] * v20[2] - tc20[1] * v10[2];
+               tc10[0] = tc1[0] - tc0[0];
+               tc20[0] = tc2[0] - tc0[0];
+               tdir[0] = tc10[0] * v20[0] - tc20[0] * v10[0];
+               tdir[1] = tc10[0] * v20[1] - tc20[0] * v10[1];
+               tdir[2] = tc10[0] * v20[2] - tc20[0] * v10[2];
+
+               // make the tangents completely perpendicular to the surface normal
+               // 12 multiply, 4 add, 6 subtract
+               f = DotProduct(sdir, normal);
+               sdir[0] -= f * normal[0];
+               sdir[1] -= f * normal[1];
+               sdir[2] -= f * normal[2];
+               f = DotProduct(tdir, normal);
+               tdir[0] -= f * normal[0];
+               tdir[1] -= f * normal[1];
+               tdir[2] -= f * normal[2];
+
+               // if texture is mapped the wrong way (counterclockwise), the tangents
+               // have to be flipped, this is detected by calculating a normal from the
+               // two tangents, and seeing if it is opposite the surface normal
+               // 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates
+               CrossProduct(tdir, sdir, tangentcross);
+               if (DotProduct(tangentcross, normal) < 0)
+               {
+                       VectorNegate(sdir, sdir);
+                       VectorNegate(tdir, tdir);
+               }
+
                if (!areaweighting)
                {
                        VectorNormalize(sdir);
@@ -639,11 +694,11 @@ void Mod_BuildTextureVectorsAndNormals(int firstvertex, int numvertices, int num
 surfmesh_t *Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriangles, qboolean lightmapoffsets, qboolean vertexcolors, qboolean neighbors)
 {
        surfmesh_t *mesh;
-       qbyte *data;
+       unsigned char *data;
        mesh = (surfmesh_t *)Mem_Alloc(mempool, sizeof(surfmesh_t) + 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));
        mesh->num_vertices = numvertices;
        mesh->num_triangles = numtriangles;
-       data = (qbyte *)(mesh + 1);
+       data = (unsigned char *)(mesh + 1);
        if (mesh->num_vertices)
        {
                mesh->data_vertex3f = (float *)data, data += sizeof(float[3]) * mesh->num_vertices;
@@ -669,7 +724,7 @@ surfmesh_t *Mod_AllocSurfMesh(mempool_t *mempool, int numvertices, int numtriang
 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)
 {
        shadowmesh_t *newmesh;
-       qbyte *data;
+       unsigned char *data;
        int size;
        size = sizeof(shadowmesh_t);
        size += maxverts * sizeof(float[3]);
@@ -680,7 +735,7 @@ shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtria
                size += maxtriangles * sizeof(int[3]);
        if (expandable)
                size += SHADOWMESHVERTEXHASH * sizeof(shadowmeshvertexhash_t *) + maxverts * sizeof(shadowmeshvertexhash_t);
-       data = (qbyte *)Mem_Alloc(mempool, size);
+       data = (unsigned char *)Mem_Alloc(mempool, size);
        newmesh = (shadowmesh_t *)data;data += sizeof(*newmesh);
        newmesh->map_diffuse = map_diffuse;
        newmesh->map_specular = map_specular;
@@ -908,19 +963,21 @@ void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
        }
 }
 
-static rtexture_t *GL_TextureForSkinLayer(const qbyte *in, int width, int height, const char *name, const unsigned int *palette, int textureflags)
+static rtexture_t *GL_TextureForSkinLayer(const unsigned char *in, int width, int height, const char *name, const unsigned int *palette, int textureflags)
 {
        int i;
        for (i = 0;i < width*height;i++)
-               if (((qbyte *)&palette[in[i]])[3] > 0)
+               if (((unsigned char *)&palette[in[i]])[3] > 0)
                        return R_LoadTexture2D (loadmodel->texturepool, name, width, height, in, TEXTYPE_PALETTE, textureflags, palette);
        return NULL;
 }
 
-int Mod_LoadSkinFrame(skinframe_t *skinframe, char *basename, int textureflags, int loadpantsandshirt, int loadglowtexture)
+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);
@@ -947,50 +1004,90 @@ int Mod_LoadSkinFrame(skinframe_t *skinframe, char *basename, int textureflags,
        return true;
 }
 
-int Mod_LoadSkinFrame_Internal(skinframe_t *skinframe, char *basename, int textureflags, int loadpantsandshirt, int loadglowtexture, qbyte *skindata, int width, int height)
+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)
 {
-       qbyte *temp1, *temp2;
+       int i;
+       unsigned char *temp1, *temp2;
        memset(skinframe, 0, sizeof(*skinframe));
+       if (cls.state == ca_dedicated)
+               return false;
        if (!skindata)
                return false;
-       if (r_shadow_bumpscale_basetexture.value > 0)
+       if (bitsperpixel == 32)
        {
-               temp1 = (qbyte *)Mem_Alloc(loadmodel->mempool, width * height * 8);
-               temp2 = temp1 + width * height * 4;
-               Image_Copy8bitRGBA(skindata, temp1, width * height, palette_nofullbrights);
-               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, NULL);
-               Mem_Free(temp1);
-       }
-       if (loadglowtexture)
-       {
-               skinframe->glow = GL_TextureForSkinLayer(skindata, width, height, va("%s_glow", basename), palette_onlyfullbrights, textureflags); // glow
-               skinframe->base = skinframe->merged = GL_TextureForSkinLayer(skindata, width, height, va("%s_merged", basename), palette_nofullbrights, textureflags); // all but fullbrights
-               if (loadpantsandshirt)
+               if (r_shadow_bumpscale_basetexture.value > 0)
                {
-                       skinframe->pants = GL_TextureForSkinLayer(skindata, width, height, va("%s_pants", basename), palette_pantsaswhite, textureflags); // pants
-                       skinframe->shirt = GL_TextureForSkinLayer(skindata, width, height, va("%s_shirt", basename), palette_shirtaswhite, textureflags); // shirt
-                       if (skinframe->pants || skinframe->shirt)
-                               skinframe->base = GL_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", basename), palette_nocolormapnofullbrights, textureflags); // no special colors
+                       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 = 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
+       else if (bitsperpixel == 8)
        {
-               skinframe->base = skinframe->merged = GL_TextureForSkinLayer(skindata, width, height, va("%s_merged", basename), palette_complete, textureflags); // all
-               if (loadpantsandshirt)
+               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); // all
+               if (!palette && loadglowtexture)
+                       skinframe->glow = GL_TextureForSkinLayer(skindata, width, height, va("%s_glow", basename), palette_onlyfullbrights, textureflags); // glow
+               if (!palette && loadpantsandshirt)
                {
                        skinframe->pants = GL_TextureForSkinLayer(skindata, width, height, va("%s_pants", basename), palette_pantsaswhite, textureflags); // pants
                        skinframe->shirt = GL_TextureForSkinLayer(skindata, width, height, va("%s_shirt", basename), palette_shirtaswhite, textureflags); // shirt
-                       if (skinframe->pants || skinframe->shirt)
-                               skinframe->base = GL_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", basename), palette_nocolormap, textureflags); // no pants or shirt
+               }
+               if (skinframe->pants || skinframe->shirt)
+                       skinframe->base = GL_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", basename),loadglowtexture ? palette_nocolormapnofullbrights : palette_nocolormap, textureflags); // 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); // fog mask
                }
        }
+       else
+               return false;
        if (!skinframe->nmap)
                skinframe->nmap = r_texture_blanknormalmap;
        return true;
 }
 
-void Mod_GetTerrainVertex3fTexCoord2fFromRGBA(const qbyte *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
+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];
        v[0] = ix;
@@ -1005,7 +1102,7 @@ void Mod_GetTerrainVertex3fTexCoord2fFromRGBA(const qbyte *imagepixels, int imag
        texcoord2f[1] = tc[1];
 }
 
-void Mod_GetTerrainVertexFromRGBA(const qbyte *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)
+void Mod_GetTerrainVertexFromRGBA(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)
 {
        float vup[3], vdown[3], vleft[3], vright[3];
        float tcup[3], tcdown[3], tcleft[3], tcright[3];
@@ -1030,7 +1127,7 @@ void Mod_GetTerrainVertexFromRGBA(const qbyte *imagepixels, int imagewidth, int
        VectorAdd(normal3f, nl, normal3f);
 }
 
-void Mod_ConstructTerrainPatchFromRGBA(const qbyte *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)
+void Mod_ConstructTerrainPatchFromRGBA(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)
 {
        int x, y, ix, iy, *e;
        e = element3i;
@@ -1081,7 +1178,7 @@ tag_torso,
 */
        memset(tagsets, 0, sizeof(tagsets));
        memset(word, 0, sizeof(word));
-       for (i = 0;i < MAX_SKINS && (data = text = (char *)FS_LoadFile(va("%s_%i.skin", loadmodel->name, i), tempmempool, true));i++)
+       for (i = 0;i < MAX_SKINS && (data = text = (char *)FS_LoadFile(va("%s_%i.skin", loadmodel->name, i), tempmempool, true, NULL));i++)
        {
                numtags = 0;