]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_shared.c
ODE physics engine support, needs more work, disabled by default
[xonotic/darkplaces.git] / model_shared.c
index 3e48438f9fff9346a69f47c72419e6720a14fd54..9389654b14823a587bdfddc24b69650f8f200d55 100644 (file)
@@ -54,7 +54,7 @@ static void mod_start(void)
        int nummodels = Mem_ExpandableArray_IndexRange(&models);
        dp_model_t *mod;
 
-       SCR_PushLoadingScreen("Loading models", 1.0);
+       SCR_PushLoadingScreen(false, "Loading models", 1.0);
        count = 0;
        for (i = 0;i < nummodels;i++)
                if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
@@ -64,11 +64,11 @@ static void mod_start(void)
                if ((mod = (dp_model_t*) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*')
                        if (mod->used)
                        {
-                               SCR_PushLoadingScreen(mod->name, 1.0 / count);
+                               SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
                                Mod_LoadModel(mod, true, false);
-                               SCR_PopLoadingScreen();
+                               SCR_PopLoadingScreen(false);
                        }
-       SCR_PopLoadingScreen();
+       SCR_PopLoadingScreen(false);
 }
 
 static void mod_shutdown(void)
@@ -192,6 +192,101 @@ void R_Model_Null_Draw(entity_render_t *ent)
        return;
 }
 
+
+typedef void (*mod_framegroupify_parsegroups_t) (unsigned int i, int start, int len, float fps, qboolean loop, void *pass);
+
+int Mod_FrameGroupify_ParseGroups(const char *buf, mod_framegroupify_parsegroups_t cb, void *pass)
+{
+       const char *bufptr;
+       int start, len;
+       float fps;
+       unsigned int i;
+       qboolean loop;
+
+       bufptr = buf;
+       i = 0;
+       for(;;)
+       {
+               // an anim scene!
+               if (!COM_ParseToken_Simple(&bufptr, true, false))
+                       break;
+               if (!strcmp(com_token, "\n"))
+                       continue; // empty line
+               start = atoi(com_token);
+               if (!COM_ParseToken_Simple(&bufptr, true, false))
+                       break;
+               if (!strcmp(com_token, "\n"))
+               {
+                       Con_Printf("framegroups file: missing number of frames\n");
+                       continue;
+               }
+               len = atoi(com_token);
+               if (!COM_ParseToken_Simple(&bufptr, true, false))
+                       break;
+               // we default to looping as it's usually wanted, so to NOT loop you append a 0
+               if (strcmp(com_token, "\n"))
+               {
+                       fps = atof(com_token);
+                       if (!COM_ParseToken_Simple(&bufptr, true, false))
+                               break;
+                       if (strcmp(com_token, "\n"))
+                               loop = atoi(com_token) != 0;
+                       else
+                               loop = true;
+               }
+               else
+               {
+                       fps = 20;
+                       loop = true;
+               }
+
+               if(cb)
+                       cb(i, start, len, fps, loop, pass);
+               ++i;
+       }
+
+       return i;
+}
+
+void Mod_FrameGroupify_ParseGroups_Count (unsigned int i, int start, int len, float fps, qboolean loop, void *pass)
+{
+       unsigned int *cnt = (unsigned int *) pass;
+       ++*cnt;
+}
+
+void Mod_FrameGroupify_ParseGroups_Store (unsigned int i, int start, int len, float fps, qboolean loop, void *pass)
+{
+       dp_model_t *mod = (dp_model_t *) pass;
+       animscene_t *anim = &mod->animscenes[i];
+       dpsnprintf(anim->name, sizeof(anim[i].name), "groupified_%d", i);
+       anim->firstframe = bound(0, start, mod->num_poses - 1);
+       anim->framecount = bound(1, len, mod->num_poses - anim->firstframe);
+       anim->framerate = max(1, fps);
+       anim->loop = !!loop;
+       //Con_Printf("frame group %d is %d %d %f %d\n", i, start, len, fps, loop);
+}
+
+void Mod_FrameGroupify(dp_model_t *mod, const char *buf)
+{
+       unsigned int cnt;
+
+       // 0. count
+       cnt = Mod_FrameGroupify_ParseGroups(buf, NULL, NULL);
+       if(!cnt)
+       {
+               Con_Printf("no scene found in framegroups file, aborting\n");
+               return;
+       }
+       mod->numframes = cnt;
+
+       // 1. reallocate
+       // (we do not free the previous animscenes, but model unloading will free the pool owning them, so it's okay)
+       mod->animscenes = (animscene_t *) Mem_Alloc(mod->mempool, sizeof(animscene_t) * mod->numframes);
+
+       // 2. parse
+       Mod_FrameGroupify_ParseGroups(buf, Mod_FrameGroupify_ParseGroups_Store, mod);
+}
+
 /*
 ==================
 Mod_LoadModel
@@ -223,7 +318,7 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
                        Con_Printf("loading model %s\n", mod->name);
 
                mod->used = true;
-               mod->crc = -1;
+               mod->crc = (unsigned int)-1;
                mod->loaded = false;
 
                VectorClear(mod->normalmins);
@@ -276,7 +371,7 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
        if (developer_loading.integer)
                Con_Printf("loading model %s\n", mod->name);
        
-       SCR_PushLoadingScreen(mod->name, 1);
+       SCR_PushLoadingScreen(true, mod->name, 1);
 
        // LordHavoc: unload the existing model in this slot (if there is one)
        if (mod->loaded || mod->mempool)
@@ -328,10 +423,14 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
                else Con_Printf("Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
                Mem_Free(buf);
 
-               Mod_BuildVBOs();
+               buf = FS_LoadFile (va("%s.framegroups", mod->name), tempmempool, false, &filesize);
+               if(buf)
+               {
+                       Mod_FrameGroupify(mod, (const char *)buf);
+                       Mem_Free(buf);
+               }
 
-               // no fatal errors occurred, so this model is ready to use.
-               mod->loaded = true;
+               Mod_BuildVBOs();
        }
        else if (crash)
        {
@@ -339,7 +438,10 @@ dp_model_t *Mod_LoadModel(dp_model_t *mod, qboolean crash, qboolean checkdisk)
                Con_Printf ("Mod_LoadModel: %s not found\n", mod->name);
        }
 
-       SCR_PopLoadingScreen();
+       // no fatal errors occurred, so this model is ready to use.
+       mod->loaded = true;
+
+       SCR_PopLoadingScreen(false);
 
        return mod;
 }
@@ -443,7 +545,7 @@ void Mod_Reload(void)
        int nummodels = Mem_ExpandableArray_IndexRange(&models);
        dp_model_t *mod;
 
-       SCR_PushLoadingScreen("Reloading models", 1.0);
+       SCR_PushLoadingScreen(false, "Reloading models", 1.0);
        count = 0;
        for (i = 0;i < nummodels;i++)
                if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
@@ -451,11 +553,11 @@ void Mod_Reload(void)
        for (i = 0;i < nummodels;i++)
                if ((mod = (dp_model_t *) Mem_ExpandableArray_RecordAtIndex(&models, i)) && mod->name[0] && mod->name[0] != '*' && mod->used)
                {
-                       SCR_PushLoadingScreen(mod->name, 1.0 / count);
+                       SCR_PushLoadingScreen(true, mod->name, 1.0 / count);
                        Mod_LoadModel(mod, true, true);
-                       SCR_PopLoadingScreen();
+                       SCR_PopLoadingScreen(false);
                }
-       SCR_PopLoadingScreen();
+       SCR_PopLoadingScreen(false);
 }
 
 unsigned char *mod_base;
@@ -863,6 +965,8 @@ shadowmesh_t *Mod_ShadowMesh_Alloc(mempool_t *mempool, int maxverts, int maxtria
        newmesh->maxtriangles = maxtriangles;
        newmesh->numverts = 0;
        newmesh->numtriangles = 0;
+       memset(newmesh->sideoffsets, 0, sizeof(newmesh->sideoffsets));
+       memset(newmesh->sidetotals, 0, sizeof(newmesh->sidetotals));
 
        newmesh->vertex3f = (float *)data;data += maxverts * sizeof(float[3]);
        if (light)
@@ -893,6 +997,8 @@ shadowmesh_t *Mod_ShadowMesh_ReAlloc(mempool_t *mempool, shadowmesh_t *oldmesh,
        newmesh = Mod_ShadowMesh_Alloc(mempool, oldmesh->numverts, oldmesh->numtriangles, oldmesh->map_diffuse, oldmesh->map_specular, oldmesh->map_normal, light, neighbors, false);
        newmesh->numverts = oldmesh->numverts;
        newmesh->numtriangles = oldmesh->numtriangles;
+       memcpy(newmesh->sideoffsets, oldmesh->sideoffsets, sizeof(oldmesh->sideoffsets));
+       memcpy(newmesh->sidetotals, oldmesh->sidetotals, sizeof(oldmesh->sidetotals));
 
        memcpy(newmesh->vertex3f, oldmesh->vertex3f, oldmesh->numverts * sizeof(float[3]));
        if (newmesh->svector3f && oldmesh->svector3f)
@@ -1148,6 +1254,34 @@ void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
        }
 }
 
+void Mod_CreateCollisionMesh(dp_model_t *mod)
+{
+       int k;
+       int numcollisionmeshtriangles;
+       const msurface_t *surface;
+       mempool_t *mempool = mod->mempool;
+       if (!mempool && mod->brush.parentmodel)
+               mempool = mod->brush.parentmodel->mempool;
+       // make a single combined collision mesh for physics engine use
+       numcollisionmeshtriangles = 0;
+       for (k = 0;k < mod->nummodelsurfaces;k++)
+       {
+               surface = mod->data_surfaces + mod->firstmodelsurface + k;
+               if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
+                       continue;
+               numcollisionmeshtriangles += surface->num_triangles;
+       }
+       mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles, NULL, NULL, NULL, false, false, true);
+       for (k = 0;k < mod->nummodelsurfaces;k++)
+       {
+               surface = mod->data_surfaces + mod->firstmodelsurface + k;
+               if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID))
+                       continue;
+               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));
+       }
+       mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mempool, mod->brush.collisionmesh, false, true, false);
+}
+
 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)
 {
        float v[3], tc[3];
@@ -1237,7 +1371,14 @@ static void Q3Shader_AddToHash (q3shaderinfo_t* shader)
        {
                if (strcasecmp (entry->shader.name, shader->name) == 0)
                {
-                       Con_Printf("Shader '%s' already defined\n", shader->name);
+                       unsigned char *start, *end, *start2;
+                       start = (unsigned char *) (&shader->Q3SHADERINFO_COMPARE_START);
+                       end = ((unsigned char *) (&shader->Q3SHADERINFO_COMPARE_END)) + sizeof(shader->Q3SHADERINFO_COMPARE_END);
+                       start2 = (unsigned char *) (&entry->shader.Q3SHADERINFO_COMPARE_START);
+                       if(memcmp(start, start2, end - start))
+                               Con_Printf("Shader '%s' already defined, ignoring mismatching redeclaration\n", shader->name);
+                       else
+                               Con_DPrintf("Shader '%s' already defined\n", shader->name);
                        return;
                }
                lastEntry = entry;
@@ -1303,6 +1444,8 @@ void Mod_LoadQ3Shaders(void)
                        shader.reflectfactor = 1;
                        Vector4Set(shader.reflectcolor4f, 1, 1, 1, 1);
                        shader.r_water_wateralpha = 1;
+                       shader.specularscalemod = 1;
+                       shader.specularpowermod = 1;
 
                        strlcpy(shader.name, com_token, sizeof(shader.name));
                        if (!COM_ParseToken_QuakeC(&text, false) || strcasecmp(com_token, "{"))
@@ -1349,8 +1492,8 @@ void Mod_LoadQ3Shaders(void)
                                                        if (!COM_ParseToken_QuakeC(&text, true))
                                                                break;
                                                }
-                                               for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
-                                                       parameter[j][0] = 0;
+                                               //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
+                                               //      parameter[j][0] = 0;
                                                if (developer.integer >= 100)
                                                {
                                                        Con_Printf("%s %i: ", shader.name, shader.numlayers - 1);
@@ -1568,8 +1711,8 @@ void Mod_LoadQ3Shaders(void)
                                        if (!COM_ParseToken_QuakeC(&text, true))
                                                break;
                                }
-                               for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
-                                       parameter[j][0] = 0;
+                               //for (j = numparameters;j < TEXTURE_MAXFRAMES + 4;j++)
+                               //      parameter[j][0] = 0;
                                if (fileindex == 0 && !strcasecmp(com_token, "}"))
                                        break;
                                if (developer.integer >= 100)
@@ -1701,6 +1844,14 @@ void Mod_LoadQ3Shaders(void)
                                        Vector4Set(shader.reflectcolor4f, atof(parameter[8]), atof(parameter[9]), atof(parameter[10]), 1);
                                        shader.r_water_wateralpha = atof(parameter[11]);
                                }
+                               else if (!strcasecmp(parameter[0], "dp_glossintensitymod") && numparameters >= 2)
+                               {
+                                       shader.specularscalemod = atof(parameter[1]);
+                               }
+                               else if (!strcasecmp(parameter[0], "dp_glossexponentmod") && numparameters >= 2)
+                               {
+                                       shader.specularpowermod = atof(parameter[1]);
+                               }
                                else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
                                {
                                        int i, deformindex;
@@ -1773,6 +1924,7 @@ void Mod_LoadQ3Shaders(void)
                }
                Mem_Free(f);
        }
+       FS_FreeSearch(search);
 }
 
 q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
@@ -1795,24 +1947,30 @@ q3shaderinfo_t *Mod_LookupQ3Shader(const char *name)
 qboolean Mod_LoadTextureFromQ3Shader(texture_t *texture, const char *name, qboolean warnmissing, qboolean fallback, int defaulttexflags)
 {
        int j;
+       int texflagsmask;
        qboolean success = true;
        q3shaderinfo_t *shader;
        if (!name)
                name = "";
        strlcpy(texture->name, name, sizeof(texture->name));
        shader = name[0] ? Mod_LookupQ3Shader(name) : NULL;
+
+       texflagsmask = ~0;
+       if(!(defaulttexflags & TEXF_PICMIP))
+               texflagsmask &= ~TEXF_PICMIP;
+       if(!(defaulttexflags & TEXF_COMPRESS))
+               texflagsmask &= ~TEXF_COMPRESS;
+       texture->specularscalemod = 1; // unless later loaded from the shader
+       texture->specularpowermod = 1; // unless later loaded from the shader
+
        if (shader)
        {
                if (developer_loading.integer)
                        Con_Printf("%s: loaded shader for %s\n", loadmodel->name, name);
                texture->surfaceparms = shader->surfaceparms;
-               texture->textureflags = shader->textureflags;
 
                // allow disabling of picmip or compression by defaulttexflags
-               if(!(defaulttexflags & TEXF_PICMIP))
-                       texture->textureflags &= ~TEXF_PICMIP;
-               if(!(defaulttexflags & TEXF_COMPRESS))
-                       texture->textureflags &= ~TEXF_COMPRESS;
+               texture->textureflags = shader->textureflags & texflagsmask;
 
                if (shader->surfaceparms & Q3SURFACEPARM_SKY)
                {
@@ -1898,7 +2056,7 @@ nothing                GL_ZERO GL_ONE
                                {
                                        texture->skinframes[j] = NULL;
                                }
-                               else if (!(texture->skinframes[j] = R_SkinFrame_LoadExternal(primarylayer->texturename[j], primarylayer->texflags, false)))
+                               else if (!(texture->skinframes[j] = R_SkinFrame_LoadExternal(primarylayer->texturename[j], primarylayer->texflags & texflagsmask, false)))
                                {
                                        Con_Printf("^1%s:^7 could not load texture ^3\"%s\"^7 (frame %i) for shader ^2\"%s\"\n", loadmodel->name, primarylayer->texturename[j], j, texture->name);
                                        texture->skinframes[j] = R_SkinFrame_LoadMissing();
@@ -1908,6 +2066,9 @@ nothing                GL_ZERO GL_ONE
                if (shader->backgroundlayer >= 0)
                {
                        q3shaderinfo_layer_t* backgroundlayer = shader->layers + shader->backgroundlayer;
+                       // copy over one secondarylayer parameter
+                       memcpy(texture->backgroundtcmods, backgroundlayer->tcmods, sizeof(texture->backgroundtcmods));
+                       // load the textures
                        texture->backgroundnumskinframes = backgroundlayer->numframes;
                        texture->backgroundskinframerate = backgroundlayer->framerate;
                        for (j = 0;j < backgroundlayer->numframes;j++)
@@ -1916,7 +2077,7 @@ nothing                GL_ZERO GL_ONE
                                {
                                        texture->skinframes[j] = NULL;
                                }
-                               else if (!(texture->backgroundskinframes[j] = R_SkinFrame_LoadExternal(backgroundlayer->texturename[j], backgroundlayer->texflags, false)))
+                               else if (!(texture->backgroundskinframes[j] = R_SkinFrame_LoadExternal(backgroundlayer->texturename[j], backgroundlayer->texflags & texflagsmask, false)))
                                {
                                        Con_Printf("^1%s:^7 could not load texture ^3\"%s\"^7 (background frame %i) for shader ^2\"%s\"\n", loadmodel->name, backgroundlayer->texturename[j], j, texture->name);
                                        texture->backgroundskinframes[j] = R_SkinFrame_LoadMissing();
@@ -1935,6 +2096,8 @@ nothing                GL_ZERO GL_ONE
                texture->reflectfactor = shader->reflectfactor;
                Vector4Copy(shader->reflectcolor4f, texture->reflectcolor4f);
                texture->r_water_wateralpha = shader->r_water_wateralpha;
+               texture->specularscalemod = shader->specularscalemod;
+               texture->specularpowermod = shader->specularpowermod;
        }
        else if (!strcmp(texture->name, "noshader") || !texture->name[0])
        {
@@ -2183,9 +2346,9 @@ void Mod_MakeSortedSurfaces(dp_model_t *mod)
        int *firstsurfacefortexture;
        int *numsurfacesfortexture;
        if (!mod->sortedmodelsurfaces)
-               mod->sortedmodelsurfaces = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
-       firstsurfacefortexture = Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
-       numsurfacesfortexture = Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
+               mod->sortedmodelsurfaces = (int *) Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
+       firstsurfacefortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*firstsurfacefortexture));
+       numsurfacesfortexture = (int *) Mem_Alloc(tempmempool, mod->num_textures * sizeof(*numsurfacesfortexture));
        memset(numsurfacesfortexture, 0, mod->num_textures * sizeof(*numsurfacesfortexture));
        for (j = 0;j < mod->nummodelsurfaces;j++)
        {
@@ -2211,6 +2374,19 @@ void Mod_MakeSortedSurfaces(dp_model_t *mod)
 
 static void Mod_BuildVBOs(void)
 {
+       if (developer.integer && loadmodel->surfmesh.data_element3s && loadmodel->surfmesh.data_element3i)
+       {
+               int i;
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+               {
+                       if (loadmodel->surfmesh.data_element3s[i] != loadmodel->surfmesh.data_element3i[i])
+                       {
+                               Con_Printf("Mod_BuildVBOs: element %u is incorrect (%u should be %u)\n", i, loadmodel->surfmesh.data_element3s[i], loadmodel->surfmesh.data_element3i[i]);
+                               loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+                       }
+               }
+       }
+
        if (!gl_support_arb_vertex_buffer_object)
                return;
 
@@ -2218,12 +2394,7 @@ static void Mod_BuildVBOs(void)
        if (loadmodel->surfmesh.num_triangles)
        {
                if (loadmodel->surfmesh.data_element3s)
-               {
-                       int i;
-                       for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
-                               loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
                        loadmodel->surfmesh.ebo3s = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3s, loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]), loadmodel->name);
-               }
                else
                        loadmodel->surfmesh.ebo3i = R_Mesh_CreateStaticBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles * sizeof(unsigned int[3]), loadmodel->name);
        }
@@ -2619,7 +2790,7 @@ static void Mod_Decompile_f(void)
                                        if ((animname[l] < '0' || animname[l] > '9') && animname[l] != '_')
                                                k = l + 1;
                                animname[k] = 0;
-                               count = (mod->num_poses / mod->num_bones) - first;
+                               count = mod->num_poses - first;
                                for (j = i + 1;j < mod->numframes;j++)
                                {
                                        strlcpy(animname2, mod->animscenes[j].name, sizeof(animname2));