diff --git a/main/models/players/human_bsuit/character.cfg b/main/models/players/human_bsuit/character.cfg index 24a6aac9dc3..4c95369af7e 100644 --- a/main/models/players/human_bsuit/character.cfg +++ b/main/models/players/human_bsuit/character.cfg @@ -6,10 +6,10 @@ modifiers { HandDelta Segmented - HumanRotations + BattlesuitRotations } -headoffset 0 -5.0 16 +headoffset 0 -5.0 16 footsteps default // the client game needs to know the names of the first and the last diff --git a/src/cgame/cg_players.cpp b/src/cgame/cg_players.cpp index 0d42a61e1bc..b80cb4b9a90 100644 --- a/src/cgame/cg_players.cpp +++ b/src/cgame/cg_players.cpp @@ -168,13 +168,20 @@ static bool CG_ParseCharacterFile( const char *filename, clientInfo_t *ci ) if ( !Q_stricmp( token, "HandDelta" ) ) { ci->modifiers.emplace_back(new AnimDelta()); - } else if ( !Q_stricmp( token, "HumanRotations" ) ) + } + else if ( !Q_stricmp( token, "HumanRotations" ) ) { ci->modifiers.emplace_back(new HumanSkeletonRotations()); - } else if ( !Q_stricmp( token, "Segmented" ) ) + } + else if ( !Q_stricmp( token, "BattlesuitRotations" ) ) + { + ci->modifiers.emplace_back(new BsuitSkeletonRotations()); + } + else if ( !Q_stricmp( token, "Segmented" ) ) { ci->modifiers.emplace_back(new SegmentedSkeletonCombiner()); - } else + } + else { Log::Notice("Unknown modifier '%s' in %s's character.cfg", token, ci->modelName); } diff --git a/src/cgame/cg_segmented_skeleton.cpp b/src/cgame/cg_segmented_skeleton.cpp index c3b841a308d..6a28927d2d3 100644 --- a/src/cgame/cg_segmented_skeleton.cpp +++ b/src/cgame/cg_segmented_skeleton.cpp @@ -85,6 +85,45 @@ void HumanSkeletonRotations::Apply(const SkeletonModifierContext& ctx, refSkelet QuatMultiply2( skeleton->bones[ leftShoulderBone ].t.rot, rotation ); } +bool BsuitSkeletonRotations::ParseConfiguration(clientInfo_t* ci, const char* token, const char** data_p) +{ + if (!Q_stricmp(token, "torsoControlBone")) { + torsoControlBone = BoneLookup(ci, COM_Parse2(data_p)); + return true; + } + if (!Q_stricmp(token, "leftShoulder")) { + leftShoulderBone = BoneLookup(ci, COM_Parse2(data_p)); + return true; + } + if (!Q_stricmp(token, "rightShoulder")) { + rightShoulderBone = BoneLookup(ci, COM_Parse2(data_p)); + return true; + } + return false; +} + +void BsuitSkeletonRotations::Apply(const SkeletonModifierContext& ctx, refSkeleton_t* skeleton) +{ + // rotate torso + if ( torsoControlBone >= 0 && torsoControlBone < skeleton->numBones ) + { + // HACK: convert angles to bone system + quat_t rotation; + QuatFromAngles( rotation, ctx.torsoYawAngle, 0, 0 ); + QuatMultiply2( skeleton->bones[ torsoControlBone ].t.rot, rotation ); + } + + // HACK: limit angle (avoids worst of the gun clipping through the body) + // Needs some proper animation fixes... + auto pitch = Math::Clamp(ctx.pitchAngle, -40, 20); + quat_t rotation; + QuatFromAngles( rotation, -pitch, 0, 0 ); + QuatMultiply2( skeleton->bones[ rightShoulderBone ].t.rot, rotation ); + + // Relationships are emphirically derived. They will probably need to be changed upon changes to the human model + QuatFromAngles( rotation, pitch, 0, 0); + QuatMultiply2( skeleton->bones[ leftShoulderBone ].t.rot, rotation ); +} bool SegmentedSkeletonCombiner::ParseConfiguration(clientInfo_t* ci, const char* token, const char** data_p) { diff --git a/src/cgame/cg_segmented_skeleton.h b/src/cgame/cg_segmented_skeleton.h index 40fa32a128a..7bef1b4e20f 100644 --- a/src/cgame/cg_segmented_skeleton.h +++ b/src/cgame/cg_segmented_skeleton.h @@ -38,6 +38,18 @@ class HumanSkeletonRotations : public SkeletonModifier int rightShoulderBone = -1; }; +class BsuitSkeletonRotations : public SkeletonModifier +{ +public: + virtual bool ParseConfiguration( clientInfo_t* ci, const char* token, const char** data_p ) override; + virtual void Apply( const SkeletonModifierContext& ctx, refSkeleton_t* skeleton ) override; + +private: + int torsoControlBone = -1; + int leftShoulderBone = -1; + int rightShoulderBone = -1; +}; + class SegmentedSkeletonCombiner : public SkeletonModifier {