You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Sandbox/Editor/Material/MaterialPythonFuncs.cpp

2209 lines
90 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Material support for Python
#include "EditorDefs.h"
#include "MaterialPythonFuncs.h"
// Editor
#include "MaterialManager.h"
#include "ShaderEnum.h"
namespace
{
void PyMaterialCreate()
{
GetIEditor()->GetMaterialManager()->Command_Create();
}
void PyMaterialCreateMulti()
{
GetIEditor()->GetMaterialManager()->Command_CreateMulti();
}
void PyMaterialConvertToMulti()
{
GetIEditor()->GetMaterialManager()->Command_ConvertToMulti();
}
void PyMaterialDuplicateCurrent()
{
GetIEditor()->GetMaterialManager()->Command_Duplicate();
}
void PyMaterialMergeSelection()
{
GetIEditor()->GetMaterialManager()->Command_Merge();
}
void PyMaterialDeleteCurrent()
{
GetIEditor()->GetMaterialManager()->Command_Delete();
}
void PyMaterialAssignCurrentToSelection()
{
CUndo undo("Assign Material To Selection");
GetIEditor()->GetMaterialManager()->Command_AssignToSelection();
}
void PyMaterialResetSelection()
{
GetIEditor()->GetMaterialManager()->Command_ResetSelection();
}
void PyMaterialSelectObjectsWithCurrent()
{
CUndo undo("Select Objects With Current Material");
GetIEditor()->GetMaterialManager()->Command_SelectAssignedObjects();
}
void PyMaterialSetCurrentFromObject()
{
GetIEditor()->GetMaterialManager()->Command_SelectFromObject();
}
AZStd::vector<AZStd::string> PyGetSubMaterial(const char* pMaterialPath)
{
QString materialPath = pMaterialPath;
CMaterial* pMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial(pMaterialPath, false);
if (!pMaterial)
{
throw std::runtime_error("Invalid multi material.");
}
AZStd::vector<AZStd::string> result;
for (int i = 0; i < pMaterial->GetSubMaterialCount(); i++)
{
if (pMaterial->GetSubMaterial(i))
{
result.push_back((materialPath + "\\" + pMaterial->GetSubMaterial(i)->GetName()).toUtf8().data());
}
}
return result;
}
CMaterial* TryLoadingMaterial(const char* pPathAndMaterialName)
{
QString varMaterialPath = pPathAndMaterialName;
std::deque<QString> splittedMaterialPath;
for (auto token : varMaterialPath.split(QRegularExpression(R"([\\/])"), Qt::SkipEmptyParts))
{
splittedMaterialPath.push_back(token);
}
CMaterial* pMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial(varMaterialPath, false);
if (!pMaterial)
{
QString subMaterialName = splittedMaterialPath.back();
bool isSubMaterialExist(false);
varMaterialPath.remove((varMaterialPath.length() - subMaterialName.length() - 1), varMaterialPath.length());
QString test = varMaterialPath;
pMaterial = GetIEditor()->GetMaterialManager()->LoadMaterial(varMaterialPath, false);
if (!pMaterial)
{
throw std::runtime_error("Invalid multi material.");
}
for (int i = 0; i < pMaterial->GetSubMaterialCount(); i++)
{
if (pMaterial->GetSubMaterial(i)->GetName() == subMaterialName)
{
pMaterial = pMaterial->GetSubMaterial(i);
isSubMaterialExist = true;
}
}
if (!pMaterial || !isSubMaterialExist)
{
throw std::runtime_error((QString("\"") + subMaterialName + "\" is an invalid sub material.").toUtf8().data());
}
}
GetIEditor()->GetMaterialManager()->SetCurrentMaterial(pMaterial);
return pMaterial;
}
std::deque<QString> PreparePropertyPath(const char* pPathAndPropertyName)
{
QString varPathProperty = pPathAndPropertyName;
std::deque<QString> splittedPropertyPath;
for (auto token : varPathProperty.split(QRegularExpression(R"([\\/])"), Qt::SkipEmptyParts))
{
splittedPropertyPath.push_back(token);
}
return splittedPropertyPath;
}
//////////////////////////////////////////////////////////////////////////
// Converter: Enum -> CString (human readable)
//////////////////////////////////////////////////////////////////////////
QString TryConvertingSEFResTextureToCString(SEfResTexture* pResTexture)
{
if (pResTexture)
{
return pResTexture->m_Name.c_str();
}
return "";
}
QString TryConvertingETEX_TypeToCString(const uint8& texTypeName)
{
switch (texTypeName)
{
case eTT_2D:
return "2D";
case eTT_Cube:
return "Cube-Map";
case eTT_NearestCube:
return "Nearest Cube-Map probe for alpha blended";
case eTT_Auto2D:
return "Auto 2D-Map";
case eTT_Dyn2D:
return "Dynamic 2D-Map";
case eTT_User:
return "From User Params";
default:
throw std::runtime_error("Invalid tex type.");
}
}
QString TryConvertingTexFilterToCString(const int& iFilterName)
{
switch (iFilterName)
{
case FILTER_NONE:
return "Default";
case FILTER_POINT:
return "Point";
case FILTER_LINEAR:
return "Linear";
case FILTER_BILINEAR:
return "Bilinear";
case FILTER_TRILINEAR:
return "Trilinear";
case FILTER_ANISO2X:
return "Anisotropic 2x";
case FILTER_ANISO4X:
return "Anisotropic 4x";
case FILTER_ANISO8X:
return "Anisotropic 8x";
case FILTER_ANISO16X:
return "Anisotropic 16x";
default:
throw std::runtime_error("Invalid tex filter.");
}
}
QString TryConvertingETexGenTypeToCString(const uint8& texGenType)
{
switch (texGenType)
{
case ETG_Stream:
return "Stream";
case ETG_World:
return "World";
case ETG_Camera:
return "Camera";
default:
throw std::runtime_error("Invalid tex gen type.");
}
}
QString TryConvertingETexModRotateTypeToCString(const uint8& rotateType)
{
switch (rotateType)
{
case ETMR_NoChange:
return "No Change";
case ETMR_Fixed:
return "Fixed Rotation";
case ETMR_Constant:
return "Constant Rotation";
case ETMR_Oscillated:
return "Oscillated Rotation";
default:
throw std::runtime_error("Invalid rotate type.");
}
}
QString TryConvertingETexModMoveTypeToCString(const uint8& oscillatorType)
{
switch (oscillatorType)
{
case ETMM_NoChange:
return "No Change";
case ETMM_Fixed:
return "Fixed Moving";
case ETMM_Constant:
return "Constant Moving";
case ETMM_Jitter:
return "Jitter Moving";
case ETMM_Pan:
return "Pan Moving";
case ETMM_Stretch:
return "Stretch Moving";
case ETMM_StretchRepeat:
return "Stretch-Repeat Moving";
default:
throw std::runtime_error("Invalid oscillator type.");
}
}
QString TryConvertingEDeformTypeToCString(const EDeformType& deformType)
{
switch (deformType)
{
case eDT_Unknown:
return "None";
case eDT_SinWave:
return "Sin Wave";
case eDT_SinWaveUsingVtxColor:
return "Sin Wave using vertex color";
case eDT_Bulge:
return "Bulge";
case eDT_Squeeze:
return "Squeeze";
case eDT_Perlin2D:
return "Perlin 2D";
case eDT_Perlin3D:
return "Perlin 3D";
case eDT_FromCenter:
return "From Center";
case eDT_Bending:
return "Bending";
case eDT_ProcFlare:
return "Proc. Flare";
case eDT_AutoSprite:
return "Auto sprite";
case eDT_Beam:
return "Beam";
case eDT_FixedOffset:
return "FixedOffset";
default:
throw std::runtime_error("Invalid deform type.");
}
}
QString TryConvertingEWaveFormToCString(const EWaveForm& waveForm)
{
switch (waveForm)
{
case eWF_None:
return "None";
case eWF_Sin:
return "Sin";
case eWF_HalfSin:
return "Half Sin";
case eWF_Square:
return "Square";
case eWF_Triangle:
return "Triangle";
case eWF_SawTooth:
return "Saw Tooth";
case eWF_InvSawTooth:
return "Inverse Saw Tooth";
case eWF_Hill:
return "Hill";
case eWF_InvHill:
return "Inverse Hill";
default:
throw std::runtime_error("Invalid wave form.");
}
}
//////////////////////////////////////////////////////////////////////////
// Converter: CString -> Enum
//////////////////////////////////////////////////////////////////////////
template<typename STRING>
// [Shader System TO DO] remove once dynamic slots assingment is in place
EEfResTextures TryConvertingCStringToEEfResTextures(const STRING& resTextureName)
{
if (resTextureName == "Diffuse")
{
return EFTT_DIFFUSE;
}
else if (resTextureName == "Specular")
{
return EFTT_SPECULAR;
}
else if (resTextureName == "Bumpmap")
{
return EFTT_NORMALS;
}
else if (resTextureName == "Heightmap")
{
return EFTT_HEIGHT;
}
else if (resTextureName == "Environment")
{
return EFTT_ENV;
}
else if (resTextureName == "Detail")
{
return EFTT_DETAIL_OVERLAY;
}
else if (resTextureName == "Opacity")
{
return EFTT_OPACITY;
}
else if (resTextureName == "Decal")
{
return EFTT_DECAL_OVERLAY;
}
else if (resTextureName == "SubSurface")
{
return EFTT_SUBSURFACE;
}
else if (resTextureName == "Custom")
{
return EFTT_CUSTOM;
}
else if (resTextureName == "[1] Custom")
{
return EFTT_DIFFUSE;
}
else if (resTextureName == "Emittance")
{
return EFTT_EMITTANCE;
}
else if (resTextureName == "Occlusion")
{
return EFTT_OCCLUSION;
}
else if (resTextureName == "Specular2")
{
return EFTT_SPECULAR_2;
}
throw std::runtime_error("Invalid texture name.");
}
template<typename STRING>
ETEX_Type TryConvertingCStringToETEX_Type(const STRING& texTypeName)
{
if (texTypeName == "2D")
{
return eTT_2D;
}
else if (texTypeName == "Cube-Map")
{
return eTT_Cube;
}
else if (texTypeName == "Nearest Cube-Map probe for alpha blended")
{
return eTT_NearestCube;
}
else if (texTypeName == "Auto 2D-Map")
{
return eTT_Auto2D;
}
else if (texTypeName == "Dynamic 2D-Map")
{
return eTT_Dyn2D;
}
else if (texTypeName == "From User Params")
{
return eTT_User;
}
throw std::runtime_error("Invalid tex type name.");
}
template<typename STRING>
signed char TryConvertingCStringToTexFilter(const STRING& filterName)
{
if (filterName == "Default")
{
return FILTER_NONE;
}
else if (filterName == "Point")
{
return FILTER_POINT;
}
else if (filterName == "Linear")
{
return FILTER_LINEAR;
}
else if (filterName == "Bilinear")
{
return FILTER_BILINEAR;
}
else if (filterName == "Trilinear")
{
return FILTER_TRILINEAR;
}
else if (filterName == "Anisotropic 2x")
{
return FILTER_ANISO2X;
}
else if (filterName == "Anisotropic 4x")
{
return FILTER_ANISO4X;
}
else if (filterName == "Anisotropic 8x")
{
return FILTER_ANISO8X;
}
else if (filterName == "Anisotropic 16x")
{
return FILTER_ANISO16X;
}
throw std::runtime_error("Invalid filter name.");
}
template<typename STRING>
ETexGenType TryConvertingCStringToETexGenType(const STRING& texGenType)
{
if (texGenType == "Stream")
{
return ETG_Stream;
}
else if (texGenType == "World")
{
return ETG_World;
}
else if (texGenType == "Camera")
{
return ETG_Camera;
}
throw std::runtime_error("Invalid tex gen type name.");
}
template<typename STRING>
ETexModRotateType TryConvertingCStringToETexModRotateType(const STRING& rotateType)
{
if (rotateType == "No Change")
{
return ETMR_NoChange;
}
else if (rotateType == "Fixed Rotation")
{
return ETMR_Fixed;
}
else if (rotateType == "Constant Rotation")
{
return ETMR_Constant;
}
else if (rotateType == "Oscillated Rotation")
{
return ETMR_Oscillated;
}
throw std::runtime_error("Invalid rotate type name.");
}
template<typename STRING>
ETexModMoveType TryConvertingCStringToETexModMoveType(const STRING& oscillatorType)
{
if (oscillatorType == "No Change")
{
return ETMM_NoChange;
}
else if (oscillatorType == "Fixed Moving")
{
return ETMM_Fixed;
}
else if (oscillatorType == "Constant Moving")
{
return ETMM_Constant;
}
else if (oscillatorType == "Jitter Moving")
{
return ETMM_Jitter;
}
else if (oscillatorType == "Pan Moving")
{
return ETMM_Pan;
}
else if (oscillatorType == "Stretch Moving")
{
return ETMM_Stretch;
}
else if (oscillatorType == "Stretch-Repeat Moving")
{
return ETMM_StretchRepeat;
}
throw std::runtime_error("Invalid oscillator type.");
}
template<typename STRING>
EDeformType TryConvertingCStringToEDeformType(const STRING& deformType)
{
if (deformType == "None")
{
return eDT_Unknown;
}
else if (deformType == "Sin Wave")
{
return eDT_SinWave;
}
else if (deformType == "Sin Wave using vertex color")
{
return eDT_SinWaveUsingVtxColor;
}
else if (deformType == "Bulge")
{
return eDT_Bulge;
}
else if (deformType == "Squeeze")
{
return eDT_Squeeze;
}
else if (deformType == "Perlin 2D")
{
return eDT_Perlin2D;
}
else if (deformType == "Perlin 3D")
{
return eDT_Perlin3D;
}
else if (deformType == "From Center")
{
return eDT_FromCenter;
}
else if (deformType == "Bending")
{
return eDT_Bending;
}
else if (deformType == "Proc. Flare")
{
return eDT_ProcFlare;
}
else if (deformType == "Auto sprite")
{
return eDT_AutoSprite;
}
else if (deformType == "Beam")
{
return eDT_Beam;
}
else if (deformType == "FixedOffset")
{
return eDT_FixedOffset;
}
throw std::runtime_error("Invalid deform type.");
}
template <typename STRING>
EWaveForm TryConvertingCStringToEWaveForm(const STRING& waveForm)
{
if (waveForm == "None")
{
return eWF_None;
}
else if (waveForm == "Sin")
{
return eWF_Sin;
}
else if (waveForm == "Half Sin")
{
return eWF_HalfSin;
}
else if (waveForm == "Square")
{
return eWF_Square;
}
else if (waveForm == "Triangle")
{
return eWF_Triangle;
}
else if (waveForm == "Saw Tooth")
{
return eWF_SawTooth;
}
else if (waveForm == "Inverse Saw Tooth")
{
return eWF_InvSawTooth;
}
else if (waveForm == "Hill")
{
return eWF_Hill;
}
else if (waveForm == "Inverse Hill")
{
return eWF_InvHill;
}
throw std::runtime_error("Invalid wave form.");
}
//////////////////////////////////////////////////////////////////////////
// Script parser
//////////////////////////////////////////////////////////////////////////
QString ParseUINameFromPublicParamsScript(const char* sUIScript)
{
string uiscript = sUIScript;
string element[3];
int p1 = 0;
string itemToken = uiscript.Tokenize(";", p1);
while (!itemToken.empty())
{
int nElements = 0;
int p2 = 0;
string token = itemToken.Tokenize(" \t\r\n=", p2);
while (!token.empty())
{
element[nElements++] = token;
if (nElements == 2)
{
element[nElements] = itemToken.substr(p2);
element[nElements].Trim(" =\t\"");
break;
}
token = itemToken.Tokenize(" \t\r\n=", p2);
}
if (_stricmp(element[1], "UIName") == 0)
{
return element[2].c_str();
}
itemToken = uiscript.Tokenize(";", p1);
}
return "";
}
std::map<QString, float> ParseValidRangeFromPublicParamsScript(const char* sUIScript)
{
string uiscript = sUIScript;
std::map<QString, float> result;
bool isUIMinExist(false);
bool isUIMaxExist(false);
string element[3];
int p1 = 0;
string itemToken = uiscript.Tokenize(";", p1);
while (!itemToken.empty())
{
int nElements = 0;
int p2 = 0;
string token = itemToken.Tokenize(" \t\r\n=", p2);
while (!token.empty())
{
element[nElements++] = token;
if (nElements == 2)
{
element[nElements] = itemToken.substr(p2);
element[nElements].Trim(" =\t\"");
break;
}
token = itemToken.Tokenize(" \t\r\n=", p2);
}
if (_stricmp(element[1], "UIMin") == 0)
{
result["UIMin"] = atof(element[2]);
isUIMinExist = true;
}
if (_stricmp(element[1], "UIMax") == 0)
{
result["UIMax"] = atof(element[2]);
isUIMaxExist = true;
}
itemToken = uiscript.Tokenize(";", p1);
}
if (!isUIMinExist || !isUIMaxExist)
{
throw std::runtime_error("Invalid range for shader param.");
}
return result;
}
//////////////////////////////////////////////////////////////////////////
// Set Flags
//////////////////////////////////////////////////////////////////////////
void SetMaterialFlag(CMaterial* pMaterial, const EMaterialFlags& eFlag, const bool& bFlag)
{
if (pMaterial->GetFlags() & eFlag && bFlag == false)
{
pMaterial->SetFlags(pMaterial->GetFlags() - eFlag);
}
else if (!(pMaterial->GetFlags() & eFlag) && bFlag == true)
{
pMaterial->SetFlags(pMaterial->GetFlags() | eFlag);
}
}
void SetPropagationFlag(CMaterial* pMaterial, const eMTL_PROPAGATION& eFlag, const bool& bFlag)
{
if (pMaterial->GetPropagationFlags() & eFlag && bFlag == false)
{
pMaterial->SetPropagationFlags(pMaterial->GetPropagationFlags() - eFlag);
}
else if (!(pMaterial->GetPropagationFlags() & eFlag) && bFlag == true)
{
pMaterial->SetPropagationFlags(pMaterial->GetPropagationFlags() | eFlag);
}
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
bool IsAnyValidRange(const AZStd::any& value, const T& low, const T& high, const QString& invalidTypeMessage, const QString& invalidValueMessage)
{
static_assert(AZStd::is_arithmetic<T>::value, "This function only works with numbers.");
if (!value.is<T>())
{
throw std::runtime_error(invalidTypeMessage.toUtf8().data());
}
T valueData;
AZStd::any_numeric_cast(&value, valueData);
if (valueData < low || valueData > high)
{
throw std::runtime_error(invalidValueMessage.toUtf8().data());
}
return true;
}
template <>
bool IsAnyValidRange(const AZStd::any& value, const AZ::Color& low, const AZ::Color& high, const QString& invalidTypeMessage, const QString& invalidValueMessage)
{
if (!value.is<AZ::Color>())
{
throw std::runtime_error(invalidTypeMessage.toUtf8().data());
}
const AZ::Color* valueData = AZStd::any_cast<const AZ::Color>(&value);
if (!valueData)
{
throw std::runtime_error(invalidValueMessage.toUtf8().data());
}
if (valueData->IsLessThan(low) || valueData->IsGreaterThan(high))
{
throw std::runtime_error(invalidValueMessage.toUtf8().data());
}
return true;
}
bool ValidateProperty(CMaterial* pMaterial, const std::deque<QString>& splittedPropertyPathParam, const AZStd::any& value)
{
std::deque<QString> splittedPropertyPath = splittedPropertyPathParam;
std::deque<QString> splittedPropertyPathSubCategory = splittedPropertyPathParam;
QString categoryName = splittedPropertyPath.front();
QString subCategoryName = "";
QString subSubCategoryName = "";
QString currentPath = "";
QString propertyName = splittedPropertyPath.back();
QString errorMsgInvalidValue = QString("Invalid value for property \"") + propertyName + "\"";
QString errorMsgInvalidDataType = QString("Invalid data type for property \"") + propertyName + "\"";
QString errorMsgInvalidPropertyPath = QString("Invalid property path");
const int iMinColorValue = 0;
const int iMaxColorValue = 255;
if (splittedPropertyPathSubCategory.size() == 3)
{
splittedPropertyPathSubCategory.pop_back();
subCategoryName = splittedPropertyPathSubCategory.back();
currentPath = QString("") + categoryName + "/" + subCategoryName;
if (
subCategoryName != "TexType" &&
subCategoryName != "Filter" &&
subCategoryName != "IsProjectedTexGen" &&
subCategoryName != "TexGenType" &&
subCategoryName != "Wave X" &&
subCategoryName != "Wave Y" &&
subCategoryName != "Wave Z" &&
subCategoryName != "Wave W" &&
subCategoryName != "Shader1" &&
subCategoryName != "Shader2" &&
subCategoryName != "Shader3" &&
subCategoryName != "Tiling" &&
subCategoryName != "Rotator" &&
subCategoryName != "Oscillator" &&
subCategoryName != "Diffuse" &&
subCategoryName != "Specular" &&
subCategoryName != "Bumpmap" &&
subCategoryName != "Heightmap" &&
subCategoryName != "Environment" &&
subCategoryName != "Detail" &&
subCategoryName != "Opacity" &&
subCategoryName != "Decal" &&
subCategoryName != "SubSurface" &&
subCategoryName != "Custom" &&
subCategoryName != "[1] Custom"
)
{
throw std::runtime_error((errorMsgInvalidPropertyPath + " (" + currentPath + ")").toUtf8().data());
}
}
else if (splittedPropertyPathSubCategory.size() == 4)
{
splittedPropertyPathSubCategory.pop_back();
subCategoryName = splittedPropertyPathSubCategory.back();
splittedPropertyPathSubCategory.pop_back();
subSubCategoryName = splittedPropertyPathSubCategory.back();
currentPath = categoryName + "/" + subSubCategoryName + "/" + subCategoryName;
if (
subSubCategoryName != "Diffuse" &&
subSubCategoryName != "Specular" &&
subSubCategoryName != "Bumpmap" &&
subSubCategoryName != "Heightmap" &&
subSubCategoryName != "Environment" &&
subSubCategoryName != "Detail" &&
subSubCategoryName != "Opacity" &&
subSubCategoryName != "Decal" &&
subSubCategoryName != "SubSurface" &&
subSubCategoryName != "Custom" &&
subSubCategoryName != "[1] Custom"
)
{
throw std::runtime_error((errorMsgInvalidPropertyPath + " (" + currentPath + ")").toUtf8().data());
}
else if (subCategoryName != "Tiling" && subCategoryName != "Rotator" && subCategoryName != "Oscillator")
{
throw std::runtime_error((errorMsgInvalidPropertyPath + " (" + currentPath + ")").toUtf8().data());
}
}
else
{
currentPath = categoryName;
}
if (
categoryName == "Material Settings" ||
categoryName == "Opacity Settings" ||
categoryName == "Lighting Settings" ||
categoryName == "Advanced" ||
categoryName == "Texture Maps" ||
categoryName == "Vertex Deformation" ||
categoryName == "Layer Presets")
{
if (propertyName == "Opacity" || propertyName == "AlphaTest" || propertyName == "Glow Amount")
{
// int: 0 < x < 100
if (!value.is<AZ::s64>())
{
throw std::runtime_error(errorMsgInvalidDataType.toUtf8().data());
}
AZ::s64 valueData;
AZStd::any_numeric_cast(&value, valueData);
if (valueData < 0 || valueData > 100)
{
throw std::runtime_error(errorMsgInvalidValue.toUtf8().data());
}
return true;
}
else if (propertyName == "Smoothness" || propertyName == "Glossiness")
{
// int: 0 < x < 255
return IsAnyValidRange<AZ::s64>(value, 0, 255, errorMsgInvalidDataType, errorMsgInvalidValue);
}
else if (propertyName == "Specular Level")
{
// float: 0.0 < x < 4.0
return IsAnyValidRange<double>(value, 0.0, 4.0, errorMsgInvalidDataType, errorMsgInvalidValue);
}
else if (
propertyName == "TileU" ||
propertyName == "TileV" ||
propertyName == "OffsetU" ||
propertyName == "OffsetV" ||
propertyName == "RotateU" ||
propertyName == "RotateV" ||
propertyName == "RotateW" ||
propertyName == "Rate" ||
propertyName == "Phase" ||
propertyName == "Amplitude" ||
propertyName == "CenterU" ||
propertyName == "CenterV" ||
propertyName == "RateU" ||
propertyName == "RateV" ||
propertyName == "PhaseU" ||
propertyName == "PhaseV" ||
propertyName == "AmplitudeU" ||
propertyName == "AmplitudeV" ||
propertyName == "Wave Length X" ||
propertyName == "Wave Length Y" ||
propertyName == "Wave Length Z" ||
propertyName == "Wave Length W" ||
propertyName == "Level" ||
propertyName == "Amplitude" ||
propertyName == "Phase" ||
propertyName == "Frequency")
{
// float: 0.0 < x < 100.0
return IsAnyValidRange<double>(value, 0.0, 100.0, errorMsgInvalidDataType, errorMsgInvalidValue);
}
else if (propertyName == "Voxel Coverage")
{
// float: 0.0 < x < 1.0
return IsAnyValidRange<double>(value, 0.0, 1.0, errorMsgInvalidDataType, errorMsgInvalidValue);
}
else if (propertyName == "Emissive Intensity")
{
// float: 0.0 < x < 200.0
return IsAnyValidRange<double>(value, 0.0, 200.0, errorMsgInvalidDataType, errorMsgInvalidValue);
}
else if (propertyName == "Diffuse Color" || propertyName == "Specular Color" || propertyName == "Emissive Color")
{
// intVector(RGB): 0 < x < 255
return IsAnyValidRange<AZ::Color>(value, AZ::Color::CreateZero(), AZ::Color::CreateOne(), errorMsgInvalidDataType, errorMsgInvalidValue);
}
else if (
propertyName == "Link to Material" ||
propertyName == "Surface Type" ||
propertyName == "Diffuse" ||
propertyName == "Specular" ||
propertyName == "Bumpmap" ||
propertyName == "Heightmap" ||
propertyName == "Environment" ||
propertyName == "Detail" ||
propertyName == "Opacity" ||
propertyName == "Decal" ||
propertyName == "SubSurface" ||
propertyName == "Custom" ||
propertyName == "[1] Custom" ||
propertyName == "TexType" ||
propertyName == "Filter" ||
propertyName == "TexGenType" ||
propertyName == "Type" ||
propertyName == "TypeU" ||
propertyName == "TypeV")
{
// string
if (!value.is<AZStd::string_view>())
{
throw std::runtime_error(errorMsgInvalidDataType.toUtf8().data());
}
return true;
}
else if (
propertyName == "Additive" ||
propertyName == "Allow layer activation" ||
propertyName == "2 Sided" ||
propertyName == "No Shadow" ||
propertyName == "Use Scattering" ||
propertyName == "Hide After Breaking" ||
propertyName == "Fog Volume Shading Quality High" ||
propertyName == "Blend Terrain Color" ||
propertyName == "Propagate Material Settings" ||
propertyName == "Propagate Opacity Settings" ||
propertyName == "Propagate Lighting Settings" ||
propertyName == "Propagate Advanced Settings" ||
propertyName == "Propagate Texture Maps" ||
propertyName == "Propagate Shader Params" ||
propertyName == "Propagate Shader Generation" ||
propertyName == "Propagate Vertex Deformation" ||
propertyName == "Propagate Layer Presets" ||
propertyName == "IsProjectedTexGen" ||
propertyName == "IsTileU" ||
propertyName == "IsTileV" ||
propertyName == "No Draw")
{
// bool
if (!value.is<bool>())
{
throw std::runtime_error(errorMsgInvalidDataType.toUtf8().data());
}
return true;
}
else if (propertyName == "Shader" || propertyName == "Shader1" || propertyName == "Shader2" || propertyName == "Shader3")
{
// string && valid shader
if (!value.is<AZStd::string_view>())
{
throw std::runtime_error(errorMsgInvalidDataType.toUtf8().data());
}
CShaderEnum* pShaderEnum = GetIEditor()->GetShaderEnum();
if (!pShaderEnum)
{
throw std::runtime_error("Shader enumerator corrupted.");
}
pShaderEnum->EnumShaders();
for (int i = 0; i < pShaderEnum->GetShaderCount(); i++)
{
if (pShaderEnum->GetShader(i) == AZStd::any_cast<AZStd::string_view>(value).data())
{
return true;
}
}
}
else if (propertyName == "Noise Scale")
{
// FloatVec: undefined < x < undefined
if (!value.is<AZ::Vector3>())
{
throw std::runtime_error(errorMsgInvalidDataType.toUtf8().data());
}
return true;
}
}
else if (categoryName == "Shader Params")
{
auto& shaderParams = pMaterial->GetShaderResources().m_ShaderParams;
for (int i = 0; i < shaderParams.size(); i++)
{
if (propertyName == ParseUINameFromPublicParamsScript(shaderParams[i].m_Script.c_str()))
{
if (shaderParams[i].m_Type == eType_FLOAT)
{
// float: valid range (from script)
float floatValue;
if (!AZStd::any_numeric_cast<float>(&value, floatValue))
{
throw std::runtime_error(errorMsgInvalidDataType.toUtf8().data());
}
std::map<QString, float> range = ParseValidRangeFromPublicParamsScript(shaderParams[i].m_Script.c_str());
if (floatValue < range["UIMin"] || floatValue > range["UIMax"])
{
QString errorMsg;
errorMsg = QStringLiteral("Invalid value for shader param \"%1\" (min: %2, max: %3)").arg(propertyName).arg(range["UIMin"]).arg(range["UIMax"]);
throw std::runtime_error(errorMsg.toUtf8().data());
}
return true;
}
else if (shaderParams[i].m_Type == eType_FCOLOR)
{
return IsAnyValidRange<AZ::Color>(value, AZ::Color::CreateZero(), AZ::Color::CreateOne(), errorMsgInvalidDataType, errorMsgInvalidValue);
}
}
}
}
else if (categoryName == "Shader Generation Params")
{
for (int i = 0; i < pMaterial->GetShaderGenParamsVars()->GetNumVariables(); i++)
{
if (propertyName == pMaterial->GetShaderGenParamsVars()->GetVariable(i)->GetHumanName())
{
if (!value.is<bool>())
{
throw std::runtime_error(errorMsgInvalidDataType.toUtf8().data());
}
return true;
}
}
}
else
{
throw std::runtime_error((errorMsgInvalidPropertyPath + " (" + currentPath + ")").toUtf8().data());
}
return false;
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
T PyFetchNumericType(const AZStd::any& value)
{
T numericValue;
AZStd::any_numeric_cast(&value, numericValue);
return numericValue;
}
AZStd::any PyGetProperty(const char* pPathAndMaterialName, const char* pPathAndPropertyName)
{
CMaterial* pMaterial = TryLoadingMaterial(pPathAndMaterialName);
std::deque<QString> splittedPropertyPath = PreparePropertyPath(pPathAndPropertyName);
std::deque<QString> splittedPropertyPathCategory = splittedPropertyPath;
QString categoryName = splittedPropertyPath.front();
QString subCategoryName = "None";
QString subSubCategoryName = "None";
QString propertyName = splittedPropertyPath.back();
QString errorMsgInvalidPropertyPath = "Invalid property path.";
if (splittedPropertyPathCategory.size() == 3)
{
splittedPropertyPathCategory.pop_back();
subCategoryName = splittedPropertyPathCategory.back();
}
else if (splittedPropertyPathCategory.size() == 4)
{
splittedPropertyPathCategory.pop_back();
subCategoryName = splittedPropertyPathCategory.back();
splittedPropertyPathCategory.pop_back();
subSubCategoryName = splittedPropertyPathCategory.back();
}
// ########## Material Settings ##########
if (categoryName == "Material Settings")
{
if (propertyName == "Shader")
{
return AZStd::make_any<AZStd::string>(pMaterial->GetShaderName().toUtf8().data());
}
else if (propertyName == "Surface Type")
{
QString stringValue = pMaterial->GetSurfaceTypeName();
if (stringValue.startsWith("mat_"))
{
stringValue.remove(0, 4);
}
return AZStd::make_any<AZStd::string>(stringValue.toLatin1().data());
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid material setting.").toUtf8().data());
}
}
// ########## Opacity Settings ##########
else if (categoryName == "Opacity Settings")
{
if (propertyName == "Opacity")
{
AZ::s64 intValue = aznumeric_cast<AZ::s64>(pMaterial->GetShaderResources().m_LMaterial.m_Opacity * 100.0f);
return AZStd::make_any<AZ::s64>(intValue);
}
else if (propertyName == "AlphaTest")
{
AZ::s64 intValue = aznumeric_cast<AZ::s64>(pMaterial->GetShaderResources().m_AlphaRef * 100.0f);
return AZStd::make_any<AZ::s64>(intValue);
}
else if (propertyName == "Additive")
{
return AZStd::make_any<bool>(pMaterial->GetFlags() & MTL_FLAG_ADDITIVE);
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid opacity setting.").toUtf8().data());
}
}
// ########## Lighting Settings ##########
else if (categoryName == "Lighting Settings")
{
if (propertyName == "Diffuse Color")
{
QColor col = ColorLinearToGamma(ColorF(
pMaterial->GetShaderResources().m_LMaterial.m_Diffuse.r,
pMaterial->GetShaderResources().m_LMaterial.m_Diffuse.g,
pMaterial->GetShaderResources().m_LMaterial.m_Diffuse.b));
return AZStd::make_any<AZ::Color>(col.red(), col.green(), col.blue(), 1.0f);
}
else if (propertyName == "Specular Color")
{
QColor col = ColorLinearToGamma(ColorF(
pMaterial->GetShaderResources().m_LMaterial.m_Specular.r / pMaterial->GetShaderResources().m_LMaterial.m_Specular.a,
pMaterial->GetShaderResources().m_LMaterial.m_Specular.g / pMaterial->GetShaderResources().m_LMaterial.m_Specular.a,
pMaterial->GetShaderResources().m_LMaterial.m_Specular.b / pMaterial->GetShaderResources().m_LMaterial.m_Specular.a));
return AZStd::make_any<AZ::Color>(col.red(), col.green(), col.blue(), 1.0f);
}
else if (propertyName == "Glossiness")
{
return AZStd::make_any<float>(pMaterial->GetShaderResources().m_LMaterial.m_Smoothness);
}
else if (propertyName == "Specular Level")
{
return AZStd::make_any<float>(pMaterial->GetShaderResources().m_LMaterial.m_Specular.a);
}
else if (propertyName == "Emissive Color")
{
QColor col = ColorLinearToGamma(ColorF(
pMaterial->GetShaderResources().m_LMaterial.m_Emittance.r,
pMaterial->GetShaderResources().m_LMaterial.m_Emittance.g,
pMaterial->GetShaderResources().m_LMaterial.m_Emittance.b));
return AZStd::make_any<AZ::Color>(col.red(), col.green(), col.blue(), 1.0f);
}
else if (propertyName == "Emissive Intensity")
{
return AZStd::make_any<float>(pMaterial->GetShaderResources().m_LMaterial.m_Emittance.a);
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid lighting setting.").toUtf8().data());
}
}
// ########## Advanced ##########
else if (categoryName == "Advanced")
{
if (propertyName == "Allow layer activation")
{
return AZStd::make_any<bool>(pMaterial->LayerActivationAllowed());
}
else if (propertyName == "2 Sided")
{
return AZStd::make_any<bool>(pMaterial->GetFlags() & MTL_FLAG_2SIDED);
}
else if (propertyName == "No Shadow")
{
return AZStd::make_any<bool>(pMaterial->GetFlags() & MTL_FLAG_NOSHADOW);
}
else if (propertyName == "Use Scattering")
{
return AZStd::make_any<bool>(pMaterial->GetFlags() & MTL_FLAG_SCATTER);
}
else if (propertyName == "Hide After Breaking")
{
return AZStd::make_any<bool>(pMaterial->GetFlags() & MTL_FLAG_HIDEONBREAK);
}
else if (propertyName == "Fog Volume Shading Quality High")
{
return AZStd::make_any<bool>(pMaterial->GetFlags() & MTL_FLAG_FOG_VOLUME_SHADING_QUALITY_HIGH);
}
else if (propertyName == "Blend Terrain Color")
{
return AZStd::make_any<bool>(pMaterial->GetFlags() & MTL_FLAG_BLEND_TERRAIN);
}
else if (propertyName == "Voxel Coverage")
{
return AZStd::make_any<float>(aznumeric_cast<float>(pMaterial->GetShaderResources().m_VoxelCoverage) / 255.0f);
}
else if (propertyName == "Link to Material")
{
return AZStd::make_any<AZStd::string>(pMaterial->GetMatInfo()->GetMaterialLinkName());
}
else if (propertyName == "Propagate Material Settings")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_MATERIAL_SETTINGS);
}
else if (propertyName == "Propagate Opacity Settings")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_OPACITY);
}
else if (propertyName == "Propagate Lighting Settings")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_LIGHTING);
}
else if (propertyName == "Propagate Advanced Settings")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_ADVANCED);
}
else if (propertyName == "Propagate Texture Maps")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_TEXTURES);
}
else if (propertyName == "Propagate Shader Params")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_SHADER_PARAMS);
}
else if (propertyName == "Propagate Shader Generation")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_SHADER_GEN);
}
else if (propertyName == "Propagate Vertex Deformation")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_VERTEX_DEF);
}
else if (propertyName == "Propagate Layer Presets")
{
return AZStd::make_any<bool>(pMaterial->GetPropagationFlags() & MTL_PROPAGATE_LAYER_PRESETS);
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid advanced setting.").toUtf8().data());
}
}
// ########## Texture Maps ##########
else if (categoryName == "Texture Maps")
{
SInputShaderResources& shaderResources = pMaterial->GetShaderResources();
// ########## Texture Maps / [name] ##########
if (splittedPropertyPath.size() == 2)
{
uint16 nSlot = aznumeric_cast<uint16>(TryConvertingCStringToEEfResTextures(propertyName));
SEfResTexture* pTextureRes = shaderResources.GetTextureResource(nSlot);
if (!pTextureRes || pTextureRes->m_Name.empty())
{
AZ_Warning("ShadersSystem", false, "PyGetProperty - Error: empty texture slot [%d] (or missing name) for material %s",
nSlot, pMaterial->GetName().toStdString().c_str());
return AZStd::any();
}
else
{
return AZStd::make_any<AZStd::string>(pTextureRes->m_Name);
}
}
// ########## Texture Maps / [TexType | Filter | IsProjectedTexGen | TexGenType ] ##########
else if (splittedPropertyPath.size() == 3)
{
SEfResTexture* pTextureRes = shaderResources.GetTextureResource(TryConvertingCStringToEEfResTextures(subCategoryName));
if (pTextureRes)
{
if (propertyName == "TexType")
{
return AZStd::make_any<AZStd::string>(TryConvertingETEX_TypeToCString(pTextureRes->m_Sampler.m_eTexType).toLatin1().data());
}
else if (propertyName == "Filter")
{
return AZStd::make_any<AZStd::string>(TryConvertingTexFilterToCString(pTextureRes->m_Filter).toLatin1().data());
}
else if (propertyName == "IsProjectedTexGen")
{
return AZStd::make_any<bool>(pTextureRes->AddModificator()->m_bTexGenProjected);
}
else if (propertyName == "TexGenType")
{
return AZStd::make_any<AZStd::string>(TryConvertingETexGenTypeToCString(pTextureRes->AddModificator()->m_eTGType).toLatin1().data());
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid property.").toUtf8().data());
}
}
else
{
AZ_Warning("ShadersSystem", false, "PyGetProperty - Error: empty 'subCategoryName' texture slot [%d] for material %s",
aznumeric_cast<uint16>(TryConvertingCStringToEEfResTextures(subCategoryName)),
pMaterial->GetName().toStdString().c_str());
}
}
// ########## Texture Maps / [Tiling | Rotator | Oscillator] ##########
else if (splittedPropertyPath.size() == 4)
{
SEfResTexture* pTextureRes = shaderResources.GetTextureResource(TryConvertingCStringToEEfResTextures(subSubCategoryName));
if (pTextureRes)
{
if (subCategoryName == "Tiling")
{
if (propertyName == "IsTileU")
{
return AZStd::make_any<bool>(pTextureRes->m_bUTile);
}
else if (propertyName == "IsTileV")
{
return AZStd::make_any<bool>(pTextureRes->m_bVTile);
}
else if (propertyName == "TileU")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_Tiling[0]);
}
else if (propertyName == "TileV")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_Tiling[1]);
}
else if (propertyName == "OffsetU")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_Offs[0]);
}
else if (propertyName == "OffsetV")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_Offs[1]);
}
else if (propertyName == "RotateU")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_Rot[0]);
}
else if (propertyName == "RotateV")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_Rot[1]);
}
else if (propertyName == "RotateW")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_Rot[2]);
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid property.").toUtf8().data());
}
}
else if (subCategoryName == "Rotator")
{
if (propertyName == "Type")
{
return AZStd::make_any<AZStd::string>(TryConvertingETexModRotateTypeToCString(pTextureRes->AddModificator()->m_eRotType).toLatin1().data());
}
else if (propertyName == "Rate")
{
return AZStd::make_any<float>(Word2Degr(pTextureRes->AddModificator()->m_RotOscRate[2]));
}
else if (propertyName == "Phase")
{
return AZStd::make_any<float>(Word2Degr(pTextureRes->AddModificator()->m_RotOscPhase[2]));
}
else if (propertyName == "Amplitude")
{
return AZStd::make_any<float>(Word2Degr(pTextureRes->AddModificator()->m_RotOscAmplitude[2]));
}
else if (propertyName == "CenterU")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_RotOscCenter[0]);
}
else if (propertyName == "CenterV")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_RotOscCenter[1]);
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid property.").toUtf8().data());
}
}
else if (subCategoryName == "Oscillator")
{
if (propertyName == "TypeU")
{
return AZStd::make_any<AZStd::string>(TryConvertingETexModMoveTypeToCString(pTextureRes->AddModificator()->m_eMoveType[0]).toLatin1().data());
}
else if (propertyName == "TypeV")
{
return AZStd::make_any<AZStd::string>(TryConvertingETexModMoveTypeToCString(pTextureRes->AddModificator()->m_eMoveType[1]).toLatin1().data());
}
else if (propertyName == "RateU")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_OscRate[0]);
}
else if (propertyName == "RateV")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_OscRate[1]);
}
else if (propertyName == "PhaseU")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_OscPhase[0]);
}
else if (propertyName == "PhaseV")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_OscPhase[1]);
}
else if (propertyName == "AmplitudeU")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_OscAmplitude[0]);
}
else if (propertyName == "AmplitudeV")
{
return AZStd::make_any<float>(pTextureRes->AddModificator()->m_OscAmplitude[1]);
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid property.").toUtf8().data());
}
}
else
{
throw std::runtime_error((QString("\"") + subCategoryName + "\" is an invalid sub category.").toUtf8().data());
}
}
else
{
AZ_Warning("ShadersSystem", false, "PyGetProperty - Error: empty 'subSubCategoryName' texture slot [%d] for material %s",
aznumeric_cast<uint16>(TryConvertingCStringToEEfResTextures(subSubCategoryName)),
pMaterial->GetName().toStdString().c_str());
}
}
else
{
throw std::runtime_error(errorMsgInvalidPropertyPath.toUtf8().data());
}
}
// ########## Shader Params ##########
else if (categoryName == "Shader Params")
{
auto& shaderParams = pMaterial->GetShaderResources().m_ShaderParams;
for (int i = 0; i < shaderParams.size(); i++)
{
if (propertyName == ParseUINameFromPublicParamsScript(shaderParams[i].m_Script.c_str()))
{
if (shaderParams[i].m_Type == eType_FLOAT)
{
return AZStd::make_any<float>(shaderParams[i].m_Value.m_Float);
}
else if (shaderParams[i].m_Type == eType_FCOLOR)
{
QColor col = ColorLinearToGamma(ColorF(
shaderParams[i].m_Value.m_Vector[0],
shaderParams[i].m_Value.m_Vector[1],
shaderParams[i].m_Value.m_Vector[2]));
return AZStd::make_any<AZ::Color>(col.red(), col.green(), col.blue(), 1.0f);
}
}
}
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid shader param.").toUtf8().data());
}
// ########## Shader Generation Params ##########
else if (categoryName == "Shader Generation Params")
{
for (int i = 0; i < pMaterial->GetShaderGenParamsVars()->GetNumVariables(); i++)
{
if (propertyName == pMaterial->GetShaderGenParamsVars()->GetVariable(i)->GetHumanName())
{
// get the current Boolean value for this shader variable and return so that the std::runtime_error() will not throw to indicate failure
bool boolValue = false;
pMaterial->GetShaderGenParamsVars()->GetVariable(i)->Get(boolValue);
return AZStd::make_any<bool>(boolValue);
}
}
// not matching property was found, so throw a std::runtime_error() to indcate an error
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid shader generation param.").toUtf8().data());
}
// ########## Vertex Deformation ##########
else if (categoryName == "Vertex Deformation")
{
// ########## Vertex Deformation / [ Type | Wave Length X | Wave Length Y | Wave Length Z | Wave Length W | Noise Scale ] ##########
if (splittedPropertyPath.size() == 2)
{
if (propertyName == "Type")
{
return AZStd::make_any<AZStd::string>(TryConvertingEDeformTypeToCString(pMaterial->GetShaderResources().m_DeformInfo.m_eType).toLatin1().data());
}
else if (propertyName == "Wave Length X")
{
return AZStd::make_any<float>(pMaterial->GetShaderResources().m_DeformInfo.m_fDividerX);
}
else if (propertyName == "Noise Scale")
{
return AZStd::make_any<AZ::Vector3>(
pMaterial->GetShaderResources().m_DeformInfo.m_vNoiseScale[0],
pMaterial->GetShaderResources().m_DeformInfo.m_vNoiseScale[1],
pMaterial->GetShaderResources().m_DeformInfo.m_vNoiseScale[2]);
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid property.").toUtf8().data());
}
}
// ########## Vertex Deformation / [ Wave X ] ##########
else if (splittedPropertyPath.size() == 3)
{
if (subCategoryName == "Wave X")
{
SWaveForm2 currentWaveForm;
if (subCategoryName == "Wave X")
{
currentWaveForm = pMaterial->GetShaderResources().m_DeformInfo.m_WaveX;
}
if (propertyName == "Type")
{
return AZStd::make_any<AZStd::string>(TryConvertingEWaveFormToCString(currentWaveForm.m_eWFType).toLatin1().data());
}
else if (propertyName == "Level")
{
return AZStd::make_any<float>(currentWaveForm.m_Level);
}
else if (propertyName == "Amplitude")
{
return AZStd::make_any<float>(currentWaveForm.m_Amp);
}
else if (propertyName == "Phase")
{
return AZStd::make_any<float>(currentWaveForm.m_Phase);
}
else if (propertyName == "Frequency")
{
return AZStd::make_any<float>(currentWaveForm.m_Freq);
}
else
{
throw std::runtime_error((QString("\"") + propertyName + "\" is an invalid property.").toUtf8().data());
}
}
else
{
throw std::runtime_error((QString("\"") + categoryName + "\" is an invalid category.").toUtf8().data());
}
}
else
{
throw std::runtime_error(errorMsgInvalidPropertyPath.toUtf8().data());
}
}
// ########## Layer Presets ##########
else if (categoryName == "Layer Presets")
{
// names are "Shader1", "Shader2" and "Shader3", because all have the name "Shader" in material editor
if (splittedPropertyPath.size() == 2)
{
int shaderNumber = -1;
if (propertyName == "Shader1")
{
shaderNumber = 0;
}
else if (propertyName == "Shader2")
{
shaderNumber = 1;
}
else if (propertyName == "Shader3")
{
shaderNumber = 2;
}
else
{
throw std::runtime_error("Invalid shader.");
}
return AZStd::make_any<AZStd::string>(pMaterial->GetMtlLayerResources()[shaderNumber].m_shaderName.toLatin1().data());
}
else if (splittedPropertyPath.size() == 3)
{
if (propertyName == "No Draw")
{
int shaderNumber = -1;
if (subCategoryName == "Shader1")
{
shaderNumber = 0;
}
else if (subCategoryName == "Shader2")
{
shaderNumber = 1;
}
else if (subCategoryName == "Shader3")
{
shaderNumber = 2;
}
else
{
throw std::runtime_error("Invalid shader.");
}
return AZStd::make_any<bool>(pMaterial->GetMtlLayerResources()[shaderNumber].m_nFlags & MTL_LAYER_USAGE_NODRAW);
}
}
}
throw std::runtime_error(errorMsgInvalidPropertyPath.toUtf8().data());
return AZStd::any();
}
void PySetProperty(const char* pPathAndMaterialName, const char* pPathAndPropertyName, const AZStd::any& value)
{
CMaterial* pMaterial = TryLoadingMaterial(pPathAndMaterialName);
std::deque<QString> splittedPropertyPath = PreparePropertyPath(pPathAndPropertyName);
std::deque<QString> splittedPropertyPathCategory = splittedPropertyPath;
QString categoryName = splittedPropertyPath.front();
QString subCategoryName = "None";
QString subSubCategoryName = "None";
QString propertyName = splittedPropertyPath.back();
QString errorMsgInvalidPropertyPath = "Invalid property path.";
if (!ValidateProperty(pMaterial, splittedPropertyPath, value))
{
throw std::runtime_error("Invalid property.");
}
QString undoMsg = "Set Material Property";
CUndo undo(undoMsg.toUtf8().data());
pMaterial->RecordUndo(undoMsg.toUtf8().data(), true);
if (splittedPropertyPathCategory.size() == 3)
{
splittedPropertyPathCategory.pop_back();
subCategoryName = splittedPropertyPathCategory.back();
}
else if (splittedPropertyPathCategory.size() == 4)
{
splittedPropertyPathCategory.pop_back();
subCategoryName = splittedPropertyPathCategory.back();
splittedPropertyPathCategory.pop_back();
subSubCategoryName = splittedPropertyPathCategory.back();
}
// ########## Material Settings ##########
if (categoryName == "Material Settings")
{
if (propertyName == "Shader")
{
pMaterial->SetShaderName(AZStd::any_cast<AZStd::string_view>(value).data());
}
else if (propertyName == "Surface Type")
{
bool isSurfaceExist(false);
QString realSurfacename = "";
ISurfaceTypeEnumerator* pSurfaceTypeEnum = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager()->GetEnumerator();
if (pSurfaceTypeEnum)
{
for (ISurfaceType* pSurfaceType = pSurfaceTypeEnum->GetFirst(); pSurfaceType; pSurfaceType = pSurfaceTypeEnum->GetNext())
{
QString surfaceName = pSurfaceType->GetName();
realSurfacename = surfaceName;
if (surfaceName.left(4) == "mat_")
{
surfaceName.remove(0, 4);
}
if (surfaceName == AZStd::any_cast<AZStd::string_view>(value).data())
{
isSurfaceExist = true;
pMaterial->SetSurfaceTypeName(realSurfacename);
}
}
if (!isSurfaceExist)
{
throw std::runtime_error("Invalid surface type name.");
}
}
else
{
throw std::runtime_error("Surface Type Enumerator corrupted.");
}
}
}
// ########## Opacity Settings ##########
else if (categoryName == "Opacity Settings")
{
if (propertyName == "Opacity")
{
pMaterial->GetShaderResources().m_LMaterial.m_Opacity = PyFetchNumericType<float>(value) / 100.0f;
}
else if (propertyName == "AlphaTest")
{
pMaterial->GetShaderResources().m_AlphaRef = PyFetchNumericType<float>(value) / 100.0f;
}
else if (propertyName == "Additive")
{
SetMaterialFlag(pMaterial, MTL_FLAG_ADDITIVE, PyFetchNumericType<bool>(value));
}
}
// ########## Lighting Settings ##########
else if (categoryName == "Lighting Settings")
{
if (propertyName == "Diffuse Color")
{
const AZ::Color* color = AZStd::any_cast<AZ::Color>(&value);
pMaterial->GetShaderResources().m_LMaterial.m_Diffuse = ColorGammaToLinear(QColor(color->GetR8(), color->GetG8(), color->GetB8()));
}
else if (propertyName == "Specular Color")
{
const AZ::Color* color = AZStd::any_cast<AZ::Color>(&value);
ColorF colorFloat = ColorGammaToLinear(QColor(color->GetR8(), color->GetG8(), color->GetB8()));
colorFloat.a = pMaterial->GetShaderResources().m_LMaterial.m_Specular.a;
colorFloat.r *= colorFloat.a;
colorFloat.g *= colorFloat.a;
colorFloat.b *= colorFloat.a;
pMaterial->GetShaderResources().m_LMaterial.m_Specular = colorFloat;
}
else if (propertyName == "Glossiness" || propertyName == "Smoothness")
{
pMaterial->GetShaderResources().m_LMaterial.m_Smoothness = PyFetchNumericType<float>(value);
}
else if (propertyName == "Specular Level")
{
const float localVariableAlpha = PyFetchNumericType<float>(value);
ColorF colorFloat = pMaterial->GetShaderResources().m_LMaterial.m_Specular;
colorFloat.r *= localVariableAlpha;
colorFloat.g *= localVariableAlpha;
colorFloat.b *= localVariableAlpha;
colorFloat.a = 1.0f;
pMaterial->GetShaderResources().m_LMaterial.m_Specular = colorFloat;
}
else if (propertyName == "Emissive Color")
{
const AZ::Color* color = AZStd::any_cast<AZ::Color>(&value);
float emissiveIntensity = pMaterial->GetShaderResources().m_LMaterial.m_Emittance.a;
pMaterial->GetShaderResources().m_LMaterial.m_Emittance = ColorGammaToLinear(QColor(color->GetR8(), color->GetG8(), color->GetB8()));
pMaterial->GetShaderResources().m_LMaterial.m_Emittance.a = emissiveIntensity;
}
else if (propertyName == "Emissive Intensity")
{
pMaterial->GetShaderResources().m_LMaterial.m_Emittance.a = PyFetchNumericType<float>(value);
}
}
// ########## Advanced ##########
else if (categoryName == "Advanced")
{
if (propertyName == "Allow layer activation")
{
pMaterial->SetLayerActivation(PyFetchNumericType<bool>(value));
}
else if (propertyName == "2 Sided")
{
SetMaterialFlag(pMaterial, MTL_FLAG_2SIDED, PyFetchNumericType<bool>(value));
}
else if (propertyName == "No Shadow")
{
SetMaterialFlag(pMaterial, MTL_FLAG_NOSHADOW, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Use Scattering")
{
SetMaterialFlag(pMaterial, MTL_FLAG_SCATTER, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Hide After Breaking")
{
SetMaterialFlag(pMaterial, MTL_FLAG_HIDEONBREAK, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Fog Volume Shading Quality High")
{
SetMaterialFlag(pMaterial, MTL_FLAG_FOG_VOLUME_SHADING_QUALITY_HIGH, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Blend Terrain Color")
{
SetMaterialFlag(pMaterial, MTL_FLAG_BLEND_TERRAIN, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Voxel Coverage")
{
pMaterial->GetShaderResources().m_VoxelCoverage = aznumeric_cast<uint8>(PyFetchNumericType<float>(value) * 255.0f);
}
else if (propertyName == "Link to Material")
{
pMaterial->GetMatInfo()->SetMaterialLinkName(AZStd::any_cast<AZStd::string_view>(value).data());
}
else if (propertyName == "Propagate Material Settings")
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_MATERIAL_SETTINGS, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Propagate Opacity Settings")
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_OPACITY, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Propagate Lighting Settings")
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_LIGHTING, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Propagate Advanced Settings")
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_ADVANCED, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Propagate Texture Maps")
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_TEXTURES, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Propagate Shader Params")
{
if (PyFetchNumericType<bool>(value))
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_MATERIAL_SETTINGS, true);
}
SetPropagationFlag(pMaterial, MTL_PROPAGATE_SHADER_PARAMS, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Propagate Shader Generation")
{
if (PyFetchNumericType<bool>(value))
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_MATERIAL_SETTINGS, true);
}
SetPropagationFlag(pMaterial, MTL_PROPAGATE_SHADER_GEN, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Propagate Vertex Deformation")
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_VERTEX_DEF, PyFetchNumericType<bool>(value));
}
else if (propertyName == "Propagate Layer Presets")
{
SetPropagationFlag(pMaterial, MTL_PROPAGATE_LAYER_PRESETS, PyFetchNumericType<bool>(value));
}
}
// ########## Texture Maps ##########
else if (categoryName == "Texture Maps")
{
// ########## Texture Maps / [name] ##########
SInputShaderResources& shaderResources = pMaterial->GetShaderResources();
if (splittedPropertyPath.size() == 2)
{
uint16 nSlot = aznumeric_cast<uint16>(TryConvertingCStringToEEfResTextures(propertyName));
auto stringValue = AZStd::any_cast<AZStd::string_view>(value);
if (stringValue.empty())
{
AZ_Warning("ShadersSystem", false, "PySetProperty - Error: empty texture [%d] name for material %s",
nSlot, pMaterial->GetName().toStdString().c_str());
}
// notice that the following is an insertion operation if the index did not exist in the map
shaderResources.m_TexturesResourcesMap[nSlot].m_Name = stringValue.data();
}
// ########## Texture Maps / [TexType | Filter | IsProjectedTexGen | TexGenType ] ##########
else if (splittedPropertyPath.size() == 3)
{
uint16 nSlot = aznumeric_cast<uint16>(TryConvertingCStringToEEfResTextures(subCategoryName));
// notice that each of the following will add the texture slot if did not exist yet
if (propertyName == "TexType")
{
shaderResources.m_TexturesResourcesMap[nSlot].m_Sampler.m_eTexType = TryConvertingCStringToETEX_Type(AZStd::any_cast<AZStd::string_view>(value));
}
else if (propertyName == "Filter")
{
shaderResources.m_TexturesResourcesMap[nSlot].m_Filter = TryConvertingCStringToTexFilter(AZStd::any_cast<AZStd::string_view>(value));
}
else if (propertyName == "IsProjectedTexGen")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_bTexGenProjected = PyFetchNumericType<bool>(value);
}
else if (propertyName == "TexGenType")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_eTGType = TryConvertingCStringToETexGenType(AZStd::any_cast<AZStd::string_view>(value));
}
}
// ########## Texture Maps / [Tiling | Rotator | Oscillator] ##########
else if (splittedPropertyPath.size() == 4)
{
uint16 nSlot = aznumeric_cast<uint16>(TryConvertingCStringToEEfResTextures(subSubCategoryName));
if (subCategoryName == "Tiling")
{
if (propertyName == "IsTileU")
{
shaderResources.m_TexturesResourcesMap[nSlot].m_bUTile = PyFetchNumericType<bool>(value);
}
else if (propertyName == "IsTileV")
{
shaderResources.m_TexturesResourcesMap[nSlot].m_bVTile = PyFetchNumericType<bool>(value);
}
else if (propertyName == "TileU")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_Tiling[0] = PyFetchNumericType<float>(value);
}
else if (propertyName == "TileV")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_Tiling[1] = PyFetchNumericType<float>(value);
}
else if (propertyName == "OffsetU")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_Offs[0] = PyFetchNumericType<float>(value);
}
else if (propertyName == "OffsetV")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_Offs[1] = PyFetchNumericType<float>(value);
}
else if (propertyName == "RotateU")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_Rot[0] = Degr2Word(PyFetchNumericType<float>(value));
}
else if (propertyName == "RotateV")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_Rot[1] = Degr2Word(PyFetchNumericType<float>(value));
}
else if (propertyName == "RotateW")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_Rot[2] = Degr2Word(PyFetchNumericType<float>(value));
}
}
else if (subCategoryName == "Rotator")
{
if (propertyName == "Type")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_eRotType = TryConvertingCStringToETexModRotateType(AZStd::any_cast<AZStd::string_view>(value));
}
else if (propertyName == "Rate")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_RotOscRate[2] = Degr2Word(PyFetchNumericType<float>(value));
}
else if (propertyName == "Phase")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_RotOscPhase[2] = Degr2Word(PyFetchNumericType<float>(value));
}
else if (propertyName == "Amplitude")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_RotOscAmplitude[2] = Degr2Word(PyFetchNumericType<float>(value));
}
else if (propertyName == "CenterU")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_RotOscCenter[0] = PyFetchNumericType<float>(value);
}
else if (propertyName == "CenterV")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_RotOscCenter[1] = PyFetchNumericType<float>(value);
}
}
else if (subCategoryName == "Oscillator")
{
if (propertyName == "TypeU")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_eMoveType[0] = TryConvertingCStringToETexModMoveType(AZStd::any_cast<AZStd::string_view>(value));
}
else if (propertyName == "TypeV")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_eMoveType[1] = TryConvertingCStringToETexModMoveType(AZStd::any_cast<AZStd::string_view>(value));
}
else if (propertyName == "RateU")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_OscRate[0] = PyFetchNumericType<float>(value);
}
else if (propertyName == "RateV")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_OscRate[1] = PyFetchNumericType<float>(value);
}
else if (propertyName == "PhaseU")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_OscPhase[0] = PyFetchNumericType<float>(value);
}
else if (propertyName == "PhaseV")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_OscPhase[1] = PyFetchNumericType<float>(value);
}
else if (propertyName == "AmplitudeU")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_OscAmplitude[0] = PyFetchNumericType<float>(value);
}
else if (propertyName == "AmplitudeV")
{
shaderResources.m_TexturesResourcesMap[nSlot].AddModificator()->m_OscAmplitude[1] = PyFetchNumericType<float>(value);
}
}
}
}
// ########## Shader Params ##########
else if (categoryName == "Shader Params")
{
auto& shaderParams = pMaterial->GetShaderResources().m_ShaderParams;
for (int i = 0; i < shaderParams.size(); i++)
{
if (propertyName == ParseUINameFromPublicParamsScript(shaderParams[i].m_Script.c_str()))
{
if (shaderParams[i].m_Type == eType_FLOAT)
{
shaderParams[i].m_Value.m_Float = PyFetchNumericType<float>(value);
break;
}
else if (shaderParams[i].m_Type == eType_FCOLOR)
{
const AZ::Color* color = AZStd::any_cast<AZ::Color>(&value);
ColorF colorLinear = ColorGammaToLinear(QColor(color->GetR8(), color->GetG8(), color->GetB8()));
shaderParams[i].m_Value.m_Vector[0] = colorLinear.r;
shaderParams[i].m_Value.m_Vector[1] = colorLinear.g;
shaderParams[i].m_Value.m_Vector[2] = colorLinear.b;
break;
}
else
{
throw std::runtime_error("Invalid data type (Shader Params)");
}
}
}
}
// ########## Shader Generation Params ##########
else if (categoryName == "Shader Generation Params")
{
for (int i = 0; i < pMaterial->GetShaderGenParamsVars()->GetNumVariables(); i++)
{
if (propertyName == pMaterial->GetShaderGenParamsVars()->GetVariable(i)->GetHumanName())
{
CVarBlock* shaderGenBlock = pMaterial->GetShaderGenParamsVars();
shaderGenBlock->GetVariable(i)->Set(PyFetchNumericType<bool>(value));
pMaterial->SetShaderGenParamsVars(shaderGenBlock);
break;
}
}
}
// ########## Vertex Deformation ##########
else if (categoryName == "Vertex Deformation")
{
// ########## Vertex Deformation / [ Type | Wave Length X | Noise Scale ] ##########
if (splittedPropertyPath.size() == 2)
{
if (propertyName == "Type")
{
pMaterial->GetShaderResources().m_DeformInfo.m_eType = TryConvertingCStringToEDeformType(AZStd::any_cast<AZStd::string_view>(value));
}
else if (propertyName == "Wave Length X")
{
pMaterial->GetShaderResources().m_DeformInfo.m_fDividerX = PyFetchNumericType<float>(value);
}
else if (propertyName == "Noise Scale")
{
const AZ::Vector3* vecValue = AZStd::any_cast<AZ::Vector3>(&value);
pMaterial->GetShaderResources().m_DeformInfo.m_vNoiseScale[0] = vecValue->GetX();
pMaterial->GetShaderResources().m_DeformInfo.m_vNoiseScale[1] = vecValue->GetY();
pMaterial->GetShaderResources().m_DeformInfo.m_vNoiseScale[2] = vecValue->GetZ();
}
}
// ########## Vertex Deformation / [ Wave X ] ##########
else if (splittedPropertyPath.size() == 3)
{
if (subCategoryName == "Wave X")
{
SWaveForm2& currentWaveForm = pMaterial->GetShaderResources().m_DeformInfo.m_WaveX;
if (propertyName == "Type")
{
currentWaveForm.m_eWFType = TryConvertingCStringToEWaveForm(AZStd::any_cast<AZStd::string_view>(value));
}
else if (propertyName == "Level")
{
currentWaveForm.m_Level = PyFetchNumericType<float>(value);
}
else if (propertyName == "Amplitude")
{
currentWaveForm.m_Amp = PyFetchNumericType<float>(value);
}
else if (propertyName == "Phase")
{
currentWaveForm.m_Phase = PyFetchNumericType<float>(value);
}
else if (propertyName == "Frequency")
{
currentWaveForm.m_Freq = PyFetchNumericType<float>(value);
}
}
}
}
// ########## Layer Presets ##########
else if (categoryName == "Layer Presets")
{
// names are "Shader1", "Shader2" and "Shader3", because all have the name "Shader" in material editor
if (splittedPropertyPath.size() == 2)
{
int shaderNumber = -1;
if (propertyName == "Shader1")
{
shaderNumber = 0;
}
else if (propertyName == "Shader2")
{
shaderNumber = 1;
}
else if (propertyName == "Shader3")
{
shaderNumber = 2;
}
pMaterial->GetMtlLayerResources()[shaderNumber].m_shaderName = AZStd::any_cast<AZStd::string_view>(value).data();
}
else if (splittedPropertyPath.size() == 3)
{
if (propertyName == "No Draw")
{
int shaderNumber = -1;
if (subCategoryName == "Shader1")
{
shaderNumber = 0;
}
else if (subCategoryName == "Shader2")
{
shaderNumber = 1;
}
else if (subCategoryName == "Shader3")
{
shaderNumber = 2;
}
if (pMaterial->GetMtlLayerResources()[shaderNumber].m_nFlags & MTL_LAYER_USAGE_NODRAW && PyFetchNumericType<bool>(value) == false)
{
pMaterial->GetMtlLayerResources()[shaderNumber].m_nFlags = pMaterial->GetMtlLayerResources()[shaderNumber].m_nFlags - MTL_LAYER_USAGE_NODRAW;
}
else if (!(pMaterial->GetMtlLayerResources()[shaderNumber].m_nFlags & MTL_LAYER_USAGE_NODRAW) && PyFetchNumericType<bool>(value) == true)
{
pMaterial->GetMtlLayerResources()[shaderNumber].m_nFlags = pMaterial->GetMtlLayerResources()[shaderNumber].m_nFlags | MTL_LAYER_USAGE_NODRAW;
}
}
}
}
pMaterial->Update();
pMaterial->Save();
GetIEditor()->GetMaterialManager()->OnUpdateProperties(pMaterial, true);
}
}
namespace AzToolsFramework
{
void MaterialPythonFuncsHandler::Reflect(AZ::ReflectContext* context)
{
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
// this will put these methods into the 'azlmbr.legacy.material' module
auto addLegacyMaterial = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
{
methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Legacy/Material")
->Attribute(AZ::Script::Attributes::Module, "legacy.material");
};
addLegacyMaterial(behaviorContext->Method("create", PyMaterialCreate, nullptr, "Creates a material."));
addLegacyMaterial(behaviorContext->Method("create_multi", PyMaterialCreateMulti, nullptr, "Creates a multi-material."));
addLegacyMaterial(behaviorContext->Method("convert_to_multi", PyMaterialConvertToMulti, nullptr, "Converts the selected material to a multi-material."));
addLegacyMaterial(behaviorContext->Method("duplicate_current", PyMaterialDuplicateCurrent, nullptr, "Duplicates the current material."));
addLegacyMaterial(behaviorContext->Method("merge_selection", PyMaterialMergeSelection, nullptr, "Merges the selected materials."));
addLegacyMaterial(behaviorContext->Method("delete_current", PyMaterialDeleteCurrent, nullptr, "Deletes the current material."));
addLegacyMaterial(behaviorContext->Method("get_submaterial", PyGetSubMaterial, nullptr, "Gets sub materials of a material."));
addLegacyMaterial(behaviorContext->Method("get_property", PyGetProperty, nullptr, "Gets a property of a material."));
addLegacyMaterial(behaviorContext->Method("set_property", PySetProperty, nullptr, "Sets a property of a material."));
}
}
}