collide: Allow saving of Jolt collide binary state with base ivp collide header

Also restore CollideIndex method, saverestore system uses it
This commit is contained in:
SCell555 2023-08-08 20:05:15 +02:00
parent c63e5211b3
commit b63b4fbc69
No known key found for this signature in database
GPG Key ID: 601CBCAF382D165F
1 changed files with 196 additions and 97 deletions

View File

@ -95,7 +95,9 @@ float JoltPhysicsCollision::ConvexSurfaceArea( CPhysConvex *pConvex )
void JoltPhysicsCollision::SetConvexGameData( CPhysConvex *pConvex, unsigned int gameData )
{
pConvex->ToConvexShape()->SetUserData( gameData );
JPH::Shape *shape = pConvex->ToConvexShape();
const uint64 upper = shape->GetUserData() & 0xFFFFFFFF00000000;
shape->SetUserData( gameData | upper );
}
void JoltPhysicsCollision::ConvexFree( CPhysConvex *pConvex )
@ -203,26 +205,6 @@ void JoltPhysicsCollision::DestroyCollide( CPhysCollide *pCollide )
//-------------------------------------------------------------------------------------------------
int JoltPhysicsCollision::CollideSize( CPhysCollide *pCollide )
{
Log_Stub( LOG_VJolt );
return 0;
}
int JoltPhysicsCollision::CollideWrite( char *pDest, CPhysCollide *pCollide, bool bSwap /*= false*/ )
{
Log_Stub( LOG_VJolt );
return 0;
}
CPhysCollide *JoltPhysicsCollision::UnserializeCollide( char *pBuffer, int size, int index )
{
Log_Stub( LOG_VJolt );
return nullptr;
}
//-------------------------------------------------------------------------------------------------
float JoltPhysicsCollision::CollideVolume( CPhysCollide *pCollide )
{
return JoltToSource::Volume( pCollide->ToShape()->GetVolume() );
@ -315,9 +297,9 @@ void JoltPhysicsCollision::CollideSetOrthographicAreas( CPhysCollide *pCollide,
int JoltPhysicsCollision::CollideIndex( const CPhysCollide *pCollide )
{
// Slart: Only used by code behind #ifdef _DEBUG
Log_Stub( LOG_VJolt );
return 0;
const JPH::Shape *shape = pCollide->ToShape();
VJoltAssert( ( shape->GetType() == JPH::EShapeType::Convex && shape->GetSubType() == JPH::EShapeSubType::ConvexHull ) || ( shape->GetType() == JPH::EShapeType::Compound && shape->GetSubType() == JPH::EShapeSubType::StaticCompound ) );
return static_cast<int>( shape->GetUserData() >> 32 );
}
//-------------------------------------------------------------------------------------------------
@ -494,6 +476,7 @@ namespace ivp_compat
static constexpr int IVP_COMPACT_SURFACE_ID_SWAPPED = MAKEID('S','P','V','I');
static constexpr int IVP_COMPACT_MOPP_ID = MAKEID('M','O','P','P');
static constexpr int VPHYSICS_COLLISION_ID = MAKEID('V','P','H','Y');
static constexpr int JOLT_COLLISION_ID = MAKEID('J','P','H','Y');
static constexpr short VPHYSICS_COLLISION_VERSION = 0x0100;
enum
@ -504,7 +487,7 @@ namespace ivp_compat
COLLIDE_VIRTUAL = 3,
};
JPH::ConvexShape *IVPLedgeToConvexShape( const compactledge_t *pLedge )
JPH::ConvexShape *IVPLedgeToConvexShape( const compactledge_t *pLedge, uint64 userData )
{
if ( !pLedge->n_triangles )
return nullptr;
@ -550,7 +533,7 @@ namespace ivp_compat
}
}
pConvexShape->SetUserData( pLedge->client_data );
pConvexShape->SetUserData( pLedge->client_data | userData );
return pConvexShape;
}
@ -568,7 +551,7 @@ namespace ivp_compat
vecOut.AddToTail( pNode->GetCompactLedge() );
}
CPhysCollide *DeserializeIVP_Poly( const compactsurface_t* pSurface )
CPhysCollide *DeserializeIVP_Poly( const compactsurface_t* pSurface, uint64 userData )
{
const compactledgenode_t *pFirstLedgeNode = reinterpret_cast< const compactledgenode_t * >(
reinterpret_cast< const char * >( pSurface ) + pSurface->offset_ledgetree_root );
@ -584,29 +567,138 @@ namespace ivp_compat
// One compound convex per ledge.
for ( int i = 0; i < ledges.Count(); i++ )
{
const JPH::Shape* pShape = IVPLedgeToConvexShape( ledges[i] );
const JPH::Shape* pShape = IVPLedgeToConvexShape( ledges[i], userData );
// Josh:
// Some models have degenerate convexes which fails to make
// a subshape in Jolt, so we need to ignore those ledges.
if ( pShape )
settings.AddShape( JPH::Vec3::sZero(), JPH::Quat::sIdentity(), pShape );
}
CPhysCollide* pCollide = ShapeSettingsToPhysCollide( settings );
return pCollide;
JPH::Shape *shape = ShapeSettingsToShape<JPH::Shape>( settings );
shape->SetUserData( userData );
return CPhysCollide::FromShape( shape );
}
else
{
JPH::ConvexShape *pShape = IVPLedgeToConvexShape( ledges[ 0 ] );
return CPhysConvex::FromConvexShape( pShape )->ToPhysCollide();
JPH::ConvexShape *pShape = IVPLedgeToConvexShape( ledges[ 0 ], userData );
return CPhysCollide::FromShape( pShape );
}
}
CPhysCollide *DeserializeIVP_Poly( const collideheader_t *pCollideHeader )
CPhysCollide *DeserializeIVP_Poly( const collideheader_t *pCollideHeader, uint64 userData )
{
const compactsurfaceheader_t *pSurfaceHeader = reinterpret_cast< const compactsurfaceheader_t* >( pCollideHeader + 1 );
const compactsurface_t *pSurface = reinterpret_cast< const compactsurface_t* >( pSurfaceHeader + 1 );
return DeserializeIVP_Poly( pSurface );
return DeserializeIVP_Poly( pSurface, userData );
}
CPhysCollide *Deserialize( const char *pCursor, int solidSize, unsigned int i )
{
const collideheader_t *pCollideHeader = reinterpret_cast<const collideheader_t *>( pCursor );
uint64 userData = static_cast<uint64>( i ) << 32ULL;
if ( pCollideHeader->vphysicsID == VPHYSICS_COLLISION_ID )
{
// This is the main path that everything falls down for a modern
// .phy file with the collide header.
if ( pCollideHeader->version != VPHYSICS_COLLISION_VERSION )
Log_Warning( LOG_VJolt, "Solid with unknown version: 0x%x, may crash!\n", pCollideHeader->version );
switch ( pCollideHeader->modelType )
{
case COLLIDE_POLY:
return DeserializeIVP_Poly( pCollideHeader, userData );
break;
case COLLIDE_MOPP:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_MOPP on solid %d. Skipping...\n", i );
break;
case COLLIDE_BALL:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_BALL on solid %d. Skipping...\n", i );
break;
case COLLIDE_VIRTUAL:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_VIRTUAL on solid %d. Skipping...\n", i );
break;
default:
Log_Warning( LOG_VJolt, "Unsupported solid type 0x%x on solid %d. Skipping...\n", (int)pCollideHeader->modelType, i );
break;
}
}
else if ( pCollideHeader->vphysicsID == JOLT_COLLISION_ID )
{
VJoltAssert( pCollideHeader->version == 1 );
struct Reader : JPH::StreamIn, CUtlBuffer
{
using CUtlBuffer::CUtlBuffer;
void ReadBytes( void *outData, size_t inNumBytes ) override
{
Get( outData, inNumBytes );
}
bool IsEOF() const override
{
return TellGet() == TellMaxPut();
}
bool IsFailed() const override
{
return !IsValid();
}
} reader( pCollideHeader + 1, solidSize - sizeof( collideheader_t ), CUtlBuffer::READ_ONLY );
JPH::Shape::ShapeResult shape = JPH::Shape::sRestoreFromBinaryState( reader );
if ( shape.HasError() )
{
Log_Warning( LOG_VJolt, "Jolt collide deserialize error: %s\n", shape.GetError().c_str() );
return nullptr;
}
const int subShapeCount = reader.GetInt();
JPH::ShapeList subShapes;
for ( int j = 0; j < subShapeCount; ++j )
{
JPH::Shape::ShapeResult subShape = JPH::Shape::sRestoreFromBinaryState( reader );
if ( subShape.HasError() )
{
Log_Warning( LOG_VJolt, "Jolt collide deserialize error: %s\n", shape.GetError().c_str() );
return nullptr;
}
subShapes.emplace_back( subShape.Get() );
}
shape.Get()->RestoreSubShapeState( subShapes.data(), subShapeCount );
return CPhysCollide::FromShape( ToDanglingRef( shape.Get() ) );
}
else
{
// This must be a legacy style .phy where it is just a dumped compact surface.
// Some props in shipping HL2 still use this format, as they have a .phy, even after their
// .qc had the $collisionmodel removed, as they didn't get the stale .phy in the game files deleted.
const compactsurface_t *pCompactSurface = reinterpret_cast<const compactsurface_t *>( pCursor );
const int legacyModelType = pCompactSurface->dummy[2];
switch ( legacyModelType )
{
case IVP_COMPACT_SURFACE_SUPER_LEGACY:
case IVP_COMPACT_SURFACE_ID:
case IVP_COMPACT_SURFACE_ID_SWAPPED:
return DeserializeIVP_Poly( pCompactSurface, userData );
break;
case IVP_COMPACT_MOPP_ID:
Log_Warning( LOG_VJolt, "Unsupported legacy solid type IVP_COMPACT_MOPP_ID on solid %d. Skipping...\n", i );
break;
default:
Log_Warning( LOG_VJolt, "Unsupported legacy solid type 0x%x on solid %d. Skipping...\n", legacyModelType, i);
break;
}
}
return nullptr;
}
}
@ -624,71 +716,10 @@ void JoltPhysicsCollision::VCollideLoad( vcollide_t *pOutput, int solidCount, co
const char *pCursor = pBuffer;
for ( int i = 0; i < solidCount; i++ )
{
// Be safe ahead of time as so much can go wrong with
// this mess! :p
pOutput->solids[ i ] = nullptr;
const int solidSize = *reinterpret_cast<const int *>( pCursor );
pCursor += sizeof( int );
const ivp_compat::collideheader_t *pCollideHeader = reinterpret_cast<const ivp_compat::collideheader_t *>( pCursor );
if ( pCollideHeader->vphysicsID == ivp_compat::VPHYSICS_COLLISION_ID )
{
// This is the main path that everything falls down for a modern
// .phy file with the collide header.
if ( pCollideHeader->version != ivp_compat::VPHYSICS_COLLISION_VERSION )
Log_Warning( LOG_VJolt, "Solid with unknown version: 0x%x, may crash!\n", pCollideHeader->version );
switch ( pCollideHeader->modelType )
{
case ivp_compat::COLLIDE_POLY:
pOutput->solids[ i ] = DeserializeIVP_Poly( pCollideHeader );
break;
case ivp_compat::COLLIDE_MOPP:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_MOPP on solid %d. Skipping...\n", i );
break;
case ivp_compat::COLLIDE_BALL:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_BALL on solid %d. Skipping...\n", i );
break;
case ivp_compat::COLLIDE_VIRTUAL:
Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_VIRTUAL on solid %d. Skipping...\n", i );
break;
default:
Log_Warning( LOG_VJolt, "Unsupported solid type 0x%x on solid %d. Skipping...\n", (int)pCollideHeader->modelType, i );
break;
}
}
else
{
// This must be a legacy style .phy where it is just a dumped compact surface.
// Some props in shipping HL2 still use this format, as they have a .phy, even after their
// .qc had the $collisionmodel removed, as they didn't get the stale .phy in the game files deleted.
const ivp_compat::compactsurface_t *pCompactSurface = reinterpret_cast<const ivp_compat::compactsurface_t *>( pCursor );
const int legacyModelType = pCompactSurface->dummy[2];
switch ( legacyModelType )
{
case ivp_compat::IVP_COMPACT_SURFACE_SUPER_LEGACY:
case ivp_compat::IVP_COMPACT_SURFACE_ID:
case ivp_compat::IVP_COMPACT_SURFACE_ID_SWAPPED:
pOutput->solids[i] = DeserializeIVP_Poly( pCompactSurface );
break;
case ivp_compat::IVP_COMPACT_MOPP_ID:
Log_Warning( LOG_VJolt, "Unsupported legacy solid type IVP_COMPACT_MOPP_ID on solid %d. Skipping...\n", i );
break;
default:
Log_Warning( LOG_VJolt, "Unsupported legacy solid type 0x%x on solid %d. Skipping...\n", legacyModelType, i);
break;
}
}
pOutput->solids[ i ] = ivp_compat::Deserialize( pCursor, solidSize, i );
pCursor += solidSize;
}
@ -705,7 +736,6 @@ void JoltPhysicsCollision::VCollideLoad( vcollide_t *pOutput, int solidCount, co
#ifdef GAME_ASW_OR_NEWER
pOutput->pUserData = nullptr;
#endif
}
void JoltPhysicsCollision::VCollideUnload( vcollide_t *pVCollide )
@ -719,6 +749,75 @@ void JoltPhysicsCollision::VCollideUnload( vcollide_t *pVCollide )
V_memset( pVCollide, 0, sizeof( *pVCollide ) );
}
int JoltPhysicsCollision::CollideSize( CPhysCollide *pCollide )
{
struct SizeCounter : JPH::StreamOut
{
void WriteBytes( const void *, size_t inNumBytes ) override
{
bytes += inNumBytes;
}
bool IsFailed() const override
{
return false;
}
size_t bytes = 0;
} counter;
counter.bytes += sizeof( ivp_compat::collideheader_t );
pCollide->ToShape()->SaveBinaryState( counter );
counter.bytes += sizeof( int );
JPH::ShapeList subShapes;
pCollide->ToShape()->SaveSubShapeState( subShapes );
for ( const auto &s : subShapes )
s->SaveBinaryState( counter );
return static_cast<int>( counter.bytes );
}
int JoltPhysicsCollision::CollideWrite( char *pDest, CPhysCollide *pCollide, bool bSwap /*= false*/ )
{
if ( bSwap )
{
Log_Error( LOG_VJolt, "If you got here. Tell me what you did!\n" );
return 0;
}
struct Writer : JPH::StreamOut, CUtlBuffer
{
using CUtlBuffer::CUtlBuffer;
void WriteBytes( const void *inData, size_t inNumBytes ) override
{
Put( inData, inNumBytes );
}
bool IsFailed() const override
{
return !IsValid();
}
} writer;
constexpr ivp_compat::collideheader_t header { ivp_compat::JOLT_COLLISION_ID, 1, 0 };
writer.Put( &header, sizeof( header ) );
pCollide->ToShape()->SaveBinaryState( writer );
JPH::ShapeList subShapes;
writer.PutInt( static_cast<int>( subShapes.size() ) );
pCollide->ToShape()->SaveSubShapeState( subShapes );
for ( const auto &s : subShapes )
s->SaveBinaryState( writer );
memcpy( pDest, writer.Base(), writer.TellMaxPut() );
return writer.TellMaxPut();
}
CPhysCollide *JoltPhysicsCollision::UnserializeCollide( char *pBuffer, int size, int index )
{
return ivp_compat::Deserialize( pBuffer, size, index );
}
//-------------------------------------------------------------------------------------------------
IVPhysicsKeyParser *JoltPhysicsCollision::VPhysicsKeyParserCreate( const char *pKeyData )