Merge branch 'development' of https://github.com/o3de/o3de into TerrainMaterialsFix
This commit is contained in:
@@ -8,33 +8,41 @@
|
||||
"Name": "UserInterface_Compressed",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R8G8B8A8",
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear"
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
},
|
||||
"PlatformsPresets": {
|
||||
"android": {
|
||||
"UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}",
|
||||
"Name": "UserInterface_Compressed",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "ASTC_4x4"
|
||||
"PixelFormat": "ASTC_4x4",
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}",
|
||||
"Name": "UserInterface_Compressed",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "ASTC_6x6"
|
||||
"PixelFormat": "ASTC_6x6",
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
},
|
||||
"mac": {
|
||||
"UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}",
|
||||
"Name": "UserInterface_Compressed",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "BC1"
|
||||
"PixelFormat": "BC1",
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
},
|
||||
"provo": {
|
||||
"UUID": "{2828FBFE-BDF9-45A7-9370-F93822719CCF}",
|
||||
"Name": "UserInterface_Compressed",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "BC1"
|
||||
"PixelFormat": "BC1",
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,33 +8,41 @@
|
||||
"Name": "UserInterface_Lossless",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R8G8B8A8",
|
||||
"SourceColor": "Linear",
|
||||
"DestColor": "Linear"
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
},
|
||||
"PlatformsPresets": {
|
||||
"android": {
|
||||
"UUID": "{83003128-F63E-422B-AEC2-68F0A947225F}",
|
||||
"Name": "UserInterface_Lossless",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R8G8B8A8"
|
||||
"PixelFormat": "R8G8B8A8",
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
},
|
||||
"ios": {
|
||||
"UUID": "{83003128-F63E-422B-AEC2-68F0A947225F}",
|
||||
"Name": "UserInterface_Lossless",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R8G8B8A8"
|
||||
"PixelFormat": "R8G8B8A8",
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
},
|
||||
"mac": {
|
||||
"UUID": "{83003128-F63E-422B-AEC2-68F0A947225F}",
|
||||
"Name": "UserInterface_Lossless",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R8G8B8A8"
|
||||
"PixelFormat": "R8G8B8A8",
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
},
|
||||
"provo": {
|
||||
"UUID": "{83003128-F63E-422B-AEC2-68F0A947225F}",
|
||||
"Name": "UserInterface_Lossless",
|
||||
"SuppressEngineReduce": true,
|
||||
"PixelFormat": "R8G8B8A8"
|
||||
"PixelFormat": "R8G8B8A8",
|
||||
"SourceColor": "sRGB",
|
||||
"DestColor": "sRGB"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#define mallocAligned(sze) _aligned_malloc(sze, 16)
|
||||
#define freeAligned(ptr) _aligned_free(ptr)
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING_GCC("-Wunused-value")
|
||||
|
||||
/* ####################################################################################################################
|
||||
*/
|
||||
|
||||
@@ -1279,3 +1281,5 @@ namespace ImageProcessingAtom
|
||||
FilterImage(filterIndex, filterOp, blurH, blurV, srcImg, srcMip, dstImg, dstMip, srcRect, dstRect);
|
||||
}
|
||||
}
|
||||
|
||||
AZ_POP_DISABLE_WARNING_GCC
|
||||
|
||||
@@ -1154,7 +1154,7 @@
|
||||
{
|
||||
"name": "thickness",
|
||||
"displayName": " Thickness",
|
||||
"description": "Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel",
|
||||
"description": "In thick transmission mode: Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel\n\nIn thin transmission mode: This value modulates the distance traversed by light inside an object.",
|
||||
"type": "float",
|
||||
"defaultValue": 0.5,
|
||||
"min": 0.0,
|
||||
@@ -1223,14 +1223,41 @@
|
||||
"min": 0.0,
|
||||
"softMax": 20.0
|
||||
},
|
||||
{
|
||||
"name": "shrinkFactor",
|
||||
"displayName": " Shrink Factor",
|
||||
"description": "Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection",
|
||||
"type": "float",
|
||||
"defaultValue": 0.005,
|
||||
"min": 0.0,
|
||||
"softMax": 0.05
|
||||
},
|
||||
{
|
||||
"name": "transmissionNdLBias",
|
||||
"displayName": " Angle Bias",
|
||||
"description": "cosine of angle to extend below (N . L = 0) in scattering through thin objects",
|
||||
"type": "float",
|
||||
"defaultValue": 0.1,
|
||||
"min": -1.0,
|
||||
"softMax": 1.0
|
||||
},
|
||||
{
|
||||
"name": "distanceAttenuation",
|
||||
"displayName": " Distance Attenuation",
|
||||
"description": "Attenuation of the transmission effect, used to hide artifacts due to low-res shadow maps\nFor directional lights: attenuation proportional to the distance from the object to the camera.\nFor other light sources: attenuation proportional to the distance from the object to the light source.",
|
||||
"type": "float",
|
||||
"defaultValue": 0.5,
|
||||
"min": 0.0,
|
||||
"softMax": 4.0
|
||||
},
|
||||
{
|
||||
"name": "transmissionScale",
|
||||
"displayName": " Scale",
|
||||
"description": "Strength of transmission",
|
||||
"type": "float",
|
||||
"defaultValue": 3.0,
|
||||
"defaultValue": 1.0,
|
||||
"min": 0.0,
|
||||
"softMax": 20.0
|
||||
"softMax": 5.0
|
||||
}
|
||||
],
|
||||
"detailLayerGroup": [
|
||||
@@ -1555,6 +1582,9 @@
|
||||
"power": "subsurfaceScattering.transmissionPower",
|
||||
"distortion": "subsurfaceScattering.transmissionDistortion",
|
||||
"attenuation": "subsurfaceScattering.transmissionAttenuation",
|
||||
"shrinkFactor": "subsurfaceScattering.shrinkFactor",
|
||||
"transmissionNdLBias": "subsurfaceScattering.transmissionNdLBias",
|
||||
"distanceAttenuation": "subsurfaceScattering.distanceAttenuation",
|
||||
"tintColor": "subsurfaceScattering.transmissionTint",
|
||||
"thickness": "subsurfaceScattering.thickness",
|
||||
"enabled": "subsurfaceScattering.enableSubsurfaceScattering",
|
||||
|
||||
@@ -93,7 +93,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
|
||||
// Elements of m_transmissionParams:
|
||||
// Thick object mode: (attenuation coefficient, power, distortion, scale)
|
||||
// Thin object mode: (float3 scatter distance, scale)
|
||||
// Thin object mode: (shrinkFactor, transmissionNdLBias, distanceAttenuation, scale)
|
||||
float4 m_transmissionParams;
|
||||
|
||||
// (float3 TintColor, thickness)
|
||||
|
||||
@@ -243,6 +243,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
surface.transmission.tint = transmissionTintThickness.rgb;
|
||||
surface.transmission.thickness = transmissionTintThickness.w;
|
||||
surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
|
||||
surface.transmission.scatterDistance = MaterialSrg::m_scatterDistance;
|
||||
|
||||
// ------- Anisotropy -------
|
||||
|
||||
@@ -275,6 +276,17 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture);
|
||||
lightingData.specularOcclusion = GetOcclusionInput(MaterialSrg::m_specularOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_specularOcclusionMapUvIndex], MaterialSrg::m_specularOcclusionFactor, o_specularOcclusion_useTexture);
|
||||
|
||||
// ------- Thin Object Light Transmission -------
|
||||
|
||||
// Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection
|
||||
lightingData.shrinkFactor = surface.transmission.transmissionParams.x;
|
||||
|
||||
// Angle offset for subsurface scattering through thin objects
|
||||
lightingData.transmissionNdLBias = surface.transmission.transmissionParams.y;
|
||||
|
||||
// Attenuation applied to hide artifacts due to low-res shadow maps
|
||||
lightingData.distanceAttenuation = surface.transmission.transmissionParams.z;
|
||||
|
||||
// ------- Clearcoat -------
|
||||
|
||||
// [GFX TODO][ATOM-14603]: Clean up the double uses of these clear coat flags
|
||||
|
||||
@@ -92,23 +92,34 @@ function ProcessEditor(context)
|
||||
|
||||
-- Update visibility for transmission...
|
||||
|
||||
local transmissionEnabled = TransmissionMode_None ~= context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
|
||||
|
||||
local commonTrasmissionVisibility
|
||||
if(transmissionEnabled) then
|
||||
local thickTransmissionEnabled = TransmissionMode_ThickObject == context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
|
||||
local thinTransmissionEnabled = TransmissionMode_ThinObject == context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
|
||||
|
||||
local commonTrasmissionVisibility = MaterialPropertyVisibility_Hidden
|
||||
local thickTransmissionVisibility = MaterialPropertyVisibility_Hidden
|
||||
local thinTransmissionVisibility = MaterialPropertyVisibility_Hidden
|
||||
if (thickTransmissionEnabled or thinTransmissionEnabled) then
|
||||
commonTrasmissionVisibility = MaterialPropertyVisibility_Enabled
|
||||
else
|
||||
commonTrasmissionVisibility = MaterialPropertyVisibility_Hidden
|
||||
|
||||
if(thickTransmissionEnabled) then
|
||||
thickTransmissionVisibility = MaterialPropertyVisibility_Enabled
|
||||
else -- thin transmission enabled
|
||||
thinTransmissionVisibility = MaterialPropertyVisibility_Enabled
|
||||
end
|
||||
end
|
||||
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.thickness", commonTrasmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.thicknessMap", commonTrasmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionTint", commonTrasmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionPower", commonTrasmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionDistortion", commonTrasmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionAttenuation", commonTrasmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionPower", thickTransmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionDistortion", thickTransmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionAttenuation", thickTransmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.shrinkFactor", thinTransmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionNdLBias", thinTransmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.distanceAttenuation", thinTransmissionVisibility)
|
||||
context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionScale", commonTrasmissionVisibility)
|
||||
|
||||
UpdateTextureDependentPropertyVisibility(context, transmissionEnabled, "subsurfaceScattering.thicknessMap", "subsurfaceScattering.useThicknessMap", "subsurfaceScattering.thicknessMapUv")
|
||||
|
||||
UpdateTextureDependentPropertyVisibility(context, thickTransmissionEnabled or thinTransmissionEnabled, "subsurfaceScattering.thicknessMap", "subsurfaceScattering.useThicknessMap", "subsurfaceScattering.thicknessMapUv")
|
||||
|
||||
end
|
||||
|
||||
@@ -325,6 +325,7 @@ PbrLightingOutput SkinPS_Common(VSOutput IN)
|
||||
surface.transmission.tint = transmissionTintThickness.rgb;
|
||||
surface.transmission.thickness = transmissionTintThickness.w;
|
||||
surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
|
||||
surface.transmission.scatterDistance = MaterialSrg::m_scatterDistance;
|
||||
|
||||
// ------- Lighting Data -------
|
||||
|
||||
@@ -341,6 +342,17 @@ PbrLightingOutput SkinPS_Common(VSOutput IN)
|
||||
lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear);
|
||||
lightingData.diffuseResponse = 1.0 - lightingData.specularResponse;
|
||||
|
||||
// ------- Thin Object Light Transmission -------
|
||||
|
||||
// Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection
|
||||
lightingData.shrinkFactor = surface.transmission.transmissionParams.x;
|
||||
|
||||
// Angle offset for subsurface scattering through thin objects
|
||||
lightingData.transmissionNdLBias = surface.transmission.transmissionParams.y;
|
||||
|
||||
// Attenuation applied to hide artifacts due to low-res shadow maps
|
||||
lightingData.distanceAttenuation = surface.transmission.transmissionParams.z;
|
||||
|
||||
// ------- Occlusion -------
|
||||
|
||||
lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture);
|
||||
|
||||
@@ -568,7 +568,7 @@
|
||||
{
|
||||
"name": "thickness",
|
||||
"displayName": " Thickness",
|
||||
"description": "Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel",
|
||||
"description": "In thick transmission mode: Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel\n\nIn thin transmission mode: This value modulates the distance traversed by light inside an object.",
|
||||
"type": "float",
|
||||
"defaultValue": 0.5,
|
||||
"min": 0.0,
|
||||
@@ -637,14 +637,41 @@
|
||||
"min": 0.0,
|
||||
"softMax": 20.0
|
||||
},
|
||||
{
|
||||
"name": "shrinkFactor",
|
||||
"displayName": " Shrink Factor",
|
||||
"description": "Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection",
|
||||
"type": "float",
|
||||
"defaultValue": 0.005,
|
||||
"min": 0.0,
|
||||
"softMax": 0.05
|
||||
},
|
||||
{
|
||||
"name": "transmissionNdLBias",
|
||||
"displayName": " Angle Bias",
|
||||
"description": "cosine of angle to extend below (N . L = 0) in scattering through thin objects",
|
||||
"type": "float",
|
||||
"defaultValue": 0.1,
|
||||
"min": -1.0,
|
||||
"softMax": 1.0
|
||||
},
|
||||
{
|
||||
"name": "distanceAttenuation",
|
||||
"displayName": " Distance Attenuation",
|
||||
"description": "Attenuation of the transmission effect, used to hide artifacts due to low-res shadow maps\nFor directional lights: attenuation proportional to the distance from the object to the camera.\nFor other light sources: attenuation proportional to the distance from the object to the light source.",
|
||||
"type": "float",
|
||||
"defaultValue": 0.5,
|
||||
"min": 0.0,
|
||||
"softMax": 4.0
|
||||
},
|
||||
{
|
||||
"name": "transmissionScale",
|
||||
"displayName": " Scale",
|
||||
"description": "Strength of transmission",
|
||||
"type": "float",
|
||||
"defaultValue": 3.0,
|
||||
"defaultValue": 1.0,
|
||||
"min": 0.0,
|
||||
"softMax": 20.0
|
||||
"softMax": 5.0
|
||||
}
|
||||
],
|
||||
"wrinkleLayers": [
|
||||
@@ -1011,6 +1038,9 @@
|
||||
"power": "subsurfaceScattering.transmissionPower",
|
||||
"distortion": "subsurfaceScattering.transmissionDistortion",
|
||||
"attenuation": "subsurfaceScattering.transmissionAttenuation",
|
||||
"shrinkFactor": "subsurfaceScattering.shrinkFactor",
|
||||
"transmissionNdLBias": "subsurfaceScattering.transmissionNdLBias",
|
||||
"distanceAttenuation": "subsurfaceScattering.distanceAttenuation",
|
||||
"tintColor": "subsurfaceScattering.transmissionTint",
|
||||
"thickness": "subsurfaceScattering.thickness",
|
||||
"enabled": "subsurfaceScattering.enableSubsurfaceScattering",
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
#include "MaterialInputs/UvSetCount.azsli"
|
||||
#include "MaterialInputs/DetailMapsInput.azsli"
|
||||
|
||||
// Use human skin profile for thin object transmission (if enabled)
|
||||
// Remove this line to use a custom profile based on the scatter color
|
||||
#define USE_HUMAN_SKIN_PROFILE
|
||||
|
||||
ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
{
|
||||
// Auto-generate material SRG fields for common inputs
|
||||
@@ -64,7 +68,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
|
||||
|
||||
// Elements of m_transmissionParams:
|
||||
// Thick object mode: (attenuation coefficient, power, distortion, scale)
|
||||
// Thin object mode: (float3 scatter distance, scale)
|
||||
// Thin object mode: (shrinkFactor, transmissionNdLBias, distanceAttenuation, scale)
|
||||
float4 m_transmissionParams;
|
||||
|
||||
// (float3 TintColor, thickness)
|
||||
|
||||
@@ -16,24 +16,28 @@
|
||||
#include <Atom/Features/PBR/LightingOptions.azsli>
|
||||
|
||||
// Analytical integation (approximation) of diffusion profile over radius, could be replaced by other pre integrated kernels
|
||||
// such as sum of Gaussian
|
||||
// such as sum of Gaussian (see T(s))
|
||||
float3 TransmissionKernel(float t, float3 s)
|
||||
{
|
||||
float3 exponent = s * t;
|
||||
return 0.25 * (1.0 / exp(exponent) + 3.0 / exp(exponent / 3.0));
|
||||
}
|
||||
|
||||
float ThinObjectFalloff(const float3 surfaceNormal, const float3 dirToLight)
|
||||
// [specific profile for human skin]
|
||||
// Analytical integation (approximation) of diffusion profile over radius, could be precomputed in a LUT
|
||||
// From d'Eon and Luebke (https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-14-advanced-techniques-realistic-real-time-skin, section 14.4.7)
|
||||
float3 T(float s)
|
||||
{
|
||||
const float ndl = saturate(dot(-surfaceNormal, dirToLight));
|
||||
|
||||
// ndl works decently well but it can produce a harsh discontinuity in the area just before
|
||||
// the shadow starts appearing on objects like cylinder and tubes.
|
||||
// Smoothing out ndl does a decent enough job of removing this artifact.
|
||||
return smoothstep(0, 1, ndl * ndl);
|
||||
// dipoles and multipoles are approximated with sums of a small number of Gaussians with variable weights and variances
|
||||
return float3(0.233, 0.455, 0.649) * exp(-s*s/0.0064) +
|
||||
float3(0.1, 0.336, 0.344) * exp(-s*s/0.0484) +
|
||||
float3(0.118, 0.198, 0.0) * exp(-s*s/0.187) +
|
||||
float3(0.113, 0.007, 0.007) * exp(-s*s/0.567) +
|
||||
float3(0.358, 0.004, 0.0) * exp(-s*s/1.99) +
|
||||
float3(0.078, 0.0, 0.0) * exp(-s*s/7.41);
|
||||
}
|
||||
|
||||
float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightIntensity, float3 dirToLight, float shadowRatio)
|
||||
float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightIntensity, float3 dirToLight, float transmissionDistance, float attenuationDistance)
|
||||
{
|
||||
float3 result = float3(0.0, 0.0, 0.0);
|
||||
|
||||
@@ -52,7 +56,7 @@ float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightI
|
||||
// https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/
|
||||
|
||||
{
|
||||
thickness = max(shadowRatio, surface.transmission.thickness);
|
||||
thickness = max(transmissionDistance, surface.transmission.thickness);
|
||||
float transmittance = pow( saturate( dot( lightingData.dirToCamera, -normalize( dirToLight + surface.normal * transmissionParams.z ) ) ), transmissionParams.y ) * transmissionParams.w;
|
||||
float lamberAttenuation = exp(-thickness * transmissionParams.x) * saturate(1.0 - thickness);
|
||||
result = transmittance * lamberAttenuation * lightIntensity;
|
||||
@@ -60,18 +64,39 @@ float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightI
|
||||
break;
|
||||
|
||||
case TransmissionMode::ThinObject:
|
||||
// Thin object mode, using thin-film assumption proposed by Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency"
|
||||
// Thin object mode, based on Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency"
|
||||
// http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf
|
||||
|
||||
float litRatio = 1.0 - shadowRatio;
|
||||
if (litRatio)
|
||||
|
||||
{
|
||||
const float thickness = surface.transmission.thickness * transmissionParams.w;
|
||||
const float3 invScattering = rcp(transmissionParams.xyz);
|
||||
const float falloff = ThinObjectFalloff(surface.normal, dirToLight);
|
||||
result = TransmissionKernel(thickness, invScattering) * falloff * lightIntensity * litRatio;
|
||||
}
|
||||
// transmissionDistance < 0.0f means shadows are not enabled --> avoid unnecessary computation
|
||||
if (transmissionDistance < 0.0f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Irradiance arround surface point.
|
||||
// Albedo at front (surface point) is used to approximate irradiance at the back of the object (observation 4 in [Jimenez J. et al, 2010])
|
||||
// Increase angle of influence to smooth transition regions
|
||||
float3 E = surface.albedo * saturate(lightingData.transmissionNdLBias + dot(-surface.normal, dirToLight));
|
||||
|
||||
// Transmission distance modulated by hardcoded constant C and the thickness exposed parameter (in this case modulating the distance traversed by the light inside the object)
|
||||
const float C = 300.0f;
|
||||
float s = transmissionDistance * C * surface.transmission.thickness;
|
||||
|
||||
// Use scattering color to weight thin object transmission color
|
||||
const float3 invScattering = rcp(max(surface.transmission.scatterDistance, float(0.00001)));
|
||||
|
||||
#ifndef USE_HUMAN_SKIN_PROFILE
|
||||
// Generic profile based on scatter color
|
||||
result = TransmissionKernel(s, invScattering) * lightIntensity * E * transmissionParams.w;
|
||||
#else // USE_HUMAN_SKIN_PROFILE
|
||||
// Profile specific to human skin
|
||||
result = T(s) * lightIntensity * E * transmissionParams.w;
|
||||
#endif
|
||||
|
||||
// Distance attenuation applied to hide artifacts due to low-res projected areas onto shadowmaps (might need some work in the future)
|
||||
result /= max(1.0, attenuationDistance * attenuationDistance * lightingData.distanceAttenuation);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -28,6 +28,15 @@ class LightingData
|
||||
// Direction light shadow coordinates
|
||||
float3 shadowCoords[ViewSrg::MaxCascadeCount];
|
||||
|
||||
// (N . L) to accept below (N . L = 0) in scattering through thin objects
|
||||
float transmissionNdLBias;
|
||||
|
||||
// Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection
|
||||
float shrinkFactor;
|
||||
|
||||
// Attenuation applied to hide artifacts due to low-res shadow maps
|
||||
float distanceAttenuation;
|
||||
|
||||
// Normalized direction from surface to camera
|
||||
float3 dirToCamera;
|
||||
|
||||
|
||||
+9
-2
@@ -135,8 +135,15 @@ void ApplyCapsuleLight(ViewSrg::CapsuleLight light, Surface surface, inout Light
|
||||
float3 closestIntersectionPoint = startPoint + closestT * startToEnd;
|
||||
float3 posToLight = closestIntersectionPoint - surface.position;
|
||||
|
||||
// Tranmission contribution
|
||||
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), 0.0);
|
||||
// Transmission contribution
|
||||
// We cannot compute the actual transmission distance so we want to:
|
||||
// - If transmission mode is thick object -> use transmission thickness parameter instead
|
||||
// - If transmission mode is thin object -> ignore back lighting
|
||||
// To detect and apply this behavior in the GetBackLighting function, we need to use a negative transmissionDistance
|
||||
const float transmissionDistance = -1.0f;
|
||||
// If the transmissionDistance is ignored then the attenuation distance (only used on thin objects) does not have any influence
|
||||
const float attenuationDistance = 0.0f;
|
||||
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), transmissionDistance, attenuationDistance);
|
||||
|
||||
// Calculate the offset from the nearest point on the reflection vector to the nearest point on the capsule light
|
||||
float3 posToClosestPointAlongReflection = dot(posToLight, reflectionDir) * reflectionDir;
|
||||
|
||||
+29
-14
@@ -18,7 +18,13 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
|
||||
// Shadowed check
|
||||
const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
|
||||
float litRatio = 1.0f;
|
||||
float backShadowRatio = 0.0f;
|
||||
float camToSurfDist = distance(ViewSrg::m_worldPosition, surface.position);
|
||||
|
||||
// Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
|
||||
// - If transmission mode is thick object -> use transmission thickness parameter instead
|
||||
// - If transmission mode is thin object -> ignore back lighting
|
||||
float transmissionDistance = -1.0f;
|
||||
|
||||
if (o_enableShadows && shadowIndex < SceneSrg::m_directionalLightCount)
|
||||
{
|
||||
litRatio = DirectionalLightShadow::GetVisibility(
|
||||
@@ -30,7 +36,19 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
|
||||
#if ENABLE_TRANSMISSION
|
||||
if (o_transmission_mode == TransmissionMode::ThickObject)
|
||||
{
|
||||
backShadowRatio = DirectionalLightShadow::GetThickness(shadowIndex, lightingData.shadowCoords);
|
||||
transmissionDistance = DirectionalLightShadow::GetThickness(shadowIndex, lightingData.shadowCoords);
|
||||
}
|
||||
else if (o_transmission_mode == TransmissionMode::ThinObject)
|
||||
{
|
||||
// Fetch and use shrinked positions for thin object transmission to ensure they fall onto the object when querying
|
||||
DirectionalLightShadow::GetShadowCoords(
|
||||
shadowIndex,
|
||||
surface.position - lightingData.shrinkFactor * surface.vertexNormal,
|
||||
surface.normal,
|
||||
lightingData.shadowCoords);
|
||||
|
||||
// the depth from the shadow map
|
||||
transmissionDistance = DirectionalLightShadow::GetThickness(shadowIndex, lightingData.shadowCoords);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -52,23 +70,20 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
|
||||
// [GFX TODO][ATOM-2012] care of multiple directional light
|
||||
// Currently shadow check is done only for index == shadowIndex.
|
||||
float currentLitRatio = 1.0f;
|
||||
float currentBackShadowRatio = 1.0f;
|
||||
if (o_enableShadows)
|
||||
float currentTransmissionDistance = -1.0f;
|
||||
|
||||
if (o_enableShadows && index == shadowIndex)
|
||||
{
|
||||
currentLitRatio = (index == shadowIndex) ? litRatio : 1.;
|
||||
|
||||
currentBackShadowRatio = 1.0 - currentLitRatio;
|
||||
#if ENABLE_TRANSMISSION
|
||||
if (o_transmission_mode == TransmissionMode::ThickObject)
|
||||
{
|
||||
currentBackShadowRatio = (index == shadowIndex) ? backShadowRatio : 0.;
|
||||
}
|
||||
#endif
|
||||
// Add contribution only if current directional light is the active one for shadows
|
||||
currentLitRatio = litRatio;
|
||||
currentTransmissionDistance = transmissionDistance;
|
||||
}
|
||||
|
||||
// Transmission contribution
|
||||
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight, currentTransmissionDistance, camToSurfDist);
|
||||
|
||||
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight) * currentLitRatio;
|
||||
lightingData.specularLighting += GetSpecularLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight) * currentLitRatio;
|
||||
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight, currentBackShadowRatio);
|
||||
}
|
||||
|
||||
// Add debug coloring for directional light shadow
|
||||
|
||||
+15
-10
@@ -76,9 +76,12 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
|
||||
|
||||
// shadow
|
||||
float litRatio = 1.0;
|
||||
|
||||
// How much is back face shadowed, it's set to the reverse of litRatio to share the same default value with thickness, which should be 0 if no shadow map available
|
||||
float backShadowRatio = 0.0;
|
||||
|
||||
// Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
|
||||
// - If transmission mode is thick object -> use transmission thickness parameter instead
|
||||
// - If transmission mode is thin object -> ignore back lighting
|
||||
float transmissionDistance = -1.0f;
|
||||
|
||||
if (o_enableShadows)
|
||||
{
|
||||
litRatio = ProjectedShadow::GetVisibility(
|
||||
@@ -88,14 +91,16 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
|
||||
-dirToConeTip,
|
||||
surface.vertexNormal);
|
||||
|
||||
// Use backShadowRatio to carry thickness from shadow map for thick mode
|
||||
backShadowRatio = 1.0 - litRatio;
|
||||
|
||||
// o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
|
||||
#if ENABLE_TRANSMISSION
|
||||
if (o_transmission_mode == TransmissionMode::ThickObject)
|
||||
{
|
||||
backShadowRatio = ProjectedShadow::GetThickness(
|
||||
light.m_shadowIndex,
|
||||
surface.position);
|
||||
transmissionDistance = ProjectedShadow::GetThickness(light.m_shadowIndex, surface.position);
|
||||
}
|
||||
else if (o_transmission_mode == TransmissionMode::ThinObject)
|
||||
{
|
||||
transmissionDistance = ProjectedShadow::GetThickness(light.m_shadowIndex, surface.position - lightingData.shrinkFactor * surface.vertexNormal);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -115,8 +120,8 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
|
||||
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, posToLightDir) * litRatio;
|
||||
|
||||
// Transmission contribution
|
||||
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, posToLightDir, backShadowRatio);
|
||||
|
||||
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, posToLightDir, transmissionDistance, distanceToLight2);
|
||||
|
||||
// Adjust the light direction for specular based on disk size
|
||||
|
||||
// Calculate the reflection off the normal from the view direction
|
||||
|
||||
+11
-1
@@ -46,6 +46,16 @@ void AddSampleContribution(
|
||||
float3 intensityRgb = float3(intensity, intensity, intensity);
|
||||
|
||||
diffuseAcc += GetDiffuseLighting(surface, lightingData, intensityRgb, posToLightSampleDir);
|
||||
translucentAcc += GetBackLighting(surface, lightingData, intensityRgb, posToLightSampleDir, 0.0);
|
||||
|
||||
// Transmission contribution
|
||||
// We cannot compute the actual transmission distance so we want to:
|
||||
// - If transmission mode is thick object -> use transmission thickness parameter instead
|
||||
// - If transmission mode is thin object -> ignore back lighting
|
||||
// To detect and apply this behavior in the GetBackLighting function, we need to use a negative transmissionDistance
|
||||
const float transmissionDistance = -1.0f;
|
||||
// If the transmissionDistance is ignored then the attenuation distance (only used on thin objects) does not have any influence
|
||||
const float attenuationDistance = 0.0f;
|
||||
translucentAcc += GetBackLighting(surface, lightingData, intensityRgb, posToLightSampleDir, transmissionDistance, attenuationDistance);
|
||||
|
||||
specularAcc += GetSpecularLighting(surface, lightingData, intensityRgb, posToLightSampleDir);
|
||||
}
|
||||
|
||||
+15
-10
@@ -67,6 +67,7 @@ uint ComputeShadowIndex(const ViewSrg::PointLight light, const Surface surface)
|
||||
void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingData lightingData)
|
||||
{
|
||||
float3 posToLight = light.m_position - surface.position;
|
||||
float posToLightDist = length(posToLight);
|
||||
float d2 = dot(posToLight, posToLight); // light distance squared
|
||||
float falloff = d2 * light.m_invAttenuationRadiusSquared;
|
||||
|
||||
@@ -84,8 +85,11 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
|
||||
// shadow
|
||||
float litRatio = 1.0;
|
||||
|
||||
// How much is back face shadowed, it's set to the reverse of litRatio to share the same default value with thickness, which should be 0 if no shadow map available
|
||||
float backShadowRatio = 0.0;
|
||||
// Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
|
||||
// - If transmission mode is thick object -> use transmission thickness parameter instead
|
||||
// - If transmission mode is thin object -> ignore back lighting
|
||||
float transmissionDistance = -1.0f;
|
||||
|
||||
if (o_enableShadows)
|
||||
{
|
||||
const float3 lightDir = normalize(light.m_position - surface.position);
|
||||
@@ -96,16 +100,17 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
|
||||
surface.position,
|
||||
lightDir,
|
||||
surface.vertexNormal);
|
||||
|
||||
// Use backShadowRatio to carry thickness from shadow map for thick mode
|
||||
backShadowRatio = 1.0 - litRatio;
|
||||
|
||||
#if ENABLE_TRANSMISSION
|
||||
// o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
|
||||
if (o_transmission_mode == TransmissionMode::ThickObject)
|
||||
{
|
||||
backShadowRatio = ProjectedShadow::GetThickness(
|
||||
shadowIndex,
|
||||
surface.position);
|
||||
}
|
||||
transmissionDistance = ProjectedShadow::GetThickness(shadowIndex, surface.position);
|
||||
}
|
||||
else if (o_transmission_mode == TransmissionMode::ThinObject)
|
||||
{
|
||||
transmissionDistance = ProjectedShadow::GetThickness(shadowIndex, surface.position - lightingData.shrinkFactor * surface.vertexNormal);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -113,7 +118,7 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
|
||||
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, normalize(posToLight)) * litRatio;
|
||||
|
||||
// Transmission contribution
|
||||
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), backShadowRatio);
|
||||
lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), transmissionDistance, posToLightDist);
|
||||
|
||||
// Adjust the light direcion for specular based on bulb size
|
||||
|
||||
|
||||
@@ -148,13 +148,21 @@ void ApplyQuadLight(ViewSrg::QuadLight light, Surface surface, inout LightingDat
|
||||
GetDiffuseLighting(surface, lightingData, intensity, dirToLightCenter)
|
||||
);
|
||||
|
||||
// Transmission contribution
|
||||
// We cannot compute the actual transmission distance so we want to:
|
||||
// - If transmission mode is thick object -> use transmission thickness parameter instead
|
||||
// - If transmission mode is thin object -> ignore back lighting
|
||||
// To detect and apply this behavior in the GetBackLighting function, we need to use a negative transmissionDistance
|
||||
const float transmissionDistance = -1.0f;
|
||||
// If the transmissionDistance is ignored then the attenuation distance (only used on thin objects) does not have any influence
|
||||
const float attenuationDistance = 0.0f;
|
||||
lightingData.translucentBackLighting +=
|
||||
(
|
||||
GetBackLighting(surface, lightingData, intensity, p0, 0.0) +
|
||||
GetBackLighting(surface, lightingData, intensity, p1, 0.0) +
|
||||
GetBackLighting(surface, lightingData, intensity, p2, 0.0) +
|
||||
GetBackLighting(surface, lightingData, intensity, p3, 0.0) +
|
||||
GetBackLighting(surface, lightingData, intensity, dirToLightCenter, 0.0)
|
||||
GetBackLighting(surface, lightingData, intensity, p0, transmissionDistance, attenuationDistance) +
|
||||
GetBackLighting(surface, lightingData, intensity, p1, transmissionDistance, attenuationDistance) +
|
||||
GetBackLighting(surface, lightingData, intensity, p2, transmissionDistance, attenuationDistance) +
|
||||
GetBackLighting(surface, lightingData, intensity, p3, transmissionDistance, attenuationDistance) +
|
||||
GetBackLighting(surface, lightingData, intensity, dirToLightCenter, transmissionDistance, attenuationDistance)
|
||||
);
|
||||
|
||||
// Calculate specular by choosing a single representative point on the light's surface based on the reflection ray
|
||||
|
||||
+3
-1
@@ -14,7 +14,8 @@ class TransmissionSurfaceData
|
||||
{
|
||||
float3 tint;
|
||||
float thickness; //!< pre baked local thickness, used for transmission
|
||||
float4 transmissionParams; //!< parameters: thick mode->(attenuation coefficient, power, distortion, scale), thin mode: (float3 scatter distance, scale)
|
||||
float4 transmissionParams; //!< parameters: thick mode->(attenuation coefficient, power, distortion, scale), thin mode: (shrinkFactor, transmissionNdLBias, distanceAttenuation, scale)
|
||||
float3 scatterDistance; //!< scatter distance (same as in MaterialSrg) >
|
||||
|
||||
void InitializeToZero();
|
||||
};
|
||||
@@ -24,6 +25,7 @@ void TransmissionSurfaceData::InitializeToZero()
|
||||
tint = float3(0.0f, 0.0f, 0.0f);
|
||||
thickness = 0.0f;
|
||||
transmissionParams = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
scatterDistance = float3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+4
-1
@@ -161,7 +161,10 @@ float DirectionalLightShadow::GetThickness(uint lightIndex, float3 shadowCoords[
|
||||
shadowCoord.y >= 0. && shadowCoord.y * size < size - PixelMargin && shadowCoord.z < (1. - DepthMargin))
|
||||
{
|
||||
const float depthBufferValue = shadowmap.Sample(PassSrg::LinearSampler, float3(shadowCoord.xy, indexOfCascade)).r;
|
||||
const float deltaDepth = abs(shadowCoord.z - depthBufferValue);
|
||||
|
||||
// Normalized thickness (avoid negative values given by precision errors or shrinking offsets)
|
||||
const float deltaDepth = max(shadowCoord.z - depthBufferValue,0.0);
|
||||
|
||||
const float viewSpaceThickness = ViewSrg::m_directionalLightShadows[lightIndex].m_far_minus_near * deltaDepth;
|
||||
return viewSpaceThickness;
|
||||
}
|
||||
|
||||
+3
-2
@@ -274,8 +274,9 @@ float ProjectedShadow::GetThickness()
|
||||
float3(atlasPosition.xy * invAtlasSize, atlasPosition.z),
|
||||
/*LOD=*/0
|
||||
).r;
|
||||
|
||||
const float viewSpaceThickness = abs(UnprojectDepth(m_shadowIndex, m_shadowPosition.z) - UnprojectDepth(m_shadowIndex, depthValue));
|
||||
|
||||
// Denormalized thickness (avoid negative values given by precision errors or shrinking offsets)
|
||||
const float viewSpaceThickness = max(UnprojectDepth(m_shadowIndex, depthValue) - UnprojectDepth(m_shadowIndex, m_shadowPosition.z), 0.0);
|
||||
return viewSpaceThickness;
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -26,6 +26,9 @@ namespace AZ
|
||||
->Field("m_power", &SubsurfaceTransmissionParameterFunctor::m_power)
|
||||
->Field("m_distortion", &SubsurfaceTransmissionParameterFunctor::m_distortion)
|
||||
->Field("m_attenuation", &SubsurfaceTransmissionParameterFunctor::m_attenuation)
|
||||
->Field("m_shrinkFactor", &SubsurfaceTransmissionParameterFunctor::m_shrinkFactor)
|
||||
->Field("m_transmissionNdLBias", &SubsurfaceTransmissionParameterFunctor::m_transmissionNdLBias)
|
||||
->Field("m_distanceAttenuation", &SubsurfaceTransmissionParameterFunctor::m_distanceAttenuation)
|
||||
->Field("m_tintColor", &SubsurfaceTransmissionParameterFunctor::m_tintColor)
|
||||
->Field("m_thickness", &SubsurfaceTransmissionParameterFunctor::m_thickness)
|
||||
->Field("m_enabled", &SubsurfaceTransmissionParameterFunctor::m_enabled)
|
||||
@@ -50,6 +53,9 @@ namespace AZ
|
||||
auto power = context.GetMaterialPropertyValue<float>(m_power);
|
||||
auto distortion = context.GetMaterialPropertyValue<float>(m_distortion);
|
||||
auto attenuation = context.GetMaterialPropertyValue<float>(m_attenuation);
|
||||
auto shrinkFactor = context.GetMaterialPropertyValue<float>(m_shrinkFactor);
|
||||
auto transmissionNdLBias = context.GetMaterialPropertyValue<float>(m_transmissionNdLBias);
|
||||
auto distanceAttenuation = context.GetMaterialPropertyValue<float>(m_distanceAttenuation);
|
||||
auto tintColor = context.GetMaterialPropertyValue<Color>(m_tintColor);
|
||||
auto thickness = context.GetMaterialPropertyValue<float>(m_thickness);
|
||||
auto scatterDistanceColor = context.GetMaterialPropertyValue<Color>(m_scatterDistanceColor);
|
||||
@@ -67,7 +73,9 @@ namespace AZ
|
||||
}
|
||||
else
|
||||
{
|
||||
transmissionParams.Set(scatterDistance);
|
||||
transmissionParams.SetX(shrinkFactor);
|
||||
transmissionParams.SetY(transmissionNdLBias);
|
||||
transmissionParams.SetZ(distanceAttenuation);
|
||||
transmissionParams.SetW(scale);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace AZ
|
||||
RPI::MaterialPropertyIndex m_power;
|
||||
RPI::MaterialPropertyIndex m_distortion;
|
||||
RPI::MaterialPropertyIndex m_attenuation;
|
||||
RPI::MaterialPropertyIndex m_shrinkFactor;
|
||||
RPI::MaterialPropertyIndex m_transmissionNdLBias;
|
||||
RPI::MaterialPropertyIndex m_distanceAttenuation;
|
||||
RPI::MaterialPropertyIndex m_tintColor;
|
||||
RPI::MaterialPropertyIndex m_thickness;
|
||||
RPI::MaterialPropertyIndex m_enabled;
|
||||
|
||||
+11
-1
@@ -25,6 +25,9 @@ namespace AZ
|
||||
->Field("power", &SubsurfaceTransmissionParameterFunctorSourceData::m_power)
|
||||
->Field("distortion", &SubsurfaceTransmissionParameterFunctorSourceData::m_distortion)
|
||||
->Field("attenuation", &SubsurfaceTransmissionParameterFunctorSourceData::m_attenuation)
|
||||
->Field("shrinkFactor", &SubsurfaceTransmissionParameterFunctorSourceData::m_shrinkFactor)
|
||||
->Field("transmissionNdLBias", &SubsurfaceTransmissionParameterFunctorSourceData::m_transmissionNdLBias)
|
||||
->Field("distanceAttenuation", &SubsurfaceTransmissionParameterFunctorSourceData::m_distanceAttenuation)
|
||||
->Field("tintColor", &SubsurfaceTransmissionParameterFunctorSourceData::m_tintColor)
|
||||
->Field("thickness", &SubsurfaceTransmissionParameterFunctorSourceData::m_thickness)
|
||||
->Field("enabled", &SubsurfaceTransmissionParameterFunctorSourceData::m_enabled)
|
||||
@@ -48,6 +51,9 @@ namespace AZ
|
||||
functor->m_power = context.FindMaterialPropertyIndex(Name{ m_power });
|
||||
functor->m_distortion = context.FindMaterialPropertyIndex(Name{ m_distortion });
|
||||
functor->m_attenuation = context.FindMaterialPropertyIndex(Name{ m_attenuation });
|
||||
functor->m_shrinkFactor = context.FindMaterialPropertyIndex(Name{ m_shrinkFactor });
|
||||
functor->m_transmissionNdLBias = context.FindMaterialPropertyIndex(Name{ m_transmissionNdLBias });
|
||||
functor->m_distanceAttenuation = context.FindMaterialPropertyIndex(Name{ m_distanceAttenuation });
|
||||
functor->m_tintColor = context.FindMaterialPropertyIndex(Name{ m_tintColor });
|
||||
functor->m_thickness = context.FindMaterialPropertyIndex(Name{ m_thickness });
|
||||
functor->m_enabled = context.FindMaterialPropertyIndex(Name{ m_enabled });
|
||||
@@ -56,7 +62,8 @@ namespace AZ
|
||||
|
||||
if (functor->m_mode.IsNull() || functor->m_scale.IsNull() || functor->m_power.IsNull() || functor->m_distortion.IsNull() ||
|
||||
functor->m_tintColor.IsNull() || functor->m_thickness.IsNull() || functor->m_enabled.IsNull() || functor->m_attenuation.IsNull() || functor->m_scatterDistanceColor.IsNull() ||
|
||||
functor->m_scatterDistanceIntensity.IsNull())
|
||||
functor->m_scatterDistanceIntensity.IsNull() || functor->m_shrinkFactor.IsNull() || functor->m_transmissionNdLBias.IsNull() ||
|
||||
functor->m_distanceAttenuation.IsNull())
|
||||
{
|
||||
return Failure();
|
||||
}
|
||||
@@ -66,6 +73,9 @@ namespace AZ
|
||||
AddMaterialPropertyDependency(functor, functor->m_power);
|
||||
AddMaterialPropertyDependency(functor, functor->m_distortion);
|
||||
AddMaterialPropertyDependency(functor, functor->m_attenuation);
|
||||
AddMaterialPropertyDependency(functor, functor->m_shrinkFactor);
|
||||
AddMaterialPropertyDependency(functor, functor->m_transmissionNdLBias);
|
||||
AddMaterialPropertyDependency(functor, functor->m_distanceAttenuation);
|
||||
AddMaterialPropertyDependency(functor, functor->m_tintColor);
|
||||
AddMaterialPropertyDependency(functor, functor->m_thickness);
|
||||
AddMaterialPropertyDependency(functor, functor->m_enabled);
|
||||
|
||||
+3
@@ -36,6 +36,9 @@ namespace AZ
|
||||
AZStd::string m_power; //!< material property for thick transmission power
|
||||
AZStd::string m_distortion; //!< material property for thick transmission distortion towards surface normal
|
||||
AZStd::string m_attenuation; //!< material property for thick transmission volume absorption
|
||||
AZStd::string m_shrinkFactor; //!< material property for thin transmission position shrink factor towards surface normal
|
||||
AZStd::string m_transmissionNdLBias; //!< material property for thin transmission bias of the NdL value
|
||||
AZStd::string m_distanceAttenuation; //!< material property for thin transmission attenuation with distance
|
||||
AZStd::string m_tintColor; //!< material property for transmission tint
|
||||
AZStd::string m_thickness; //!< material property for normalized object thickness
|
||||
AZStd::string m_enabled; //!< material property for subsurface scattering feature switch (enabled or disabled)
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
|
||||
|
||||
class QWidget;
|
||||
class QMenu;
|
||||
class QAction;
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
class AssetBrowserEntry;
|
||||
} // namespace AssetBrowser
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
//! Provides common asset browser interactions, source control integration, and functionality to add custom menu actions
|
||||
class AtomToolsAssetBrowserInteractions : public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(AtomToolsAssetBrowserInteractions, AZ::SystemAllocator, 0);
|
||||
|
||||
AtomToolsAssetBrowserInteractions();
|
||||
~AtomToolsAssetBrowserInteractions();
|
||||
|
||||
using AssetBrowserEntryVector = AZStd::vector<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>;
|
||||
using FilterCallback = AZStd::function<bool(const AssetBrowserEntryVector&)>;
|
||||
using ActionCallback = AZStd::function<void(QWidget* caller, QMenu* menu, const AssetBrowserEntryVector&)>;
|
||||
|
||||
//! Add a filter and handler for custom context menu entries that will be added to the top of the context menu
|
||||
void RegisterContextMenuActions(const FilterCallback& filterCallback, const ActionCallback& actionCallback);
|
||||
|
||||
private:
|
||||
//! AssetBrowserInteractionNotificationBus::Handler overrides...
|
||||
void AddContextMenuActions(QWidget* caller, QMenu* menu, const AssetBrowserEntryVector& entries) override;
|
||||
|
||||
void AddContextMenuActionsForSourceEntries(
|
||||
QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
|
||||
void AddContextMenuActionsForFolderEntries(
|
||||
QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
|
||||
void AddContextMenuActionsForAllEntries(
|
||||
QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
|
||||
void AddContextMenuActionsForSourceControl(
|
||||
QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
|
||||
void UpdateContextMenuActionsForSourceControl(bool success, AzToolsFramework::SourceControlFileInfo info);
|
||||
|
||||
QWidget* m_caller = {};
|
||||
QAction* m_addAction = {};
|
||||
QAction* m_checkOutAction = {};
|
||||
QAction* m_undoCheckOutAction = {};
|
||||
QAction* m_getLatestAction = {};
|
||||
AZStd::vector<AZStd::pair<FilterCallback, ActionCallback>> m_contextMenuCallbacks;
|
||||
};
|
||||
} // namespace AtomToolsFramework
|
||||
+64
-158
@@ -7,44 +7,47 @@
|
||||
*/
|
||||
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
|
||||
#include <AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowserInteractions.h>
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/std/string/wildcard.h>
|
||||
#include <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
|
||||
#include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h>
|
||||
#include <Window/CreateMaterialDialog/CreateMaterialDialog.h>
|
||||
#include <Window/MaterialEditorBrowserInteractions.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QInputDialog>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace MaterialEditor
|
||||
namespace AtomToolsFramework
|
||||
{
|
||||
MaterialEditorBrowserInteractions::MaterialEditorBrowserInteractions()
|
||||
AtomToolsAssetBrowserInteractions::AtomToolsAssetBrowserInteractions()
|
||||
{
|
||||
using namespace AzToolsFramework::AssetBrowser;
|
||||
|
||||
AssetBrowserInteractionNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
MaterialEditorBrowserInteractions::~MaterialEditorBrowserInteractions()
|
||||
AtomToolsAssetBrowserInteractions::~AtomToolsAssetBrowserInteractions()
|
||||
{
|
||||
AssetBrowserInteractionNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void MaterialEditorBrowserInteractions::AddContextMenuActions(QWidget* caller, QMenu* menu, const AZStd::vector<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>& entries)
|
||||
void AtomToolsAssetBrowserInteractions::RegisterContextMenuActions(
|
||||
const FilterCallback& filterCallback, const ActionCallback& actionCallback)
|
||||
{
|
||||
m_contextMenuCallbacks.emplace_back(filterCallback, actionCallback);
|
||||
}
|
||||
|
||||
void AtomToolsAssetBrowserInteractions::AddContextMenuActions(
|
||||
QWidget* caller, QMenu* menu, const AssetBrowserEntryVector& entries)
|
||||
{
|
||||
AssetBrowserEntry* entry = entries.empty() ? nullptr : entries.front();
|
||||
if (!entry)
|
||||
@@ -53,79 +56,33 @@ namespace MaterialEditor
|
||||
}
|
||||
|
||||
m_caller = caller;
|
||||
QObject::connect(m_caller, &QObject::destroyed, [this]()
|
||||
{
|
||||
m_caller = nullptr;
|
||||
});
|
||||
QObject::connect(m_caller, &QObject::destroyed, [this]() { m_caller = nullptr; });
|
||||
|
||||
AddGenericContextMenuActions(caller, menu, entry);
|
||||
// Add all of the custom context menu entries first
|
||||
for (const auto& contextMenuCallbackPair : m_contextMenuCallbacks)
|
||||
{
|
||||
if (contextMenuCallbackPair.first(entries))
|
||||
{
|
||||
contextMenuCallbackPair.second(caller, menu, entries);
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
|
||||
{
|
||||
const auto source = azalias_cast<const SourceAssetBrowserEntry*>(entry);
|
||||
if (AzFramework::StringFunc::Path::IsExtension(entry->GetFullPath().c_str(), AZ::RPI::MaterialSourceData::Extension))
|
||||
{
|
||||
AddContextMenuActionsForMaterialSource(caller, menu, source);
|
||||
}
|
||||
else if (AzFramework::StringFunc::Path::IsExtension(entry->GetFullPath().c_str(), AZ::RPI::MaterialTypeSourceData::Extension))
|
||||
{
|
||||
AddContextMenuActionsForMaterialTypeSource(caller, menu, source);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddContextMenuActionsForOtherSource(caller, menu, source);
|
||||
}
|
||||
AddContextMenuActionsForSourceEntries(caller, menu, entry);
|
||||
}
|
||||
else if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
|
||||
{
|
||||
const auto folder = azalias_cast<const FolderAssetBrowserEntry*>(entry);
|
||||
AddContextMenuActionsForFolder(caller, menu, folder);
|
||||
AddContextMenuActionsForFolderEntries(caller, menu, entry);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialEditorBrowserInteractions::AddGenericContextMenuActions([[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
|
||||
{
|
||||
menu->addAction(QObject::tr("Copy Name To Clipboard"), [=]()
|
||||
{
|
||||
QApplication::clipboard()->setText(entry->GetName().c_str());
|
||||
});
|
||||
menu->addAction(QObject::tr("Copy Path To Clipboard"), [=]()
|
||||
{
|
||||
QApplication::clipboard()->setText(entry->GetFullPath().c_str());
|
||||
});
|
||||
|
||||
AddContextMenuActionsForAllEntries(caller, menu, entry);
|
||||
AddContextMenuActionsForSourceControl(caller, menu, entry);
|
||||
}
|
||||
|
||||
void MaterialEditorBrowserInteractions::AddContextMenuActionsForMaterialTypeSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry)
|
||||
void AtomToolsAssetBrowserInteractions::AddContextMenuActionsForSourceEntries(
|
||||
[[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
|
||||
{
|
||||
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
|
||||
});
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
menu->addAction("Create Material...", [entry]()
|
||||
{
|
||||
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
|
||||
QString(AZ::Utils::GetProjectPath().c_str()) +
|
||||
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Assets" +
|
||||
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
|
||||
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
|
||||
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
|
||||
entry->GetFullPath(), AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
|
||||
});
|
||||
|
||||
AddPerforceMenuActions(caller, menu, entry);
|
||||
}
|
||||
|
||||
void MaterialEditorBrowserInteractions::AddContextMenuActionsForOtherSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry)
|
||||
{
|
||||
menu->addAction("Open", [entry]()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(entry->GetFullPath().c_str()));
|
||||
});
|
||||
|
||||
menu->addAction("Duplicate...", [entry]()
|
||||
{
|
||||
const QFileInfo duplicateFileInfo(AtomToolsFramework::GetDuplicationFileInfo(entry->GetFullPath().c_str()));
|
||||
@@ -142,79 +99,26 @@ namespace MaterialEditor
|
||||
}
|
||||
});
|
||||
|
||||
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
|
||||
});
|
||||
|
||||
AddPerforceMenuActions(caller, menu, entry);
|
||||
}
|
||||
|
||||
void MaterialEditorBrowserInteractions::AddContextMenuActionsForMaterialSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry)
|
||||
{
|
||||
menu->addAction("Open", [entry]()
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, entry->GetFullPath());
|
||||
});
|
||||
|
||||
menu->addAction("Duplicate...", [entry]()
|
||||
{
|
||||
const QFileInfo duplicateFileInfo(AtomToolsFramework::GetDuplicationFileInfo(entry->GetFullPath().c_str()));
|
||||
if (!duplicateFileInfo.absoluteFilePath().isEmpty())
|
||||
menu->addAction("Run Python on File...", [caller, entry]()
|
||||
{
|
||||
const QString script = QFileDialog::getOpenFileName(
|
||||
caller, QObject::tr("Run Script"), QString(AZ::Utils::GetProjectPath().c_str()), QString("*.py"));
|
||||
if (!script.isEmpty())
|
||||
{
|
||||
if (QFile::copy(entry->GetFullPath().c_str(), duplicateFileInfo.absoluteFilePath()))
|
||||
{
|
||||
QFile::setPermissions(duplicateFileInfo.absoluteFilePath(), QFile::ReadOther | QFile::WriteOther);
|
||||
|
||||
// Auto add file to source control
|
||||
AzToolsFramework::SourceControlCommandBus::Broadcast(&AzToolsFramework::SourceControlCommandBus::Events::RequestEdit,
|
||||
duplicateFileInfo.absoluteFilePath().toUtf8().constData(), true, [](bool, const AzToolsFramework::SourceControlFileInfo&) {});
|
||||
}
|
||||
AZStd::vector<AZStd::string_view> pythonArgs{ entry->GetFullPath() };
|
||||
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, script.toUtf8().constData(),
|
||||
pythonArgs);
|
||||
}
|
||||
});
|
||||
|
||||
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
|
||||
});
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
menu->addAction("Create Child Material...", [entry]()
|
||||
{
|
||||
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
|
||||
QString(AZ::Utils::GetProjectPath().c_str()) +
|
||||
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Assets" +
|
||||
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
|
||||
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
|
||||
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
|
||||
entry->GetFullPath(), AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
|
||||
});
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
QAction* openParentAction = menu->addAction("Open Parent Material", [entry]()
|
||||
{
|
||||
AZ_UNUSED(entry);
|
||||
// ToDo
|
||||
});
|
||||
openParentAction->setEnabled(false);
|
||||
|
||||
AddPerforceMenuActions(caller, menu, entry);
|
||||
}
|
||||
|
||||
void MaterialEditorBrowserInteractions::AddContextMenuActionsForFolder(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry* entry)
|
||||
void AtomToolsAssetBrowserInteractions::AddContextMenuActionsForFolderEntries(
|
||||
QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
|
||||
{
|
||||
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
|
||||
menu->addAction(QObject::tr("Create new sub folder..."), [caller, entry]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
|
||||
});
|
||||
|
||||
QAction* createFolderAction = menu->addAction(QObject::tr("Create new sub folder..."));
|
||||
QObject::connect(createFolderAction, &QAction::triggered, caller, [caller, entry]()
|
||||
{
|
||||
bool ok;
|
||||
bool ok = false;
|
||||
QString newFolderName = QInputDialog::getText(caller, "Enter new folder name", "name:", QLineEdit::Normal, "NewFolder", &ok);
|
||||
if (ok)
|
||||
{
|
||||
@@ -242,27 +146,29 @@ namespace MaterialEditor
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AtomToolsAssetBrowserInteractions::AddContextMenuActionsForAllEntries(
|
||||
[[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
|
||||
{
|
||||
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
|
||||
});
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
QAction* createMaterialAction = menu->addAction(QObject::tr("Create Material..."));
|
||||
QObject::connect(createMaterialAction, &QAction::triggered, caller, [caller, entry]()
|
||||
menu->addAction(QObject::tr("Copy Name To Clipboard"), [=]()
|
||||
{
|
||||
CreateMaterialDialog createDialog(entry->GetFullPath().c_str(), caller);
|
||||
createDialog.adjustSize();
|
||||
|
||||
if (createDialog.exec() == QDialog::Accepted &&
|
||||
!createDialog.m_materialFileInfo.absoluteFilePath().isEmpty() &&
|
||||
!createDialog.m_materialTypeFileInfo.absoluteFilePath().isEmpty())
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
|
||||
createDialog.m_materialTypeFileInfo.absoluteFilePath().toUtf8().constData(),
|
||||
createDialog.m_materialFileInfo.absoluteFilePath().toUtf8().constData());
|
||||
}
|
||||
QApplication::clipboard()->setText(entry->GetName().c_str());
|
||||
});
|
||||
menu->addAction(QObject::tr("Copy Path To Clipboard"), [=]()
|
||||
{
|
||||
QApplication::clipboard()->setText(entry->GetFullPath().c_str());
|
||||
});
|
||||
}
|
||||
|
||||
void MaterialEditorBrowserInteractions::AddPerforceMenuActions([[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
|
||||
void AtomToolsAssetBrowserInteractions::AddContextMenuActionsForSourceControl(
|
||||
[[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
|
||||
{
|
||||
using namespace AzToolsFramework;
|
||||
|
||||
@@ -282,7 +188,7 @@ namespace MaterialEditor
|
||||
QMenu::connect(sourceControlMenu, &QMenu::aboutToShow, [this, path]()
|
||||
{
|
||||
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::GetFileInfo, path.c_str(),
|
||||
[this](bool success, const SourceControlFileInfo& info) { UpdateSourceControlActions(success, info); });
|
||||
[this](bool success, const SourceControlFileInfo& info) { UpdateContextMenuActionsForSourceControl(success, info); });
|
||||
});
|
||||
|
||||
// add get latest action
|
||||
@@ -344,7 +250,7 @@ namespace MaterialEditor
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialEditorBrowserInteractions::UpdateSourceControlActions(bool success, AzToolsFramework::SourceControlFileInfo info)
|
||||
void AtomToolsAssetBrowserInteractions::UpdateContextMenuActionsForSourceControl(bool success, AzToolsFramework::SourceControlFileInfo info)
|
||||
{
|
||||
if (!success && m_caller)
|
||||
{
|
||||
@@ -367,4 +273,4 @@ namespace MaterialEditor
|
||||
m_undoCheckOutAction->setEnabled(info.IsManaged() && !info.IsReadOnly());
|
||||
}
|
||||
}
|
||||
} // namespace MaterialEditor
|
||||
} // namespace AtomToolsFramework
|
||||
@@ -9,6 +9,7 @@
|
||||
set(FILES
|
||||
Include/AtomToolsFramework/Application/AtomToolsApplication.h
|
||||
Include/AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowser.h
|
||||
Include/AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowserInteractions.h
|
||||
Include/AtomToolsFramework/AssetGridDialog/AssetGridDialog.h
|
||||
Include/AtomToolsFramework/Communication/LocalServer.h
|
||||
Include/AtomToolsFramework/Communication/LocalSocket.h
|
||||
@@ -44,6 +45,7 @@ set(FILES
|
||||
Source/AssetBrowser/AtomToolsAssetBrowser.cpp
|
||||
Source/AssetBrowser/AtomToolsAssetBrowser.qrc
|
||||
Source/AssetBrowser/AtomToolsAssetBrowser.ui
|
||||
Source/AssetBrowser/AtomToolsAssetBrowserInteractions.cpp
|
||||
Source/AssetGridDialog/AssetGridDialog.cpp
|
||||
Source/AssetGridDialog/AssetGridDialog.ui
|
||||
Source/Communication/LocalServer.cpp
|
||||
|
||||
@@ -1,47 +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 <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <Document/MaterialDocumentSettings.h>
|
||||
|
||||
namespace MaterialEditor
|
||||
{
|
||||
void MaterialDocumentSettings::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<MaterialDocumentSettings, AZ::UserSettings>()
|
||||
->Version(1)
|
||||
->Field("defaultMaterialTypeName", &MaterialDocumentSettings::m_defaultMaterialTypeName)
|
||||
;
|
||||
|
||||
if (auto editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<MaterialDocumentSettings>(
|
||||
"MaterialDocumentSettings", "")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialDocumentSettings::m_defaultMaterialTypeName, "Default Material Type Name", "")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<MaterialDocumentSettings>("MaterialDocumentSettings")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
->Attribute(AZ::Script::Attributes::Category, "Editor")
|
||||
->Attribute(AZ::Script::Attributes::Module, "materialeditor")
|
||||
->Constructor()
|
||||
->Constructor<const MaterialDocumentSettings&>()
|
||||
->Property("defaultMaterialTypeName", BehaviorValueProperty(&MaterialDocumentSettings::m_defaultMaterialTypeName))
|
||||
;
|
||||
}
|
||||
}
|
||||
} // namespace MaterialEditor
|
||||
@@ -1,30 +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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/UserSettings/UserSettings.h>
|
||||
#endif
|
||||
|
||||
namespace MaterialEditor
|
||||
{
|
||||
struct MaterialDocumentSettings
|
||||
: public AZ::UserSettings
|
||||
{
|
||||
AZ_RTTI(MaterialDocumentSettings, "{12E8461F-65AD-4AD2-8A1D-82C3B1183522}", AZ::UserSettings);
|
||||
AZ_CLASS_ALLOCATOR(MaterialDocumentSettings, AZ::SystemAllocator, 0);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
AZStd::string m_defaultMaterialTypeName = "StandardPBR";
|
||||
};
|
||||
} // namespace MaterialEditor
|
||||
@@ -6,21 +6,32 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
|
||||
#include <Document/MaterialDocument.h>
|
||||
#include <Document/MaterialDocumentRequestBus.h>
|
||||
#include <Document/MaterialDocumentSettings.h>
|
||||
#include <MaterialEditorApplication.h>
|
||||
#include <MaterialEditor_Traits_Platform.h>
|
||||
#include <Viewport/MaterialViewportModule.h>
|
||||
#include <Window/CreateMaterialDialog/CreateMaterialDialog.h>
|
||||
#include <Window/MaterialEditorWindow.h>
|
||||
#include <Window/MaterialEditorWindowSettings.h>
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QDialog>
|
||||
#include <QMenu>
|
||||
#include <QUrl>
|
||||
|
||||
void InitMaterialEditorResources()
|
||||
{
|
||||
// Must register qt resources from other modules
|
||||
@@ -56,7 +67,6 @@ namespace MaterialEditor
|
||||
void MaterialEditorApplication::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
Base::Reflect(context);
|
||||
MaterialDocumentSettings::Reflect(context);
|
||||
MaterialEditorWindowSettings::Reflect(context);
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
@@ -112,8 +122,77 @@ namespace MaterialEditor
|
||||
|
||||
void MaterialEditorApplication::CreateMainWindow()
|
||||
{
|
||||
m_materialEditorBrowserInteractions.reset(aznew MaterialEditorBrowserInteractions);
|
||||
m_window.reset(aznew MaterialEditorWindow);
|
||||
m_assetBrowserInteractions.reset(aznew AtomToolsFramework::AtomToolsAssetBrowserInteractions);
|
||||
m_assetBrowserInteractions->RegisterContextMenuActions(
|
||||
[](const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
|
||||
{
|
||||
return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source;
|
||||
},
|
||||
[]([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
|
||||
{
|
||||
const bool isMaterial = AzFramework::StringFunc::Path::IsExtension(
|
||||
entries.front()->GetFullPath().c_str(), AZ::RPI::MaterialSourceData::Extension);
|
||||
const bool isMaterialType = AzFramework::StringFunc::Path::IsExtension(
|
||||
entries.front()->GetFullPath().c_str(), AZ::RPI::MaterialTypeSourceData::Extension);
|
||||
if (isMaterial || isMaterialType)
|
||||
{
|
||||
menu->addAction(QObject::tr("Open"), [entries]()
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
|
||||
&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument,
|
||||
entries.front()->GetFullPath());
|
||||
});
|
||||
|
||||
const QString createActionName =
|
||||
isMaterialType ? QObject::tr("Create Material...") : QObject::tr("Create Child Material...");
|
||||
|
||||
menu->addAction(createActionName, [entries]()
|
||||
{
|
||||
const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
|
||||
QString(AZ::Utils::GetProjectPath().c_str()) +
|
||||
AZ_CORRECT_FILESYSTEM_SEPARATOR + "Assets" +
|
||||
AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
|
||||
AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
|
||||
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
|
||||
&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
|
||||
entries.front()->GetFullPath(),
|
||||
AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
menu->addAction(QObject::tr("Open"), [entries]()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(entries.front()->GetFullPath().c_str()));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
m_assetBrowserInteractions->RegisterContextMenuActions(
|
||||
[](const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
|
||||
{
|
||||
return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Folder;
|
||||
},
|
||||
[](QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
|
||||
{
|
||||
menu->addAction(QObject::tr("Create Material..."), [caller, entries]()
|
||||
{
|
||||
CreateMaterialDialog createDialog(entries.front()->GetFullPath().c_str(), caller);
|
||||
createDialog.adjustSize();
|
||||
|
||||
if (createDialog.exec() == QDialog::Accepted &&
|
||||
!createDialog.m_materialFileInfo.absoluteFilePath().isEmpty() &&
|
||||
!createDialog.m_materialTypeFileInfo.absoluteFilePath().isEmpty())
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
|
||||
&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
|
||||
createDialog.m_materialTypeFileInfo.absoluteFilePath().toUtf8().constData(),
|
||||
createDialog.m_materialFileInfo.absoluteFilePath().toUtf8().constData());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void MaterialEditorApplication::DestroyMainWindow()
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowserInteractions.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentApplication.h>
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h>
|
||||
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
|
||||
#include <Window/MaterialEditorBrowserInteractions.h>
|
||||
#include <Window/MaterialEditorWindow.h>
|
||||
|
||||
namespace MaterialEditor
|
||||
@@ -49,6 +49,6 @@ namespace MaterialEditor
|
||||
QWidget* GetAppMainWindow() override;
|
||||
|
||||
AZStd::unique_ptr<MaterialEditorWindow> m_window;
|
||||
AZStd::unique_ptr<MaterialEditorBrowserInteractions> m_materialEditorBrowserInteractions;
|
||||
AZStd::unique_ptr<AtomToolsFramework::AtomToolsAssetBrowserInteractions> m_assetBrowserInteractions;
|
||||
};
|
||||
} // namespace MaterialEditor
|
||||
|
||||
+3
-5
@@ -14,7 +14,6 @@
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzQtComponents/Components/Widgets/FileDialog.h>
|
||||
#include <Document/MaterialDocumentSettings.h>
|
||||
#include <Window/CreateMaterialDialog/CreateMaterialDialog.h>
|
||||
|
||||
namespace MaterialEditor
|
||||
@@ -65,11 +64,10 @@ namespace MaterialEditor
|
||||
|
||||
m_ui->m_materialTypeComboBox->model()->sort(0, Qt::AscendingOrder);
|
||||
|
||||
// Select the default material type from settings
|
||||
auto settings =
|
||||
AZ::UserSettings::CreateFind<MaterialDocumentSettings>(AZ::Crc32("MaterialDocumentSettings"), AZ::UserSettings::CT_GLOBAL);
|
||||
const AZStd::string defaultMaterialType = AtomToolsFramework::GetSettingOrDefault<AZStd::string>(
|
||||
"/O3DE/Atom/MaterialEditor/CreateMaterialDialog/DefaultMaterialType", "StandardPBR");
|
||||
|
||||
const int index = m_ui->m_materialTypeComboBox->findText(settings->m_defaultMaterialTypeName.c_str());
|
||||
const int index = m_ui->m_materialTypeComboBox->findText(defaultMaterialType.c_str());
|
||||
if (index >= 0)
|
||||
{
|
||||
m_ui->m_materialTypeComboBox->setCurrentIndex(index);
|
||||
|
||||
@@ -1,58 +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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
|
||||
|
||||
class QWidget;
|
||||
class QMenu;
|
||||
class QAction;
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
class AssetBrowserEntry;
|
||||
class SourceAssetBrowserEntry;
|
||||
class FolderAssetBrowserEntry;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MaterialEditor
|
||||
{
|
||||
class MaterialEditorBrowserInteractions
|
||||
: public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(MaterialEditorBrowserInteractions, AZ::SystemAllocator, 0);
|
||||
|
||||
MaterialEditorBrowserInteractions();
|
||||
~MaterialEditorBrowserInteractions();
|
||||
|
||||
private:
|
||||
//! AssetBrowserInteractionNotificationBus::Handler overrides...
|
||||
void AddContextMenuActions(QWidget* caller, QMenu* menu, const AZStd::vector<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>& entries) override;
|
||||
|
||||
void AddGenericContextMenuActions(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
|
||||
void AddContextMenuActionsForOtherSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry);
|
||||
void AddContextMenuActionsForMaterialSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry);
|
||||
void AddContextMenuActionsForMaterialTypeSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry);
|
||||
void AddContextMenuActionsForFolder(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry* entry);
|
||||
void AddPerforceMenuActions(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
|
||||
|
||||
void UpdateSourceControlActions(bool success, AzToolsFramework::SourceControlFileInfo info);
|
||||
|
||||
QWidget* m_caller = nullptr;
|
||||
QAction* m_addAction = nullptr;
|
||||
QAction* m_checkOutAction = nullptr;
|
||||
QAction* m_undoCheckOutAction = nullptr;
|
||||
QAction* m_getLatestAction = nullptr;
|
||||
};
|
||||
} // namespace MaterialEditor
|
||||
@@ -146,7 +146,7 @@ namespace MaterialEditor
|
||||
|
||||
bool MaterialEditorWindow::GetCreateDocumentParams(AZStd::string& openPath, AZStd::string& savePath)
|
||||
{
|
||||
CreateMaterialDialog createDialog(this);
|
||||
CreateMaterialDialog createDialog(openPath.c_str(), this);
|
||||
createDialog.adjustSize();
|
||||
|
||||
if (createDialog.exec() == QDialog::Accepted &&
|
||||
|
||||
+1
-32
@@ -14,8 +14,6 @@ namespace MaterialEditor
|
||||
SettingsWidget::SettingsWidget(QWidget* parent)
|
||||
: AtomToolsFramework::InspectorWidget(parent)
|
||||
{
|
||||
m_documentSettings =
|
||||
AZ::UserSettings::CreateFind<MaterialDocumentSettings>(AZ_CRC_CE("MaterialDocumentSettings"), AZ::UserSettings::CT_GLOBAL);
|
||||
m_documentSystemSettings = AZ::UserSettings::CreateFind<AtomToolsFramework::AtomToolsDocumentSystemSettings>(
|
||||
AZ_CRC_CE("AtomToolsDocumentSystemSettings"), AZ::UserSettings::CT_GLOBAL);
|
||||
}
|
||||
@@ -29,23 +27,9 @@ namespace MaterialEditor
|
||||
{
|
||||
AddGroupsBegin();
|
||||
AddDocumentSystemSettingsGroup();
|
||||
AddDocumentSettingsGroup();
|
||||
AddGroupsEnd();
|
||||
}
|
||||
|
||||
void SettingsWidget::AddDocumentSettingsGroup()
|
||||
{
|
||||
const AZStd::string groupName = "documentSettings";
|
||||
const AZStd::string groupDisplayName = "Document Settings";
|
||||
const AZStd::string groupDescription = "Document Settings";
|
||||
|
||||
const AZ::Crc32 saveStateKey(AZStd::string::format("SettingsWidget::DocumentSettingsGroup"));
|
||||
AddGroup(
|
||||
groupName, groupDisplayName, groupDescription,
|
||||
new AtomToolsFramework::InspectorPropertyGroupWidget(
|
||||
m_documentSettings.get(), nullptr, m_documentSettings->TYPEINFO_Uuid(), this, this, saveStateKey));
|
||||
}
|
||||
|
||||
void SettingsWidget::AddDocumentSystemSettingsGroup()
|
||||
{
|
||||
const AZStd::string groupName = "documentSystemSettings";
|
||||
@@ -56,7 +40,7 @@ namespace MaterialEditor
|
||||
AddGroup(
|
||||
groupName, groupDisplayName, groupDescription,
|
||||
new AtomToolsFramework::InspectorPropertyGroupWidget(
|
||||
m_documentSystemSettings.get(), nullptr, m_documentSystemSettings->TYPEINFO_Uuid(), this, this, saveStateKey));
|
||||
m_documentSystemSettings.get(), nullptr, m_documentSystemSettings->TYPEINFO_Uuid(), nullptr, this, saveStateKey));
|
||||
}
|
||||
|
||||
void SettingsWidget::Reset()
|
||||
@@ -64,21 +48,6 @@ namespace MaterialEditor
|
||||
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
|
||||
AtomToolsFramework::InspectorWidget::Reset();
|
||||
}
|
||||
|
||||
void SettingsWidget::BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode)
|
||||
{
|
||||
AZ_UNUSED(pNode);
|
||||
}
|
||||
|
||||
void SettingsWidget::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
|
||||
{
|
||||
AZ_UNUSED(pNode);
|
||||
}
|
||||
|
||||
void SettingsWidget::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode)
|
||||
{
|
||||
AZ_UNUSED(pNode);
|
||||
}
|
||||
} // namespace MaterialEditor
|
||||
|
||||
//#include <Window/SettingsWidget/moc_SettingsWidget.cpp>
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemSettings.h>
|
||||
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
|
||||
#include <Document/MaterialDocumentSettings.h>
|
||||
#endif
|
||||
|
||||
namespace MaterialEditor
|
||||
@@ -20,7 +19,6 @@ namespace MaterialEditor
|
||||
//! Provides controls for viewing and editing settings.
|
||||
class SettingsWidget
|
||||
: public AtomToolsFramework::InspectorWidget
|
||||
, private AzToolsFramework::IPropertyEditorNotify
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -32,22 +30,11 @@ namespace MaterialEditor
|
||||
void Populate();
|
||||
|
||||
private:
|
||||
void AddDocumentSettingsGroup();
|
||||
void AddDocumentSystemSettingsGroup();
|
||||
|
||||
// AtomToolsFramework::InspectorRequestBus::Handler overrides...
|
||||
void Reset() override;
|
||||
|
||||
// AzToolsFramework::IPropertyEditorNotify overrides...
|
||||
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
|
||||
void AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
|
||||
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
|
||||
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
|
||||
void SealUndoStack() override {}
|
||||
void RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode*, const QPoint&) override {}
|
||||
void PropertySelectionChanged(AzToolsFramework::InstanceDataNode*, bool) override {}
|
||||
|
||||
AZStd::intrusive_ptr<MaterialDocumentSettings> m_documentSettings;
|
||||
AZStd::intrusive_ptr<AtomToolsFramework::AtomToolsDocumentSystemSettings> m_documentSystemSettings;
|
||||
};
|
||||
} // namespace MaterialEditor
|
||||
|
||||
@@ -12,10 +12,8 @@ set(FILES
|
||||
Source/MaterialEditorApplication.h
|
||||
|
||||
Source/Document/MaterialDocumentRequestBus.h
|
||||
Source/Document/MaterialDocumentSettings.h
|
||||
Source/Document/MaterialDocument.cpp
|
||||
Source/Document/MaterialDocument.h
|
||||
Source/Document/MaterialDocumentSettings.cpp
|
||||
|
||||
Source/Viewport/MaterialViewportModule.h
|
||||
Source/Viewport/MaterialViewportModule.cpp
|
||||
@@ -49,8 +47,6 @@ set(FILES
|
||||
Source/Viewport/MaterialViewportWidget.ui
|
||||
|
||||
Source/Window/MaterialEditorWindowSettings.h
|
||||
Source/Window/MaterialEditorBrowserInteractions.h
|
||||
Source/Window/MaterialEditorBrowserInteractions.cpp
|
||||
Source/Window/MaterialEditorWindow.h
|
||||
Source/Window/MaterialEditorWindow.cpp
|
||||
Source/Window/MaterialEditorWindowSettings.cpp
|
||||
|
||||
+47
-4
@@ -12,12 +12,16 @@
|
||||
#include <Atom/RPI.Public/Material/Material.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/UI/UICore/QWidgetSavedState.h>
|
||||
#include <Document/ShaderManagementConsoleDocument.h>
|
||||
#include <Document/ShaderManagementConsoleDocumentRequestBus.h>
|
||||
@@ -25,11 +29,13 @@
|
||||
#include <ShaderManagementConsoleRequestBus.h>
|
||||
#include <ShaderManagementConsole_Traits_Platform.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QDesktopServices>
|
||||
#include <QDialog>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
#include <QUrl>
|
||||
|
||||
void InitShaderManagementConsoleResources()
|
||||
{
|
||||
@@ -135,9 +141,46 @@ namespace ShaderManagementConsole
|
||||
|
||||
void ShaderManagementConsoleApplication::CreateMainWindow()
|
||||
{
|
||||
m_assetBrowserInteractions.reset(aznew ShaderManagementConsoleBrowserInteractions);
|
||||
m_window.reset(aznew ShaderManagementConsoleWindow);
|
||||
m_window->show();
|
||||
m_assetBrowserInteractions.reset(aznew AtomToolsFramework::AtomToolsAssetBrowserInteractions);
|
||||
m_assetBrowserInteractions->RegisterContextMenuActions(
|
||||
[](const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
|
||||
{
|
||||
return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source;
|
||||
},
|
||||
[]([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
|
||||
{
|
||||
if (AzFramework::StringFunc::Path::IsExtension(
|
||||
entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderSourceData::Extension))
|
||||
{
|
||||
menu->addAction("Generate Shader Variant List", [entries]()
|
||||
{
|
||||
const QString script =
|
||||
"@engroot@/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py";
|
||||
AZStd::vector<AZStd::string_view> pythonArgs{ entries.front()->GetFullPath() };
|
||||
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, script.toUtf8().constData(),
|
||||
pythonArgs);
|
||||
});
|
||||
}
|
||||
else if (AzFramework::StringFunc::Path::IsExtension(
|
||||
entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
|
||||
{
|
||||
menu->addAction(QObject::tr("Open"), [entries]()
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
|
||||
&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument,
|
||||
entries.front()->GetFullPath());
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
menu->addAction(QObject::tr("Open"), [entries]()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(entries.front()->GetFullPath().c_str()));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleApplication::DestroyMainWindow()
|
||||
|
||||
+2
-2
@@ -9,11 +9,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowserInteractions.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentApplication.h>
|
||||
#include <AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h>
|
||||
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
|
||||
#include <ShaderManagementConsoleRequestBus.h>
|
||||
#include <Window/ShaderManagementConsoleBrowserInteractions.h>
|
||||
#include <Window/ShaderManagementConsoleWindow.h>
|
||||
|
||||
namespace ShaderManagementConsole
|
||||
@@ -55,6 +55,6 @@ namespace ShaderManagementConsole
|
||||
|
||||
private:
|
||||
AZStd::unique_ptr<ShaderManagementConsoleWindow> m_window;
|
||||
AZStd::unique_ptr<ShaderManagementConsoleBrowserInteractions> m_assetBrowserInteractions;
|
||||
AZStd::unique_ptr<AtomToolsFramework::AtomToolsAssetBrowserInteractions> m_assetBrowserInteractions;
|
||||
};
|
||||
} // namespace ShaderManagementConsole
|
||||
|
||||
-273
@@ -1,273 +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 <Atom/RPI.Edit/Shader/ShaderVariantListSourceData.h>
|
||||
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
|
||||
#include <AtomToolsFramework/Util/Util.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzQtComponents/Utilities/DesktopUtilities.h>
|
||||
#include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
|
||||
#include <AzToolsFramework/Thumbnails/SourceControlThumbnail.h>
|
||||
#include <Window/ShaderManagementConsoleBrowserInteractions.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace ShaderManagementConsole
|
||||
{
|
||||
ShaderManagementConsoleBrowserInteractions::ShaderManagementConsoleBrowserInteractions()
|
||||
{
|
||||
using namespace AzToolsFramework::AssetBrowser;
|
||||
|
||||
AssetBrowserInteractionNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
ShaderManagementConsoleBrowserInteractions::~ShaderManagementConsoleBrowserInteractions()
|
||||
{
|
||||
AssetBrowserInteractionNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
AzToolsFramework::AssetBrowser::SourceFileDetails ShaderManagementConsoleBrowserInteractions::GetSourceFileDetails([[maybe_unused]] const char* fullSourceFileName)
|
||||
{
|
||||
return AzToolsFramework::AssetBrowser::SourceFileDetails();
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleBrowserInteractions::AddContextMenuActions(QWidget* caller, QMenu* menu, const AZStd::vector<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>& entries)
|
||||
{
|
||||
AssetBrowserEntry* entry = entries.empty() ? nullptr : entries.front();
|
||||
if (!entry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_caller = caller;
|
||||
QObject::connect(m_caller, &QObject::destroyed, [this]()
|
||||
{
|
||||
m_caller = nullptr;
|
||||
});
|
||||
|
||||
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
|
||||
{
|
||||
const auto source = azalias_cast<const SourceAssetBrowserEntry*>(entry);
|
||||
AddContextMenuActionsForOtherSource(caller, menu, source);
|
||||
}
|
||||
else if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Folder)
|
||||
{
|
||||
const auto folder = azalias_cast<const FolderAssetBrowserEntry*>(entry);
|
||||
AddContextMenuActionsForFolder(caller, menu, folder);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleBrowserInteractions::AddContextMenuActionsForOtherSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry)
|
||||
{
|
||||
menu->addAction("Open", [entry]()
|
||||
{
|
||||
if (AzFramework::StringFunc::Path::IsExtension(entry->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
|
||||
{
|
||||
AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, entry->GetFullPath().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(entry->GetFullPath().c_str()));
|
||||
}
|
||||
});
|
||||
|
||||
menu->addAction("Duplicate...", [entry]()
|
||||
{
|
||||
const QFileInfo duplicateFileInfo(AtomToolsFramework::GetDuplicationFileInfo(entry->GetFullPath().c_str()));
|
||||
if (!duplicateFileInfo.absoluteFilePath().isEmpty())
|
||||
{
|
||||
if (QFile::copy(entry->GetFullPath().c_str(), duplicateFileInfo.absoluteFilePath()))
|
||||
{
|
||||
QFile::setPermissions(duplicateFileInfo.absoluteFilePath(), QFile::ReadOther | QFile::WriteOther);
|
||||
|
||||
// Auto add file to source control
|
||||
AzToolsFramework::SourceControlCommandBus::Broadcast(&AzToolsFramework::SourceControlCommandBus::Events::RequestEdit,
|
||||
duplicateFileInfo.absoluteFilePath().toUtf8().constData(), true, [](bool, const AzToolsFramework::SourceControlFileInfo&) {});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
|
||||
});
|
||||
|
||||
menu->addSeparator();
|
||||
menu->addAction("Generate Shader Variant List", [entry]() {
|
||||
const QString script = "@engroot@/Gems/Atom/Tools/ShaderManagementConsole/Scripts/GenerateShaderVariantListForMaterials.py";
|
||||
AZStd::vector<AZStd::string_view> pythonArgs{ entry->GetFullPath() };
|
||||
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, script.toUtf8().constData(), pythonArgs);
|
||||
});
|
||||
|
||||
menu->addAction("Run Python on Asset...", [entry]()
|
||||
{
|
||||
const QString script = QFileDialog::getOpenFileName(nullptr, "Run Script", QString(), QString("*.py"));
|
||||
if (!script.isEmpty())
|
||||
{
|
||||
AZStd::vector<AZStd::string_view> pythonArgs { entry->GetFullPath() };
|
||||
AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, script.toUtf8().constData(), pythonArgs);
|
||||
}
|
||||
});
|
||||
|
||||
AddPerforceMenuActions(caller, menu, entry);
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleBrowserInteractions::AddContextMenuActionsForFolder(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry* entry)
|
||||
{
|
||||
menu->addAction(AzQtComponents::fileBrowserActionName(), [entry]()
|
||||
{
|
||||
AzQtComponents::ShowFileOnDesktop(entry->GetFullPath().c_str());
|
||||
});
|
||||
|
||||
QAction* createFolderAction = menu->addAction(QObject::tr("Create new sub folder..."));
|
||||
QObject::connect(createFolderAction, &QAction::triggered, caller, [caller, entry]()
|
||||
{
|
||||
bool ok;
|
||||
QString newFolderName = QInputDialog::getText(caller, "Enter new folder name", "name:", QLineEdit::Normal, "NewFolder", &ok);
|
||||
if (ok)
|
||||
{
|
||||
if (newFolderName.isEmpty())
|
||||
{
|
||||
QMessageBox msgBox(QMessageBox::Icon::Critical, "Error", "Folder name can't be empty", QMessageBox::Ok, caller);
|
||||
msgBox.exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
AZStd::string newFolderPath;
|
||||
AzFramework::StringFunc::Path::Join(entry->GetFullPath().c_str(), newFolderName.toUtf8().constData(), newFolderPath);
|
||||
QDir dir(newFolderPath.c_str());
|
||||
if (dir.exists())
|
||||
{
|
||||
QMessageBox::critical(caller, "Error", "Folder with this name already exists");
|
||||
return;
|
||||
}
|
||||
auto result = dir.mkdir(newFolderPath.c_str());
|
||||
if (!result)
|
||||
{
|
||||
AZ_Error("ShaderManagementConsoleBrowser", false, "Failed to make new folder");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleBrowserInteractions::AddPerforceMenuActions([[maybe_unused]] QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
|
||||
{
|
||||
using namespace AzToolsFramework;
|
||||
|
||||
bool isActive = false;
|
||||
SourceControlConnectionRequestBus::BroadcastResult(isActive, &SourceControlConnectionRequests::IsActive);
|
||||
|
||||
if (isActive)
|
||||
{
|
||||
menu->addSeparator();
|
||||
|
||||
AZStd::string path = entry->GetFullPath();
|
||||
AzFramework::StringFunc::Path::Normalize(path);
|
||||
|
||||
QMenu* sourceControlMenu = menu->addMenu("Source Control");
|
||||
|
||||
// Update the enabled state of source control menu actions only if menu is shown
|
||||
QMenu::connect(sourceControlMenu, &QMenu::aboutToShow, [this, path]()
|
||||
{
|
||||
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::GetFileInfo, path.c_str(),
|
||||
[this](bool success, const SourceControlFileInfo& info) { UpdateSourceControlActions(success, info); });
|
||||
});
|
||||
|
||||
// add get latest action
|
||||
m_getLatestAction = sourceControlMenu->addAction("Get Latest", [path]()
|
||||
{
|
||||
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestLatest, path.c_str(),
|
||||
[](bool, const SourceControlFileInfo&) {});
|
||||
});
|
||||
QObject::connect(m_getLatestAction, &QObject::destroyed, [this]()
|
||||
{
|
||||
m_getLatestAction = nullptr;
|
||||
});
|
||||
m_getLatestAction->setEnabled(false);
|
||||
|
||||
// add add action
|
||||
m_addAction = sourceControlMenu->addAction("Add", [path]()
|
||||
{
|
||||
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestEdit, path.c_str(), true,
|
||||
[path](bool, const SourceControlFileInfo&)
|
||||
{
|
||||
SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
|
||||
});
|
||||
});
|
||||
QObject::connect(m_addAction, &QObject::destroyed, [this]()
|
||||
{
|
||||
m_addAction = nullptr;
|
||||
});
|
||||
m_addAction->setEnabled(false);
|
||||
|
||||
// add checkout action
|
||||
m_checkOutAction = sourceControlMenu->addAction("Check Out", [path]()
|
||||
{
|
||||
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestEdit, path.c_str(), true,
|
||||
[path](bool, const SourceControlFileInfo&)
|
||||
{
|
||||
SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
|
||||
});
|
||||
});
|
||||
QObject::connect(m_checkOutAction, &QObject::destroyed, [this]()
|
||||
{
|
||||
m_checkOutAction = nullptr;
|
||||
});
|
||||
m_checkOutAction->setEnabled(false);
|
||||
|
||||
// add undo checkout action
|
||||
m_undoCheckOutAction = sourceControlMenu->addAction("Undo Check Out", [path]()
|
||||
{
|
||||
SourceControlCommandBus::Broadcast(&SourceControlCommandBus::Events::RequestRevert, path.c_str(),
|
||||
[path](bool, const SourceControlFileInfo&)
|
||||
{
|
||||
SourceControlThumbnailRequestBus::Broadcast(&SourceControlThumbnailRequests::FileStatusChanged, path.c_str());
|
||||
});
|
||||
});
|
||||
QObject::connect(m_undoCheckOutAction, &QObject::destroyed, [this]()
|
||||
{
|
||||
m_undoCheckOutAction = nullptr;
|
||||
});
|
||||
m_undoCheckOutAction->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderManagementConsoleBrowserInteractions::UpdateSourceControlActions(bool success, AzToolsFramework::SourceControlFileInfo info)
|
||||
{
|
||||
if (!success && m_caller)
|
||||
{
|
||||
QMessageBox::critical(m_caller, "Error", "Source control operation failed.");
|
||||
}
|
||||
if (m_getLatestAction)
|
||||
{
|
||||
m_getLatestAction->setEnabled(info.IsManaged() && info.HasFlag(AzToolsFramework::SCF_OutOfDate));
|
||||
}
|
||||
if (m_addAction)
|
||||
{
|
||||
m_addAction->setEnabled(!info.IsManaged());
|
||||
}
|
||||
if (m_checkOutAction)
|
||||
{
|
||||
m_checkOutAction->setEnabled(info.IsManaged() && info.IsReadOnly() && !info.IsLockedByOther());
|
||||
}
|
||||
if (m_undoCheckOutAction)
|
||||
{
|
||||
m_undoCheckOutAction->setEnabled(info.IsManaged() && !info.IsReadOnly());
|
||||
}
|
||||
}
|
||||
} // namespace ShaderManagementConsole
|
||||
-58
@@ -1,58 +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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
|
||||
#include <AzToolsFramework/SourceControl/SourceControlAPI.h>
|
||||
|
||||
class QWidget;
|
||||
class QMenu;
|
||||
class QAction;
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace AssetBrowser
|
||||
{
|
||||
class AssetBrowserEntry;
|
||||
class SourceAssetBrowserEntry;
|
||||
class FolderAssetBrowserEntry;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShaderManagementConsole
|
||||
{
|
||||
class ShaderManagementConsoleBrowserInteractions
|
||||
: public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ShaderManagementConsoleBrowserInteractions, AZ::SystemAllocator, 0);
|
||||
|
||||
ShaderManagementConsoleBrowserInteractions();
|
||||
~ShaderManagementConsoleBrowserInteractions();
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AssetBrowserInteractionNotificationBus::Handler implementation
|
||||
AzToolsFramework::AssetBrowser::SourceFileDetails GetSourceFileDetails(const char* fullSourceFileName) override;
|
||||
void AddContextMenuActions(QWidget* caller, QMenu* menu, const AZStd::vector<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>& entries) override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AddContextMenuActionsForOtherSource(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* entry);
|
||||
void AddContextMenuActionsForFolder(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::FolderAssetBrowserEntry* entry);
|
||||
void AddPerforceMenuActions(QWidget* caller, QMenu* menu, const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
|
||||
|
||||
void UpdateSourceControlActions(bool success, AzToolsFramework::SourceControlFileInfo info);
|
||||
|
||||
QWidget* m_caller = nullptr;
|
||||
QAction* m_addAction = nullptr;
|
||||
QAction* m_checkOutAction = nullptr;
|
||||
QAction* m_undoCheckOutAction = nullptr;
|
||||
QAction* m_getLatestAction = nullptr;
|
||||
};
|
||||
} // namespace ShaderManagementConsole
|
||||
@@ -11,8 +11,6 @@ set(FILES
|
||||
Source/Document/ShaderManagementConsoleDocument.cpp
|
||||
Source/Document/ShaderManagementConsoleDocument.h
|
||||
|
||||
Source/Window/ShaderManagementConsoleBrowserInteractions.h
|
||||
Source/Window/ShaderManagementConsoleBrowserInteractions.cpp
|
||||
Source/Window/ShaderManagementConsoleWindow.h
|
||||
Source/Window/ShaderManagementConsoleWindow.cpp
|
||||
Source/Window/ShaderManagementConsole.qrc
|
||||
|
||||
@@ -177,10 +177,19 @@ namespace EMStudio
|
||||
m_rotateManipulators.ConfigureView(
|
||||
AzToolsFramework::RotationManipulatorRadius(), AzFramework::ViewportColors::XAxisColor, AzFramework::ViewportColors::YAxisColor,
|
||||
AzFramework::ViewportColors::ZAxisColor);
|
||||
m_rotateManipulators.InstallLeftMouseDownCallback(
|
||||
[this]([[maybe_unused]]const AzToolsFramework::AngularManipulator::Action& action)
|
||||
{
|
||||
const AZ::EntityId entityId = m_animViewportWidget->GetAnimViewportRenderer()->GetEntityId();
|
||||
AZ::TransformBus::EventResult(m_mouseDownStartTransform, entityId, &AZ::TransformBus::Events::GetLocalTM);
|
||||
});
|
||||
|
||||
m_rotateManipulators.InstallMouseMoveCallback(
|
||||
[this](const AzToolsFramework::AngularManipulator::Action& action)
|
||||
{
|
||||
OnManipulatorRotated(action.LocalOrientation());
|
||||
const AZ::EntityId entityId = m_animViewportWidget->GetAnimViewportRenderer()->GetEntityId();
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion,
|
||||
m_mouseDownStartTransform.GetRotation() * action.m_current.m_delta);
|
||||
});
|
||||
|
||||
// Setup the scale manipulator
|
||||
@@ -189,10 +198,21 @@ namespace EMStudio
|
||||
m_scaleManipulators.ConfigureView(
|
||||
AzToolsFramework::LinearManipulatorAxisLength(), AzFramework::ViewportColors::XAxisColor,
|
||||
AzFramework::ViewportColors::YAxisColor, AzFramework::ViewportColors::ZAxisColor);
|
||||
m_scaleManipulators.InstallAxisLeftMouseDownCallback(
|
||||
[this]([[maybe_unused]] const AzToolsFramework::LinearManipulator::Action& action)
|
||||
{
|
||||
const AZ::EntityId entityId = m_animViewportWidget->GetAnimViewportRenderer()->GetEntityId();
|
||||
AZ::TransformBus::EventResult(m_mouseDownStartTransform, entityId, &AZ::TransformBus::Events::GetLocalTM);
|
||||
});
|
||||
m_scaleManipulators.InstallAxisMouseMoveCallback(
|
||||
[this](const AzToolsFramework::LinearManipulator::Action& action)
|
||||
{
|
||||
OnManipulatorScaled(action.LocalScale(), action.LocalScaleOffset());
|
||||
// Since we are compulting a uniform scale, the delta scale should be the none-zero value from one of the three axis.
|
||||
const float deltaScale = action.m_current.m_localPositionOffset.GetMaxElement()
|
||||
+ action.m_current.m_localPositionOffset.GetMinElement();
|
||||
const AZ::EntityId entityId = m_animViewportWidget->GetAnimViewportRenderer()->GetEntityId();
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale,
|
||||
m_mouseDownStartTransform.GetUniformScale() + deltaScale);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -259,33 +279,6 @@ namespace EMStudio
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalTranslation, position);
|
||||
}
|
||||
|
||||
void AtomRenderPlugin::OnManipulatorRotated(const AZ::Quaternion& rotation)
|
||||
{
|
||||
const AZ::EntityId entityId = m_animViewportWidget->GetAnimViewportRenderer()->GetEntityId();
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation);
|
||||
}
|
||||
|
||||
void AtomRenderPlugin::OnManipulatorScaled(
|
||||
const AZ::Vector3& scale, const AZ::Vector3& scaleOffset)
|
||||
{
|
||||
// Use the scaleOffset to determine which axis to use on the uniform scale.
|
||||
float localScale = 1.0f;
|
||||
if (scaleOffset.GetX() != 0.0f)
|
||||
{
|
||||
localScale = scale.GetX();
|
||||
}
|
||||
else if (scaleOffset.GetY() != 0.0f)
|
||||
{
|
||||
localScale = scale.GetY();
|
||||
}
|
||||
else if (scaleOffset.GetZ() != 0.0f)
|
||||
{
|
||||
localScale = scale.GetZ();
|
||||
}
|
||||
const AZ::EntityId entityId = m_animViewportWidget->GetAnimViewportRenderer()->GetEntityId();
|
||||
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, localScale);
|
||||
}
|
||||
|
||||
void AtomRenderPlugin::LoadRenderOptions()
|
||||
{
|
||||
AZStd::string renderOptionsFilename(GetManager()->GetAppDataFolder());
|
||||
|
||||
@@ -70,8 +70,6 @@ namespace EMStudio
|
||||
|
||||
void SetupManipulators();
|
||||
void OnManipulatorMoved(const AZ::Vector3& position);
|
||||
void OnManipulatorRotated(const AZ::Quaternion& rotation);
|
||||
void OnManipulatorScaled(const AZ::Vector3& scale, const AZ::Vector3& scaleOffset);
|
||||
|
||||
QWidget* m_innerWidget = nullptr;
|
||||
AnimViewportWidget* m_animViewportWidget = nullptr;
|
||||
@@ -82,6 +80,7 @@ namespace EMStudio
|
||||
AzToolsFramework::RotationManipulators m_rotateManipulators;
|
||||
AzToolsFramework::ScaleManipulators m_scaleManipulators;
|
||||
AZStd::shared_ptr<AzToolsFramework::ManipulatorManager> m_manipulatorManager;
|
||||
AZ::Transform m_mouseDownStartTransform;
|
||||
|
||||
MCORE_DEFINECOMMANDCALLBACK(ImportActorCallback);
|
||||
MCORE_DEFINECOMMANDCALLBACK(RemoveActorCallback);
|
||||
|
||||
+20
-21
@@ -282,8 +282,8 @@ namespace AMD
|
||||
TressFXTFXFileHeader header = {};
|
||||
|
||||
// read the header
|
||||
EI_Seek(ioObject, 0); // make sure the stream pos is at the beginning.
|
||||
EI_Read((void*)&header, sizeof(TressFXTFXFileHeader), ioObject);
|
||||
[[maybe_unused]] auto eiSeekResult = EI_Seek(ioObject, 0); // make sure the stream pos is at the beginning.
|
||||
[[maybe_unused]] auto eiReadResult = EI_Read((void*)&header, sizeof(TressFXTFXFileHeader), ioObject);
|
||||
|
||||
// If the tfx version is lower than the current major version, exit.
|
||||
if (header.version < AMD_TRESSFX_VERSION_MAJOR)
|
||||
@@ -317,8 +317,8 @@ namespace AMD
|
||||
m_positions.resize(m_numTotalVertices); // size of m_positions = number of total vertices * sizeo of each position vector.
|
||||
|
||||
// Read position data from the io stream.
|
||||
EI_Seek(ioObject, header.offsetVertexPosition);
|
||||
EI_Read((void*)m_positions.data(), numStrandsInFile * m_numVerticesPerStrand * sizeof(AMD::float4), ioObject); // note that the position data in io stream contains only guide hairs. If we call GenerateFollowHairs
|
||||
eiSeekResult = EI_Seek(ioObject, header.offsetVertexPosition);
|
||||
eiReadResult = EI_Read((void*)m_positions.data(), numStrandsInFile * m_numVerticesPerStrand * sizeof(AMD::float4), ioObject); // note that the position data in io stream contains only guide hairs. If we call GenerateFollowHairs
|
||||
// to generate follow hairs, m_positions will be re-allocated.
|
||||
|
||||
// We need to make up some strands to fill up the buffer because the number of strands from stream is not necessarily multile of thread size.
|
||||
@@ -335,11 +335,11 @@ namespace AMD
|
||||
}
|
||||
|
||||
// Read strand UVs
|
||||
EI_Seek(ioObject, header.offsetStrandUV);
|
||||
eiSeekResult = EI_Seek(ioObject, header.offsetStrandUV);
|
||||
m_strandUV.resize(m_numTotalStrands); // If we call GenerateFollowHairs to generate follow hairs,
|
||||
// m_strandUV will be re-allocated.
|
||||
|
||||
EI_Read((void*)m_strandUV.data(), numStrandsInFile * sizeof(AMD::float2), ioObject);
|
||||
eiReadResult = EI_Read((void*)m_strandUV.data(), numStrandsInFile * sizeof(AMD::float2), ioObject);
|
||||
|
||||
// Fill up the last empty space
|
||||
AMD::int32 indexLastStrand = (numStrandsInFile - 1);
|
||||
@@ -683,21 +683,21 @@ namespace AMD
|
||||
void TressFXAsset::GetBonesNames(FILE* ioObject, std::vector<std::string>& boneNames)
|
||||
{
|
||||
AMD::int32 numOfBones = 0;
|
||||
EI_Seek(ioObject, 0);
|
||||
EI_Read((void*)&numOfBones, sizeof(AMD::int32), ioObject);
|
||||
[[maybe_unused]] auto eiSeekResult = EI_Seek(ioObject, 0);
|
||||
[[maybe_unused]] auto eiReadResult = EI_Read((void*)&numOfBones, sizeof(AMD::int32), ioObject);
|
||||
|
||||
// boneNames.reserve(numOfBones);
|
||||
boneNames.resize(numOfBones);
|
||||
for (int i = 0; i < numOfBones; i++)
|
||||
{
|
||||
int boneIndex;
|
||||
EI_Read((char*)&boneIndex, sizeof(AMD::int32), ioObject);
|
||||
eiReadResult = EI_Read((char*)&boneIndex, sizeof(AMD::int32), ioObject);
|
||||
|
||||
AMD::int32 charLen = 0;
|
||||
EI_Read((char*)&charLen, sizeof(AMD::int32), ioObject); // character length includes null termination already.
|
||||
eiReadResult = EI_Read((char*)&charLen, sizeof(AMD::int32), ioObject); // character length includes null termination already.
|
||||
|
||||
char boneName[128];
|
||||
EI_Read(boneName, sizeof(char) * charLen, ioObject);
|
||||
eiReadResult = EI_Read(boneName, sizeof(char) * charLen, ioObject);
|
||||
boneName[charLen] = '\0'; // adding 0 termination to be on the safe side.
|
||||
boneNames[i] = std::string(boneName);
|
||||
}
|
||||
@@ -730,8 +730,8 @@ namespace AMD
|
||||
m_boneSkinningData.resize(0);
|
||||
|
||||
AMD::int32 numOfBones = 0;
|
||||
EI_Seek(ioObject, 0);
|
||||
EI_Read((void*)&numOfBones, sizeof(AMD::int32), ioObject);
|
||||
[[maybe_unused]] auto eiSeekResult = EI_Seek(ioObject, 0);
|
||||
[[maybe_unused]] auto eiReadResult = EI_Read((void*)&numOfBones, sizeof(AMD::int32), ioObject);
|
||||
|
||||
if (skeletonBoneIndices.size() != numOfBones)
|
||||
{
|
||||
@@ -742,18 +742,18 @@ namespace AMD
|
||||
for (int i = 0; i < numOfBones; i++)
|
||||
{
|
||||
int boneIndex;
|
||||
EI_Read((char*)&boneIndex, sizeof(AMD::int32), ioObject);
|
||||
eiReadResult = EI_Read((char*)&boneIndex, sizeof(AMD::int32), ioObject);
|
||||
|
||||
AMD::int32 charLen = 0;
|
||||
EI_Read((char*)&charLen, sizeof(AMD::int32), ioObject); // character length includes null termination already.
|
||||
eiReadResult = EI_Read((char*)&charLen, sizeof(AMD::int32), ioObject); // character length includes null termination already.
|
||||
|
||||
char boneName[128];
|
||||
EI_Read(boneName, sizeof(char) * charLen, ioObject);
|
||||
eiReadResult = EI_Read(boneName, sizeof(char) * charLen, ioObject);
|
||||
}
|
||||
|
||||
// Reading the number of strands
|
||||
AMD::int32 numOfStrandsInStream = 0;
|
||||
EI_Read((char*)&numOfStrandsInStream, sizeof(AMD::int32), ioObject);
|
||||
eiReadResult = EI_Read((char*)&numOfStrandsInStream, sizeof(AMD::int32), ioObject);
|
||||
|
||||
//If the number of strands from the input stream (tfxbone) is bigger than what we already know from tfx, something is wrong.
|
||||
if (m_numGuideStrands < numOfStrandsInStream)
|
||||
@@ -765,15 +765,15 @@ namespace AMD
|
||||
for (int i = 0; i < numOfStrandsInStream; ++i)
|
||||
{
|
||||
AMD::int32 index = 0; // Well, we don't really use this here.
|
||||
EI_Read((char*)&index, sizeof(AMD::int32), ioObject);
|
||||
eiReadResult = EI_Read((char*)&index, sizeof(AMD::int32), ioObject);
|
||||
|
||||
for (AMD::int32 j = 0; j < TRESSFX_MAX_INFLUENTIAL_BONE_COUNT; ++j)
|
||||
{
|
||||
AMD::int32 boneIndex;
|
||||
EI_Read((char*)&boneIndex, sizeof(AMD::int32), ioObject);
|
||||
eiReadResult = EI_Read((char*)&boneIndex, sizeof(AMD::int32), ioObject);
|
||||
assert(boneIndex >= 0);
|
||||
skinData.boneIndex[j] = (float)skeletonBoneIndices[boneIndex]; // Change the joint index to be what the engine wants
|
||||
EI_Read((char*)&skinData.weight[j], sizeof(AMD::real32), ioObject);
|
||||
eiReadResult = EI_Read((char*)&skinData.weight[j], sizeof(AMD::real32), ioObject);
|
||||
}
|
||||
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
@@ -987,4 +987,3 @@ namespace AMD
|
||||
return true;
|
||||
}
|
||||
} // namespace AMD
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ namespace GradientSignal
|
||||
const float value = m_gradientSampler.GetValue(sampleParams);
|
||||
if (value >= m_configuration.m_thresholdMin && value <= m_configuration.m_thresholdMax)
|
||||
{
|
||||
weights.AddSurfaceWeightsIfGreater(m_configuration.m_modifierTags, value);
|
||||
weights.AddSurfaceTagWeights(m_configuration.m_modifierTags, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+397
-1141
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f974fa29f3542311ee6a4b6fbb92047186c3124b3bd6f88b728646bf9d686c45
|
||||
size 39099
|
||||
@@ -99,57 +99,49 @@ endif()
|
||||
################################################################################
|
||||
# See if globally, tests are supported
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
# We globally support tests, see if we support tests on this platform for MotionMatching.Static
|
||||
if(PAL_TRAIT_MOTIONMATCHING_TEST_SUPPORTED)
|
||||
# We support MotionMatching.Tests on this platform, add MotionMatching.Tests target which depends on MotionMatching.Static
|
||||
ly_add_target(
|
||||
NAME MotionMatching.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
motionmatching_files.cmake
|
||||
motionmatching_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::AzFramework
|
||||
Gem::EMotionFX.Tests.Static
|
||||
Gem::MotionMatching.Static
|
||||
)
|
||||
ly_add_target(
|
||||
NAME MotionMatching.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
motionmatching_files.cmake
|
||||
motionmatching_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::AzFramework
|
||||
Gem::EMotionFX.Tests.Static
|
||||
Gem::MotionMatching.Static
|
||||
)
|
||||
|
||||
# Add MotionMatching.Tests to googletest
|
||||
ly_add_googletest(
|
||||
NAME Gem::MotionMatching.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
# If we are a host platform we want to add tools test like editor tests here
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
# We are a host platform, see if Editor tests are supported on this platform
|
||||
if(PAL_TRAIT_MOTIONMATCHING_EDITOR_TEST_SUPPORTED)
|
||||
# We support MotionMatching.Editor.Tests on this platform, add MotionMatching.Editor.Tests target which depends on MotionMatching.Editor
|
||||
ly_add_target(
|
||||
NAME MotionMatching.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
motionmatching_editor_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Gem::MotionMatching.Editor
|
||||
)
|
||||
|
||||
# Add MotionMatching.Editor.Tests to googletest
|
||||
ly_add_googletest(
|
||||
NAME Gem::MotionMatching.Editor.Tests
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
# Add MotionMatching.Tests to googletest
|
||||
ly_add_googletest(
|
||||
NAME Gem::MotionMatching.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
# If we are a host platform we want to add tools test like editor tests here
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
NAME MotionMatching.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
motionmatching_editor_tests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Tests
|
||||
Source
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Gem::MotionMatching.Editor
|
||||
)
|
||||
|
||||
# Add MotionMatching.Editor.Tests to googletest
|
||||
ly_add_googletest(
|
||||
NAME Gem::MotionMatching.Editor.Tests
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -11,8 +11,20 @@
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
|
||||
namespace EMotionFX::MotionMatching
|
||||
{
|
||||
class DebugDrawRequests
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(DebugDrawRequests, "{7BBA4249-EC00-445C-8A0C-4472841049C3}");
|
||||
|
||||
virtual void DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay) = 0;
|
||||
};
|
||||
using DebugDrawRequestBus = AZ::EBus<DebugDrawRequests>;
|
||||
|
||||
class MotionMatchingRequests
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -297,8 +297,6 @@ namespace EMotionFX::MotionMatching
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::PushPerformanceHistogramValue, "Output", m_outputTimeInMs);
|
||||
#endif
|
||||
}
|
||||
|
||||
instance->DebugDraw();
|
||||
}
|
||||
|
||||
AZ::Crc32 BlendTreeMotionMatchNode::GetTrajectoryPathSettingsVisibility() const
|
||||
|
||||
@@ -16,10 +16,11 @@ namespace EMotionFX::MotionMatching
|
||||
|
||||
ImGuiMonitor::ImGuiMonitor()
|
||||
{
|
||||
m_performanceStats.m_name = "Performance Statistics";
|
||||
m_performanceStats.SetName("Performance Statistics");
|
||||
m_performanceStats.SetHistogramBinCount(500);
|
||||
|
||||
m_featureCosts.m_name = "Feature Costs";
|
||||
m_featureCosts.m_histogramContainerCount = 100;
|
||||
m_featureCosts.SetName("Feature Costs");
|
||||
m_featureCosts.SetHistogramBinCount(100);
|
||||
|
||||
ImGui::ImGuiUpdateListenerBus::Handler::BusConnect();
|
||||
ImGuiMonitorRequestBus::Handler::BusConnect();
|
||||
@@ -40,18 +41,40 @@ namespace EMotionFX::MotionMatching
|
||||
|
||||
if (ImGui::Begin("Motion Matching"))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("Motion Database", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
|
||||
{
|
||||
if (ImGui::BeginTable("MDB", 2))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Text("Memory Usage: %.2f MB", m_frameDatabaseInfo.m_memoryUsedInBytes / 1024.0f / 1024.0f);
|
||||
ImGui::TableNextColumn(); ImGui::Text("Motion Data: %.0f minutes", m_frameDatabaseInfo.m_motionDataInSeconds / 60.0f);
|
||||
ImGui::TableNextColumn(); ImGui::Text("Num Frames: %zu", m_frameDatabaseInfo.m_numFrames);
|
||||
ImGui::TableNextColumn(); ImGui::Text("Num Motions: %zu", m_frameDatabaseInfo.m_numMotions);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Feature Matrix", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
|
||||
{
|
||||
ImGui::Text("Memory Usage: %.2f MB", m_featureMatrixMemoryUsageInBytes / 1024.0f / 1024.0f);
|
||||
ImGui::Text("Num Frames: %zu", m_featureMatrixNumFrames);
|
||||
ImGui::Text("Num Feature Components: %zu", m_featureMatrixNumComponents);
|
||||
if (ImGui::BeginTable("FM", 2))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Text("Memory Usage: %.2f MB", m_featurMatrixInfo.m_memoryUsedInBytes / 1024.0f / 1024.0f);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn(); ImGui::Text("Num Frames: %zu", m_featurMatrixInfo.m_numFrames);
|
||||
ImGui::TableNextColumn(); ImGui::Text("Num Feature Components: %zu", m_featurMatrixInfo.m_numDimensions);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Kd-Tree", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
|
||||
{
|
||||
ImGui::Text("Memory Usage: %.2f MB", m_kdTreeMemoryUsageInBytes / 1024.0f / 1024.0f);
|
||||
ImGui::Text("Num Nodes: %zu", m_kdTreeNumNodes);
|
||||
ImGui::Text("Num Dimensions: %zu", m_kdTreeNumDimensions);
|
||||
if (ImGui::BeginTable("KDT", 2))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Text("Memory Usage: %.2f MB", m_kdTreeInfo.m_memoryUsedInBytes / 1024.0f / 1024.0f);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn(); ImGui::Text("Num Nodes: %zu", m_kdTreeInfo.m_numNodes);
|
||||
ImGui::TableNextColumn(); ImGui::Text("Num Dimensions: %zu", m_kdTreeInfo.m_numDimensions);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
m_performanceStats.OnImGuiUpdate();
|
||||
@@ -63,8 +86,8 @@ namespace EMotionFX::MotionMatching
|
||||
{
|
||||
if (ImGui::BeginMenu("Motion Matching"))
|
||||
{
|
||||
ImGui::MenuItem(m_performanceStats.m_name.c_str(), "", &m_performanceStats.m_show);
|
||||
ImGui::MenuItem(m_featureCosts.m_name.c_str(), "", &m_featureCosts.m_show);
|
||||
ImGui::MenuItem(m_performanceStats.GetName(), "", &m_performanceStats.m_show);
|
||||
ImGui::MenuItem(m_featureCosts.GetName(), "", &m_featureCosts.m_show);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
@@ -78,67 +101,6 @@ namespace EMotionFX::MotionMatching
|
||||
{
|
||||
m_featureCosts.PushHistogramValue(costName, value, color);
|
||||
}
|
||||
|
||||
void ImGuiMonitor::HistogramGroup::PushHistogramValue(const char* valueName, float value, const AZ::Color& color)
|
||||
{
|
||||
auto iterator = m_histogramIndexByName.find(valueName);
|
||||
if (iterator != m_histogramIndexByName.end())
|
||||
{
|
||||
ImGui::LYImGuiUtils::HistogramContainer& histogramContiner = m_histograms[iterator->second];
|
||||
histogramContiner.PushValue(value);
|
||||
histogramContiner.SetBarLineColor(ImColor(color.GetR(), color.GetG(), color.GetB(), color.GetA()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::LYImGuiUtils::HistogramContainer newHistogram;
|
||||
newHistogram.Init(/*histogramName=*/valueName,
|
||||
/*containerCount=*/m_histogramContainerCount,
|
||||
/*viewType=*/ImGui::LYImGuiUtils::HistogramContainer::ViewType::Histogram,
|
||||
/*displayOverlays=*/true,
|
||||
/*min=*/0.0f,
|
||||
/*max=*/0.0f);
|
||||
|
||||
newHistogram.SetMoveDirection(ImGui::LYImGuiUtils::HistogramContainer::PushRightMoveLeft);
|
||||
newHistogram.PushValue(value);
|
||||
|
||||
m_histogramIndexByName[valueName] = m_histograms.size();
|
||||
m_histograms.push_back(newHistogram);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiMonitor::HistogramGroup::OnImGuiUpdate()
|
||||
{
|
||||
if (!m_show)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader(m_name.c_str(), ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
|
||||
{
|
||||
for (auto& histogram : m_histograms)
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
histogram.Draw(ImGui::GetColumnWidth() - 70, s_histogramHeight);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0,0,0,255));
|
||||
{
|
||||
const ImColor color = histogram.GetBarLineColor();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color.Value);
|
||||
{
|
||||
const AZStd::string valueString = AZStd::string::format("%.2f", histogram.GetLastValue());
|
||||
ImGui::Button(valueString.c_str());
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace EMotionFX::MotionMatching
|
||||
|
||||
#endif // IMGUI_ENABLED
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <imgui/imgui.h>
|
||||
#include <ImGuiBus.h>
|
||||
#include <ImGuiMonitorBus.h>
|
||||
#include <LYImGuiUtils/HistogramContainer.h>
|
||||
#include <LYImGuiUtils/HistogramGroup.h>
|
||||
|
||||
namespace EMotionFX::MotionMatching
|
||||
{
|
||||
@@ -43,41 +43,17 @@ namespace EMotionFX::MotionMatching
|
||||
void PushPerformanceHistogramValue(const char* performanceMetricName, float value) override;
|
||||
void PushCostHistogramValue(const char* costName, float value, const AZ::Color& color) override;
|
||||
|
||||
void SetFeatureMatrixMemoryUsage(size_t sizeInBytes) override { m_featureMatrixMemoryUsageInBytes = sizeInBytes; }
|
||||
void SetFeatureMatrixNumFrames(size_t numFrames) override { m_featureMatrixNumFrames = numFrames; }
|
||||
void SetFeatureMatrixNumComponents(size_t numFeatureComponents) override { m_featureMatrixNumComponents = numFeatureComponents; }
|
||||
|
||||
void SetKdTreeMemoryUsage(size_t sizeInBytes) override { m_kdTreeMemoryUsageInBytes = sizeInBytes; }
|
||||
void SetKdTreeNumNodes(size_t numNodes) override { m_kdTreeNumNodes = numNodes; }
|
||||
void SetKdTreeNumDimensions(size_t numDimensions) override { m_kdTreeNumDimensions = numDimensions; }
|
||||
void SetFrameDatabaseInfo(const ImGuiMonitorRequests::FrameDatabaseInfo& info) override { m_frameDatabaseInfo = info; }
|
||||
void SetFeatureMatrixInfo(const ImGuiMonitorRequests::FeatureMatrixInfo& info) override { m_featurMatrixInfo = info; }
|
||||
void SetKdTreeInfo(const ImGuiMonitorRequests::KdTreeInfo& info) override { m_kdTreeInfo = info; }
|
||||
|
||||
private:
|
||||
//! Named and sub-divided group containing several histograms.
|
||||
struct HistogramGroup
|
||||
{
|
||||
void OnImGuiUpdate();
|
||||
void PushHistogramValue(const char* valueName, float value, const AZ::Color& color);
|
||||
ImGui::LYImGuiUtils::HistogramGroup m_performanceStats;
|
||||
ImGui::LYImGuiUtils::HistogramGroup m_featureCosts;
|
||||
|
||||
bool m_show = true;
|
||||
AZStd::string m_name;
|
||||
using HistogramIndexByNames = AZStd::unordered_map<const char*, size_t>;
|
||||
HistogramIndexByNames m_histogramIndexByName;
|
||||
AZStd::vector<ImGui::LYImGuiUtils::HistogramContainer> m_histograms;
|
||||
int m_histogramContainerCount = 500;
|
||||
|
||||
static constexpr float s_histogramHeight = 95.0f;
|
||||
};
|
||||
|
||||
HistogramGroup m_performanceStats;
|
||||
HistogramGroup m_featureCosts;
|
||||
|
||||
size_t m_featureMatrixMemoryUsageInBytes = 0;
|
||||
size_t m_featureMatrixNumFrames = 0;
|
||||
size_t m_featureMatrixNumComponents = 0;
|
||||
|
||||
size_t m_kdTreeMemoryUsageInBytes = 0;
|
||||
size_t m_kdTreeNumNodes = 0;
|
||||
size_t m_kdTreeNumDimensions = 0;
|
||||
ImGuiMonitorRequests::FrameDatabaseInfo m_frameDatabaseInfo;
|
||||
ImGuiMonitorRequests::FeatureMatrixInfo m_featurMatrixInfo;
|
||||
ImGuiMonitorRequests::KdTreeInfo m_kdTreeInfo;
|
||||
};
|
||||
} // namespace EMotionFX::MotionMatching
|
||||
|
||||
|
||||
@@ -25,13 +25,30 @@ namespace EMotionFX::MotionMatching
|
||||
virtual void PushPerformanceHistogramValue(const char* performanceMetricName, float value) = 0;
|
||||
virtual void PushCostHistogramValue(const char* costName, float value, const AZ::Color& color) = 0;
|
||||
|
||||
virtual void SetFeatureMatrixMemoryUsage(size_t sizeInBytes) = 0;
|
||||
virtual void SetFeatureMatrixNumFrames(size_t numFrames) = 0;
|
||||
virtual void SetFeatureMatrixNumComponents(size_t numFeatureComponents) = 0;
|
||||
struct FrameDatabaseInfo
|
||||
{
|
||||
size_t m_memoryUsedInBytes = 0;
|
||||
size_t m_numFrames;
|
||||
size_t m_numMotions;
|
||||
float m_motionDataInSeconds;
|
||||
};
|
||||
virtual void SetFrameDatabaseInfo(const FrameDatabaseInfo& info) = 0;
|
||||
|
||||
virtual void SetKdTreeMemoryUsage(size_t sizeInBytes) = 0;
|
||||
virtual void SetKdTreeNumNodes(size_t numNodes) = 0;
|
||||
virtual void SetKdTreeNumDimensions(size_t numDimensions) = 0;
|
||||
struct FeatureMatrixInfo
|
||||
{
|
||||
size_t m_memoryUsedInBytes = 0;
|
||||
size_t m_numFrames = 0;
|
||||
size_t m_numDimensions = 0;
|
||||
};
|
||||
virtual void SetFeatureMatrixInfo(const FeatureMatrixInfo& info) = 0;
|
||||
|
||||
struct KdTreeInfo
|
||||
{
|
||||
size_t m_memoryUsedInBytes = 0;
|
||||
size_t m_numNodes = 0;
|
||||
size_t m_numDimensions = 0;
|
||||
};
|
||||
virtual void SetKdTreeInfo(const KdTreeInfo& info) = 0;
|
||||
};
|
||||
using ImGuiMonitorRequestBus = AZ::EBus<ImGuiMonitorRequests>;
|
||||
} // namespace EMotionFX::MotionMatching
|
||||
|
||||
@@ -28,14 +28,14 @@
|
||||
#include <EMotionFX/Source/TransformData.h>
|
||||
#include <PoseDataJointVelocities.h>
|
||||
|
||||
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h>
|
||||
|
||||
namespace EMotionFX::MotionMatching
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(MotionMatchingInstance, MotionMatchAllocator, 0)
|
||||
|
||||
MotionMatchingInstance::~MotionMatchingInstance()
|
||||
{
|
||||
DebugDrawRequestBus::Handler::BusDisconnect();
|
||||
|
||||
if (m_motionInstance)
|
||||
{
|
||||
GetMotionInstancePool().Free(m_motionInstance);
|
||||
@@ -58,6 +58,8 @@ namespace EMotionFX::MotionMatching
|
||||
AZ_Assert(settings.m_actorInstance, "The actor instance cannot be a nullptr.");
|
||||
AZ_Assert(settings.m_data, "The motion match data cannot be nullptr.");
|
||||
|
||||
DebugDrawRequestBus::Handler::BusConnect();
|
||||
|
||||
// Update the cached pointer to the trajectory feature.
|
||||
const FeatureSchema& featureSchema = settings.m_data->GetFeatureSchema();
|
||||
for (Feature* feature : featureSchema.GetFeatures())
|
||||
@@ -69,29 +71,6 @@ namespace EMotionFX::MotionMatching
|
||||
}
|
||||
}
|
||||
|
||||
// Debug display initialization.
|
||||
const auto AddDebugDisplay = [=](AZ::s32 debugDisplayId)
|
||||
{
|
||||
if (debugDisplayId == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
|
||||
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, debugDisplayId);
|
||||
|
||||
AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
|
||||
if (debugDisplay)
|
||||
{
|
||||
m_debugDisplays.emplace_back(debugDisplay);
|
||||
}
|
||||
};
|
||||
// Draw the debug visualizations to the Animation Editor as well as the LY Editor viewport.
|
||||
AZ::s32 animationEditorViewportId = -1;
|
||||
EMStudio::ViewportPluginRequestBus::BroadcastResult(animationEditorViewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId);
|
||||
AddDebugDisplay(animationEditorViewportId);
|
||||
AddDebugDisplay(AzFramework::g_defaultSceneEntityDebugDisplayId);
|
||||
|
||||
m_actorInstance = settings.m_actorInstance;
|
||||
m_data = settings.m_data;
|
||||
if (settings.m_data->GetFrameDatabase().GetNumFrames() == 0)
|
||||
@@ -123,30 +102,17 @@ namespace EMotionFX::MotionMatching
|
||||
m_queryFeatureValues.resize(numValuesInKdTree);
|
||||
|
||||
// Initialize the trajectory history.
|
||||
size_t rootJointIndex = m_actorInstance->GetActor()->GetMotionExtractionNodeIndex();
|
||||
if (rootJointIndex == InvalidIndex32)
|
||||
if (m_cachedTrajectoryFeature)
|
||||
{
|
||||
rootJointIndex = 0;
|
||||
}
|
||||
m_trajectoryHistory.Init(*m_actorInstance->GetTransformData()->GetCurrentPose(),
|
||||
rootJointIndex,
|
||||
m_cachedTrajectoryFeature->GetFacingAxisDir(),
|
||||
m_trajectorySecsToTrack);
|
||||
}
|
||||
|
||||
void MotionMatchingInstance::DebugDraw()
|
||||
{
|
||||
if (m_data && !m_debugDisplays.empty())
|
||||
{
|
||||
for (AzFramework::DebugDisplayRequests* debugDisplay : m_debugDisplays)
|
||||
size_t rootJointIndex = m_actorInstance->GetActor()->GetMotionExtractionNodeIndex();
|
||||
if (rootJointIndex == InvalidIndex32)
|
||||
{
|
||||
if (debugDisplay)
|
||||
{
|
||||
const AZ::u32 prevState = debugDisplay->GetState();
|
||||
DebugDraw(*debugDisplay);
|
||||
debugDisplay->SetState(prevState);
|
||||
}
|
||||
rootJointIndex = 0;
|
||||
}
|
||||
m_trajectoryHistory.Init(*m_actorInstance->GetTransformData()->GetCurrentPose(),
|
||||
rootJointIndex,
|
||||
m_cachedTrajectoryFeature->GetFacingAxisDir(),
|
||||
m_trajectorySecsToTrack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +272,7 @@ namespace EMotionFX::MotionMatching
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "MotionMatchingInstance::Update");
|
||||
|
||||
if (!m_data)
|
||||
if (!m_data || !m_motionInstance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -322,7 +288,7 @@ namespace EMotionFX::MotionMatching
|
||||
// Update the time. After this there is no sample for the updated time in the history as we're about to prepare this with the current update.
|
||||
m_trajectoryHistory.Update(timePassedInSeconds);
|
||||
|
||||
// Register the current actor instance position to the history data of the spline.
|
||||
// Update the trajectory query control points.
|
||||
m_trajectoryQuery.Update(m_actorInstance,
|
||||
m_cachedTrajectoryFeature,
|
||||
m_trajectoryHistory,
|
||||
@@ -371,8 +337,7 @@ namespace EMotionFX::MotionMatching
|
||||
SamplePose(m_motionInstance->GetMotion(), m_queryPose, newMotionTime);
|
||||
|
||||
// Copy over the motion extraction joint transform from the current pose to the newly sampled pose.
|
||||
// When sampling a motion, the motion extraction joint is in animation space, while we need the query pose to be in
|
||||
// world space.
|
||||
// When sampling a motion, the motion extraction joint is in animation space, while we need the query pose to be in world space.
|
||||
// Note: This does not yet take the extraction delta from the current tick into account.
|
||||
if (m_actorInstance->GetActor()->GetMotionExtractionNode())
|
||||
{
|
||||
@@ -433,16 +398,17 @@ namespace EMotionFX::MotionMatching
|
||||
// ImGui monitor
|
||||
{
|
||||
#ifdef IMGUI_ENABLED
|
||||
const KdTree& kdTree = m_data->GetKdTree();
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetKdTreeMemoryUsage, kdTree.CalcMemoryUsageInBytes());
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetKdTreeNumNodes, kdTree.GetNumNodes());
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetKdTreeNumDimensions, kdTree.GetNumDimensions());
|
||||
// TODO: add memory usage for frame database
|
||||
const FrameDatabase& frameDatabase = m_data->GetFrameDatabase();
|
||||
ImGuiMonitorRequests::FrameDatabaseInfo frameDatabaseInfo{frameDatabase.CalcMemoryUsageInBytes(), frameDatabase.GetNumFrames(), frameDatabase.GetNumUsedMotions(), frameDatabase.GetNumFrames() / (float)frameDatabase.GetSampleRate()};
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFrameDatabaseInfo, frameDatabaseInfo);
|
||||
|
||||
const KdTree& kdTree = m_data->GetKdTree();
|
||||
ImGuiMonitorRequests::KdTreeInfo kdTreeInfo{kdTree.CalcMemoryUsageInBytes(), kdTree.GetNumNodes(), kdTree.GetNumDimensions()};
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetKdTreeInfo, kdTreeInfo);
|
||||
|
||||
const FeatureMatrix& featureMatrix = m_data->GetFeatureMatrix();
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFeatureMatrixMemoryUsage, featureMatrix.CalcMemoryUsageInBytes());
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFeatureMatrixNumFrames, featureMatrix.rows());
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFeatureMatrixNumComponents, featureMatrix.cols());
|
||||
ImGuiMonitorRequests::FeatureMatrixInfo featureMatrixInfo{featureMatrix.CalcMemoryUsageInBytes(), static_cast<size_t>(featureMatrix.rows()), static_cast<size_t>(featureMatrix.cols())};
|
||||
ImGuiMonitorRequestBus::Broadcast(&ImGuiMonitorRequests::SetFeatureMatrixInfo, featureMatrixInfo);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <TrajectoryHistory.h>
|
||||
#include <TrajectoryQuery.h>
|
||||
|
||||
#include <MotionMatching/MotionMatchingBus.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
@@ -34,12 +36,17 @@ namespace EMotionFX::MotionMatching
|
||||
{
|
||||
class MotionMatchingData;
|
||||
|
||||
//! The instance is where everything comes together. It stores the trajectory history, the trajectory query along with the query vector, knows about the
|
||||
//! last lowest cost frame frame index and stores the time of the animation that the instance is currently playing. It is responsible for motion extraction,
|
||||
//! blending towards a new frame in the motion capture database in case the algorithm found a better matching frame and executes the actual search.
|
||||
class EMFX_API MotionMatchingInstance
|
||||
: public DebugDrawRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(MotionMatchingInstance, "{1ED03AD8-0FB2-431B-AF01-02F7E930EB73}")
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
|
||||
MotionMatchingInstance() = default;
|
||||
virtual ~MotionMatchingInstance();
|
||||
|
||||
struct EMFX_API InitSettings
|
||||
@@ -49,8 +56,8 @@ namespace EMotionFX::MotionMatching
|
||||
};
|
||||
void Init(const InitSettings& settings);
|
||||
|
||||
void DebugDraw();
|
||||
void DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay);
|
||||
// DebugDrawRequestBus::Handler overrides
|
||||
void DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
|
||||
void Update(float timePassedInSeconds, const AZ::Vector3& targetPos, const AZ::Vector3& targetFacingDir, TrajectoryQuery::EMode mode, float pathRadius, float pathSpeed);
|
||||
void PostUpdate(float timeDelta);
|
||||
@@ -64,10 +71,8 @@ namespace EMotionFX::MotionMatching
|
||||
void SetLowestCostSearchFrequency(float frequency) { m_lowestCostSearchFrequency = frequency; }
|
||||
float GetNewMotionTime() const { return m_newMotionTime; }
|
||||
|
||||
/**
|
||||
* Get the cached trajectory feature.
|
||||
* The trajectory feature is searched in the feature schema used in the current instance at init time.
|
||||
*/
|
||||
//! Get the cached trajectory feature.
|
||||
//! The trajectory feature is searched in the feature schema used in the current instance at init time.
|
||||
FeatureTrajectory* GetTrajectoryFeature() const { return m_cachedTrajectoryFeature; }
|
||||
const TrajectoryQuery& GetTrajectoryQuery() const { return m_trajectoryQuery; }
|
||||
const TrajectoryHistory& GetTrajectoryHistory() const { return m_trajectoryHistory; }
|
||||
@@ -90,10 +95,10 @@ namespace EMotionFX::MotionMatching
|
||||
Transform m_motionExtractionDelta = Transform::CreateIdentity();
|
||||
|
||||
/// Buffers used for the broad-phase KD-tree search.
|
||||
AZStd::vector<float> m_queryFeatureValues; /** The input query features to be compared to every entry/row in the feature matrix with the motion matching search. */
|
||||
AZStd::vector<size_t> m_nearestFrames; /** Stores the nearest matching frames / search result from the KD-tree. */
|
||||
AZStd::vector<float> m_queryFeatureValues; //< The input query features to be compared to every entry/row in the feature matrix with the motion matching search.
|
||||
AZStd::vector<size_t> m_nearestFrames; //< Stores the nearest matching frames / search result from the KD-tree.
|
||||
|
||||
FeatureTrajectory* m_cachedTrajectoryFeature = nullptr; /** Cached pointer to the trajectory feature in the feature schema. */
|
||||
FeatureTrajectory* m_cachedTrajectoryFeature = nullptr; //< Cached pointer to the trajectory feature in the feature schema.
|
||||
TrajectoryQuery m_trajectoryQuery;
|
||||
TrajectoryHistory m_trajectoryHistory;
|
||||
static constexpr float m_trajectorySecsToTrack = 5.0f;
|
||||
@@ -105,7 +110,7 @@ namespace EMotionFX::MotionMatching
|
||||
|
||||
bool m_blending = false;
|
||||
float m_blendWeight = 1.0f;
|
||||
float m_blendProgressTime = 0.0f; // How long are we already blending? In seconds.
|
||||
float m_blendProgressTime = 0.0f; //< How long are we already blending? In seconds.
|
||||
|
||||
/// Buffers used for FindLowestCostFrameIndex().
|
||||
AZStd::vector<float> m_tempCosts;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include <Integration/EMotionFXBus.h>
|
||||
|
||||
#include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/ViewportPluginBus.h>
|
||||
|
||||
#include <BlendTreeMotionMatchNode.h>
|
||||
#include <Feature.h>
|
||||
#include <FeaturePosition.h>
|
||||
@@ -39,7 +41,7 @@ namespace EMotionFX::MotionMatching
|
||||
{
|
||||
ec->Class<MotionMatchingSystemComponent>("MotionMatching", "[Description of functionality provided by this System Component]")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System"))
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
;
|
||||
}
|
||||
@@ -69,9 +71,9 @@ namespace EMotionFX::MotionMatching
|
||||
incompatible.push_back(AZ_CRC_CE("MotionMatchingService"));
|
||||
}
|
||||
|
||||
void MotionMatchingSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
void MotionMatchingSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC("EMotionFXAnimationService", 0x3f8a6369));
|
||||
required.push_back(AZ_CRC_CE("EMotionFXAnimationService"));
|
||||
}
|
||||
|
||||
void MotionMatchingSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
@@ -122,7 +124,33 @@ namespace EMotionFX::MotionMatching
|
||||
MotionMatchingRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void MotionMatchingSystemComponent::DebugDraw(AZ::s32 debugDisplayId)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Animation, "MotionMatchingSystemComponent::DebugDraw");
|
||||
|
||||
if (debugDisplayId == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
|
||||
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, debugDisplayId);
|
||||
|
||||
AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
|
||||
if (debugDisplay)
|
||||
{
|
||||
const AZ::u32 prevState = debugDisplay->GetState();
|
||||
EMotionFX::MotionMatching::DebugDrawRequestBus::Broadcast(&EMotionFX::MotionMatching::DebugDrawRequests::DebugDraw, *debugDisplay);
|
||||
debugDisplay->SetState(prevState);
|
||||
}
|
||||
}
|
||||
|
||||
void MotionMatchingSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
// Draw the debug visualizations to the Animation Editor as well as the LY Editor viewport.
|
||||
AZ::s32 animationEditorViewportId = -1;
|
||||
EMStudio::ViewportPluginRequestBus::BroadcastResult(animationEditorViewportId, &EMStudio::ViewportPluginRequestBus::Events::GetViewportId);
|
||||
DebugDraw(animationEditorViewportId);
|
||||
DebugDraw(AzFramework::g_defaultSceneEntityDebugDisplayId);
|
||||
}
|
||||
} // namespace EMotionFX::MotionMatching
|
||||
|
||||
@@ -33,8 +33,7 @@ namespace EMotionFX::MotionMatching
|
||||
~MotionMatchingSystemComponent();
|
||||
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// MotionMatchingRequestBus interface implementation
|
||||
void DebugDraw(AZ::s32 debugDisplayId);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component interface implementation
|
||||
@@ -45,6 +44,10 @@ namespace EMotionFX::MotionMatching
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// AZTickBus interface implementation
|
||||
int GetTickOrder() override
|
||||
{
|
||||
return AZ::TICK_PRE_RENDER;
|
||||
}
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
};
|
||||
|
||||
@@ -24,9 +24,17 @@ namespace SurfaceData
|
||||
using SurfaceTagVector = AZStd::vector<SurfaceTag>;
|
||||
|
||||
//! SurfaceTagWeights stores a collection of surface tags and weights.
|
||||
//! A surface tag can only appear once in the collection. Attempting to add it multiple times will always preserve the
|
||||
//! highest weight value.
|
||||
class SurfaceTagWeights
|
||||
{
|
||||
public:
|
||||
//! The maximum number of surface weights that we can store.
|
||||
//! For performance reasons, we want to limit this so that we can preallocate the max size in advance.
|
||||
//! The current number is chosen to be higher than expected needs, but small enough to avoid being excessively wasteful.
|
||||
//! (Dynamic structures would end up taking more memory than what we're preallocating)
|
||||
static inline constexpr size_t MaxSurfaceWeights = 16;
|
||||
|
||||
SurfaceTagWeights() = default;
|
||||
|
||||
//! Construct a collection of SurfaceTagWeights from the given SurfaceTagWeightList.
|
||||
@@ -45,42 +53,68 @@ namespace SurfaceData
|
||||
//! @param weight - The weight to assign to each tag.
|
||||
void AssignSurfaceTagWeights(const SurfaceTagVector& tags, float weight);
|
||||
|
||||
//! Add a surface tag weight to this collection.
|
||||
//! @param tag - The surface tag.
|
||||
//! @param weight - The surface tag weight.
|
||||
void AddSurfaceTagWeight(const AZ::Crc32 tag, const float weight);
|
||||
|
||||
//! Replace the surface tag weight with the new one if it's higher, or add it if the tag isn't found.
|
||||
//! Add a surface tag weight to this collection. If the tag already exists, the higher weight will be preserved.
|
||||
//! (This method is intentionally inlined for its performance impact)
|
||||
//! @param tag - The surface tag.
|
||||
//! @param weight - The surface tag weight.
|
||||
void AddSurfaceWeightIfGreater(const AZ::Crc32 tag, const float weight)
|
||||
void AddSurfaceTagWeight(const AZ::Crc32 tag, const float weight)
|
||||
{
|
||||
const auto maskItr = m_weights.find(tag);
|
||||
const float previousValue = maskItr != m_weights.end() ? maskItr->second : 0.0f;
|
||||
m_weights[tag] = AZ::GetMax(weight, previousValue);
|
||||
}
|
||||
|
||||
//! Replace the surface tag weight with the new one if it's higher, or add it if the tag isn't found.
|
||||
//! (This method is intentionally inlined for its performance impact)
|
||||
//! @param tags - The surface tags to replace/add.
|
||||
//! @param weight - The surface tag weight to use for each tag.
|
||||
void AddSurfaceWeightsIfGreater(const SurfaceTagVector& tags, const float weight)
|
||||
{
|
||||
for (const auto& tag : tags)
|
||||
for (auto weightItr = m_weights.begin(); weightItr != m_weights.end(); ++weightItr)
|
||||
{
|
||||
AddSurfaceWeightIfGreater(tag, weight);
|
||||
// Since we need to scan for duplicate surface types, store the entries sorted by surface type so that we can
|
||||
// early-out once we pass the location for the entry instead of always searching every entry.
|
||||
if (weightItr->m_surfaceType > tag)
|
||||
{
|
||||
if (m_weights.size() != MaxSurfaceWeights)
|
||||
{
|
||||
// We didn't find the surface type, so add the new entry in sorted order.
|
||||
m_weights.insert(weightItr, { tag, weight });
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "SurfaceTagWeights has reached max capacity, it cannot add a new tag / weight.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (weightItr->m_surfaceType == tag)
|
||||
{
|
||||
// We found the surface type, so just keep the higher of the two weights.
|
||||
weightItr->m_weight = AZ::GetMax(weight, weightItr->m_weight);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find the surface weight, and the sort order for it is at the end, so add it to the back of the list.
|
||||
if (m_weights.size() != MaxSurfaceWeights)
|
||||
{
|
||||
m_weights.emplace_back(tag, weight);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Assert(false, "SurfaceTagWeights has reached max capacity, it cannot add a new tag / weight.");
|
||||
}
|
||||
}
|
||||
|
||||
//! Replace the surface tag weight with the new one if it's higher, or add it if the tag isn't found.
|
||||
//! Add surface tags and weights to this collection. If a tag already exists, the higher weight will be preserved.
|
||||
//! (This method is intentionally inlined for its performance impact)
|
||||
//! @param tags - The surface tags to replace/add.
|
||||
//! @param weight - The surface tag weight to use for each tag.
|
||||
void AddSurfaceTagWeights(const SurfaceTagVector& tags, const float weight)
|
||||
{
|
||||
for (const auto& tag : tags)
|
||||
{
|
||||
AddSurfaceTagWeight(tag, weight);
|
||||
}
|
||||
}
|
||||
|
||||
//! Add surface tags and weights to this collection. If a tag already exists, the higher weight will be preserved.
|
||||
//! (This method is intentionally inlined for its performance impact)
|
||||
//! @param weights - The surface tags and weights to replace/add.
|
||||
void AddSurfaceWeightsIfGreater(const SurfaceTagWeights& weights)
|
||||
void AddSurfaceTagWeights(const SurfaceTagWeights& weights)
|
||||
{
|
||||
for (const auto& [tag, weight] : weights.m_weights)
|
||||
{
|
||||
AddSurfaceWeightIfGreater(tag, weight);
|
||||
AddSurfaceTagWeight(tag, weight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +159,7 @@ namespace SurfaceData
|
||||
//! Check to see if the collection contains the given tag.
|
||||
//! @param sampleTag - The tag to look for.
|
||||
//! @return True if the tag is found, false if it isn't.
|
||||
bool HasMatchingTag(const AZ::Crc32& sampleTag) const;
|
||||
bool HasMatchingTag(AZ::Crc32 sampleTag) const;
|
||||
|
||||
//! Check to see if the collection contains the given tag with the given weight range.
|
||||
//! The range check is inclusive on both sides of the range: [weightMin, weightMax]
|
||||
@@ -133,7 +167,7 @@ namespace SurfaceData
|
||||
//! @param weightMin - The minimum weight for this tag.
|
||||
//! @param weightMax - The maximum weight for this tag.
|
||||
//! @return True if the tag is found, false if it isn't.
|
||||
bool HasMatchingTag(const AZ::Crc32& sampleTag, float weightMin, float weightMax) const;
|
||||
bool HasMatchingTag(AZ::Crc32 sampleTag, float weightMin, float weightMax) const;
|
||||
|
||||
//! Check to see if the collection contains any of the given tags.
|
||||
//! @param sampleTags - The tags to look for.
|
||||
@@ -149,7 +183,12 @@ namespace SurfaceData
|
||||
bool HasAnyMatchingTags(const SurfaceTagVector& sampleTags, float weightMin, float weightMax) const;
|
||||
|
||||
private:
|
||||
AZStd::unordered_map<AZ::Crc32, float> m_weights;
|
||||
//! Search for the given tag entry.
|
||||
//! @param tag - The tag to search for.
|
||||
//! @return The pointer to the tag that's found, or end() if it wasn't found.
|
||||
const AzFramework::SurfaceData::SurfaceTagWeight* FindTag(AZ::Crc32 tag) const;
|
||||
|
||||
AZStd::fixed_vector<AzFramework::SurfaceData::SurfaceTagWeight, MaxSurfaceWeights> m_weights;
|
||||
};
|
||||
|
||||
//! SurfacePointList stores a collection of surface point data, which consists of positions, normals, and surface tag weights.
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace SurfaceData
|
||||
if (DoRayTrace(position, queryPointOnly, hitPosition, hitNormal))
|
||||
{
|
||||
// If the query point collides with the volume, add all our modifier tags with a weight of 1.0f.
|
||||
weights.AddSurfaceWeightsIfGreater(m_configuration.m_modifierTags, 1.0f);
|
||||
weights.AddSurfaceTagWeights(m_configuration.m_modifierTags, 1.0f);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace SurfaceData
|
||||
if (m_shapeBounds.Contains(position) && shape->IsPointInside(position))
|
||||
{
|
||||
// If the point is inside our shape, add all our modifier tags with a weight of 1.0f.
|
||||
weights.AddSurfaceWeightsIfGreater(m_configuration.m_modifierTags, 1.0f);
|
||||
weights.AddSurfaceTagWeights(m_configuration.m_modifierTags, 1.0f);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,28 +14,21 @@ namespace SurfaceData
|
||||
void SurfaceTagWeights::AssignSurfaceTagWeights(const AzFramework::SurfaceData::SurfaceTagWeightList& weights)
|
||||
{
|
||||
m_weights.clear();
|
||||
m_weights.reserve(weights.size());
|
||||
for (auto& weight : weights)
|
||||
{
|
||||
m_weights.emplace(weight.m_surfaceType, weight.m_weight);
|
||||
AddSurfaceTagWeight(weight.m_surfaceType, weight.m_weight);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceTagWeights::AssignSurfaceTagWeights(const SurfaceTagVector& tags, float weight)
|
||||
{
|
||||
m_weights.clear();
|
||||
m_weights.reserve(tags.size());
|
||||
for (auto& tag : tags)
|
||||
{
|
||||
m_weights[tag] = weight;
|
||||
AddSurfaceTagWeight(tag.operator AZ::Crc32(), weight);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceTagWeights::AddSurfaceTagWeight(const AZ::Crc32 tag, const float value)
|
||||
{
|
||||
m_weights[tag] = value;
|
||||
}
|
||||
|
||||
void SurfaceTagWeights::Clear()
|
||||
{
|
||||
m_weights.clear();
|
||||
@@ -52,7 +45,7 @@ namespace SurfaceData
|
||||
weights.reserve(m_weights.size());
|
||||
for (auto& weight : m_weights)
|
||||
{
|
||||
weights.emplace_back(weight.first, weight.second);
|
||||
weights.emplace_back(weight);
|
||||
}
|
||||
return weights;
|
||||
}
|
||||
@@ -65,17 +58,8 @@ namespace SurfaceData
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& weight : m_weights)
|
||||
{
|
||||
auto rhsWeight = rhs.m_weights.find(weight.first);
|
||||
if ((rhsWeight == rhs.m_weights.end()) || (rhsWeight->second != weight.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All the entries matched, and the lists are the same size, so they're equal.
|
||||
return true;
|
||||
// The lists are stored in sorted order, so we can compare every entry in order for equivalence.
|
||||
return (m_weights == rhs.m_weights);
|
||||
}
|
||||
|
||||
bool SurfaceTagWeights::SurfaceWeightsAreEqual(const AzFramework::SurfaceData::SurfaceTagWeightList& compareWeights) const
|
||||
@@ -92,7 +76,7 @@ namespace SurfaceData
|
||||
compareWeights.begin(), compareWeights.end(),
|
||||
[weight](const AzFramework::SurfaceData::SurfaceTagWeight& compareWeight) -> bool
|
||||
{
|
||||
return (weight.first == compareWeight.m_surfaceType) && (weight.second == compareWeight.m_weight);
|
||||
return (weight == compareWeight);
|
||||
});
|
||||
|
||||
// If we didn't find a match, they're not equal.
|
||||
@@ -119,9 +103,9 @@ namespace SurfaceData
|
||||
|
||||
bool SurfaceTagWeights::HasValidTags() const
|
||||
{
|
||||
for (const auto& sourceTag : m_weights)
|
||||
for (const auto& weight : m_weights)
|
||||
{
|
||||
if (sourceTag.first != Constants::s_unassignedTagCrc)
|
||||
if (weight.m_surfaceType != Constants::s_unassignedTagCrc)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -129,9 +113,9 @@ namespace SurfaceData
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SurfaceTagWeights::HasMatchingTag(const AZ::Crc32& sampleTag) const
|
||||
bool SurfaceTagWeights::HasMatchingTag(AZ::Crc32 sampleTag) const
|
||||
{
|
||||
return m_weights.find(sampleTag) != m_weights.end();
|
||||
return FindTag(sampleTag) != m_weights.end();
|
||||
}
|
||||
|
||||
bool SurfaceTagWeights::HasAnyMatchingTags(const SurfaceTagVector& sampleTags) const
|
||||
@@ -147,10 +131,10 @@ namespace SurfaceData
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SurfaceTagWeights::HasMatchingTag(const AZ::Crc32& sampleTag, float weightMin, float weightMax) const
|
||||
bool SurfaceTagWeights::HasMatchingTag(AZ::Crc32 sampleTag, float weightMin, float weightMax) const
|
||||
{
|
||||
auto maskItr = m_weights.find(sampleTag);
|
||||
return maskItr != m_weights.end() && weightMin <= maskItr->second && weightMax >= maskItr->second;
|
||||
auto weightEntry = FindTag(sampleTag);
|
||||
return weightEntry != m_weights.end() && weightMin <= weightEntry->m_weight && weightMax >= weightEntry->m_weight;
|
||||
}
|
||||
|
||||
bool SurfaceTagWeights::HasAnyMatchingTags(const SurfaceTagVector& sampleTags, float weightMin, float weightMax) const
|
||||
@@ -166,7 +150,25 @@ namespace SurfaceData
|
||||
return false;
|
||||
}
|
||||
|
||||
const AzFramework::SurfaceData::SurfaceTagWeight* SurfaceTagWeights::FindTag(AZ::Crc32 tag) const
|
||||
{
|
||||
for (auto weightItr = m_weights.begin(); weightItr != m_weights.end(); ++weightItr)
|
||||
{
|
||||
if (weightItr->m_surfaceType == tag)
|
||||
{
|
||||
// Found the tag, return a pointer to the entry.
|
||||
return weightItr;
|
||||
}
|
||||
else if (weightItr->m_surfaceType > tag)
|
||||
{
|
||||
// Our list is stored in sorted order by surfaceType, so early-out if our values get too high.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The tag wasn't found, so return end().
|
||||
return m_weights.end();
|
||||
}
|
||||
|
||||
|
||||
SurfacePointList::SurfacePointList(AZStd::initializer_list<const AzFramework::SurfaceData::SurfacePoint> surfacePoints)
|
||||
@@ -192,7 +194,7 @@ namespace SurfaceData
|
||||
if (m_surfacePositionList[index].IsClose(position) && m_surfaceNormalList[index].IsClose(normal))
|
||||
{
|
||||
// consolidate points with similar attributes by adding masks/weights to the similar point instead of adding a new one.
|
||||
m_surfaceWeightsList[index].AddSurfaceWeightsIfGreater(masks);
|
||||
m_surfaceWeightsList[index].AddSurfaceTagWeights(masks);
|
||||
return;
|
||||
}
|
||||
else if (m_surfacePositionList[index].GetZ() < position.GetZ())
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Memory/PoolAllocator.h>
|
||||
#include <AzCore/Math/Random.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
#include <AzCore/std/containers/span.h>
|
||||
@@ -270,6 +271,88 @@ namespace UnitTest
|
||||
->Arg( 2048 )
|
||||
->Unit(::benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(SurfaceDataBenchmark, BM_AddSurfaceTagWeight)(benchmark::State& state)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
|
||||
AZ::Crc32 tags[SurfaceData::SurfaceTagWeights::MaxSurfaceWeights];
|
||||
AZ::SimpleLcgRandom randomGenerator(1234567);
|
||||
|
||||
// Declare this outside the loop so that we aren't benchmarking creation and destruction.
|
||||
SurfaceData::SurfaceTagWeights weights;
|
||||
|
||||
bool clearEachTime = state.range(0) > 0;
|
||||
|
||||
// Create a list of randomly-generated tag values.
|
||||
for (auto& tag : tags)
|
||||
{
|
||||
tag = randomGenerator.GetRandom();
|
||||
}
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
// We'll benchmark this two ways:
|
||||
// 1. We clear each time, which means each AddSurfaceWeightIfGreater call will search the whole list then add.
|
||||
// 2. We don't clear, which means that after the first run, AddSurfaceWeightIfGreater will always try to replace values.
|
||||
if (clearEachTime)
|
||||
{
|
||||
weights.Clear();
|
||||
}
|
||||
|
||||
// For each tag, try to add it with a random weight.
|
||||
for (auto& tag : tags)
|
||||
{
|
||||
weights.AddSurfaceTagWeight(tag, randomGenerator.GetRandomFloat());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SurfaceDataBenchmark, BM_AddSurfaceTagWeight)
|
||||
->Arg(false)
|
||||
->Arg(true)
|
||||
->ArgName("ClearEachTime");
|
||||
|
||||
BENCHMARK_DEFINE_F(SurfaceDataBenchmark, BM_HasAnyMatchingTags_NoMatches)(benchmark::State& state)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Entity);
|
||||
|
||||
AZ::Crc32 tags[SurfaceData::SurfaceTagWeights::MaxSurfaceWeights];
|
||||
AZ::SimpleLcgRandom randomGenerator(1234567);
|
||||
|
||||
// Declare this outside the loop so that we aren't benchmarking creation and destruction.
|
||||
SurfaceData::SurfaceTagWeights weights;
|
||||
|
||||
// Create a list of randomly-generated tag values.
|
||||
for (auto& tag : tags)
|
||||
{
|
||||
// Specifically always set the last bit so that we can create comparison tags that won't match.
|
||||
tag = randomGenerator.GetRandom() | 0x01;
|
||||
|
||||
// Add the tag to our weights list with a random weight.
|
||||
weights.AddSurfaceTagWeight(tag, randomGenerator.GetRandomFloat());
|
||||
}
|
||||
|
||||
// Create a set of similar comparison tags that won't match. We still want a random distribution of values though,
|
||||
// because the SurfaceTagWeights might behave differently with ordered lists.
|
||||
SurfaceData::SurfaceTagVector comparisonTags;
|
||||
for (auto& tag : tags)
|
||||
{
|
||||
comparisonTags.emplace_back(tag ^ 0x01);
|
||||
}
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
// Test to see if any of our tags match.
|
||||
// All of comparison tags should get compared against all of the added tags.
|
||||
bool result = weights.HasAnyMatchingTags(comparisonTags);
|
||||
benchmark::DoNotOptimize(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(SurfaceDataBenchmark, BM_HasAnyMatchingTags_NoMatches);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ class MockSurfaceProvider
|
||||
|
||||
if (surfacePoints != m_GetSurfacePoints.end())
|
||||
{
|
||||
weights.AddSurfaceWeightsIfGreater(m_tags, 1.0f);
|
||||
weights.AddSurfaceTagWeights(m_tags, 1.0f);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1137,7 +1137,7 @@ namespace Vegetation
|
||||
claimPoint.m_position = position;
|
||||
claimPoint.m_normal = normal;
|
||||
claimPoint.m_masks = masks;
|
||||
sectorInfo.m_baseContext.m_masks.AddSurfaceWeightsIfGreater(masks);
|
||||
sectorInfo.m_baseContext.m_masks.AddSurfaceTagWeights(masks);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user