merge from development

Signed-off-by: greerdv <greerdv@amazon.com>
This commit is contained in:
greerdv
2021-10-18 11:25:57 +01:00
236 changed files with 5045 additions and 3845 deletions
@@ -10,7 +10,6 @@
#include <scenesrg.srgi>
#include <viewsrg.srgi>
#include "JitterTablePcf.azsli"
#include "Shadow.azsli"
#include "ShadowmapAtlasLib.azsli"
#include "BicubicPcfFilters.azsli"
@@ -82,12 +81,6 @@ class DirectionalLightShadow
// result.y == true if the given coordinate is in shadow.
bool2 IsShadowed(float3 shadowCoord, uint indexOfCascade);
// This checks if the point is shadowed or not for the given center coordinate and jitter.
bool IsShadowedWithJitter(
float3 jitterUnit,
float jitterDepthDiffBase,
uint jitterIndex);
// This outputs visibility ratio (from 0.0 to 1.0) of the given coordinate
// from the light origin without filtering.
float GetVisibilityFromLightNoFilter();
@@ -189,75 +182,6 @@ bool2 DirectionalLightShadow::IsShadowed(float3 shadowCoord, uint indexOfCascade
return bool2(false, false);
}
bool DirectionalLightShadow::IsShadowedWithJitter(
float3 jitterUnit,
float jitterDepthDiffBase,
uint jitterIndex)
{
const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount;
const float4x4 worldToLightViewMatrices[ViewSrg::MaxCascadeCount] =
ViewSrg::m_directionalLightShadows[m_lightIndex].m_worldToLightViewMatrices;
const float4x4 lightViewToShadowmapMatrices[ViewSrg::MaxCascadeCount] =
ViewSrg::m_directionalLightShadows[m_lightIndex].m_lightViewToShadowmapMatrices;
const float boundaryScale =
ViewSrg::m_directionalLightShadows[m_lightIndex].m_boundaryScale;
const float2 jitterXY = g_jitterTablePcf[jitterIndex];
// jitterLightView is the jittering diff vector from the lighted point on the surface
// in the light view space. It is remarked as "v_J" in the comment
// named "Calculate depth adjusting diff for jittered samples"
// just before the function GetJitterUnitVectorDepthDiffBase.
const float4 jitterLightView = float4(jitterXY, 0., 0.) * boundaryScale;
// It checks the jittered point is lit or shadowed from the detailed cascade
// to the less detailed one.
for (uint indexOfCascade = 0; indexOfCascade < cascadeCount; ++indexOfCascade)
{
// jitterShadowmap is the jittering diff vector in the shadowmap space.
const float4 jitterShadowmap = mul(lightViewToShadowmapMatrices[indexOfCascade], jitterLightView);
// Calculation of the jittering for Z-coordinate (light direction) is required
// to check lit/shadowed for the jittered point.
// jitterDepthDiff is the Z-coordinate of the jittering diff vector
// in the shadowmap space.
float jitterDepthDiff = 0.;
// jitterDepthDiffBase is "1/tan(theta)" in the comment.
if (jitterDepthDiffBase != 0.)
{
// jitterUnitLightView is the unit vector in the light view space
// noted as "v_M" in the comment.
const float3 jitterUnitLightView =
normalize(mul(worldToLightViewMatrices[indexOfCascade], float4(jitterUnit, 0.)).xyz);
const float lightViewToShadowmapZScale = -lightViewToShadowmapMatrices[indexOfCascade]._m22;
// jitterDepthDiff is the "d" in the note, and it is calculated by
// d = (v_J . v_M) / tan(theta)
// in the light view space. Furthermore it have to be converted
// to the light clip space, which can be done by lightViewToShadowmapZScale.
jitterDepthDiff =
dot(jitterLightView.xyz, jitterUnitLightView) * jitterDepthDiffBase *
lightViewToShadowmapZScale;
}
// jitteredCoord is the coordinate of the jittered point in the shadowmap space.
const float3 jitteredCoord =
m_shadowCoords[indexOfCascade] + float3(jitterShadowmap.xy, jitterDepthDiff);
// Check for the jittered point is lit or shadowed.
const bool2 checkedShadowed = IsShadowed(
jitteredCoord,
indexOfCascade);
// If check is done, return the lit/shadowed flag.
// Otherwise make it pend to the next cascade.
if (checkedShadowed.x)
{
m_debugInfo.m_cascadeIndex = indexOfCascade;
return checkedShadowed.y;
}
}
m_debugInfo.m_cascadeIndex = cascadeCount;
return false;
}
float DirectionalLightShadow::GetVisibilityFromLightNoFilter()
{
const uint cascadeCount = ViewSrg::m_directionalLightShadows[m_lightIndex].m_cascadeCount;
@@ -1,185 +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
*
*/
/*
The following is the output of
$ python3 pcf_jitter_table.py 6 g_jitterTablePcf 0
where pcf_jitter_table.py has the following contents.
@code
#!/usr/bin/env python3
import random
import sys
import math
""" Returns if a point in the range
[radius_min, radius_sup)*[angle_min, angle_sup)
is contained in the tuple polar coordinates.
"""
def is_point_include(radius_min, radius_sup, angle_min, angle_sup, polars):
for polar in polars:
if (radius_min <= polar[0] and polar[0] < radius_sup and
angle_min <= polar[1] and polar[1] < angle_sup):
return True
return False
""" Insert a randomly generated polar coordianted point in each
range [r0, r1)*[a0, a1) if there has not been such a point
in tuple coords yet, where [0, 1)*[0, 2pi) is divided
into the rad_count*agl_count ranges.
"""
def add_jitter_coords(radius_count, angle_count, polars):
radius_base = 1.0 / math.sqrt(radius_count)
for radius_index in range(radius_count):
# range of radius
radius_min = math.sqrt(radius_index) * radius_base
radius_sup = math.sqrt(radius_index + 1) * radius_base
# randomize angle order
random_state = random.getstate()
angle_indices = list(range(angle_count))
random.shuffle(angle_indices)
random.setstate(random_state)
for angle_index in angle_indices:
# range of angle
angle_min = 2 * math.pi * angle_index / angle_count
angle_sup = 2 * math.pi * (angle_index + 1) / angle_count
# if no point in the radius/angle range, add a new point
if not is_point_include(radius_min, radius_sup,
angle_min, angle_sup,
polars):
radius = radius_min + (radius_sup - radius_min) * random.random()
angle = angle_min + (angle_sup - angle_min) * random.random()
polars += [[radius, angle]]
""" Return a formatted string readable as an array of
orthogonal coordinated points which are in inside of the unit disk.
"""
def conv_array_string(polars):
result = "{\n"
for [radius, angle] in polars:
x = radius * math.cos(angle)
y = radius * math.sin(angle)
result += str.format(" float2({: 1.20e}, {: 1.20e}),\n", x, y)
result = result.rstrip(",\n") + "\n};\n"
return result
if __name__ == "__main__":
rad_size = 1
ang_size = 1
if len(sys.argv) > 3:
random_seed = int(sys.argv[3])
else:
random_seed = 0
if len(sys.argv) > 2:
array_name = sys.argv[2]
else:
array_name = False
if len(sys.argv) > 1:
len_log = int(sys.argv[1])
else:
print(" usage: {} array_len_log2 [array_file_name] [random_seed]".format(__file__))
print(" array_len_log2 = 2 -> array length = 4")
print(" array_len_log2 = 6 -> array length = 64")
sys.exit()
random.seed(random_seed)
coords = []
add_jitter_coords(rad_size, ang_size, coords)
for index in range(len_log):
if index % 2 == 0:
rad_size *= 2
else:
ang_size *= 2
add_jitter_coords(rad_size, ang_size, coords)
if array_name:
print(str.format("static const float2 {}[{}] =", array_name, len(coords)))
print(conv_array_string(coords))
@endcode
*/
#pragma once
static const float2 g_jitterTablePcf[64] =
{
float2( 4.21857815578105532772e-02, -8.43367430701083664601e-01),
float2(-1.66526814909220763350e-02, 2.96922406531470617352e-01),
float2(-1.06374665780382349212e-01, -3.45521852905696924552e-01),
float2( 5.42648241814168375008e-01, 7.63475573328278533936e-01),
float2(-1.55045122122251910479e-01, 5.78282315712970729216e-01),
float2( 1.01310018770242576264e-02, -6.88001749851880561870e-01),
float2(-5.41276603451248283783e-01, 5.21888233660957712168e-01),
float2(-6.69885071867917680777e-01, -6.72019666097878665134e-01),
float2( 1.22985029409499718039e-02, 4.54706838949524849713e-01),
float2( 4.00334354168925599105e-01, -6.20112671104014120949e-02),
float2( 2.32326155804074424571e-01, 5.14183027524470093184e-01),
float2(-3.26788693165450228051e-01, -6.03339478694129849323e-01),
float2( 7.72374386126136736053e-01, 1.23204314299169448432e-01),
float2(-4.45379212004159807936e-01, -6.35591042627205338178e-01),
float2( 9.86986293787213919693e-01, -5.18195017297516449806e-02),
float2(-9.09197225477999193544e-01, 1.95281945570711268356e-01),
float2( 8.78123785413316704229e-02, -2.77671865082058690055e-02),
float2( 1.93947312440399088906e-01, 4.27852204081567363825e-03),
float2(-2.06133675819526185347e-01, -1.49183652412411493771e-01),
float2(-4.11351098583102647854e-01, 2.36214692717993696158e-01),
float2( 3.50058750095615767162e-01, -3.57193658067260721989e-01),
float2(-5.54174780014121681759e-01, -2.23361040823672196698e-01),
float2(-6.29913348094886860196e-01, 1.29962593232600148729e-01),
float2( 3.96119563669521335125e-01, 4.90495219155295036906e-01),
float2( 7.26077464944819728210e-01, -3.70531027878536270426e-02),
float2(-5.50726266551596621568e-01, 6.48997654184258587762e-01),
float2(-6.98067624269093189859e-01, -3.83843898992943299842e-01),
float2( 8.72900706885875177221e-02, 8.24287559846993866941e-01),
float2( 6.65413234189638491678e-01, -5.66029707430476647367e-01),
float2(-5.97071574457786802270e-01, -6.93417220711863180327e-01),
float2( 6.09778569514949131403e-01, 6.92279483269558570946e-01),
float2(-8.10051800827623957879e-01, 5.82366304247235455627e-01),
float2(-8.77200948157437071506e-02, -1.88326609190753474499e-01),
float2( 9.79306884403889771340e-02, 1.86693151785678163046e-01),
float2( 4.60071424048798319206e-02, -1.98255149016034859510e-01),
float2(-5.37585860722621794450e-02, 3.99205315590760584366e-02),
float2( 2.18621803321778829243e-01, -3.85632280444686503795e-01),
float2(-2.98409571230789372187e-02, 4.22286693608096730390e-01),
float2( 3.58654757584850270025e-01, 2.95175871390239985548e-01),
float2(-3.85631921979480485341e-01, -3.00322047091407640096e-01),
float2( 4.49800763439369810648e-01, 3.98492182500493397068e-01),
float2(-4.97878650048238891035e-01, 2.57984038389083569776e-01),
float2(-3.12055242602567339816e-01, -4.88013525550807125697e-01),
float2( 5.87078632117718268724e-01, -6.97256834327608099322e-02),
float2( 6.23692403999373534695e-01, 3.11519734097943645779e-01),
float2( 6.64426445690903810792e-01, -2.27661844509491811950e-01),
float2(-3.24662942872471160793e-01, 5.68939932480760024447e-01),
float2(-5.31263995010459511015e-01, -4.66108719959298256619e-01),
float2( 5.10323549430644951563e-01, 5.81027848262460677731e-01),
float2( 2.82695533021593392586e-01, -7.03582425015577883620e-01),
float2(-5.98419541732174709026e-01, -4.68015982003612274198e-01),
float2(-3.95281650646674975746e-01, 6.10614720709622194050e-01),
float2( 7.87454411900813555647e-01, 1.37726315874787758053e-01),
float2(-7.36310249594224086600e-01, 4.25723821775386646049e-01),
float2( 6.48232481978769037312e-01, -5.53108138515975955585e-01),
float2(-1.88558544306507869237e-01, -7.79120748356531223067e-01),
float2(-3.78614630625567993860e-01, 7.82366459873827913007e-01),
float2(-8.48582606942172357201e-01, -3.78504015913022351381e-01),
float2( 1.91472859899175090748e-02, -9.13050020447597532325e-01),
float2( 8.08826910050883585157e-01, 4.17202663034078935489e-01),
float2(-9.27062588380768493046e-01, -2.94160352051227980130e-01),
float2( 6.67882607007592055126e-01, -6.88642020601400450808e-01),
float2(-1.59349274307943010454e-02, 9.37629353656756814317e-01),
float2( 9.86975590293644233775e-01, 1.44401793964158337014e-01)
};
@@ -13,7 +13,6 @@
#include <Atom/Features/Shadow/ShadowmapAtlasLib.azsli>
#include <Atom/RPI/Math.azsli>
#include "BicubicPcfFilters.azsli"
#include "JitterTablePcf.azsli"
#include "Shadow.azsli"
// ProjectedShadow calculates shadowed area projected from a light.
@@ -44,11 +43,6 @@ class ProjectedShadow
float GetThickness();
bool IsShadowed(float3 shadowPosition);
bool IsShadowedWithJitter(
float3 jitterUnitX,
float3 jitterUnitY,
float jitterDepthDiffBase,
uint jitterIndex);
void SetShadowPosition();
float3 GetAtlasPosition(float2 texturePosition);
static float UnprojectDepth(uint shadowIndex, float depthBufferValue);
@@ -321,35 +315,6 @@ bool ProjectedShadow::IsShadowed(float3 shadowPosition)
return false;
}
bool ProjectedShadow::IsShadowedWithJitter(
float3 jitterUnitX,
float3 jitterUnitY,
float jitterDepthDiffBase,
uint jitterIndex)
{
ViewSrg::ProjectedShadow shadow = ViewSrg::m_projectedShadows[m_shadowIndex];
const float4x4 depthBiasMatrix = shadow.m_depthBiasMatrix;
const float boundaryScale = shadow.m_boundaryScale;
const float2 jitterXY = g_jitterTablePcf[jitterIndex];
const float dist = distance(m_worldPosition, m_viewPosition);
const float boundaryRadius = dist * tan(boundaryScale);
// jitterWorldXY is the jittering diff vector from the lighted point on the surface
// in the world space. It is remarked as "v_J" in the comment
// named "Calculate depth adjusting diff for jittered samples"
// just before the function GetJitterUnitVectorDepthDiffBase.
const float3 jitterWorldXY = jitterUnitX * (jitterXY.x * boundaryRadius) + jitterUnitY * (jitterXY.y * boundaryRadius);
// The adjusting diff of depth ("d" in the comment) is calculated by
// jitterXY.y * boundaryRadius * jitterDepthDiffBase.
const float3 jitterWorldZ = m_lightDirection * (jitterXY.y * boundaryRadius * jitterDepthDiffBase);
const float3 jitteredWorldPosition = m_worldPosition + jitterWorldXY + jitterWorldZ;
const float4 jitteredShadowmapHomogeneous = mul(depthBiasMatrix, float4(jitteredWorldPosition, 1));
return IsShadowed(jitteredShadowmapHomogeneous.xyz / jitteredShadowmapHomogeneous.w);
}
void ProjectedShadow::SetShadowPosition()
{
const float4x4 depthBiasMatrix = ViewSrg::m_projectedShadows[m_shadowIndex].m_depthBiasMatrix;
@@ -23,14 +23,11 @@ struct FilterParameter
uint m_isEnabled;
uint2 m_shadowmapOriginInSlice;
uint m_shadowmapSize;
uint m_parameterOffset;
uint m_parameterCount;
float m_lightDistanceOfCameraViewFrustum;
float m_n_f_n; // n / (f - n)
float m_n_f; // n - f
float m_f; // f
// where n: nearDepth, f: farDepth.
float2 m_padding; // explicit padding
};
class Shadow
@@ -22,14 +22,11 @@ partial ShaderResourceGroup ViewSrg
uint m_isEnabled;
uint2 m_shadowmapOriginInSlice;
uint m_shadowmapSize;
uint m_parameterOffset;
uint m_parameterCount;
float m_lightDistanceOfCameraViewFrustum;
float m_n_f_n; // n / (f - n)
float m_n_f; // n - f
float m_f; // f
// where n: nearDepth, f: farDepth.
float2 m_padding; // explicit padding
};
// Simple Point Lights
@@ -286,7 +286,6 @@ set(FILES
ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli
ShaderLib/Atom/Features/Shadow/BicubicPcfFilters.azsli
ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli
ShaderLib/Atom/Features/Shadow/JitterTablePcf.azsli
ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli
ShaderLib/Atom/Features/Shadow/Shadow.azsli
ShaderLib/Atom/Features/Shadow/ShadowmapAtlasLib.azsli
@@ -154,12 +154,6 @@ namespace AZ
//! @param count Sample Count for filtering (up to 64)
virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0;
//! This specifies the width of boundary between shadowed area and lit area.
//! @param handle the light handle.
//! @param width Boundary width. The shadow is gradually changed the degree of shadowed.
//! If width == 0, softening edge is disabled. Units are in meters.
virtual void SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth) = 0;
//! Sets whether the directional shadowmap should use receiver plane bias.
//! This attempts to reduce shadow acne when using large pcf filters.
virtual void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) = 0;
@@ -90,8 +90,6 @@ namespace AZ
virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0;
//! Specifies filter method of shadows.
virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0;
//! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on the boundary. 0 disables softening.
virtual void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) = 0;
//! Sets sample count for filtering of shadow boundary (up to 64)
virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0;
//! Sets the Esm exponent to use. Higher values produce a steeper falloff in the border areas between light and shadow.
@@ -70,9 +70,6 @@ namespace AZ
virtual void SetShadowBias(LightHandle handle, float bias) = 0;
//! Specifies filter method of shadows.
virtual void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) = 0;
//! Specifies the width of boundary between shadowed area and lit area in radians. The degree ofshadowed gradually changes on
//! the boundary. 0 disables softening.
virtual void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) = 0;
//! Sets sample count for filtering of shadow boundary (up to 64)
virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0;
//! Sets the Esm exponent to use. Higher values produce a steeper falloff in the border areas between light and shadow.
@@ -42,7 +42,6 @@ namespace AZ
// [GFX TODO][ATOM-2408] Make the max number of cascade modifiable at runtime.
static constexpr uint16_t MaxNumberOfCascades = 4;
static constexpr uint16_t MaxPcfSamplingCount = 64;
static constexpr float MaxSofteningBoundaryWidth = 0.1f;
} // namespace Shadow
} // namespace Render
@@ -54,8 +54,6 @@ namespace AZ::Render
virtual void SetShadowBias(ShadowId id, float bias) = 0;
//! Sets the shadow filter method
virtual void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) = 0;
//! Sets the width of boundary between shadowed area and lit area.
virtual void SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) = 0;
//! Sets the sample count for filtering of the shadow boundary, max 64.
virtual void SetFilteringSampleCount(ShadowId id, uint16_t count) = 0;
//! Sets all of the shadow properites in one call
@@ -584,15 +584,6 @@ namespace AZ
m_shadowBufferNeedsUpdate = true;
}
void DirectionalLightFeatureProcessor::SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth)
{
for (auto& it : m_shadowData)
{
it.second.GetData(handle.GetIndex()).m_boundaryScale = boundaryWidth / 2.f;
}
m_shadowBufferNeedsUpdate = true;
}
void DirectionalLightFeatureProcessor::SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable)
{
m_shadowProperties.GetData(handle.GetIndex()).m_isReceiverPlaneBiasEnabled = enable;
@@ -1116,50 +1107,13 @@ namespace AZ
for (const auto& passIt : m_esmShadowmapsPasses)
{
const RPI::View* cameraView = passIt.second.front()->GetRenderPipeline()->GetDefaultView().get();
UpdateStandardDeviations(handle, cameraView);
UpdateFilterOffsetsCounts(handle, cameraView);
UpdateFilterEnabled(handle, cameraView);
UpdateShadowmapPositionInAtlas(handle, cameraView);
SetFilterParameterToPass(handle, cameraView);
}
}
void DirectionalLightFeatureProcessor::UpdateStandardDeviations(LightHandle handle, const RPI::View* cameraView)
{
if (handle != m_shadowingLightHandle)
{
return;
}
const DirectionalLightShadowData& data = m_shadowData.at(cameraView).GetData(handle.GetIndex());
const ShadowProperty& property = m_shadowProperties.GetData(handle.GetIndex());
AZStd::fixed_vector<float, Shadow::MaxNumberOfCascades> standardDeviations;
for (size_t cascadeIndex = 0; cascadeIndex < property.m_segments.at(cameraView).size(); ++cascadeIndex)
{
const Aabb& aabb = property.m_segments.at(cameraView)[cascadeIndex].m_aabb;
const float aabbDiameter = AZStd::GetMax(
aabb.GetMax().GetX() - aabb.GetMin().GetX(),
aabb.GetMax().GetZ() - aabb.GetMin().GetZ());
float standardDeviation = 0.f;
if (aabbDiameter > 0.f)
{
const float boundaryWidth = data.m_boundaryScale * 2.f;
const float ratioToAabbWidth = boundaryWidth / aabbDiameter;
const float widthInPixels = ratioToAabbWidth * data.m_shadowmapSize;
standardDeviation = widthInPixels / (2 * GaussianMathFilter::ReliableSectionFactor);
}
standardDeviations.push_back(standardDeviation);
}
for (const RPI::RenderPipelineId& pipelineId : m_renderPipelineIdsForPersistentView.at(cameraView))
{
for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses.at(pipelineId))
{
esmPass->SetFilterParameters(standardDeviations);
}
}
}
void DirectionalLightFeatureProcessor::UpdateFilterOffsetsCounts(LightHandle handle, const RPI::View* cameraView)
void DirectionalLightFeatureProcessor::UpdateFilterEnabled(LightHandle handle, const RPI::View* cameraView)
{
if (handle != m_shadowingLightHandle)
{
@@ -1170,29 +1124,11 @@ namespace AZ
if (shadowData.m_shadowFilterMethod == aznumeric_cast<uint32_t>(ShadowFilterMethod::Esm) ||
(shadowData.m_shadowFilterMethod == aznumeric_cast<uint32_t>(ShadowFilterMethod::EsmPcf)))
{
// Get array of filter counts for the camera view.
const RPI::RenderPipelineId& pipelineId = m_renderPipelineIdsForPersistentView.at(cameraView).front();
AZ_Assert(!m_esmShadowmapsPasses.at(pipelineId).empty(), "Cannot find a EsmShadowmapsPass.");
const AZStd::array_view<uint32_t> filterCounts = m_esmShadowmapsPasses.at(pipelineId).front()->GetFilterCounts();
AZ_Assert(filterCounts.size() == GetCascadeCount(handle), "FilterCounts differs with cascade count.");
// Create array of filter offsets
AZStd::vector<uint32_t> filterOffsets;
filterOffsets.reserve(filterCounts.size());
uint32_t filterOffset = 0;
for (const uint32_t count : filterCounts)
{
filterOffsets.push_back(filterOffset);
filterOffset += count;
}
// Write filter offsets and filter counts to ESM data
for (uint16_t index = 0; index < GetCascadeCount(handle); ++index)
{
EsmShadowmapsPass::FilterParameter& filterParameter = m_esmParameterData.at(cameraView).GetData(index);
filterParameter.m_isEnabled = true;
filterParameter.m_parameterOffset = filterOffsets[index];
filterParameter.m_parameterCount = filterCounts[index];
}
}
else
@@ -1202,8 +1138,6 @@ namespace AZ
{
EsmShadowmapsPass::FilterParameter& filterParameter = m_esmParameterData.at(cameraView).GetData(index);
filterParameter.m_isEnabled = false;
filterParameter.m_parameterOffset = 0;
filterParameter.m_parameterCount = 0;
}
}
}
@@ -217,7 +217,6 @@ namespace AZ
void SetDebugFlags(LightHandle handle, DebugDrawFlags flags) override;
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
void SetFilteringSampleCount(LightHandle handle, uint16_t count) override;
void SetShadowBoundaryWidth(LightHandle handle, float boundaryWidth) override;
void SetShadowReceiverPlaneBiasEnabled(LightHandle handle, bool enable) override;
const Data::Instance<RPI::Buffer> GetLightBuffer() const;
@@ -278,10 +277,8 @@ namespace AZ
//! This updates the parameter of Gaussian filter used in ESM.
void UpdateFilterParameters(LightHandle handle);
//! This updates standard deviations for each cascade.
void UpdateStandardDeviations(LightHandle handle, const RPI::View* cameraView);
//! This updates filter offset and size for each cascade.
void UpdateFilterOffsetsCounts(LightHandle handle, const RPI::View* cameraView);
//! This updates if the filter is enabled.
void UpdateFilterEnabled(LightHandle handle, const RPI::View* cameraView);
//! This updates shadowmap position(origin and size) in the atlas for each cascade.
void UpdateShadowmapPositionInAtlas(LightHandle handle, const RPI::View* cameraView);
//! This set filter parameters to passes which execute filtering.
@@ -322,11 +322,6 @@ namespace AZ
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowFilterMethod, method);
}
void DiskLightFeatureProcessor::SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle, boundaryWidthRadians);
}
void DiskLightFeatureProcessor::SetFilteringSampleCount(LightHandle handle, uint16_t count)
{
@@ -53,7 +53,6 @@ namespace AZ
void SetShadowBias(LightHandle handle, float bias) override;
void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override;
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override;
void SetFilteringSampleCount(LightHandle handle, uint16_t count) override;
void SetEsmExponent(LightHandle handle, float esmExponent) override;
@@ -42,28 +42,6 @@ namespace AZ
return m_lightTypeName;
}
void EsmShadowmapsPass::SetFilterParameters(const AZStd::array_view<float>& standardDeviations)
{
// Set descriptor for Gaussian filters for given set of standard deviations.
MathFilterDescriptor descriptor;
descriptor.m_kind = MathFilterKind::Gaussian;
descriptor.m_gaussians.reserve(standardDeviations.size());
for (const float standardDeviation : standardDeviations)
{
descriptor.m_gaussians.emplace_back(GaussianFilterDescriptor{ standardDeviation });
}
// Set filter paramter buffer along with element counts for each filter.
MathFilter::BufferWithElementCounts bufferCounts = MathFilter::FindOrCreateFilterBuffer(descriptor);
m_filterTableBuffer = bufferCounts.first;
m_filterCounts = AZStd::move(bufferCounts.second);
}
AZStd::array_view<uint32_t> EsmShadowmapsPass::GetFilterCounts() const
{
return m_filterCounts;
}
void EsmShadowmapsPass::SetShadowmapIndexTableBuffer(const Data::Instance<RPI::Buffer>& tableBuffer)
{
m_shadowmapIndexTableBuffer = tableBuffer;
@@ -50,14 +50,11 @@ namespace AZ
uint32_t m_isEnabled = false;
AZStd::array<uint32_t, 2> m_shadowmapOriginInSlice = { {0, 0 } }; // shadowmap origin in the slice of the atlas.
uint32_t m_shadowmapSize = static_cast<uint32_t>(ShadowmapSize::None); // width and height of shadowmap.
uint32_t m_parameterOffset; // offset of the filter parameter.
uint32_t m_parameterCount; // element count of the filter parameter.
float m_lightDistanceOfCameraViewFrustum = 0.f;
float m_n_f_n = 0.f; // n / (f - n)
float m_n_f = 0.f; // n - f
float m_f = 0.f; // f
// where n: nearDepth, f: farDepth.
AZStd::array<float, 2> m_padding = {{0.f, 0.f}}; // explicit padding
};
virtual ~EsmShadowmapsPass() = default;
@@ -65,13 +62,6 @@ namespace AZ
const Name& GetLightTypeName() const;
//! This sets the standard deviations of the Gaussian filter
//! for each cascade.
void SetFilterParameters(const AZStd::array_view<float>& standardDeviations);
//! This returns element count of filters.
AZStd::array_view<uint32_t> GetFilterCounts() const;
//! This sets the buffer of the table which enable to get shadowmap index
//! from the coordinate in the atlas.
//! Note that shadowmpa index is shader light index for a spot light
@@ -292,11 +292,6 @@ namespace AZ
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowFilterMethod, method);
}
void PointLightFeatureProcessor::SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle, boundaryWidthRadians);
}
void PointLightFeatureProcessor::SetFilteringSampleCount(LightHandle handle, uint16_t count)
{
SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetFilteringSampleCount, count);
@@ -50,7 +50,6 @@ namespace AZ
void SetShadowBias(LightHandle handle, float bias) override;
void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override;
void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(LightHandle handle, float boundaryWidthRadians) override;
void SetFilteringSampleCount(LightHandle handle, uint16_t count) override;
void SetEsmExponent(LightHandle handle, float esmExponent) override;
void SetPointData(LightHandle handle, const PointLightData& data) override;
@@ -54,10 +54,14 @@ namespace AZ
{
AZStd::shared_ptr<AZStd::vector<uint8_t>> buffer = readbackResult.m_dataBuffer;
RHI::Format format = readbackResult.m_imageDescriptor.m_format;
// convert bgra to rgba by swapping channels
const int numChannels = AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format);
if (readbackResult.m_imageDescriptor.m_format == RHI::Format::B8G8R8A8_UNORM)
if (format == RHI::Format::B8G8R8A8_UNORM)
{
format = RHI::Format::R8G8B8A8_UNORM;
buffer = AZStd::make_shared<AZStd::vector<uint8_t>>(readbackResult.m_dataBuffer->size());
AZStd::copy(readbackResult.m_dataBuffer->begin(), readbackResult.m_dataBuffer->end(), buffer->begin());
@@ -89,7 +93,7 @@ namespace AZ
jobCompletion.StartAndWaitForCompletion();
}
Utils::PngFile image = Utils::PngFile::Create(readbackResult.m_imageDescriptor.m_size, readbackResult.m_imageDescriptor.m_format, *buffer);
Utils::PngFile image = Utils::PngFile::Create(readbackResult.m_imageDescriptor.m_size, format, *buffer);
Utils::PngFile::SaveSettings saveSettings;
saveSettings.m_compressionLevel = r_pngCompressionLevel;
@@ -507,7 +507,8 @@ namespace AZ
}
}
//Check if buffer view data changed from previous frame.
// Check if buffer view data changed from previous frame.
// Look into making 'm_meshBuffers != meshBuffers' faster by possibly building a crc and doing a crc check.
if (m_meshBuffers.size() != meshBuffers.size() || m_meshBuffers != meshBuffers)
{
m_meshBuffers = meshBuffers;
@@ -186,17 +186,6 @@ namespace AZ::Render
m_filterParameterNeedsUpdate = true;
}
void ProjectedShadowFeatureProcessor::SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians)
{
AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBoundaryWidthAngle().");
ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(id.GetIndex());
shadowData.m_boundaryScale = boundaryWidthRadians / 2.0f;
m_shadowmapPassNeedsUpdate = true;
m_filterParameterNeedsUpdate = true;
}
void ProjectedShadowFeatureProcessor::SetFilteringSampleCount(ShadowId id, uint16_t count)
{
AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFilteringSampleCount().");
@@ -368,14 +357,13 @@ namespace AZ::Render
{
if (m_filterParameterNeedsUpdate)
{
UpdateStandardDeviations();
UpdateFilterOffsetsCounts();
UpdateEsmPassEnabled();
SetFilterParameterToPass();
m_filterParameterNeedsUpdate = false;
}
}
void ProjectedShadowFeatureProcessor::UpdateStandardDeviations()
void ProjectedShadowFeatureProcessor::UpdateEsmPassEnabled()
{
if (m_esmShadowmapsPasses.empty())
{
@@ -383,24 +371,7 @@ namespace AZ::Render
return;
}
AZStd::vector<float> standardDeviations(m_shadowProperties.GetDataCount());
for (uint32_t i = 0; i < m_shadowProperties.GetDataCount(); ++i)
{
ShadowProperty& shadowProperty = m_shadowProperties.GetDataVector().at(i);
const ShadowData& shadow = m_shadowData.GetElement<ShadowDataIndex>(shadowProperty.m_shadowId.GetIndex());
if (!FilterMethodIsEsm(shadow))
{
continue;
}
const FilterParameter& filter = m_shadowData.GetElement<FilterParamIndex>(shadowProperty.m_shadowId.GetIndex());
const float boundaryWidthAngle = shadow.m_boundaryScale * 2.0f;
const float fieldOfView = GetMax(shadowProperty.m_desc.m_fieldOfViewYRadians, MinimumFieldOfView);
const float ratioToEntireWidth = boundaryWidthAngle / fieldOfView;
const float widthInPixels = ratioToEntireWidth * filter.m_shadowmapSize;
standardDeviations.at(i) = widthInPixels / (2.0f * GaussianMathFilter::ReliableSectionFactor);
}
if (standardDeviations.empty())
if (m_shadowProperties.GetDataCount() == 0)
{
for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses)
{
@@ -411,50 +382,6 @@ namespace AZ::Render
for (EsmShadowmapsPass* esmPass : m_esmShadowmapsPasses)
{
esmPass->SetEnabledComputation(true);
esmPass->SetFilterParameters(standardDeviations);
}
}
void ProjectedShadowFeatureProcessor::UpdateFilterOffsetsCounts()
{
if (m_esmShadowmapsPasses.empty())
{
AZ_Error("ProjectedShadowFeatureProcessor", false, "Cannot find a required pass.");
return;
}
// Get array of filter counts for the camera view.
const AZStd::array_view<uint32_t> filterCounts = m_esmShadowmapsPasses.front()->GetFilterCounts();
// Create array of filter offsets.
AZStd::vector<uint32_t> filterOffsets;
filterOffsets.reserve(filterCounts.size());
uint32_t filterOffset = 0;
for (const uint32_t count : filterCounts)
{
filterOffsets.push_back(filterOffset);
filterOffset += count;
}
auto& shadowProperties = m_shadowProperties.GetDataVector();
for (uint32_t i = 0; i < shadowProperties.size(); ++i)
{
ShadowProperty& shadowProperty = shadowProperties.at(i);
const ShadowId shadowId = shadowProperty.m_shadowId;
ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(shadowId.GetIndex());
FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowId.GetIndex());
if (FilterMethodIsEsm(shadowData))
{
filterData.m_parameterOffset = filterOffsets[i];
filterData.m_parameterCount = filterCounts[i];
}
else
{
// If filter is not required, reset offsets and counts of filter in ESM data.
filterData.m_parameterOffset = 0;
filterData.m_parameterCount = 0;
}
}
}
@@ -49,7 +49,6 @@ namespace AZ::Render
void SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size) override;
void SetShadowBias(ShadowId id, float bias) override;
void SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(ShadowId id, float boundaryWidthRadians) override;
void SetFilteringSampleCount(ShadowId id, uint16_t count) override;
void SetShadowProperties(ShadowId id, const ProjectedShadowDescriptor& descriptor) override;
const ProjectedShadowDescriptor& GetShadowProperties(ShadowId id) override;
@@ -101,8 +100,7 @@ namespace AZ::Render
//! Functions to update the parameter of Gaussian filter used in ESM.
void UpdateFilterParameters();
void UpdateStandardDeviations();
void UpdateFilterOffsetsCounts();
void UpdateEsmPassEnabled();
void SetFilterParameterToPass();
bool FilterMethodIsEsm(const ShadowData& shadowData) const;
+1 -3
View File
@@ -109,13 +109,11 @@ namespace AZ
{
if (attachment->GetFirstScopeAttachment() == nullptr)
{
//We allow the rendering to continue even if an attachment is not used.
AZ_Error(
"FrameGraph", false,
"Invalid State: attachment '%s' was added but never used!",
attachment->GetId().GetCStr());
Clear();
return ResultCode::InvalidOperation;
}
}
}
@@ -111,13 +111,19 @@ namespace AZ
{
AZStd::lock_guard<AZStd::shared_mutex> lock(m_groupsToCompileMutex);
AZ_Assert(!shaderResourceGroup.IsQueuedForCompile(), "Attempting to compile an SRG that's already been queued for compile. Only compile an SRG once per frame.");
bool isQueuedForCompile = shaderResourceGroup.IsQueuedForCompile();
AZ_Warning(
"ShaderResourceGroupPool", !isQueuedForCompile,
"Attempting to compile an SRG that's already been queued for compile. Only compile an SRG once per frame.");
CalculateGroupDataDiff(shaderResourceGroup, groupData);
if (!isQueuedForCompile)
{
CalculateGroupDataDiff(shaderResourceGroup, groupData);
shaderResourceGroup.SetData(groupData);
shaderResourceGroup.SetData(groupData);
QueueForCompileNoLock(shaderResourceGroup);
QueueForCompileNoLock(shaderResourceGroup);
}
}
void ShaderResourceGroupPool::QueueForCompile(ShaderResourceGroup& group)
@@ -55,7 +55,7 @@ namespace AZ
RHI::Ptr<BufferMemory> bufferMemory;
const VkMemoryPropertyFlags flags = ConvertHeapMemoryLevel(m_descriptor.m_heapMemoryLevel) | m_descriptor.m_additionalMemoryPropertyFlags;
RHI::Ptr<Memory> memory = GetDevice().AllocateMemory(memoryRequirements.size, memoryRequirements.memoryTypeBits, flags);
RHI::Ptr<Memory> memory = GetDevice().AllocateMemory(memoryRequirements.size, memoryRequirements.memoryTypeBits, flags, m_descriptor.m_bindFlags);
if (memory)
{
@@ -822,7 +822,7 @@ namespace AZ
{
RHI::ConstPtr<ShaderResourceGroup> shaderResourceGroup;
const auto& srgBitset = pipelineLayout.GetAZSLBindingSlotsOfIndex(index);
AZStd::vector<const ShaderResourceGroup*> shaderResourceGroupList;
AZStd::fixed_vector<const ShaderResourceGroup*, RHI::Limits::Pipeline::ShaderResourceGroupCountMax> shaderResourceGroupList;
// Collect all the SRGs that are part of this descriptor set. They could be more than
// 1, so we would need to merge their values before committing the descriptor set.
for (uint32_t bindingSlot = 0; bindingSlot < srgBitset.size(); ++bindingSlot)
@@ -696,8 +696,7 @@ namespace AZ
usageFlags |=
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR;
}
if (RHI::CheckBitsAny(bindFlags, BindFlags::Constant))
@@ -742,12 +741,24 @@ namespace AZ
if (RHI::CheckBitsAny(bindFlags, BindFlags::RayTracingShaderTable))
{
usageFlags |= VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
usageFlags |= VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR;
}
if (ShouldApplyDeviceAddressBit(bindFlags))
{
usageFlags |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
return usageFlags;
}
bool ShouldApplyDeviceAddressBit(RHI::BufferBindFlags bindFlags)
{
return RHI::CheckBitsAny(
bindFlags,
RHI::BufferBindFlags::InputAssembly | RHI::BufferBindFlags::DynamicInputAssembly | RHI::BufferBindFlags::RayTracingShaderTable);
}
VkPipelineStageFlags GetSupportedPipelineStages(RHI::PipelineStateType type)
{
// These stages don't need any special queue to be supported.
@@ -82,5 +82,6 @@ namespace AZ
VkImageUsageFlags ImageUsageFlagsOfFormatFeatureFlags(VkFormatFeatureFlags formatFeatureFlags);
VkAccessFlags GetSupportedAccessFlags(VkPipelineStageFlags pipelineStageFlags);
bool ShouldApplyDeviceAddressBit(RHI::BufferBindFlags bindFlags);
}
}
@@ -185,6 +185,9 @@ namespace AZ
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR float16Int8 = {};
VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR separateDepthStencil = {};
VkDeviceCreateInfo deviceInfo = {};
deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
// If we are running Vulkan >= 1.2, then we must use VkPhysicalDeviceVulkan12Features instead
// of VkPhysicalDeviceShaderFloat16Int8FeaturesKHR or VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR.
if (majorVersion >= 1 && minorVersion >= 2)
@@ -194,7 +197,14 @@ namespace AZ
vulkan12Features.shaderFloat16 = physicalDevice.GetPhysicalDeviceVulkan12Features().shaderFloat16;
vulkan12Features.shaderInt8 = physicalDevice.GetPhysicalDeviceVulkan12Features().shaderInt8;
vulkan12Features.separateDepthStencilLayouts = physicalDevice.GetPhysicalDeviceVulkan12Features().separateDepthStencilLayouts;
vulkan12Features.descriptorBindingPartiallyBound = physicalDevice.GetPhysicalDeviceVulkan12Features().separateDepthStencilLayouts;
vulkan12Features.descriptorIndexing = physicalDevice.GetPhysicalDeviceVulkan12Features().separateDepthStencilLayouts;
vulkan12Features.descriptorBindingVariableDescriptorCount = physicalDevice.GetPhysicalDeviceVulkan12Features().separateDepthStencilLayouts;
vulkan12Features.bufferDeviceAddress = physicalDevice.GetPhysicalDeviceVulkan12Features().bufferDeviceAddress;
vulkan12Features.bufferDeviceAddressMultiDevice = physicalDevice.GetPhysicalDeviceVulkan12Features().bufferDeviceAddressMultiDevice;
vulkan12Features.runtimeDescriptorArray = physicalDevice.GetPhysicalDeviceVulkan12Features().runtimeDescriptorArray;
robustness2.pNext = &vulkan12Features;
deviceInfo.pNext = &depthClipEnabled;
}
else
{
@@ -206,11 +216,11 @@ namespace AZ
float16Int8.pNext = &separateDepthStencil;
robustness2.pNext = &float16Int8;
deviceInfo.pNext = &descriptorIndexingFeatures;
}
VkDeviceCreateInfo deviceInfo = {};
deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceInfo.pNext = &descriptorIndexingFeatures;
deviceInfo.flags = 0;
deviceInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreationInfo.size());
deviceInfo.pQueueCreateInfos = queueCreationInfo.data();
@@ -740,7 +750,7 @@ namespace AZ
vkGetPhysicalDeviceQueueFamilyProperties(nativePhysicalDevice, &queueFamilyCount, m_queueFamilyProperties.data());
}
RHI::Ptr<Memory> Device::AllocateMemory(uint64_t sizeInBytes, const uint32_t memoryTypeMask, const VkMemoryPropertyFlags flags)
RHI::Ptr<Memory> Device::AllocateMemory(uint64_t sizeInBytes, const uint32_t memoryTypeMask, const VkMemoryPropertyFlags flags, const RHI::BufferBindFlags bufferBindFlags)
{
const auto& physicalDevice = static_cast<const PhysicalDevice&>(GetPhysicalDevice());
const VkPhysicalDeviceMemoryProperties& memProp = physicalDevice.GetMemoryProperties();
@@ -770,6 +780,7 @@ namespace AZ
RHI::CheckBitsAll(memoryTypesToUseMask, memoryTypeBit))
{
memoryDesc.m_memoryTypeIndex = memoryIndex;
memoryDesc.m_bufferBindFlags = bufferBindFlags;
auto result = memory->Init(*this, memoryDesc);
if (result == RHI::ResultCode::Success)
{
@@ -100,7 +100,11 @@ namespace AZ
RHI::Ptr<CommandList> AcquireCommandList(uint32_t familyQueueIndex, VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY);
RHI::Ptr<CommandList> AcquireCommandList(RHI::HardwareQueueClass queueClass, VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY);
RHI::Ptr<Memory> AllocateMemory(uint64_t sizeInBytes, const uint32_t memoryTypeMask, const VkMemoryPropertyFlags flags);
RHI::Ptr<Memory> AllocateMemory(
uint64_t sizeInBytes,
const uint32_t memoryTypeMask,
const VkMemoryPropertyFlags flags,
const RHI::BufferBindFlags bufferBindFlags = RHI::BufferBindFlags::None);
uint32_t GetCurrentFrameIndex() const;
@@ -59,7 +59,9 @@ namespace AZ
void FrameGraphExecuteGroupMerged::BeginInternal()
{
m_commandList = AcquireCommandList(VK_COMMAND_BUFFER_LEVEL_PRIMARY);
m_commandList->BeginCommandBuffer();
m_workRequest.m_commandList = m_commandList;
}
void FrameGraphExecuteGroupMerged::EndInternal()
@@ -41,9 +41,7 @@ namespace AZ
RETURN_RESULT_IF_UNSUCCESSFUL(result);
}
// Set the command list and renderpass contexts.
m_primaryCommandList = device.AcquireCommandList(m_hardwareQueueClass);
group->SetPrimaryCommandList(*m_primaryCommandList);
// Set the renderpass contexts.
group->SetRenderPasscontexts(m_renderPassContexts);
return RHI::ResultCode::Success;
@@ -54,7 +52,8 @@ namespace AZ
AZ_Assert(m_executeGroups.size() == 1, "Too many execute groups when initializing context");
FrameGraphExecuteGroupBase* group = static_cast<FrameGraphExecuteGroupBase*>(m_executeGroups.back());
AddWorkRequest(group->GetWorkRequest());
m_workRequest.m_commandList = m_primaryCommandList;
//Merged handler will only have one commandlist.
m_workRequest.m_commandList = group->GetCommandLists()[0];
}
}
}
@@ -31,7 +31,12 @@ namespace AZ
{
return static_cast<Device&>(Base::GetDevice());
}
FrameGraphExecuter::FrameGraphExecuter()
{
SetJobPolicy(RHI::JobPolicy::Parallel);
}
RHI::ResultCode FrameGraphExecuter::InitInternal(const RHI::FrameGraphExecuterDescriptor& descriptor)
{
const RHI::ConstPtr<RHI::PlatformLimitsDescriptor> rhiPlatformLimitsDescriptor = descriptor.m_platformLimitsDescriptor;
@@ -35,6 +35,8 @@ namespace AZ
Device& GetDevice() const;
private:
FrameGraphExecuter();
//////////////////////////////////////////////////////////////////////////
// RHI::FrameGraphExecuter
RHI::ResultCode InitInternal(const RHI::FrameGraphExecuterDescriptor& descriptor) override;
@@ -7,6 +7,7 @@
*/
#include <AzCore/std/parallel/lock.h>
#include <Atom/RHI.Reflect/Bits.h>
#include <Atom/RHI.Reflect/BufferDescriptor.h>
#include <RHI/Memory.h>
#include <RHI/Conversion.h>
#include <RHI/Device.h>
@@ -31,6 +32,15 @@ namespace AZ
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = descriptor.m_sizeInBytes;
allocInfo.memoryTypeIndex = descriptor.m_memoryTypeIndex;
VkMemoryAllocateFlagsInfo memAllocInfo{};
if (ShouldApplyDeviceAddressBit(descriptor.m_bufferBindFlags))
{
memAllocInfo.flags |= VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
}
memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
allocInfo.pNext = &memAllocInfo;
VkDeviceMemory deviceMemory;
VkResult vkResult = vkAllocateMemory(device.GetNativeDevice(), &allocInfo, nullptr, &deviceMemory);
AZ_Error(
@@ -37,6 +37,7 @@ namespace AZ
{
VkDeviceSize m_sizeInBytes = 0;
uint32_t m_memoryTypeIndex = 0;
RHI::BufferBindFlags m_bufferBindFlags = RHI::BufferBindFlags::None;
};
~Memory() = default;
@@ -38,7 +38,7 @@ namespace AZ
static RHI::Ptr<MergedShaderResourceGroupPool> Create();
using ShaderResourceGroupList = AZStd::vector<const ShaderResourceGroup*>;
using ShaderResourceGroupList = AZStd::fixed_vector<const ShaderResourceGroup*, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>;
//! Finds or create a new instance of a MergedShaderResourceGroup.
//! @param shaderResourceGroupList The list of ShaderResourceGroups that are being merged.
MergedShaderResourceGroup* FindOrCreate(const ShaderResourceGroupList& shaderResourceGroupList);
@@ -115,77 +115,65 @@ namespace AZ
const RHI::ShaderResourceGroupLayout* layout = groupData.GetLayout();
if (groupData.IsResourceTypeEnabledForCompilation(static_cast<uint32_t>(RHI::ShaderResourceGroupData::ResourceTypeMask::BufferViewMask)))
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(layout->GetShaderInputListForBuffers().size()); ++groupIndex)
{
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(layout->GetShaderInputListForBuffers().size()); ++groupIndex)
{
const RHI::ShaderInputBufferIndex index(groupIndex);
auto bufViews = groupData.GetBufferViewArray(index);
uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::BufferView);
descriptorSet.UpdateBufferViews(layoutIndex, bufViews);
}
const RHI::ShaderInputBufferIndex index(groupIndex);
auto bufViews = groupData.GetBufferViewArray(index);
uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::BufferView);
descriptorSet.UpdateBufferViews(layoutIndex, bufViews);
}
if (groupData.IsResourceTypeEnabledForCompilation(static_cast<uint32_t>(RHI::ShaderResourceGroupData::ResourceTypeMask::ImageViewMask)))
auto const& shaderImageList = layout->GetShaderInputListForImages();
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(shaderImageList.size()); ++groupIndex)
{
auto const& shaderImageList = layout->GetShaderInputListForImages();
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(layout->GetShaderInputListForImages().size()); ++groupIndex)
{
const RHI::ShaderInputImageIndex index(groupIndex);
auto imgViews = groupData.GetImageViewArray(index);
uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageView);
descriptorSet.UpdateImageViews(layoutIndex, imgViews, shaderImageList[groupIndex].m_type);
}
const RHI::ShaderInputImageIndex index(groupIndex);
auto imgViews = groupData.GetImageViewArray(index);
uint32_t layoutIndex =
m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageView);
descriptorSet.UpdateImageViews(layoutIndex, imgViews, shaderImageList[groupIndex].m_type);
}
if (groupData.IsResourceTypeEnabledForCompilation(static_cast<uint32_t>(RHI::ShaderResourceGroupData::ResourceTypeMask::BufferViewUnboundedArrayMask)))
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(layout->GetShaderInputListForBufferUnboundedArrays().size()); ++groupIndex)
{
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(layout->GetShaderInputListForBufferUnboundedArrays().size()); ++groupIndex)
const RHI::ShaderInputBufferUnboundedArrayIndex index(groupIndex);
auto bufViews = groupData.GetBufferViewUnboundedArray(index);
if (bufViews.empty())
{
const RHI::ShaderInputBufferUnboundedArrayIndex index(groupIndex);
auto bufViews = groupData.GetBufferViewUnboundedArray(index);
if (bufViews.empty())
{
// skip empty unbounded arrays
continue;
}
uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::BufferViewUnboundedArray);
descriptorSet.UpdateBufferViews(layoutIndex, bufViews);
// skip empty unbounded arrays
continue;
}
uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::BufferViewUnboundedArray);
descriptorSet.UpdateBufferViews(layoutIndex, bufViews);
}
if (groupData.IsResourceTypeEnabledForCompilation(static_cast<uint32_t>(RHI::ShaderResourceGroupData::ResourceTypeMask::ImageViewUnboundedArrayMask)))
auto const& shaderImageUnboundeArrayList = layout->GetShaderInputListForImageUnboundedArrays();
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(shaderImageUnboundeArrayList.size()); ++groupIndex)
{
auto const& shaderImageUnboundeArrayList = layout->GetShaderInputListForImageUnboundedArrays();
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(layout->GetShaderInputListForImageUnboundedArrays().size()); ++groupIndex)
const RHI::ShaderInputImageUnboundedArrayIndex index(groupIndex);
auto imgViews = groupData.GetImageViewUnboundedArray(index);
if (imgViews.empty())
{
const RHI::ShaderInputImageUnboundedArrayIndex index(groupIndex);
auto imgViews = groupData.GetImageViewUnboundedArray(index);
if (imgViews.empty())
{
// skip empty unbounded arrays
continue;
}
uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageViewUnboundedArray);
descriptorSet.UpdateImageViews(layoutIndex, imgViews, shaderImageUnboundeArrayList[groupIndex].m_type);
// skip empty unbounded arrays
continue;
}
uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::ImageViewUnboundedArray);
descriptorSet.UpdateImageViews(layoutIndex, imgViews, shaderImageUnboundeArrayList[groupIndex].m_type);
}
if (groupData.IsResourceTypeEnabledForCompilation(static_cast<uint32_t>(RHI::ShaderResourceGroupData::ResourceTypeMask::SamplerMask)))
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(layout->GetShaderInputListForSamplers().size()); ++groupIndex)
{
for (uint32_t groupIndex = 0; groupIndex < static_cast<uint32_t>(layout->GetShaderInputListForSamplers().size()); ++groupIndex)
{
const RHI::ShaderInputSamplerIndex index(groupIndex);
auto samplerArray = groupData.GetSamplerArray(index);
uint32_t layoutIndex = m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::Sampler);
descriptorSet.UpdateSamplers(layoutIndex, samplerArray);
}
const RHI::ShaderInputSamplerIndex index(groupIndex);
auto samplerArray = groupData.GetSamplerArray(index);
uint32_t layoutIndex =
m_descriptorSetLayout->GetLayoutIndexFromGroupIndex(groupIndex, DescriptorSetLayout::ResourceType::Sampler);
descriptorSet.UpdateSamplers(layoutIndex, samplerArray);
}
auto constantData = groupData.GetConstantData();
if (!constantData.empty() && groupData.IsResourceTypeEnabledForCompilation(static_cast<uint32_t>(RHI::ShaderResourceGroupData::ResourceTypeMask::ConstantDataMask)))
if (!constantData.empty())
{
descriptorSet.UpdateConstantData(constantData);
}
@@ -36,6 +36,7 @@ ly_add_target(
Gem::Atom_RPI.Edit
Gem::Atom_RPI.Public
Gem::Atom_RHI.Reflect
Gem::Atom_Feature_Common.Static
Gem::Atom_Bootstrap.Headers
)
@@ -0,0 +1,40 @@
/*
* 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 <AzCore/Memory/Memory.h>
#include <AzCore/Memory/SystemAllocator.h>
namespace AtomToolsFramework
{
//! Interface for describing scene content that will be rendered using the PreviewRenderer
class PreviewContent
{
public:
AZ_CLASS_ALLOCATOR(PreviewContent, AZ::SystemAllocator, 0);
PreviewContent() = default;
virtual ~PreviewContent() = default;
//! Initiate loading of scene content, models, materials, etc
virtual void Load() = 0;
//! Return true if content is loaded and ready to render
virtual bool IsReady() const = 0;
//! Return true if content failed to load
virtual bool IsError() const = 0;
//! Report any issues encountered while loading
virtual void ReportErrors() = 0;
//! Prepare or pose content before rendering
virtual void Update() = 0;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,31 @@
/*
* 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 <AtomToolsFramework/PreviewRenderer/PreviewContent.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
class QPixmap;
namespace AtomToolsFramework
{
//! PreviewRendererCaptureRequest describes the size, content, and behavior of a scene to be rendered to an image
struct PreviewRendererCaptureRequest final
{
AZ_CLASS_ALLOCATOR(PreviewRendererCaptureRequest, AZ::SystemAllocator, 0);
int m_size = 512;
AZStd::shared_ptr<PreviewContent> m_content;
AZStd::function<void()> m_captureFailedCallback;
AZStd::function<void(const QPixmap&)> m_captureCompleteCallback;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,29 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/RPI.Public/Base.h>
namespace AtomToolsFramework
{
struct PreviewRendererCaptureRequest;
//! Public interface for PreviewRenderer so that it can be used in other modules
class PreviewRendererInterface
{
public:
AZ_RTTI(PreviewRendererInterface, "{C5B5E3D0-0055-4C08-9B98-FDBBB5F05BED}");
virtual ~PreviewRendererInterface() = default;
virtual void AddCaptureRequest(const PreviewRendererCaptureRequest& captureRequest) = 0;
virtual AZ::RPI::ScenePtr GetScene() const = 0;
virtual AZ::RPI::ViewPtr GetView() const = 0;
virtual AZ::Uuid GetEntityContextId() const = 0;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,24 @@
/*
* 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 <AzCore/EBus/EBus.h>
namespace AtomToolsFramework
{
//! PreviewRendererSystemRequests provides an interface for PreviewRendererSystemComponent
class PreviewRendererSystemRequests : public AZ::EBusTraits
{
public:
// Only a single handler is allowed
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
};
using PreviewRendererSystemRequestBus = AZ::EBus<PreviewRendererSystemRequests>;
} // namespace AtomToolsFramework
@@ -0,0 +1,25 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/string/string.h>
namespace AtomToolsFramework
{
//! PreviewerFeatureProcessorProviderRequests allows registering custom Feature Processors for preview image generation
class PreviewerFeatureProcessorProviderRequests : public AZ::EBusTraits
{
public:
//! Get a list of custom feature processors to register with preview image renderer
virtual void GetRequiredFeatureProcessors(AZStd::unordered_set<AZStd::string>& featureProcessors) const = 0;
};
using PreviewerFeatureProcessorProviderBus = AZ::EBus<PreviewerFeatureProcessorProviderRequests>;
} // namespace AtomToolsFramework
@@ -25,7 +25,7 @@
namespace AtomToolsFramework
{
//! The RenderViewportWidget class is a Qt wrapper around an Atom viewport.
//! RenderViewportWidget renders to an internal window using RPI::ViewportContext
//! RenderViewportWidget renders to an internal window using AZ::RPI::ViewportContext
//! and delegates input via its internal ViewportControllerList.
//! @see AZ::RPI::ViewportContext for Atom's API for setting up
class RenderViewportWidget
@@ -39,7 +39,7 @@ namespace AtomToolsFramework
public:
//! Creates a RenderViewportWidget.
//! Requires the Atom RPI to be initialized in order
//! to internally construct an RPI::ViewportContext.
//! to internally construct an AZ::RPI::ViewportContext.
//! If initializeViewportContext is set to false, nothing will be displayed on-screen until InitiliazeViewportContext is called.
explicit RenderViewportWidget(QWidget* parent = nullptr, bool shouldInitializeViewportContext = true);
~RenderViewportWidget();
@@ -10,6 +10,7 @@
#include <AtomToolsFrameworkSystemComponent.h>
#include <Document/AtomToolsDocumentSystemComponent.h>
#include <Window/AtomToolsMainWindowSystemComponent.h>
#include <PreviewRenderer/PreviewRendererSystemComponent.h>
namespace AtomToolsFramework
{
@@ -19,6 +20,7 @@ namespace AtomToolsFramework
AtomToolsFrameworkSystemComponent::CreateDescriptor(),
AtomToolsDocumentSystemComponent::CreateDescriptor(),
AtomToolsMainWindowSystemComponent::CreateDescriptor(),
PreviewRendererSystemComponent::CreateDescriptor(),
});
}
@@ -28,6 +30,7 @@ namespace AtomToolsFramework
azrtti_typeid<AtomToolsFrameworkSystemComponent>(),
azrtti_typeid<AtomToolsDocumentSystemComponent>(),
azrtti_typeid<AtomToolsMainWindowSystemComponent>(),
azrtti_typeid<PreviewRendererSystemComponent>(),
};
}
}
@@ -0,0 +1,252 @@
/*
* 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/Feature/Utils/FrameCaptureBus.h>
#include <Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
#include <Atom/RPI.Reflect/System/SceneDescriptor.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Transform.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
#include <PreviewRenderer/PreviewRenderer.h>
#include <PreviewRenderer/PreviewRendererCaptureState.h>
#include <PreviewRenderer/PreviewRendererIdleState.h>
#include <PreviewRenderer/PreviewRendererLoadState.h>
#include <QImage>
#include <QPixmap>
namespace AtomToolsFramework
{
PreviewRenderer::PreviewRenderer(const AZStd::string& sceneName, const AZStd::string& pipelineName)
{
PreviewerFeatureProcessorProviderBus::Handler::BusConnect();
m_entityContext = AZStd::make_unique<AzFramework::EntityContext>();
m_entityContext->InitContext();
// Create and register a scene with all required feature processors
AZStd::unordered_set<AZStd::string> featureProcessors;
PreviewerFeatureProcessorProviderBus::Broadcast(
&PreviewerFeatureProcessorProviderBus::Handler::GetRequiredFeatureProcessors, featureProcessors);
AZ::RPI::SceneDescriptor sceneDesc;
sceneDesc.m_featureProcessorNames.assign(featureProcessors.begin(), featureProcessors.end());
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
// Bind m_frameworkScene to the entity context's AzFramework::Scene
auto sceneSystem = AzFramework::SceneSystemInterface::Get();
AZ_Assert(sceneSystem, "Failed to get scene system implementation.");
AZ::Outcome<AZStd::shared_ptr<AzFramework::Scene>, AZStd::string> createSceneOutcome = sceneSystem->CreateScene(sceneName);
AZ_Assert(createSceneOutcome, createSceneOutcome.GetError().c_str());
m_frameworkScene = createSceneOutcome.TakeValue();
m_frameworkScene->SetSubsystem(m_scene);
m_frameworkScene->SetSubsystem(m_entityContext.get());
// Create a render pipeline from the specified asset for the window context and add the pipeline to the scene
AZ::RPI::RenderPipelineDescriptor pipelineDesc;
pipelineDesc.m_mainViewTagName = "MainCamera";
pipelineDesc.m_name = pipelineName;
pipelineDesc.m_rootPassTemplate = "MainPipelineRenderToTexture";
// We have to set the samples to 4 to match the pipeline passes' setting, otherwise it may lead to device lost issue
// [GFX TODO] [ATOM-13551] Default value sand validation required to prevent pipeline crash and device lost
pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
m_scene->AddRenderPipeline(m_renderPipeline);
m_scene->Activate();
AZ::RPI::RPISystemInterface::Get()->RegisterScene(m_scene);
m_passHierarchy.push_back(pipelineName);
m_passHierarchy.push_back("CopyToSwapChain");
// Connect camera to pipeline's default view after camera entity activated
AZ::Matrix4x4 viewToClipMatrix;
AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, FieldOfView, AspectRatio, NearDist, FarDist, true);
m_view = AZ::RPI::View::CreateView(AZ::Name("MainCamera"), AZ::RPI::View::UsageCamera);
m_view->SetViewToClipMatrix(viewToClipMatrix);
m_renderPipeline->SetDefaultView(m_view);
m_state.reset(new PreviewRendererIdleState(this));
AZ::Interface<PreviewRendererInterface>::Register(this);
}
PreviewRenderer::~PreviewRenderer()
{
PreviewerFeatureProcessorProviderBus::Handler::BusDisconnect();
m_state.reset();
m_currentCaptureRequest = {};
m_captureRequestQueue = {};
m_scene->Deactivate();
m_scene->RemoveRenderPipeline(m_renderPipeline->GetId());
AZ::RPI::RPISystemInterface::Get()->UnregisterScene(m_scene);
m_frameworkScene->UnsetSubsystem(m_scene);
m_frameworkScene->UnsetSubsystem(m_entityContext.get());
AZ::Interface<PreviewRendererInterface>::Unregister(this);
}
void PreviewRenderer::AddCaptureRequest(const PreviewRendererCaptureRequest& captureRequest)
{
m_captureRequestQueue.push(captureRequest);
}
AZ::RPI::ScenePtr PreviewRenderer::GetScene() const
{
return m_scene;
}
AZ::RPI::ViewPtr PreviewRenderer::GetView() const
{
return m_view;
}
AZ::Uuid PreviewRenderer::GetEntityContextId() const
{
return m_entityContext->GetContextId();
}
void PreviewRenderer::ProcessCaptureRequests()
{
if (!m_captureRequestQueue.empty())
{
// pop the next request to be rendered from the queue
m_currentCaptureRequest = m_captureRequestQueue.front();
m_captureRequestQueue.pop();
m_state.reset();
m_state.reset(new PreviewRendererLoadState(this));
}
}
void PreviewRenderer::CancelCaptureRequest()
{
if (m_currentCaptureRequest.m_captureFailedCallback)
{
m_currentCaptureRequest.m_captureFailedCallback();
}
m_state.reset();
m_state.reset(new PreviewRendererIdleState(this));
}
void PreviewRenderer::CompleteCaptureRequest()
{
m_state.reset();
m_state.reset(new PreviewRendererIdleState(this));
}
void PreviewRenderer::LoadContent()
{
m_currentCaptureRequest.m_content->Load();
}
void PreviewRenderer::UpdateLoadContent()
{
if (m_currentCaptureRequest.m_content->IsReady())
{
m_state.reset();
m_state.reset(new PreviewRendererCaptureState(this));
return;
}
if (m_currentCaptureRequest.m_content->IsError())
{
CancelLoadContent();
return;
}
}
void PreviewRenderer::CancelLoadContent()
{
m_currentCaptureRequest.m_content->ReportErrors();
CancelCaptureRequest();
}
void PreviewRenderer::PoseContent()
{
m_currentCaptureRequest.m_content->Update();
}
bool PreviewRenderer::StartCapture()
{
auto captureCompleteCallback = m_currentCaptureRequest.m_captureCompleteCallback;
auto captureFailedCallback = m_currentCaptureRequest.m_captureFailedCallback;
auto captureCallback = [captureCompleteCallback, captureFailedCallback](const AZ::RPI::AttachmentReadback::ReadbackResult& result)
{
if (result.m_dataBuffer)
{
if (captureCompleteCallback)
{
captureCompleteCallback(QPixmap::fromImage(QImage(
result.m_dataBuffer.get()->data(), result.m_imageDescriptor.m_size.m_width,
result.m_imageDescriptor.m_size.m_height, QImage::Format_RGBA8888)));
}
}
else
{
if (captureFailedCallback)
{
captureFailedCallback();
}
}
};
if (auto renderToTexturePass = azrtti_cast<AZ::RPI::RenderToTexturePass*>(m_renderPipeline->GetRootPass().get()))
{
renderToTexturePass->ResizeOutput(m_currentCaptureRequest.m_size, m_currentCaptureRequest.m_size);
}
m_renderPipeline->AddToRenderTickOnce();
bool startedCapture = false;
AZ::Render::FrameCaptureRequestBus::BroadcastResult(
startedCapture, &AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback, m_passHierarchy,
AZStd::string("Output"), captureCallback, AZ::RPI::PassAttachmentReadbackOption::Output);
return startedCapture;
}
void PreviewRenderer::EndCapture()
{
m_currentCaptureRequest = {};
m_renderPipeline->RemoveFromRenderTick();
}
void PreviewRenderer::GetRequiredFeatureProcessors(AZStd::unordered_set<AZStd::string>& featureProcessors) const
{
featureProcessors.insert({
"AZ::Render::TransformServiceFeatureProcessor",
"AZ::Render::MeshFeatureProcessor",
"AZ::Render::SimplePointLightFeatureProcessor",
"AZ::Render::SimpleSpotLightFeatureProcessor",
"AZ::Render::PointLightFeatureProcessor",
// There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow
// flickering [ATOM-13568]
// as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now.
// Possibly re-enable with [GFX TODO][ATOM-13639]
// "AZ::Render::DirectionalLightFeatureProcessor",
"AZ::Render::DiskLightFeatureProcessor",
"AZ::Render::CapsuleLightFeatureProcessor",
"AZ::Render::QuadLightFeatureProcessor",
"AZ::Render::DecalTextureArrayFeatureProcessor",
"AZ::Render::ImageBasedLightFeatureProcessor",
"AZ::Render::PostProcessFeatureProcessor",
"AZ::Render::SkyBoxFeatureProcessor" });
}
} // namespace AtomToolsFramework
@@ -0,0 +1,75 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewContent.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererCaptureRequest.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererInterface.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewerFeatureProcessorProviderBus.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
#include <PreviewRenderer/PreviewRendererState.h>
namespace AtomToolsFramework
{
//! Processes requests for setting up content that gets rendered to a texture and captured to an image
class PreviewRenderer final
: public PreviewRendererInterface
, public PreviewerFeatureProcessorProviderBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(PreviewRenderer, AZ::SystemAllocator, 0);
AZ_RTTI(PreviewRenderer, "{60FCB7AB-2A94-417A-8C5E-5B588D17F5D1}", PreviewRendererInterface);
PreviewRenderer(const AZStd::string& sceneName, const AZStd::string& pipelineName);
~PreviewRenderer() override;
void AddCaptureRequest(const PreviewRendererCaptureRequest& captureRequest) override;
AZ::RPI::ScenePtr GetScene() const override;
AZ::RPI::ViewPtr GetView() const override;
AZ::Uuid GetEntityContextId() const override;
void ProcessCaptureRequests();
void CancelCaptureRequest();
void CompleteCaptureRequest();
void LoadContent();
void UpdateLoadContent();
void CancelLoadContent();
void PoseContent();
bool StartCapture();
void EndCapture();
private:
//! AZ::Render::PreviewerFeatureProcessorProviderBus::Handler interface overrides...
void GetRequiredFeatureProcessors(AZStd::unordered_set<AZStd::string>& featureProcessors) const override;
static constexpr float AspectRatio = 1.0f;
static constexpr float NearDist = 0.001f;
static constexpr float FarDist = 100.0f;
static constexpr float FieldOfView = AZ::Constants::HalfPi;
AZ::RPI::ScenePtr m_scene;
AZStd::shared_ptr<AzFramework::Scene> m_frameworkScene;
AZ::RPI::RenderPipelinePtr m_renderPipeline;
AZ::RPI::ViewPtr m_view;
AZStd::vector<AZStd::string> m_passHierarchy;
AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
//! Incoming requests are appended to this queue and processed one at a time in OnTick function.
AZStd::queue<PreviewRendererCaptureRequest> m_captureRequestQueue;
PreviewRendererCaptureRequest m_currentCaptureRequest;
AZStd::unique_ptr<PreviewRendererState> m_state;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,42 @@
/*
* 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 <PreviewRenderer/PreviewRenderer.h>
#include <PreviewRenderer/PreviewRendererCaptureState.h>
namespace AtomToolsFramework
{
PreviewRendererCaptureState::PreviewRendererCaptureState(PreviewRenderer* renderer)
: PreviewRendererState(renderer)
{
m_renderer->PoseContent();
AZ::TickBus::Handler::BusConnect();
}
PreviewRendererCaptureState::~PreviewRendererCaptureState()
{
AZ::Render::FrameCaptureNotificationBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
m_renderer->EndCapture();
}
void PreviewRendererCaptureState::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
if ((m_ticksToCapture-- <= 0) && m_renderer->StartCapture())
{
AZ::Render::FrameCaptureNotificationBus::Handler::BusConnect();
AZ::TickBus::Handler::BusDisconnect();
}
}
void PreviewRendererCaptureState::OnCaptureFinished(
[[maybe_unused]] AZ::Render::FrameCaptureResult result, [[maybe_unused]] const AZStd::string& info)
{
m_renderer->CompleteCaptureRequest();
}
} // namespace AtomToolsFramework
@@ -0,0 +1,37 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/Feature/Utils/FrameCaptureBus.h>
#include <AzCore/Component/TickBus.h>
#include <PreviewRenderer/PreviewRendererState.h>
namespace AtomToolsFramework
{
//! PreviewRendererCaptureState renders a thumbnail to a pixmap and notifies MaterialOrModelThumbnail once finished
class PreviewRendererCaptureState final
: public PreviewRendererState
, public AZ::TickBus::Handler
, public AZ::Render::FrameCaptureNotificationBus::Handler
{
public:
PreviewRendererCaptureState(PreviewRenderer* renderer);
~PreviewRendererCaptureState();
private:
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
//! AZ::Render::FrameCaptureNotificationBus::Handler overrides...
void OnCaptureFinished(AZ::Render::FrameCaptureResult result, const AZStd::string& info) override;
//! This is necessary to suspend capture to allow a frame for Material and Mesh components to assign materials
int m_ticksToCapture = 1;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,29 @@
/*
* 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 <PreviewRenderer/PreviewRenderer.h>
#include <PreviewRenderer/PreviewRendererIdleState.h>
namespace AtomToolsFramework
{
PreviewRendererIdleState::PreviewRendererIdleState(PreviewRenderer* renderer)
: PreviewRendererState(renderer)
{
AZ::TickBus::Handler::BusConnect();
}
PreviewRendererIdleState::~PreviewRendererIdleState()
{
AZ::TickBus::Handler::BusDisconnect();
}
void PreviewRendererIdleState::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
m_renderer->ProcessCaptureRequests();
}
} // namespace AtomToolsFramework
@@ -0,0 +1,29 @@
/*
* 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 <AzCore/Component/TickBus.h>
#include <PreviewRenderer/PreviewRendererState.h>
namespace AtomToolsFramework
{
//! PreviewRendererIdleState checks whether there are any new thumbnails that need to be rendered every tick
class PreviewRendererIdleState final
: public PreviewRendererState
, public AZ::TickBus::Handler
{
public:
PreviewRendererIdleState(PreviewRenderer* renderer);
~PreviewRendererIdleState();
private:
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,36 @@
/*
* 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 <PreviewRenderer/PreviewRenderer.h>
#include <PreviewRenderer/PreviewRendererLoadState.h>
namespace AtomToolsFramework
{
PreviewRendererLoadState::PreviewRendererLoadState(PreviewRenderer* renderer)
: PreviewRendererState(renderer)
{
m_renderer->LoadContent();
AZ::TickBus::Handler::BusConnect();
}
PreviewRendererLoadState::~PreviewRendererLoadState()
{
AZ::TickBus::Handler::BusDisconnect();
}
void PreviewRendererLoadState::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
if ((m_timeRemainingS += deltaTime) > TimeOutS)
{
m_renderer->CancelLoadContent();
return;
}
m_renderer->UpdateLoadContent();
}
} // namespace AtomToolsFramework
@@ -0,0 +1,32 @@
/*
* 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 <AzCore/Component/TickBus.h>
#include <PreviewRenderer/PreviewRendererState.h>
namespace AtomToolsFramework
{
//! PreviewRendererLoadState pauses further rendering until all assets used for rendering a thumbnail have been loaded
class PreviewRendererLoadState final
: public PreviewRendererState
, public AZ::TickBus::Handler
{
public:
PreviewRendererLoadState(PreviewRenderer* renderer);
~PreviewRendererLoadState();
private:
//! AZ::TickBus::Handler interface overrides...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
static constexpr float TimeOutS = 5.0f;
float m_timeRemainingS = 0.0f;
};
} // namespace AtomToolsFramework
@@ -0,0 +1,29 @@
/*
* 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
namespace AtomToolsFramework
{
class PreviewRenderer;
//! PreviewRendererState is an interface for defining states that manages the logic flow of the PreviewRenderer
class PreviewRendererState
{
public:
explicit PreviewRendererState(PreviewRenderer* renderer)
: m_renderer(renderer)
{
}
virtual ~PreviewRendererState() = default;
protected:
PreviewRenderer* m_renderer = {};
};
} // namespace AtomToolsFramework
@@ -0,0 +1,75 @@
/*
* 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/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Serialization/SerializeContext.h>
#include <PreviewRenderer/PreviewRendererSystemComponent.h>
namespace AtomToolsFramework
{
void PreviewRendererSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<PreviewRendererSystemComponent, AZ::Component>()
->Version(0);
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<PreviewRendererSystemComponent>("PreviewRendererSystemComponent", "System component that manages a global PreviewRenderer.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
}
void PreviewRendererSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("PreviewRendererSystem"));
}
void PreviewRendererSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("PreviewRendererSystem"));
}
void PreviewRendererSystemComponent::Init()
{
}
void PreviewRendererSystemComponent::Activate()
{
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
PreviewRendererSystemRequestBus::Handler::BusConnect();
}
void PreviewRendererSystemComponent::Deactivate()
{
PreviewRendererSystemRequestBus::Handler::BusDisconnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
m_previewRenderer.reset();
}
void PreviewRendererSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
AZ::TickBus::QueueFunction([this](){
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
"PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline"));
});
}
void PreviewRendererSystemComponent::OnApplicationAboutToStop()
{
m_previewRenderer.reset();
}
} // namespace AtomToolsFramework
@@ -0,0 +1,49 @@
/*
* 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 <AtomToolsFramework/PreviewRenderer/PreviewRendererSystemRequestBus.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzFramework/Application/Application.h>
#include <PreviewRenderer/PreviewRenderer.h>
namespace AtomToolsFramework
{
//! System component that manages a global PreviewRenderer.
class PreviewRendererSystemComponent final
: public AZ::Component
, public AzFramework::AssetCatalogEventBus::Handler
, public AzFramework::ApplicationLifecycleEvents::Bus::Handler
, public PreviewRendererSystemRequestBus::Handler
{
public:
AZ_COMPONENT(PreviewRendererSystemComponent, "{E9F79FD8-82F2-4C80-966D-95F28484F229}");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
protected:
// AZ::Component interface overrides...
void Init() override;
void Activate() override;
void Deactivate() override;
private:
// AzFramework::AssetCatalogEventBus::Handler overrides ...
void OnCatalogLoaded(const char* catalogFile) override;
// AzFramework::ApplicationLifecycleEvents overrides...
void OnApplicationAboutToStop() override;
AZStd::unique_ptr<AtomToolsFramework::PreviewRenderer> m_previewRenderer;
};
} // namespace AtomToolsFramework
@@ -58,4 +58,20 @@ set(FILES
Source/Window/AtomToolsMainWindow.cpp
Source/Window/AtomToolsMainWindowSystemComponent.cpp
Source/Window/AtomToolsMainWindowSystemComponent.h
Include/AtomToolsFramework/PreviewRenderer/PreviewContent.h
Include/AtomToolsFramework/PreviewRenderer/PreviewRendererCaptureRequest.h
Include/AtomToolsFramework/PreviewRenderer/PreviewRendererInterface.h
Include/AtomToolsFramework/PreviewRenderer/PreviewRendererSystemRequestBus.h
Include/AtomToolsFramework/PreviewRenderer/PreviewerFeatureProcessorProviderBus.h
Source/PreviewRenderer/PreviewRenderer.cpp
Source/PreviewRenderer/PreviewRenderer.h
Source/PreviewRenderer/PreviewRendererState.h
Source/PreviewRenderer/PreviewRendererIdleState.cpp
Source/PreviewRenderer/PreviewRendererIdleState.h
Source/PreviewRenderer/PreviewRendererLoadState.cpp
Source/PreviewRenderer/PreviewRendererLoadState.h
Source/PreviewRenderer/PreviewRendererCaptureState.cpp
Source/PreviewRenderer/PreviewRendererCaptureState.h
Source/PreviewRenderer/PreviewRendererSystemComponent.cpp
Source/PreviewRenderer/PreviewRendererSystemComponent.h
)
@@ -1353,8 +1353,9 @@ namespace AZ::AtomBridge
// if 2d draw need to project pos to screen first
AzFramework::TextDrawParameters params;
AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext();
const auto dpiScaleFactor = viewportContext->GetDpiScalingFactor();
params.m_drawViewportId = viewportContext->GetId(); // get the viewport ID so default viewport works
params.m_position = AZ::Vector3(x, y, 1.0f);
params.m_position = AZ::Vector3(x * dpiScaleFactor, y * dpiScaleFactor, 1.0f);
params.m_color = m_rendState.m_color;
params.m_scale = AZ::Vector2(size);
params.m_hAlign = center ? AzFramework::TextHorizontalAlignment::Center : AzFramework::TextHorizontalAlignment::Left; //! Horizontal text alignment
@@ -120,13 +120,6 @@ namespace AZ
//! Sets the filter method of shadows.
virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0;
//! Gets the width of softening boundary between shadowed area and lit area in degrees.
virtual float GetSofteningBoundaryWidthAngle() const = 0;
//! Sets the width of softening boundary between shadowed area and lit area in degrees.
//! 0 disables softening.
virtual void SetSofteningBoundaryWidthAngle(float degrees) = 0;
//! Gets the sample count for filtering of the shadow boundary.
virtual uint32_t GetFilteringSampleCount() const = 0;
@@ -59,7 +59,6 @@ namespace AZ
float m_bias = 0.1f;
ShadowmapSize m_shadowmapMaxSize = ShadowmapSize::Size256;
ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None;
float m_boundaryWidthInDegrees = 0.25f;
uint16_t m_filteringSampleCount = 12;
float m_esmExponent = 87.0f;
@@ -153,15 +153,6 @@ namespace AZ
//! @param method filter method.
virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0;
//! This gets the width of boundary between shadowed area and lit area.
//! @return Boundary width. The shadow is gradually changed the degree of shadowed.
virtual float GetSofteningBoundaryWidth() const = 0;
//! This specifies the width of boundary between shadowed area and lit area.
//! @param width Boundary width. The shadow is gradually changed the degree of shadowed.
//! If width == 0, softening edge is disabled. Units are in meters.
virtual void SetSofteningBoundaryWidth(float width) = 0;
//! This gets the sample count for filtering of the shadow boundary.
//! @return Sample Count for filtering (up to 64)
virtual uint32_t GetFilteringSampleCount() const = 0;
@@ -101,10 +101,6 @@ namespace AZ
//! Method of shadow's filtering.
ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None;
//! Width of the boundary between shadowed area and lit one.
//! If this is 0, edge softening is disabled. Units are in meters.
float m_boundaryWidth = 0.03f; // 3cm
//! Sample Count for filtering (from 4 to 64)
//! It is used only when the pixel is predicted as on the boundary.
uint16_t m_filteringSampleCount = 32;
@@ -0,0 +1,34 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/Feature/Material/MaterialAssignmentId.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
class QPixmap;
namespace AZ
{
namespace Render
{
//! EditorMaterialSystemComponentNotifications is an interface for handling notifications from EditorMaterialSystemComponent, like
//! being informed that material preview images are available
class EditorMaterialSystemComponentNotifications : public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
//! Notify that a material preview image is ready
virtual void OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap) = 0;
};
using EditorMaterialSystemComponentNotificationBus = AZ::EBus<EditorMaterialSystemComponentNotifications>;
} // namespace Render
} // namespace AZ
@@ -5,20 +5,22 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/Feature/Material/MaterialAssignmentId.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
#include <QPixmap>
namespace AZ
{
namespace Render
{
//! EditorMaterialSystemComponentRequests provides an interface to communicate with MaterialEditor
class EditorMaterialSystemComponentRequests
: public AZ::EBusTraits
//! EditorMaterialSystemComponentRequests provides an interface for interacting with EditorMaterialSystemComponent, performing
//! different operations like opening the material editor, the material instance inspector, and managing material preview images
class EditorMaterialSystemComponentRequests : public AZ::EBusTraits
{
public:
// Only a single handler is allowed
@@ -31,6 +33,14 @@ namespace AZ
//! Open material instance editor
virtual void OpenMaterialInspector(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) = 0;
//! Generate a material preview image
virtual void RenderMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) = 0;
//! Get recently rendered material preview image
virtual QPixmap GetRenderedMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const = 0;
};
using EditorMaterialSystemComponentRequestBus = AZ::EBus<EditorMaterialSystemComponentRequests>;
} // namespace Render
@@ -1,33 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
//! ThumbnailFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation
//! Duplicates will be ignored
//! You can check minimal feature processors that are already registered in CommonThumbnailRenderer.cpp
class ThumbnailFeatureProcessorProviderRequests
: public AZ::EBusTraits
{
public:
//! Get a list of custom feature processors to register with thumbnail renderer
virtual const AZStd::vector<AZStd::string>& GetCustomFeatureProcessors() const = 0;
};
using ThumbnailFeatureProcessorProviderBus = AZ::EBus<ThumbnailFeatureProcessorProviderRequests>;
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -36,7 +36,6 @@ namespace AZ
->Field("Shadow Bias", &AreaLightComponentConfig::m_bias)
->Field("Shadowmap Max Size", &AreaLightComponentConfig::m_shadowmapMaxSize)
->Field("Shadow Filter Method", &AreaLightComponentConfig::m_shadowFilterMethod)
->Field("Softening Boundary Width", &AreaLightComponentConfig::m_boundaryWidthInDegrees)
->Field("Filtering Sample Count", &AreaLightComponentConfig::m_filteringSampleCount)
->Field("Esm Exponent", &AreaLightComponentConfig::m_esmExponent)
;
@@ -74,8 +74,6 @@ namespace AZ::Render
->Event("SetShadowmapMaxSize", &AreaLightRequestBus::Events::SetShadowmapMaxSize)
->Event("GetShadowFilterMethod", &AreaLightRequestBus::Events::GetShadowFilterMethod)
->Event("SetShadowFilterMethod", &AreaLightRequestBus::Events::SetShadowFilterMethod)
->Event("GetSofteningBoundaryWidthAngle", &AreaLightRequestBus::Events::GetSofteningBoundaryWidthAngle)
->Event("SetSofteningBoundaryWidthAngle", &AreaLightRequestBus::Events::SetSofteningBoundaryWidthAngle)
->Event("GetFilteringSampleCount", &AreaLightRequestBus::Events::GetFilteringSampleCount)
->Event("SetFilteringSampleCount", &AreaLightRequestBus::Events::SetFilteringSampleCount)
->Event("GetEsmExponent", &AreaLightRequestBus::Events::GetEsmExponent)
@@ -95,7 +93,6 @@ namespace AZ::Render
->VirtualProperty("ShadowBias", "GetShadowBias", "SetShadowBias")
->VirtualProperty("ShadowmapMaxSize", "GetShadowmapMaxSize", "SetShadowmapMaxSize")
->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod")
->VirtualProperty("SofteningBoundaryWidthAngle", "GetSofteningBoundaryWidthAngle", "SetSofteningBoundaryWidthAngle")
->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount")
->VirtualProperty("EsmExponent", "GetEsmExponent", "SetEsmExponent");
;
@@ -307,7 +304,6 @@ namespace AZ::Render
m_lightShapeDelegate->SetShadowBias(m_configuration.m_bias);
m_lightShapeDelegate->SetShadowmapMaxSize(m_configuration.m_shadowmapMaxSize);
m_lightShapeDelegate->SetShadowFilterMethod(m_configuration.m_shadowFilterMethod);
m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(m_configuration.m_boundaryWidthInDegrees);
m_lightShapeDelegate->SetFilteringSampleCount(m_configuration.m_filteringSampleCount);
m_lightShapeDelegate->SetEsmExponent(m_configuration.m_esmExponent);
}
@@ -506,20 +502,6 @@ namespace AZ::Render
}
}
float AreaLightComponentController::GetSofteningBoundaryWidthAngle() const
{
return m_configuration.m_boundaryWidthInDegrees;
}
void AreaLightComponentController::SetSofteningBoundaryWidthAngle(float width)
{
m_configuration.m_boundaryWidthInDegrees = width;
if (m_lightShapeDelegate)
{
m_lightShapeDelegate->SetSofteningBoundaryWidthAngle(width);
}
}
uint32_t AreaLightComponentController::GetFilteringSampleCount() const
{
return m_configuration.m_filteringSampleCount;
@@ -82,8 +82,6 @@ namespace AZ
void SetShadowmapMaxSize(ShadowmapSize size) override;
ShadowFilterMethod GetShadowFilterMethod() const override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
float GetSofteningBoundaryWidthAngle() const override;
void SetSofteningBoundaryWidthAngle(float width) override;
uint32_t GetFilteringSampleCount() const override;
void SetFilteringSampleCount(uint32_t count) override;
float GetEsmExponent() const override;
@@ -37,7 +37,6 @@ namespace AZ
->Field("IsCascadeCorrectionEnabled", &DirectionalLightComponentConfig::m_isCascadeCorrectionEnabled)
->Field("IsDebugColoringEnabled", &DirectionalLightComponentConfig::m_isDebugColoringEnabled)
->Field("ShadowFilterMethod", &DirectionalLightComponentConfig::m_shadowFilterMethod)
->Field("SofteningBoundaryWidth", &DirectionalLightComponentConfig::m_boundaryWidth)
->Field("PcfFilteringSampleCount", &DirectionalLightComponentConfig::m_filteringSampleCount)
->Field("ShadowReceiverPlaneBiasEnabled", &DirectionalLightComponentConfig::m_receiverPlaneBiasEnabled);
}
@@ -80,8 +80,6 @@ namespace AZ
->Event("SetDebugColoringEnabled", &DirectionalLightRequestBus::Events::SetDebugColoringEnabled)
->Event("GetShadowFilterMethod", &DirectionalLightRequestBus::Events::GetShadowFilterMethod)
->Event("SetShadowFilterMethod", &DirectionalLightRequestBus::Events::SetShadowFilterMethod)
->Event("GetSofteningBoundaryWidth", &DirectionalLightRequestBus::Events::GetSofteningBoundaryWidth)
->Event("SetSofteningBoundaryWidth", &DirectionalLightRequestBus::Events::SetSofteningBoundaryWidth)
->Event("GetFilteringSampleCount", &DirectionalLightRequestBus::Events::GetFilteringSampleCount)
->Event("SetFilteringSampleCount", &DirectionalLightRequestBus::Events::SetFilteringSampleCount)
->Event("GetShadowReceiverPlaneBiasEnabled", &DirectionalLightRequestBus::Events::GetShadowReceiverPlaneBiasEnabled)
@@ -99,7 +97,6 @@ namespace AZ
->VirtualProperty("ViewFrustumCorrectionEnabled", "GetViewFrustumCorrectionEnabled", "SetViewFrustumCorrectionEnabled")
->VirtualProperty("DebugColoringEnabled", "GetDebugColoringEnabled", "SetDebugColoringEnabled")
->VirtualProperty("ShadowFilterMethod", "GetShadowFilterMethod", "SetShadowFilterMethod")
->VirtualProperty("SofteningBoundaryWidth", "GetSofteningBoundaryWidth", "SetSofteningBoundaryWidth")
->VirtualProperty("FilteringSampleCount", "GetFilteringSampleCount", "SetFilteringSampleCount")
->VirtualProperty("ShadowReceiverPlaneBiasEnabled", "GetShadowReceiverPlaneBiasEnabled", "SetShadowReceiverPlaneBiasEnabled");
;
@@ -404,21 +401,6 @@ namespace AZ
}
}
float DirectionalLightComponentController::GetSofteningBoundaryWidth() const
{
return m_configuration.m_boundaryWidth;
}
void DirectionalLightComponentController::SetSofteningBoundaryWidth(float width)
{
width = GetMin(Shadow::MaxSofteningBoundaryWidth, GetMax(0.f, width));
m_configuration.m_boundaryWidth = width;
if (m_featureProcessor)
{
m_featureProcessor->SetShadowBoundaryWidth(m_lightHandle, width);
}
}
uint32_t DirectionalLightComponentController::GetFilteringSampleCount() const
{
return aznumeric_cast<uint32_t>(m_configuration.m_filteringSampleCount);
@@ -517,7 +499,6 @@ namespace AZ
SetViewFrustumCorrectionEnabled(m_configuration.m_isCascadeCorrectionEnabled);
SetDebugColoringEnabled(m_configuration.m_isDebugColoringEnabled);
SetShadowFilterMethod(m_configuration.m_shadowFilterMethod);
SetSofteningBoundaryWidth(m_configuration.m_boundaryWidth);
SetFilteringSampleCount(m_configuration.m_filteringSampleCount);
SetShadowReceiverPlaneBiasEnabled(m_configuration.m_receiverPlaneBiasEnabled);
@@ -76,8 +76,6 @@ namespace AZ
void SetDebugColoringEnabled(bool enabled) override;
ShadowFilterMethod GetShadowFilterMethod() const override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
float GetSofteningBoundaryWidth() const override;
void SetSofteningBoundaryWidth(float width) override;
uint32_t GetFilteringSampleCount() const override;
void SetFilteringSampleCount(uint32_t count) override;
bool GetShadowReceiverPlaneBiasEnabled() const override;
@@ -147,14 +147,6 @@ namespace AZ::Render
}
}
void DiskLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees));
}
}
void DiskLightDelegate::SetFilteringSampleCount(uint32_t count)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
@@ -44,7 +44,6 @@ namespace AZ
void SetShadowBias(float bias) override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(float widthInDegrees) override;
void SetFilteringSampleCount(uint32_t count) override;
void SetEsmExponent(float exponent) override;
@@ -154,15 +154,6 @@ namespace AZ
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::AttributesAndValues)
->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows)
->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::ShadowsDisabled)
->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_boundaryWidthInDegrees, "Softening boundary width",
"Width of the boundary between shadowed area and lit one. "
"Units are in degrees. "
"If this is 0, softening edge is disabled.")
->Attribute(Edit::Attributes::Min, 0.f)
->Attribute(Edit::Attributes::Max, 1.f)
->Attribute(Edit::Attributes::Suffix, " deg")
->Attribute(Edit::Attributes::Visibility, &AreaLightComponentConfig::SupportsShadows)
->Attribute(Edit::Attributes::ReadOnly, &AreaLightComponentConfig::IsEsmDisabled)
->DataElement(Edit::UIHandlers::Slider, &AreaLightComponentConfig::m_filteringSampleCount, "Filtering sample count",
"This is only used when the pixel is predicted to be on the boundary. Specific to PCF and ESM+PCF.")
->Attribute(Edit::Attributes::Min, 4)
@@ -133,15 +133,6 @@ namespace AZ
->EnumAttribute(ShadowFilterMethod::Esm, "ESM")
->EnumAttribute(ShadowFilterMethod::EsmPcf, "ESM+PCF")
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_boundaryWidth, "Softening boundary width",
"Width of the boundary between shadowed area and lit one. "
"Units are in meters. "
"If this is 0, softening edge is disabled.")
->Attribute(Edit::Attributes::Min, 0.f)
->Attribute(Edit::Attributes::Max, 0.1f)
->Attribute(Edit::Attributes::Suffix, " m")
->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
->Attribute(Edit::Attributes::ReadOnly, &DirectionalLightComponentConfig::IsEsmDisabled)
->DataElement(Edit::UIHandlers::Slider, &DirectionalLightComponentConfig::m_filteringSampleCount, "Filtering sample count",
"This is used only when the pixel is predicted as on the boundary. "
"Specific to PCF and ESM+PCF.")
@@ -56,7 +56,6 @@ namespace AZ
void SetShadowBias([[maybe_unused]] float bias) override {};
void SetShadowmapMaxSize([[maybe_unused]] ShadowmapSize size) override {};
void SetShadowFilterMethod([[maybe_unused]] ShadowFilterMethod method) override {};
void SetSofteningBoundaryWidthAngle([[maybe_unused]] float widthInDegrees) override {};
void SetFilteringSampleCount([[maybe_unused]] uint32_t count) override {};
void SetEsmExponent([[maybe_unused]] float esmExponent) override{};
@@ -75,8 +75,6 @@ namespace AZ
virtual void SetShadowmapMaxSize(ShadowmapSize size) = 0;
//! Sets the filter method for the shadow
virtual void SetShadowFilterMethod(ShadowFilterMethod method) = 0;
//! Sets the width of boundary between shadowed area and lit area in degrees.
virtual void SetSofteningBoundaryWidthAngle(float widthInDegrees) = 0;
//! Sets the sample count for filtering of the shadow boundary, max 64.
virtual void SetFilteringSampleCount(uint32_t count) = 0;
//! Sets the Esm exponent to use. Higher values produce a steeper falloff between light and shadow.
@@ -92,14 +92,6 @@ namespace AZ::Render
}
}
void SphereLightDelegate::SetSofteningBoundaryWidthAngle(float widthInDegrees)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
{
GetFeatureProcessor()->SetSofteningBoundaryWidthAngle(GetLightHandle(), DegToRad(widthInDegrees));
}
}
void SphereLightDelegate::SetFilteringSampleCount(uint32_t count)
{
if (GetShadowsEnabled() && GetLightHandle().IsValid())
@@ -34,7 +34,6 @@ namespace AZ
void SetShadowBias(float bias) override;
void SetShadowmapMaxSize(ShadowmapSize size) override;
void SetShadowFilterMethod(ShadowFilterMethod method) override;
void SetSofteningBoundaryWidthAngle(float widthInDegrees) override;
void SetFilteringSampleCount(uint32_t count) override;
void SetEsmExponent(float esmExponent) override;
@@ -6,15 +6,17 @@
*
*/
#include <EditorCommonFeaturesSystemComponent.h>
#include <SkinnedMesh/SkinnedMeshDebugDisplay.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/API/EditorCameraBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <EditorCommonFeaturesSystemComponent.h>
#include <SharedPreview/SharedThumbnail.h>
#include <SkinnedMesh/SkinnedMeshDebugDisplay.h>
#include <IEditor.h>
@@ -68,7 +70,7 @@ namespace AZ
void EditorCommonFeaturesSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
AZ_UNUSED(required);
required.push_back(AZ_CRC_CE("ThumbnailerService"));
}
void EditorCommonFeaturesSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
@@ -82,24 +84,23 @@ namespace AZ
void EditorCommonFeaturesSystemComponent::Activate()
{
m_renderer = AZStd::make_unique<AZ::LyIntegration::Thumbnails::CommonThumbnailRenderer>();
m_previewerFactory = AZStd::make_unique <LyIntegration::CommonPreviewerFactory>();
m_skinnedMeshDebugDisplay = AZStd::make_unique<SkinnedMeshDebugDisplay>();
AzToolsFramework::EditorLevelNotificationBus::Handler::BusConnect();
AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler::BusConnect();
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
}
void EditorCommonFeaturesSystemComponent::Deactivate()
{
AzToolsFramework::EditorLevelNotificationBus::Handler::BusDisconnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
AzToolsFramework::EditorLevelNotificationBus::Handler::BusDisconnect();
AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler::BusDisconnect();
m_skinnedMeshDebugDisplay.reset();
m_previewerFactory.reset();
m_renderer.reset();
TeardownThumbnails();
}
void EditorCommonFeaturesSystemComponent::OnNewLevelCreated()
@@ -191,6 +192,13 @@ namespace AZ
}
}
void EditorCommonFeaturesSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
AZ::TickBus::QueueFunction([this](){
SetupThumbnails();
});
}
const AzToolsFramework::AssetBrowser::PreviewerFactory* EditorCommonFeaturesSystemComponent::GetPreviewerFactory(
const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const
{
@@ -199,7 +207,33 @@ namespace AZ
void EditorCommonFeaturesSystemComponent::OnApplicationAboutToStop()
{
TeardownThumbnails();
}
void EditorCommonFeaturesSystemComponent::SetupThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(
&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(SharedThumbnailCache),
ThumbnailContext::DefaultContext);
m_renderer = AZStd::make_unique<AZ::LyIntegration::SharedThumbnailRenderer>();
m_previewerFactory = AZStd::make_unique<LyIntegration::SharedPreviewerFactory>();
}
void EditorCommonFeaturesSystemComponent::TeardownThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(
&ThumbnailerRequests::UnregisterThumbnailProvider, SharedThumbnailCache::ProviderName,
ThumbnailContext::DefaultContext);
m_renderer.reset();
m_previewerFactory.reset();
}
} // namespace Render
} // namespace AZ
@@ -11,10 +11,10 @@
#include <AzCore/Component/Component.h>
#include <AzFramework/Application/Application.h>
#include <AzToolsFramework/API/EditorLevelNotificationBus.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <AzToolsFramework/AssetBrowser/Previewer/PreviewerBus.h>
#include <Thumbnails/Rendering/CommonThumbnailRenderer.h>
#include <Source/Thumbnails/Preview/CommonPreviewerFactory.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <SharedPreview/SharedPreviewerFactory.h>
#include <SharedPreview/SharedThumbnailRenderer.h>
namespace AZ
{
@@ -28,6 +28,7 @@ namespace AZ
, public AzToolsFramework::EditorLevelNotificationBus::Handler
, public AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler
, public AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler
, public AzFramework::AssetCatalogEventBus::Handler
, public AzFramework::ApplicationLifecycleEvents::Bus::Handler
{
public:
@@ -53,15 +54,23 @@ namespace AZ
void OnNewLevelCreated() override;
// SliceEditorEntityOwnershipServiceBus overrides ...
void OnSliceInstantiated(const AZ::Data::AssetId&, AZ::SliceComponent::SliceInstanceAddress&, const AzFramework::SliceInstantiationTicket&) override;
void OnSliceInstantiated(
const AZ::Data::AssetId&, AZ::SliceComponent::SliceInstanceAddress&, const AzFramework::SliceInstantiationTicket&) override;
void OnSliceInstantiationFailed(const AZ::Data::AssetId&, const AzFramework::SliceInstantiationTicket&) override;
// AzFramework::AssetCatalogEventBus::Handler overrides ...
void OnCatalogLoaded(const char* catalogFile) override;
// AzToolsFramework::AssetBrowser::PreviewerRequestBus::Handler overrides...
const AzToolsFramework::AssetBrowser::PreviewerFactory* GetPreviewerFactory(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override;
const AzToolsFramework::AssetBrowser::PreviewerFactory* GetPreviewerFactory(
const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry) const override;
// AzFramework::ApplicationLifecycleEvents overrides...
void OnApplicationAboutToStop() override;
void SetupThumbnails();
void TeardownThumbnails();
private:
AZStd::unique_ptr<SkinnedMeshDebugDisplay> m_skinnedMeshDebugDisplay;
@@ -69,8 +78,8 @@ namespace AZ
AZStd::string m_atomLevelDefaultAssetPath{ "LevelAssets/default.slice" };
float m_envProbeHeight{ 200.0f };
AZStd::unique_ptr<AZ::LyIntegration::Thumbnails::CommonThumbnailRenderer> m_renderer;
AZStd::unique_ptr<LyIntegration::CommonPreviewerFactory> m_previewerFactory;
AZStd::unique_ptr<AZ::LyIntegration::SharedThumbnailRenderer> m_renderer;
AZStd::unique_ptr<LyIntegration::SharedPreviewerFactory> m_previewerFactory;
};
} // namespace Render
} // namespace AZ
@@ -148,11 +148,13 @@ namespace AZ
BaseClass::Activate();
MaterialReceiverNotificationBus::Handler::BusConnect(GetEntityId());
MaterialComponentNotificationBus::Handler::BusConnect(GetEntityId());
EditorMaterialSystemComponentNotificationBus::Handler::BusConnect();
UpdateMaterialSlots();
}
void EditorMaterialComponent::Deactivate()
{
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
MaterialReceiverNotificationBus::Handler::BusDisconnect();
MaterialComponentNotificationBus::Handler::BusDisconnect();
BaseClass::Deactivate();
@@ -260,6 +262,18 @@ namespace AZ
}
}
void EditorMaterialComponent::OnRenderMaterialPreviewComplete(
[[maybe_unused]] const AZ::EntityId& entityId,
[[maybe_unused]] const AZ::Render::MaterialAssignmentId& materialAssignmentId,
[[maybe_unused]] const QPixmap& pixmap)
{
if (entityId == GetEntityId())
{
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues);
}
}
AZ::u32 EditorMaterialComponent::OnConfigurationChanged()
{
return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
@@ -9,6 +9,7 @@
#pragma once
#include <Atom/Feature/Utils/EditorRenderComponentAdapter.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <Material/EditorMaterialComponentSlot.h>
@@ -21,8 +22,9 @@ namespace AZ
//! In-editor material component for displaying and editing material assignments.
class EditorMaterialComponent final
: public EditorRenderComponentAdapter<MaterialComponentController, MaterialComponent, MaterialComponentConfig>
, private MaterialReceiverNotificationBus::Handler
, private MaterialComponentNotificationBus::Handler
, public MaterialReceiverNotificationBus::Handler
, public MaterialComponentNotificationBus::Handler
, public EditorMaterialSystemComponentNotificationBus::Handler
{
public:
using BaseClass = EditorRenderComponentAdapter<MaterialComponentController, MaterialComponent, MaterialComponentConfig>;
@@ -52,6 +54,10 @@ namespace AZ
//! MaterialComponentNotificationBus::Handler overrides...
void OnMaterialInstanceCreated(const MaterialAssignment& materialAssignment) override;
//! EditorMaterialSystemComponentNotificationBus::Handler overrides...
void OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap) override;
// Regenerates the editor component material slots based on the material and
// LOD mapping from the model or other consumer of materials.
// If any corresponding material assignments are found in the component
@@ -23,10 +23,6 @@
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/Thumbnails/ProductThumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <AzToolsFramework/Thumbnails/ThumbnailWidget.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConfig.h>
@@ -49,29 +45,18 @@ namespace AZ
MaterialPropertyInspector::MaterialPropertyInspector(QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
{
// Create the menu button
QToolButton* menuButton = new QToolButton(this);
menuButton->setAutoRaise(true);
menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg"));
menuButton->setVisible(true);
QObject::connect(menuButton, &QToolButton::clicked, this, [this]() { OpenMenu(); });
AddHeading(menuButton);
m_messageLabel = new QLabel(this);
m_messageLabel->setWordWrap(true);
m_messageLabel->setVisible(true);
m_messageLabel->setAlignment(Qt::AlignCenter);
m_messageLabel->setText(tr("Material not available"));
AddHeading(m_messageLabel);
CreateHeading();
AZ::TickBus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
EditorMaterialSystemComponentNotificationBus::Handler::BusConnect();
}
MaterialPropertyInspector::~MaterialPropertyInspector()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AZ::EntitySystemBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AZ::EntitySystemBus::Handler::BusDisconnect();
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
MaterialComponentNotificationBus::Handler::BusDisconnect();
}
@@ -140,7 +125,7 @@ namespace AZ
}
Populate();
m_messageLabel->setVisible(false);
LoadOverridesFromEntity();
return true;
}
@@ -152,8 +137,9 @@ namespace AZ
m_dirtyPropertyFlags.set();
m_editorFunctors = {};
m_internalEditNotification = {};
m_messageLabel->setVisible(true);
m_messageLabel->setText(tr("Material not available"));
m_updateUI = {};
m_updatePreview = {};
UpdateHeading();
}
bool MaterialPropertyInspector::IsLoaded() const
@@ -168,49 +154,63 @@ namespace AZ
m_dirtyPropertyFlags.set();
m_internalEditNotification = {};
AZ::TickBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
void MaterialPropertyInspector::AddDetailsGroup()
void MaterialPropertyInspector::CreateHeading()
{
const AZStd::string& groupName = "Details";
const AZStd::string& groupDisplayName = "Details";
const AZStd::string& groupDescription = "";
// Create the menu button
QToolButton* menuButton = new QToolButton(this);
menuButton->setAutoRaise(true);
menuButton->setIcon(QIcon(":/Cards/img/UI20/Cards/menu_ico.svg"));
menuButton->setVisible(true);
QObject::connect(menuButton, &QToolButton::clicked, this, [this]() { OpenMenu(); });
AddHeading(menuButton);
auto propertyGroupContainer = new QWidget(this);
propertyGroupContainer->setLayout(new QHBoxLayout());
m_overviewImage = new QLabel(this);
m_overviewImage->setFixedSize(QSize(120, 120));
m_overviewImage->setScaledContents(true);
m_overviewImage->setVisible(false);
AzToolsFramework::Thumbnailer::SharedThumbnailKey thumbnailKey =
MAKE_TKEY(AzToolsFramework::AssetBrowser::ProductThumbnailKey, m_editData.m_materialAssetId);
auto thumbnailWidget = new AzToolsFramework::Thumbnailer::ThumbnailWidget(this);
thumbnailWidget->setFixedSize(QSize(120, 120));
thumbnailWidget->setVisible(true);
thumbnailWidget->SetThumbnailKey(thumbnailKey, AzToolsFramework::Thumbnailer::ThumbnailContext::DefaultContext);
propertyGroupContainer->layout()->addWidget(thumbnailWidget);
auto materialInfoWidget = new QLabel(this);
m_overviewText = new QLabel(this);
QSizePolicy sizePolicy1(QSizePolicy::Ignored, QSizePolicy::Preferred);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(materialInfoWidget->sizePolicy().hasHeightForWidth());
materialInfoWidget->setSizePolicy(sizePolicy1);
materialInfoWidget->setMinimumSize(QSize(0, 0));
materialInfoWidget->setMaximumSize(QSize(16777215, 16777215));
materialInfoWidget->setTextFormat(Qt::AutoText);
materialInfoWidget->setScaledContents(false);
materialInfoWidget->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop);
materialInfoWidget->setWordWrap(true);
sizePolicy1.setHeightForWidth(m_overviewText->sizePolicy().hasHeightForWidth());
m_overviewText->setSizePolicy(sizePolicy1);
m_overviewText->setMinimumSize(QSize(0, 0));
m_overviewText->setMaximumSize(QSize(16777215, 16777215));
m_overviewText->setTextFormat(Qt::AutoText);
m_overviewText->setScaledContents(false);
m_overviewText->setWordWrap(true);
m_overviewText->setVisible(true);
auto overviewContainer = new QWidget(this);
overviewContainer->setLayout(new QHBoxLayout());
overviewContainer->layout()->addWidget(m_overviewImage);
overviewContainer->layout()->addWidget(m_overviewText);
AddHeading(overviewContainer);
}
void MaterialPropertyInspector::UpdateHeading()
{
if (!IsLoaded())
{
m_overviewText->setText(tr("Material not available"));
m_overviewText->setAlignment(Qt::AlignCenter);
m_overviewImage->setVisible(false);
return;
}
QFileInfo materialFileInfo(AZ::RPI::AssetUtils::GetProductPathByAssetId(m_editData.m_materialAsset.GetId()).c_str());
QFileInfo materialSourceFileInfo(m_editData.m_materialSourcePath.c_str());
QFileInfo materialTypeSourceFileInfo(m_editData.m_materialTypeSourcePath.c_str());
QFileInfo materialParentSourceFileInfo(AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_editData.m_materialParentAsset.GetId()).c_str());
QFileInfo materialParentSourceFileInfo(
AZ::RPI::AssetUtils::GetSourcePathByAssetId(m_editData.m_materialParentAsset.GetId()).c_str());
AZStd::string entityName;
AZ::ComponentApplicationBus::BroadcastResult(
entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_entityId);
AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, m_entityId);
AZStd::string slotName;
MaterialComponentRequestBus::EventResult(
@@ -226,7 +226,8 @@ namespace AZ
}
if (!materialTypeSourceFileInfo.fileName().isEmpty())
{
materialInfo += tr("<tr><td><b>Material Type&emsp;</b></td><td>%1</td></tr>").arg(materialTypeSourceFileInfo.fileName());
materialInfo +=
tr("<tr><td><b>Material Type&emsp;</b></td><td>%1</td></tr>").arg(materialTypeSourceFileInfo.fileName());
}
if (!materialSourceFileInfo.fileName().isEmpty())
{
@@ -234,14 +235,21 @@ namespace AZ
}
if (!materialParentSourceFileInfo.fileName().isEmpty())
{
materialInfo += tr("<tr><td><b>Material Parent&emsp;</b></td><td>%1</td></tr>").arg(materialParentSourceFileInfo.fileName());
materialInfo +=
tr("<tr><td><b>Material Parent&emsp;</b></td><td>%1</td></tr>").arg(materialParentSourceFileInfo.fileName());
}
materialInfo += tr("</table>");
materialInfoWidget->setText(materialInfo);
propertyGroupContainer->layout()->addWidget(materialInfoWidget);
m_overviewText->setText(materialInfo);
m_overviewText->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop);
AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupContainer);
QPixmap pixmap;
EditorMaterialSystemComponentRequestBus::BroadcastResult(
pixmap, &EditorMaterialSystemComponentRequestBus::Events::GetRenderedMaterialPreview, m_entityId,
m_materialAssignmentId);
m_overviewImage->setPixmap(pixmap);
m_overviewImage->setVisible(true);
m_updatePreview |= pixmap.isNull();
}
void MaterialPropertyInspector::AddUvNamesGroup()
@@ -282,13 +290,8 @@ namespace AZ
AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget);
}
void MaterialPropertyInspector::Populate()
void MaterialPropertyInspector::AddPropertiesGroup()
{
AddGroupsBegin();
AddDetailsGroup();
AddUvNamesGroup();
// Copy all of the properties from the material asset to the source data that will be exported
for (const auto& groupDefinition : m_editData.m_materialTypeSourceData.GetGroupDefinitionsInDisplayOrder())
{
@@ -327,10 +330,14 @@ namespace AZ
[this](const auto node) { return GetInstanceNodePropertyIndicator(node); }, 0);
AddGroup(groupName, groupDisplayName, groupDescription, propertyGroupWidget);
}
}
void MaterialPropertyInspector::Populate()
{
AddGroupsBegin();
AddUvNamesGroup();
AddPropertiesGroup();
AddGroupsEnd();
LoadOverridesFromEntity();
}
void MaterialPropertyInspector::LoadOverridesFromEntity()
@@ -375,6 +382,7 @@ namespace AZ
m_dirtyPropertyFlags.set();
RunEditorMaterialFunctors();
RebuildAll();
UpdateHeading();
}
void MaterialPropertyInspector::SaveOverridesToEntity(bool commitChanges)
@@ -398,6 +406,9 @@ namespace AZ
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited);
m_internalEditNotification = false;
}
// m_updatePreview should be set to true here for continuous preview updates as slider/color properties change but needs
// throttling
}
void MaterialPropertyInspector::RunEditorMaterialFunctors()
@@ -607,7 +618,8 @@ namespace AZ
MaterialComponentRequestBus::Event(
m_entityId, &MaterialComponentRequestBus::Events::SetPropertyOverrides, m_materialAssignmentId,
MaterialPropertyOverrideMap());
QueueUpdateUI();
m_updateUI = true;
m_updatePreview = true;
});
action->setEnabled(IsLoaded());
@@ -702,10 +714,7 @@ namespace AZ
void MaterialPropertyInspector::OnEntityActivated(const AZ::EntityId& entityId)
{
if (m_entityId == entityId)
{
QueueUpdateUI();
}
m_updateUI |= (m_entityId == entityId);
}
void MaterialPropertyInspector::OnEntityDeactivated(const AZ::EntityId& entityId)
@@ -719,25 +728,39 @@ namespace AZ
void MaterialPropertyInspector::OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name)
{
AZ_UNUSED(name);
if (m_entityId == entityId)
{
QueueUpdateUI();
}
m_updateUI |= (m_entityId == entityId);
}
void MaterialPropertyInspector::OnTick(float deltaTime, ScriptTimePoint time)
{
AZ_UNUSED(time);
AZ_UNUSED(deltaTime);
UpdateUI();
AZ::TickBus::Handler::BusDisconnect();
if (m_updateUI)
{
m_updateUI = false;
UpdateUI();
}
if (m_updatePreview)
{
m_updatePreview = false;
EditorMaterialSystemComponentRequestBus::Broadcast(
&EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_materialAssignmentId);
}
}
void MaterialPropertyInspector::OnMaterialsEdited()
{
if (!m_internalEditNotification)
m_updateUI |= !m_internalEditNotification;
m_updatePreview = true;
}
void MaterialPropertyInspector::OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap)
{
if (m_overviewImage && m_entityId == entityId && m_materialAssignmentId == materialAssignmentId)
{
QueueUpdateUI();
m_overviewImage->setPixmap(pixmap);
}
}
@@ -761,16 +784,6 @@ namespace AZ
LoadMaterial(m_entityId, m_materialAssignmentId);
}
}
void MaterialPropertyInspector::QueueUpdateUI()
{
if (!AZ::TickBus::Handler::BusIsConnected())
{
AZ::TickBus::Handler::BusConnect();
}
}
} // namespace EditorMaterialComponentInspector
} // namespace Render
} // namespace AZ
//#include <AtomLyIntegration/CommonFeatures/moc_EditorMaterialComponentInspector.cpp>
@@ -9,6 +9,7 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
@@ -31,14 +32,13 @@ namespace AZ
{
namespace EditorMaterialComponentInspector
{
using PropertyChangedCallback = AZStd::function<void(const MaterialPropertyOverrideMap&)>;
class MaterialPropertyInspector
: public AtomToolsFramework::InspectorWidget
, public AzToolsFramework::IPropertyEditorNotify
, public AZ::EntitySystemBus::Handler
, public AZ::TickBus::Handler
, public MaterialComponentNotificationBus::Handler
, public EditorMaterialSystemComponentNotificationBus::Handler
{
Q_OBJECT
public:
@@ -89,11 +89,19 @@ namespace AZ
//! MaterialComponentNotificationBus::Handler overrides...
void OnMaterialsEdited() override;
void UpdateUI();
void QueueUpdateUI();
//! EditorMaterialSystemComponentNotificationBus::Handler overrides...
void OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId,
const AZ::Render::MaterialAssignmentId& materialAssignmentId,
const QPixmap& pixmap) override;
void UpdateUI();
void CreateHeading();
void UpdateHeading();
void AddDetailsGroup();
void AddUvNamesGroup();
void AddPropertiesGroup();
void LoadOverridesFromEntity();
void SaveOverridesToEntity(bool commitChanges);
@@ -115,7 +123,10 @@ namespace AZ
AZ::RPI::MaterialPropertyFlags m_dirtyPropertyFlags = {};
AZStd::unordered_map<AZStd::string, AtomToolsFramework::DynamicPropertyGroup> m_groups = {};
bool m_internalEditNotification = {};
QLabel* m_messageLabel = {};
bool m_updateUI = {};
bool m_updatePreview = {};
QLabel* m_overviewText = {};
QLabel* m_overviewImage = {};
};
} // namespace EditorMaterialComponentInspector
} // namespace Render
@@ -6,23 +6,25 @@
*
*/
#include <Material/EditorMaterialComponentSlot.h>
#include <Material/EditorMaterialComponentExporter.h>
#include <Material/EditorMaterialComponentInspector.h>
#include <Material/EditorMaterialModelUvNameMapInspector.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <Material/EditorMaterialComponentExporter.h>
#include <Material/EditorMaterialComponentInspector.h>
#include <Material/EditorMaterialComponentSlot.h>
#include <Material/EditorMaterialModelUvNameMapInspector.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QMenu>
#include <QAction>
#include <QAction>
#include <QByteArray>
#include <QCursor>
#include <QDataStream>
#include <QMenu>
AZ_POP_DISABLE_WARNING
namespace AZ
@@ -100,6 +102,7 @@ namespace AZ
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &EditorMaterialComponentSlot::GetLabel)
->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true)
->Attribute("ThumbnailCallback", &EditorMaterialComponentSlot::OpenPopupMenu)
->Attribute("ThumbnailIcon", &EditorMaterialComponentSlot::GetPreviewPixmapData)
;
}
}
@@ -118,6 +121,33 @@ namespace AZ
}
};
AZStd::vector<char> EditorMaterialComponentSlot::GetPreviewPixmapData() const
{
if (!GetActiveAssetId().IsValid())
{
return {};
}
QPixmap pixmap;
EditorMaterialSystemComponentRequestBus::BroadcastResult(
pixmap, &EditorMaterialSystemComponentRequestBus::Events::GetRenderedMaterialPreview, m_entityId, m_id);
if (pixmap.isNull())
{
if (m_updatePreview)
{
EditorMaterialSystemComponentRequestBus::Broadcast(
&EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_id);
m_updatePreview = false;
}
return {};
}
QByteArray pixmapBytes;
QDataStream stream(&pixmapBytes, QIODevice::WriteOnly);
stream << pixmap;
return AZStd::vector<char>(pixmapBytes.begin(), pixmapBytes.end());
}
AZ::Data::AssetId EditorMaterialComponentSlot::GetActiveAssetId() const
{
return m_materialAsset.GetId().IsValid() ? m_materialAsset.GetId() : GetDefaultAssetId();
@@ -169,14 +199,6 @@ namespace AZ
ClearOverrides();
}
void EditorMaterialComponentSlot::ClearToDefaultAsset()
{
m_materialAsset = AZ::Data::Asset<AZ::RPI::MaterialAsset>(GetDefaultAssetId(), AZ::AzTypeInfo<AZ::RPI::MaterialAsset>::Uuid());
MaterialComponentRequestBus::Event(
m_entityId, &MaterialComponentRequestBus::Events::SetMaterialOverride, m_id, m_materialAsset.GetId());
ClearOverrides();
}
void EditorMaterialComponentSlot::ClearOverrides()
{
MaterialComponentRequestBus::Event(
@@ -315,6 +337,10 @@ namespace AZ
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, m_entityId);
EditorMaterialSystemComponentRequestBus::Broadcast(
&EditorMaterialSystemComponentRequestBus::Events::RenderMaterialPreview, m_entityId, m_id);
m_updatePreview = false;
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsEdited);
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
@@ -8,37 +8,52 @@
#pragma once
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Asset/AssetCommon.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/Feature/Material/MaterialAssignment.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <QPixmap>
namespace AZ
{
namespace Render
{
static const size_t DefaultMaterialSlotIndex = std::numeric_limits<size_t>::max();
//! Details for a single editable material assignment
struct EditorMaterialComponentSlot final
{
AZ_RTTI(EditorMaterialComponentSlot, "{344066EB-7C3D-4E92-B53D-3C9EBD546488}");
AZ_CLASS_ALLOCATOR(EditorMaterialComponentSlot, SystemAllocator, 0);
static void Reflect(ReflectContext* context);
static bool ConvertVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
static void Reflect(ReflectContext* context);
//! Get cached preview image as a buffer to use as an RPE attribute
//! If a cached image isn't avalible then a request will be made to render one
AZStd::vector<char> GetPreviewPixmapData() const;
//! Returns the overridden asset id if it's valid, otherwise gets the default asseet id
AZ::Data::AssetId GetActiveAssetId() const;
//! Returns the default asseet id of the material provded by the model
AZ::Data::AssetId GetDefaultAssetId() const;
//! Returns the display name of the material slot
AZStd::string GetLabel() const;
//! Returns true if the active material asset has a source material
bool HasSourceData() const;
//! Assign a new material override asset
void SetAsset(const Data::AssetId& assetId);
//! Assign a new material override asset
void SetAsset(const Data::Asset<RPI::MaterialAsset>& asset);
//! Remove material and prperty overrides
void Clear();
void ClearToDefaultAsset();
//! Remove prperty overrides
void ClearOverrides();
void OpenMaterialExporter();
@@ -54,6 +69,7 @@ namespace AZ
void OpenPopupMenu(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType);
void OnMaterialChanged() const;
void OnDataChanged() const;
mutable bool m_updatePreview = true;
};
// Vector of slots for assignable or overridable material data.
@@ -62,8 +78,8 @@ namespace AZ
// Table containing all editable material data that is displayed in the edit context and inspector
// The vector represents all the LODs that can have material overrides.
// The container will be populated with every potential material slot on an associated model, using its default values.
// Whenever changes are made to this container, the modified values are copied into the controller configuration material assignment map
// as overrides that will be applied to material instances
// Whenever changes are made to this container, the modified values are copied into the controller configuration material assignment
// map as overrides that will be applied to material instances
using EditorMaterialComponentSlotsByLodContainer = AZStd::vector<EditorMaterialComponentSlotContainer>;
} // namespace Render
} // namespace AZ
@@ -7,6 +7,11 @@
*/
#include <Atom/RHI/Factory.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentNotificationBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererCaptureRequest.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererInterface.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
@@ -16,11 +21,10 @@
#include <AzFramework/Application/Application.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ViewPaneOptions.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <Editor/LyViewPaneNames.h>
#include <Material/EditorMaterialComponentInspector.h>
#include <Material/EditorMaterialSystemComponent.h>
#include <Material/MaterialThumbnail.h>
#include <SharedPreview/SharedPreviewContent.h>
// Disables warning messages triggered by the Qt library
// 4251: class needs to have dll-interface to be used by clients of class
@@ -30,6 +34,8 @@ AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QApplication>
#include <QDockWidget>
#include <QObject>
#include <QPixmap>
#include <QImage>
#include <QProcessEnvironment>
AZ_POP_DISABLE_WARNING
@@ -55,7 +61,7 @@ namespace AZ
{
ec->Class<EditorMaterialSystemComponent>("EditorMaterialSystemComponent", "System component that manages launching and maintaining connections the material editor.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
@@ -64,17 +70,17 @@ namespace AZ
void EditorMaterialSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("EditorMaterialSystem", 0x5c93bc4e));
provided.push_back(AZ_CRC_CE("EditorMaterialSystem"));
}
void EditorMaterialSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("EditorMaterialSystem", 0x5c93bc4e));
incompatible.push_back(AZ_CRC_CE("EditorMaterialSystem"));
}
void EditorMaterialSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("ThumbnailerService", 0x65422b97));
required.push_back(AZ_CRC_CE("PreviewRendererSystem"));
}
void EditorMaterialSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
@@ -89,25 +95,23 @@ namespace AZ
void EditorMaterialSystemComponent::Activate()
{
EditorMaterialSystemComponentNotificationBus::Handler::BusConnect();
EditorMaterialSystemComponentRequestBus::Handler::BusConnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorMenuNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
SetupThumbnails();
m_materialBrowserInteractions.reset(aznew MaterialBrowserInteractions);
}
void EditorMaterialSystemComponent::Deactivate()
{
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
EditorMaterialSystemComponentRequestBus::Handler::BusDisconnect();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorMenuNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
TeardownThumbnails();
m_materialBrowserInteractions.reset();
if (m_openMaterialEditorAction)
@@ -154,11 +158,76 @@ namespace AZ
}
}
void EditorMaterialSystemComponent::OnApplicationAboutToStop()
void EditorMaterialSystemComponent::RenderMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId)
{
TeardownThumbnails();
static constexpr const char* DefaultModelPath = "models/sphere.azmodel";
static constexpr const char* DefaultLightingPresetPath = "lightingpresets/thumbnail.lightingpreset.azasset";
if (auto previewRenderer = AZ::Interface<AtomToolsFramework::PreviewRendererInterface>::Get())
{
AZ::Data::AssetId materialAssetId = {};
MaterialComponentRequestBus::EventResult(
materialAssetId, entityId, &MaterialComponentRequestBus::Events::GetMaterialOverride, materialAssignmentId);
if (!materialAssetId.IsValid())
{
MaterialComponentRequestBus::EventResult(
materialAssetId, entityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId, materialAssignmentId);
if (!materialAssetId.IsValid())
{
return;
}
}
AZ::Render::MaterialPropertyOverrideMap propertyOverrides;
AZ::Render::MaterialComponentRequestBus::EventResult(
propertyOverrides, entityId, &AZ::Render::MaterialComponentRequestBus::Events::GetPropertyOverrides,
materialAssignmentId);
previewRenderer->AddCaptureRequest(
{ 128,
AZStd::make_shared<AZ::LyIntegration::SharedPreviewContent>(
previewRenderer->GetScene(), previewRenderer->GetView(), previewRenderer->GetEntityContextId(),
AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultModelPath), materialAssetId,
AZ::RPI::AssetUtils::GetAssetIdForProductPath(DefaultLightingPresetPath), propertyOverrides),
[entityId, materialAssignmentId]()
{
AZ_Warning(
"EditorMaterialSystemComponent", false, "RenderMaterialPreview capture failed for entity %s slot %s.",
entityId.ToString().c_str(), materialAssignmentId.ToString().c_str());
},
[entityId, materialAssignmentId](const QPixmap& pixmap)
{
AZ::Render::EditorMaterialSystemComponentNotificationBus::Broadcast(
&AZ::Render::EditorMaterialSystemComponentNotificationBus::Events::OnRenderMaterialPreviewComplete, entityId,
materialAssignmentId, pixmap);
} });
}
}
QPixmap EditorMaterialSystemComponent::GetRenderedMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const
{
const auto& itr1 = m_materialPreviews.find(entityId);
if (itr1 != m_materialPreviews.end())
{
const auto& itr2 = itr1->second.find(materialAssignmentId);
if (itr2 != itr1->second.end())
{
return itr2->second;
}
}
return QPixmap();
}
void EditorMaterialSystemComponent::OnRenderMaterialPreviewComplete(
[[maybe_unused]] const AZ::EntityId& entityId,
[[maybe_unused]] const AZ::Render::MaterialAssignmentId& materialAssignmentId,
[[maybe_unused]] const QPixmap& pixmap)
{
m_materialPreviews[entityId][materialAssignmentId] = pixmap;
}
void EditorMaterialSystemComponent::OnPopulateToolMenuItems()
{
if (!m_openMaterialEditorAction)
@@ -201,26 +270,6 @@ namespace AZ
"Material Property Inspector", LyViewPane::CategoryTools, inspectorOptions);
}
void EditorMaterialSystemComponent::SetupThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(
&ThumbnailerRequests::RegisterThumbnailProvider, MAKE_TCACHE(Thumbnails::MaterialThumbnailCache),
ThumbnailContext::DefaultContext);
}
void EditorMaterialSystemComponent::TeardownThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(
&ThumbnailerRequests::UnregisterThumbnailProvider, Thumbnails::MaterialThumbnailCache::ProviderName,
ThumbnailContext::DefaultContext);
}
AzToolsFramework::AssetBrowser::SourceFileDetails EditorMaterialSystemComponent::GetSourceFileDetails(
const char* fullSourceFileName)
{
@@ -5,31 +5,30 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzFramework/Application/Application.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Viewport/ActionBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <Material/MaterialBrowserInteractions.h>
#include <QPixmap>
namespace AZ
{
namespace Render
{
//! System component that manages launching and maintaining connections with the material editor.
class EditorMaterialSystemComponent
class EditorMaterialSystemComponent final
: public AZ::Component
, private EditorMaterialSystemComponentRequestBus::Handler
, private AzFramework::ApplicationLifecycleEvents::Bus::Handler
, private AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
, private AzToolsFramework::EditorMenuNotificationBus::Handler
, private AzToolsFramework::EditorEvents::Bus::Handler
, public EditorMaterialSystemComponentNotificationBus::Handler
, public EditorMaterialSystemComponentRequestBus::Handler
, public AzToolsFramework::AssetBrowser::AssetBrowserInteractionNotificationBus::Handler
, public AzToolsFramework::EditorMenuNotificationBus::Handler
, public AzToolsFramework::EditorEvents::Bus::Handler
{
public:
AZ_COMPONENT(EditorMaterialSystemComponent, "{96652157-DA0B-420F-B49C-0207C585144C}");
@@ -51,9 +50,13 @@ namespace AZ
//! EditorMaterialSystemComponentRequestBus::Handler overrides...
void OpenMaterialEditor(const AZStd::string& sourcePath) override;
void OpenMaterialInspector(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override;
void RenderMaterialPreview(const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) override;
QPixmap GetRenderedMaterialPreview(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId) const override;
// AzFramework::ApplicationLifecycleEvents overrides...
void OnApplicationAboutToStop() override;
//! EditorMaterialSystemComponentNotificationBus::Handler overrides...
void OnRenderMaterialPreviewComplete(
const AZ::EntityId& entityId, const AZ::Render::MaterialAssignmentId& materialAssignmentId, const QPixmap& pixmap)override;
//! AssetBrowserInteractionNotificationBus::Handler overrides...
AzToolsFramework::AssetBrowser::SourceFileDetails GetSourceFileDetails(const char* fullSourceFileName) override;
@@ -65,12 +68,9 @@ namespace AZ
// AztoolsFramework::EditorEvents::Bus::Handler overrides...
void NotifyRegisterViews() override;
void SetupThumbnails();
void TeardownThumbnails();
QAction* m_openMaterialEditorAction = nullptr;
AZStd::unique_ptr<MaterialBrowserInteractions> m_materialBrowserInteractions;
AZStd::unordered_map<AZ::EntityId, AZStd::unordered_map<AZ::Render::MaterialAssignmentId, QPixmap>> m_materialPreviews;
};
} // namespace Render
} // namespace AZ
@@ -1,112 +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 <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <QtConcurrent/QtConcurrent>
#include <Source/Material/MaterialThumbnail.h>
#include <Source/Thumbnails/ThumbnailUtils.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
static constexpr const int MaterialThumbnailSize = 512; // 512 is the default size in render to texture pass
//////////////////////////////////////////////////////////////////////////
// MaterialThumbnail
//////////////////////////////////////////////////////////////////////////
MaterialThumbnail::MaterialThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key)
: Thumbnail(key)
{
m_assetId = GetAssetId(key, RPI::MaterialAsset::RTTI_Type());
if (!m_assetId.IsValid())
{
AZ_Error("MaterialThumbnail", false, "Failed to find matching assetId for the thumbnailKey.");
m_state = State::Failed;
return;
}
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusConnect(key);
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
}
void MaterialThumbnail::LoadThread()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent(
RPI::MaterialAsset::RTTI_Type(),
&AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail,
m_key,
MaterialThumbnailSize);
// wait for response from thumbnail renderer
m_renderWait.acquire();
}
MaterialThumbnail::~MaterialThumbnail()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void MaterialThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage)
{
m_pixmap = thumbnailImage;
m_renderWait.release();
}
void MaterialThumbnail::ThumbnailFailedToRender()
{
m_state = State::Failed;
m_renderWait.release();
}
void MaterialThumbnail::OnCatalogAssetChanged([[maybe_unused]] const AZ::Data::AssetId& assetId)
{
if (m_assetId == assetId &&
m_state == State::Ready)
{
m_state = State::Unloaded;
Load();
}
}
//////////////////////////////////////////////////////////////////////////
// MaterialThumbnailCache
//////////////////////////////////////////////////////////////////////////
MaterialThumbnailCache::MaterialThumbnailCache()
: ThumbnailCache<MaterialThumbnail>()
{
}
MaterialThumbnailCache::~MaterialThumbnailCache() = default;
int MaterialThumbnailCache::GetPriority() const
{
// Material thumbnails override default source thumbnails, so carry higher priority
return 1;
}
const char* MaterialThumbnailCache::GetProviderName() const
{
return ProviderName;
}
bool MaterialThumbnailCache::IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const
{
return
GetAssetId(key, RPI::MaterialAsset::RTTI_Type()).IsValid() &&
// in case it's a source scene file, it will contain both material and model products
// model thumbnails are handled by MeshThumbnail
!GetAssetId(key, RPI::ModelAsset::RTTI_Type()).IsValid();
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
#include <Material/moc_MaterialThumbnail.cpp>
@@ -1,73 +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/std/parallel/binary_semaphore.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <Thumbnails/Rendering/CommonThumbnailRenderer.h>
#endif
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
/**
* Custom material or model thumbnail that detects when an asset changes and updates the thumbnail
*/
class MaterialThumbnail
: public AzToolsFramework::Thumbnailer::Thumbnail
, public AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
{
Q_OBJECT
public:
MaterialThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key);
~MaterialThumbnail() override;
//! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides...
void ThumbnailRendered(const QPixmap& thumbnailImage) override;
void ThumbnailFailedToRender() override;
protected:
void LoadThread() override;
private:
// AzFramework::AssetCatalogEventBus::Handler interface overrides...
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
AZStd::binary_semaphore m_renderWait;
Data::AssetId m_assetId;
};
/**
* Cache configuration for large material thumbnails
*/
class MaterialThumbnailCache
: public AzToolsFramework::Thumbnailer::ThumbnailCache<MaterialThumbnail>
{
public:
MaterialThumbnailCache();
~MaterialThumbnailCache() override;
int GetPriority() const override;
const char* GetProviderName() const override;
static constexpr const char* ProviderName = "Material Thumbnails";
protected:
bool IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const override;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -6,13 +6,10 @@
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Utils/Utils.h>
#include <AzToolsFramework/Thumbnails/ThumbnailContext.h>
#include <Source/Mesh/EditorMeshSystemComponent.h>
#include <Source/Mesh/MeshThumbnail.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Mesh/EditorMeshSystemComponent.h>
namespace AZ
{
@@ -47,11 +44,6 @@ namespace AZ
incompatible.push_back(AZ_CRC_CE("EditorMeshSystem"));
}
void EditorMeshSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC_CE("ThumbnailerService"));
}
void EditorMeshSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
AZ_UNUSED(dependent);
@@ -59,39 +51,10 @@ namespace AZ
void EditorMeshSystemComponent::Activate()
{
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
SetupThumbnails();
}
void EditorMeshSystemComponent::Deactivate()
{
TeardownThumbnails();
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
}
void EditorMeshSystemComponent::OnApplicationAboutToStop()
{
TeardownThumbnails();
}
void EditorMeshSystemComponent::SetupThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(&ThumbnailerRequests::RegisterThumbnailProvider,
MAKE_TCACHE(Thumbnails::MeshThumbnailCache),
ThumbnailContext::DefaultContext);
}
void EditorMeshSystemComponent::TeardownThumbnails()
{
using namespace AzToolsFramework::Thumbnailer;
using namespace LyIntegration;
ThumbnailerRequestsBus::Broadcast(&ThumbnailerRequests::UnregisterThumbnailProvider,
Thumbnails::MeshThumbnailCache::ProviderName,
ThumbnailContext::DefaultContext);
}
} // namespace Render
} // namespace AZ
@@ -8,7 +8,6 @@
#pragma once
#include <AzCore/Component/Component.h>
#include <AzFramework/Application/Application.h>
namespace AZ
{
@@ -17,7 +16,6 @@ namespace AZ
//! System component that sets up necessary logic related to EditorMeshComponent.
class EditorMeshSystemComponent
: public AZ::Component
, private AzFramework::ApplicationLifecycleEvents::Bus::Handler
{
public:
AZ_COMPONENT(EditorMeshSystemComponent, "{4D332E3D-C4FC-410B-A915-8E234CBDD4EC}");
@@ -26,20 +24,12 @@ namespace AZ
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
protected:
// AZ::Component interface overrides...
void Activate() override;
void Deactivate() override;
private:
// AzFramework::ApplicationLifecycleEvents overrides...
void OnApplicationAboutToStop() override;
void SetupThumbnails();
void TeardownThumbnails();
};
} // namespace Render
} // namespace AZ
@@ -1,109 +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 <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <QtConcurrent/QtConcurrent>
#include <Source/Mesh/MeshThumbnail.h>
#include <Source/Thumbnails/ThumbnailUtils.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
static constexpr const int MeshThumbnailSize = 512; // 512 is the default size in render to texture pass
//////////////////////////////////////////////////////////////////////////
// MeshThumbnail
//////////////////////////////////////////////////////////////////////////
MeshThumbnail::MeshThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key)
: Thumbnail(key)
{
m_assetId = GetAssetId(key, RPI::ModelAsset::RTTI_Type());
if (!m_assetId.IsValid())
{
AZ_Error("MeshThumbnail", false, "Failed to find matching assetId for the thumbnailKey.");
m_state = State::Failed;
return;
}
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusConnect(key);
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
}
void MeshThumbnail::LoadThread()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::QueueEvent(
RPI::ModelAsset::RTTI_Type(),
&AzToolsFramework::Thumbnailer::ThumbnailerRendererRequests::RenderThumbnail,
m_key,
MeshThumbnailSize);
// wait for response from thumbnail renderer
m_renderWait.acquire();
}
MeshThumbnail::~MeshThumbnail()
{
AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler::BusDisconnect();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void MeshThumbnail::ThumbnailRendered(const QPixmap& thumbnailImage)
{
m_pixmap = thumbnailImage;
m_renderWait.release();
}
void MeshThumbnail::ThumbnailFailedToRender()
{
m_state = State::Failed;
m_renderWait.release();
}
void MeshThumbnail::OnCatalogAssetChanged([[maybe_unused]] const AZ::Data::AssetId& assetId)
{
if (m_assetId == assetId &&
m_state == State::Ready)
{
m_state = State::Unloaded;
Load();
}
}
//////////////////////////////////////////////////////////////////////////
// MeshThumbnailCache
//////////////////////////////////////////////////////////////////////////
MeshThumbnailCache::MeshThumbnailCache()
: ThumbnailCache<MeshThumbnail>()
{
}
MeshThumbnailCache::~MeshThumbnailCache() = default;
int MeshThumbnailCache::GetPriority() const
{
// Material thumbnails override default source thumbnails, so carry higher priority
return 1;
}
const char* MeshThumbnailCache::GetProviderName() const
{
return ProviderName;
}
bool MeshThumbnailCache::IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const
{
return GetAssetId(key, RPI::ModelAsset::RTTI_Type()).IsValid();
}
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
#include <Mesh/moc_MeshThumbnail.cpp>
@@ -1,72 +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/std/parallel/binary_semaphore.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#endif
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
{
/**
* Custom material or model thumbnail that detects when an asset changes and updates the thumbnail
*/
class MeshThumbnail
: public AzToolsFramework::Thumbnailer::Thumbnail
, public AzToolsFramework::Thumbnailer::ThumbnailerRendererNotificationBus::Handler
, private AzFramework::AssetCatalogEventBus::Handler
{
Q_OBJECT
public:
MeshThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key);
~MeshThumbnail() override;
//! AzToolsFramework::ThumbnailerRendererNotificationBus::Handler overrides...
void ThumbnailRendered(const QPixmap& thumbnailImage) override;
void ThumbnailFailedToRender() override;
protected:
void LoadThread() override;
private:
// AzFramework::AssetCatalogEventBus::Handler interface overrides...
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
AZStd::binary_semaphore m_renderWait;
Data::AssetId m_assetId;
};
/**
* Cache configuration for large material thumbnails
*/
class MeshThumbnailCache
: public AzToolsFramework::Thumbnailer::ThumbnailCache<MeshThumbnail>
{
public:
MeshThumbnailCache();
~MeshThumbnailCache() override;
int GetPriority() const override;
const char* GetProviderName() const override;
static constexpr const char* ProviderName = "Mesh Thumbnails";
protected:
bool IsSupportedThumbnail(AzToolsFramework::Thumbnailer::SharedThumbnailKey key) const override;
};
} // namespace Thumbnails
} // namespace LyIntegration
} // namespace AZ
@@ -0,0 +1,173 @@
/*
* 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/Feature/ImageBasedLights/ImageBasedLightFeatureProcessorInterface.h>
#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
#include <Atom/Feature/Utils/LightingPreset.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Transform.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <SharedPreview/SharedPreviewContent.h>
namespace AZ
{
namespace LyIntegration
{
SharedPreviewContent::SharedPreviewContent(
RPI::ScenePtr scene,
RPI::ViewPtr view,
AZ::Uuid entityContextId,
const Data::AssetId& modelAssetId,
const Data::AssetId& materialAssetId,
const Data::AssetId& lightingPresetAssetId,
const Render::MaterialPropertyOverrideMap& materialPropertyOverrides)
: m_scene(scene)
, m_view(view)
, m_entityContextId(entityContextId)
, m_materialPropertyOverrides(materialPropertyOverrides)
{
// Create preview model
AzFramework::EntityContextRequestBus::EventResult(
m_modelEntity, m_entityContextId, &AzFramework::EntityContextRequestBus::Events::CreateEntity, "SharedPreviewContentModel");
m_modelEntity->CreateComponent(Render::MeshComponentTypeId);
m_modelEntity->CreateComponent(Render::MaterialComponentTypeId);
m_modelEntity->CreateComponent(azrtti_typeid<AzFramework::TransformComponent>());
m_modelEntity->Init();
m_modelEntity->Activate();
m_modelAsset.Create(modelAssetId);
m_materialAsset.Create(materialAssetId);
m_lightingPresetAsset.Create(lightingPresetAssetId);
}
SharedPreviewContent::~SharedPreviewContent()
{
if (m_modelEntity)
{
m_modelEntity->Deactivate();
AzFramework::EntityContextRequestBus::Event(
m_entityContextId, &AzFramework::EntityContextRequestBus::Events::DestroyEntity, m_modelEntity);
m_modelEntity = nullptr;
}
}
void SharedPreviewContent::Load()
{
m_modelAsset.QueueLoad();
m_materialAsset.QueueLoad();
m_lightingPresetAsset.QueueLoad();
}
bool SharedPreviewContent::IsReady() const
{
return (!m_modelAsset.GetId().IsValid() || m_modelAsset.IsReady()) &&
(!m_materialAsset.GetId().IsValid() || m_materialAsset.IsReady()) &&
(!m_lightingPresetAsset.GetId().IsValid() || m_lightingPresetAsset.IsReady());
}
bool SharedPreviewContent::IsError() const
{
return m_modelAsset.IsError() || m_materialAsset.IsError() || m_lightingPresetAsset.IsError();
}
void SharedPreviewContent::ReportErrors()
{
AZ_Warning(
"SharedPreviewContent", !m_modelAsset.GetId().IsValid() || m_modelAsset.IsReady(), "Asset failed to load in time: %s",
m_modelAsset.ToString<AZStd::string>().c_str());
AZ_Warning(
"SharedPreviewContent", !m_materialAsset.GetId().IsValid() || m_materialAsset.IsReady(), "Asset failed to load in time: %s",
m_materialAsset.ToString<AZStd::string>().c_str());
AZ_Warning(
"SharedPreviewContent", !m_lightingPresetAsset.GetId().IsValid() || m_lightingPresetAsset.IsReady(),
"Asset failed to load in time: %s", m_lightingPresetAsset.ToString<AZStd::string>().c_str());
}
void SharedPreviewContent::Update()
{
UpdateModel();
UpdateLighting();
UpdateCamera();
}
void SharedPreviewContent::UpdateModel()
{
Render::MeshComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MeshComponentRequestBus::Events::SetModelAsset, m_modelAsset);
Render::MaterialComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MaterialComponentRequestBus::Events::SetMaterialOverride,
Render::DefaultMaterialAssignmentId, m_materialAsset.GetId());
Render::MaterialComponentRequestBus::Event(
m_modelEntity->GetId(), &Render::MaterialComponentRequestBus::Events::SetPropertyOverrides,
Render::DefaultMaterialAssignmentId, m_materialPropertyOverrides);
}
void SharedPreviewContent::UpdateLighting()
{
if (m_lightingPresetAsset.IsReady())
{
auto preset = m_lightingPresetAsset->GetDataAs<Render::LightingPreset>();
if (preset)
{
auto iblFeatureProcessor = m_scene->GetFeatureProcessor<Render::ImageBasedLightFeatureProcessorInterface>();
auto postProcessFeatureProcessor = m_scene->GetFeatureProcessor<Render::PostProcessFeatureProcessorInterface>();
auto postProcessSettingInterface = postProcessFeatureProcessor->GetOrCreateSettingsInterface(EntityId());
auto exposureControlSettingInterface = postProcessSettingInterface->GetOrCreateExposureControlSettingsInterface();
auto directionalLightFeatureProcessor =
m_scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
auto skyboxFeatureProcessor = m_scene->GetFeatureProcessor<Render::SkyBoxFeatureProcessorInterface>();
skyboxFeatureProcessor->Enable(true);
skyboxFeatureProcessor->SetSkyboxMode(Render::SkyBoxMode::Cubemap);
Camera::Configuration cameraConfig;
cameraConfig.m_fovRadians = FieldOfView;
cameraConfig.m_nearClipDistance = NearDist;
cameraConfig.m_farClipDistance = FarDist;
cameraConfig.m_frustumWidth = 100.0f;
cameraConfig.m_frustumHeight = 100.0f;
AZStd::vector<Render::DirectionalLightFeatureProcessorInterface::LightHandle> lightHandles;
preset->ApplyLightingPreset(
iblFeatureProcessor, skyboxFeatureProcessor, exposureControlSettingInterface, directionalLightFeatureProcessor,
cameraConfig, lightHandles);
}
}
}
void SharedPreviewContent::UpdateCamera()
{
// Get bounding sphere of the model asset and estimate how far the camera needs to be see all of it
Vector3 center = {};
float radius = {};
if (m_modelAsset.IsReady())
{
m_modelAsset->GetAabb().GetAsSphere(center, radius);
}
const auto distance = radius + NearDist;
const auto cameraRotation = Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), CameraRotationAngle);
const auto cameraPosition = center + cameraRotation.TransformVector(Vector3(0.0f, distance, 0.0f));
const auto cameraTransform = Transform::CreateLookAt(cameraPosition, center);
m_view->SetCameraTransform(Matrix3x4::CreateFromTransform(cameraTransform));
}
} // namespace LyIntegration
} // namespace AZ
@@ -0,0 +1,67 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/Feature/Material/MaterialAssignment.h>
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
#include <AtomToolsFramework/PreviewRenderer/PreviewContent.h>
namespace AZ
{
namespace LyIntegration
{
//! Creates a simple scene used for most previews and thumbnails
class SharedPreviewContent final : public AtomToolsFramework::PreviewContent
{
public:
AZ_CLASS_ALLOCATOR(SharedPreviewContent, AZ::SystemAllocator, 0);
SharedPreviewContent(
RPI::ScenePtr scene,
RPI::ViewPtr view,
AZ::Uuid entityContextId,
const Data::AssetId& modelAssetId,
const Data::AssetId& materialAssetId,
const Data::AssetId& lightingPresetAssetId,
const Render::MaterialPropertyOverrideMap& materialPropertyOverrides);
~SharedPreviewContent() override;
void Load() override;
bool IsReady() const override;
bool IsError() const override;
void ReportErrors() override;
void Update() override;
private:
void UpdateModel();
void UpdateLighting();
void UpdateCamera();
static constexpr float AspectRatio = 1.0f;
static constexpr float NearDist = 0.001f;
static constexpr float FarDist = 100.0f;
static constexpr float FieldOfView = Constants::HalfPi;
static constexpr float CameraRotationAngle = Constants::QuarterPi / 2.0f;
RPI::ScenePtr m_scene;
RPI::ViewPtr m_view;
AZ::Uuid m_entityContextId;
Entity* m_modelEntity = nullptr;
Data::Asset<RPI::ModelAsset> m_modelAsset;
Data::Asset<RPI::MaterialAsset> m_materialAsset;
Data::Asset<RPI::AnyAsset> m_lightingPresetAsset;
Render::MaterialPropertyOverrideMap m_materialPropertyOverrides;
};
} // namespace LyIntegration
} // namespace AZ
@@ -11,37 +11,42 @@
#include <AssetBrowser/Thumbnails/SourceThumbnail.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Model/ModelAsset.h>
#include <Thumbnails/ThumbnailUtils.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
#include <SharedPreview/SharedPreviewUtils.h>
namespace AZ
{
namespace LyIntegration
{
namespace Thumbnails
namespace SharedPreviewUtils
{
Data::AssetId GetAssetId(AzToolsFramework::Thumbnailer::SharedThumbnailKey key, const Data::AssetType& assetType)
Data::AssetId GetAssetId(
AzToolsFramework::Thumbnailer::SharedThumbnailKey key,
const Data::AssetType& assetType,
const Data::AssetId& defaultAssetId)
{
static const Data::AssetId invalidAssetId;
// if it's a source thumbnail key, find first product with a matching asset type
auto sourceKey = azrtti_cast<const AzToolsFramework::AssetBrowser::SourceThumbnailKey*>(key.data());
if (sourceKey)
{
bool foundIt = false;
AZStd::vector<Data::AssetInfo> productsAssetInfo;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetsProducedBySourceUUID, sourceKey->GetSourceUuid(), productsAssetInfo);
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
foundIt, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetsProducedBySourceUUID,
sourceKey->GetSourceUuid(), productsAssetInfo);
if (!foundIt)
{
return invalidAssetId;
return defaultAssetId;
}
auto assetInfoIt = AZStd::find_if(productsAssetInfo.begin(), productsAssetInfo.end(),
auto assetInfoIt = AZStd::find_if(
productsAssetInfo.begin(), productsAssetInfo.end(),
[&assetType](const Data::AssetInfo& assetInfo)
{
return assetInfo.m_assetType == assetType;
});
if (assetInfoIt == productsAssetInfo.end())
{
return invalidAssetId;
return defaultAssetId;
}
return assetInfoIt->m_assetId;
@@ -53,10 +58,9 @@ namespace AZ
{
return productKey->GetAssetId();
}
return invalidAssetId;
return defaultAssetId;
}
QString WordWrap(const QString& string, int maxLength)
{
QString result;
@@ -81,6 +85,32 @@ namespace AZ
}
return result;
}
} // namespace Thumbnails
AZStd::unordered_set<AZ::Uuid> GetSupportedAssetTypes()
{
return { RPI::AnyAsset::RTTI_Type(), RPI::MaterialAsset::RTTI_Type(), RPI::ModelAsset::RTTI_Type() };
}
bool IsSupportedAssetType(AzToolsFramework::Thumbnailer::SharedThumbnailKey key)
{
for (const AZ::Uuid& typeId : SharedPreviewUtils::GetSupportedAssetTypes())
{
const AZ::Data::AssetId& assetId = SharedPreviewUtils::GetAssetId(key, typeId);
if (assetId.IsValid())
{
if (typeId == RPI::AnyAsset::RTTI_Type())
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, assetId);
return AzFramework::StringFunc::EndsWith(assetInfo.m_relativePath.c_str(), "lightingpreset.azasset");
}
return true;
}
}
return false;
}
} // namespace SharedPreviewUtils
} // namespace LyIntegration
} // namespace AZ

Some files were not shown because too many files have changed in this diff Show More