Files
FlaxEngine/Source/Engine/Render2D/SpriteAtlas.cpp
2021-02-23 13:57:51 +01:00

191 lines
4.4 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "SpriteAtlas.h"
#include "Engine/Core/Log.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Content/Loading/Tasks/LoadAssetDataTask.h"
#include "Engine/Content/Upgraders/TextureAssetUpgrader.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Graphics/GPUDevice.h"
const SpriteHandle SpriteHandle::Invalid = { nullptr, INVALID_INDEX };
REGISTER_BINARY_ASSET(SpriteAtlas, "FlaxEngine.SpriteAtlas", ::New<TextureAssetUpgrader>(), true);
bool SpriteHandle::GetSprite(Sprite* result) const
{
if (IsValid())
{
*result = Atlas->Sprites[Index];
return true;
}
return false;
}
bool SpriteHandle::IsValid() const
{
return Atlas && Index >= 0 && Atlas->Sprites.Count() > Index;
}
GPUTexture* SpriteHandle::GetAtlasTexture() const
{
ASSERT(Atlas);
return Atlas->GetTexture();
}
SpriteAtlas::SpriteAtlas(const SpawnParams& params, const AssetInfo* info)
: TextureBase(params, info)
{
}
SpriteHandle SpriteAtlas::FindSprite(const StringView& name) const
{
SpriteHandle result(const_cast<SpriteAtlas*>(this), -1);
for (int32 i = 0; i < Sprites.Count(); i++)
{
if (name == Sprites[i].Name)
{
result.Index = i;
break;
}
}
return result;
}
SpriteHandle SpriteAtlas::AddSprite(const Sprite& sprite)
{
const int32 index = Sprites.Count();
Sprites.Add(sprite);
return SpriteHandle(this, index);
}
void SpriteAtlas::RemoveSprite(int32 index)
{
Sprites.RemoveAt(index);
}
#if USE_EDITOR
bool SpriteAtlas::SaveSprites()
{
ScopeLock lock(Locker);
// Load whole asset
if (LoadChunks(ALL_ASSET_CHUNKS))
{
return true;
}
// Prepare asset data
AssetInitData data;
data.SerializedVersion = 4;
data.CustomData.Copy(_texture.GetHeader());
// Write sprites data
MemoryWriteStream stream(1024);
stream.WriteInt32(1); // Version
stream.WriteInt32(Sprites.Count()); // Sprites Count
for (int32 i = 0; i < Sprites.Count(); i++)
{
// Save sprite
Sprite t = Sprites[i];
stream.Write(&t.Area);
stream.WriteString(t.Name, 49);
}
// Link sprites data (unlink after safe)
auto dataChunk = GetOrCreateChunk(15);
dataChunk->Data.Link(stream.GetHandle(), stream.GetLength());
// Save (use silent mode to prevent asset reloading)
bool saveResult = SaveAsset(data, true);
dataChunk->Data.Release();
if (saveResult)
{
LOG(Warning, "Failed to save sprite atlas \'{0}\'.", GetPath());
return true;
}
return false;
}
#endif
bool SpriteAtlas::LoadSprites(ReadStream& stream)
{
#if USE_EDITOR
// Sprites may be used on rendering thread so lock drawing for a while
if (GPUDevice::Instance)
GPUDevice::Instance->Locker.Lock();
#endif
// Cleanup first
Sprites.Clear();
// Load tiles data
int32 tilesVersion, tilesCount;
stream.ReadInt32(&tilesVersion);
if (tilesVersion != 1)
{
#if USE_EDITOR
if (GPUDevice::Instance)
GPUDevice::Instance->Locker.Unlock();
#endif
LOG(Warning, "Invalid tiles version.");
return true;
}
stream.ReadInt32(&tilesCount);
Sprites.Resize(tilesCount);
for (int32 i = 0; i < tilesCount; i++)
{
// Load sprite
Sprite& t = Sprites[i];
stream.Read(&t.Area);
stream.ReadString(&t.Name, 49);
}
#if USE_EDITOR
if (GPUDevice::Instance)
GPUDevice::Instance->Locker.Unlock();
#endif
return false;
}
Asset::LoadResult SpriteAtlas::load()
{
// Get sprites data
auto spritesDataChunk = GetChunk(15);
if (spritesDataChunk == nullptr || spritesDataChunk->IsMissing())
return LoadResult::MissingDataChunk;
MemoryReadStream spritesData(spritesDataChunk->Get(), spritesDataChunk->Size());
// Load sprites
if (LoadSprites(spritesData))
{
LOG(Warning, "Cannot load sprites atlas data.");
return LoadResult::Failed;
}
return TextureBase::load();
}
void SpriteAtlas::unload(bool isReloading)
{
// Release sprites
Sprites.Resize(0);
// Base
TextureBase::unload(isReloading);
}
AssetChunksFlag SpriteAtlas::getChunksToPreload() const
{
return GET_CHUNK_FLAG(15);
}