/** * @brief Precache all menu models for faster access * @sa CL_ViewPrecacheModels * @todo Does not precache armoured models */ static float CL_PrecacheCharacterModels (float alreadyLoadedPercent) { teamDef_t* td; int i, j; const float percent = 40.0f; if (!cl_precache->integer) return 0; /* search the name */ for (i = 0, td = csi.teamDef; i < csi.numTeamDefs; i++, td++) for (j = NAME_NEUTRAL; j < NAME_LAST; j++) { /* search one of the model definitions */ for (linkedList_t const* list = td->models[j]; list; list = list->next) { teamDef_t::model_t const& m = *static_cast<teamDef_t::model_t const*>(list->data); /* register body */ char model[MAX_QPATH]; Com_sprintf(model, sizeof(model), "%s/%s", m.path, m.body); if (!R_FindModel(model)) Com_Printf("Com_PrecacheCharacterModels: Could not register model %s\n", model); /* register head */ Com_sprintf(model, sizeof(model), "%s/%s", m.path, m.head); if (!R_FindModel(model)) Com_Printf("Com_PrecacheCharacterModels: Could not register model %s\n", model); alreadyLoadedPercent += percent / (td->numModels[j] * csi.numTeamDefs * NAME_LAST); SCR_DrawLoadingScreen(true, alreadyLoadedPercent); } } /* some genders may not have models - ensure that we do the wanted percent step */ return percent; }
/** * @brief Loads the image or model for a given particle art */ static inline void CL_ParticleLoadArt (ptlArt_t *a) { /* register the art */ switch (a->type) { case ART_PIC: { const char *imageName; /* only one image */ if (a->name[0] != '+') imageName = a->name; else /* load several frames */ imageName = va("%s%c%c", a->name + 1, a->frame / 10 + '0', a->frame % 10 + '0'); a->art.image = R_FindPics(imageName); if (!a->art.image) Com_Printf("CL_ParticleLoadArt: Could not load image: '%s'\n", imageName); } break; case ART_MODEL: /** @todo Support the frame data from ptlArt_t for models, too */ a->art.model = R_FindModel(a->name); break; default: Com_Error(ERR_DROP, "CL_ParticleLoadArt: Unknown art type\n"); } }
/** * @brief Precaches all models at game startup - for faster access */ void CL_ViewPrecacheModels (void) { float percent = 30.0f; float alreadyLoadedPercent = 30.0f; float loaded = CL_PrecacheCharacterModels(alreadyLoadedPercent); alreadyLoadedPercent += loaded; if (loaded == 0) percent = 100 - alreadyLoadedPercent; for (int i = 0; i < csi.numODs; i++) { const objDef_t* od = INVSH_GetItemByIDX(i); alreadyLoadedPercent += percent / csi.numODs; SCR_DrawLoadingScreen(true, alreadyLoadedPercent); if (od->type[0] == '\0' || od->isDummy) continue; if (od->model[0] != '\0') { cls.modelPool[i] = R_FindModel(od->model); if (cls.modelPool[i]) Com_DPrintf(DEBUG_CLIENT, "CL_PrecacheModels: Registered object model: '%s' (%i)\n", od->model, i); } } /* now make sure that all the precached models are stored until we quit the game * otherwise they would be freed with every map change */ R_SwitchModelMemPoolTag(); SCR_DrawLoadingScreen(false, 100.f); }
/** * @brief Adds a camera edicts to the client for displaying them * @sa EV_CAMERA_APPEAR */ void CL_CameraAppear (const eventRegister_t *self, dbuffer *msg) { int entnum; int team; int levelflags; int dir; int rotate; vec3_t origin; camera_type_t cameraType; NET_ReadFormat(msg, self->formatString, &entnum, &origin, &team, &dir, &cameraType, &levelflags, &rotate); le_t *le = LE_Get(entnum); if (!le) { le = LE_Add(entnum); } else { le->inuse = true; } VectorCopy(origin, le->origin); le->type = ET_CAMERA; le->team = team; le->angles[YAW] = directionAngles[le->angle]; le->flags |= LE_CHECK_LEVELFLAGS; le->levelflags = levelflags; le->model1 = R_FindModel(va("objects/cameras/camera%i", cameraType)); if (rotate) { const char *rotateAnim = "rotate"; R_AnimChange(&le->as, le->model1, rotateAnim); } Com_DPrintf(DEBUG_CLIENT, "CL_CameraAppear: entnum: %i\n", entnum); }
/** * @brief Parse values for a sequence model * @return 1 - increase the command position of the sequence by one * @sa seqEnt_vals * @sa CL_SequenceFindEnt */ static int SEQ_ExecuteModel (sequenceContext_t *context, const char *name, const char *data) { /* get sequence entity */ seqEnt_t *se = SEQ_FindEnt(context, name); if (!se) { int i; /* create new sequence entity */ for (i = 0, se = context->ents; i < context->numEnts; i++, se++) if (!se->inuse) break; if (i >= context->numEnts) { if (context->numEnts >= MAX_SEQENTS) Com_Error(ERR_FATAL, "Too many sequence entities"); se = &context->ents[context->numEnts++]; } /* allocate */ OBJZERO(*se); se->inuse = true; Q_strncpyz(se->name, name, sizeof(se->name)); VectorSet(se->color, 0.7, 0.7, 0.7); } /* get values */ while (*data) { const value_t *vp; for (vp = seqEnt_vals; vp->string; vp++) if (Q_streq(data, vp->string)) { data += strlen(data) + 1; Com_EParseValue(se, data, vp->type, vp->ofs, vp->size); break; } if (!vp->string) { if (Q_streq(data, "model")) { data += strlen(data) + 1; Com_DPrintf(DEBUG_CLIENT, "Registering model: %s\n", data); se->model = R_FindModel(data); if (se->model == NULL) se->inuse = false; } else if (Q_streq(data, "anim")) { if (se->model == NULL) Com_Error(ERR_FATAL, "could not change the animation - no model loaded yet"); data += strlen(data) + 1; Com_DPrintf(DEBUG_CLIENT, "Change anim to: %s\n", data); R_AnimChange(&se->as, se->model, data); } else Com_Printf("SEQ_ExecuteModel: unknown token '%s'\n", data); } data += strlen(data) + 1; } return 1; }
/** * @brief Register misc_models * @sa CL_ViewLoadMedia */ void LM_Register (void) { for (int i = 0; i < cl.numLMs; i++) { localModel_t& lm = cl.LMs[i]; /* register the model */ lm.model = R_FindModel(lm.name); if (lm.animname[0]) { R_AnimChange(&lm.as, lm.model, lm.animname); if (!lm.as.change) Com_Printf("LM_Register: Could not change anim of %s to '%s'\n", lm.name, lm.animname); } if (!lm.model) lm.inuse = false; } }
/** * @brief Draw 3D Marker on the 2D geoscape. * @param[in] screenPos Position on screenlongitude and latitude of the model to draw. * @param[in] direction angle giving the direction the model is heading toward. */ void R_Draw2DMapMarkers (const vec2_t screenPos, float direction, const char *model, int skin) { modelInfo_t mi; vec2_t size; vec3_t scale, center, position, angles; float zoom = 0.4f; OBJZERO(mi); VectorCopy(vec3_origin, position); VectorCopy(vec3_origin, angles); mi.model = R_FindModel(model); if (!mi.model) { Com_Printf("Could not find model '%s'\n", model); return; } mi.name = model; mi.origin = position; mi.angles = angles; mi.skin = skin; size[0] = size[1] = MARKER_SIZE * zoom; R_ModelAutoScale(size, &mi, scale, center); /* reset the center, as we want to place the models onto the surface of the earth */ mi.center = nullptr; /* go to a new matrix */ glPushMatrix(); /* Apply all transformation to model. Note that the transformations are applied starting * from the last one and ending with the first one */ /* move model to its location */ glTranslatef(screenPos[0]* viddef.rx, screenPos[1]* viddef.ry, 0); /* scale model to proper resolution */ glScalef(viddef.rx, viddef.ry, 1.0f); /* rotate model to proper direction. */ glRotatef(-90.f + direction, 0, 0, 1); R_DrawModelDirect(&mi, nullptr, nullptr); /* restore previous matrix */ glPopMatrix(); }
/** * @brief Precaches the models and images for a sequence * @return 1 - increase the command position of the sequence by one * @sa R_RegisterModelShort * @sa R_RegisterImage */ static int SEQ_ExecutePrecache (sequenceContext_t *context, const char *name, const char *data) { if (Q_streq(name, "models")) { while (*data) { Com_DPrintf(DEBUG_CLIENT, "Precaching model: %s\n", data); R_FindModel(data); data += strlen(data) + 1; } } else if (Q_streq(name, "pics")) { while (*data) { Com_DPrintf(DEBUG_CLIENT, "Precaching image: %s\n", data); R_FindPics(data); data += strlen(data) + 1; } } else Com_Printf("SEQ_ExecutePrecache: unknown format '%s'\n", name); return 1; }
/** * @brief Draw 3D Marker on the 3D geoscape. * @param[in] rotate vector giving the angles of earth rotation due to player view. * @param[in] pos longitude and latitude of the model to draw. * @param[in] direction angle giving the direction the model is heading toward. * @param[in] earthRadius Radius of earth on screen (this include zoom). */ void R_Draw3DMapMarkers (const vec2_t nodePos, const vec2_t nodeSize, const vec3_t rotate, const vec2_t pos, float direction, float earthRadius, const char *model, int skin) { /* normalize */ const float nx = nodePos[0] * viddef.rx; const float ny = nodePos[1] * viddef.ry; const float nw = nodeSize[0] * viddef.rx; const float nh = nodeSize[1] * viddef.ry; /* Earth center is in the middle of node. * Due to Orthographic view, this is also camera position */ const vec3_t earthPos = {nx + nw / 2.0f, ny + nh / 2.0f, 0.0f}; modelInfo_t mi; vec2_t size; vec3_t scale, center, position, angles; float zoom = 0.4f; OBJZERO(mi); VectorCopy(vec3_origin, position); VectorCopy(vec3_origin, angles); mi.model = R_FindModel(model); if (!mi.model) { Com_Printf("Could not find model '%s'\n", model); return; } mi.name = model; mi.origin = position; mi.angles = angles; mi.skin = skin; size[0] = size[1] = MARKER_SIZE * zoom; R_ModelAutoScale(size, &mi, scale, center); /* reset the center, as we want to place the models onto the surface of the earth */ mi.center = nullptr; /* go to a new matrix */ glPushMatrix(); /* Apply all transformation to model. Note that the transformations are applied starting * from the last one and ending with the first one */ /* center model on earth. Translate also along z to avoid seeing * bottom part of the model through earth (only half of earth is drawn) */ glTranslatef(earthPos[0], earthPos[1], 10.0f); /* scale model to proper resolution */ glScalef(viddef.rx, viddef.ry, 1.0f); /* place model on earth: make it tangent to earth surface, heading toward it if direction is used. */ glRotatef(-rotate[1], 1, 0, 0); glRotatef(rotate[2], 0, 1, 0); glRotatef(rotate[0] - pos[0], 0, 0, 1); glRotatef(90.0f - pos[1], 1, 0, 0); glTranslatef(0, 0, earthRadius); glRotatef(90.0f + direction, 0, 0, 1); R_DrawModelDirect(&mi, nullptr, nullptr); /* restore previous matrix */ glPopMatrix(); }
/** * @brief Register local entities for SOLID_BSP models like func_breakable or func_door * @note func_breakable, func_door * @sa G_SendEdictsAndBrushModels * @sa EV_ADD_BRUSH_MODEL * @sa CL_SpawnParseEntitystring */ void CL_AddBrushModel (const eventRegister_t *self, struct dbuffer *msg) { le_t *le; int entnum, modelnum1, levelflags, speed, dir; entity_type_t type; const cBspModel_t *model; int angle; vec3_t origin, angles; NET_ReadFormat(msg, self->formatString, &type, &entnum, &modelnum1, &levelflags, &origin, &angles, &speed, &angle, &dir); if (type != ET_BREAKABLE && type != ET_DOOR && type != ET_ROTATING && type != ET_DOOR_SLIDING && type != ET_TRIGGER_RESCUE && type != ET_TRIGGER_NEXTMAP) Com_Error(ERR_DROP, "Invalid le announced via EV_ADD_BRUSH_MODEL type: %i\n", type); else if (modelnum1 > MAX_MODELS || modelnum1 < 1) Com_Error(ERR_DROP, "Invalid le modelnum1 announced via EV_ADD_BRUSH_MODEL\n"); /* check if the ent is already visible */ le = LE_Get(entnum); if (le) Com_Error(ERR_DROP, "le announced a second time - le for entnum %i (type: %i) already exists (via EV_ADD_BRUSH_MODEL)\n", entnum, type); le = LE_Add(entnum); assert(le); le->rotationSpeed = speed / 100.0f; le->slidingSpeed = speed; le->angle = angle; le->dir = dir; le->type = type; le->modelnum1 = modelnum1; le->levelflags = levelflags; le->addFunc = LE_BrushModelAction; LE_SetThink(le, LET_BrushModel); /* The origin and angles are REQUIRED for doors to work! */ VectorCopy(origin, le->origin); /* store the initial position - needed for sliding doors */ VectorCopy(le->origin, le->oldOrigin); VectorCopy(angles, le->angles); Com_sprintf(le->inlineModelName, sizeof(le->inlineModelName), "*%i", le->modelnum1); model = LE_GetClipModel(le); le->model1 = R_FindModel(le->inlineModelName); if (!le->model1) Com_Error(ERR_DROP, "CL_AddBrushModel: Could not register inline model %i", le->modelnum1); /* Transfer model mins and maxs to entity */ VectorCopy(model->mins, le->mins); VectorCopy(model->maxs, le->maxs); VectorSubtract(le->maxs, le->mins, le->size); VecToPos(le->origin, le->pos); /* to allow tracing against this le */ if (!LE_IsNotSolid(le)) { /* This is to help the entity collision code out */ /* Copy entity origin and angles to model*/ CM_SetInlineModelOrientation(cl.mapTiles, le->inlineModelName, le->origin, le->angles); le->contents = CONTENTS_SOLID; CL_RecalcRouting(le); } }
/** * @brief Call before entering a new level, or after vid_restart */ void CL_ViewLoadMedia (void) { le_t* le; int i, max; float loadingPercent; CL_ViewUpdateRenderData(); if (CL_GetConfigString(CS_TILES)[0] == '\0') return; /* no map loaded */ GAME_InitMissionBriefing(_(CL_GetConfigString(CS_MAPTITLE))); loadingPercent = 0; /* register models, pics, and skins */ SCR_DrawLoading(loadingPercent); R_ModBeginLoading(CL_GetConfigString(CS_TILES), CL_GetConfigStringInteger(CS_LIGHTMAP), CL_GetConfigString(CS_POSITIONS), CL_GetConfigString(CS_NAME), CL_GetConfigString(CS_MAPZONE)); CL_SpawnParseEntitystring(); loadingPercent += 10.0f; SCR_DrawLoading(loadingPercent); LM_Register(); CL_ParticleRegisterArt(); for (i = 1, max = 0; i < MAX_MODELS && CL_GetConfigString(CS_MODELS + i)[0] != '\0'; i++) max++; max += csi.numODs; for (i = 1; i < MAX_MODELS; i++) { const char* name = CL_GetConfigString(CS_MODELS + i); if (name[0] == '\0') break; SCR_DrawLoading(loadingPercent); cl.model_draw[i] = R_FindModel(name); if (!cl.model_draw[i]) { Cmd_ExecuteString("fs_info"); Com_Error(ERR_DROP, "Could not load model '%s'\n", name); } /* initialize clipping for bmodels */ if (name[0] == '*') cl.model_clip[i] = CM_InlineModel(cl.mapTiles, name); else cl.model_clip[i] = nullptr; loadingPercent += 100.0f / (float)max; } /* update le model references */ le = nullptr; while ((le = LE_GetNextInUse(le))) { if (le->modelnum1 > 0) le->model1 = LE_GetDrawModel(le->modelnum1); if (le->modelnum2 > 0) le->model2 = LE_GetDrawModel(le->modelnum2); } refdef.ready = true; /* waiting for EV_START */ SCR_EndLoadingPlaque(); }
void draw_stats() { if (mdview.baseModel) { iTextX = 2*TEXT_WIDTH; // arb start pos 2 in from both edges iTextY = 4*TEXT_DEPTH; // ... or 4... :-) // // Displays text at a 2d screen coord. 0,0 is top left corner, add TEXT_DEPTH per Y to go down a line // // Text_DisplayFlat("testing testing HELLO!!", 100,100, 0, 255,0, true); // // Vec3 v={0,0,0}; // Text_Display("testing testing HELLO!!", v, 255,0,0); // // gl_model *model = mdview.baseModel; stat_skeleton( model, NULL ); // anim locks on the whole model?, if so display 'em... // char sString[1024]; for (int i=0; i<2; i++) { // display anim locks at bottom of screen... // Sequence_t* pSeq=NULL; bool bIsUpper = false; if (i==0 && iAnimLockNumber_Upper ) { pSeq = Animation_GetUpperSequence( iAnimLockNumber_Upper-1 ); bIsUpper = true; } if (i==1 && iAnimLockNumber_Lower ) { pSeq = Animation_GetLowerSequence( iAnimLockNumber_Lower-1 ); bIsUpper = false; } if (pSeq) { int iYpos = g_iScreenHeight-((!i?4:3)*TEXT_DEPTH); // cheat, do this next bit here just to get the X coord, then overwrite later sprintf(sString,"%s anim lock: %s Frames: %4d...%4d%s",!i?"Upper":"Lower",String_EnsureMinLength(pSeq->sName.c_str(),iAnimLockLongestString),pSeq->iTargetFrame,(pSeq->iTargetFrame+pSeq->iFrameCount)-1,String_EnsureMinLength((pSeq->iLoopFrame==-1)?"":va(" loop: %3d(%3d)",pSeq->iLoopFrame,pSeq->iTargetFrame+pSeq->iLoopFrame),25)); int iXpos = (g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH); if (pSeq->bMultiSeq) { Sequence_t *pCurrentMultiSeq = GetMultiLockedSequenceFromFrame(bIsUpper?pModel_Upper->currentFrame:pModel_Lower->currentFrame, bIsUpper ); int iStrlenAtCurrentSeqPoint = 0; sprintf(sString,"%s anim lock: ",bIsUpper?"Upper":"Lower"); MultiSequenceLock_t* pMultiLock = (bIsUpper)?&MultiSequenceLock_Upper:&MultiSequenceLock_Lower; MultiSequenceLock_t::iterator it; for (it = pMultiLock->begin(); it != pMultiLock->end(); ++it) { int iSeqIndex = *it; pSeq = (bIsUpper)?Animation_GetUpperSequence(iSeqIndex):Animation_GetLowerSequence(iSeqIndex); assert(pSeq); if (pSeq) { if (pSeq == pCurrentMultiSeq) iStrlenAtCurrentSeqPoint = strlen(sString); strcat(sString,va("%s ",pSeq->sName.c_str())); } } // int iXpos = (g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH); Text_DisplayFlat(sString, iXpos, iYpos, 255,0,0, true); // now overlay the highlighted one for current... // if (pCurrentMultiSeq) { iXpos += iStrlenAtCurrentSeqPoint*TEXT_WIDTH; Text_DisplayFlat(pCurrentMultiSeq->sName.c_str(), iXpos, iYpos, 255,0,255, true); } } else { sprintf(sString,"%s anim lock: %s Frames: %4d...%4d%s",!i?"Upper":"Lower",String_EnsureMinLength(pSeq->sName.c_str(),iAnimLockLongestString),pSeq->iTargetFrame,(pSeq->iTargetFrame+pSeq->iFrameCount)-1,String_EnsureMinLength((pSeq->iLoopFrame==-1)?"":va(" loop: %3d(%3d)",pSeq->iLoopFrame,pSeq->iTargetFrame+pSeq->iLoopFrame),25)); // int iXpos = (g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH); Text_DisplayFlat(sString, iXpos, iYpos, 255,0,0, true); } } if (RunningNT() == 4) // only needed on NT4, NT 2000 and W95/98 are ok { pSeq = NULL; if (i==0 && iAnimDisplayNumber_Upper) pSeq = Animation_GetUpperSequence( iAnimDisplayNumber_Upper-1); if (i==1 && iAnimDisplayNumber_Lower) pSeq = Animation_GetLowerSequence( iAnimDisplayNumber_Lower-1); if (pSeq) { if (pSeq->bMultiSeq) { assert(0); // should never be able to get here in the animlock display-cycler } else { sprintf(sString,"( %s anim : %s Frames: %4d...%4d%s )",!i?"Upper":"Lower",String_EnsureMinLength(pSeq->sName.c_str(),iAnimLockLongestString),pSeq->iTargetFrame,(pSeq->iTargetFrame+pSeq->iFrameCount)-1,String_EnsureMinLength((pSeq->iLoopFrame==-1)?"":va(" loop: %3d(%3d)",pSeq->iLoopFrame,pSeq->iTargetFrame+pSeq->iLoopFrame),25)); int iXpos = (g_iScreenWidth/2)-( ((strlen(sString)/2)/*+2*/)*TEXT_WIDTH); // 2 chars back from LOCk string, because of bracket+space at start int iYpos = g_iScreenHeight-((!i?7:6)*TEXT_DEPTH); Text_DisplayFlat(sString, iXpos, iYpos, 0,200,200, true); // dim yellow } } } } // display current FPS and interp state... // sprintf(sString,"FPS: %2.2f %s",1/(mdview.animSpeed),mdview.animate?"(Playing)":"(Stopped)"); iTextX = Text_DisplayFlat(sString, (g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH), 1*TEXT_DEPTH, 255,255,255, false ); if (mdview.interpolate) { iTextX = Text_DisplayFlat("(Interpolated)", iTextX+(2*TEXT_WIDTH),1*TEXT_DEPTH, 255/2,255/2,255/2,false); } iTextX = Text_DisplayFlat(va("(LOD: %d)",mdview.iLODLevel+1), iTextX+(2*TEXT_WIDTH), 1*TEXT_DEPTH, 255/2,255,255/2,false); /* Text_DisplayFlat(sString, g_iScreenWidth-((strlen(sString)+2)*TEXT_WIDTH), 2 *TEXT_DEPTH, 255,255,255, false );*/ iTextX = Text_DisplayFlat(va("( FOV: %g )",mdview.dFOV), iTextX+(2*TEXT_WIDTH),1*TEXT_DEPTH, 255, 255, 255, false); if (mdview.bUseAlpha) { Text_DisplayFlat("( Alpha )", iTextX+(2*TEXT_WIDTH),1*TEXT_DEPTH, 128, 128, 128, false); } // display head skin numbers in top left... // gl_model* pModel = R_FindModel( mdview.baseModel, "head"); if (pModel) { sprintf(sString,"Head skin: %s", va("\"%s%s\"",model->sHeadSkinName,!mdview.iSkinNumber?"":va("-%1d",mdview.iSkinNumber))); Text_DisplayFlat(sString, 2*TEXT_WIDTH,//(g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH), 1*TEXT_DEPTH, 200,200,200, false ); } // display which format anim table this is using (if any)... // if (mdview.bAnimCFGLoaded) { if (mdview.bAnimIsMultiPlayerFormat) sprintf(sString,"( MultiPlayer ) "); else sprintf(sString,"( SinglePlayer ) "); int iYpos = g_iScreenHeight-(2*TEXT_DEPTH); int iXpos = g_iScreenWidth -(strlen(sString)*TEXT_WIDTH); Text_DisplayFlat(sString, iXpos, iYpos, 128,128,128, true); // grey } // display current picmip state... // { extern int TextureList_GetMip(void); int iYpos = g_iScreenHeight-(2*TEXT_DEPTH); int iXpos = 1*TEXT_WIDTH; sprintf(sString,"( PICMIP: %d )",TextureList_GetMip()); Text_DisplayFlat(sString, iXpos, iYpos, 100,100,100, false ); } } }
/** * @brief Draws a model in 2d mode (for rendering model data from the ui) * @param[in,out] mi All the needed model information to render the model * @param[in,out] pmi The model information of the parent model. This is used * in those cases, where the model that should get rendered here is placed relativly * to an already existing model in the world. * @param[in] tagname If a parent model is given, a @c tagname is given in most cases, too. It's used * to transform the model location relative to the parent model location again. E.g. a * @c tagname of tag_rweapon will transform the location to the right hand of an actor. * @sa R_DrawAliasModel */ void R_DrawModelDirect (modelInfo_t * mi, modelInfo_t * pmi, const char *tagname) { image_t *skin; mAliasMesh_t *mesh; if (Q_strnull(mi->name)) return; /* register the model */ mi->model = R_FindModel(mi->name); /* check if the model exists */ if (!mi->model) { Com_Printf("No model found for '%s'\n", mi->name); return; } skin = R_AliasModelState(mi->model, &mi->mesh, &mi->frame, &mi->oldframe, &mi->skin); if (skin == NULL) { Com_Printf("Model '%s' is broken\n", mi->name); return; } glPushMatrix(); glScalef(viddef.rx, viddef.ry, (viddef.rx + viddef.ry) / 2); R_Color(mi->color); if (pmi) { /* register the parent model */ pmi->model = R_FindModel(pmi->name); /* transform - the next transform for the child model will be relative from the * parent model location now */ R_TransformModelDirect(pmi); /* tag transformation */ if (tagname) { const mAliasTagOrientation_t *current = NULL; const mAliasTagOrientation_t *old = NULL; R_GetTags(pmi->model, tagname, pmi->frame, pmi->oldframe, ¤t, &old); if (current != NULL && old != NULL) { float interpolated[16]; /* do interpolation */ R_InterpolateTransform(pmi->backlerp, pmi->model->alias.num_frames, current, old, interpolated); /* transform */ glMultMatrixf(interpolated); R_CheckError(); } } } /* transform */ R_TransformModelDirect(mi); /* we have to reenable this here - we are in 2d mode here already */ glEnable(GL_DEPTH_TEST); /* draw it */ R_BindTexture(skin->texnum); /* draw the model */ mesh = &mi->model->alias.meshes[0]; refdef.aliasCount += mesh->num_tris; if (mi->model->alias.num_frames == 1) R_DrawAliasStaticWithReset(mesh, vec4_origin); else R_DrawAliasFrameLerp(&mi->model->alias, mesh, mi->backlerp, mi->frame, mi->oldframe, vec4_origin); /* show model bounding box */ if (r_showbox->integer) R_DrawBoundingBox(mi->model->alias.frames[mi->frame].mins, mi->model->alias.frames[mi->frame].maxs); glDisable(GL_DEPTH_TEST); glPopMatrix(); R_Color(NULL); }
/** * @todo need to merge UI model case, and the common case (looks to be a copy-pasted code) */ void UI_DrawModelNode (uiNode_t* node, const char* source) { modelInfo_t mi; uiModel_t* model; vec3_t nodeorigin; vec2_t screenPos; assert(UI_NodeInstanceOf(node, "model")); /**< We use model extradata */ if (!source || source[0] == '\0') return; model = UI_GetUIModel(source); /* direct model name - no UI model definition */ if (!model) { /* prevent the searching for a model def in the next frame */ mi.model = R_FindModel(source); mi.name = source; if (!mi.model) { Com_Printf("Could not find model '%s'\n", source); return; } } /* compute the absolute origin ('origin' property is relative to the node center) */ UI_GetNodeScreenPos(node, screenPos); UI_GetNodeAbsPos(node, nodeorigin); R_CleanupDepthBuffer(nodeorigin[0], nodeorigin[1], node->box.size[0], node->box.size[1]); if (EXTRADATA(node).clipOverflow) { UI_PushClipRect(screenPos[0], screenPos[1], node->box.size[0], node->box.size[1]); } nodeorigin[0] += node->box.size[0] / 2 + EXTRADATA(node).origin[0]; nodeorigin[1] += node->box.size[1] / 2 + EXTRADATA(node).origin[1]; nodeorigin[2] = EXTRADATA(node).origin[2]; VectorMA(EXTRADATA(node).angles, cls.frametime, EXTRADATA(node).omega, EXTRADATA(node).angles); mi.origin = nodeorigin; mi.angles = EXTRADATA(node).angles; mi.scale = EXTRADATA(node).scale; mi.center = nullVector; mi.color = node->color; mi.mesh = 0; /* special case to draw models with UI model */ if (model) { UI_DrawModelNodeWithUIModel(node, source, &mi, model); if (EXTRADATA(node).clipOverflow) UI_PopClipRect(); return; } /* if the node is linked to a parent, the parent will display it */ if (EXTRADATA(node).tag) { if (EXTRADATA(node).clipOverflow) UI_PopClipRect(); return; } /* autoscale? */ if (EXTRADATA(node).autoscale) { vec3_t autoScale; vec3_t autoCenter; const vec2_t size = {node->box.size[0] - node->padding, node->box.size[1] - node->padding}; R_ModelAutoScale(size, &mi, autoScale, autoCenter); } /* no animation */ mi.frame = 0; mi.oldframe = 0; mi.backlerp = 0; /* get skin */ if (EXTRADATA(node).skin && *EXTRADATA(node).skin) mi.skin = atoi(UI_GetReferenceString(node, EXTRADATA(node).skin)); else mi.skin = 0; /* do animations */ if (EXTRADATA(node).animation && *EXTRADATA(node).animation) { const char* ref; ref = UI_GetReferenceString(node, EXTRADATA(node).animation); /* check whether the cvar value changed */ if (strncmp(EXTRADATA(node).oldRefValue, source, MAX_OLDREFVALUE)) { Q_strncpyz(EXTRADATA(node).oldRefValue, source, MAX_OLDREFVALUE); /* model has changed but mem is already reserved in pool */ Mem_Free(EXTRADATA(node).animationState); EXTRADATA(node).animationState = nullptr; } animState_t* as = EXTRADATA(node).animationState; if (!as) { as = Mem_PoolAllocType(animState_t, cl_genericPool); if (!as) Com_Error(ERR_DROP, "Model %s should have animState_t for animation %s - but doesn't\n", mi.name, ref); R_AnimChange(as, mi.model, ref); EXTRADATA(node).animationState = as; } else { const char* anim; /* change anim if needed */ anim = R_AnimGetName(as, mi.model); if (anim && !Q_streq(anim, ref)) R_AnimChange(as, mi.model, ref); R_AnimRun(as, mi.model, cls.frametime * 1000); } mi.frame = as->frame; mi.oldframe = as->oldframe; mi.backlerp = as->backlerp; } /* draw the main model on the node */ R_DrawModelDirect(&mi, nullptr, nullptr); /* draw all children */ if (node->firstChild) { uiNode_t* child; modelInfo_t pmi = mi; for (child = node->firstChild; child; child = child->next) { const char* tag; char childSource[MAX_VAR]; const char* childRef; /* skip non "model" nodes */ if (child->behaviour != node->behaviour) continue; /* skip invisible child */ if (child->invis || !UI_CheckVisibility(child)) continue; OBJZERO(mi); mi.angles = EXTRADATA(child).angles; mi.scale = EXTRADATA(child).scale; mi.center = nullVector; mi.origin = EXTRADATA(child).origin; mi.color = pmi.color; /* get the anchor name to link the model into the parent */ tag = EXTRADATA(child).tag; /* init model name */ childRef = UI_GetReferenceString(child, EXTRADATA(child).model); if (Q_strnull(childRef)) childSource[0] = '\0'; else Q_strncpyz(childSource, childRef, sizeof(childSource)); mi.model = R_FindModel(childSource); mi.name = childSource; /* init skin */ if (EXTRADATA(child).skin && *EXTRADATA(child).skin) mi.skin = atoi(UI_GetReferenceString(child, EXTRADATA(child).skin)); else mi.skin = 0; R_DrawModelDirect(&mi, &pmi, tag); } } if (EXTRADATA(node).clipOverflow) UI_PopClipRect(); }
/** * @brief Draw a model using UI model definition */ static void UI_DrawModelNodeWithUIModel (uiNode_t* node, const char* source, modelInfo_t* mi, uiModel_t* model) { bool autoScaleComputed = false; vec3_t autoScale; vec3_t autoCenter; while (model) { /* no animation */ mi->frame = 0; mi->oldframe = 0; mi->backlerp = 0; assert(model->model); mi->model = R_FindModel(model->model); if (!mi->model) { model = model->next; continue; } mi->skin = model->skin; mi->name = model->model; /* set mi pointers to model */ mi->origin = model->origin; mi->angles = model->angles; mi->center = model->center; mi->color = model->color; mi->scale = model->scale; if (model->tag && model->parent) { /* tag and parent defined */ uiModel_t* parentModel; modelInfo_t pmi; vec3_t pmiorigin; animState_t* as; /* place this model part on an already existing model tag */ parentModel = UI_GetUIModel(model->parent); if (!parentModel) { Com_Printf("UI Model: Could not get the model '%s'\n", model->parent); break; } pmi.model = R_FindModel(parentModel->model); if (!pmi.model) { Com_Printf("UI Model: Could not get the model '%s'\n", parentModel->model); break; } pmi.name = parentModel->model; pmi.origin = pmiorigin; pmi.angles = parentModel->angles; pmi.scale = parentModel->scale; pmi.center = parentModel->center; pmi.color = parentModel->color; pmi.origin[0] = parentModel->origin[0] + mi->origin[0]; pmi.origin[1] = parentModel->origin[1] + mi->origin[1]; pmi.origin[2] = parentModel->origin[2]; /* don't count window offset twice for tagged models */ mi->origin[0] -= node->root->box.pos[0]; mi->origin[1] -= node->root->box.pos[1]; /* autoscale? */ if (EXTRADATA(node).autoscale) { if (!autoScaleComputed) Sys_Error("Wrong order of model nodes - the tag and parent model node must be after the base model node"); pmi.scale = autoScale; pmi.center = autoCenter; } as = &parentModel->animState; pmi.frame = as->frame; pmi.oldframe = as->oldframe; pmi.backlerp = as->backlerp; R_DrawModelDirect(mi, &pmi, model->tag); } else { /* no tag and no parent means - base model or single model */ const char* ref; UI_InitModelInfoView(node, mi, model); Vector4Copy(node->color, mi->color); /* compute the scale and center for the first model. * it think its the bigger of composite models. * All next elements use the same result */ if (EXTRADATA(node).autoscale) { if (!autoScaleComputed) { vec2_t size; size[0] = node->box.size[0] - node->padding; size[1] = node->box.size[1] - node->padding; R_ModelAutoScale(size, mi, autoScale, autoCenter); autoScaleComputed = true; } else { mi->scale = autoScale; mi->center = autoCenter; } } /* get the animation given by node properties */ if (EXTRADATA(node).animation && *EXTRADATA(node).animation) { ref = UI_GetReferenceString(node, EXTRADATA(node).animation); /* otherwise use the standard animation from UI model definition */ } else ref = model->anim; /* only base models have animations */ if (ref && *ref) { animState_t* as = &model->animState; const char* anim = R_AnimGetName(as, mi->model); /* initial animation or animation change */ if (anim == nullptr || !Q_streq(anim, ref)) R_AnimChange(as, mi->model, ref); else R_AnimRun(as, mi->model, cls.frametime * 1000); mi->frame = as->frame; mi->oldframe = as->oldframe; mi->backlerp = as->backlerp; } R_DrawModelDirect(mi, nullptr, nullptr); } /* next */ model = model->next; } }