// Copyright (c) 2012-2020 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(); 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, ¶m); 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