Files
FC1/CryCommon/StreamData.h
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

258 lines
7.4 KiB
C++

#pragma once
#include "ISystem.h" // ISystem
#include "IGame.h" // IGame
#include "IStreamEngine.h" // IStreamEngine
#if defined(LINUX)
// #include "stream.h"
#endif
// usable:
//
// CStreamData_WorldPos Vec3(x,y,z) world position with fallback if outside of typical area
// CStreamData_Normal normalized Vec3(x,y,z) in very well compressed in 16bit (cubemap side+2d pos on cubemap)
// next possible candidates:
//
// Direction Vec3, components in range -1..1
// Angle range: 0..2*PI
// EulerAnglesXYZ components in range: 0..2*PI
// EulerAnglesXZ components in range: 0..2*PI
//
// todo:
//
// test the whole system in multiplayer
// save stream compression mask in file or init 0 for load and save
// check if fallback is working
// -------------------------------------------------------------------------------------------------------------
class CStreamDataBase :public IStreamData
{
public:
//!
static bool DoCompression( const DWORD indwMask )
{
ISystem *pSystem=GetISystem(); assert(pSystem);
IGame *pGame=pSystem->GetIGame(); assert(pGame);
IStreamEngine *pStreamE=pSystem->GetStreamEngine(); assert(pStreamE);
if(!pGame->GetModuleState(EGameMultiplayer))
return false; // compression is only needed to save bandwidth
return (pStreamE->GetStreamCompressionMask()&indwMask) !=0;
}
};
// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------
// world position (always positive) 96 -> 59 bits for 2mm resolution
class CStreamData_WorldPos :public CStreamDataBase
{
Vec3 & m_vData; //!< reference to the Vec3 data
public: // --------------------------
//! constructor
CStreamData_WorldPos( Vec3 &inoutvValue ) :m_vData(inoutvValue)
{
}
//! to reproduce the quality loss form compression and decompression
Vec3 GetCompressed() const
{
CStream stm;
Write(stm);Read(stm);
return m_vData;
}
static bool IsActive()
{
ISystem *pSystem=GetISystem(); assert(pSystem);
IGame *pGame=pSystem->GetIGame(); assert(pGame);
// return DoCompression(0x1);
return pGame->GetModuleState(EGameMultiplayer); // compression is only needed to save bandwidth
}
// interface IStreamData -----------------------------
virtual bool Read( CStream &inStream ) const
{
if(!IsActive()) // same mask for read and write
return inStream.Read(m_vData);
bool bCompressed;
if(!inStream.Read(bCompressed)) return false;
if(!bCompressed)
return inStream.Read(m_vData);
DWORD x=0,y=0,z=0;
if(!inStream.ReadNumberInBits((unsigned int&)x, (size_t)20)) return false;
if(!inStream.ReadNumberInBits((unsigned int&)y, (size_t)20)) return false;
if(!inStream.ReadNumberInBits((unsigned int&)z, (size_t)18)) return false;
m_vData=Vec3((float)x,(float)y,(float)z) * 0.002f; // 2mm units
return true;
}
virtual bool Write( CStream &inStream ) const
{
if(!IsActive()) // same mask for read and write
return inStream.Write(m_vData);
DWORD x,y,z;
if(m_vData.x<0 || m_vData.x>2048.0f // do a fallback outside of the typical world area
|| m_vData.y<0 || m_vData.y>2048.0f
|| m_vData.z<0 || m_vData.z>512.0f)
{
if(!inStream.Write(false)) return false; // uncompressed
return inStream.Write(m_vData);
}
x=(DWORD)(m_vData.x*500.0f+0.5f); // 2mm units +0.5f for rounding
y=(DWORD)(m_vData.y*500.0f+0.5f);
z=(DWORD)(m_vData.z*500.0f+0.5f);
if(!inStream.Write(true)) return false; // compressed
if(!inStream.WriteNumberInBits((unsigned int)x, 20)) return false;
if(!inStream.WriteNumberInBits((unsigned int)y, 20)) return false;
if(!inStream.WriteNumberInBits((unsigned int)z, 18)) return false;
return true;
}
};
// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------
// low quality (16bit) normalized vector 96 -> 16 bits
// projected on a cube: x=0..104 x y=0..140 x side=0..5 -> 0.. 64896
class CStreamData_Normal :public CStreamDataBase
{
Vec3 & m_vData; //!< reference to the Vec3 data
public: // --------------------------
//! constructor
CStreamData_Normal( Vec3 &inoutvValue ) :m_vData(inoutvValue)
{
}
//! to reproduce the quality loss form compression and decompression
Vec3 GetCompressed() const
{
CStream stm;
Write(stm);Read(stm);
return m_vData;
}
static bool IsActive()
{
ISystem *pSystem=GetISystem(); assert(pSystem);
IGame *pGame = pSystem->GetIGame(); assert(pGame);
// return DoCompression(0x4);
return pGame->GetModuleState(EGameMultiplayer); // compression is only needed to save bandwidth
}
// interface IStreamData -----------------------------
virtual bool Read( CStream &inStream ) const
{
if(!IsActive()) // same mask for read and write
return inStream.Read(m_vData);
DWORD dwWord=0;
if(!inStream.ReadNumberInBits((unsigned int&)dwWord, (size_t)16)) return false;
DWORD dwDominantDirection=dwWord%6;
DWORD dwIntermediate=dwWord / 6; // 6 sides
DWORD dwX=dwIntermediate % 104; // 104 values on a cube size
DWORD dwY=dwIntermediate / 104; // 104 values on a cube size
float fX=(((float)dwX+0.5f)/(float)(104))*2.0f-1.0f;
float fY=(((float)dwY+0.5f)/(float)(104))*2.0f-1.0f;
switch(dwDominantDirection)
{
case 0: m_vData[0]=1.0f; m_vData[1]=fX; m_vData[2]=fY; break;
case 1: m_vData[0]=fY; m_vData[1]=1.0f; m_vData[2]=fX; break;
case 2: m_vData[0]=fX; m_vData[1]=fY; m_vData[2]=1.0f; break;
case 3: m_vData[0]=-1.0f; m_vData[1]=fX; m_vData[2]=fY; break;
case 4: m_vData[0]=fY; m_vData[1]=-1.0f; m_vData[2]=fX; break;
case 5: m_vData[0]=fX; m_vData[1]=fY; m_vData[2]=-1.0f; break;
default: assert(0);
}
m_vData.Normalize();
return true;
}
virtual bool Write( CStream &inStream ) const
{
if(!IsActive()) // same mask for read and write
return inStream.Write(m_vData);
int iDominantDirection=-1;
float vAbs[3]={ m_vData[0], m_vData[1], m_vData[2] };
// calc absolute value
if(vAbs[0]<0)vAbs[0]=-vAbs[0];
if(vAbs[1]<0)vAbs[1]=-vAbs[1];
if(vAbs[2]<0)vAbs[2]=-vAbs[2];
if(vAbs[0]>=vAbs[1] && vAbs[0]>=vAbs[2])iDominantDirection=0;
else
if(vAbs[1]>=vAbs[0] && vAbs[1]>=vAbs[2])iDominantDirection=1;
else
if(vAbs[2]>=vAbs[1] && vAbs[2]>=vAbs[0])iDominantDirection=2;
assert(iDominantDirection!=-1 && "this should never hapen or the vector has zero length");
if(iDominantDirection==-1)iDominantDirection=0;
float fX=m_vData[(iDominantDirection+1)%3];
float fY=m_vData[(iDominantDirection+2)%3];
float fZ=m_vData[iDominantDirection];
if(vAbs[iDominantDirection]!=m_vData[iDominantDirection])iDominantDirection+=3;
float fScale=1.0f/(float)fabs(fZ);
fX*=fScale;fY*=fScale;
DWORD dwX=(DWORD)((fX*0.5f+0.5f)*104); // 104 values on a cube size
DWORD dwY=(DWORD)((fY*0.5f+0.5f)*104); // 104 values on a cube size
if(dwX>=104)dwX=104-1; // 104 values on a cube size
if(dwY>=104)dwY=104-1; // 104 values on a cube size
DWORD dwWord=iDominantDirection+6*(dwX+104*dwY); // 104 values on a cube size
if(!inStream.WriteNumberInBits((unsigned int)dwWord, 16)) return false;
return true;
}
};