581 lines
20 KiB
C++
581 lines
20 KiB
C++
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
|
|
|
#include "Material.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Core/Types/DataContainer.h"
|
|
#include "Engine/Content/Upgraders/ShaderAssetUpgrader.h"
|
|
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
|
#include "Engine/Graphics/RenderTools.h"
|
|
#include "Engine/Graphics/Materials/MaterialShader.h"
|
|
#include "Engine/Graphics/Shaders/Cache/ShaderCacheManager.h"
|
|
#include "Engine/Graphics/Shaders/Cache/ShaderStorage.h"
|
|
#include "Engine/Serialization/MemoryReadStream.h"
|
|
#include "Engine/Threading/Threading.h"
|
|
#if COMPILE_WITH_SHADER_COMPILER
|
|
#include "MaterialFunction.h"
|
|
#include "Engine/Serialization/MemoryWriteStream.h"
|
|
#include "Engine/Utilities/Encryption.h"
|
|
#include "Engine/Tools/MaterialGenerator/MaterialGenerator.h"
|
|
#include "Engine/ShadersCompilation/Config.h"
|
|
#if BUILD_DEBUG
|
|
#include "Engine/Engine/Globals.h"
|
|
#endif
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Enable/disable automatic material shader source code generation (if missing)
|
|
/// </summary>
|
|
#define MATERIAL_AUTO_GENERATE_MISSING_SOURCE (USE_EDITOR)
|
|
|
|
REGISTER_BINARY_ASSET_WITH_UPGRADER(Material, "FlaxEngine.Material", ShaderAssetUpgrader, false);
|
|
|
|
Material::Material(const SpawnParams& params, const AssetInfo* info)
|
|
: ShaderAssetTypeBase<MaterialBase>(params, info)
|
|
{
|
|
}
|
|
|
|
bool Material::IsMaterialInstance() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const MaterialInfo& Material::GetInfo() const
|
|
{
|
|
if (_materialShader)
|
|
return _materialShader->GetInfo();
|
|
|
|
static MaterialInfo EmptyInfo;
|
|
return EmptyInfo;
|
|
}
|
|
|
|
GPUShader* Material::GetShader() const
|
|
{
|
|
return _materialShader ? _materialShader->GetShader() : nullptr;
|
|
}
|
|
|
|
bool Material::IsReady() const
|
|
{
|
|
return _materialShader && _materialShader->IsReady();
|
|
}
|
|
|
|
DrawPass Material::GetDrawModes() const
|
|
{
|
|
if (_materialShader)
|
|
return _materialShader->GetDrawModes();
|
|
return DrawPass::None;
|
|
}
|
|
|
|
bool Material::CanUseLightmap() const
|
|
{
|
|
return _materialShader && _materialShader->CanUseLightmap();
|
|
}
|
|
|
|
bool Material::CanUseInstancing(InstancingHandler& handler) const
|
|
{
|
|
return _materialShader && _materialShader->CanUseInstancing(handler);
|
|
}
|
|
|
|
void Material::Bind(BindParameters& params)
|
|
{
|
|
ASSERT(IsReady());
|
|
|
|
MaterialParamsLink* lastLink = params.ParamsLink;
|
|
MaterialParamsLink link;
|
|
link.This = &Params;
|
|
if (lastLink)
|
|
{
|
|
while (lastLink->Down)
|
|
lastLink = lastLink->Down;
|
|
lastLink->Down = &link;
|
|
}
|
|
else
|
|
{
|
|
params.ParamsLink = &link;
|
|
}
|
|
link.Up = lastLink;
|
|
link.Down = nullptr;
|
|
|
|
_materialShader->Bind(params);
|
|
|
|
if (lastLink)
|
|
{
|
|
lastLink->Down = nullptr;
|
|
}
|
|
else
|
|
{
|
|
params.ParamsLink = nullptr;
|
|
}
|
|
}
|
|
|
|
#if COMPILE_WITH_MATERIAL_GRAPH && COMPILE_WITH_SHADER_COMPILER
|
|
|
|
namespace
|
|
{
|
|
void OnGeneratorError(ShaderGraph<>::Node* node, ShaderGraphBox* box, const StringView& text)
|
|
{
|
|
LOG(Error, "Material error: {0} (Node:{1}:{2}, Box:{3})", text, node ? node->Type : -1, node ? node->ID : -1, box ? box->ID : -1);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
Asset::LoadResult Material::load()
|
|
{
|
|
ASSERT(_materialShader == nullptr);
|
|
FlaxChunk* materialParamsChunk;
|
|
|
|
// Special case for Null renderer
|
|
if (IsNullRenderer())
|
|
{
|
|
// Hack loading
|
|
MemoryReadStream shaderCacheStream(nullptr, 0);
|
|
_materialShader = MaterialShader::CreateDummy(shaderCacheStream, _shaderHeader.Material.Info);
|
|
if (_materialShader == nullptr)
|
|
{
|
|
LOG(Warning, "Cannot load material.");
|
|
return LoadResult::Failed;
|
|
}
|
|
materialParamsChunk = GetChunk(SHADER_FILE_CHUNK_MATERIAL_PARAMS);
|
|
if (materialParamsChunk != nullptr && materialParamsChunk->IsLoaded())
|
|
{
|
|
MemoryReadStream materialParamsStream(materialParamsChunk->Get(), materialParamsChunk->Size());
|
|
if (Params.Load(&materialParamsStream))
|
|
{
|
|
LOG(Warning, "Cannot load material parameters.");
|
|
return LoadResult::Failed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Don't use parameters
|
|
Params.Dispose();
|
|
}
|
|
ParamsChanged();
|
|
return LoadResult::Ok;
|
|
}
|
|
|
|
// If engine was compiled with shaders compiling service:
|
|
// - Material should be changed in need to convert it to the newer version (via Visject Surface)
|
|
// Shader should be recompiled if shader source code has been modified
|
|
// otherwise:
|
|
// - If material version is not supported then material cannot be loaded
|
|
#if COMPILE_WITH_SHADER_COMPILER
|
|
|
|
// Check if current engine has different materials version or convert it by force or has no source generated at all
|
|
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION
|
|
#if MATERIAL_AUTO_GENERATE_MISSING_SOURCE
|
|
|| !HasChunk(SHADER_FILE_CHUNK_SOURCE)
|
|
#endif
|
|
|| HasDependenciesModified()
|
|
#if COMPILE_WITH_DEV_ENV
|
|
// Set to true to enable force GPU shader regeneration (don't commit it)
|
|
|| false
|
|
#endif
|
|
)
|
|
{
|
|
// Prepare
|
|
MaterialGenerator generator;
|
|
generator.Error.Bind(&OnGeneratorError);
|
|
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION)
|
|
LOG(Info, "Converting material \'{0}\', from version {1} to {2}...", ToString(), _shaderHeader.Material.GraphVersion, MATERIAL_GRAPH_VERSION);
|
|
else
|
|
LOG(Info, "Updating material \'{0}\'...", ToString());
|
|
|
|
// Load or create material surface
|
|
MaterialLayer* layer;
|
|
if (HasChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE))
|
|
{
|
|
// Load graph
|
|
if (LoadChunks(GET_CHUNK_FLAG(SHADER_FILE_CHUNK_VISJECT_SURFACE)))
|
|
{
|
|
LOG(Warning, "Cannot load \'{0}\' data from chunk {1}.", ToString(), SHADER_FILE_CHUNK_VISJECT_SURFACE);
|
|
return LoadResult::Failed;
|
|
}
|
|
|
|
// Get stream with graph data
|
|
auto surfaceChunk = GetChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE);
|
|
MemoryReadStream stream(surfaceChunk->Get(), surfaceChunk->Size());
|
|
|
|
// Load layer
|
|
layer = MaterialLayer::Load(GetID(), &stream, _shaderHeader.Material.Info, ToString());
|
|
}
|
|
else
|
|
{
|
|
// Create default layer
|
|
layer = MaterialLayer::CreateDefault(GetID());
|
|
|
|
// Create surface chunk
|
|
auto surfaceChunk = GetOrCreateChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE);
|
|
if (surfaceChunk == nullptr)
|
|
return LoadResult::MissingDataChunk;
|
|
|
|
// Save layer to the chunk data
|
|
MemoryWriteStream stream(512);
|
|
layer->Graph.Save(&stream, false);
|
|
surfaceChunk->Data.Copy(stream.GetHandle(), stream.GetPosition());
|
|
}
|
|
generator.AddLayer(layer);
|
|
|
|
// Get chunk with material parameters
|
|
materialParamsChunk = GetOrCreateChunk(SHADER_FILE_CHUNK_MATERIAL_PARAMS);
|
|
if (materialParamsChunk == nullptr)
|
|
return LoadResult::MissingDataChunk;
|
|
materialParamsChunk->Data.Release();
|
|
|
|
// Generate material source code and metadata
|
|
MemoryWriteStream newMaterialMeta(1024);
|
|
MemoryWriteStream source(64 * 1024);
|
|
MaterialInfo info = _shaderHeader.Material.Info;
|
|
if (generator.Generate(source, info, materialParamsChunk->Data))
|
|
{
|
|
LOG(Error, "Cannot generate material source code for \'{0}\'. Please see log for more info.", ToString());
|
|
return LoadResult::Failed;
|
|
}
|
|
|
|
// Update asset dependencies
|
|
ClearDependencies();
|
|
for (auto& asset : generator.Assets)
|
|
{
|
|
if (asset->Is<MaterialBase>() ||
|
|
asset->Is<MaterialFunction>())
|
|
AddDependency(asset.As<BinaryAsset>());
|
|
}
|
|
|
|
#if BUILD_DEBUG && USE_EDITOR
|
|
// Dump generated material source to the temporary file
|
|
source.SaveToFile(Globals::ProjectCacheFolder / TEXT("material.txt"));
|
|
#endif
|
|
|
|
// Encrypt source code
|
|
Encryption::EncryptBytes((byte*)source.GetHandle(), source.GetPosition());
|
|
|
|
// Set new source code chunk
|
|
SetChunk(SHADER_FILE_CHUNK_SOURCE, ToSpan(source.GetHandle(), source.GetPosition()));
|
|
|
|
// Clear shader cache
|
|
for (int32 chunkIndex = 1; chunkIndex < 14; chunkIndex++)
|
|
ReleaseChunk(chunkIndex);
|
|
|
|
// Setup shader header
|
|
Platform::MemoryClear(&_shaderHeader, sizeof(_shaderHeader));
|
|
_shaderHeader.Material.GraphVersion = MATERIAL_GRAPH_VERSION;
|
|
_shaderHeader.Material.Info = info;
|
|
|
|
// Save to file
|
|
#if USE_EDITOR
|
|
if (Save())
|
|
{
|
|
LOG(Error, "Cannot save \'{0}\'", ToString());
|
|
return LoadResult::Failed;
|
|
}
|
|
#endif
|
|
#if COMPILE_WITH_SHADER_CACHE_MANAGER
|
|
// Invalidate shader cache
|
|
ShaderCacheManager::RemoveCache(GetID());
|
|
#endif
|
|
}
|
|
|
|
#else
|
|
|
|
// Ensure that material is in the current version (whole materials pipeline depends on that)
|
|
if (_shaderHeader.Material.GraphVersion != MATERIAL_GRAPH_VERSION)
|
|
{
|
|
LOG(Fatal, "Unsupported material version: {0} in material \'{1}\'. Current is {2}.", _shaderHeader.Material.GraphVersion, ToString(), MATERIAL_GRAPH_VERSION);
|
|
return LoadResult::Failed;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Load shader cache (it may call compilation or gather cached data)
|
|
ShaderCacheResult shaderCache;
|
|
if (LoadShaderCache(shaderCache))
|
|
{
|
|
LOG(Error, "Cannot load \'{0}\' shader cache.", ToString());
|
|
#if 1
|
|
return LoadResult::Failed;
|
|
#else
|
|
// Custom path: don't fail asset loading but use dummy material instead (surface and parameters will be available for editing)
|
|
LOG(Error, "Using dummy material shader for \'{0}\'.", ToString());
|
|
MemoryWriteStream dummyShader(64);
|
|
{
|
|
dummyShader.WriteInt32(6);
|
|
dummyShader.WriteInt32(0);
|
|
dummyShader.WriteInt32(0);
|
|
dummyShader.WriteByte(0);
|
|
dummyShader.WriteByte(0);
|
|
}
|
|
MemoryReadStream shaderCacheStream(dummyShader.GetHandle(), dummyShader.GetPosition());
|
|
_materialShader = MaterialShader::CreateDummy(shaderCacheStream, _shaderHeader.Material.Info);
|
|
if (_materialShader == nullptr)
|
|
{
|
|
LOG(Warning, "Cannot load material.");
|
|
return LoadResult::Failed;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Load material (load shader from cache, load params, setup pipeline stuff)
|
|
MemoryReadStream shaderCacheStream(shaderCache.Data.Get(), shaderCache.Data.Length());
|
|
#if GPU_ENABLE_RESOURCE_NAMING
|
|
const StringView name(GetPath());
|
|
#else
|
|
const StringView name;
|
|
#endif
|
|
_materialShader = MaterialShader::Create(name, shaderCacheStream, _shaderHeader.Material.Info);
|
|
if (_materialShader == nullptr)
|
|
{
|
|
LOG(Warning, "Cannot load material.");
|
|
return LoadResult::Failed;
|
|
}
|
|
}
|
|
|
|
// Load material parameters
|
|
materialParamsChunk = GetChunk(SHADER_FILE_CHUNK_MATERIAL_PARAMS);
|
|
if (materialParamsChunk != nullptr && materialParamsChunk->IsLoaded())
|
|
{
|
|
MemoryReadStream materialParamsStream(materialParamsChunk->Get(), materialParamsChunk->Size());
|
|
if (Params.Load(&materialParamsStream))
|
|
{
|
|
LOG(Warning, "Cannot load material parameters.");
|
|
return LoadResult::Failed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Don't use parameters
|
|
Params.Dispose();
|
|
}
|
|
ParamsChanged();
|
|
|
|
#if COMPILE_WITH_SHADER_COMPILER
|
|
RegisterForShaderReloads(this, shaderCache);
|
|
#endif
|
|
|
|
return LoadResult::Ok;
|
|
}
|
|
|
|
void Material::unload(bool isReloading)
|
|
{
|
|
#if COMPILE_WITH_SHADER_COMPILER
|
|
UnregisterForShaderReloads(this);
|
|
#endif
|
|
|
|
if (_materialShader)
|
|
{
|
|
_materialShader->Unload();
|
|
Delete(_materialShader);
|
|
_materialShader = nullptr;
|
|
}
|
|
|
|
Params.Dispose();
|
|
}
|
|
|
|
AssetChunksFlag Material::getChunksToPreload() const
|
|
{
|
|
AssetChunksFlag result = ShaderAssetTypeBase<MaterialBase>::getChunksToPreload();
|
|
result |= GET_CHUNK_FLAG(SHADER_FILE_CHUNK_MATERIAL_PARAMS);
|
|
return result;
|
|
}
|
|
|
|
#if USE_EDITOR
|
|
|
|
void Material::OnDependencyModified(BinaryAsset* asset)
|
|
{
|
|
BinaryAsset::OnDependencyModified(asset);
|
|
|
|
Reload();
|
|
}
|
|
|
|
#endif
|
|
|
|
#if USE_EDITOR
|
|
|
|
void Material::InitCompilationOptions(ShaderCompilationOptions& options)
|
|
{
|
|
// Base
|
|
ShaderAssetBase::InitCompilationOptions(options);
|
|
|
|
#if COMPILE_WITH_SHADER_COMPILER
|
|
// Ensure that this call is valid (material features switches may depend on target compilation platform)
|
|
ASSERT(options.Profile != ShaderProfile::Unknown);
|
|
|
|
// Prepare
|
|
auto& info = _shaderHeader.Material.Info;
|
|
const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable;
|
|
const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage;
|
|
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle;
|
|
const bool useTess =
|
|
info.TessellationMode != TessellationMethod::None &&
|
|
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
|
|
const bool useDistortion =
|
|
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
|
|
info.BlendMode != MaterialBlendMode::Opaque &&
|
|
(info.UsageFlags & MaterialUsageFlags::UseRefraction) != 0 &&
|
|
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0;
|
|
|
|
// @formatter:off
|
|
static const char* Numbers[] =
|
|
{
|
|
"0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69",
|
|
};
|
|
// @formatter:on
|
|
|
|
// Setup shader macros
|
|
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
|
|
options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] });
|
|
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)info.ShadingModel] });
|
|
options.Macros.Add({ "MATERIAL_MASKED", Numbers[info.UsageFlags & MaterialUsageFlags::UseMask ? 1 : 0] });
|
|
options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
|
|
options.Macros.Add({ "USE_EMISSIVE", Numbers[info.UsageFlags & MaterialUsageFlags::UseEmissive ? 1 : 0] });
|
|
options.Macros.Add({ "USE_NORMAL", Numbers[info.UsageFlags & MaterialUsageFlags::UseNormal ? 1 : 0] });
|
|
options.Macros.Add({ "USE_POSITION_OFFSET", Numbers[info.UsageFlags & MaterialUsageFlags::UsePositionOffset ? 1 : 0] });
|
|
options.Macros.Add({ "USE_VERTEX_COLOR", Numbers[info.UsageFlags & MaterialUsageFlags::UseVertexColor ? 1 : 0] });
|
|
options.Macros.Add({ "USE_DISPLACEMENT", Numbers[info.UsageFlags & MaterialUsageFlags::UseDisplacement ? 1 : 0] });
|
|
options.Macros.Add({ "USE_DITHERED_LOD_TRANSITION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DitheredLODTransition ? 1 : 0] });
|
|
options.Macros.Add({ "USE_GBUFFER_CUSTOM_DATA", Numbers[useCustomData ? 1 : 0] });
|
|
options.Macros.Add({ "USE_REFLECTIONS", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableReflections ? 0 : 1] });
|
|
if (!(info.FeaturesFlags & MaterialFeaturesFlags::DisableReflections) && info.FeaturesFlags & MaterialFeaturesFlags::ScreenSpaceReflections)
|
|
options.Macros.Add({ "MATERIAL_REFLECTIONS", Numbers[1] });
|
|
options.Macros.Add({ "USE_FOG", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableFog ? 0 : 1] });
|
|
if (useForward)
|
|
{
|
|
options.Macros.Add({ "USE_PIXEL_NORMAL_OFFSET_REFRACTION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::PixelNormalOffsetRefraction ? 1 : 0] });
|
|
switch (info.TransparentLightingMode)
|
|
{
|
|
case MaterialTransparentLightingMode::Surface:
|
|
break;
|
|
case MaterialTransparentLightingMode::SurfaceNonDirectional:
|
|
options.Macros.Add({ "LIGHTING_NO_DIRECTIONAL", "1" });
|
|
break;
|
|
}
|
|
}
|
|
|
|
// TODO: don't compile VS_Depth for deferred/forward materials if material doesn't use position offset or masking
|
|
|
|
options.Macros.Add({ "USE_TESSELLATION", Numbers[useTess ? 1 : 0] });
|
|
options.Macros.Add({ "TESSELLATION_IN_CONTROL_POINTS", "3" });
|
|
if (useTess)
|
|
{
|
|
switch (info.TessellationMode)
|
|
{
|
|
case TessellationMethod::Flat:
|
|
options.Macros.Add({ "MATERIAL_TESSELLATION", "MATERIAL_TESSELLATION_FLAT" });
|
|
break;
|
|
case TessellationMethod::PointNormal:
|
|
options.Macros.Add({ "MATERIAL_TESSELLATION", "MATERIAL_TESSELLATION_PN" });
|
|
break;
|
|
case TessellationMethod::Phong:
|
|
options.Macros.Add({ "MATERIAL_TESSELLATION", "MATERIAL_TESSELLATION_PHONG" });
|
|
break;
|
|
}
|
|
options.Macros.Add({ "MAX_TESSELLATION_FACTOR", Numbers[info.MaxTessellationFactor] });
|
|
}
|
|
|
|
// Helper macros (used by the parser)
|
|
options.Macros.Add({ "IS_SURFACE", Numbers[info.Domain == MaterialDomain::Surface ? 1 : 0] });
|
|
options.Macros.Add({ "IS_POST_FX", Numbers[info.Domain == MaterialDomain::PostProcess ? 1 : 0] });
|
|
options.Macros.Add({ "IS_GUI", Numbers[info.Domain == MaterialDomain::GUI ? 1 : 0] });
|
|
options.Macros.Add({ "IS_DECAL", Numbers[info.Domain == MaterialDomain::Decal ? 1 : 0] });
|
|
options.Macros.Add({ "IS_TERRAIN", Numbers[info.Domain == MaterialDomain::Terrain ? 1 : 0] });
|
|
options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] });
|
|
options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] });
|
|
options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] });
|
|
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] });
|
|
options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] });
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
BytesContainer Material::LoadSurface(bool createDefaultIfMissing)
|
|
{
|
|
BytesContainer result;
|
|
if (WaitForLoaded() && !LastLoadFailed())
|
|
return result;
|
|
ScopeLock lock(Locker);
|
|
|
|
// Check if has that chunk
|
|
if (HasChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE))
|
|
{
|
|
// Load graph
|
|
if (!LoadChunks(GET_CHUNK_FLAG(SHADER_FILE_CHUNK_VISJECT_SURFACE)))
|
|
{
|
|
// Get stream with graph data
|
|
const auto data = GetChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE);
|
|
result.Copy(data->Data);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
LOG(Warning, "Material \'{0}\' surface data is missing.", ToString());
|
|
|
|
#if COMPILE_WITH_MATERIAL_GRAPH
|
|
|
|
// Check if create default surface
|
|
if (createDefaultIfMissing)
|
|
{
|
|
// Create default layer
|
|
const auto layer = MaterialLayer::CreateDefault(GetID());
|
|
|
|
// Serialize layer to stream
|
|
MemoryWriteStream stream(256);
|
|
layer->Graph.Save(&stream, false);
|
|
|
|
// Set output data
|
|
result.Copy(stream.GetHandle(), stream.GetPosition());
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
#if USE_EDITOR
|
|
|
|
bool Material::SaveSurface(BytesContainer& data, const MaterialInfo& info)
|
|
{
|
|
// Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error)
|
|
if (LastLoadFailed())
|
|
{
|
|
LOG(Warning, "Saving asset that failed to load.");
|
|
}
|
|
else if (WaitForLoaded())
|
|
{
|
|
LOG(Error, "Asset loading failed. Cannot save it.");
|
|
return true;
|
|
}
|
|
|
|
ScopeLock lock(Locker);
|
|
|
|
// Release all chunks
|
|
for (int32 i = 0; i < ASSET_FILE_DATA_CHUNKS; i++)
|
|
ReleaseChunk(i);
|
|
|
|
// Update material info
|
|
Platform::MemoryClear(&_shaderHeader, sizeof(_shaderHeader));
|
|
_shaderHeader.Material.GraphVersion = MATERIAL_GRAPH_VERSION;
|
|
_shaderHeader.Material.Info = info;
|
|
|
|
// Set Visject Surface data
|
|
auto visjectSurfaceChunk = GetOrCreateChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE);
|
|
ASSERT(visjectSurfaceChunk != nullptr);
|
|
visjectSurfaceChunk->Data.Copy(data);
|
|
|
|
if (Save())
|
|
{
|
|
LOG(Error, "Cannot save \'{0}\'", ToString());
|
|
return true;
|
|
}
|
|
|
|
#if COMPILE_WITH_SHADER_CACHE_MANAGER
|
|
// Invalidate shader cache
|
|
ShaderCacheManager::RemoveCache(GetID());
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
#endif
|