diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 4deaf8fa2..3f797f565 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -3377,8 +3377,8 @@ typedef struct int boneidx; int bonebias; //shift the bones menu down to ensure the boneidx stays visible int textype; - double framechangetime; - double skinchangetime; + double frametime; + double skintime; float pitch; float yaw; @@ -3394,6 +3394,7 @@ typedef struct char shaderfile[MAX_QPATH]; char *shadertext; + qboolean paused; #ifdef RAGDOLL lerpents_t ragent; world_t ragworld; @@ -3523,7 +3524,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu r_refdef.grect.height = vid.height; r_refdef.grect.x = 0; r_refdef.grect.y = 0; - r_refdef.time = realtime; + r_refdef.time = mods->skintime; r_refdef.flags = RDF_NOWORLDMODEL; @@ -3620,7 +3621,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu ent.shaderTime = 0;//realtime; ent.framestate.g[FS_REG].lerpweight[0] = 1; ent.framestate.g[FS_REG].frame[0] = mods->framegroup; - ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime; + ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = mods->frametime; ent.framestate.g[FS_REG].endbone = 0x7fffffff; if (*mods->skinname) { @@ -3651,6 +3652,12 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu #endif V_ApplyRefdef(); + + if (!mods->paused) + { + mods->frametime += host_frametime; + mods->skintime += host_frametime; + } /* { trace_t tr; @@ -3707,9 +3714,8 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu ent.framestate.g[FS_REG].frame[0] |= 0x8000; if (ent.model->dollinfo && mods->ragworld.rbe) { - float rate = 1.0/60; + float rate = 1.0/60; //try a fixed tick rate... rag_doallanimations(&mods->ragworld); - mods->fixedrate += host_frametime; if (mods->fixedrate > 1) mods->fixedrate = 1; while (mods->fixedrate >= rate) @@ -3717,6 +3723,8 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu mods->ragworld.rbe->RunFrame(&mods->ragworld, rate, 800); mods->fixedrate -= rate; } + if (!mods->paused) + mods->fixedrate += host_frametime; rag_updatedeltaent(&mods->ragworld, &ent, &mods->ragent); } @@ -4250,10 +4258,18 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct emenu_s *m, int case MV_NORMALS: mods->mode = MV_NONE; break; } } + else if (key >= '1' && key <= '7') + { + Z_Free(mods->shadertext); + mods->shadertext = NULL; + mods->mode = MV_NONE + (key - '1'); + } + else if (key == 'p') + mods->paused = !mods->paused; else if (key == 'r') { - mods->framechangetime = realtime; - mods->skinchangetime = realtime; + mods->frametime = 0; + mods->skintime = 0; } #ifdef RAGDOLL else if (key == 'f') @@ -4290,28 +4306,28 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct emenu_s *m, int else if (key == K_END) { mods->skingroup = max(0, mods->skingroup-1); - mods->skinchangetime = realtime; + mods->skintime = 0; Z_Free(mods->shadertext); mods->shadertext = NULL; } else if (key == K_HOME) { mods->skingroup += 1; - mods->skinchangetime = realtime; + mods->skintime = 0; Z_Free(mods->shadertext); mods->shadertext = NULL; } else if (key == K_PGDN) { mods->framegroup = max(0, mods->framegroup-1); - mods->framechangetime = realtime; + mods->frametime = 0; Z_Free(mods->shadertext); mods->shadertext = NULL; } else if (key == K_PGUP) { mods->framegroup += 1; - mods->framechangetime = realtime; + mods->frametime = 0; } else if (key == K_DEL) { @@ -4393,8 +4409,8 @@ void M_Menu_ModelViewer_f(void) Q_strncpyz(mv->skinname, Cmd_Argv(2), sizeof(mv->skinname)); Q_strncpyz(mv->animname, Cmd_Argv(3), sizeof(mv->animname)); - mv->framechangetime = realtime; - mv->skinchangetime = realtime; + mv->frametime = 0; + mv->skintime = 0; #ifdef RAGDOLL menu->menu.videoreset = M_Modelviewer_Reset; menu->remove = M_Modelviewer_Shutdown; diff --git a/engine/client/pr_skelobj.c b/engine/client/pr_skelobj.c index c446902a8..a897b993d 100644 --- a/engine/client/pr_skelobj.c +++ b/engine/client/pr_skelobj.c @@ -52,6 +52,8 @@ typedef struct doll_s struct doll_s *next; qboolean drawn:1; + int refanim; //-1 for skining pose. otherwise the first pose of the specified anim. probaly 0. + float refanimtime; //usually just 0 int numdefaultanimated; int numbodies; int numjoints; @@ -349,6 +351,7 @@ static dollcreatectx_t *rag_createdoll(model_t *mod, const char *fname, int numb ctx->d->next = dolllist; ctx->d->name = strdup(fname); ctx->d->model = mod; + ctx->d->refanim = -1; //skin pose. ctx->d->numbodies = 0; ctx->d->body = NULL; ctx->d->numjoints = 0; @@ -377,6 +380,13 @@ static qboolean rag_dollline(dollcreatectx_t *ctx, int linenum) if (!argc) { } + else if (argc == 2 && !stricmp(cmd, "refpose") && !strcmp(val, "skin")) + ctx->d->refanim = -1; + else if (argc == 3 && !stricmp(cmd, "refpose")) + { + ctx->d->refanim = atoi(val); + ctx->d->refanimtime = atoi(Cmd_Argv(2)); + } //create a new body else if (argc == 3 && !stricmp(cmd, "body")) { @@ -911,7 +921,13 @@ void skel_generateragdoll_f(void) if (i == 0) VFS_PUTS(f, "//NO FRAME INFO\n"); - //print background frame info. + VFS_PUTS(f, "\n//reference pose that offsets are defined in terms of\n"); + if (mod->type == mod_halflife) + VFS_PUTS(f, "refpose 0 0.0 //use first anim's first pose\n"); + else + VFS_PUTS(f, "refpose skin\n"); + + //print background frame info. VFS_PUTS(f, "\n//skins are as follows:\n"); for (i = 0; i < 32768; i++) { @@ -1248,11 +1264,34 @@ static qboolean rag_instanciate(skelobject_t *sko, doll_t *doll, float *emat, we int bone; rbebody_t *body1, *body2; rbejointinfo_t *j; + float *absolutes; sko->numbodies = doll->numbodies; sko->body = BZ_Malloc(sizeof(*sko->body) * sko->numbodies); sko->doll = doll; doll->uses++; sko->numanimated = 0; + + if (bones && doll->refanim) + { + framestate_t fstate = {0}; + float *relatives = alloca(sizeof(float)*12*numbones*2); + fstate.g[FS_REG].frame[0] = doll->refanim; //which anim we're using as the reference + fstate.g[FS_REG].frametime[0] = doll->refanimtime; //first pose of the anim + fstate.g[FS_REG].lerpweight[0] = 1; + fstate.g[FS_REG].endbone = numbones; + + absolutes = relatives+numbones*12; + numbones = Mod_GetBoneRelations(sko->model, 0, numbones, bones, &fstate, relatives); + for (i = 0; i < numbones; i++) + { //compute the absolutes. not gonna make a bg3 reference here. + if (bones[i].parent>=0) + R_ConcatTransforms((void*)(absolutes+12*bones[i].parent), (void*)(relatives+12*i), (void*)(absolutes+12*i)); + else + memcpy(absolutes+12*i, relatives+12*i, sizeof(float)*12); + } + } + else absolutes = NULL; + for (i = 0; i < sko->numbodies; i++) { memset(&sko->body[i], 0, sizeof(sko->body[i])); @@ -1262,7 +1301,9 @@ static qboolean rag_instanciate(skelobject_t *sko, doll_t *doll, float *emat, we sko->numanimated++; //spawn the body in the base pose, so we can add joints etc (also ignoring the entity matrix, we'll fix all that up later). - if (1) + if (absolutes) //we have a reference pose + memcpy(bodymat, absolutes+12*doll->body[i].bone, sizeof(float)*12); + else if (1) Matrix3x4_Invert_Simple(bones[doll->body[i].bone].inverse, bodymat); else rag_genbodymatrix(sko, &doll->body[i], emat, bodymat); @@ -1281,8 +1322,10 @@ static qboolean rag_instanciate(skelobject_t *sko, doll_t *doll, float *emat, we bone = j->bonepivot; bmat = sko->bonematrix + bone*12; - if (1) - { + if (absolutes) //we have a reference pose + memcpy(worldmat, absolutes+12*doll->body[i].bone, sizeof(float)*12); + else if (1) + { //FIXME: j->offset isn't actually used?!? Matrix3x4_Invert_Simple(bones[j->bonepivot].inverse, worldmat); } else diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 20f9b8161..5816d6ef5 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -1160,6 +1160,9 @@ static int Alias_FindRawSkelData(galiasinfo_t *inf, const framestate_t *fstate, int endbone; int numbonegroups=0; + if (lastbone > inf->numbones) + lastbone = inf->numbones; + for (bonegroup = 0; bonegroup < FS_COUNT; bonegroup++) { endbone = fstate->g[bonegroup].endbone; @@ -1878,7 +1881,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in else { if (meshcache.bonecachetype != SKEL_ABSOLUTE) - meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES, NULL); + meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, inf->numbones, NULL); #ifndef SERVERONLY if (inf->shares_bones != surfnum && qrenderer) Alias_DrawSkeletalBones(inf->ofsbones, (const float *)meshcache.usebonepose, inf->numbones, e->framestate.g[0].endbone); @@ -1888,7 +1891,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in else { if (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE) - meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES, NULL); + meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, inf->numbones, NULL); //hardware bone animation mesh->xyz_array = inf->ofs_skel_xyz; @@ -2903,11 +2906,14 @@ typedef struct float fps; qboolean loop; int action; - int actionweight; + galiasevent_t *events; + float actionweight; char name[MAX_QPATH]; } frameinfo_t; -static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) +static frameinfo_t *ParseFrameInfo(model_t *mod, int *numgroups) { + const char *modelname = mod->name; + int count = 0; int maxcount = 0; char *line, *eol; @@ -2917,11 +2923,84 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) char tok[MAX_FRAMEINFO_POSES * 4]; size_t fsize; com_tokentype_t ttype; + json_t *rootjson; Q_snprintfz(fname, sizeof(fname), "%s.framegroups", modelname); line = file = FS_LoadMallocFile(fname, &fsize); if (!file) return NULL; - while(line && *line) + + rootjson = JSON_Parse(file); //must be a fully valid json file, so any space-separated tokens from the dp format will return NULL here just fine. + if (rootjson) + { + json_t *framegroups = JSON_FindChild(rootjson, "framegroups"); + maxcount = JSON_GetCount(framegroups); + frames = realloc(frames, sizeof(*frames)*maxcount); + for(count = 0; count < maxcount; count++) + { + galiasevent_t *ev, **link; + char eventdata[65536]; + unsigned int posecount; + json_t *arr, *c; + json_t *in = JSON_GetIndexed(framegroups, count); + if (!in) + break; //erk? shouldn't really happen. not an issue though. + + frames[count].firstpose = JSON_GetInteger(in, "firstpose", 0); + frames[count].posecount = JSON_GetInteger(in, "numposes", 1); + frames[count].posesarray = false; + arr = JSON_FindChild(in, "poses"); + if (arr) + { //override with explicit poses, if specified. + for (posecount = 0; posecount < countof(frames[count].poses); posecount++) + { + c = JSON_GetIndexed(arr, posecount); + if (!c) //ran out of elements... + break; + frames[count].poses[posecount] = JSON_GetUInteger(c, NULL, 0); + } + if (posecount>0) + { + frames[count].posecount = posecount; + frames[count].posesarray = true; + } + } + + frames[count].fps = JSON_GetFloat(in, "fps", 20); + if (frames[count].fps <= 0) + frames[count].fps = 10; + frames[count].loop = JSON_GetUInteger(in, "loop", false); + + Q_snprintfz(frames[count].name,sizeof(frames[count].name), "%s[%d]", fname, count); + JSON_GetString(in, "name", frames[count].name, sizeof(frames[count].name), NULL); + + frames[count].action = JSON_GetInteger(in, "action", -1); + frames[count].actionweight = JSON_GetFloat(in, "actionweight", 0); + frames[count].events = NULL; + + arr = JSON_FindChild(in, "events"); + for (posecount = 0; (c=JSON_GetIndexed(arr, posecount))!=NULL; posecount++) + { + *eventdata = 0; + JSON_GetString(c, "value", eventdata,sizeof(eventdata), NULL); + ev = ZG_Malloc(&mod->memgroup, sizeof(*ev) + strlen(eventdata)+1); + ev->code = JSON_GetInteger(c, "code", 0); + ev->timestamp = JSON_GetFloat(c, "timestamp", JSON_GetFloat(c, "pose", 0)/frames[count].fps); + ev->data = strcpy((char*)(ev+1), eventdata); + + link = &frames[count].events; + while (*link && (*link)->timestamp <= ev->timestamp) + link = &(*link)->next; + ev->next = *link; + *link = ev; + } + } + + mod->flags = JSON_GetUInteger(rootjson, "modelflags", mod->flags); + + + JSON_Destroy(rootjson); + } + else while(line && *line) { unsigned int posecount = 0; @@ -2961,6 +3040,7 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) else frames[count].loop = !!atoi(tok); + frames[count].events = NULL; frames[count].action = -1; frames[count].actionweight = 0; Q_snprintfz(frames[count].name, sizeof(frames[count].name), "groupified_%d_anim", count); //to match DP. frameforname cares. @@ -2990,6 +3070,11 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) BZ_Free(file); *numgroups = count; + if (!count) + { + free(frames); + frames = NULL; + } return frames; } @@ -3985,10 +4070,10 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model #endif static void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias) -{ +{ //use ONLY with vertex models. unsigned int numanims, a, p, g, oldnumanims = galias->numanimations, targpose; galiasanimation_t *o, *oldanims = galias->ofsanimations, *frame; - frameinfo_t *framegroups = ParseFrameInfo(mod->name, &numanims); + frameinfo_t *framegroups = ParseFrameInfo(mod, &numanims); if (framegroups) { galias->ofsanimations = o = ZG_Malloc(&mod->memgroup, sizeof(*galias->ofsanimations) * numanims); @@ -4014,8 +4099,9 @@ static void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias) o->numposes = p; o->rate = framegroups[a].fps; o->loop = framegroups[a].loop; - o->action = -1; - o->actionweight = 0; + o->events = framegroups[a].events; + o->action = framegroups[a].action; + o->actionweight = framegroups[a].actionweight; Q_strncpyz(o->name, framegroups[a].name, sizeof(o->name)); } galias->numanimations = numanims; @@ -5128,25 +5214,23 @@ int Mod_GetBoneRelations(model_t *model, int firstbone, int lastbone, const gali galiasbone_t *Mod_GetBoneInfo(model_t *model, int *numbones) { #ifdef SKELETALMODELS - galiasbone_t *bone; - galiasinfo_t *inf; - - - if (!model || model->type != mod_alias) + if (model && model->type == mod_alias) { - *numbones = 0; - return NULL; + galiasinfo_t *inf = Mod_Extradata(model); + *numbones = inf->numbones; + return inf->ofsbones; } - - inf = Mod_Extradata(model); - - bone = inf->ofsbones; - *numbones = inf->numbones; - return bone; -#else +#endif +#ifdef HALFLIFEMODELS + if (model && model->type == mod_halflife) + { + hlmodel_t *hlmod = Mod_Extradata(model); + *numbones = hlmod->header->numbones; + return hlmod->compatbones; + } +#endif *numbones = 0; return NULL; -#endif } int Mod_GetBoneParent(model_t *model, int bonenum) @@ -6014,7 +6098,7 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff externalskins = Mod_CountSkinFiles(mod); #endif - framegroups = ParseFrameInfo(mod->name, &numgroups); + framegroups = ParseFrameInfo(mod, &numgroups); ClearBounds(min, max); @@ -6087,46 +6171,6 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff tvector = (vec3_t*)(svector + numverts*numposes); #endif - if (framegroups) - { //group the poses into animations. - for (i = 0; i < numgroups; i++) - { - int first = framegroups[i].firstpose, count = framegroups[i].posecount; - if (first >= numposes) //bound the numbers. - first = numposes-1; - if (first < 0) - first = 0; - if (count > numposes-first) - count = numposes-first; - if (count < 0) - count = 0; - Q_snprintfz(group->name, sizeof(group->name), "%s", framegroups[i].name); - group->numposes = count; - group->rate = framegroups[i].fps; - group->poseofs = pose + first; - group->loop = framegroups[i].loop; - group->events = NULL; - group->action = -1; - group->actionweight = 0; - group++; - } - } - else - { //raw poses, no animations. - for (i = 0; i < numgroups; i++) - { - Q_snprintfz(group->name, sizeof(group->name), "frame%i", i); - group->numposes = 1; - group->rate = 1; - group->poseofs = pose + i; - group->loop = false; - group->events = NULL; - group->action = -1; - group->actionweight = 0; - group++; - } - } - //load in that per-pose data invert = (md3XyzNormal_t *)((qbyte*)surf + LittleLong(surf->ofsXyzNormals)); for (i = 0; i < numposes; i++) @@ -6172,6 +6216,64 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff #endif pose++; } + pose -= numposes; + + if (framegroups) + { //group the poses into animations. + for (i = 0; i < numgroups; i++) + { + Q_snprintfz(group->name, sizeof(group->name), "%s", framegroups[i].name); + + if (framegroups[i].posesarray) + { + unsigned int p, targpose; + group->poseofs = ZG_Malloc(&mod->memgroup, sizeof(*group->poseofs) * framegroups[i].posecount); + group->numposes = 0; + for (p = 0; p < framegroups[i].posecount; p++) + { + targpose = framegroups[i].poses[p]; + if (targpose < numposes) + group->poseofs[group->numposes++] = pose[targpose]; + } + } + else + { + int first = framegroups[i].firstpose, count = framegroups[i].posecount; + if (first >= numposes) //bound the numbers. + first = numposes-1; + if (first < 0) + first = 0; + if (count > numposes-first) + count = numposes-first; + if (count < 0) + count = 0; + group->poseofs = pose + first; + group->numposes = count; + } + + group->rate = framegroups[i].fps; + group->loop = framegroups[i].loop; + group->events = framegroups[i].events; + group->action = framegroups[i].action; + group->actionweight = framegroups[i].actionweight; + group++; + } + } + else + { //raw poses, no animations. + for (i = 0; i < numgroups; i++) + { + Q_snprintfz(group->name, sizeof(group->name), "frame%i", i); + group->numposes = 1; + group->rate = 1; + group->poseofs = pose + i; + group->loop = false; + group->events = NULL; + group->action = -1; + group->actionweight = 0; + group++; + } + } #ifndef SERVERONLY if (externalskinsnumShaders)) @@ -7160,7 +7262,7 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) if (animinfo && animkeys) { int numgroups = 0; - frameinfo_t *frameinfo = ParseFrameInfo(mod->name, &numgroups); + frameinfo_t *frameinfo = ParseFrameInfo(mod, &numgroups); if (numgroups) { /*externally supplied listing of frames. ignore all framegroups in the model and use only the pose info*/ @@ -7169,6 +7271,8 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) for (j = 0; j < numgroups; j++) { /*bound check*/ + if (frameinfo[j].posesarray) + Con_Printf(CON_WARNING"Mod_LoadPSKModel(%s): framegroup[%i] poses array not suppported\n", mod->name, j); if (frameinfo[j].firstpose+frameinfo[j].posecount > num_animkeys) frameinfo[j].posecount = num_animkeys - frameinfo[j].firstpose; if (frameinfo[j].firstpose >= num_animkeys) @@ -7186,8 +7290,9 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) group[j].loop = frameinfo[j].loop; group[j].rate = frameinfo[j].fps; group[j].skeltype = SKEL_RELATIVE; - group[j].action = -1; - group[j].actionweight = 0; + group[j].events = frameinfo[j].events; + group[j].action = frameinfo[j].action; + group[j].actionweight = frameinfo[j].actionweight; } num_animinfo = numgroups; } @@ -7537,7 +7642,7 @@ static qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t //throw away the flags. } - framegroups = ParseFrameInfo(mod->name, &numgroups); + framegroups = ParseFrameInfo(mod, &numgroups); if (!framegroups) { //use the dpm's poses directly. numgroups = header->num_frames; @@ -7596,14 +7701,16 @@ static qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t numposes = 0; if (firstpose + numposes > header->num_frames) numposes = header->num_frames - firstpose; + if (framegroups[i].posesarray) + Con_Printf(CON_WARNING"Mod_LoadDarkPlacesModel(%s): No support for explicit pose lists\n", mod->name); outgroups[i].skeltype = SKEL_RELATIVE; outgroups[i].boneofs = outposedata + firstpose*header->num_bones*12; outgroups[i].numposes = numposes; outgroups[i].loop = framegroups[i].loop; outgroups[i].rate = framegroups[i].fps; - outgroups[i].events = NULL; - outgroups[i].action = -1; - outgroups[i].actionweight = 0; + outgroups[i].events = framegroups[i].events; + outgroups[i].action = framegroups[i].action; + outgroups[i].actionweight = framegroups[i].actionweight; Q_strncpyz(outgroups[i].name, framegroups[i].name, sizeof(outgroups[i].name)); } } @@ -8406,7 +8513,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz numgroups = 0; framegroups = NULL; if (!numgroups) - framegroups = ParseFrameInfo(mod->name, &numgroups); + framegroups = ParseFrameInfo(mod, &numgroups); if (!numgroups && h->num_anims) { /*use the model's framegroups*/ @@ -8421,6 +8528,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz framegroups[i].posecount = LittleLong(anim[i].num_frames); framegroups[i].fps = LittleFloat(anim[i].framerate); framegroups[i].loop = !!(LittleLong(anim[i].flags) & IQM_LOOP); + framegroups[i].events = NULL; framegroups[i].action = -1; framegroups[i].actionweight = 0; Q_strncpyz(framegroups[i].name, Mod_IQMString(&strings, anim[i].name), sizeof(fgroup[i].name)); @@ -8437,6 +8545,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz framegroups->posecount = 1; framegroups->fps = 10; framegroups->loop = 1; + framegroups->events = NULL; framegroups->action = -1; framegroups->actionweight = 0; strcpy(framegroups->name, "base"); @@ -8606,6 +8715,8 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz //now generate the animations. for (i = 0; i < numgroups; i++) { + if (framegroups[i].posesarray) + Con_Printf(CON_WARNING"Mod_ParseIQMMeshModel(%s): framegroup[%i] poses array not suppported\n", mod->name, i); if (framegroups[i].firstpose + framegroups[i].posecount > h->num_frames) framegroups[i].posecount = h->num_frames - framegroups[i].firstpose; if (framegroups[i].firstpose >= h->num_frames) @@ -8629,6 +8740,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz if (fgroup[i].rate <= 0) fgroup[i].rate = 10; + fgroup[i].events = framegroups[i].events; fgroup[i].action = framegroups[i].action; fgroup[i].actionweight = framegroups[i].actionweight; } diff --git a/engine/gl/gl_hlmdl.c b/engine/gl/gl_hlmdl.c index 54f619726..3079a3fa9 100644 --- a/engine/gl/gl_hlmdl.c +++ b/engine/gl/gl_hlmdl.c @@ -371,6 +371,31 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) model->bonectls = bonectls; #ifndef SERVERONLY + model->compatbones = ZG_Malloc(&mod->memgroup, header->numbones * sizeof(*model->compatbones)); + for (i = 0; i < header->numbones; i++) + { + float matrix[12]; + Q_strncpyz(model->compatbones[i].name, model->bones[i].name, sizeof(model->bones[i].name)); + model->compatbones[i].parent = model->bones[i].parent; + model->compatbones[i].ref.org[0] = model->bones[i].value[0]; + model->compatbones[i].ref.org[1] = model->bones[i].value[1]; + model->compatbones[i].ref.org[2] = model->bones[i].value[2]; + QuaternionGLAngle(model->bones[i].value+3, model->compatbones[i].ref.quat); + model->compatbones[i].ref.scale[0] = 1.0f; + model->compatbones[i].ref.scale[1] = 1.0f; + model->compatbones[i].ref.scale[2] = 1.0f; + + //compute rel matrix + GenMatrixPosQuat4Scale(model->compatbones[i].ref.org, model->compatbones[i].ref.quat, model->compatbones[i].ref.scale, matrix); + //compute abs matrix. + if(model->bones[i].parent>=0) + R_ConcatTransforms((void*)transform_matrix[model->bones[i].parent], (void*)matrix, transform_matrix[i]); + else + memcpy(transform_matrix[i], matrix, 12 * sizeof(float)); + //keep the ragdoll code happy with its insistance on using inverses. + Matrix3x4_Invert_Simple((const float*)transform_matrix[i], model->compatbones[i].inverse); + } + tex = (hlmdl_tex_t *) ((qbyte *) texheader + texheader->textures); shaders = ZG_Malloc(&mod->memgroup, texheader->numtextures*sizeof(shader_t)); @@ -1192,7 +1217,7 @@ static int HLMDL_GetBoneData_Internal(hlmodel_t *model, int firstbone, int lastb lastbone = model->header->numbones; if (cbone >= lastbone) continue; - HL_SetupBones(model, fstate->g[bgroup].frame[0], cbone, lastbone, fstate->g[bgroup].subblendfrac, fstate->g[bgroup].subblend2frac, fstate->g[bgroup].frametime[0], result); /* Setup the bones */ + HL_SetupBones(model, fstate->g[bgroup].frame[0] & ~0x8000, cbone, lastbone, fstate->g[bgroup].subblendfrac, fstate->g[bgroup].subblend2frac, fstate->g[bgroup].frametime[0], result); /* Setup the bones */ cbone = lastbone; } return cbone; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 8a1e7b97f..03585ba7e 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -1031,27 +1031,28 @@ void Mod_ModelLoaded(void *ctx, void *data, size_t a, size_t b) { if (qrenderer != QR_NONE) Mod_LoadAliasShaders(mod); - + } #ifdef RAGDOLL + if (mod->type == mod_alias || mod->type == mod_halflife) + { + int numbones = Mod_GetNumBones(mod, false); + if (numbones) { - int numbones = Mod_GetNumBones(mod, false); - if (numbones) + size_t filesize; + char *buf; + char dollname[MAX_QPATH]; + Q_snprintfz(dollname, sizeof(dollname), "%s.doll", mod->name); + buf = FS_LoadMallocFile(dollname, &filesize); + if (buf) { - size_t filesize; - char *buf; - char dollname[MAX_QPATH]; - Q_snprintfz(dollname, sizeof(dollname), "%s.doll", mod->name); - buf = FS_LoadMallocFile(dollname, &filesize); - if (buf) - { - mod->dollinfo = rag_createdollfromstring(mod, dollname, numbones, buf); - BZ_Free(buf); - } + mod->dollinfo = rag_createdollfromstring(mod, dollname, numbones, buf); + BZ_Free(buf); } } -#endif } +#endif + #endif switch(verbose) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 75991d143..d1210e243 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -4349,7 +4349,9 @@ qboolean Shader_Init (void) #ifdef FTE_TARGET_WEB sh_config.max_gpu_bones = 0; //webgl tends to crap out if this is too high, so 32 is a good enough value to play safe. some browsers have really shitty uniform performance too, so lets just default to pure-cpu transforms. in javascript. yes, its that bad. #else - sh_config.max_gpu_bones = 64; //ATI drivers bug out and start to crash if you put this at 128. + //some of our APIs will set their own guesses from queries. don't stomp on that. + if (!sh_config.max_gpu_bones) + sh_config.max_gpu_bones = 64; //ATI drivers bug out and start to crash if you put this at 128. #endif } else diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 9177475cf..38eab475e 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -1541,7 +1541,7 @@ static const char *glsl_hdrs[] = "float e_time;" "};\n" "#ifdef SKELETAL\n" - "layout(std140) unform u_bones\n" + "layout(std140) uniform u_bones\n" "{\n" "#ifdef PACKEDBONES\n" "vec4 m_bones_packed[3*MAX_GPU_BONES];\n" @@ -1648,7 +1648,7 @@ static const char *glsl_hdrs[] = "wmat[2] += m_bones_packed[2+3*int(v_bone.y)] * v_weight.y;" "wmat[2] += m_bones_packed[2+3*int(v_bone.z)] * v_weight.z;" "wmat[2] += m_bones_packed[2+3*int(v_bone.w)] * v_weight.w;" - "wmat[3] = vec4(0.0,0.0,0.0,1.0);\n" + "wmat[3] = vec4(0.0,0.0,0.0,1.0);" "return m_modelviewprojection * (vec4(v_position.xyz, 1.0) * wmat);" "}\n" "vec4 skeletaltransform_nst(out vec3 n, out vec3 t, out vec3 b)" @@ -3664,6 +3664,21 @@ qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name)) sh_config.progs_supported = gl_config.arb_shader_objects; sh_config.progs_required = gl_config_nofixedfunc; + if (gl_config.glversion >= 4.1) + { + GLint maxuniformvecs = 256; //minimum value... or 128 on webgl1(grr) + if (gl_config.glversion >= 4.1) + qglGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxuniformvecs); + else if (gl_config.glversion >= 2.0) + { + qglGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &maxuniformvecs); //at least 1024, supposedly. + maxuniformvecs /= 4; + } + + sh_config.max_gpu_bones = (maxuniformvecs-64)/3; //we don't know how many we're actually going to use for any specific bit of glsl, so make a generous guess and throw in a bit more for drivers that lie. we need 3 per bone matrix. + sh_config.max_gpu_bones = bound(0, sh_config.max_gpu_bones, MAX_BONES); //o.O + } + if (sh_config.progs_supported) { sh_config.pDeleteProg = GLSlang_DeleteProg; diff --git a/engine/gl/gl_videgl.c b/engine/gl/gl_videgl.c index fdf2fa141..a7b4302fa 100644 --- a/engine/gl/gl_videgl.c +++ b/engine/gl/gl_videgl.c @@ -582,7 +582,7 @@ static qboolean EGLHeadless_Init (rendererstate_t *info, unsigned char *palette) if (GL_Init(info, &EGL_Proc)) return true; - Con_Printf(CON_ERROR "Unable to initialise opengl-on-wayland.\n"); + Con_Printf(CON_ERROR "Unable to initialise egl_headless.\n"); return false; } static void EGLHeadless_DeInit(void) diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h index 7187c5999..629e732ba 100644 --- a/engine/gl/glsupp.h +++ b/engine/gl/glsupp.h @@ -349,9 +349,12 @@ typedef void (APIENTRY * PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); #endif - - - +#ifndef GL_VERSION_4_1 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#endif +#ifndef GL_VERSION_2_0 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#endif #ifndef GL_ARB_vertex_program diff --git a/engine/gl/model_hl.h b/engine/gl/model_hl.h index 44b22b030..e169f4816 100644 --- a/engine/gl/model_hl.h +++ b/engine/gl/model_hl.h @@ -298,6 +298,7 @@ typedef struct //this is stored as the cache. an hlmodel_t is generated when dra hlmdl_header_t *header; hlmdl_bone_t *bones; + struct galiasbone_s *compatbones; hlmdl_bonecontroller_t *bonectls; hlmdl_sequencefile_t *animcache[MAX_ANIM_GROUPS]; zonegroup_t *memgroup; diff --git a/plugins/models/gltf.c b/plugins/models/gltf.c index 0caaf6530..632516182 100644 --- a/plugins/models/gltf.c +++ b/plugins/models/gltf.c @@ -2898,6 +2898,8 @@ static float *QDECL GLTF_AnimateBones(const galiasinfo_t *surf, const galiasanim int j = 0, l; const struct galiasanimation_gltf_s *a = anim->boneofs; + numbones = min(numbones, surf->numbones); //if you're asking for more bones, then expect bugs. + if (anim->loop && time >= a->duration) time = time - a->duration*floor(time/a->duration);