Formalized the concept of an model's material slots
Formalized the concept of an model's material slots Before, the ModelAsset and MaterialComponent code was conflating the idea of a material slot ID and a default material assignment. The default material asset's sub-ID was being used to uniquely identify the material slot as well. This blocks our ability to use other materials as the default assignment for individual meshes; we are forced to use whatever material that was generated from the source model file (like FBX). With these changes, we separate the concept of a material AssetId and a material slot ID, and store them separately. There is a new ModelMaterialSlot struct to describe each slot, including a unique "StableId". The ModelAsset stores a map of the slots, and each mesh refers to a slot by its StableId. This is a precursor to another task that will optionally disable the auto-conversion of materials from source model files. Also: - These changes also enable material property overrides without having to make an editable material first, which I don't think was supported before. - Removed unused Default.materialtype from the RPI Assets folder. - Encapsulated members in EditorMaterialComponentExporter::ExportItem for better maintainability. See also https://github.com/o3de/o3de-atom-sampleviewer/pull/175 Testing: - Took screenshots of several AtomTest levels with material overrides before making any changes. Compared these after the changes. Test levels included ActorTest_SingleActor, ActorTest_MultipleActors, and two custom levels that used shaderball and multi-mat_mesh-groups_1m_cubes. - Lots of manual fiddling with material component. - Created a white box component and saw that it rendered correctly. - Cherry-picked these changes into Apocalypse's code base and verified with one of their levels. - Ran AtomSampleViewer automated test suite. Some tests failed, but these were failing before my changes.monroegm-disable-blank-issue-2
commit
fa52124f9c
@ -1,90 +0,0 @@
|
||||
{
|
||||
"description": "A simple default base material used primarily for imported model files like FBX.",
|
||||
"propertyLayout": {
|
||||
"version": 1,
|
||||
"properties": {
|
||||
"general": [
|
||||
{
|
||||
"id": "DiffuseColor",
|
||||
"type": "color",
|
||||
"defaultValue": [ 1.0, 1.0, 1.0 ],
|
||||
"connection": {
|
||||
"type": "shaderInput",
|
||||
"id": "m_diffuseColor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "DiffuseMap",
|
||||
"type": "image",
|
||||
"defaultValue": "",
|
||||
"connection": {
|
||||
"type": "shaderInput",
|
||||
"id": "m_diffuseMap"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UseDiffuseMap",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"connection": {
|
||||
"type": "shaderOption",
|
||||
"id": "o_useDiffuseMap"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "SpecularColor",
|
||||
"type": "color",
|
||||
"defaultValue": [ 0.0, 0.0, 0.0 ],
|
||||
"connection": {
|
||||
"type": "shaderInput",
|
||||
"id": "m_specularColor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "SpecularMap",
|
||||
"type": "image",
|
||||
"defaultValue": "",
|
||||
"connection": {
|
||||
"type": "shaderInput",
|
||||
"id": "m_specularMap"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UseSpecularMap",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"connection": {
|
||||
"type": "shaderOption",
|
||||
"id": "o_useSpecularMap"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "NormalMap",
|
||||
"type": "image",
|
||||
"defaultValue": "",
|
||||
"connection": {
|
||||
"type": "shaderInput",
|
||||
"id": "m_normalMap"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "UseNormalMap",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"connection": {
|
||||
"type": "shaderOption",
|
||||
"id": "o_useNormalMap"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"shaders": [
|
||||
{
|
||||
"file": "DefaultMaterial.shader"
|
||||
},
|
||||
{
|
||||
"file": "DefaultMaterial_DepthPass.shader"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
|
||||
/*
|
||||
* 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 <scenesrg.srgi>
|
||||
#include <viewsrg.srgi>
|
||||
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultObjectSrg.azsli>
|
||||
#include <Atom/RPI/TangentSpace.azsli>
|
||||
|
||||
ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
{
|
||||
float4 m_diffuseColor;
|
||||
float3 m_specularColor;
|
||||
|
||||
Texture2D m_diffuseMap;
|
||||
Texture2D m_normalMap;
|
||||
Texture2D m_specularMap;
|
||||
|
||||
Sampler m_sampler
|
||||
{
|
||||
MaxAnisotropy = 16;
|
||||
AddressU = Wrap;
|
||||
AddressV = Wrap;
|
||||
AddressW = Wrap;
|
||||
};
|
||||
}
|
||||
|
||||
option bool o_useDiffuseMap = false;
|
||||
option bool o_useSpecularMap = false;
|
||||
option bool o_useNormalMap = false;
|
||||
|
||||
struct VertexInput
|
||||
{
|
||||
float3 m_position : POSITION;
|
||||
float3 m_normal : NORMAL;
|
||||
float4 m_tangent : TANGENT;
|
||||
float3 m_bitangent : BITANGENT;
|
||||
float2 m_uv : UV0;
|
||||
};
|
||||
|
||||
struct VertexOutput
|
||||
{
|
||||
float4 m_position : SV_Position;
|
||||
float3 m_normal : NORMAL;
|
||||
float3 m_tangent : TANGENT;
|
||||
float3 m_bitangent : BITANGENT;
|
||||
float2 m_uv : UV0;
|
||||
float3 m_positionToCamera : VIEW;
|
||||
};
|
||||
|
||||
VertexOutput MainVS(VertexInput input)
|
||||
{
|
||||
const float4x4 objectToWorldMatrix = ObjectSrg::GetWorldMatrix();
|
||||
|
||||
VertexOutput output;
|
||||
float3 worldPosition = mul(objectToWorldMatrix, float4(input.m_position,1)).xyz;
|
||||
output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0));
|
||||
|
||||
output.m_uv = input.m_uv;
|
||||
|
||||
output.m_positionToCamera = ViewSrg::m_worldPosition - worldPosition;
|
||||
|
||||
float3x3 objectToWorldMatrixIT = ObjectSrg::GetWorldMatrixInverseTranspose();
|
||||
|
||||
ConstructTBN(input.m_normal, input.m_tangent, input.m_bitangent, objectToWorldMatrix, objectToWorldMatrixIT, output.m_normal, output.m_tangent, output.m_bitangent);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
struct PixelOutput
|
||||
{
|
||||
float4 m_color : SV_Target0;
|
||||
};
|
||||
|
||||
PixelOutput MainPS(VertexOutput input)
|
||||
{
|
||||
PixelOutput output;
|
||||
|
||||
// Very rough placeholder lighting
|
||||
static const float3 lightDir = normalize(float3(1,1,1));
|
||||
|
||||
float4 baseColor = MaterialSrg::m_diffuseColor;
|
||||
if (o_useDiffuseMap)
|
||||
{
|
||||
baseColor *= MaterialSrg::m_diffuseMap.Sample(MaterialSrg::m_sampler, input.m_uv);
|
||||
}
|
||||
|
||||
float3 specular = MaterialSrg::m_specularColor;
|
||||
if (o_useSpecularMap)
|
||||
{
|
||||
specular *= MaterialSrg::m_specularMap.Sample(MaterialSrg::m_sampler, input.m_uv).rgb;
|
||||
}
|
||||
|
||||
float3 normal;
|
||||
if (o_useNormalMap)
|
||||
{
|
||||
float4 sampledValue = MaterialSrg::m_normalMap.Sample(MaterialSrg::m_sampler, input.m_uv);
|
||||
normal = GetWorldSpaceNormal(sampledValue.xy, input.m_normal, input.m_tangent, input.m_bitangent);
|
||||
}
|
||||
else
|
||||
{
|
||||
normal = normalize(input.m_normal);
|
||||
}
|
||||
|
||||
float3 viewDir = normalize(input.m_positionToCamera);
|
||||
float3 H = normalize(lightDir + viewDir);
|
||||
float NdotH = max(0.001, dot(normal, H));
|
||||
float NdotL = saturate(dot(normal, lightDir));
|
||||
|
||||
float3 diffuse = NdotL * baseColor.xyz;
|
||||
|
||||
specular = pow(NdotH, 5.0) * specular;
|
||||
|
||||
// Combined
|
||||
float3 result = diffuse + specular + float3(0.1, 0.1, 0.1) * baseColor.xyz;
|
||||
|
||||
output.m_color = float4(result.xyz, baseColor.a);
|
||||
|
||||
return output;
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
{
|
||||
"Source" : "DefaultMaterial.azsl",
|
||||
|
||||
"DepthStencilState" : {
|
||||
"Depth" : { "Enable" : true, "CompareFunc" : "Equal" }
|
||||
},
|
||||
|
||||
"DrawList" : "forward",
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
},
|
||||
{
|
||||
"name": "MainPS",
|
||||
"type": "Fragment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
|
||||
/*
|
||||
* 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 <scenesrg.srgi>
|
||||
#include <viewsrg.srgi>
|
||||
|
||||
#include <Atom/RPI/ShaderResourceGroups/DefaultObjectSrg.azsli>
|
||||
|
||||
struct VertexInput
|
||||
{
|
||||
float3 m_position : POSITION;
|
||||
};
|
||||
|
||||
struct VertexOutput
|
||||
{
|
||||
float4 m_position : SV_Position;
|
||||
};
|
||||
|
||||
VertexOutput MainVS(VertexInput input)
|
||||
{
|
||||
const float4x4 objectToWorldMatrix = ObjectSrg::GetWorldMatrix();
|
||||
|
||||
VertexOutput output;
|
||||
float3 worldPosition = mul(objectToWorldMatrix, float4(input.m_position,1)).xyz;
|
||||
output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0));
|
||||
return output;
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
{
|
||||
"Source" : "DefaultMaterial_DepthPass.azsl",
|
||||
|
||||
"DepthStencilState" : {
|
||||
"Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" }
|
||||
},
|
||||
|
||||
"DrawList" : "depth",
|
||||
|
||||
"ProgramSettings":
|
||||
{
|
||||
"EntryPoints":
|
||||
[
|
||||
{
|
||||
"name": "MainVS",
|
||||
"type": "Vertex"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
|
||||
namespace RPI
|
||||
{
|
||||
//! Use by model assets to identify a logical material slot.
|
||||
//! Each slot has a unique ID, a name, and a default material. Each mesh in model will reference a single ModelMaterialSlot.
|
||||
//! Other classes like MeshFeatureProcessor and MaterialComponent can override the material associated with individual slots
|
||||
//! to alter the default appearance of the mesh.
|
||||
struct ModelMaterialSlot
|
||||
{
|
||||
AZ_TYPE_INFO(ModelMaterialSlot, "{0E88A62A-D83D-4C1B-8DE7-CE972B8124B5}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
using StableId = uint32_t;
|
||||
static const StableId InvalidStableId;
|
||||
|
||||
//! This ID must have a consistent value when the asset is reprocessed by the asset pipeline, and must be unique within the ModelLodAsset.
|
||||
//! In practice, this set using the MaterialUid from SceneAPI. See ModelAssetBuilderComponent::CreateMesh.
|
||||
StableId m_stableId = InvalidStableId;
|
||||
|
||||
Name m_displayName; //!< The name of the slot as displayed to the user in UI. (Using Name instead of string for fast copies)
|
||||
|
||||
Data::Asset<MaterialAsset> m_defaultMaterialAsset{ Data::AssetLoadBehavior::PreLoad }; //!< The material that will be applied to this slot by default.
|
||||
};
|
||||
|
||||
using ModelMaterialSlotMap = AZStd::unordered_map<ModelMaterialSlot::StableId, ModelMaterialSlot>;
|
||||
|
||||
} //namespace RPI
|
||||
} // namespace AZ
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 <Atom/RPI.Reflect/Model/ModelMaterialSlot.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
// Normally this would be defined in the header file and substituted by the compiler, but for
|
||||
// some reason clang doesn't accept it.
|
||||
const ModelMaterialSlot::StableId ModelMaterialSlot::InvalidStableId = -1;
|
||||
|
||||
void ModelMaterialSlot::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<ModelMaterialSlot>()
|
||||
->Version(0)
|
||||
->Field("StableId", &ModelMaterialSlot::m_stableId)
|
||||
->Field("DisplayName", &ModelMaterialSlot::m_displayName)
|
||||
->Field("DefaultMaterialAsset", &ModelMaterialSlot::m_defaultMaterialAsset)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
Loading…
Reference in New Issue