Merge branch 'master' into 1.5
This commit is contained in:
@@ -435,6 +435,11 @@ API_ENUM() enum class MaterialSceneTextures
|
||||
/// The material shading mode.
|
||||
/// </summary>
|
||||
ShadingModel = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The scene world-space position (relative to the render view origin).
|
||||
/// </summary>
|
||||
WorldPosition = 11,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -439,6 +439,7 @@ void MaterialParameter::Bind(BindMeta& meta) const
|
||||
switch (type)
|
||||
{
|
||||
case MaterialSceneTextures::SceneDepth:
|
||||
case MaterialSceneTextures::WorldPosition:
|
||||
view = meta.CanSampleDepth
|
||||
? meta.Buffers->DepthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView
|
||||
? meta.Buffers->DepthBuffer->ViewReadOnlyDepth()
|
||||
|
||||
@@ -18,6 +18,7 @@ PACK_STRUCT(struct PostFxMaterialShaderData {
|
||||
Float4 ViewInfo;
|
||||
Float4 ScreenSize;
|
||||
Float4 TemporalAAJitter;
|
||||
Matrix InverseViewProjectionMatrix;
|
||||
});
|
||||
|
||||
void PostFxMaterialShader::Bind(BindParameters& params)
|
||||
@@ -44,6 +45,7 @@ void PostFxMaterialShader::Bind(BindParameters& params)
|
||||
// Setup material constants
|
||||
{
|
||||
Matrix::Transpose(view.View, materialData->ViewMatrix);
|
||||
Matrix::Transpose(view.IVP, materialData->InverseViewProjectionMatrix);
|
||||
materialData->ViewPos = view.Position;
|
||||
materialData->ViewFar = view.Far;
|
||||
materialData->ViewDir = view.Direction;
|
||||
|
||||
@@ -113,6 +113,11 @@ bool ShaderAssetBase::Save()
|
||||
|
||||
bool IsValidShaderCache(DataContainer<byte>& shaderCache, Array<String>& includes)
|
||||
{
|
||||
if (shaderCache.Length() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MemoryReadStream stream(shaderCache.Get(), shaderCache.Length());
|
||||
|
||||
// Read cache format version
|
||||
|
||||
@@ -245,6 +245,9 @@ void ParticleEmitterGPUGenerator::ProcessGroupTextures(Box* box, Node* node, Val
|
||||
value = writeLocal(VariantType::Int, String::Format(TEXT("(int)({0}.a * 3.999)"), gBuffer1Sample.Value), node);
|
||||
break;
|
||||
}
|
||||
case MaterialSceneTextures::WorldPosition:
|
||||
value = Value::Zero; // Not implemented
|
||||
break;
|
||||
default:
|
||||
{
|
||||
// Sample single texture
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
/// <summary>
|
||||
/// Physical materials are used to define the response of a physical object when interacting dynamically with the world.
|
||||
/// </summary>
|
||||
API_CLASS() class FLAXENGINE_API PhysicalMaterial final : public ISerializable
|
||||
API_CLASS(Attributes = "ContentContextMenu(\"New/Physics/Physical Material\")") class FLAXENGINE_API PhysicalMaterial final : public ISerializable
|
||||
{
|
||||
API_AUTO_SERIALIZATION();
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(PhysicalMaterial);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute is used to show content items that can be created in the content browser context menu. Separate the subcontext menus with a /.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ContentContextMenuAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to be used in the context menu
|
||||
/// </summary>
|
||||
public string Path;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentContextMenuAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to use to create the context menu</param>
|
||||
public ContentContextMenuAttribute(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
// Copyright (c) 2012-2022 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<ShaderGraphNode<>>();
|
||||
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::Float3 : VariantType::Float2).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::Float3, 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::Float4, 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);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,165 @@
|
||||
|
||||
#include "MaterialGenerator.h"
|
||||
|
||||
MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, Box* box, SerializedMaterialParam* texture)
|
||||
{
|
||||
ASSERT(texture && box);
|
||||
|
||||
// Cache data
|
||||
const auto parent = box->GetParent<ShaderGraphNode<>>();
|
||||
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::Float3 : VariantType::Float2).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::Float3, 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::Float4, 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);
|
||||
}
|
||||
|
||||
void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
||||
{
|
||||
switch (node->TypeID)
|
||||
@@ -257,6 +416,23 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
||||
value = writeLocal(VariantType::Int, String::Format(TEXT("(int)({0}.a * 3.999)"), gBuffer1Sample->Value), node);
|
||||
break;
|
||||
}
|
||||
case MaterialSceneTextures::WorldPosition:
|
||||
{
|
||||
auto depthParam = findOrAddSceneTexture(MaterialSceneTextures::SceneDepth);
|
||||
auto depthSample = sampleTextureRaw(node, value, box, &depthParam);
|
||||
if (depthSample == nullptr)
|
||||
break;
|
||||
const auto parent = box->GetParent<ShaderGraphNode<>>();
|
||||
MaterialGraphBox* uvBox = parent->GetBox(0);
|
||||
bool useCustomUVs = uvBox->HasConnection();
|
||||
String uv;
|
||||
if (useCustomUVs)
|
||||
uv = MaterialValue::Cast(tryGetValue(uvBox, getUVs), VariantType::Float2).Value;
|
||||
else
|
||||
uv = TEXT("input.TexCoord.xy");
|
||||
value = writeLocal(VariantType::Float3, String::Format(TEXT("GetWorldPos({1}, {0}.rgb)"), depthSample->Value, uv), node);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Sample single texture
|
||||
|
||||
@@ -229,6 +229,11 @@ namespace FlaxEngine.GUI
|
||||
[EditorOrder(529)]
|
||||
public bool ClipText { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether you can scroll the text in the text box (eg. with a mouse wheel).
|
||||
/// </summary>
|
||||
public bool IsMultilineScrollable { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets textbox background color when the control is selected (has focus).
|
||||
/// </summary>
|
||||
@@ -1157,7 +1162,7 @@ namespace FlaxEngine.GUI
|
||||
return true;
|
||||
|
||||
// Multiline scroll
|
||||
if (IsMultiline && _text.Length != 0)
|
||||
if (IsMultiline && _text.Length != 0 && IsMultilineScrollable)
|
||||
{
|
||||
TargetViewOffset = Float2.Clamp(_targetViewOffset - new Float2(0, delta * 10.0f), Float2.Zero, new Float2(_targetViewOffset.X, _textSize.Y));
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user