initial
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
// ----------------------------------------- //
|
||||
// File generated by VPC //
|
||||
// ----------------------------------------- //
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\bonesetup\bone_constraints.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\bonesetup\bone_constraints.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\bonesetup\bone_constraints.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\bonesetup\bone_decode.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\bonesetup\bone_decode.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\bonesetup\bone_decode.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\bonesetup\bone_ik.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\bonesetup\bone_ik.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\bonesetup\bone_ik.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\bonesetup\bone_setup.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\bonesetup\bone_setup.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\bonesetup\bone_setup.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\bonesetup\bone_utils.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\bonesetup\bone_utils.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\bonesetup\bone_utils.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\debug_lib_check.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
@@ -0,0 +1,898 @@
|
||||
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// Routines used for various prodcedural bones but meant to be called from
|
||||
// datamodel or maya as well
|
||||
//
|
||||
// In a separate source file so linking bonesetup.lib doesn't get more than
|
||||
// needed
|
||||
//
|
||||
//===============================================================================
|
||||
|
||||
|
||||
// Valve includes
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "mathlib/vector.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "bone_setup.h"
|
||||
#include "bone_constraints.h"
|
||||
#include "bone_accessor.h"
|
||||
#include "studio.h"
|
||||
|
||||
#include "tier0/tslist.h"
|
||||
#include "tier0/miniprofiler.h"
|
||||
|
||||
#include "bone_utils.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
//
|
||||
// CConstraintBones
|
||||
//
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Compute the aggregate target position and orientation from the weighted target list and return
|
||||
// the resulting position and orientation in addition to updating the target dag.
|
||||
// All passed arrays must be nTargetCount in length
|
||||
//-----------------------------------------------------------------------------
|
||||
float CConstraintBones::ComputeTargetPosition(
|
||||
Vector &vTargetPosition,
|
||||
int nTargetCount,
|
||||
float *flTargetWeights,
|
||||
Vector *vTargetPositions,
|
||||
Vector *vTargetOffsets )
|
||||
{
|
||||
vTargetPosition = vec3_origin;
|
||||
float flWeightSum = 0.0;
|
||||
|
||||
for ( int i = 0; i < nTargetCount; ++i )
|
||||
{
|
||||
vTargetPosition += ( flTargetWeights[i] * ( vTargetPositions[i] + vTargetOffsets[i] ) );
|
||||
flWeightSum += flTargetWeights[i];
|
||||
}
|
||||
|
||||
if ( flWeightSum > 0.0f )
|
||||
{
|
||||
vTargetPosition *= 1.0f / flWeightSum;
|
||||
}
|
||||
|
||||
return MIN( 1.0f, flWeightSum );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Compute the aggregate target orientation from the weighted target list and
|
||||
// return the total weight
|
||||
// All passed arrays must be nTargetCount in length
|
||||
//-----------------------------------------------------------------------------
|
||||
float CConstraintBones::ComputeTargetOrientation(
|
||||
Quaternion &qTargetOrientation,
|
||||
int nTargetCount,
|
||||
float *pflTargetWeights,
|
||||
Quaternion *pqTargetOrientations,
|
||||
Quaternion *pqTargetOffsets )
|
||||
{
|
||||
// If there is only one target, for efficiency don't bother with the weighting
|
||||
if ( nTargetCount == 1 )
|
||||
{
|
||||
QuaternionMult( pqTargetOrientations[0], pqTargetOffsets[0], qTargetOrientation );
|
||||
|
||||
return MIN( 1.0f, pflTargetWeights[0] );
|
||||
}
|
||||
|
||||
qTargetOrientation = quat_identity;
|
||||
|
||||
// If no targets, return identity quaternion and weight of 0
|
||||
if ( nTargetCount <= 0 )
|
||||
return 0.0f;
|
||||
|
||||
Quaternion *pQuats = reinterpret_cast< Quaternion * >( stackalloc( nTargetCount * sizeof( Quaternion ) ) );
|
||||
|
||||
float flWeightSum = 0.0f;
|
||||
|
||||
for ( int i = 0; i < nTargetCount; ++i )
|
||||
{
|
||||
QuaternionMult( pqTargetOrientations[i], pqTargetOffsets[i], pQuats[i] );
|
||||
flWeightSum += pflTargetWeights[i];
|
||||
}
|
||||
|
||||
QuaternionAverageExponential( qTargetOrientation, nTargetCount, pQuats, pflTargetWeights );
|
||||
|
||||
return MIN( 1.0f, flWeightSum );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Compute the aggregate target position and orientation from the weighted
|
||||
// target list and return the total weight
|
||||
// All passed arrays must be nTargetCount in length
|
||||
//-----------------------------------------------------------------------------
|
||||
float CConstraintBones::ComputeTargetPositionOrientation(
|
||||
Vector &vTargetPosition,
|
||||
Quaternion &qTargetOrientation,
|
||||
int nTargetCount,
|
||||
float *pflTargetWeights,
|
||||
Vector *pvTargetPositions,
|
||||
Vector *pvTargetOffsets,
|
||||
Quaternion *pqTargetOrientations,
|
||||
Quaternion *pqTargetOffsets )
|
||||
{
|
||||
Quaternion *pQuats = reinterpret_cast< Quaternion *>( stackalloc( nTargetCount * sizeof( Quaternion ) ) );
|
||||
|
||||
float flWeightSum = 0.0f;
|
||||
|
||||
vTargetPosition = vec3_origin;
|
||||
qTargetOrientation = quat_identity;
|
||||
|
||||
for ( int i = 0; i < nTargetCount; ++i )
|
||||
{
|
||||
float flWeight = pflTargetWeights[i];
|
||||
|
||||
matrix3x4a_t mTarget;
|
||||
AngleMatrix( RadianEuler(pqTargetOrientations[i]), pvTargetPositions[i], mTarget );
|
||||
|
||||
matrix3x4a_t mOffset;
|
||||
AngleMatrix( RadianEuler(pqTargetOffsets[i]), pvTargetOffsets[i], mOffset );
|
||||
|
||||
matrix3x4a_t mAbs;
|
||||
ConcatTransforms( mTarget, mOffset, mAbs );
|
||||
|
||||
Vector vPos;
|
||||
MatrixAngles( mAbs, pQuats[i], vPos );
|
||||
|
||||
vTargetPosition += ( flWeight * vPos );
|
||||
|
||||
// For normalization
|
||||
flWeightSum += flWeight;
|
||||
}
|
||||
|
||||
if ( flWeightSum > 0.0f )
|
||||
{
|
||||
vTargetPosition *= 1.0f / flWeightSum;
|
||||
}
|
||||
|
||||
QuaternionAverageExponential( qTargetOrientation, nTargetCount, pQuats, pflTargetWeights );
|
||||
|
||||
return MIN( 1.0f, flWeightSum );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Compute the aggregate target position and orientation from the weighted
|
||||
// target list and return the total weight
|
||||
// All passed arrays must be nTargetCount in length
|
||||
//-----------------------------------------------------------------------------
|
||||
float CConstraintBones::ComputeTargetPositionOrientation(
|
||||
Vector &vTargetPosition,
|
||||
Quaternion &qTargetOrientation,
|
||||
int nTargetCount,
|
||||
float *pflTargetWeights,
|
||||
matrix3x4a_t *pmTargets,
|
||||
matrix3x4a_t *pmOffsets )
|
||||
{
|
||||
Quaternion *pQuats = reinterpret_cast< Quaternion *>( stackalloc( nTargetCount * sizeof( Quaternion ) ) );
|
||||
|
||||
float flWeightSum = 0.0f;
|
||||
|
||||
vTargetPosition = vec3_origin;
|
||||
qTargetOrientation = quat_identity;
|
||||
|
||||
matrix3x4a_t mAbs;
|
||||
Vector vPos;
|
||||
|
||||
for ( int i = 0; i < nTargetCount; ++i )
|
||||
{
|
||||
float flWeight = pflTargetWeights[i];
|
||||
|
||||
ConcatTransforms( pmTargets[i], pmOffsets[i], mAbs );
|
||||
|
||||
MatrixAngles( mAbs, pQuats[i], vPos );
|
||||
|
||||
vTargetPosition += ( flWeight * vPos );
|
||||
|
||||
// For normalization
|
||||
flWeightSum += flWeight;
|
||||
}
|
||||
|
||||
if ( flWeightSum > 0.0f )
|
||||
{
|
||||
vTargetPosition *= 1.0f / flWeightSum;
|
||||
}
|
||||
|
||||
QuaternionAverageExponential( qTargetOrientation, nTargetCount, pQuats, pflTargetWeights );
|
||||
|
||||
return MIN( 1.0f, flWeightSum );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConstraintBones::ComputeAimConstraintOffset(
|
||||
Quaternion &qAimOffset,
|
||||
bool bPreserveOffset,
|
||||
const Vector &vTargetWorldPos,
|
||||
const matrix3x4_t &mSlaveParentToWorld,
|
||||
const Vector &vUp,
|
||||
const Vector &vSlaveLocalPos,
|
||||
const Quaternion &qSlaveLocal,
|
||||
matrix3x4_t *pmUpToWorld,
|
||||
AimConstraintUpType_t eUpType )
|
||||
{
|
||||
if ( !bPreserveOffset )
|
||||
{
|
||||
qAimOffset = quat_identity;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the desired orientation based the target position
|
||||
Quaternion qAim;
|
||||
ComputeAimConstraint( qAim, vTargetWorldPos, mSlaveParentToWorld, vUp, vSlaveLocalPos, pmUpToWorld, eUpType );
|
||||
|
||||
// Compute the difference between the slave's current orientation and the target orientation
|
||||
Quaternion qAimInv;
|
||||
QuaternionInvert( qAim, qAimInv );
|
||||
|
||||
QuaternionMult( qAimInv, qSlaveLocal, qAimOffset );
|
||||
|
||||
RadianEuler eAim(qAim);
|
||||
RadianEuler eSlaveLocal(qSlaveLocal);
|
||||
RadianEuler eAimOffset(qAimOffset);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calculate the orientation needed to make a transform where the y
|
||||
// vector of the transform matches the forward vector and the z vector matches
|
||||
// the up reference vector as closely as possible. The x vector will be in the
|
||||
// plane defined by using the forward vector as the normal.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConstraintBones::ComputeAimConstraintAimAt(
|
||||
Quaternion &qAim,
|
||||
const Vector &vForward,
|
||||
const Vector &vReferenceUp )
|
||||
{
|
||||
Vector vFwd = vForward;
|
||||
vFwd.NormalizeInPlace();
|
||||
const float flRatio = DotProduct( vFwd, vReferenceUp );
|
||||
Vector vUp = vReferenceUp - ( vFwd * flRatio );
|
||||
vUp.NormalizeInPlace();
|
||||
|
||||
Vector vRight = vFwd.Cross( vUp );
|
||||
vRight.NormalizeInPlace();
|
||||
|
||||
const Vector &vX = vRight;
|
||||
const Vector &vY = vFwd;
|
||||
const Vector &vZ = vUp;
|
||||
|
||||
const float flTr = vX.x + vY.y + vZ.z;
|
||||
qAim.Init( vY.z - vZ.y , vZ.x - vX.z, vX.y - vY.x, flTr + 1.0f );
|
||||
const float flRadius = qAim[0] * qAim[0] + qAim[1] * qAim[1] + qAim[2] * qAim[2] + qAim[3] * qAim[3];
|
||||
if ( flRadius > FLT_EPSILON )
|
||||
{
|
||||
QuaternionNormalize( qAim );
|
||||
}
|
||||
else
|
||||
{
|
||||
matrix3x4_t mRot;
|
||||
MatrixSetColumn( vX, 0, mRot );
|
||||
MatrixSetColumn( vY, 1, mRot );
|
||||
MatrixSetColumn( vZ, 2, mRot );
|
||||
MatrixQuaternion( mRot, qAim );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Given the various parameters, computes the local vForward & vReferenceUp
|
||||
// and calls ComputeAimConstraintAimAt
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConstraintBones::ComputeAimConstraint(
|
||||
Quaternion &qAim,
|
||||
const Vector &vTargetWorldPos,
|
||||
const matrix3x4_t &mParentToWorld,
|
||||
const Vector &vUp,
|
||||
const Vector &vSlaveLocalPos,
|
||||
const matrix3x4_t *pmUpToWorld,
|
||||
AimConstraintUpType_t eUpType )
|
||||
{
|
||||
matrix3x4_t mWorldToParent;
|
||||
MatrixInvert( mParentToWorld, mWorldToParent );
|
||||
|
||||
// If the up vector is in world space, convert it into local space
|
||||
Vector vWorldUp;
|
||||
|
||||
ComputeWorldUpVector( &vWorldUp, mParentToWorld, vUp, vSlaveLocalPos, pmUpToWorld, eUpType );
|
||||
|
||||
Vector vLocalUp;
|
||||
VectorRotate( vWorldUp, mWorldToParent, vLocalUp );
|
||||
|
||||
// Convert the target's world space position into the local space of the slave.
|
||||
Vector vTargetLocalPos;
|
||||
VectorTransform( vTargetWorldPos, mWorldToParent, vTargetLocalPos );
|
||||
|
||||
// Compute the local space forward vector
|
||||
Vector vLocalForward = vTargetLocalPos - vSlaveLocalPos;
|
||||
vLocalForward.NormalizeInPlace();
|
||||
|
||||
// Compute the orientation
|
||||
CConstraintBones::ComputeAimConstraintAimAt( qAim, vLocalForward, vLocalUp );
|
||||
RadianEuler e(qAim);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CConstraintBones::ComputeWorldUpVector(
|
||||
Vector *pvWorldUp,
|
||||
const matrix3x4_t & mParentToWorld,
|
||||
const Vector &vUp,
|
||||
const Vector &vSlaveLocalPos,
|
||||
const matrix3x4_t *pmUpToWorld,
|
||||
AimConstraintUpType_t eUpType )
|
||||
{
|
||||
switch ( eUpType )
|
||||
{
|
||||
case AC_UP_TYPE_VECTOR:
|
||||
VectorCopy( vUp, *pvWorldUp );
|
||||
break;
|
||||
case AC_UP_TYPE_OBJECT:
|
||||
if ( pmUpToWorld )
|
||||
{
|
||||
Vector vUpObjectWorldPos;
|
||||
MatrixPosition( *pmUpToWorld, vUpObjectWorldPos );
|
||||
Vector vSlaveWorldPos;
|
||||
VectorTransform( vSlaveLocalPos, mParentToWorld, vSlaveWorldPos );
|
||||
VectorSubtract( vUpObjectWorldPos, vSlaveWorldPos, *pvWorldUp );
|
||||
VectorNormalize( *pvWorldUp );
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( vUp, *pvWorldUp );
|
||||
}
|
||||
break;
|
||||
case AC_UP_TYPE_PARENT_ROTATION:
|
||||
VectorRotate( vUp, mParentToWorld, *pvWorldUp );
|
||||
break;
|
||||
default:
|
||||
case AC_UP_TYPE_OBJECT_ROTATION:
|
||||
if ( pmUpToWorld )
|
||||
{
|
||||
VectorRotate( vUp, *pmUpToWorld, *pvWorldUp );
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( vUp, *pvWorldUp );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
//
|
||||
// CStudioConstraintBones
|
||||
//
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
float CStudioConstraintBones::ComputeTargetPosition(
|
||||
Vector &vTargetPosition,
|
||||
mstudioconstrainttarget_t *pTargets,
|
||||
int nTargetCount,
|
||||
CBoneAccessor &boneToWorld )
|
||||
{
|
||||
float *pflTargetWeights = reinterpret_cast< float * >( stackalloc( nTargetCount * sizeof( float ) ) );
|
||||
Vector *pvTargetPositions = reinterpret_cast< Vector * >( stackalloc( nTargetCount * sizeof( Vector ) ) );
|
||||
Vector *pvTargetOffsets = reinterpret_cast< Vector * >( stackalloc( nTargetCount * sizeof( Vector ) ) );
|
||||
|
||||
mstudioconstrainttarget_t *pTarget;
|
||||
|
||||
for ( int i = 0; i < nTargetCount; ++i )
|
||||
{
|
||||
pTarget = pTargets + i;
|
||||
pflTargetWeights[i] = pTarget->m_flWeight;
|
||||
pvTargetOffsets[i] = pTarget->m_vOffset;
|
||||
MatrixPosition( boneToWorld.GetBone( pTarget->m_nBone ), pvTargetPositions[i] );
|
||||
}
|
||||
|
||||
return CConstraintBones::ComputeTargetPosition( vTargetPosition, nTargetCount, pflTargetWeights, pvTargetPositions, pvTargetOffsets );
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// CStudioConstraintBones : Studio Interface Functions
|
||||
//=============================================================================
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// studio interface
|
||||
//-----------------------------------------------------------------------------
|
||||
float CStudioConstraintBones::ComputeTargetOrientation(
|
||||
Quaternion &qTargetOrientation,
|
||||
mstudioconstrainttarget_t *pTargets,
|
||||
int nTargetCount,
|
||||
CBoneAccessor &boneToWorld )
|
||||
{
|
||||
float *pflTargetWeights = reinterpret_cast< float * >( stackalloc( nTargetCount * sizeof( float ) ) );
|
||||
Quaternion *pqTargetOrientations = reinterpret_cast< Quaternion * >( stackalloc( nTargetCount * sizeof( Quaternion ) ) );
|
||||
Quaternion *pqTargetOffsets = reinterpret_cast< Quaternion * >( stackalloc( nTargetCount * sizeof( Quaternion ) ) );
|
||||
|
||||
mstudioconstrainttarget_t *pTarget;
|
||||
|
||||
for ( int i = 0; i < nTargetCount; ++i )
|
||||
{
|
||||
pTarget = pTargets + i;
|
||||
pflTargetWeights[i] = pTarget->m_flWeight;
|
||||
pqTargetOffsets[i] = pTarget->m_qOffset;
|
||||
MatrixQuaternion( boneToWorld.GetBone( pTarget->m_nBone ), pqTargetOrientations[i] );
|
||||
}
|
||||
|
||||
return CConstraintBones::ComputeTargetOrientation( qTargetOrientation, nTargetCount, pflTargetWeights, pqTargetOrientations, pqTargetOffsets );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// studio interface
|
||||
//-----------------------------------------------------------------------------
|
||||
float CStudioConstraintBones::ComputeTargetPositionOrientation(
|
||||
Vector &vTargetPosition,
|
||||
Quaternion &qTargetOrientation,
|
||||
mstudioconstrainttarget_t *pTargets,
|
||||
int nTargetCount,
|
||||
CBoneAccessor &boneToWorld )
|
||||
{
|
||||
float *pflTargetWeights = reinterpret_cast< float * >( stackalloc( nTargetCount * sizeof( float ) ) );
|
||||
matrix3x4a_t *pmTargets = reinterpret_cast< matrix3x4a_t * >( stackalloc( nTargetCount * sizeof( matrix3x4a_t ) ) );
|
||||
matrix3x4a_t *pmOffsets = reinterpret_cast< matrix3x4a_t * >( stackalloc( nTargetCount * sizeof( matrix3x4a_t ) ) );
|
||||
|
||||
mstudioconstrainttarget_t *pTarget = pTargets;
|
||||
|
||||
for ( int i = 0; i < nTargetCount; ++i, ++pTarget )
|
||||
{
|
||||
pflTargetWeights[i] = pTarget->m_flWeight;
|
||||
QuaternionMatrix( pTarget->m_qOffset, pTarget->m_vOffset, pmOffsets[i] );
|
||||
pmTargets[i] = boneToWorld.GetBone( pTarget->m_nBone );
|
||||
}
|
||||
|
||||
return CConstraintBones::ComputeTargetPositionOrientation( vTargetPosition, qTargetOrientation, nTargetCount, pflTargetWeights, pmTargets, pmOffsets );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// studio interface
|
||||
//-----------------------------------------------------------------------------
|
||||
void CStudioConstraintBones::ComputeBaseWorldMatrix(
|
||||
matrix3x4a_t &mBaseWorldMatrix,
|
||||
mstudioconstraintslave_t *pSlave,
|
||||
CBoneAccessor &boneToWorld,
|
||||
const CStudioHdr *pStudioHdr,
|
||||
const matrix3x4_t *pmViewTransform /* = NULL */ )
|
||||
{
|
||||
// studiomdl shouldn't create mstudioconstraintslave_t's with invalid bone indices
|
||||
Assert( pSlave->m_nBone >= 0 && pSlave->m_nBone < MAXSTUDIOBONES );
|
||||
|
||||
const int nBoneParent = pStudioHdr->boneParent( pSlave->m_nBone );
|
||||
if ( nBoneParent < 0 )
|
||||
{
|
||||
if ( pmViewTransform )
|
||||
{
|
||||
matrix3x4a_t mTmp;
|
||||
QuaternionMatrix( pSlave->m_qBaseOrientation, pSlave->m_vBasePosition, mTmp );
|
||||
ConcatTransforms( *pmViewTransform, mTmp, mBaseWorldMatrix );
|
||||
}
|
||||
else
|
||||
{
|
||||
QuaternionMatrix( pSlave->m_qBaseOrientation, pSlave->m_vBasePosition, mBaseWorldMatrix );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matrix3x4a_t mTmp;
|
||||
QuaternionMatrix( pSlave->m_qBaseOrientation, pSlave->m_vBasePosition, mTmp );
|
||||
ConcatTransforms( boneToWorld.GetBone( nBoneParent ), mTmp, mBaseWorldMatrix );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// studio interface
|
||||
//-----------------------------------------------------------------------------
|
||||
void CStudioConstraintBones::ComputePointConstraint(
|
||||
const mstudiobone_t *pBones,
|
||||
int nBone,
|
||||
CBoneAccessor &boneToWorld,
|
||||
const CStudioHdr *pStudioHdr )
|
||||
{
|
||||
BONE_PROFILE_FUNC();
|
||||
|
||||
mstudiopointconstraint_t *pProc = ( mstudiopointconstraint_t * )pBones[nBone].pProcedure();
|
||||
|
||||
// Calculate the current target position and the total weight
|
||||
// of the the targets contributing to the target position.
|
||||
Vector vTargetPosition;
|
||||
const float flWeight = CStudioConstraintBones::ComputeTargetPosition( vTargetPosition, pProc->pTarget( 0 ), pProc->m_nTargetCount, boneToWorld );
|
||||
|
||||
Vector vFinalPosition;
|
||||
|
||||
matrix3x4a_t &mBaseWorldMatrix = boneToWorld.GetBoneForWrite( nBone );
|
||||
CStudioConstraintBones::ComputeBaseWorldMatrix( mBaseWorldMatrix, &( pProc->m_slave ), boneToWorld, pStudioHdr );
|
||||
|
||||
// Blend between the target position and the base position using the target weight
|
||||
if ( flWeight < 1.0f )
|
||||
{
|
||||
Vector vBasePosition;
|
||||
MatrixPosition( mBaseWorldMatrix, vBasePosition );
|
||||
|
||||
vFinalPosition = Lerp( flWeight, vBasePosition, vTargetPosition );
|
||||
}
|
||||
else
|
||||
{
|
||||
vFinalPosition = vTargetPosition;
|
||||
}
|
||||
|
||||
// Update the bone the new position.
|
||||
PositionMatrix( vFinalPosition, mBaseWorldMatrix );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// studio interface
|
||||
//-----------------------------------------------------------------------------
|
||||
void CStudioConstraintBones::ComputeOrientConstraint(
|
||||
const mstudiobone_t *pBones,
|
||||
int nBone,
|
||||
CBoneAccessor &boneToWorld,
|
||||
const CStudioHdr *pStudioHdr,
|
||||
const matrix3x4_t *pmViewTransform )
|
||||
{
|
||||
BONE_PROFILE_FUNC();
|
||||
|
||||
mstudioorientconstraint_t *pProc = ( mstudioorientconstraint_t * )pBones[nBone].pProcedure();
|
||||
|
||||
// Calculate the current target position and the total weight
|
||||
// of the the targets contributing to the target position.
|
||||
Quaternion qTargetOrientation;
|
||||
const float flWeight = CStudioConstraintBones::ComputeTargetOrientation( qTargetOrientation, pProc->pTarget( 0 ), pProc->m_nTargetCount, boneToWorld );
|
||||
|
||||
// Blend between the target orientation and the base orientation using the target weight
|
||||
Quaternion qFinalOrientation;
|
||||
|
||||
matrix3x4a_t &mBaseWorldMatrix = boneToWorld.GetBoneForWrite( nBone );
|
||||
CStudioConstraintBones::ComputeBaseWorldMatrix( mBaseWorldMatrix, &( pProc->m_slave ), boneToWorld, pStudioHdr, pmViewTransform );
|
||||
|
||||
if ( flWeight < 1.0f )
|
||||
{
|
||||
Quaternion qBaseOrientation;
|
||||
MatrixQuaternion( mBaseWorldMatrix, qBaseOrientation );
|
||||
|
||||
QuaternionSlerp( qBaseOrientation, qTargetOrientation, flWeight, qFinalOrientation );
|
||||
}
|
||||
else
|
||||
{
|
||||
qFinalOrientation = qTargetOrientation;
|
||||
}
|
||||
|
||||
// Quaternion matrix wipes out the translate component
|
||||
Vector vTmpPosition;
|
||||
MatrixPosition( mBaseWorldMatrix, vTmpPosition );
|
||||
QuaternionMatrix( qFinalOrientation, vTmpPosition, mBaseWorldMatrix );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// studio interface
|
||||
//
|
||||
// pmViewTransform is for hlmv which modifies the actual parentless bones
|
||||
// to position things in the viewer
|
||||
//
|
||||
// TODO: Split into hlmv and "normal" versions, i.e. hlmv needs ViewTransform
|
||||
//-----------------------------------------------------------------------------
|
||||
void CStudioConstraintBones::ComputeAimConstraint(
|
||||
const mstudiobone_t *pBones,
|
||||
int nBone,
|
||||
CBoneAccessor &boneToWorld,
|
||||
const CStudioHdr *pStudioHdr,
|
||||
const matrix3x4_t *pmViewTransform,
|
||||
AimConstraintUpType_t eType )
|
||||
{
|
||||
BONE_PROFILE_FUNC();
|
||||
|
||||
mstudioaimconstraint_t *pProc = ( mstudioaimconstraint_t * )pBones[nBone].pProcedure();
|
||||
|
||||
// Calculate the current target position and the total weight
|
||||
// of the the targets contributing to the target position.
|
||||
Vector vTargetPos;
|
||||
const float flWeight = CStudioConstraintBones::ComputeTargetPosition( vTargetPos, pProc->pTarget( 0 ), pProc->m_nTargetCount, boneToWorld );
|
||||
Vector vTargetWorldPos;
|
||||
|
||||
matrix3x4a_t mSlaveParentToWorld;
|
||||
const int nParentBone = pBones[nBone].parent;
|
||||
|
||||
if ( pmViewTransform )
|
||||
{
|
||||
matrix3x4_t mInv;
|
||||
MatrixInvert( *pmViewTransform, mInv );
|
||||
|
||||
VectorTransform( vTargetPos, mInv, vTargetWorldPos );
|
||||
|
||||
if ( nParentBone >= 0 )
|
||||
{
|
||||
ConcatTransforms( mInv, boneToWorld[nParentBone], mSlaveParentToWorld );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIdentityMatrix( mSlaveParentToWorld );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( vTargetPos, vTargetWorldPos );
|
||||
|
||||
if ( nParentBone >= 0 )
|
||||
{
|
||||
MatrixCopy( boneToWorld[nParentBone], mSlaveParentToWorld );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetIdentityMatrix( mSlaveParentToWorld );
|
||||
}
|
||||
}
|
||||
|
||||
Quaternion qTargetOrientation;
|
||||
CConstraintBones::ComputeAimConstraint(
|
||||
qTargetOrientation,
|
||||
vTargetWorldPos,
|
||||
mSlaveParentToWorld,
|
||||
pProc->m_vUp,
|
||||
pProc->m_slave.m_vBasePosition,
|
||||
pProc->m_nUpSpaceTarget >= 0 ? &boneToWorld[ pProc->m_nUpSpaceTarget ] : NULL,
|
||||
eType );
|
||||
|
||||
// Add in initial offset
|
||||
Quaternion qOffsetOrientation;
|
||||
QuaternionMult( qTargetOrientation, pProc->m_qAimOffset, qOffsetOrientation );
|
||||
|
||||
// Add in parent matrix
|
||||
Quaternion qParentToWorld;
|
||||
MatrixQuaternion( mSlaveParentToWorld, qParentToWorld );
|
||||
Quaternion qTmp;
|
||||
QuaternionMult( qParentToWorld, qOffsetOrientation, qTmp );
|
||||
qOffsetOrientation = qTmp;
|
||||
|
||||
// Blend between the target orientation and the base orientation using the target weight
|
||||
Quaternion qFinalOrientation;
|
||||
|
||||
matrix3x4a_t &mBaseWorldMatrix = boneToWorld.GetBoneForWrite( nBone );
|
||||
CStudioConstraintBones::ComputeBaseWorldMatrix( mBaseWorldMatrix, &( pProc->m_slave ), boneToWorld, pStudioHdr, pmViewTransform );
|
||||
|
||||
if ( flWeight < 1.0f )
|
||||
{
|
||||
Quaternion qBaseOrientation;
|
||||
MatrixQuaternion( mBaseWorldMatrix, qBaseOrientation );
|
||||
|
||||
QuaternionSlerp( qBaseOrientation, qOffsetOrientation, flWeight, qFinalOrientation );
|
||||
}
|
||||
else
|
||||
{
|
||||
qFinalOrientation = qOffsetOrientation;
|
||||
}
|
||||
|
||||
if ( pmViewTransform )
|
||||
{
|
||||
Quaternion qTmp0;
|
||||
Quaternion qTmp1;
|
||||
MatrixQuaternion( *pmViewTransform, qTmp0 );
|
||||
QuaternionMult( qTmp0, qFinalOrientation, qTmp1 );
|
||||
qFinalOrientation = qTmp1;
|
||||
}
|
||||
|
||||
// Quaternion matrix wipes out the translate component
|
||||
Vector vTmpPosition;
|
||||
MatrixPosition( mBaseWorldMatrix, vTmpPosition );
|
||||
QuaternionMatrix( qFinalOrientation, vTmpPosition, mBaseWorldMatrix );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// studio interface
|
||||
//-----------------------------------------------------------------------------
|
||||
void CStudioConstraintBones::ComputeParentConstraint(
|
||||
const mstudiobone_t *pBones,
|
||||
int nBone,
|
||||
CBoneAccessor &boneToWorld,
|
||||
const CStudioHdr *pStudioHdr )
|
||||
{
|
||||
BONE_PROFILE_FUNC();
|
||||
|
||||
mstudioorientconstraint_t *pProc = ( mstudioorientconstraint_t * )pBones[nBone].pProcedure();
|
||||
|
||||
// Calculate the current target position and the total weight
|
||||
// of the the targets contributing to the target position.
|
||||
Vector vTargetPosition;
|
||||
Quaternion qTargetOrientation;
|
||||
const float flWeight = CStudioConstraintBones::ComputeTargetPositionOrientation( vTargetPosition, qTargetOrientation, pProc->pTarget( 0 ), pProc->m_nTargetCount, boneToWorld );
|
||||
|
||||
// Blend between the target orientation and the base orientation using the target weight
|
||||
Quaternion qFinalOrientation;
|
||||
Vector vFinalPosition;
|
||||
|
||||
matrix3x4a_t &mBaseWorldMatrix = boneToWorld.GetBoneForWrite( nBone );
|
||||
CStudioConstraintBones::ComputeBaseWorldMatrix( mBaseWorldMatrix, &( pProc->m_slave ), boneToWorld, pStudioHdr );
|
||||
|
||||
if ( flWeight < 1.0f )
|
||||
{
|
||||
Vector vBasePosition;
|
||||
Quaternion qBaseOrientation;
|
||||
MatrixAngles( mBaseWorldMatrix, qBaseOrientation, vBasePosition );
|
||||
|
||||
QuaternionSlerp( qBaseOrientation, qTargetOrientation, flWeight, qFinalOrientation );
|
||||
VectorLerp( vBasePosition, vTargetPosition, flWeight, vFinalPosition );
|
||||
}
|
||||
else
|
||||
{
|
||||
qFinalOrientation = qTargetOrientation;
|
||||
vFinalPosition = vTargetPosition;
|
||||
}
|
||||
|
||||
QuaternionMatrix( qFinalOrientation, vFinalPosition, mBaseWorldMatrix );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Twist bones are bones which take a portion of the rotation around a specified
|
||||
// axis.
|
||||
//
|
||||
// The axis is defined as the vector between a parent and child bone
|
||||
// The twist bones must also be children of the parent
|
||||
//
|
||||
// + parent
|
||||
// |
|
||||
// +--+ twist 0.25
|
||||
// |
|
||||
// +--+ twist 0.5
|
||||
// |
|
||||
// +--+ twist 0.75
|
||||
// |
|
||||
// +--+ child
|
||||
//
|
||||
// If inverse is false each twist takes a portion of the child rotation around
|
||||
// the specified axis
|
||||
//
|
||||
// If inverse is true each twist takes a portion of the parent rotation around
|
||||
// the specified axis from a specified reference orientation
|
||||
//
|
||||
// All specified matrices & Quaternions are local to the bone, they are not
|
||||
// worldToBone transformations
|
||||
//
|
||||
// pqTwists, pflWeights, pqTwistBinds are all pointers to arrays which must be
|
||||
// at least nCount in size
|
||||
//
|
||||
// This code is called directly from:
|
||||
// maya, datamodel & CalcProceduralBone/DoTwistBones
|
||||
//-----------------------------------------------------------------------------
|
||||
void ComputeTwistBones(
|
||||
Quaternion *pqTwists,
|
||||
int nCount,
|
||||
bool bInverse,
|
||||
const Vector &vUp,
|
||||
const Quaternion &qParent,
|
||||
const matrix3x4_t &mChild,
|
||||
const Quaternion &qBaseInv,
|
||||
const float *pflWeights,
|
||||
const Quaternion *pqTwistBinds )
|
||||
{
|
||||
const float flEps = FLT_EPSILON * 10.0f;
|
||||
const float flEpsSq = flEps * flEps;
|
||||
|
||||
Vector vUpRotate;
|
||||
Vector vLocalTranslation;
|
||||
Vector vRotatedTranslation;
|
||||
Quaternion qTmp0;
|
||||
Quaternion qTmp1;
|
||||
|
||||
{
|
||||
Quaternion qChild;
|
||||
MatrixAngles( mChild, qChild, vLocalTranslation );
|
||||
|
||||
// Check for 0 length translation - perhaps use Vector::IsZero?
|
||||
if ( vLocalTranslation.LengthSqr() < flEpsSq )
|
||||
{
|
||||
// No translation, can't compute rotation axis, do nothing
|
||||
V_memcpy( pqTwists, pqTwistBinds, nCount * sizeof( Quaternion ) );
|
||||
return;
|
||||
}
|
||||
|
||||
VectorNormalize( vLocalTranslation );
|
||||
|
||||
if ( bInverse )
|
||||
{
|
||||
QuaternionMult( qBaseInv, qParent, qTmp0 );
|
||||
VectorRotate( vUp, qTmp0, vUpRotate );
|
||||
VectorRotate( vLocalTranslation, qTmp0, vRotatedTranslation );
|
||||
}
|
||||
else
|
||||
{
|
||||
QuaternionMult( qBaseInv, qChild, qTmp0 );
|
||||
VectorRotate( vUp, qTmp0, vUpRotate );
|
||||
VectorRotate( vLocalTranslation, qBaseInv, vRotatedTranslation );
|
||||
}
|
||||
}
|
||||
|
||||
// If the specified up axis and the rotated translation vector are parallel then quit
|
||||
if ( 1.0f - FloatMakePositive( DotProduct( vRotatedTranslation, vUp ) ) < flEps )
|
||||
{
|
||||
V_memcpy( pqTwists, pqTwistBinds, nCount * sizeof( Quaternion ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// If the rotated up axis and the rotated translation vector are parallel then quit
|
||||
if ( 1.0f - FloatMakePositive( DotProduct( vRotatedTranslation, vUpRotate ) ) < flEps )
|
||||
{
|
||||
V_memcpy( pqTwists, pqTwistBinds, nCount * sizeof( Quaternion ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Project Up (V) & Rotated Up (V) into the plane defined by the
|
||||
// rotated up vector (N)
|
||||
//
|
||||
// U = V - ( V dot N ) N;
|
||||
//
|
||||
// U is V projected into plane with normal N
|
||||
|
||||
Vector vTmp0;
|
||||
|
||||
vTmp0 = vRotatedTranslation;
|
||||
Vector vUpProject;
|
||||
vTmp0 *= DotProduct( vUp, vRotatedTranslation );
|
||||
VectorSubtract( vUp, vTmp0, vUpProject );
|
||||
VectorNormalize( vUpProject );
|
||||
|
||||
vTmp0 = vRotatedTranslation;
|
||||
Vector vUpRotateProject;
|
||||
vTmp0 *= DotProduct( vUpRotate, vRotatedTranslation );
|
||||
VectorSubtract( vUpRotate, vTmp0, vUpRotateProject );
|
||||
VectorNormalize( vUpRotateProject );
|
||||
|
||||
if ( VectorsAreEqual( vUpProject, vUpRotateProject, 0.001 ) )
|
||||
{
|
||||
V_memcpy( pqTwists, pqTwistBinds, nCount * sizeof( Quaternion ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
CrossProduct( vUpProject, vUpRotateProject, vTmp0 );
|
||||
VectorNormalize( vTmp0 );
|
||||
const float flDot = DotProduct( vUpProject, vUpRotateProject );
|
||||
const float flAngle = DotProduct( vTmp0, vRotatedTranslation ) < 0.0f ? -acos( flDot ) : acos( flDot );
|
||||
|
||||
AxisAngleQuaternion( vLocalTranslation, RAD2DEG( flAngle ), qTmp0 );
|
||||
|
||||
if ( bInverse )
|
||||
{
|
||||
for ( int i = 0; i < nCount; ++i )
|
||||
{
|
||||
QuaternionScale( qTmp0, pflWeights[i] - 1.0f, qTmp1 );
|
||||
QuaternionMult( qTmp1, pqTwistBinds[i], pqTwists[i] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < nCount; ++i )
|
||||
{
|
||||
QuaternionScale( qTmp0, pflWeights[i], qTmp1 );
|
||||
QuaternionMult( qTmp1, pqTwistBinds[i], pqTwists[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
|
||||
|
||||
DLL_IMPORT CLinkedMiniProfiler *g_pPhysicsMiniProfilers;
|
||||
#if 0
|
||||
#define BONE_PROFILE(ID) static CLinkedMiniProfiler s_miniprofiler_##ID(#ID, &g_pPhysicsMiniProfilers); \
|
||||
CMiniProfilerGuard mpguard_##ID(&s_miniprofiler_##ID);
|
||||
#define BONE_PROFILE_LOOP(ID,COUNT) static CLinkedMiniProfiler s_miniprofiler_##ID(#ID, &g_pPhysicsMiniProfilers); \
|
||||
CMiniProfilerGuard mpguard_##ID(&s_miniprofiler_##ID,(COUNT));
|
||||
#define BONE_PROFILE_FUNC() static CLinkedMiniProfiler s_miniprofiler_FUNC(__FUNCTION__, &g_pPhysicsMiniProfilers); \
|
||||
CMiniProfilerGuard mpguard_##ID(&s_miniprofiler_FUNC);
|
||||
#else
|
||||
#define BONE_PROFILE(ID)
|
||||
#define BONE_PROFILE_LOOP(ID,COUNT)
|
||||
#define BONE_PROFILE_FUNC()
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
class CBoneSetupMemoryPool
|
||||
{
|
||||
public:
|
||||
T *Alloc()
|
||||
{
|
||||
T *p = (T *)m_FreeBlocks.Pop();
|
||||
if ( !p )
|
||||
{
|
||||
p = (T *)MemAlloc_AllocAligned( sizeof( T ) * MAXSTUDIOBONES, 16 );
|
||||
if ( ((size_t)p) % MAX(TSLIST_NODE_ALIGNMENT,16) != 0 )
|
||||
DebuggerBreak();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void Free( T *p )
|
||||
{
|
||||
m_FreeBlocks.Push( (TSLNodeBase_t *)p );
|
||||
}
|
||||
|
||||
private:
|
||||
CTSListBase m_FreeBlocks;
|
||||
};
|
||||
|
||||
extern CBoneSetupMemoryPool<BoneQuaternionAligned> g_QuaternionPool;
|
||||
extern CBoneSetupMemoryPool<BoneVector> g_VectorPool;
|
||||
extern CBoneSetupMemoryPool<matrix3x4a_t> g_MatrixPool;
|
||||
|
||||
|
||||
|
||||
void CalcDecompressedAnimation( const mstudiocompressedikerror_t *pCompressed, int iFrame, float fraq, BoneVector &pos, BoneQuaternion &q );
|
||||
void QuaternionAccumulate( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt );
|
||||
void CalcAnimation( const CStudioHdr *pStudioHdr, BoneVector *pos, BoneQuaternion *q, mstudioseqdesc_t &seqdesc, int sequence, int animation, float cycle, int boneMask );
|
||||
void BlendBones( const CStudioHdr *pStudioHdr, BoneQuaternionAligned q1[MAXSTUDIOBONES], BoneVector pos1[MAXSTUDIOBONES], mstudioseqdesc_t &seqdesc, int sequence, const BoneQuaternionAligned q2[MAXSTUDIOBONES], const BoneVector pos2[MAXSTUDIOBONES], float s, int boneMask );
|
||||
void ScaleBones( const CStudioHdr *pStudioHdr, BoneQuaternion q1[MAXSTUDIOBONES], BoneVector pos1[MAXSTUDIOBONES], int sequence, float s, int boneMask );
|
||||
|
||||
void CalcPose( const CStudioHdr *pStudioHdr, CIKContext *pIKContext, BoneVector pos[], BoneQuaternionAligned q[], int sequence, float cycle, const float poseParameter[], int boneMask, float flWeight = 1.0f, float flTime = 0.0f );
|
||||
bool CalcPoseSingle( const CStudioHdr *pStudioHdr, BoneVector pos[], BoneQuaternionAligned q[], mstudioseqdesc_t &seqdesc, int sequence, float cycle, const float poseParameter[], int boneMask, float flTime );
|
||||
|
||||
void CalcBoneAdj( const CStudioHdr *pStudioHdr, BoneVector pos[], BoneQuaternion q[], const float controllers[], int boneMask );
|
||||
|
||||
void BuildBoneChainPartial(
|
||||
const CStudioHdr *pStudioHdr,
|
||||
const matrix3x4_t &rootxform,
|
||||
const BoneVector pos[],
|
||||
const BoneQuaternion q[],
|
||||
int iBone,
|
||||
matrix3x4_t *pBoneToWorld,
|
||||
CBoneBitList &boneComputed,
|
||||
int iRoot );
|
||||
|
||||
|
||||
class CBoneSetup
|
||||
{
|
||||
public:
|
||||
CBoneSetup( const CStudioHdr *pStudioHdr, int boneMask, const float poseParameter[], IPoseDebugger *pPoseDebugger = NULL );
|
||||
void InitPose( BoneVector pos[], BoneQuaternionAligned q[] );
|
||||
void AccumulatePose( BoneVector pos[], BoneQuaternion q[], int sequence, float cycle, float flWeight, float flTime, CIKContext *pIKContext );
|
||||
void CalcAutoplaySequences( BoneVector pos[], BoneQuaternion q[], float flRealTime, CIKContext *pIKContext );
|
||||
private:
|
||||
void AddSequenceLayers( BoneVector pos[], BoneQuaternion q[], mstudioseqdesc_t &seqdesc, int sequence, float cycle, float flWeight, float flTime, CIKContext *pIKContext );
|
||||
void AddLocalLayers( BoneVector pos[], BoneQuaternion q[], mstudioseqdesc_t &seqdesc, int sequence, float cycle, float flWeight, float flTime, CIKContext *pIKContext );
|
||||
public:
|
||||
const CStudioHdr *m_pStudioHdr;
|
||||
int m_boneMask;
|
||||
const float *m_flPoseParameter;
|
||||
IPoseDebugger *m_pPoseDebugger;
|
||||
};
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,359 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef BONE_UTILS_PS3_H
|
||||
#define BONE_UTILS_PS3_H
|
||||
|
||||
#ifndef _PS3
|
||||
#error "This header is for PS3 target only"
|
||||
#endif
|
||||
|
||||
#include "vjobs_interface.h"
|
||||
|
||||
#if defined(__SPU__)
|
||||
#include "ps3/spu_job_shared.h"
|
||||
#include "cell/dma.h"
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#define DotProduct_PS3 DotProduct
|
||||
#define MatrixAngles_PS3 MatrixAngles
|
||||
#define VectorRotate_PS3 VectorRotate
|
||||
#define VectorSubtract_PS3 VectorSubtract
|
||||
#define MatrixPosition_PS3 MatrixPosition
|
||||
#define _VMX_VectorNormalize_PS3 _VMX_VectorNormalize
|
||||
#define VectorNormalize_PS3 VectorNormalize
|
||||
#define VectorMultiply_PS3 VectorMultiply
|
||||
#define VectorScale_PS3 VectorScale
|
||||
#define VectorMAInline_PS3 VectorMAInline
|
||||
#define VectorMA_PS3 VectorMA
|
||||
#define SimpleSpline_PS3 SimpleSpline
|
||||
#define ConcatTransforms_PS3 ConcatTransforms
|
||||
#define ConcatTransforms_Aligned_PS3 ConcatTransforms_Aligned
|
||||
//#define QuaternionMatrix_PS3 QuaternionMatrix
|
||||
//#define QuaternionAlign_PS3 QuaternionAlign
|
||||
//#define QuaternionSlerp_PS3 QuaternionSlerp
|
||||
//#define QuaternionSlerpNoAlign_PS3 QuaternionSlerpNoAlign
|
||||
//#define QuaternionNormalize_PS3 QuaternionNormalize
|
||||
//#define QuaternionBlend_PS3 QuaternionBlend
|
||||
//#define QuaternionBlendNoAlign_PS3 QuaternionBlendNoAlign
|
||||
//#define QuaternionIdentityBlend_PS3 QuaternionIdentityBlend
|
||||
//#define QuaternionScale_PS3 QuaternionScale
|
||||
//#define QuaternionAdd_PS3 QuaternionAdd
|
||||
//#define QuaternionDotProduct_PS3 QuaternionDotProduct
|
||||
//#define QuaternionMult_PS3 QuaternionMult
|
||||
#define MatrixSetColumn_PS3 MatrixSetColumn
|
||||
#define MatrixGetColumn_PS3 MatrixGetColumn
|
||||
#define MatrixInvert_PS3 MatrixInvert
|
||||
#define VectorRotate_PS3 VectorRotate
|
||||
#define AngleMatrix_PS3 AngleMatrix
|
||||
#define AngleQuaternion_PS3 AngleQuaternion
|
||||
#define Hermite_Spline_PS3 Hermite_Spline
|
||||
#define Hermite_SplineBasis_PS3 Hermite_SplineBasis
|
||||
#define SetIdentityMatrix_PS3 SetIdentityMatrix
|
||||
|
||||
FORCEINLINE Vector Lerp_PS3( float flPercent, Vector const &A, Vector const &B )
|
||||
{
|
||||
return A + (B - A) * flPercent;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// from mathlib.h, mathlib_base.cpp
|
||||
#if 0
|
||||
FORCEINLINE float DotProduct_PS3(const Vector& a, const Vector& b)
|
||||
{
|
||||
// return( a.x*b.x + a.y*b.y + a.z*b.z );
|
||||
return a.Dot(b);
|
||||
}
|
||||
FORCEINLINE float DotProduct_PS3(const float* a, const float* b)
|
||||
{
|
||||
return( a[0]*b[0] + a[1]*b[1] + a[2]*b[2] );
|
||||
}
|
||||
void ConcatTransforms_PS3( const matrix3x4a_t &m0, const matrix3x4a_t &m1, matrix3x4a_t &out );
|
||||
void ConcatTransforms_Aligned_PS3( const matrix3x4a_t &m0, const matrix3x4a_t &m1, matrix3x4a_t &out );
|
||||
void MatrixAngles_PS3( const matrix3x4_t & matrix, float *angles ); // !!!!
|
||||
void MatrixAngles_PS3( const matrix3x4_t& matrix, RadianEuler &angles, Vector &position );
|
||||
void MatrixAngles_PS3( const matrix3x4_t &matrix, Quaternion &q, Vector &pos );
|
||||
inline void MatrixAngles_PS3( const matrix3x4_t &matrix, RadianEuler &angles )
|
||||
{
|
||||
MatrixAngles_PS3( matrix, &angles.x );
|
||||
|
||||
angles.Init( DEG2RAD( angles.z ), DEG2RAD( angles.x ), DEG2RAD( angles.y ) );
|
||||
}
|
||||
void MatrixGetColumn_PS3( const matrix3x4_t& in, int column, Vector &out );
|
||||
void MatrixSetColumn_PS3( const Vector &in, int column, matrix3x4_t& out );
|
||||
void MatrixInvert_PS3( const matrix3x4_t& in, matrix3x4_t& out );
|
||||
void VectorRotate_PS3( const float *in1, const matrix3x4_t & in2, float *out);
|
||||
inline void VectorRotate_PS3( const Vector& in1, const matrix3x4_t &in2, Vector &out)
|
||||
{
|
||||
VectorRotate_PS3( &in1.x, in2, &out.x );
|
||||
}
|
||||
FORCEINLINE void VectorSubtract_PS3( const Vector& a, const Vector& b, Vector& c )
|
||||
{
|
||||
c.x = a.x - b.x;
|
||||
c.y = a.y - b.y;
|
||||
c.z = a.z - b.z;
|
||||
}
|
||||
inline void MatrixPosition_PS3( const matrix3x4_t &matrix, Vector &position )
|
||||
{
|
||||
position[0] = matrix[0][3];
|
||||
position[1] = matrix[1][3];
|
||||
position[2] = matrix[2][3];
|
||||
}
|
||||
FORCEINLINE float _VMX_VectorNormalize_PS3( Vector &vec )
|
||||
{
|
||||
vec_float4 vIn;
|
||||
vec_float4 v0, v1;
|
||||
vector unsigned char permMask;
|
||||
v0 = vec_ld( 0, vec.Base() );
|
||||
permMask = vec_lvsl( 0, vec.Base() );
|
||||
v1 = vec_ld( 11, vec.Base() );
|
||||
vIn = vec_perm(v0, v1, permMask);
|
||||
float mag = vmathV3Length((VmathVector3 *)&vIn);
|
||||
float den = 1.f / (mag + FLT_EPSILON );
|
||||
vec.x *= den;
|
||||
vec.y *= den;
|
||||
vec.z *= den;
|
||||
return mag;
|
||||
}
|
||||
FORCEINLINE float VectorNormalize_PS3( Vector& v )
|
||||
{
|
||||
return _VMX_VectorNormalize_PS3( v );
|
||||
}
|
||||
|
||||
|
||||
FORCEINLINE void VectorMultiply_PS3( const Vector& a, float b, Vector& c )
|
||||
{
|
||||
c.x = a.x * b;
|
||||
c.y = a.y * b;
|
||||
c.z = a.z * b;
|
||||
}
|
||||
|
||||
FORCEINLINE void VectorMultiply_PS3( const Vector& a, const Vector& b, Vector& c )
|
||||
{
|
||||
c.x = a.x * b.x;
|
||||
c.y = a.y * b.y;
|
||||
c.z = a.z * b.z;
|
||||
}
|
||||
|
||||
inline void VectorScale_PS3 ( const Vector& in, float scale, Vector& result )
|
||||
{
|
||||
VectorMultiply_PS3( in, scale, result );
|
||||
}
|
||||
FORCEINLINE Vector Lerp_PS3( float flPercent, Vector const &A, Vector const &B )
|
||||
{
|
||||
return A + (B - A) * flPercent;
|
||||
}
|
||||
FORCEINLINE void VectorMAInline_PS3( const float* start, float scale, const float* direction, float* dest )
|
||||
{
|
||||
dest[0]=start[0]+direction[0]*scale;
|
||||
dest[1]=start[1]+direction[1]*scale;
|
||||
dest[2]=start[2]+direction[2]*scale;
|
||||
}
|
||||
FORCEINLINE void VectorMAInline_PS3( const Vector& start, float scale, const Vector& direction, Vector& dest )
|
||||
{
|
||||
dest.x=start.x+direction.x*scale;
|
||||
dest.y=start.y+direction.y*scale;
|
||||
dest.z=start.z+direction.z*scale;
|
||||
}
|
||||
FORCEINLINE void VectorMA_PS3( const Vector& start, float scale, const Vector& direction, Vector& dest )
|
||||
{
|
||||
VectorMAInline_PS3(start, scale, direction, dest);
|
||||
}
|
||||
FORCEINLINE void VectorMA_PS3( const float * start, float scale, const float *direction, float *dest )
|
||||
{
|
||||
VectorMAInline_PS3(start, scale, direction, dest);
|
||||
}
|
||||
void AngleMatrix_PS3( RadianEuler const &angles, const Vector &position, matrix3x4_t& matrix );
|
||||
void AngleMatrix_PS3( const RadianEuler& angles, matrix3x4_t& matrix );
|
||||
void AngleMatrix_PS3( const QAngle &angles, const Vector &position, matrix3x4_t& matrix );
|
||||
void AngleMatrix_PS3( const QAngle &angles, matrix3x4_t& matrix );
|
||||
void AngleQuaternion_PS3( const RadianEuler &angles, Quaternion &outQuat );
|
||||
void Hermite_Spline_PS3( const Vector &p1, const Vector &p2, const Vector &d1, const Vector &d2, float t, Vector& output );
|
||||
float Hermite_Spline_PS3( float p1, float p2, float d1, float d2, float t );
|
||||
void Hermite_SplineBasis_PS3( float t, float basis[4] );
|
||||
void Hermite_Spline_PS3( const Vector &p0, const Vector &p1, const Vector &p2, float t, Vector& output );
|
||||
float Hermite_Spline_PS3( float p0, float p1, float p2, float t );
|
||||
void Hermite_Spline_PS3( const Quaternion &q0, const Quaternion &q1, const Quaternion &q2, float t, Quaternion &output );
|
||||
inline float SimpleSpline_PS3( float value )
|
||||
{
|
||||
float valueSquared = value * value;
|
||||
|
||||
// Nice little ease-in, ease-out spline-like curve
|
||||
return (3.0f * valueSquared - 2.0f * valueSquared * value);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void QuaternionMatrix_PS3( const Quaternion &q, const Vector &pos, matrix3x4a_t& matrix );
|
||||
void QuaternionAlign_PS3( const Quaternion &p, const Quaternion &q, QuaternionAligned &qt );
|
||||
void QuaternionSlerp_PS3( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt );
|
||||
void QuaternionSlerpNoAlign_PS3( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt );
|
||||
float QuaternionNormalize_PS3( Quaternion &q );
|
||||
void QuaternionBlend_PS3( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt );
|
||||
void QuaternionBlendNoAlign_PS3( const Quaternion &p, const Quaternion &q, float t, Quaternion &qt );
|
||||
void QuaternionIdentityBlend_PS3( const Quaternion &p, float t, Quaternion &qt );
|
||||
void QuaternionScale_PS3( const Quaternion &p, float t, Quaternion &q );
|
||||
void QuaternionAdd_PS3( const Quaternion &p, const Quaternion &q, Quaternion &qt );
|
||||
float QuaternionDotProduct_PS3( const Quaternion &p, const Quaternion &q );
|
||||
void QuaternionMult_PS3( const Quaternion &p, const Quaternion &q, Quaternion &qt );
|
||||
|
||||
|
||||
|
||||
void AddDependencies_SPU( bonejob_SPU *pBonejobSPU, accumposeentry_SPU *pPoseEntry, float flWeight = 1.0f );
|
||||
//void AddDependencies_PPU( CStudioHdr *pHdr, float m_flTime, int m_boneMask, mstudioseqdesc_t &seqdesc, int iSequence, float flCycle, const float poseParameters[], float flWeight );
|
||||
|
||||
void GetBoneMapBoneWeight_SPU( bonejob_SPU *pSPUJob, accumposeentry_SPU *pPoseEntry, int *&pLS_boneMap, float *&pLS_boneWeight );
|
||||
|
||||
void CalcDecompressedAnimation_PS3( void *pEA_Compressed, const mstudiocompressedikerror_t_PS3 *pLS_Compressed, int iFrame, float fraq, BoneVector &pos, BoneQuaternion &q );
|
||||
void QuaternionAccumulate_PS3( const Quaternion &p, float s, const Quaternion &q, Quaternion &qt );
|
||||
void CalcAnimation_PS3( const bonejob_SPU *pBonejob, const accumposeentry_SPU *pPoseEntry, BoneVector *pos, BoneQuaternion *q, const int *boneMap, const float *boneWeight, int animIndex, float cycle, int boneMask );
|
||||
void BlendBones_PS3( const bonejob_SPU *pBonejob, const accumposeentry_SPU *pPoseEntry, BoneQuaternion *q1, BoneVector *pos1, const int *boneMap, const float *boneWeight, const BoneQuaternion *q2, const BoneVector *pos2, float s, int boneMask );
|
||||
void ScaleBones_PS3( const bonejob_SPU *pBonejob, const accumposeentry_SPU *pPoseEntry, BoneQuaternion *q1, BoneVector *pos1, const int *boneMap, const float *boneWeight, float s, int boneMask );
|
||||
|
||||
// void CalcPose( const CStudioHdr *pStudioHdr, CIKContext *pIKContext, Vector pos[], BoneQuaternion q[], int sequence, float cycle, const float poseParameter[], int boneMask, float flWeight = 1.0f, float flTime = 0.0f );
|
||||
//bool CalcPoseSingle( const CStudioHdr *pStudioHdr, Vector pos[], BoneQuaternion q[], mstudioseqdesc_t &seqdesc, int sequence, float cycle, const float poseParameter[], int boneMask, float flTime );
|
||||
|
||||
// void CalcBoneAdj( const CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], const float controllers[], int boneMask );
|
||||
|
||||
void BuildBoneChainPartial_PS3(
|
||||
const int *pBoneParent,
|
||||
const matrix3x4a_t &rootxform,
|
||||
const BoneVector pos[],
|
||||
const BoneQuaternion q[],
|
||||
int iBone,
|
||||
matrix3x4a_t *pBoneToWorld,
|
||||
CBoneBitList_PS3 &boneComputed,
|
||||
int iRoot );
|
||||
|
||||
struct PS3BoneJobData;
|
||||
|
||||
#define BONEJOB_ERROR_EXCEEDEDPQSTACK (1<<0)
|
||||
#define BONEJOB_ERROR_EXCEEDEDMAXCALLS (1<<1)
|
||||
#define BONEJOB_ERROR_LOCALHIER (1<<2)
|
||||
|
||||
#if !defined(__SPU__)
|
||||
|
||||
|
||||
class CBoneSetup_PS3
|
||||
{
|
||||
public:
|
||||
|
||||
CBoneSetup_PS3( const CStudioHdr *pStudioHdr, int boneMask, const float poseParameter[], bonejob_SPU *pBoneJobSPU );
|
||||
|
||||
void CalcAutoplaySequences_AddPoseCalls( float flRealTime );
|
||||
void AccumulatePose_AddToBoneJob( bonejob_SPU* pSPUJob, int sequence, float cycle, float flWeight, CIKContext *pIKContext, int pqStackLevel );
|
||||
|
||||
int RunAccumulatePoseJobs_PPU( bonejob_SPU *pBoneJob );
|
||||
int RunAccumulatePoseJobs_SPU( bonejob_SPU *pBoneJob, job_accumpose::JobDescriptor_t *pJobDescriptor );
|
||||
|
||||
private:
|
||||
|
||||
bool SetAnimData( accumposeentry_SPU *pPoseEntry, const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int sequence, int x, int y, int animIndex, float weight );
|
||||
|
||||
bool CalcPoseSingle( accumposeentry_SPU *pPoseEntry, const CStudioHdr *pStudioHdr, mstudioseqdesc_t &seqdesc, int sequence, float cycle, const float poseParameter[], float flTime );
|
||||
|
||||
int AddSequenceLayers( bonejob_SPU *pSPUJob, mstudioseqdesc_t &seqdesc, int sequence, float cycle, float flWeight, CIKContext *pIKContext, int pqStackLevel );
|
||||
int AddLocalLayers( bonejob_SPU *pSPUJob, mstudioseqdesc_t &seqdesc, int sequence, float cycle, float flWeight, CIKContext *pIKContext, int pqStackLevel );
|
||||
|
||||
public:
|
||||
|
||||
const CStudioHdr *m_pStudioHdr;
|
||||
int m_boneMask;
|
||||
const float *m_flPoseParameter;
|
||||
|
||||
bonejob_SPU *m_pBoneJobSPU;
|
||||
|
||||
int m_errorFlags; // accpose call failure flags (if so do not run on SPU, could be for a number of reasons, right now => exceeded PQ stack, or an anim uses the local hierarchy path
|
||||
};
|
||||
|
||||
|
||||
#endif // #if !defined(__SPU__)
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------
|
||||
// SPU dummy funcs
|
||||
//-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#define DMATAG_ANIM_SYNC_BONEMAPWEIGHT (DMATAG_ANIM+1)
|
||||
#define DMATAG_ANIM_SYNC_POSQ (DMATAG_ANIM+2)
|
||||
|
||||
FORCEINLINE void *SPUmemcpy_UnalignedGet( void *ls, uint32 ea, uint32_t size )
|
||||
{
|
||||
void *aligned_ls;
|
||||
|
||||
aligned_ls = (void *)((uint32)ls | (ea & 0xf)); // + 0xf in case ls not 16B aligned
|
||||
|
||||
#if defined(__SPU__)
|
||||
//spu_printf("GET ls:0x%x, ea:0x%x, size:%d\n", (uint32_t)aligned_ls, ea, size);
|
||||
// SPU
|
||||
cellDmaUnalignedGet( aligned_ls, ea, size, DMATAG_SYNC, 0, 0 );
|
||||
cellDmaWaitTagStatusAny( 1 << DMATAG_SYNC );
|
||||
#else
|
||||
// PPU
|
||||
memcpy( aligned_ls, (void *)ea, size );
|
||||
#endif
|
||||
|
||||
return aligned_ls;
|
||||
}
|
||||
|
||||
|
||||
FORCEINLINE void *SPUmemcpy_UnalignedGet_MustSync( void *ls, uint32 ea, uint32_t size, uint32_t dmatag )
|
||||
{
|
||||
void *aligned_ls;
|
||||
|
||||
aligned_ls = (void *)((uint32)ls | (ea & 0xf)); // + 0xf in case ls not 16B aligned
|
||||
|
||||
#if defined(__SPU__)
|
||||
//spu_printf("GET ls:0x%x, ea:0x%x, size:%d\n", (uint32_t)aligned_ls, ea, size);
|
||||
// SPU
|
||||
cellDmaUnalignedGet( aligned_ls, ea, size, dmatag, 0, 0 );
|
||||
#else
|
||||
// PPU
|
||||
memcpy( aligned_ls, (void *)ea, size );
|
||||
#endif
|
||||
|
||||
|
||||
return aligned_ls;
|
||||
}
|
||||
|
||||
|
||||
FORCEINLINE void SPUmemcpy_Sync( uint32_t dmatag )
|
||||
{
|
||||
#if defined(__SPU__)
|
||||
// cellDmaWaitTagStatusAll( 1 << dmatag );
|
||||
cellDmaWaitTagStatusAll( dmatag );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
FORCEINLINE void SPUmemcpy_UnalignedPut( void *ls, uint32 ea, uint32_t size )
|
||||
{
|
||||
#if defined(__SPU__)
|
||||
//spu_printf("PUT ls:0x%x, ea:0x%x, size:%d\n", (uint32_t)ls, ea, size);
|
||||
// SPU
|
||||
cellDmaUnalignedPut( ls, ea, size, DMATAG_SYNC, 0, 0 );
|
||||
cellDmaWaitTagStatusAny( 1 << DMATAG_SYNC );
|
||||
#else
|
||||
Assert(((uint32)ls&0xf) == ea&0xf);
|
||||
|
||||
// PPU
|
||||
memcpy( (void *)ea, ls, size );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================//
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// MATHLIB.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$macro SRCDIR ".."
|
||||
|
||||
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
|
||||
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE;..\public"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "bonesetup"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "bone_setup_PS3.cpp" [$PS3]
|
||||
$File "bone_ik_PS3.cpp" [$PS3]
|
||||
$File "bone_utils_PS3.cpp" [$PS3]
|
||||
$File "bone_decode_PS3.cpp" [$PS3]
|
||||
$File "bone_setup.cpp"
|
||||
$File "bone_ik.cpp"
|
||||
$File "bone_utils.cpp"
|
||||
$File "bone_decode.cpp"
|
||||
$File "bone_constraints.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "bone_utils.h"
|
||||
$File "bone_utils_PS3.h" [$PS3]
|
||||
}
|
||||
|
||||
|
||||
$Folder "Public Header Files"
|
||||
{
|
||||
$File "$SRCDIR\public\bone_setup.h" [$WINDOWS||$POSIX]
|
||||
$File "$SRCDIR\public\bone_setup_PS3.h" [$PS3]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "bonesetup.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "bonesetup.vcxproj"
|
||||
"1" "bonesetup.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
SN Visual Studio Integration
|
||||
IMPORTANT: Do not remove the custom build step for this file
|
||||
Reference in New Issue
Block a user