Skip to content

Commit

Permalink
Merge pull request ddnet#5737 from Robyt3/Map-Format-Port
Browse files Browse the repository at this point in the history
Support bezier envelope curves in maps and editor, forward compatiblity for upstream map item changes
  • Loading branch information
def- authored Jul 16, 2023
2 parents 4a0c850 + 54df98b commit 8d12bce
Show file tree
Hide file tree
Showing 20 changed files with 1,129 additions and 279 deletions.
14 changes: 14 additions & 0 deletions src/base/math.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ constexpr inline T mix(const T a, const T b, TB amount)
return a + (b - a) * amount;
}

template<typename T, typename TB>
inline T bezier(const T p0, const T p1, const T p2, const T p3, TB amount)
{
// De-Casteljau Algorithm
const T c10 = mix(p0, p1, amount);
const T c11 = mix(p1, p2, amount);
const T c12 = mix(p2, p3, amount);

const T c20 = mix(c10, c11, amount);
const T c21 = mix(c11, c12, amount);

return mix(c20, c21, amount); // c30
}

inline float random_float()
{
return rand() / (float)(RAND_MAX);
Expand Down
51 changes: 43 additions & 8 deletions src/engine/shared/datafile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ struct CDatafile
CDatafileHeader m_Header;
int m_DataStartOffset;
char **m_ppDataPtrs;
int *m_pDataSizes;
char *m_pData;
};

Expand Down Expand Up @@ -163,18 +164,27 @@ bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename, int
unsigned AllocSize = Size;
AllocSize += sizeof(CDatafile); // add space for info structure
AllocSize += Header.m_NumRawData * sizeof(void *); // add space for data pointers
AllocSize += Header.m_NumRawData * sizeof(int); // add space for data sizes
if(Size > (((int64_t)1) << 31) || Header.m_NumItemTypes < 0 || Header.m_NumItems < 0 || Header.m_NumRawData < 0 || Header.m_ItemSize < 0)
{
io_close(File);
dbg_msg("datafile", "unable to load file, invalid file information");
return false;
}

CDatafile *pTmpDataFile = (CDatafile *)malloc(AllocSize);
pTmpDataFile->m_Header = Header;
pTmpDataFile->m_DataStartOffset = sizeof(CDatafileHeader) + Size;
pTmpDataFile->m_ppDataPtrs = (char **)(pTmpDataFile + 1);
pTmpDataFile->m_pData = (char *)(pTmpDataFile + 1) + Header.m_NumRawData * sizeof(char *);
pTmpDataFile->m_pDataSizes = (int *)(pTmpDataFile->m_ppDataPtrs + Header.m_NumRawData);
pTmpDataFile->m_pData = (char *)(pTmpDataFile->m_pDataSizes + Header.m_NumRawData);
pTmpDataFile->m_File = File;
pTmpDataFile->m_Sha256 = Sha256;
pTmpDataFile->m_Crc = Crc;

// clear the data pointers
// clear the data pointers and sizes
mem_zero(pTmpDataFile->m_ppDataPtrs, Header.m_NumRawData * sizeof(void *));
mem_zero(pTmpDataFile->m_pDataSizes, Header.m_NumRawData * sizeof(int));

// read types, offsets, sizes and item data
unsigned ReadSize = io_read(File, pTmpDataFile->m_pData, Size);
Expand Down Expand Up @@ -224,7 +234,11 @@ bool CDataFileReader::Close()

// free the data that is loaded
for(int i = 0; i < m_pDataFile->m_Header.m_NumRawData; i++)
{
free(m_pDataFile->m_ppDataPtrs[i]);
m_pDataFile->m_ppDataPtrs[i] = nullptr;
m_pDataFile->m_pDataSizes[i] = 0;
}

io_close(m_pDataFile->m_File);
free(m_pDataFile);
Expand Down Expand Up @@ -258,21 +272,30 @@ int CDataFileReader::GetFileDataSize(int Index) const

if(Index == m_pDataFile->m_Header.m_NumRawData - 1)
return m_pDataFile->m_Header.m_DataSize - m_pDataFile->m_Info.m_pDataOffsets[Index];

return m_pDataFile->m_Info.m_pDataOffsets[Index + 1] - m_pDataFile->m_Info.m_pDataOffsets[Index];
}

// returns the size of the resulting data
int CDataFileReader::GetDataSize(int Index) const
{
if(!m_pDataFile)
if(!m_pDataFile || Index < 0 || Index >= m_pDataFile->m_Header.m_NumRawData)
{
return 0;
}

if(m_pDataFile->m_Header.m_Version == 4)
return m_pDataFile->m_Info.m_pDataSizes[Index];
else
return GetFileDataSize(Index);
if(!m_pDataFile->m_ppDataPtrs[Index])
{
if(m_pDataFile->m_Header.m_Version >= 4)
{
return m_pDataFile->m_Info.m_pDataSizes[Index];
}
else
{
return GetFileDataSize(Index);
}
}
return m_pDataFile->m_pDataSizes[Index];
}

void *CDataFileReader::GetDataImpl(int Index, int Swap)
Expand Down Expand Up @@ -303,6 +326,7 @@ void *CDataFileReader::GetDataImpl(int Index, int Swap)

log_trace("datafile", "loading data index=%d size=%d uncompressed=%lu", Index, DataSize, UncompressedSize);
m_pDataFile->m_ppDataPtrs[Index] = (char *)malloc(UncompressedSize);
m_pDataFile->m_pDataSizes[Index] = UncompressedSize;

// read the compressed data
io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset + m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START);
Expand All @@ -323,6 +347,7 @@ void *CDataFileReader::GetDataImpl(int Index, int Swap)
// load the data
log_trace("datafile", "loading data index=%d size=%d", Index, DataSize);
m_pDataFile->m_ppDataPtrs[Index] = (char *)malloc(DataSize);
m_pDataFile->m_pDataSizes[Index] = DataSize;
io_seek(m_pDataFile->m_File, m_pDataFile->m_DataStartOffset + m_pDataFile->m_Info.m_pDataOffsets[Index], IOSEEK_START);
io_read(m_pDataFile->m_File, m_pDataFile->m_ppDataPtrs[Index], DataSize);
}
Expand All @@ -346,14 +371,24 @@ void *CDataFileReader::GetDataSwapped(int Index)
return GetDataImpl(Index, 1);
}

void CDataFileReader::ReplaceData(int Index, char *pData, size_t Size)
{
// make sure the data has been loaded
GetDataImpl(Index, 0);

UnloadData(Index);
m_pDataFile->m_ppDataPtrs[Index] = pData;
m_pDataFile->m_pDataSizes[Index] = Size;
}

void CDataFileReader::UnloadData(int Index)
{
if(Index < 0 || Index >= m_pDataFile->m_Header.m_NumRawData)
return;

//
free(m_pDataFile->m_ppDataPtrs[Index]);
m_pDataFile->m_ppDataPtrs[Index] = nullptr;
m_pDataFile->m_pDataSizes[Index] = 0;
}

int CDataFileReader::GetItemSize(int Index) const
Expand Down
1 change: 1 addition & 0 deletions src/engine/shared/datafile.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class CDataFileReader
void *GetData(int Index);
void *GetDataSwapped(int Index); // makes sure that the data is 32bit LE ints when saved
int GetDataSize(int Index) const;
void ReplaceData(int Index, char *pData, size_t Size);
void UnloadData(int Index);
int NumData() const;

Expand Down
52 changes: 51 additions & 1 deletion src/engine/shared/map.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "map.h"

#include <engine/storage.h>

#include <game/mapitems.h>

CMap::CMap() = default;

void *CMap::GetData(int Index)
Expand Down Expand Up @@ -60,7 +63,38 @@ bool CMap::Load(const char *pMapName)
IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
if(!pStorage)
return false;
return m_DataFile.Open(pStorage, pMapName, IStorage::TYPE_ALL);
if(!m_DataFile.Open(pStorage, pMapName, IStorage::TYPE_ALL))
return false;
// check version
const CMapItemVersion *pItem = (CMapItemVersion *)m_DataFile.FindItem(MAPITEMTYPE_VERSION, 0);
if(!pItem || pItem->m_Version != CMapItemVersion::CURRENT_VERSION)
return false;

// replace compressed tile layers with uncompressed ones
int GroupsStart, GroupsNum, LayersStart, LayersNum;
m_DataFile.GetType(MAPITEMTYPE_GROUP, &GroupsStart, &GroupsNum);
m_DataFile.GetType(MAPITEMTYPE_LAYER, &LayersStart, &LayersNum);
for(int g = 0; g < GroupsNum; g++)
{
const CMapItemGroup *pGroup = static_cast<CMapItemGroup *>(m_DataFile.GetItem(GroupsStart + g));
for(int l = 0; l < pGroup->m_NumLayers; l++)
{
CMapItemLayer *pLayer = static_cast<CMapItemLayer *>(m_DataFile.GetItem(LayersStart + pGroup->m_StartLayer + l));
if(pLayer->m_Type == LAYERTYPE_TILES)
{
CMapItemLayerTilemap *pTilemap = reinterpret_cast<CMapItemLayerTilemap *>(pLayer);
if(pTilemap->m_Version >= CMapItemLayerTilemap::TILE_SKIP_MIN_VERSION)
{
const size_t TilemapSize = (size_t)pTilemap->m_Width * pTilemap->m_Height * sizeof(CTile);
CTile *pTiles = static_cast<CTile *>(malloc(TilemapSize));
ExtractTiles(pTiles, (size_t)pTilemap->m_Width * pTilemap->m_Height, static_cast<CTile *>(m_DataFile.GetData(pTilemap->m_Data)), m_DataFile.GetDataSize(pTilemap->m_Data) / sizeof(CTile));
m_DataFile.ReplaceData(pTilemap->m_Data, reinterpret_cast<char *>(pTiles), TilemapSize);
}
}
}
}

return true;
}

void CMap::Unload()
Expand Down Expand Up @@ -93,4 +127,20 @@ int CMap::MapSize() const
return m_DataFile.MapSize();
}

void CMap::ExtractTiles(CTile *pDest, size_t DestSize, const CTile *pSrc, size_t SrcSize)
{
size_t DestIndex = 0;
size_t SrcIndex = 0;
while(DestIndex < DestSize && SrcIndex < SrcSize)
{
for(unsigned Counter = 0; Counter <= pSrc[SrcIndex].m_Skip && DestIndex < DestSize; Counter++)
{
pDest[DestIndex] = pSrc[SrcIndex];
pDest[DestIndex].m_Skip = 0;
DestIndex++;
}
SrcIndex++;
}
}

extern IEngineMap *CreateEngineMap() { return new CMap; }
4 changes: 4 additions & 0 deletions src/engine/shared/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class CMap : public IEngineMap
public:
CMap();

CDataFileReader *GetReader() { return &m_DataFile; }

void *GetData(int Index) override;
int GetDataSize(int Index) const override;
void *GetDataSwapped(int Index) override;
Expand All @@ -35,6 +37,8 @@ class CMap : public IEngineMap
SHA256_DIGEST Sha256() const override;
unsigned Crc() const override;
int MapSize() const override;

static void ExtractTiles(class CTile *pDest, size_t DestSize, const class CTile *pSrc, size_t SrcSize);
};

#endif
9 changes: 6 additions & 3 deletions src/game/client/components/mapimages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,21 +105,24 @@ void CMapImages::OnMapLoadImpl(class CLayers *pLayers, IMap *pMap)
for(int i = 0; i < m_Count; i++)
{
int LoadFlag = (((m_aTextureUsedByTileOrQuadLayerFlag[i] & 1) != 0) ? TextureLoadFlag : 0) | (((m_aTextureUsedByTileOrQuadLayerFlag[i] & 2) != 0) ? 0 : (Graphics()->IsTileBufferingEnabled() ? IGraphics::TEXLOAD_NO_2D_TEXTURE : 0));
CMapItemImage *pImg = (CMapItemImage *)pMap->GetItem(Start + i);
if(pImg->m_External)
const CMapItemImage_v2 *pImg = (CMapItemImage_v2 *)pMap->GetItem(Start + i);
const int Format = pImg->m_Version < CMapItemImage_v2::CURRENT_VERSION ? CImageInfo::FORMAT_RGBA : pImg->m_Format;
if(pImg->m_External || (Format != CImageInfo::FORMAT_RGB && Format != CImageInfo::FORMAT_RGBA))
{
char aPath[IO_MAX_PATH_LENGTH];
char *pName = (char *)pMap->GetData(pImg->m_ImageName);
str_format(aPath, sizeof(aPath), "mapres/%s.png", pName);
m_aTextures[i] = Graphics()->LoadTexture(aPath, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, LoadFlag);
pMap->UnloadData(pImg->m_ImageName);
}
else
{
void *pData = pMap->GetData(pImg->m_ImageData);
char *pName = (char *)pMap->GetData(pImg->m_ImageName);
char aTexName[128];
str_format(aTexName, sizeof(aTexName), "%s %s", "embedded:", pName);
m_aTextures[i] = Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, CImageInfo::FORMAT_RGBA, pData, CImageInfo::FORMAT_RGBA, LoadFlag, aTexName);
m_aTextures[i] = Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, Format, pData, CImageInfo::FORMAT_RGBA, LoadFlag, aTexName);
pMap->UnloadData(pImg->m_ImageName);
pMap->UnloadData(pImg->m_ImageData);
}
}
Expand Down
22 changes: 8 additions & 14 deletions src/game/client/components/maplayers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <game/layers.h>
#include <game/mapitems.h>
#include <game/mapitems_ex.h>

#include <game/client/components/camera.h>
#include <game/client/components/mapimages.h>
Expand Down Expand Up @@ -60,22 +61,15 @@ void CMapLayers::EnvelopeEval(int TimeOffsetMillis, int Env, ColorRGBA &Channels
CMapLayers *pThis = (CMapLayers *)pUser;
Channels = ColorRGBA();

CEnvPoint *pPoints = 0;
const CMapBasedEnvelopePointAccess EnvelopePoints(pThis->m_pLayers->Map());

{
int Start, Num;
pThis->m_pLayers->Map()->GetType(MAPITEMTYPE_ENVPOINTS, &Start, &Num);
if(Num)
pPoints = (CEnvPoint *)pThis->m_pLayers->Map()->GetItem(Start);
}

int Start, Num;
pThis->m_pLayers->Map()->GetType(MAPITEMTYPE_ENVELOPE, &Start, &Num);
int EnvStart, EnvNum;
pThis->m_pLayers->Map()->GetType(MAPITEMTYPE_ENVELOPE, &EnvStart, &EnvNum);

if(Env >= Num)
if(EnvelopePoints.NumPoints() == 0 || Env < 0 || Env >= EnvNum)
return;

CMapItemEnvelope *pItem = (CMapItemEnvelope *)pThis->m_pLayers->Map()->GetItem(Start + Env);
const CMapItemEnvelope *pItem = (CMapItemEnvelope *)pThis->m_pLayers->Map()->GetItem(EnvStart + Env);

const auto TickToNanoSeconds = std::chrono::nanoseconds(1s) / (int64_t)pThis->Client()->GameTickSpeed();

Expand Down Expand Up @@ -117,7 +111,7 @@ void CMapLayers::EnvelopeEval(int TimeOffsetMillis, int Env, ColorRGBA &Channels
MinTick * TickToNanoSeconds;
}
}
CRenderTools::RenderEvalEnvelope(pPoints + pItem->m_StartPoint, pItem->m_NumPoints, 4, s_Time + (int64_t)TimeOffsetMillis * std::chrono::nanoseconds(1ms), Channels);
CRenderTools::RenderEvalEnvelope(&EnvelopePoints, 4, s_Time + (int64_t)TimeOffsetMillis * std::chrono::nanoseconds(1ms), Channels);
}
else
{
Expand All @@ -142,7 +136,7 @@ void CMapLayers::EnvelopeEval(int TimeOffsetMillis, int Env, ColorRGBA &Channels
s_Time += CurTime - s_LastLocalTime;
s_LastLocalTime = CurTime;
}
CRenderTools::RenderEvalEnvelope(pPoints + pItem->m_StartPoint, pItem->m_NumPoints, 4, s_Time + std::chrono::nanoseconds(std::chrono::milliseconds(TimeOffsetMillis)), Channels);
CRenderTools::RenderEvalEnvelope(&EnvelopePoints, 4, s_Time + std::chrono::nanoseconds(std::chrono::milliseconds(TimeOffsetMillis)), Channels);
}
}

Expand Down
27 changes: 26 additions & 1 deletion src/game/client/render.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ struct CDataSprite;
}
struct CDataSprite;
struct CEnvPoint;
struct CEnvPointBezier;
struct CEnvPointBezier_upstream;
struct CMapItemGroup;
struct CMapItemGroupEx;
struct CQuad;
Expand Down Expand Up @@ -71,6 +73,29 @@ enum
TILERENDERFLAG_EXTEND = 4,
};

class IEnvelopePointAccess
{
public:
virtual int NumPoints() const = 0;
virtual const CEnvPoint *GetPoint(int Index) const = 0;
virtual const CEnvPointBezier *GetBezier(int Index) const = 0;
};

class CMapBasedEnvelopePointAccess : public IEnvelopePointAccess
{
int m_NumPoints;
CEnvPoint *m_pPoints;
CEnvPointBezier *m_pPointsBezier;
CEnvPointBezier_upstream *m_pPointsBezierUpstream;

public:
CMapBasedEnvelopePointAccess(class CDataFileReader *pReader);
CMapBasedEnvelopePointAccess(class IMap *pMap);
int NumPoints() const override;
const CEnvPoint *GetPoint(int Index) const override;
const CEnvPointBezier *GetBezier(int Index) const override;
};

typedef void (*ENVELOPE_EVAL)(int TimeOffsetMillis, int Env, ColorRGBA &Channels, void *pUser);

class CRenderTools
Expand Down Expand Up @@ -117,7 +142,7 @@ class CRenderTools
void RenderTee(const CAnimState *pAnim, const CTeeRenderInfo *pInfo, int Emote, vec2 Dir, vec2 Pos, float Alpha = 1.0f);

// map render methods (render_map.cpp)
static void RenderEvalEnvelope(CEnvPoint *pPoints, int NumPoints, int Channels, std::chrono::nanoseconds TimeNanos, ColorRGBA &Result);
static void RenderEvalEnvelope(const IEnvelopePointAccess *pPoints, int Channels, std::chrono::nanoseconds TimeNanos, ColorRGBA &Result);
void RenderQuads(CQuad *pQuads, int NumQuads, int Flags, ENVELOPE_EVAL pfnEval, void *pUser);
void ForceRenderQuads(CQuad *pQuads, int NumQuads, int Flags, ENVELOPE_EVAL pfnEval, void *pUser, float Alpha = 1.0f);
void RenderTilemap(CTile *pTiles, int w, int h, float Scale, ColorRGBA Color, int RenderFlags, ENVELOPE_EVAL pfnEval, void *pUser, int ColorEnv, int ColorEnvOffset);
Expand Down
Loading

0 comments on commit 8d12bce

Please sign in to comment.