**Refactor meshes format to support custom vertex layouts and new flexible api to access mesh data**
#3044 #2667
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
#include "Engine/Content/WeakAssetReference.h"
|
||||
#include "Engine/Content/Upgraders/ModelAssetUpgrader.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
|
||||
@@ -18,13 +18,13 @@
|
||||
#include "Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h"
|
||||
#include "Engine/Graphics/Models/MeshDeformation.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Renderer/DrawCall.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Tools/ModelTool/ModelTool.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
#endif
|
||||
|
||||
REGISTER_BINARY_ASSET_ABSTRACT(ModelBase, "FlaxEngine.ModelBase");
|
||||
@@ -246,330 +246,6 @@ bool Model::SetupLODs(const Span<int32>& meshesCountPerLod)
|
||||
return Init(meshesCountPerLod);
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
bool Model::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
{
|
||||
// Validate state
|
||||
if (WaitForLoaded())
|
||||
{
|
||||
LOG(Error, "Asset loading failed. Cannot save it.");
|
||||
return true;
|
||||
}
|
||||
if (IsVirtual() && path.IsEmpty())
|
||||
{
|
||||
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
|
||||
return true;
|
||||
}
|
||||
if (withMeshDataFromGpu && IsInMainThread())
|
||||
{
|
||||
LOG(Error, "To save model with GPU mesh buffers it needs to be called from the other thread (not the main thread).");
|
||||
return true;
|
||||
}
|
||||
if (IsVirtual() && !withMeshDataFromGpu)
|
||||
{
|
||||
LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data).");
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
// Create model data header
|
||||
MemoryWriteStream headerStream(1024);
|
||||
MemoryWriteStream* stream = &headerStream;
|
||||
{
|
||||
// Min Screen Size
|
||||
stream->WriteFloat(MinScreenSize);
|
||||
|
||||
// Amount of material slots
|
||||
stream->WriteInt32(MaterialSlots.Count());
|
||||
|
||||
// For each material slot
|
||||
for (int32 materialSlotIndex = 0; materialSlotIndex < MaterialSlots.Count(); materialSlotIndex++)
|
||||
{
|
||||
auto& slot = MaterialSlots[materialSlotIndex];
|
||||
|
||||
const auto id = slot.Material.GetID();
|
||||
stream->Write(id);
|
||||
stream->WriteByte(static_cast<byte>(slot.ShadowsMode));
|
||||
stream->WriteString(slot.Name, 11);
|
||||
}
|
||||
|
||||
// Amount of LODs
|
||||
const int32 lods = LODs.Count();
|
||||
stream->WriteByte(lods);
|
||||
|
||||
// For each LOD
|
||||
for (int32 lodIndex = 0; lodIndex < lods; lodIndex++)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
|
||||
// Screen Size
|
||||
stream->WriteFloat(lod.ScreenSize);
|
||||
|
||||
// Amount of meshes
|
||||
const int32 meshes = lod.Meshes.Count();
|
||||
stream->WriteUint16(meshes);
|
||||
|
||||
// For each mesh
|
||||
for (int32 meshIndex = 0; meshIndex < meshes; meshIndex++)
|
||||
{
|
||||
const auto& mesh = lod.Meshes[meshIndex];
|
||||
|
||||
// Material Slot index
|
||||
stream->WriteInt32(mesh.GetMaterialSlotIndex());
|
||||
|
||||
// Box
|
||||
const auto box = mesh.GetBox();
|
||||
stream->WriteBoundingBox(box);
|
||||
|
||||
// Sphere
|
||||
const auto sphere = mesh.GetSphere();
|
||||
stream->WriteBoundingSphere(sphere);
|
||||
|
||||
// Has Lightmap UVs
|
||||
stream->WriteBool(mesh.HasLightmapUVs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
// Check if use data from drive or from GPU
|
||||
if (withMeshDataFromGpu)
|
||||
{
|
||||
// Download all meshes buffers
|
||||
Array<Task*> tasks;
|
||||
for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
|
||||
const int32 meshesCount = lod.Meshes.Count();
|
||||
struct MeshData
|
||||
{
|
||||
BytesContainer VB0;
|
||||
BytesContainer VB1;
|
||||
BytesContainer VB2;
|
||||
BytesContainer IB;
|
||||
|
||||
uint32 DataSize() const
|
||||
{
|
||||
return VB0.Length() + VB1.Length() + VB2.Length() + IB.Length();
|
||||
}
|
||||
};
|
||||
Array<MeshData> meshesData;
|
||||
meshesData.Resize(meshesCount);
|
||||
tasks.EnsureCapacity(meshesCount * 4);
|
||||
|
||||
for (int32 meshIndex = 0; meshIndex < meshesCount; meshIndex++)
|
||||
{
|
||||
const auto& mesh = lod.Meshes[meshIndex];
|
||||
auto& meshData = meshesData[meshIndex];
|
||||
|
||||
// Vertex Buffer 0 (required)
|
||||
auto task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex0, meshData.VB0);
|
||||
if (task == nullptr)
|
||||
return true;
|
||||
task->Start();
|
||||
tasks.Add(task);
|
||||
|
||||
// Vertex Buffer 1 (required)
|
||||
task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex1, meshData.VB1);
|
||||
if (task == nullptr)
|
||||
return true;
|
||||
task->Start();
|
||||
tasks.Add(task);
|
||||
|
||||
// Vertex Buffer 2 (optional)
|
||||
task = mesh.DownloadDataGPUAsync(MeshBufferType::Vertex2, meshData.VB2);
|
||||
if (task)
|
||||
{
|
||||
task->Start();
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
// Index Buffer (required)
|
||||
task = mesh.DownloadDataGPUAsync(MeshBufferType::Index, meshData.IB);
|
||||
if (task == nullptr)
|
||||
return true;
|
||||
task->Start();
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
// Wait for all
|
||||
if (Task::WaitAll(tasks))
|
||||
return true;
|
||||
tasks.Clear();
|
||||
|
||||
// Create meshes data
|
||||
{
|
||||
int32 dataSize = meshesCount * (2 * sizeof(uint32) + sizeof(bool));
|
||||
for (int32 meshIndex = 0; meshIndex < meshesCount; meshIndex++)
|
||||
{
|
||||
dataSize += meshesData[meshIndex].DataSize();
|
||||
}
|
||||
|
||||
MemoryWriteStream meshesStream(dataSize);
|
||||
|
||||
for (int32 meshIndex = 0; meshIndex < meshesCount; meshIndex++)
|
||||
{
|
||||
const auto& mesh = lod.Meshes[meshIndex];
|
||||
const auto& meshData = meshesData[meshIndex];
|
||||
|
||||
uint32 vertices = mesh.GetVertexCount();
|
||||
uint32 triangles = mesh.GetTriangleCount();
|
||||
bool hasColors = meshData.VB2.IsValid();
|
||||
uint32 vb0Size = vertices * sizeof(VB0ElementType);
|
||||
uint32 vb1Size = vertices * sizeof(VB1ElementType);
|
||||
uint32 vb2Size = vertices * sizeof(VB2ElementType);
|
||||
uint32 indicesCount = triangles * 3;
|
||||
bool shouldUse16BitIndexBuffer = indicesCount <= MAX_uint16;
|
||||
bool use16BitIndexBuffer = mesh.Use16BitIndexBuffer();
|
||||
uint32 ibSize = indicesCount * (use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32));
|
||||
|
||||
if (vertices == 0 || triangles == 0)
|
||||
{
|
||||
LOG(Warning, "Cannot save model with empty meshes.");
|
||||
return true;
|
||||
}
|
||||
if ((uint32)meshData.VB0.Length() < vb0Size)
|
||||
{
|
||||
LOG(Warning, "Invalid vertex buffer 0 size.");
|
||||
return true;
|
||||
}
|
||||
if ((uint32)meshData.VB1.Length() < vb1Size)
|
||||
{
|
||||
LOG(Warning, "Invalid vertex buffer 1 size.");
|
||||
return true;
|
||||
}
|
||||
if (hasColors && (uint32)meshData.VB2.Length() < vb2Size)
|
||||
{
|
||||
LOG(Warning, "Invalid vertex buffer 2 size.");
|
||||
return true;
|
||||
}
|
||||
if ((uint32)meshData.IB.Length() < ibSize)
|
||||
{
|
||||
LOG(Warning, "Invalid index buffer size.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// #MODEL_DATA_FORMAT_USAGE
|
||||
meshesStream.WriteUint32(vertices);
|
||||
meshesStream.WriteUint32(triangles);
|
||||
|
||||
meshesStream.WriteBytes(meshData.VB0.Get(), vb0Size);
|
||||
meshesStream.WriteBytes(meshData.VB1.Get(), vb1Size);
|
||||
|
||||
meshesStream.WriteBool(hasColors);
|
||||
|
||||
if (hasColors)
|
||||
{
|
||||
meshesStream.WriteBytes(meshData.VB2.Get(), vb2Size);
|
||||
}
|
||||
|
||||
if (shouldUse16BitIndexBuffer == use16BitIndexBuffer)
|
||||
{
|
||||
meshesStream.WriteBytes(meshData.IB.Get(), ibSize);
|
||||
}
|
||||
else if (shouldUse16BitIndexBuffer)
|
||||
{
|
||||
auto ib = (const int32*)meshData.IB.Get();
|
||||
for (uint32 i = 0; i < indicesCount; i++)
|
||||
{
|
||||
meshesStream.WriteUint16(ib[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CRASH;
|
||||
}
|
||||
}
|
||||
|
||||
// Override LOD data chunk with the fetched GPU meshes memory
|
||||
auto lodChunk = GET_CHUNK(MODEL_LOD_TO_CHUNK_INDEX(lodIndex));
|
||||
if (lodChunk == nullptr)
|
||||
return true;
|
||||
lodChunk->Data.Copy(meshesStream.GetHandle(), meshesStream.GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
// Download SDF data
|
||||
if (SDF.Texture)
|
||||
{
|
||||
auto sdfChunk = GET_CHUNK(15);
|
||||
if (sdfChunk == nullptr)
|
||||
return true;
|
||||
MemoryWriteStream sdfStream;
|
||||
sdfStream.WriteInt32(1); // Version
|
||||
ModelSDFHeader data(SDF, SDF.Texture->GetDescription());
|
||||
sdfStream.WriteBytes(&data, sizeof(data));
|
||||
TextureData sdfTextureData;
|
||||
if (SDF.Texture->DownloadData(sdfTextureData))
|
||||
return true;
|
||||
for (int32 mipLevel = 0; mipLevel < sdfTextureData.Items[0].Mips.Count(); mipLevel++)
|
||||
{
|
||||
auto& mip = sdfTextureData.Items[0].Mips[mipLevel];
|
||||
ModelSDFMip mipData(mipLevel, mip);
|
||||
sdfStream.WriteBytes(&mipData, sizeof(mipData));
|
||||
sdfStream.WriteBytes(mip.Data.Get(), mip.Data.Length());
|
||||
}
|
||||
sdfChunk->Data.Copy(sdfStream.GetHandle(), sdfStream.GetPosition());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load all chunks with a mesh data
|
||||
for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++)
|
||||
{
|
||||
if (LoadChunk(MODEL_LOD_TO_CHUNK_INDEX(lodIndex)))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SDF.Texture)
|
||||
{
|
||||
// SDF data from file (only if has no cached texture data)
|
||||
if (LoadChunk(15))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No SDF texture
|
||||
ReleaseChunk(15);
|
||||
}
|
||||
}
|
||||
|
||||
// Set mesh header data
|
||||
auto headerChunk = GET_CHUNK(0);
|
||||
ASSERT(headerChunk != nullptr);
|
||||
headerChunk->Data.Copy(headerStream.GetHandle(), headerStream.GetPosition());
|
||||
|
||||
#undef GET_CHUNK
|
||||
|
||||
// Save
|
||||
AssetInitData data;
|
||||
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 Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, float backfacesThreshold, bool useGPU)
|
||||
{
|
||||
if (EnableModelSDF == 2)
|
||||
@@ -666,6 +342,172 @@ bool Model::Init(const Span<int32>& meshesCountPerLod)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Model::LoadHeader(ReadStream& stream, byte& headerVersion)
|
||||
{
|
||||
if (ModelBase::LoadHeader(stream, headerVersion))
|
||||
return true;
|
||||
|
||||
// LODs
|
||||
byte lods = stream.ReadByte();
|
||||
if (lods == 0 || lods > MODEL_MAX_LODS)
|
||||
return true;
|
||||
LODs.Resize(lods);
|
||||
_initialized = true;
|
||||
for (int32 lodIndex = 0; lodIndex < lods; lodIndex++)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
lod._model = this;
|
||||
lod._lodIndex = lodIndex;
|
||||
stream.ReadFloat(&lod.ScreenSize);
|
||||
|
||||
// Meshes
|
||||
uint16 meshesCount;
|
||||
stream.ReadUint16(&meshesCount);
|
||||
if (meshesCount == 0 || meshesCount > MODEL_MAX_MESHES)
|
||||
return true;
|
||||
ASSERT(lodIndex == 0 || LODs[0].Meshes.Count() >= meshesCount);
|
||||
lod.Meshes.Resize(meshesCount, false);
|
||||
for (uint16 meshIndex = 0; meshIndex < meshesCount; meshIndex++)
|
||||
{
|
||||
Mesh& mesh = lod.Meshes[meshIndex];
|
||||
mesh.Link(this, lodIndex, meshIndex);
|
||||
|
||||
// Material Slot index
|
||||
int32 materialSlotIndex;
|
||||
stream.ReadInt32(&materialSlotIndex);
|
||||
if (materialSlotIndex < 0 || materialSlotIndex >= MaterialSlots.Count())
|
||||
{
|
||||
LOG(Warning, "Invalid material slot index {0} for mesh {1}. Slots count: {2}.", materialSlotIndex, meshIndex, MaterialSlots.Count());
|
||||
return true;
|
||||
}
|
||||
mesh.SetMaterialSlotIndex(materialSlotIndex);
|
||||
|
||||
// Bounds
|
||||
BoundingBox box;
|
||||
stream.Read(box);
|
||||
BoundingSphere sphere;
|
||||
stream.Read(sphere);
|
||||
mesh.SetBounds(box, sphere);
|
||||
|
||||
// Lightmap UVs channel
|
||||
int8 lightmapUVs;
|
||||
stream.ReadInt8(&lightmapUVs);
|
||||
mesh.LightmapUVsIndex = (int32)lightmapUVs;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
bool Model::SaveHeader(WriteStream& stream)
|
||||
{
|
||||
if (ModelBase::SaveHeader(stream))
|
||||
return true;
|
||||
static_assert(HeaderVersion == 2, "Update code");
|
||||
|
||||
// LODs
|
||||
stream.Write((byte)LODs.Count());
|
||||
for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
stream.Write(lod.ScreenSize);
|
||||
|
||||
// Meshes
|
||||
stream.Write((uint16)lod.Meshes.Count());
|
||||
for (const auto& mesh : lod.Meshes)
|
||||
{
|
||||
stream.Write(mesh.GetMaterialSlotIndex());
|
||||
stream.Write(mesh.GetBox());
|
||||
stream.Write(mesh.GetSphere());
|
||||
stream.Write((int8)mesh.LightmapUVsIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Model::SaveHeader(WriteStream& stream, const ModelData& modelData)
|
||||
{
|
||||
if (ModelBase::SaveHeader(stream, modelData))
|
||||
return true;
|
||||
static_assert(HeaderVersion == 2, "Update code");
|
||||
|
||||
// LODs
|
||||
stream.Write((byte)modelData.LODs.Count());
|
||||
for (int32 lodIndex = 0; lodIndex < modelData.LODs.Count(); lodIndex++)
|
||||
{
|
||||
auto& lod = modelData.LODs[lodIndex];
|
||||
stream.Write(lod.ScreenSize);
|
||||
|
||||
// Meshes
|
||||
stream.Write((uint16)lod.Meshes.Count());
|
||||
for (const auto& mesh : lod.Meshes)
|
||||
{
|
||||
BoundingBox box;
|
||||
BoundingSphere sphere;
|
||||
mesh->CalculateBounds(box, sphere);
|
||||
stream.Write(mesh->MaterialSlotIndex);
|
||||
stream.Write(box);
|
||||
stream.Write(sphere);
|
||||
stream.Write((int8)mesh->LightmapUVsIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Model::Save(bool withMeshDataFromGpu, Function<FlaxChunk*(int32)>& getChunk)
|
||||
{
|
||||
if (ModelBase::Save(withMeshDataFromGpu, getChunk))
|
||||
return true;
|
||||
|
||||
if (withMeshDataFromGpu)
|
||||
{
|
||||
// Download SDF data
|
||||
if (SDF.Texture)
|
||||
{
|
||||
auto sdfChunk = getChunk(15);
|
||||
if (sdfChunk == nullptr)
|
||||
return true;
|
||||
MemoryWriteStream sdfStream;
|
||||
sdfStream.WriteInt32(1); // Version
|
||||
ModelSDFHeader data(SDF, SDF.Texture->GetDescription());
|
||||
sdfStream.WriteBytes(&data, sizeof(data));
|
||||
TextureData sdfTextureData;
|
||||
if (SDF.Texture->DownloadData(sdfTextureData))
|
||||
return true;
|
||||
for (int32 mipLevel = 0; mipLevel < sdfTextureData.Items[0].Mips.Count(); mipLevel++)
|
||||
{
|
||||
auto& mip = sdfTextureData.Items[0].Mips[mipLevel];
|
||||
ModelSDFMip mipData(mipLevel, mip);
|
||||
sdfStream.WriteBytes(&mipData, sizeof(mipData));
|
||||
sdfStream.WriteBytes(mip.Data.Get(), mip.Data.Length());
|
||||
}
|
||||
sdfChunk->Data.Copy(sdfStream.GetHandle(), sdfStream.GetPosition());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDF.Texture)
|
||||
{
|
||||
// SDF data from file (only if has no cached texture data)
|
||||
if (LoadChunk(15))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No SDF texture
|
||||
ReleaseChunk(15);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Model::SetupMaterialSlots(int32 slotsCount)
|
||||
{
|
||||
ModelBase::SetupMaterialSlots(slotsCount);
|
||||
@@ -687,6 +529,26 @@ int32 Model::GetLODsCount() const
|
||||
return LODs.Count();
|
||||
}
|
||||
|
||||
const MeshBase* Model::GetMesh(int32 meshIndex, int32 lodIndex) const
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
return &lod.Meshes[meshIndex];
|
||||
}
|
||||
|
||||
MeshBase* Model::GetMesh(int32 meshIndex, int32 lodIndex)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
return &lod.Meshes[meshIndex];
|
||||
}
|
||||
|
||||
void Model::GetMeshes(Array<const MeshBase*>& meshes, int32 lodIndex) const
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
meshes.Resize(lod.Meshes.Count());
|
||||
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
|
||||
meshes[meshIndex] = &lod.Meshes[meshIndex];
|
||||
}
|
||||
|
||||
void Model::GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
@@ -722,91 +584,11 @@ Asset::LoadResult Model::load()
|
||||
if (chunk0 == nullptr || chunk0->IsMissing())
|
||||
return LoadResult::MissingDataChunk;
|
||||
MemoryReadStream headerStream(chunk0->Get(), chunk0->Size());
|
||||
ReadStream* stream = &headerStream;
|
||||
|
||||
// Min Screen Size
|
||||
stream->ReadFloat(&MinScreenSize);
|
||||
|
||||
// Amount of material slots
|
||||
int32 materialSlotsCount;
|
||||
stream->ReadInt32(&materialSlotsCount);
|
||||
if (materialSlotsCount <= 0 || materialSlotsCount > 4096)
|
||||
|
||||
// Load asset data (anything but mesh contents that use streaming)
|
||||
byte headerVersion;
|
||||
if (LoadHeader(headerStream, headerVersion))
|
||||
return LoadResult::InvalidData;
|
||||
MaterialSlots.Resize(materialSlotsCount, false);
|
||||
|
||||
// For each material slot
|
||||
for (int32 materialSlotIndex = 0; materialSlotIndex < materialSlotsCount; materialSlotIndex++)
|
||||
{
|
||||
auto& slot = MaterialSlots[materialSlotIndex];
|
||||
|
||||
// Material
|
||||
Guid materialId;
|
||||
stream->Read(materialId);
|
||||
slot.Material = materialId;
|
||||
|
||||
// Shadows Mode
|
||||
slot.ShadowsMode = static_cast<ShadowsCastingMode>(stream->ReadByte());
|
||||
|
||||
// Name
|
||||
stream->ReadString(&slot.Name, 11);
|
||||
}
|
||||
|
||||
// Amount of LODs
|
||||
byte lods;
|
||||
stream->ReadByte(&lods);
|
||||
if (lods == 0 || lods > MODEL_MAX_LODS)
|
||||
return LoadResult::InvalidData;
|
||||
LODs.Resize(lods);
|
||||
_initialized = true;
|
||||
|
||||
// For each LOD
|
||||
for (int32 lodIndex = 0; lodIndex < lods; lodIndex++)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
lod._model = this;
|
||||
lod._lodIndex = lodIndex;
|
||||
|
||||
// Screen Size
|
||||
stream->ReadFloat(&lod.ScreenSize);
|
||||
|
||||
// Amount of meshes
|
||||
uint16 meshesCount;
|
||||
stream->ReadUint16(&meshesCount);
|
||||
if (meshesCount == 0 || meshesCount > MODEL_MAX_MESHES)
|
||||
return LoadResult::InvalidData;
|
||||
ASSERT(lodIndex == 0 || LODs[0].Meshes.Count() >= meshesCount);
|
||||
|
||||
// Allocate memory
|
||||
lod.Meshes.Resize(meshesCount, false);
|
||||
|
||||
// For each mesh
|
||||
for (uint16 meshIndex = 0; meshIndex < meshesCount; meshIndex++)
|
||||
{
|
||||
Mesh& mesh = lod.Meshes[meshIndex];
|
||||
mesh.Link(this, lodIndex, meshIndex);
|
||||
|
||||
// Material Slot index
|
||||
int32 materialSlotIndex;
|
||||
stream->ReadInt32(&materialSlotIndex);
|
||||
if (materialSlotIndex < 0 || materialSlotIndex >= materialSlotsCount)
|
||||
{
|
||||
LOG(Warning, "Invalid material slot index {0} for mesh {1}. Slots count: {2}.", materialSlotIndex, meshIndex, materialSlotsCount);
|
||||
return LoadResult::InvalidData;
|
||||
}
|
||||
mesh.SetMaterialSlotIndex(materialSlotIndex);
|
||||
|
||||
// Bounds
|
||||
BoundingBox box;
|
||||
stream->ReadBoundingBox(&box);
|
||||
BoundingSphere sphere;
|
||||
stream->ReadBoundingSphere(&sphere);
|
||||
mesh.SetBounds(box, sphere);
|
||||
|
||||
// Has Lightmap UVs
|
||||
bool hasLightmapUVs = stream->ReadBool();
|
||||
mesh.LightmapUVsIndex = hasLightmapUVs ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Load SDF
|
||||
auto chunk15 = GetChunk(15);
|
||||
@@ -815,7 +597,7 @@ Asset::LoadResult Model::load()
|
||||
PROFILE_CPU_NAMED("SDF");
|
||||
MemoryReadStream sdfStream(chunk15->Get(), chunk15->Size());
|
||||
int32 version;
|
||||
sdfStream.ReadInt32(&version);
|
||||
sdfStream.Read(version);
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
|
||||
Reference in New Issue
Block a user