Files
FlaxEngine/Source/Engine/Content/Assets/Texture.cpp

189 lines
5.7 KiB
C++

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "Texture.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/TextureAssetUpgrader.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Graphics/RenderTools.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h"
#include "Engine/Tools/TextureTool/TextureTool.h"
REGISTER_BINARY_ASSET_WITH_UPGRADER(Texture, "FlaxEngine.Texture", TextureAssetUpgrader, true);
Texture::Texture(const SpawnParams& params, const AssetInfo* info)
: TextureBase(params, info)
{
}
TextureFormatType Texture::GetFormatType() const
{
return _texture.GetFormatType();
}
bool Texture::IsNormalMap() const
{
return _texture.GetFormatType() == TextureFormatType::NormalMap;
}
#if USE_EDITOR
bool Texture::Save(const StringView& path)
{
return Save(path, nullptr);
}
bool Texture::Save(const StringView& path, const InitData* customData)
{
if (OnCheckSave())
return true;
ScopeLock lock(Locker);
AssetInitData data;
const class StreamingTexture* texture = StreamingTexture();
// Use a temporary chunks for data storage for virtual assets
FlaxChunk* tmpChunks[ASSET_FILE_DATA_CHUNKS];
Platform::MemoryClear(tmpChunks, sizeof(tmpChunks));
Array<FlaxChunk> chunks;
if (IsVirtual())
chunks.Resize(ASSET_FILE_DATA_CHUNKS);
#define GET_CHUNK(index) (IsVirtual() ? tmpChunks[index] = &chunks[index] : GetOrCreateChunk(index))
// Save header
data.CustomData.Copy(texture->GetHeader());
// Validate custom data
if (customData == nullptr)
customData = _customData;
if (IsVirtual())
{
// Virtual asset must have a custom data provided
if (!customData)
{
LOG(Error, "To save virtual texture you need to initialize it first with a valid data.");
return true;
}
}
else if (customData)
{
// Normal asset if has custom data passed it must match the texture info
if (customData->Mips.Count() != texture->TotalMipLevels() ||
customData->ArraySize != texture->TotalArraySize() ||
customData->Format != Format() ||
customData->Width != Width() ||
customData->Height != Height())
{
LOG(Error, "Invalid custom texture data to save.");
return true;
}
}
// Get texture data to chunks
if (customData)
{
for (int32 mipIndex = 0; mipIndex < texture->TotalMipLevels(); mipIndex++)
{
auto chunk = GET_CHUNK(mipIndex);
uint32 rowPitch, slicePitch;
const int32 mipWidth = Math::Max(1, Width() >> mipIndex);
const int32 mipHeight = Math::Max(1, Height() >> mipIndex);
RenderTools::ComputePitch(Format(), mipWidth, mipHeight, rowPitch, slicePitch);
chunk->Data.Allocate(slicePitch * texture->TotalArraySize());
auto& mipData = customData->Mips[mipIndex];
if (mipData.Data.Length() != (int32)mipData.SlicePitch * texture->TotalArraySize())
{
LOG(Error, "Invalid custom texture data (slice pitch * array size is different than data bytes count).");
return true;
}
// Faster path if source and destination data layout matches
if (rowPitch == mipData.RowPitch && slicePitch == mipData.SlicePitch)
{
const auto src = mipData.Data.Get();
const auto dst = chunk->Data.Get();
Platform::MemoryCopy(dst, src, slicePitch * texture->TotalArraySize());
}
else
{
const auto copyRowSize = Math::Min(mipData.RowPitch, rowPitch);
for (int32 slice = 0; slice < texture->TotalArraySize(); slice++)
{
auto src = mipData.Data.Get() + slice * mipData.SlicePitch;
auto dst = chunk->Data.Get() + slice * slicePitch;
for (int32 line = 0; line < texture->TotalHeight(); line++)
{
Platform::MemoryCopy(dst, src, copyRowSize);
src += mipData.RowPitch;
dst += rowPitch;
}
}
}
}
}
else
{
// Load all chunks
if (LoadChunks(ALL_ASSET_CHUNKS))
return true;
}
#undef GET_CHUNK
// Save
data.SerializedVersion = SerializedVersion;
if (IsVirtual())
Platform::MemoryCopy(_header.Chunks, tmpChunks, sizeof(_header.Chunks));
const bool saveResult = path.HasChars() ? SaveAsset(path, data) : SaveAsset(data, true);
if (IsVirtual())
Platform::MemoryClear(_header.Chunks, sizeof(_header.Chunks));
if (saveResult)
{
LOG(Error, "Cannot save \'{0}\'", ToString());
return true;
}
return false;
}
#endif
bool Texture::LoadFile(const StringView& path, bool generateMips)
{
if (!IsVirtual())
{
LOG(Error, "Loading image from file is supported only for virtual textures.");
return true;
}
TextureData textureData;
if (TextureTool::ImportTexture(path, textureData))
{
return true;
}
auto initData = New<InitData>();
initData->FromTextureData(textureData, generateMips);
return Init(initData);
}
Texture* Texture::FromFile(const StringView& path, bool generateMips)
{
auto texture = Content::CreateVirtualAsset<Texture>();
if (texture->LoadFile(path, generateMips))
{
texture->DeleteObject();
texture = nullptr;
}
return texture;
}