You're breathtaking!
This commit is contained in:
283
Source/Engine/ContentImporters/ImportModelFile.cpp
Normal file
283
Source/Engine/ContentImporters/ImportModelFile.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "ImportModel.h"
|
||||
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Engine/Graphics/Models/ModelData.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#include "Engine/Content/Assets/SkinnedModel.h"
|
||||
#include "Engine/Content/Storage/ContentStorageManager.h"
|
||||
#include "Engine/Content/Assets/Animation.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
|
||||
bool ImportModelFile::TryGetImportOptions(String path, Options& options)
|
||||
{
|
||||
#if IMPORT_MODEL_CACHE_OPTIONS
|
||||
|
||||
// Check if target asset file exists
|
||||
if (FileSystem::FileExists(path))
|
||||
{
|
||||
// Try to load asset file and asset info
|
||||
auto tmpFile = ContentStorageManager::GetStorage(path);
|
||||
AssetInitData data;
|
||||
if (tmpFile
|
||||
&& tmpFile->GetEntriesCount() == 1
|
||||
&& (
|
||||
(tmpFile->GetEntry(0).TypeName == Model::TypeName && !tmpFile->LoadAssetHeader(0, data) && data.SerializedVersion >= 4)
|
||||
||
|
||||
(tmpFile->GetEntry(0).TypeName == SkinnedModel::TypeName && !tmpFile->LoadAssetHeader(0, data) && data.SerializedVersion >= 1)
|
||||
||
|
||||
(tmpFile->GetEntry(0).TypeName == Animation::TypeName && !tmpFile->LoadAssetHeader(0, data) && data.SerializedVersion >= 1)
|
||||
))
|
||||
{
|
||||
// Check import meta
|
||||
rapidjson_flax::Document metadata;
|
||||
metadata.Parse((const char*)data.Metadata.Get(), data.Metadata.Length());
|
||||
if (metadata.HasParseError() == false)
|
||||
{
|
||||
// Success
|
||||
options.Deserialize(metadata, nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TryRestoreMaterials(CreateAssetContext& context, ModelData& modelData)
|
||||
{
|
||||
// Skip if file is missing
|
||||
if (!FileSystem::FileExists(context.TargetAssetPath))
|
||||
return;
|
||||
|
||||
// Try to load asset that gets reimported
|
||||
AssetReference<Asset> asset = Content::LoadAsync<Asset>(context.TargetAssetPath);
|
||||
if (asset == nullptr)
|
||||
return;
|
||||
if (asset->WaitForLoaded())
|
||||
return;
|
||||
|
||||
// Get model object
|
||||
ModelBase* model = nullptr;
|
||||
if (asset.Get()->GetTypeName() == Model::TypeName)
|
||||
{
|
||||
model = ((Model*)asset.Get());
|
||||
}
|
||||
else if (asset.Get()->GetTypeName() == SkinnedModel::TypeName)
|
||||
{
|
||||
model = ((SkinnedModel*)asset.Get());
|
||||
}
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
// Peek materials
|
||||
for (int32 i = 0; i < modelData.Materials.Count(); i++)
|
||||
{
|
||||
auto& dstSlot = modelData.Materials[i];
|
||||
|
||||
if (model->MaterialSlots.Count() > i)
|
||||
{
|
||||
auto& srcSlot = model->MaterialSlots[i];
|
||||
|
||||
dstSlot.Name = srcSlot.Name;
|
||||
dstSlot.ShadowsMode = srcSlot.ShadowsMode;
|
||||
dstSlot.AssetID = srcSlot.Material.GetID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModelFile::Import(CreateAssetContext& context)
|
||||
{
|
||||
// Get import options
|
||||
Options options;
|
||||
if (context.CustomArg != nullptr)
|
||||
{
|
||||
// Copy import options from argument
|
||||
options = *static_cast<Options*>(context.CustomArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore the previous settings or use default ones
|
||||
if (!TryGetImportOptions(context.TargetAssetPath, options))
|
||||
{
|
||||
LOG(Warning, "Missing model import options. Using default values.");
|
||||
}
|
||||
}
|
||||
|
||||
// Import model file
|
||||
ModelData modelData;
|
||||
String errorMsg;
|
||||
if (ModelTool::ImportModel(context.InputPath, modelData, options, errorMsg, StringUtils::GetDirectoryName(context.TargetAssetPath) / StringUtils::GetFileNameWithoutExtension(context.InputPath)))
|
||||
{
|
||||
LOG(Error, "Cannot import model file. {0}", errorMsg);
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
|
||||
// Check if restore materials on model reimport
|
||||
if (options.RestoreMaterialsOnReimport && modelData.Materials.HasItems())
|
||||
{
|
||||
TryRestoreMaterials(context, modelData);
|
||||
}
|
||||
|
||||
// Auto calculate LODs transition settings
|
||||
modelData.CalculateLODsScreenSizes();
|
||||
|
||||
// Create destination asset type
|
||||
CreateAssetResult result = CreateAssetResult::InvalidTypeID;
|
||||
switch (options.Type)
|
||||
{
|
||||
case ModelTool::ModelType::Model:
|
||||
result = ImportModel(context, modelData);
|
||||
break;
|
||||
case ModelTool::ModelType::SkinnedModel:
|
||||
result = ImportSkinnedModel(context, modelData);
|
||||
break;
|
||||
case ModelTool::ModelType::Animation:
|
||||
result = ImportAnimation(context, modelData);
|
||||
break;
|
||||
}
|
||||
if (result != CreateAssetResult::Ok)
|
||||
return result;
|
||||
|
||||
#if IMPORT_MODEL_CACHE_OPTIONS
|
||||
|
||||
// Create json with import context
|
||||
rapidjson_flax::StringBuffer importOptionsMetaBuffer;
|
||||
importOptionsMetaBuffer.Reserve(256);
|
||||
CompactJsonWriter importOptionsMetaObj(importOptionsMetaBuffer);
|
||||
JsonWriter& importOptionsMeta = importOptionsMetaObj;
|
||||
importOptionsMeta.StartObject();
|
||||
{
|
||||
context.AddMeta(importOptionsMeta);
|
||||
options.Serialize(importOptionsMeta, nullptr);
|
||||
}
|
||||
importOptionsMeta.EndObject();
|
||||
context.Data.Metadata.Copy((const byte*)importOptionsMetaBuffer.GetString(), (uint32)importOptionsMetaBuffer.GetSize());
|
||||
|
||||
#endif
|
||||
|
||||
return CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModelFile::Create(CreateAssetContext& context)
|
||||
{
|
||||
ASSERT(context.CustomArg != nullptr);
|
||||
auto& modelData = *(ModelData*)context.CustomArg;
|
||||
|
||||
// Ensure model has any meshes
|
||||
if ((modelData.LODs.IsEmpty() || modelData.LODs[0].Meshes.IsEmpty()))
|
||||
{
|
||||
LOG(Warning, "Models has no valid meshes");
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
|
||||
// Auto calculate LODs transition settings
|
||||
modelData.CalculateLODsScreenSizes();
|
||||
|
||||
// Import
|
||||
return ImportModel(context, modelData);
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModelFile::ImportModel(CreateAssetContext& context, ModelData& modelData)
|
||||
{
|
||||
// Base
|
||||
IMPORT_SETUP(Model, Model::SerializedVersion);
|
||||
|
||||
// Save model header
|
||||
MemoryWriteStream stream(4096);
|
||||
if (modelData.Pack2ModelHeader(&stream))
|
||||
return CreateAssetResult::Error;
|
||||
if (context.AllocateChunk(0))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
context.Data.Header.Chunks[0]->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
||||
|
||||
// Pack model LODs data
|
||||
const auto lodCount = modelData.GetLODsCount();
|
||||
for (int32 lodIndex = 0; lodIndex < lodCount; lodIndex++)
|
||||
{
|
||||
stream.SetPosition(0);
|
||||
|
||||
// Pack meshes
|
||||
auto& meshes = modelData.LODs[lodIndex].Meshes;
|
||||
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
|
||||
{
|
||||
if (meshes[meshIndex]->Pack2Model(&stream))
|
||||
{
|
||||
LOG(Warning, "Cannot pack mesh.");
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
}
|
||||
|
||||
const int32 chunkIndex = lodIndex + 1;
|
||||
if (context.AllocateChunk(chunkIndex))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
context.Data.Header.Chunks[chunkIndex]->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
||||
}
|
||||
|
||||
return CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModelFile::ImportSkinnedModel(CreateAssetContext& context, ModelData& modelData)
|
||||
{
|
||||
// Base
|
||||
IMPORT_SETUP(SkinnedModel, 4);
|
||||
|
||||
// Save skinned model header
|
||||
MemoryWriteStream stream(4096);
|
||||
if (modelData.Pack2SkinnedModelHeader(&stream))
|
||||
return CreateAssetResult::Error;
|
||||
if (context.AllocateChunk(0))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
context.Data.Header.Chunks[0]->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
||||
|
||||
// Pack model LODs data
|
||||
const auto lodCount = modelData.GetLODsCount();
|
||||
for (int32 lodIndex = 0; lodIndex < lodCount; lodIndex++)
|
||||
{
|
||||
stream.SetPosition(0);
|
||||
|
||||
// Pack meshes
|
||||
auto& meshes = modelData.LODs[lodIndex].Meshes;
|
||||
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
|
||||
{
|
||||
if (meshes[meshIndex]->Pack2SkinnedModel(&stream))
|
||||
{
|
||||
LOG(Warning, "Cannot pack mesh.");
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
}
|
||||
|
||||
const int32 chunkIndex = lodIndex + 1;
|
||||
if (context.AllocateChunk(chunkIndex))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
context.Data.Header.Chunks[chunkIndex]->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
||||
}
|
||||
|
||||
return CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
CreateAssetResult ImportModelFile::ImportAnimation(CreateAssetContext& context, ModelData& modelData)
|
||||
{
|
||||
// Base
|
||||
IMPORT_SETUP(Animation, Animation::SerializedVersion);
|
||||
|
||||
// Save animation data
|
||||
MemoryWriteStream stream(8182);
|
||||
if (modelData.Pack2AnimationHeader(&stream))
|
||||
return CreateAssetResult::Error;
|
||||
if (context.AllocateChunk(0))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
context.Data.Header.Chunks[0]->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
||||
|
||||
return CreateAssetResult::Ok;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user