Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cg_players: Refactor handling of delta animations. #974

Merged
merged 1 commit into from Dec 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions main/models/players/human_base/character.cfg
Expand Up @@ -2,6 +2,11 @@

sex m

modifiers
{
HandDelta
}

headoffset 0 0 2.5
footsteps default

Expand Down
5 changes: 5 additions & 0 deletions main/models/players/human_light/character.cfg
Expand Up @@ -2,6 +2,11 @@

sex m

modifiers
{
HandDelta
}

headoffset 0 0 2.5
footsteps default

Expand Down
5 changes: 5 additions & 0 deletions main/models/players/human_medium/character.cfg
Expand Up @@ -2,6 +2,11 @@

sex m

modifiers
{
HandDelta
}

headoffset 0 0 2.5
footsteps default

Expand Down
5 changes: 5 additions & 0 deletions main/models/players/human_naked/character.cfg
Expand Up @@ -2,6 +2,11 @@

sex m

modifiers
{
HandDelta
}

headoffset 0 0 2.5
footsteps default

Expand Down
1 change: 1 addition & 0 deletions src.cmake
Expand Up @@ -47,6 +47,7 @@ set(CGAMELIST
${GAMELOGIC_DIR}/cgame/cg_utils.cpp
${GAMELOGIC_DIR}/cgame/cg_view.cpp
${GAMELOGIC_DIR}/cgame/cg_weapons.cpp
${GAMELOGIC_DIR}/cgame/cg_animdelta.cpp
${GAMELOGIC_DIR}/cgame/Filter.h

${GAMELOGIC_DIR}/cgame/rocket/rocket.cpp
Expand Down
119 changes: 119 additions & 0 deletions src/cgame/cg_animdelta.cpp
@@ -0,0 +1,119 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2009 Darklegion Development

This file is part of Daemon.

Daemon is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Daemon is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Daemon; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/

#include "cg_animdelta.h"

namespace
{

int LoadDeltaAnimation( weapon_t weapon, const char* modelName, bool iqm )
{
int handle = 0;
if ( iqm )
{
handle = trap_R_RegisterAnimation( va( "models/players/%s/%s.iqm:%s_delta", modelName, modelName, BG_Weapon( weapon )->name ) );
}
else
{
handle = trap_R_RegisterAnimation( va( "models/players/%s/%s_delta.md5anim", modelName, BG_Weapon( weapon )->name ) );
}
return handle;
}

} // namespace

bool AnimDelta::ParseConfiguration(clientInfo_t* ci, const char* token2, const char** data_p)
{
if ( Q_stricmp( token2, "handBones" ) ) return false;
char* token = COM_Parse2( data_p );
if ( !token || token[0] != '{' )
{
Log::Notice( "^1ERROR^7: Expected '{' but found '%s' in %s's character.cfg", token, ci->modelName );
return false;
}
while ( 1 )
{
token = COM_Parse( data_p );
if ( !token || token[0] == '}' ) break;
int index = trap_R_BoneIndex( ci->bodyModel, token );
if ( index < 0 )
{
Log::Warn("AnimDelta: Error finding bone '%s' in %s", token, ci->modelName );
}
boneIndicies_.push_back( index );
}
return true;
}

bool AnimDelta::LoadData(clientInfo_t* ci)
{
char newModelName[ MAX_QPATH ];
// special handling for human_(naked|light|medium)
if ( !Q_stricmp( ci->modelName, "human_naked" ) ||
!Q_stricmp( ci->modelName, "human_light" ) ||
!Q_stricmp( ci->modelName, "human_medium" ) )
{
Q_strncpyz( newModelName, "human_nobsuit_common", sizeof( newModelName ) );
}
else
{
Q_strncpyz( newModelName, ci->modelName, sizeof( newModelName ) );
}

refSkeleton_t base;
refSkeleton_t delta;
for ( int i = WP_NONE + 1; i < WP_NUM_WEAPONS; ++i )
{
int handle = LoadDeltaAnimation( static_cast<weapon_t>( i ), newModelName, ci->iqm );
if ( !handle ) continue;
Log::Debug("Loaded delta for %s %s", newModelName, BG_Weapon( i )->humanName);
trap_R_BuildSkeleton( &delta, handle, 1, 1, 0, false );
// Derive the delta from the base stand animation.
trap_R_BuildSkeleton( &base, ci->animations[ TORSO_STAND ].handle, 1, 1, 0, false );
auto ret = deltas_.insert( std::make_pair( i, std::vector<delta_t>( boneIndicies_.size() ) ) );
auto& weaponDeltas = ret.first->second;
for ( size_t j = 0; j < boneIndicies_.size(); ++j )
{
VectorSubtract( delta.bones[ boneIndicies_[ j ] ].t.trans, base.bones[ boneIndicies_[ j ] ].t.trans, weaponDeltas[ j ].delta );
QuatInverse( base.bones[ boneIndicies_[ j ] ].t.rot );
QuatMultiply( base.bones[ boneIndicies_[ j ] ].t.rot, delta.bones[ boneIndicies_[ j ] ].t.rot, weaponDeltas[ j ].rot );
}
}
return true;
}

void AnimDelta::Apply(const entityState_t* es, refSkeleton_t* skeleton)
{
if ( ( es->torsoAnim & ~ANIM_TOGGLEBIT ) < TORSO_ATTACK ) return;
auto it = deltas_.find( es->weapon );
if ( it == deltas_.end() ) return;
for ( size_t i = 0; i < it->second.size(); ++i )
{
VectorAdd( it->second[ i ].delta, skeleton->bones[ boneIndicies_[ i ] ].t.trans, skeleton->bones[ boneIndicies_[ i ] ].t.trans );
QuatMultiply2( skeleton->bones[ boneIndicies_[ i ] ].t.rot, it->second[ i ].rot );
}
}




47 changes: 47 additions & 0 deletions src/cgame/cg_animdelta.h
@@ -0,0 +1,47 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2009 Darklegion Development

This file is part of Daemon.

Daemon is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Daemon is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Daemon; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#ifndef CG_ANIMDELTA_H
#define CG_ANIMDELTA_H
#include <vector>
#include <unordered_map>

#include "cg_local.h"
#include "cg_skeleton_modifier.h"

class AnimDelta : public SkeletonModifier
{
public:
virtual bool ParseConfiguration( clientInfo_t* ci, const char* token, const char** data_p ) override;
virtual bool LoadData( clientInfo_t* ci ) override;
virtual void Apply( const entityState_t* es, refSkeleton_t* skeleton ) override;

private:
typedef struct {
vec3_t delta;
quat_t rot;
} delta_t;

std::unordered_map<int, std::vector<delta_t>> deltas_;
std::vector<int> boneIndicies_;
};
#endif // CG_ANIMDELTA_H
16 changes: 9 additions & 7 deletions src/cgame/cg_local.h
Expand Up @@ -23,12 +23,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifndef CG_LOCAL_H
#define CG_LOCAL_H

#include <memory>
#include <vector>

#include "engine/qcommon/q_shared.h"
#include "engine/renderer/tr_types.h"
#include "engine/client/cg_api.h"
#include "shared/bg_public.h"
#include "engine/client/keycodes.h"
#include "cg_ui.h"
#include "cg_skeleton_modifier.h"

// The entire cgame module is unloaded and reloaded on each level change,
// so there is no persistent data between levels on the client side.
Expand Down Expand Up @@ -849,7 +853,7 @@ typedef struct
// this is regenerated each time a client's configstring changes,
// usually as a result of a userinfo (name, model, etc) change
#define MAX_CUSTOM_SOUNDS 32
typedef struct
struct clientInfo_t
{
bool infoValid;

Expand All @@ -869,7 +873,7 @@ typedef struct
char modelName[ MAX_QPATH ];
char skinName[ MAX_QPATH ];

bool newAnims; // true if using the new mission pack animations
bool iqm; // true if model is an iqm model
bool fixedlegs; // true if legs yaw is always the same as torso yaw
bool fixedtorso; // true if torso never changes yaw
bool nonsegmented; // true if model is Q2 style nonsegmented
Expand Down Expand Up @@ -909,16 +913,14 @@ typedef struct
int legBones[ MAX_BONES ];
int numLegBones;

int weaponAdjusted; // bitmask of all weapons that have hand deltas
int handBones[ MAX_BONES ];
int numHandBones;

sfxHandle_t customFootsteps[ 4 ];
sfxHandle_t customMetalFootsteps[ 4 ];

char voice[ MAX_VOICE_NAME_LEN ];
int voiceTime;
} clientInfo_t;
std::vector<std::shared_ptr<SkeletonModifier>> modifiers;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment below about the abstraction.
Barring a correction there (which would change this to store by value), this can also use std::unique_ptr instead of std::shared_ptr.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to store pointers here so that we can share one copy of a modifier among all the different clientInfos. There is one per player model and they are generally shared.


};

typedef struct weaponInfoMode_s
{
Expand Down