Files
FlaxEngine/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp
2021-01-02 14:28:49 +01:00

190 lines
6.4 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if COMPILE_WITH_MATERIAL_GRAPH
#include "MaterialGenerator.h"
MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture)
{
ASSERT(texture && box);
// Cache data
const auto parent = box->GetParent<MaterialGraphNode>();
const bool isCubemap = texture->Type == MaterialParameterType::CubeTexture;
const bool isArray = texture->Type == MaterialParameterType::GPUTextureArray;
const bool isVolume = texture->Type == MaterialParameterType::GPUTextureVolume;
const bool isNormalMap = texture->Type == MaterialParameterType::NormalMap;
const bool canUseSample = CanUseSample(_treeType);
MaterialGraphBox* valueBox = parent->GetBox(1);
// Check if has variable assigned
if (texture->Type != MaterialParameterType::Texture
&& texture->Type != MaterialParameterType::NormalMap
&& texture->Type != MaterialParameterType::SceneTexture
&& texture->Type != MaterialParameterType::GPUTexture
&& texture->Type != MaterialParameterType::GPUTextureVolume
&& texture->Type != MaterialParameterType::GPUTextureCube
&& texture->Type != MaterialParameterType::GPUTextureArray
&& texture->Type != MaterialParameterType::CubeTexture)
{
OnError(caller, box, TEXT("No parameter for texture sample node."));
return nullptr;
}
// Check if it's 'Object' box that is using only texture object without sampling
if (box->ID == 6)
{
// Return texture object
value.Value = texture->ShaderName;
value.Type = VariantType::Object;
return nullptr;
}
// Check if hasn't been sampled during that tree eating
if (valueBox->Cache.IsInvalid())
{
// Check if use custom UVs
String uv;
MaterialGraphBox* uvBox = parent->GetBox(0);
bool useCustomUVs = uvBox->HasConnection();
bool use3dUVs = isCubemap || isArray || isVolume;
if (useCustomUVs)
{
// Get custom UVs
auto textureParamId = texture->ID;
ASSERT(textureParamId.IsValid());
MaterialValue v = tryGetValue(uvBox, getUVs);
uv = MaterialValue::Cast(v, use3dUVs ? VariantType::Vector3 : VariantType::Vector2).Value;
// Restore texture (during tryGetValue pointer could go invalid)
texture = findParam(textureParamId);
ASSERT(texture);
}
else
{
// Use default UVs
uv = use3dUVs ? TEXT("float3(input.TexCoord.xy, 0)") : TEXT("input.TexCoord.xy");
}
// Select sampler
// TODO: add option for texture groups and per texture options like wrap mode etc.
// TODO: changing texture sampler option
const Char* sampler = TEXT("SamplerLinearWrap");
// Sample texture
if (isNormalMap)
{
const Char* format = canUseSample ? TEXT("{0}.Sample({1}, {2}).xyz") : TEXT("{0}.SampleLevel({1}, {2}, 0).xyz");
// Sample encoded normal map
const String sampledValue = String::Format(format, texture->ShaderName, sampler, uv);
const auto normalVector = writeLocal(VariantType::Vector3, sampledValue, parent);
// Decode normal vector
_writer.Write(TEXT("\t{0}.xy = {0}.xy * 2.0 - 1.0;\n"), normalVector.Value);
_writer.Write(TEXT("\t{0}.z = sqrt(saturate(1.0 - dot({0}.xy, {0}.xy)));\n"), normalVector.Value);
valueBox->Cache = normalVector;
}
else
{
// Select format string based on texture type
const Char* format;
/*if (isCubemap)
{
MISSING_CODE("sampling cube maps and 3d texture in material generator");
//format = TEXT("SAMPLE_CUBEMAP({0}, {1})");
}
else*/
{
/*if (useCustomUVs)
{
createGradients(writer, parent);
format = TEXT("SAMPLE_TEXTURE_GRAD({0}, {1}, {2}, {3})");
}
else*/
{
format = canUseSample ? TEXT("{0}.Sample({1}, {2})") : TEXT("{0}.SampleLevel({1}, {2}, 0)");
}
}
// Sample texture
String sampledValue = String::Format(format, texture->ShaderName, sampler, uv, _ddx.Value, _ddy.Value);
valueBox->Cache = writeLocal(VariantType::Vector4, sampledValue, parent);
}
}
return &valueBox->Cache;
}
void MaterialGenerator::sampleTexture(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture)
{
const auto sample = sampleTextureRaw(caller, value, box, texture);
if (sample == nullptr)
return;
// Set result values based on box ID
switch (box->ID)
{
case 1:
value = *sample;
break;
case 2:
value.Value = sample->Value + _subs[0];
break;
case 3:
value.Value = sample->Value + _subs[1];
break;
case 4:
value.Value = sample->Value + _subs[2];
break;
case 5:
value.Value = sample->Value + _subs[3];
break;
default: CRASH;
break;
}
value.Type = box->Type.Type;
}
void MaterialGenerator::sampleSceneDepth(Node* caller, Value& value, Box* box)
{
// Sample depth buffer
auto param = findOrAddSceneTexture(MaterialSceneTextures::SceneDepth);
const auto depthSample = sampleTextureRaw(caller, value, box, &param);
if (depthSample == nullptr)
return;
// Linearize raw device depth
linearizeSceneDepth(caller, *depthSample, value);
}
void MaterialGenerator::linearizeSceneDepth(Node* caller, const Value& depth, Value& value)
{
value = writeLocal(VariantType::Float, String::Format(TEXT("ViewInfo.w / ({0}.x - ViewInfo.z)"), depth.Value), caller);
}
byte MaterialGenerator::getStartSrvRegister(MaterialLayer* baseLayer)
{
// Note: this must match material templates
switch (baseLayer->Domain)
{
case MaterialDomain::Surface:
return baseLayer->BlendMode == MaterialBlendMode::Transparent ? 3 : 3;
case MaterialDomain::PostProcess:
return 0;
case MaterialDomain::Decal:
return 1;
case MaterialDomain::GUI:
return 0;
case MaterialDomain::Terrain:
return 6;
case MaterialDomain::Particle:
return 5;
default:
CRASH;
return 0;
}
}
#endif