Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,12 @@
#
# 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.
#
add_subdirectory(GFxFramework)
@@ -0,0 +1,29 @@
#
# 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.
#
if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME GFxFramework STATIC
NAMESPACE AZ
FILES_CMAKE
gfxframework_files.cmake
PLATFORM_INCLUDE_FILES
../Platform/${PAL_PLATFORM_NAME}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
INCLUDE_DIRECTORIES
PUBLIC
..
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzToolsFramework
)
endif()
@@ -0,0 +1,185 @@
#pragma once
/*
* 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.
*
*/
#ifndef AZINCLUDE_GFXFRAMEWORK_IMATERIAL_H_
#define AZINCLUDE_GFXFRAMEWORK_IMATERIAL_H_
#include <AzCore/std/string/string.h>
#include <AzCore/XML/rapidxml.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/RTTI/RTTI.h>
namespace AZ
{
class Vector3;
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
{
extern const char* g_subMaterialString;
extern const char* g_materialString;
extern const char* g_texturesString;
extern const char* g_textureString;
extern const char* g_diffuseMapName;
extern const char* g_specularMapName;
extern const char* g_bumpMapName;
extern const char* g_shininessString;
extern const char* g_opacity;
extern const char* g_genString;
extern const char* g_illumShaderName;
extern const char* g_defaultIllumGenMask;
extern const char* g_defaultNoDrawGenMask;
extern const char* g_defaultShininess;
extern const char* g_defaultOpacity;
extern const char* g_mapString;
extern const char* g_fileString;
extern const char* g_nameString;
extern const char* g_shaderString;
extern const char* g_noDrawShaderName;
extern const char* g_mtlFlagString;
extern const char* g_whiteColor;
extern const char* g_blackColor;
extern const char* g_stringGenMask;
extern const char* g_stringGenMaskOption_VertexColors;
extern const char* g_stringPhysicsNoDraw;
extern const char* g_mtlExtension;
extern const char* g_dccMaterialExtension;
extern const char* g_dccMaterialHashString;
extern const unsigned g_materialNotFound;
}
enum EMaterialFlags
{
MTL_FLAG_PURE_CHILD = 0x0080, // Not shared sub material, sub material unique to his parent multi material.
MTL_FLAG_MULTI_SUBMTL = 0x0100, // This material is a multi sub material.
MTL_FLAG_NODRAW = 0x0400, // Do not render this material.
MTL_64BIT_SHADERGENMASK = 0x80000, // ShaderGen mask is remapped
///! Set to 0x8000000 because it is not in CryCommon::EMaterialFlags
MTL_FLAG_NODRAW_TOUCHBENDING = 0x8000000, //Do not render this material. Used for TouchBending Simulation Trigger.
};
enum class TextureMapType
{
Diffuse,
Specular,
Bump
};
enum class ShaderType
{
Illum,
NoDraw
};
class IMaterial
{
public:
AZ_RTTI(IMaterial, "{9623F88B-0DD0-4772-A019-C109DE287335}");
virtual ~IMaterial() = default;
virtual void SetDataFromMtl(rapidxml::xml_node<char>* materialNode) = 0;
virtual const AZStd::string& GetName() const = 0;
virtual void SetName(const AZStd::string& name) = 0;
virtual const AZStd::string& GetTexture(TextureMapType type) const = 0;
virtual void SetTexture(TextureMapType type, const AZStd::string& texture) = 0;
virtual bool UseVertexColor() const = 0;
virtual void EnableUseVertexColor(bool useVertexColor) = 0;
///! ORed enum EMaterialFlags
virtual int GetMaterialFlags() const = 0;
virtual void SetMaterialFlags(int flags) = 0;
virtual const AZ::Vector3& GetDiffuseColor() const = 0;
virtual void SetDiffuseColor(const AZ::Vector3& color) = 0;
virtual const AZ::Vector3& GetSpecularColor() const = 0;
virtual void SetSpecularColor(const AZ::Vector3& color) = 0;
virtual const AZ::Vector3& GetEmissiveColor() const = 0;
virtual void SetEmissiveColor(const AZ::Vector3& color) = 0;
virtual float GetOpacity() const = 0;
virtual void SetOpacity(float opacity) = 0;
virtual float GetShininess() const = 0;
virtual void SetShininess(float shininess) = 0;
virtual AZ::u32 GetDccMaterialHash() const = 0;
virtual void SetDccMaterialHash(AZ::u32 hash) = 0;
AZ::u32 CalculateDccMaterialHash()
{
// Hash name
AZ::Crc32 hash(GetName().c_str());
// Hash texture names
hash.Add(GetTexture(TextureMapType::Diffuse).c_str());
hash.Add(GetTexture(TextureMapType::Specular).c_str());
hash.Add(GetTexture(TextureMapType::Bump).c_str());
// Hash colors
hash.Add(&GetDiffuseColor(), sizeof(AZ::Vector3));
hash.Add(&GetSpecularColor(), sizeof(AZ::Vector3));
hash.Add(&GetEmissiveColor(), sizeof(AZ::Vector3));
// Hash floats
float tempFloat = GetOpacity();
hash.Add(&tempFloat, sizeof(float));
tempFloat = GetShininess();
hash.Add(&tempFloat, sizeof(float));
// Hash ints
int tempInt = GetMaterialFlags();
hash.Add(&tempInt, sizeof(int));
// Hash booleans
bool tempBool = UseVertexColor();
hash.Add(&tempBool, sizeof(bool));
return hash;
}
};
class IMaterialGroup
{
public:
AZ_RTTI(IMaterialGroup, "{D9417F20-D52B-4E00-9DB3-13F9ED5F2F28}")
virtual ~IMaterialGroup() = default;
virtual void AddMaterial(const AZStd::shared_ptr<IMaterial>& material) = 0;
virtual void RemoveMaterial(const AZStd::string& name) = 0;
virtual size_t FindMaterialIndex(const AZStd::string& name) = 0;
virtual size_t GetMaterialCount() = 0;
virtual AZStd::shared_ptr<IMaterial> GetMaterial(size_t index) = 0;
virtual AZStd::shared_ptr<const IMaterial> GetMaterial(size_t index) const = 0;
virtual bool ReadMtlFile(const char* fileName) = 0;
virtual bool WriteMtlFile(const char* filename) = 0;
virtual const AZStd::string& GetMtlName() = 0;
virtual void SetMtlName(const AZStd::string& name) = 0;
};
} //namespace GFxFramework
} //namespace AZ
#endif // AZINCLUDE_GFXFRAMEWORK_IMATERIAL_H_
@@ -0,0 +1,955 @@
/*
* 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.
*
*/
#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
@@ -0,0 +1,121 @@
#pragma once
/*
* 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.
*
*/
#include <AzCore/XML/rapidxml.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <GFxFramework/MaterialIO/IMaterial.h>
namespace AZ
{
namespace GFxFramework
{
class Material
: public IMaterial
{
public:
Material();
~Material() override = default;
void SetDataFromMtl(rapidxml::xml_node<char>* materialNode) override;
const AZStd::string& GetName() const override;
void SetName(const AZStd::string& name) override;
const AZStd::string& GetTexture(TextureMapType type) const override;
void SetTexture(TextureMapType type, const AZStd::string& texture) override;
bool UseVertexColor() const override;
void EnableUseVertexColor(bool useVertexColor) override;
int GetMaterialFlags() const override;
void SetMaterialFlags(int flags) override;
const AZ::Vector3& GetDiffuseColor() const override;
const AZ::Vector3& GetSpecularColor() const override;
const AZ::Vector3& GetEmissiveColor() const override;
float GetOpacity() const override;
float GetShininess() const override;
void SetDiffuseColor(const AZ::Vector3& color) override;
void SetSpecularColor(const AZ::Vector3& color) override;
void SetEmissiveColor(const AZ::Vector3& color) override;
void SetOpacity(float opacity) override;
void SetShininess(float shininess) override;
AZ::u32 GetDccMaterialHash() const override;
void SetDccMaterialHash(AZ::u32 hash) override;
private:
AZStd::string m_materialName;
AZStd::string m_diffuseMap;
AZStd::string m_specularMap;
AZStd::string m_normalMap;
AZStd::string m_empty;//dummy place holder string to kill warning
int m_flags;
bool m_useVertexColor;
AZ::Vector3 m_diffuseColor;
AZ::Vector3 m_specularColor;
AZ::Vector3 m_emissiveColor;
float m_opacity;
float m_shininess;
AZ::u32 m_dccMaterialHash;
};
class MaterialGroup
: public IMaterialGroup
{
public:
MaterialGroup();
~MaterialGroup() override = default;
void AddMaterial(const AZStd::shared_ptr<IMaterial>& material) override;
void RemoveMaterial(const AZStd::string& name) override;
size_t FindMaterialIndex(const AZStd::string& name) override;
size_t GetMaterialCount() override;
AZStd::shared_ptr<IMaterial> GetMaterial(size_t index) override;
AZStd::shared_ptr<const IMaterial> GetMaterial(size_t index) const override;
const AZStd::string& GetMtlName() override;
void SetMtlName(const AZStd::string& name) override;
bool ReadMtlFile(const char* fileName) override;
bool WriteMtlFile(const char* fileName) override;
private:
AZStd::vector<AZStd::shared_ptr<IMaterial>> m_materials;
AZStd::vector<char> m_mtlBuffer;
rapidxml::xml_document<char> m_mtlDoc;
AZStd::string m_materialGroupName;
bool m_readFromMtl;
bool AddMaterialNode(const IMaterial& mat);
bool UpdateMaterialNode(const IMaterial& mat);
void RemoveMaterialNode(const IMaterial& mat);
AZ::u32 CalculateDccMaterialHash();
void CreateMtlFile();
void UpdateMtlFile();
rapidxml::xml_node<char>* FindMaterialNode(const IMaterial& mat) const;
rapidxml::xml_node<char>* CreateMaterialMtlNode(const IMaterial& material);
rapidxml::xml_node<char>* CreateTextureMtlNode(const char* name, const char* fileName);
};
} //GFxFramework
}//AZ
@@ -0,0 +1,16 @@
#
# 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.
#
set(FILES
MaterialIO/IMaterial.h
MaterialIO/Material.h
MaterialIO/Material.cpp
)
@@ -0,0 +1,16 @@
#
# 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.
#
# Platform specific cmake file for configuring target compiler/link properties
# based on the active platform
# NOTE: functions in cmake are global, therefore adding functions to this file
# is being avoided to prevent overriding functions declared in other targets platfrom
# specific cmake files
@@ -0,0 +1,16 @@
#
# 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.
#
# Platform specific cmake file for configuring target compiler/link properties
# based on the active platform
# NOTE: functions in cmake are global, therefore adding functions to this file
# is being avoided to prevent overriding functions declared in other targets platfrom
# specific cmake files
@@ -0,0 +1,16 @@
#
# 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.
#
# Platform specific cmake file for configuring target compiler/link properties
# based on the active platform
# NOTE: functions in cmake are global, therefore adding functions to this file
# is being avoided to prevent overriding functions declared in other targets platfrom
# specific cmake files