Files
o3de/Code/Framework/GFxFramework/GFxFramework/MaterialIO/Material.cpp
T
Esteban Papp f414cd3966 More fixes
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
2021-09-08 16:50:57 -07:00

952 lines
38 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/IO/TextStreamWriters.h>
#include <AzCore/XML/rapidxml_print.h>
#include <AzCore/Math/Vector3.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <GFxFramework/MaterialIO/Material.h>
namespace AZ
{
namespace GFxFramework
{
//These strings form the basis for a default illum or physics shader that can be
//used in the case where we are generating new mtl files and not just updating or
//reading an mtl file for processing purposes.
namespace MaterialExport
{
const char* g_subMaterialString = "SubMaterials";
const char* g_materialString = "Material";
const char* g_texturesString = "Textures";
const char* g_textureString = "Texture";
const char* g_diffuseMapName = "Diffuse";
const char* g_specularMapName = "Specular";
const char* g_emissiveMapName = "Emittance";
const char* g_bumpMapName = "Bumpmap";
const char* g_shininessString = "Shininess";
const char* g_opacityString = "Opacity";
const char* g_genString = "GenMask";
const char* g_illumShaderName = "Illum";
const char* g_defaultIllumGenMask = "80000001";
const char* g_defaultNoDrawGenMask = "0";
const char* g_defaultShininess = "10";
const char* g_defaultOpacity = "1";
const char* g_mapString = "Map";
const char* g_fileString = "File";
const char* g_nameString = "Name";
const char* g_shaderString = "Shader";
const char* g_noDrawShaderName = "Nodraw";
const char* g_mtlFlagString = "MtlFlags";
const char* g_whiteColor = "1,1,1";
const char* g_blackColor = "0,0,0";
const char* g_stringGenMask = "StringGenMask";
const char* g_stringGenMaskOption_VertexColors = "%VERTCOLORS";
const char* g_stringPhysicsNoDraw = "PhysicsNoDraw";
const char* g_mtlExtension = ".mtl";
const char* g_dccMaterialExtension = ".dccmtl";
const char* g_dccMaterialHashString = "DccMaterialHash";
const char* g_whiteTexture = "EngineAssets/Textures/white.dds";
const unsigned g_materialNotFound = unsigned(-1);
bool ParseVectorAttribute(rapidxml::xml_attribute<char>* attribute, AZ::Vector3& vectorValue)
{
if (attribute)
{
float x, y, z;
const char* value = attribute->value();
if (azsscanf(value, "%f,%f,%f", &x, &y, &z) == 3)
{
vectorValue.Set(x, y, z);
return true;
}
}
return false;
}
bool ParseFloatAttribute(rapidxml::xml_attribute<char>* attribute, float& floatValue)
{
if (attribute)
{
const char* value = attribute->value();
if (azsscanf(value, "%f", &floatValue) == 1)
{
return true;
}
}
return false;
}
bool ParseUintAttribute(rapidxml::xml_attribute<char>* attribute, AZ::u32& uintValue)
{
if (attribute)
{
const char* value = attribute->value();
if (azsscanf(value, "%u", &uintValue) == 1)
{
return true;
}
}
return false;
}
bool ParseIntAttribute(rapidxml::xml_attribute<char>* attribute, AZ::s32& intValue)
{
if (attribute)
{
const char* value = attribute->value();
if (azsscanf(value, "%d", &intValue) == 1)
{
return true;
}
}
return false;
}
}
Material::Material()
: m_useVertexColor(false)
, m_flags(0)
, m_diffuseColor(AZ::Vector3::CreateOne())
, m_specularColor(AZ::Vector3::CreateZero())
, m_emissiveColor(AZ::Vector3::CreateZero())
, m_opacity(1.f)
, m_shininess(0.f)
, m_dccMaterialHash(0)
{
}
void Material::SetDataFromMtl(rapidxml::xml_node<char>* materialNode)
{
if (materialNode)
{
rapidxml::xml_attribute<char>* name = materialNode->first_attribute(MaterialExport::g_nameString);
rapidxml::xml_attribute<char>* mtlFlags = materialNode->first_attribute(MaterialExport::g_mtlFlagString);
rapidxml::xml_attribute<char>* stringGenMask = materialNode->first_attribute(MaterialExport::g_stringGenMask);
rapidxml::xml_node<char>* textureNode = materialNode->first_node(MaterialExport::g_texturesString);
rapidxml::xml_attribute<char>* diffuseColor = materialNode->first_attribute(MaterialExport::g_diffuseMapName);
rapidxml::xml_attribute<char>* specularColor = materialNode->first_attribute(MaterialExport::g_specularMapName);
rapidxml::xml_attribute<char>* emissiveColor = materialNode->first_attribute(MaterialExport::g_emissiveMapName);
rapidxml::xml_attribute<char>* opacity = materialNode->first_attribute(MaterialExport::g_opacityString);
rapidxml::xml_attribute<char>* shininess = materialNode->first_attribute(MaterialExport::g_shininessString);
rapidxml::xml_attribute<char>* hash = materialNode->first_attribute(MaterialExport::g_dccMaterialHashString);
if (name)
{
m_materialName = AZStd::string(name->value());
}
MaterialExport::ParseVectorAttribute(diffuseColor, m_diffuseColor);
MaterialExport::ParseVectorAttribute(specularColor, m_specularColor);
MaterialExport::ParseVectorAttribute(emissiveColor, m_emissiveColor);
MaterialExport::ParseFloatAttribute(opacity, m_opacity);
MaterialExport::ParseFloatAttribute(shininess, m_shininess);
MaterialExport::ParseUintAttribute(hash, m_dccMaterialHash);
if (mtlFlags)
{
MaterialExport::ParseIntAttribute(mtlFlags, m_flags);
}
if (stringGenMask)
{
AZStd::string stringMask(stringGenMask->value());
if (stringMask.find(MaterialExport::g_stringGenMaskOption_VertexColors) != AZStd::string::npos)
{
m_useVertexColor = true;
}
else
{
m_useVertexColor = false;
}
}
if (textureNode)
{
rapidxml::xml_node<char>* texture = textureNode->first_node(MaterialExport::g_textureString);
while (texture)
{
rapidxml::xml_attribute<char>* mapType = texture->first_attribute(MaterialExport::g_mapString);
rapidxml::xml_attribute<char>* fileName = texture->first_attribute(MaterialExport::g_fileString);
if (!mapType || !fileName)
{
AZ_TracePrintf("Warning", "Detected malformed texture data in MTL file.");
texture = texture->next_sibling(MaterialExport::g_texturesString);
continue;
}
if (azstrnicmp(mapType->value(), MaterialExport::g_diffuseMapName,
AZStd::GetMin(strlen(mapType->value()), strlen(MaterialExport::g_diffuseMapName))) == 0)
{
m_diffuseMap = AZStd::string(fileName->value());
}
if (azstrnicmp(mapType->value(), MaterialExport::g_specularMapName,
AZStd::GetMin(strlen(mapType->value()), strlen(MaterialExport::g_specularMapName))) == 0)
{
m_specularMap = AZStd::string(fileName->value());
}
if (azstrnicmp(mapType->value(), MaterialExport::g_bumpMapName,
AZStd::GetMin(strlen(mapType->value()), strlen(MaterialExport::g_bumpMapName))) == 0)
{
m_normalMap = AZStd::string(fileName->value());
}
texture = texture->next_sibling(MaterialExport::g_textureString);
}
}
}
}
const AZStd::string& Material::GetName() const
{
return m_materialName;
}
void Material::SetName(const AZStd::string& name)
{
m_materialName = name;
}
const AZStd::string& Material::GetTexture(TextureMapType type) const
{
switch (type)
{
case TextureMapType::Diffuse:
return m_diffuseMap;
case TextureMapType::Specular:
return m_specularMap;
case TextureMapType::Bump:
return m_normalMap;
default:
AZ_Assert(false, "Invalid Texture map requested.");
return m_empty;
}
}
void Material::SetTexture(TextureMapType type, const AZStd::string& texture)
{
switch (type)
{
case TextureMapType::Diffuse:
m_diffuseMap = texture;
break;
case TextureMapType::Specular:
m_specularMap = texture;
break;
case TextureMapType::Bump:
m_normalMap = texture;
break;
default:
AZ_Assert(false, "Invalid Texture map requested.");
break;
}
}
bool Material::UseVertexColor() const
{
return m_useVertexColor;
}
void Material::EnableUseVertexColor(bool useVertexColor)
{
m_useVertexColor = useVertexColor;
}
int Material::GetMaterialFlags() const
{
return m_flags;
}
void Material::SetMaterialFlags(int flags)
{
m_flags = flags;
}
const AZ::Vector3& Material::GetDiffuseColor() const
{
return m_diffuseColor;
}
const AZ::Vector3& Material::GetSpecularColor() const
{
return m_specularColor;
}
const AZ::Vector3& Material::GetEmissiveColor() const
{
return m_emissiveColor;
}
float Material::GetOpacity() const
{
return m_opacity;
}
float Material::GetShininess() const
{
return m_shininess;
}
void Material::SetDiffuseColor(const AZ::Vector3& color)
{
m_diffuseColor = color;
}
void Material::SetSpecularColor(const AZ::Vector3& color)
{
m_specularColor = color;
}
void Material::SetEmissiveColor(const AZ::Vector3& color)
{
m_emissiveColor = color;
}
void Material::SetOpacity(float opacity)
{
m_opacity = opacity;
}
void Material::SetShininess(float shininess)
{
m_shininess = shininess;
}
AZ::u32 Material::GetDccMaterialHash() const
{
return m_dccMaterialHash;
}
void Material::SetDccMaterialHash(AZ::u32 hash)
{
m_dccMaterialHash = hash;
}
MaterialGroup::MaterialGroup()
: m_readFromMtl(false)
{
}
size_t MaterialGroup::FindMaterialIndex(const AZStd::string& name)
{
size_t index = 0;
for (const auto& mat : m_materials)
{
if (mat && mat->GetName().compare(name) == 0)
{
return index;
}
++index;
}
return GFxFramework::MaterialExport::g_materialNotFound;
}
size_t MaterialGroup::GetMaterialCount()
{
return m_materials.size();
}
AZStd::shared_ptr<IMaterial> MaterialGroup::GetMaterial(size_t index)
{
if (index < m_materials.size())
{
return m_materials[index];
}
else
{
return nullptr;
}
}
AZStd::shared_ptr<const IMaterial> MaterialGroup::GetMaterial(size_t index) const
{
if (index < m_materials.size())
{
return m_materials[index];
}
else
{
return nullptr;
}
}
void MaterialGroup::AddMaterial(const AZStd::shared_ptr<IMaterial>& material)
{
//Don't add two materials with the same name.
for (const auto& mat : m_materials)
{
if (mat && mat->GetName().compare(material->GetName()) == 0)
{
return;
}
}
m_materials.push_back(material);
}
void MaterialGroup::RemoveMaterial(const AZStd::string& name)
{
for (size_t i = 0; i < m_materials.size(); ++i)
{
if (m_materials[i] && m_materials[i]->GetName().compare(name) == 0)
{
if (m_readFromMtl)
{
RemoveMaterialNode(*(m_materials[i].get()));
}
m_materials.erase(m_materials.begin() + i);
return;
}
}
}
bool MaterialGroup::ReadMtlFile(const char* fileName)
{
IO::SystemFile mtlFile;
bool fileOpened = mtlFile.Open(fileName, IO::SystemFile::SF_OPEN_READ_ONLY);
if (!fileOpened || mtlFile.Length() == 0)
{
//If the file successfully opened, but the length was 0, report an error
if (fileOpened)
{
AZ_Error("MaterialIO", false, "Invalid material file %s. File length was 0. Try removing the file or replacing it with a valid material file.", fileName);
}
m_readFromMtl = false;
return false;
}
//Read mtl file into a persistent buffer, due to the mechanics of rapidxml this buffer must
//have the same lifetime as the mtl file if we intend to edit the file.
m_mtlBuffer.reserve(mtlFile.Length());
mtlFile.Read(mtlFile.Length(), m_mtlBuffer.data());
mtlFile.Close();
//Apparently in rapidxml if 'parse_no_data_nodes' isn't set it creates both value and data nodes
//with the data nodes having precedence such that updating values doesn't work.
m_mtlDoc.parse<rapidxml::parse_no_data_nodes>(m_mtlBuffer.data());
//Parse MTL file for materials and/or submaterials.
rapidxml::xml_node<char>* materialNode = m_mtlDoc.first_node(MaterialExport::g_materialString);
if (!materialNode)
{
AZ_Error("MaterialIO", false, "Invalid material file %s. File does not contain a 'Material' node. Try removing the file or replacing it with a valid material file.", fileName);
m_readFromMtl = false;
return false;
}
rapidxml::xml_node<char>* submaterialNode = materialNode->first_node("SubMaterials");
if (submaterialNode)
{
materialNode = submaterialNode->first_node();
while (materialNode)
{
AZStd::shared_ptr<IMaterial> mat = AZStd::make_shared<Material>();
mat->SetDataFromMtl(materialNode);
AddMaterial(mat);
materialNode = materialNode->next_sibling();
}
}
else
{
AZStd::shared_ptr<IMaterial> mat = AZStd::make_shared<Material>();
mat->SetDataFromMtl(materialNode);
AddMaterial(mat);
}
m_readFromMtl = true;
return true;
}
bool MaterialGroup::WriteMtlFile(const char* fileName)
{
//The MaterialGroup is responsible for updating the mtl XML data and/or creating
//new default mtl files. This needs to be EBUS-ified eventually.
AZ_TraceContext("MTL File Name", fileName);
if (!m_readFromMtl)
{
CreateMtlFile();
}
if(m_readFromMtl)
{
UpdateMtlFile();
}
IO::SystemFile mtlFile;
if (!mtlFile.Open(fileName, IO::SystemFile::SF_OPEN_CREATE | IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
AZ_TracePrintf("Error", "Unable to write MTL file to disk");
return false;
}
//Write out MTL data from rapidXML then write mtl file to disk.
AZStd::vector<char> buffer;
rapidxml::print(AZStd::back_inserter(buffer), m_mtlDoc);
mtlFile.Write(buffer.data(), buffer.size());
mtlFile.Close();
return true;
}
const AZStd::string& MaterialGroup::GetMtlName()
{
return m_materialGroupName;
}
void MaterialGroup::SetMtlName(const AZStd::string& name)
{
m_materialGroupName = name;
}
AZ::u32 MaterialGroup::CalculateDccMaterialHash()
{
// Compound material group hash from sub material hashes.
AZ::Crc32 hash(0u);
for (const auto& mat : m_materials)
{
if (mat)
{
AZ::u32 subMaterialHash = mat->GetDccMaterialHash();
hash.Add(&subMaterialHash, sizeof(AZ::u32));
}
}
return hash;
}
void MaterialGroup::CreateMtlFile()
{
rapidxml::xml_node<char>* rootNode = m_mtlDoc.allocate_node(rapidxml::node_element, MaterialExport::g_materialString);
// MtlFlags
rapidxml::xml_attribute<char>* attr = m_mtlDoc.allocate_attribute(MaterialExport::g_mtlFlagString,
m_mtlDoc.allocate_string(AZStd::to_string(EMaterialFlags::MTL_64BIT_SHADERGENMASK | EMaterialFlags::MTL_FLAG_MULTI_SUBMTL).c_str()));
rootNode->append_attribute(attr);
// DccMaterialHash
const AZStd::string hashString = AZStd::string::format("%u", CalculateDccMaterialHash());
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_dccMaterialHashString, m_mtlDoc.allocate_string(hashString.c_str()));
rootNode->append_attribute(attr);
// SubMaterials
rapidxml::xml_node<char>* subMaterialNode = m_mtlDoc.allocate_node(rapidxml::node_element, MaterialExport::g_subMaterialString);
rootNode->append_node(subMaterialNode);
m_mtlDoc.append_node(rootNode);
for (const auto& mat : m_materials)
{
if (mat)
{
subMaterialNode->append_node(CreateMaterialMtlNode(*(mat.get())));
}
}
}
void MaterialGroup::UpdateMtlFile()
{
// Update DCC material hash
rapidxml::xml_node<char>* rootNode = m_mtlDoc.first_node(MaterialExport::g_materialString);
if (rootNode)
{
rapidxml::xml_attribute<char>* dccMaterialHashAttribute = rootNode->first_attribute(MaterialExport::g_dccMaterialHashString);
if (dccMaterialHashAttribute)
{
const AZStd::string hashString = AZStd::string::format("%u", CalculateDccMaterialHash());
dccMaterialHashAttribute->value(m_mtlDoc.allocate_string(hashString.c_str()));
}
//update or add materials
for (auto& mat : m_materials)
{
if (mat)
{
bool updated = UpdateMaterialNode(*(mat.get()));
if (!updated)
{
AddMaterialNode(*(mat.get()));
}
}
}
}
}
bool MaterialGroup::AddMaterialNode(const IMaterial& mat)
{
rapidxml::xml_node<char>* materialNode = m_mtlDoc.first_node(MaterialExport::g_materialString);
rapidxml::xml_node<char>* submaterialNode = nullptr;
if (!materialNode)
{
AZ_Assert(false, "Attempted to add material to invalid xml document.");
return false;
}
submaterialNode = materialNode->first_node("SubMaterials");
if (submaterialNode)
{
submaterialNode->append_node(CreateMaterialMtlNode(mat));
}
else
{
materialNode->append_node(CreateMaterialMtlNode(mat));
}
return true;
}
struct TextureData
{
AZStd::string fileName;
AZStd::string exportName;
bool updated;
};
bool MaterialGroup::UpdateMaterialNode(const IMaterial& mat)
{
rapidxml::xml_node<char>* node = FindMaterialNode(mat);
if (!node)
{
return false;
}
//Set appropriate attributes
{
rapidxml::xml_attribute<char>* flagAttribute = node->first_attribute(MaterialExport::g_mtlFlagString);
if (flagAttribute)
{
int flag = atoi(flagAttribute->value());
flagAttribute->value(m_mtlDoc.allocate_string(AZStd::to_string(flag).c_str()));
}
}
// Update DCC material hash
{
rapidxml::xml_attribute<char>* dccMaterialHashAttribute = node->first_attribute(MaterialExport::g_dccMaterialHashString);
if (dccMaterialHashAttribute)
{
const AZStd::string hashString = AZStd::string::format("%u", mat.GetDccMaterialHash());
dccMaterialHashAttribute->value(m_mtlDoc.allocate_string(hashString.c_str()));
}
}
{
AZStd::string genMask;
rapidxml::xml_attribute<char>* stringGenMaskAttribute = node->first_attribute(MaterialExport::g_stringGenMask);
if (stringGenMaskAttribute)
{
genMask = AZStd::string(stringGenMaskAttribute->value());
}
if (mat.UseVertexColor() && genMask.find(MaterialExport::g_stringGenMaskOption_VertexColors) == AZStd::string::npos)
{
genMask += MaterialExport::g_stringGenMaskOption_VertexColors;
}
else if (!mat.UseVertexColor())
{
size_t index = genMask.find(MaterialExport::g_stringGenMaskOption_VertexColors);
if (index != AZStd::string::npos)
{
genMask = genMask.substr(0, index) + genMask.substr(index + strlen(MaterialExport::g_stringGenMaskOption_VertexColors), AZStd::string::npos);
}
}
if (stringGenMaskAttribute && genMask.length() > 0)
{
stringGenMaskAttribute->value(m_mtlDoc.allocate_string(genMask.c_str()));
}
else if (stringGenMaskAttribute && genMask.length() == 0)
{
node->remove_attribute(stringGenMaskAttribute);
}
else if (!stringGenMaskAttribute && genMask.length() > 0)
{
stringGenMaskAttribute = m_mtlDoc.allocate_attribute(MaterialExport::g_stringGenMask, m_mtlDoc.allocate_string(genMask.c_str()));
node->append_attribute(stringGenMaskAttribute);
}
}
//Set Texture fields
AZStd::vector<TextureData> textures;
TextureData diffuse;
diffuse.fileName = mat.GetTexture(TextureMapType::Diffuse);
if (diffuse.fileName.empty())
{
// Default to white texture if the material has no textures. This ensures
// meshes purely using material/vertex colors render properly in-engine.
diffuse.fileName = MaterialExport::g_whiteTexture;
}
diffuse.exportName = MaterialExport::g_diffuseMapName;
diffuse.updated = false;
textures.push_back(diffuse);
TextureData specular;
specular.fileName = mat.GetTexture(TextureMapType::Specular);
specular.exportName = MaterialExport::g_specularMapName;
specular.updated = false;
textures.push_back(specular);
TextureData bump;
bump.fileName = mat.GetTexture(TextureMapType::Bump);
bump.exportName = MaterialExport::g_bumpMapName;
bump.updated = false;
textures.push_back(bump);
rapidxml::xml_node<char>* texturesNode = node->first_node(MaterialExport::g_texturesString);
if (texturesNode)
{
for (auto& currentTexture : textures)
{
rapidxml::xml_node<char>* textureNode = texturesNode->first_node(MaterialExport::g_textureString);
while (textureNode)
{
rapidxml::xml_attribute<char>* mapType = textureNode->first_attribute(MaterialExport::g_mapString);
rapidxml::xml_attribute<char>* fileName = textureNode->first_attribute(MaterialExport::g_fileString);
//malformed texture node.
if (!mapType || !fileName)
{
AZ_TracePrintf("Warning", "Detected malformed texture data in MTL file.");
textureNode = textureNode->next_sibling(MaterialExport::g_texturesString);
continue;
}
if (azstrnicmp(mapType->value(), currentTexture.exportName.c_str(),
AZStd::GetMin(strlen(mapType->value()), strlen(currentTexture.exportName.c_str()))) == 0)
{
//We have removed this texture in this case. Remove the texture node.
if (currentTexture.fileName.empty())
{
texturesNode->remove_node(textureNode);
}
else
{
fileName->value(m_mtlDoc.allocate_string(currentTexture.fileName.c_str()));
currentTexture.updated = true;
}
break;
}
textureNode = textureNode->next_sibling(MaterialExport::g_texturesString);
}
//texture not found in list add it.
if (!currentTexture.updated && !currentTexture.fileName.empty())
{
textureNode = CreateTextureMtlNode(currentTexture.exportName.c_str(), currentTexture.fileName.c_str());
texturesNode->append_node(textureNode);
}
}
}
else
{
//Add Texture paramaters to the material.
texturesNode = m_mtlDoc.allocate_node(rapidxml::node_element, MaterialExport::g_texturesString);
if (texturesNode)
{
for (const auto& currentTexture : textures)
{
if (!currentTexture.fileName.empty())
{
rapidxml::xml_node<char>* textureNode = CreateTextureMtlNode(currentTexture.exportName.c_str(), currentTexture.fileName.c_str());
texturesNode->append_node(textureNode);
}
}
node->append_node(texturesNode);
}
}
return true;
}
void MaterialGroup::RemoveMaterialNode(const IMaterial& mat)
{
rapidxml::xml_node<char>* node = FindMaterialNode(mat);
if (node && node->parent())
{
node->parent()->remove_node(node);
}
}
rapidxml::xml_node<char>* MaterialGroup::FindMaterialNode(const IMaterial& mat) const
{
rapidxml::xml_node<char>* materialNode = m_mtlDoc.first_node(MaterialExport::g_materialString);
if (materialNode == nullptr)
{
return nullptr;
}
rapidxml::xml_node<char>* submaterialNode = nullptr;
submaterialNode = materialNode->first_node("SubMaterials");
if (submaterialNode)
{
materialNode = submaterialNode->first_node();
while (materialNode)
{
rapidxml::xml_attribute<char>* name = materialNode->first_attribute(MaterialExport::g_nameString);
if (name && azstrnicmp(name->value(), mat.GetName().c_str(), mat.GetName().size()) == 0)
{
break;
}
materialNode = materialNode->next_sibling();
}
}
else
{
rapidxml::xml_attribute<char>* name = materialNode->first_attribute(MaterialExport::g_nameString);
if (name && azstrnicmp(name->value(), mat.GetName().c_str(), mat.GetName().size()) != 0)
{
return nullptr;
}
}
return materialNode;
}
rapidxml::xml_node<char>* MaterialGroup::CreateMaterialMtlNode(const IMaterial& material)
{
rapidxml::xml_node<char>* materialNode = m_mtlDoc.allocate_node(rapidxml::node_element, MaterialExport::g_materialString);
rapidxml::xml_attribute<char>* attr = m_mtlDoc.allocate_attribute(MaterialExport::g_nameString, m_mtlDoc.allocate_string(material.GetName().c_str()));
materialNode->append_attribute(attr);
int materialFlags = material.GetMaterialFlags();
if (materialFlags & (EMaterialFlags::MTL_FLAG_NODRAW | EMaterialFlags::MTL_FLAG_NODRAW_TOUCHBENDING))
{
materialFlags |= EMaterialFlags::MTL_FLAG_PURE_CHILD;
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_mtlFlagString,
m_mtlDoc.allocate_string(AZStd::to_string(materialFlags).c_str()));
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_shaderString, MaterialExport::g_noDrawShaderName);
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_genString, MaterialExport::g_defaultNoDrawGenMask);
materialNode->append_attribute(attr);
}
else
{
materialFlags |= (EMaterialFlags::MTL_64BIT_SHADERGENMASK | EMaterialFlags::MTL_FLAG_PURE_CHILD);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_mtlFlagString,
m_mtlDoc.allocate_string(AZStd::to_string(materialFlags).c_str()));
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_shaderString, MaterialExport::g_illumShaderName);
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_genString, MaterialExport::g_defaultIllumGenMask);
materialNode->append_attribute(attr);
}
const AZ::Vector3& diffuseColor = material.GetDiffuseColor();
const AZ::Vector3& specularColor = material.GetSpecularColor();
const AZ::Vector3& emissiveColor = material.GetEmissiveColor();
const AZStd::string diffuseColorString = AZStd::string::format("%f,%f,%f", (float)diffuseColor.GetX(), (float)diffuseColor.GetY(), (float)diffuseColor.GetZ());
const AZStd::string specularColorString = AZStd::string::format("%f,%f,%f", (float)specularColor.GetX(), (float)specularColor.GetY(), (float)specularColor.GetZ());
const AZStd::string emissiveColorString = AZStd::string::format("%f,%f,%f", (float)emissiveColor.GetX(), (float)emissiveColor.GetY(), (float)emissiveColor.GetZ());
const AZStd::string opacityString = AZStd::string::format("%f", material.GetOpacity());
const AZStd::string shininessString = AZStd::string::format("%f", material.GetShininess());
const AZStd::string hashString = AZStd::string::format("%u", material.GetDccMaterialHash());
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_diffuseMapName, m_mtlDoc.allocate_string(diffuseColorString.c_str()));
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_specularMapName, m_mtlDoc.allocate_string(specularColorString.c_str()));
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_emissiveMapName, m_mtlDoc.allocate_string(emissiveColorString.c_str()));
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_opacityString, m_mtlDoc.allocate_string(opacityString.c_str()));
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_shininessString, m_mtlDoc.allocate_string(shininessString.c_str()));
materialNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_dccMaterialHashString, m_mtlDoc.allocate_string(hashString.c_str()));
materialNode->append_attribute(attr);
AZStd::string genMask;
if (material.UseVertexColor())
{
genMask += MaterialExport::g_stringGenMaskOption_VertexColors;
}
if (genMask.length() > 0)
{
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_stringGenMask, m_mtlDoc.allocate_string(genMask.c_str()));
materialNode->append_attribute(attr);
}
//Set Texture fields
AZStd::vector<TextureData> textures;
TextureData diffuse;
diffuse.fileName = material.GetTexture(TextureMapType::Diffuse);
if (diffuse.fileName.empty())
{
// Default to white texture if the material has no textures. This ensures
// meshes purely using material/vertex colors render properly in-engine.
diffuse.fileName = MaterialExport::g_whiteTexture;
}
diffuse.exportName = MaterialExport::g_diffuseMapName;
diffuse.updated = false;
textures.push_back(diffuse);
TextureData specular;
specular.fileName = material.GetTexture(TextureMapType::Specular);
specular.exportName = MaterialExport::g_specularMapName;
specular.updated = false;
textures.push_back(specular);
TextureData bump;
bump.fileName = material.GetTexture(TextureMapType::Bump);
bump.exportName = MaterialExport::g_bumpMapName;
bump.updated = false;
textures.push_back(bump);
//Add Texture paramaters to the material.
rapidxml::xml_node<char>* texturesNode = m_mtlDoc.allocate_node(rapidxml::node_element, MaterialExport::g_texturesString);
if (texturesNode)
{
for (const auto& currentTexture : textures)
{
if (!currentTexture.fileName.empty())
{
rapidxml::xml_node<char>* textureNode = CreateTextureMtlNode(currentTexture.exportName.c_str(), currentTexture.fileName.c_str());
texturesNode->append_node(textureNode);
}
}
materialNode->append_node(texturesNode);
}
return materialNode;
}
rapidxml::xml_node<char>* MaterialGroup::CreateTextureMtlNode(const char* name, const char* fileName)
{
rapidxml::xml_node<char>* textureNode = m_mtlDoc.allocate_node(rapidxml::node_element, MaterialExport::g_textureString);
rapidxml::xml_attribute<char>* attr = m_mtlDoc.allocate_attribute(MaterialExport::g_mapString, m_mtlDoc.allocate_string(name));
textureNode->append_attribute(attr);
attr = m_mtlDoc.allocate_attribute(MaterialExport::g_fileString, m_mtlDoc.allocate_string(fileName));
textureNode->append_attribute(attr);
return textureNode;
}
}//GFxFramework
}//AZ