Merge remote-tracking branch 'upstream/development' into Atom/santorac/FixMaterialInitializationBug-ATOM-16476
This commit is contained in:
@@ -395,12 +395,6 @@ namespace AzToolsFramework
|
||||
AzQtComponents::CardNotification* ComponentEditor::CreateNotificationForWarningComponents(const QString& message)
|
||||
{
|
||||
AzQtComponents::CardNotification * notification = CreateNotification(message);
|
||||
const QPushButton * featureButton = notification->addButtonFeature(tr("Continue"));
|
||||
|
||||
connect(featureButton, &QPushButton::clicked, this, [notification]()
|
||||
{
|
||||
notification->close();
|
||||
});
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
+79
-80
@@ -122,12 +122,14 @@ namespace TestImpact
|
||||
};
|
||||
|
||||
//! Base class for all sequence report types.
|
||||
template<typename PolicyStateType>
|
||||
template<SequenceReportType Type, typename PolicyStateType>
|
||||
class SequenceReportBase
|
||||
{
|
||||
public:
|
||||
static constexpr SequenceReportType ReportType = Type;
|
||||
using PolicyState = PolicyStateType;
|
||||
|
||||
//! Constructs the report for a sequence of selected tests.
|
||||
//! @param type The type of sequence this report is generated for.
|
||||
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
|
||||
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
|
||||
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
|
||||
@@ -136,33 +138,49 @@ namespace TestImpact
|
||||
//! @param selectedTestRuns The target names of the selected test runs.
|
||||
//! @param selectedTestRunReport The report for the set of selected test runs.
|
||||
SequenceReportBase(
|
||||
SequenceReportType type,
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const PolicyStateType& policyState,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
|
||||
PolicyStateType policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport)
|
||||
: m_type(type)
|
||||
, m_maxConcurrency(maxConcurrency)
|
||||
, m_testTargetTimeout(testTargetTimeout)
|
||||
, m_globalTimeout(globalTimeout)
|
||||
, m_policyState(policyState)
|
||||
TestRunSelection selectedTestRuns,
|
||||
TestRunReport selectedTestRunReport)
|
||||
: m_maxConcurrency(maxConcurrency)
|
||||
, m_testTargetTimeout(AZStd::move(testTargetTimeout))
|
||||
, m_globalTimeout(AZStd::move(globalTimeout))
|
||||
, m_policyState(AZStd::move(policyState))
|
||||
, m_suite(suiteType)
|
||||
, m_selectedTestRuns(selectedTestRuns)
|
||||
, m_selectedTestRuns(AZStd::move(selectedTestRuns))
|
||||
, m_selectedTestRunReport(AZStd::move(selectedTestRunReport))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~SequenceReportBase() = default;
|
||||
|
||||
//! Returns the identifying type for this sequence report.
|
||||
SequenceReportType GetType() const
|
||||
SequenceReportBase(SequenceReportBase&& report)
|
||||
: SequenceReportBase(
|
||||
AZStd::move(report.m_maxConcurrency),
|
||||
AZStd::move(report.m_testTargetTimeout),
|
||||
AZStd::move(report.m_globalTimeout),
|
||||
AZStd::move(report.m_policyState),
|
||||
AZStd::move(report.m_suite),
|
||||
AZStd::move(report.m_selectedTestRuns),
|
||||
AZStd::move(report.m_selectedTestRunReport))
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
SequenceReportBase(const SequenceReportBase& report)
|
||||
: SequenceReportBase(
|
||||
report.m_maxConcurrency,
|
||||
report.m_testTargetTimeout,
|
||||
report.m_globalTimeout,
|
||||
report.m_policyState,
|
||||
report.m_suite,
|
||||
report.m_selectedTestRuns,
|
||||
report.m_selectedTestRunReport)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~SequenceReportBase() = default;
|
||||
|
||||
//! Returns the maximum concurrency for this sequence.
|
||||
size_t GetMaxConcurrency() const
|
||||
{
|
||||
@@ -284,7 +302,6 @@ namespace TestImpact
|
||||
}
|
||||
|
||||
private:
|
||||
SequenceReportType m_type;
|
||||
size_t m_maxConcurrency = 0;
|
||||
AZStd::optional<AZStd::chrono::milliseconds> m_testTargetTimeout;
|
||||
AZStd::optional<AZStd::chrono::milliseconds> m_globalTimeout;
|
||||
@@ -296,58 +313,27 @@ namespace TestImpact
|
||||
|
||||
//! Report type for regular test sequences.
|
||||
class RegularSequenceReport
|
||||
: public SequenceReportBase<SequencePolicyState>
|
||||
: public SequenceReportBase<SequenceReportType::RegularSequence, SequencePolicyState>
|
||||
{
|
||||
public:
|
||||
//! Constructs the report for a regular sequence.
|
||||
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
|
||||
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
|
||||
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
|
||||
//! @param policyState The policy state this sequence was executed under.
|
||||
//! @param suiteType The suite from which the tests have been selected from.
|
||||
//! @param selectedTestRuns The target names of the selected test runs.
|
||||
//! @param selectedTestRunReport The report for the set of selected test runs.
|
||||
RegularSequenceReport(
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const SequencePolicyState& policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport);
|
||||
using SequenceReportBase::SequenceReportBase;
|
||||
};
|
||||
|
||||
//! Report type for seed test sequences.
|
||||
class SeedSequenceReport
|
||||
: public SequenceReportBase<SequencePolicyState>
|
||||
: public SequenceReportBase<SequenceReportType::SeedSequence, SequencePolicyState>
|
||||
{
|
||||
public:
|
||||
//! Constructs the report for a seed sequence.
|
||||
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
|
||||
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
|
||||
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
|
||||
//! @param policyState The policy state this sequence was executed under.
|
||||
//! @param suiteType The suite from which the tests have been selected from.
|
||||
//! @param selectedTestRuns The target names of the selected test runs.
|
||||
//! @param selectedTestRunReport The report for the set of selected test runs.
|
||||
SeedSequenceReport(
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const SequencePolicyState& policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport);
|
||||
using SequenceReportBase::SequenceReportBase;
|
||||
};
|
||||
|
||||
//! Report detailing a test run sequence of selected and drafted tests.
|
||||
template<typename PolicyStateType>
|
||||
template<SequenceReportType Type, typename PolicyStateType>
|
||||
class DraftingSequenceReportBase
|
||||
: public SequenceReportBase<PolicyStateType>
|
||||
: public SequenceReportBase<Type, PolicyStateType>
|
||||
{
|
||||
public:
|
||||
//! Constructs the report for sequences that draft in previously failed/newly added test targets.
|
||||
//! @param type The type of sequence this report is generated for.
|
||||
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
|
||||
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
|
||||
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
|
||||
@@ -358,18 +344,16 @@ namespace TestImpact
|
||||
//! @param selectedTestRunReport The report for the set of selected test runs.
|
||||
//! @param draftedTestRunReport The report for the set of drafted test runs.
|
||||
DraftingSequenceReportBase(
|
||||
SequenceReportType type,
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const PolicyStateType& policyState,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
|
||||
PolicyStateType policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
const AZStd::vector<AZStd::string>& draftedTestRuns,
|
||||
TestRunSelection selectedTestRuns,
|
||||
AZStd::vector<AZStd::string> draftedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport,
|
||||
TestRunReport&& draftedTestRunReport)
|
||||
: SequenceReportBase<PolicyStateType> (
|
||||
type,
|
||||
: SequenceReportBase<Type, PolicyStateType>(
|
||||
maxConcurrency,
|
||||
testTargetTimeout,
|
||||
globalTimeout,
|
||||
@@ -377,7 +361,17 @@ namespace TestImpact
|
||||
suiteType,
|
||||
selectedTestRuns,
|
||||
AZStd::move(selectedTestRunReport))
|
||||
, m_draftedTestRuns(draftedTestRuns)
|
||||
, m_draftedTestRuns(AZStd::move(draftedTestRuns))
|
||||
, m_draftedTestRunReport(AZStd::move(draftedTestRunReport))
|
||||
{
|
||||
}
|
||||
|
||||
DraftingSequenceReportBase(
|
||||
SequenceReportBase<Type, PolicyStateType>&& report,
|
||||
AZStd::vector<AZStd::string> draftedTestRuns,
|
||||
TestRunReport&& draftedTestRunReport)
|
||||
: SequenceReportBase<Type, PolicyStateType>(AZStd::move(report))
|
||||
, m_draftedTestRuns(AZStd::move(draftedTestRuns))
|
||||
, m_draftedTestRunReport(AZStd::move(draftedTestRunReport))
|
||||
{
|
||||
}
|
||||
@@ -456,7 +450,7 @@ namespace TestImpact
|
||||
|
||||
//! Report detailing an impact analysis sequence of selected, discarded and drafted tests.
|
||||
class ImpactAnalysisSequenceReport
|
||||
: public DraftingSequenceReportBase<ImpactAnalysisSequencePolicyState>
|
||||
: public DraftingSequenceReportBase<SequenceReportType::ImpactAnalysisSequence, ImpactAnalysisSequencePolicyState>
|
||||
{
|
||||
public:
|
||||
//! Constructs the report for an impact analysis sequence.
|
||||
@@ -471,16 +465,18 @@ namespace TestImpact
|
||||
//! @param draftedTestRunReport The report for the set of drafted test runs.
|
||||
ImpactAnalysisSequenceReport(
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const ImpactAnalysisSequencePolicyState& policyState,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
|
||||
ImpactAnalysisSequencePolicyState policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
const AZStd::vector<AZStd::string>& discardedTestRuns,
|
||||
const AZStd::vector<AZStd::string>& draftedTestRuns,
|
||||
TestRunSelection selectedTestRuns,
|
||||
AZStd::vector<AZStd::string> discardedTestRuns,
|
||||
AZStd::vector<AZStd::string> draftedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport,
|
||||
TestRunReport&& draftedTestRunReport);
|
||||
|
||||
ImpactAnalysisSequenceReport(DraftingSequenceReportBase&& report, AZStd::vector<AZStd::string> discardedTestRuns);
|
||||
|
||||
//! Returns the test runs discarded from running in the sequence.
|
||||
const AZStd::vector<AZStd::string>& GetDiscardedTestRuns() const;
|
||||
private:
|
||||
@@ -489,7 +485,7 @@ namespace TestImpact
|
||||
|
||||
//! Report detailing an impact analysis sequence of selected, discarded and drafted test runs.
|
||||
class SafeImpactAnalysisSequenceReport
|
||||
: public DraftingSequenceReportBase<SafeImpactAnalysisSequencePolicyState>
|
||||
: public DraftingSequenceReportBase<SequenceReportType::SafeImpactAnalysisSequence, SafeImpactAnalysisSequencePolicyState>
|
||||
{
|
||||
public:
|
||||
//! Constructs the report for a sequence of selected, discarded and drafted test runs.
|
||||
@@ -506,17 +502,20 @@ namespace TestImpact
|
||||
//! @param draftedTestRunReport The report for the set of drafted test runs.
|
||||
SafeImpactAnalysisSequenceReport(
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const SafeImpactAnalysisSequencePolicyState& policyState,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
|
||||
SafeImpactAnalysisSequencePolicyState policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
const TestRunSelection& discardedTestRuns,
|
||||
const AZStd::vector<AZStd::string>& draftedTestRuns,
|
||||
TestRunSelection selectedTestRuns,
|
||||
TestRunSelection discardedTestRuns,
|
||||
AZStd::vector<AZStd::string> draftedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport,
|
||||
TestRunReport&& discardedTestRunReport,
|
||||
TestRunReport&& draftedTestRunReport);
|
||||
|
||||
SafeImpactAnalysisSequenceReport(
|
||||
DraftingSequenceReportBase&& report, TestRunSelection discardedTestRuns, TestRunReport&& discardedTestRunReport);
|
||||
|
||||
// SequenceReport overrides ...
|
||||
AZStd::chrono::milliseconds GetDuration() const override;
|
||||
TestSequenceResult GetResult() const override;
|
||||
|
||||
+16
-4
@@ -14,15 +14,27 @@
|
||||
|
||||
namespace TestImpact
|
||||
{
|
||||
//! Serializes a regular sequence report to JSON format.
|
||||
//! Serializes a regular sequence report to Json format.
|
||||
AZStd::string SerializeSequenceReport(const Client::RegularSequenceReport& sequenceReport);
|
||||
|
||||
//! Serializes a seed sequence report to JSON format.
|
||||
//! Serializes a seed sequence report to Json format.
|
||||
AZStd::string SerializeSequenceReport(const Client::SeedSequenceReport& sequenceReport);
|
||||
|
||||
//! Serializes an impact analysis sequence report to JSON format.
|
||||
//! Serializes an impact analysis sequence report to Json format.
|
||||
AZStd::string SerializeSequenceReport(const Client::ImpactAnalysisSequenceReport& sequenceReport);
|
||||
|
||||
//! Serializes a safe impact analysis sequence report to JSON format.
|
||||
//! Serializes a safe impact analysis sequence report to Json format.
|
||||
AZStd::string SerializeSequenceReport(const Client::SafeImpactAnalysisSequenceReport& sequenceReport);
|
||||
|
||||
//! Deserialize a regular sequence report from Json format.
|
||||
Client::RegularSequenceReport DeserializeRegularSequenceReport(const AZStd::string& sequenceReportJson);
|
||||
|
||||
//! Deserialize a seed sequence report from Json format.
|
||||
Client::SeedSequenceReport DeserializeSeedSequenceReport(const AZStd::string& sequenceReportJson);
|
||||
|
||||
//! Deserialize an impact analysis sequence report from Json format.
|
||||
Client::ImpactAnalysisSequenceReport DeserializeImpactAnalysisSequenceReport(const AZStd::string& sequenceReportJson);
|
||||
|
||||
//! Deserialize a safe impact analysis sequence report from Json format.
|
||||
Client::SafeImpactAnalysisSequenceReport DeserializeSafeImpactAnalysisSequenceReport(const AZStd::string& sequenceReportJson);
|
||||
} // namespace TestImpact
|
||||
|
||||
+39
@@ -103,4 +103,43 @@ namespace TestImpact
|
||||
|
||||
//! User-friendly names for the client test result types.
|
||||
AZStd::string ClientTestResultAsString(Client::TestResult result);
|
||||
|
||||
//! User-friendly names for the suite types.
|
||||
SuiteType SuiteTypeFromString(const AZStd::string& suiteType);
|
||||
|
||||
//! Returns the sequence report type for the specified string.
|
||||
Client::SequenceReportType SequenceReportTypeFromString(const AZStd::string& type);
|
||||
|
||||
//! Returns the test run result for the specified string.
|
||||
Client::TestRunResult TestRunResultFromString(const AZStd::string& result);
|
||||
|
||||
//! Returns the test result for the specified string.
|
||||
Client::TestResult TestResultFromString(const AZStd::string& result);
|
||||
|
||||
//! Returns the test sequence result for the specified string.
|
||||
TestSequenceResult TestSequenceResultFromString(const AZStd::string& result);
|
||||
|
||||
//! Returns the execution failure policy for the specified string.
|
||||
Policy::ExecutionFailure ExecutionFailurePolicyFromString(const AZStd::string& executionFailurePolicy);
|
||||
|
||||
//! Returns the failed test coverage policy for the specified string.
|
||||
Policy::FailedTestCoverage FailedTestCoveragePolicyFromString(const AZStd::string& failedTestCoveragePolicy);
|
||||
|
||||
//! Returns the test prioritization policy for the specified string.
|
||||
Policy::TestPrioritization TestPrioritizationPolicyFromString(const AZStd::string& testPrioritizationPolicy);
|
||||
|
||||
//! Returns the test failure policy for the specified string.
|
||||
Policy::TestFailure TestFailurePolicyFromString(const AZStd::string& testFailurePolicy);
|
||||
|
||||
//! Returns the integrity failure policy for the specified string.
|
||||
Policy::IntegrityFailure IntegrityFailurePolicyFromString(const AZStd::string& integrityFailurePolicy);
|
||||
|
||||
//! Returns the dynamic dependency map policy for the specified string.
|
||||
Policy::DynamicDependencyMap DynamicDependencyMapPolicyFromString(const AZStd::string& dynamicDependencyMapPolicy);
|
||||
|
||||
//! Returns the test sharding policy for the specified string.
|
||||
Policy::TestSharding TestShardingPolicyFromString(const AZStd::string& testShardingPolicy);
|
||||
|
||||
//! Returns the target output capture policy for the specified string.
|
||||
Policy::TargetOutputCapture TargetOutputCapturePolicyFromString(const AZStd::string& targetOutputCapturePolicy);
|
||||
} // namespace TestImpact
|
||||
|
||||
+42
-69
@@ -159,69 +159,35 @@ namespace TestImpact
|
||||
return m_totalNumDisabledTests;
|
||||
}
|
||||
|
||||
RegularSequenceReport::RegularSequenceReport(
|
||||
ImpactAnalysisSequenceReport::ImpactAnalysisSequenceReport(
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const SequencePolicyState& policyState,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
|
||||
ImpactAnalysisSequencePolicyState policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport)
|
||||
: SequenceReportBase(
|
||||
SequenceReportType::RegularSequence,
|
||||
maxConcurrency,
|
||||
testTargetTimeout,
|
||||
globalTimeout,
|
||||
policyState,
|
||||
suiteType,
|
||||
selectedTestRuns,
|
||||
AZStd::move(selectedTestRunReport))
|
||||
{
|
||||
}
|
||||
|
||||
SeedSequenceReport::SeedSequenceReport(
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const SequencePolicyState& policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport)
|
||||
: SequenceReportBase(
|
||||
SequenceReportType::SeedSequence,
|
||||
maxConcurrency,
|
||||
testTargetTimeout,
|
||||
globalTimeout,
|
||||
policyState,
|
||||
suiteType,
|
||||
selectedTestRuns,
|
||||
AZStd::move(selectedTestRunReport))
|
||||
TestRunSelection selectedTestRuns,
|
||||
AZStd::vector<AZStd::string> discardedTestRuns,
|
||||
AZStd::vector<AZStd::string> draftedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport,
|
||||
TestRunReport&& draftedTestRunReport)
|
||||
: DraftingSequenceReportBase(
|
||||
maxConcurrency,
|
||||
AZStd::move(testTargetTimeout),
|
||||
AZStd::move(globalTimeout),
|
||||
AZStd::move(policyState),
|
||||
suiteType,
|
||||
AZStd::move(selectedTestRuns),
|
||||
AZStd::move(draftedTestRuns),
|
||||
AZStd::move(selectedTestRunReport),
|
||||
AZStd::move(draftedTestRunReport))
|
||||
, m_discardedTestRuns(discardedTestRuns)
|
||||
{
|
||||
}
|
||||
|
||||
ImpactAnalysisSequenceReport::ImpactAnalysisSequenceReport(
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const ImpactAnalysisSequencePolicyState& policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
const AZStd::vector<AZStd::string>& discardedTestRuns,
|
||||
const AZStd::vector<AZStd::string>& draftedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport,
|
||||
TestRunReport&& draftedTestRunReport)
|
||||
: DraftingSequenceReportBase(
|
||||
SequenceReportType::ImpactAnalysisSequence,
|
||||
maxConcurrency,
|
||||
testTargetTimeout,
|
||||
globalTimeout,
|
||||
policyState,
|
||||
suiteType,
|
||||
selectedTestRuns,
|
||||
draftedTestRuns,
|
||||
AZStd::move(selectedTestRunReport),
|
||||
AZStd::move(draftedTestRunReport))
|
||||
, m_discardedTestRuns(discardedTestRuns)
|
||||
DraftingSequenceReportBase&& report, AZStd::vector<AZStd::string> discardedTestRuns)
|
||||
: DraftingSequenceReportBase(AZStd::move(report))
|
||||
, m_discardedTestRuns(AZStd::move(discardedTestRuns))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -232,25 +198,24 @@ namespace TestImpact
|
||||
|
||||
SafeImpactAnalysisSequenceReport::SafeImpactAnalysisSequenceReport(
|
||||
size_t maxConcurrency,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
|
||||
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
|
||||
const SafeImpactAnalysisSequencePolicyState& policyState,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
|
||||
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
|
||||
SafeImpactAnalysisSequencePolicyState policyState,
|
||||
SuiteType suiteType,
|
||||
const TestRunSelection& selectedTestRuns,
|
||||
const TestRunSelection& discardedTestRuns,
|
||||
const AZStd::vector<AZStd::string>& draftedTestRuns,
|
||||
TestRunSelection selectedTestRuns,
|
||||
TestRunSelection discardedTestRuns,
|
||||
AZStd::vector<AZStd::string> draftedTestRuns,
|
||||
TestRunReport&& selectedTestRunReport,
|
||||
TestRunReport&& discardedTestRunReport,
|
||||
TestRunReport&& draftedTestRunReport)
|
||||
: DraftingSequenceReportBase(
|
||||
SequenceReportType::SafeImpactAnalysisSequence,
|
||||
maxConcurrency,
|
||||
testTargetTimeout,
|
||||
globalTimeout,
|
||||
policyState,
|
||||
AZStd::move(testTargetTimeout),
|
||||
AZStd::move(globalTimeout),
|
||||
AZStd::move(policyState),
|
||||
suiteType,
|
||||
selectedTestRuns,
|
||||
draftedTestRuns,
|
||||
AZStd::move(selectedTestRuns),
|
||||
AZStd::move(draftedTestRuns),
|
||||
AZStd::move(selectedTestRunReport),
|
||||
AZStd::move(draftedTestRunReport))
|
||||
, m_discardedTestRuns(discardedTestRuns)
|
||||
@@ -258,6 +223,14 @@ namespace TestImpact
|
||||
{
|
||||
}
|
||||
|
||||
SafeImpactAnalysisSequenceReport::SafeImpactAnalysisSequenceReport(
|
||||
DraftingSequenceReportBase&& report, TestRunSelection discardedTestRuns, TestRunReport&& discardedTestRunReport)
|
||||
: DraftingSequenceReportBase(AZStd::move(report))
|
||||
, m_discardedTestRuns(AZStd::move(discardedTestRuns))
|
||||
, m_discardedTestRunReport(AZStd::move(discardedTestRunReport))
|
||||
{
|
||||
}
|
||||
|
||||
TestSequenceResult SafeImpactAnalysisSequenceReport::GetResult() const
|
||||
{
|
||||
return CalculateMultiTestSequenceResult({ DraftingSequenceReportBase::GetResult(), m_discardedTestRunReport.GetResult() });
|
||||
|
||||
+256
-6
@@ -21,7 +21,7 @@ namespace TestImpact
|
||||
{
|
||||
namespace SequenceReportFields
|
||||
{
|
||||
// Keys for pertinent JSON node and attribute names
|
||||
// Keys for pertinent Json node and attribute names
|
||||
constexpr const char* Keys[] =
|
||||
{
|
||||
"name",
|
||||
@@ -417,13 +417,13 @@ namespace TestImpact
|
||||
writer.String(DynamicDependencyMapPolicyAsString(policyState.m_dynamicDependencyMap).c_str());
|
||||
}
|
||||
|
||||
template<typename PolicyStateType>
|
||||
template<typename SequenceReportBaseType>
|
||||
void SerializeSequenceReportBaseMembers(
|
||||
const Client::SequenceReportBase<PolicyStateType>& sequenceReport, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
|
||||
const SequenceReportBaseType& sequenceReport, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
|
||||
{
|
||||
// Type
|
||||
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Type]);
|
||||
writer.String(SequenceReportTypeAsString(sequenceReport.GetType()).c_str());
|
||||
writer.String(SequenceReportTypeAsString(sequenceReport.ReportType).c_str());
|
||||
|
||||
// Test target timeout
|
||||
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TestTargetTimeout]);
|
||||
@@ -510,9 +510,9 @@ namespace TestImpact
|
||||
writer.Uint64(sequenceReport.GetTotalNumDisabledTests());
|
||||
}
|
||||
|
||||
template<typename PolicyStateType>
|
||||
template<typename DraftingSequenceReportBaseType>
|
||||
void SerializeDraftingSequenceReportMembers(
|
||||
const Client::DraftingSequenceReportBase<PolicyStateType>& sequenceReport, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
|
||||
const DraftingSequenceReportBaseType& sequenceReport, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
|
||||
{
|
||||
SerializeSequenceReportBaseMembers(sequenceReport, writer);
|
||||
|
||||
@@ -603,4 +603,254 @@ namespace TestImpact
|
||||
|
||||
return stringBuffer.GetString();
|
||||
}
|
||||
|
||||
AZStd::chrono::high_resolution_clock::time_point TimePointFromMsInt64(AZ::s64 ms)
|
||||
{
|
||||
return AZStd::chrono::high_resolution_clock::time_point(AZStd::chrono::milliseconds(ms));
|
||||
}
|
||||
|
||||
AZStd::vector<Client::Test> DeserializeTests(const rapidjson::Value& serialTests)
|
||||
{
|
||||
AZStd::vector<Client::Test> tests;
|
||||
tests.reserve(serialTests[SequenceReportFields::Keys[SequenceReportFields::Tests]].GetArray().Size());
|
||||
for (const auto& test : serialTests[SequenceReportFields::Keys[SequenceReportFields::Tests]].GetArray())
|
||||
{
|
||||
const AZStd::string name = test[SequenceReportFields::Keys[SequenceReportFields::Name]].GetString();
|
||||
const auto result = TestResultFromString(test[SequenceReportFields::Keys[SequenceReportFields::Result]].GetString());
|
||||
tests.emplace_back(name, result);
|
||||
}
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
Client::TestRunBase DeserializeTestRunBase(const rapidjson::Value& serialTestRun)
|
||||
{
|
||||
return Client::TestRunBase(
|
||||
serialTestRun[SequenceReportFields::Keys[SequenceReportFields::Name]].GetString(),
|
||||
serialTestRun[SequenceReportFields::Keys[SequenceReportFields::CommandArgs]].GetString(),
|
||||
TimePointFromMsInt64(serialTestRun[SequenceReportFields::Keys[SequenceReportFields::StartTime]].GetInt64()),
|
||||
AZStd::chrono::milliseconds(serialTestRun[SequenceReportFields::Keys[SequenceReportFields::Duration]].GetInt64()),
|
||||
TestRunResultFromString(serialTestRun[SequenceReportFields::Keys[SequenceReportFields::Result]].GetString()));
|
||||
}
|
||||
|
||||
template<typename TestRunType>
|
||||
AZStd::vector<TestRunType> DeserializeTestRuns(const rapidjson::Value& serialTestRuns)
|
||||
{
|
||||
AZStd::vector<TestRunType> testRuns;
|
||||
testRuns.reserve(serialTestRuns.GetArray().Size());
|
||||
for (const auto& testRun : serialTestRuns.GetArray())
|
||||
{
|
||||
testRuns.emplace_back(DeserializeTestRunBase(testRun));
|
||||
}
|
||||
|
||||
return testRuns;
|
||||
}
|
||||
|
||||
template<typename CompletedTestRunType>
|
||||
AZStd::vector<CompletedTestRunType> DeserializeCompletedTestRuns(const rapidjson::Value& serialCompletedTestRuns)
|
||||
{
|
||||
AZStd::vector<CompletedTestRunType> testRuns;
|
||||
testRuns.reserve(serialCompletedTestRuns.GetArray().Size());
|
||||
for (const auto& testRun : serialCompletedTestRuns.GetArray())
|
||||
{
|
||||
testRuns.emplace_back(
|
||||
DeserializeTestRunBase(testRun), DeserializeTests(testRun[SequenceReportFields::Keys[SequenceReportFields::Tests]]));
|
||||
}
|
||||
|
||||
return testRuns;
|
||||
}
|
||||
|
||||
Client::TestRunReport DeserializeTestRunReport(const rapidjson::Value& serialTestRunReport)
|
||||
{
|
||||
return Client::TestRunReport(
|
||||
TestSequenceResultFromString(serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::Result]].GetString()),
|
||||
TimePointFromMsInt64(serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::StartTime]].GetInt64()),
|
||||
AZStd::chrono::milliseconds(serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::Duration]].GetInt64()),
|
||||
DeserializeCompletedTestRuns<Client::PassingTestRun>(
|
||||
serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::PassingTestRuns]]),
|
||||
DeserializeCompletedTestRuns<Client::FailingTestRun>(
|
||||
serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::FailingTestRuns]]),
|
||||
DeserializeTestRuns<Client::TestRunWithExecutionFailure>(
|
||||
serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::ExecutionFailureTestRuns]]),
|
||||
DeserializeTestRuns<Client::TimedOutTestRun>(
|
||||
serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::TimedOutTestRuns]]),
|
||||
DeserializeTestRuns<Client::UnexecutedTestRun>(
|
||||
serialTestRunReport[SequenceReportFields::Keys[SequenceReportFields::UnexecutedTestRuns]]));
|
||||
}
|
||||
|
||||
Client::TestRunSelection DeserializeTestSelection(const rapidjson::Value& serialTestRunSelection)
|
||||
{
|
||||
const auto extractTestTargetNames = [](const rapidjson::Value& serialTestTargets)
|
||||
{
|
||||
AZStd::vector<AZStd::string> testTargets;
|
||||
testTargets.reserve(serialTestTargets.GetArray().Size());
|
||||
for (const auto& testTarget : serialTestTargets.GetArray())
|
||||
{
|
||||
testTargets.emplace_back(testTarget.GetString());
|
||||
}
|
||||
|
||||
return testTargets;
|
||||
};
|
||||
|
||||
return Client::TestRunSelection(
|
||||
extractTestTargetNames(serialTestRunSelection[SequenceReportFields::Keys[SequenceReportFields::IncludedTestRuns]]),
|
||||
extractTestTargetNames(serialTestRunSelection[SequenceReportFields::Keys[SequenceReportFields::ExcludedTestRuns]]));
|
||||
}
|
||||
|
||||
PolicyStateBase DeserializePolicyStateBaseMembers(const rapidjson::Value& serialPolicyState)
|
||||
{
|
||||
return
|
||||
{
|
||||
ExecutionFailurePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::ExecutionFailure]].GetString()),
|
||||
FailedTestCoveragePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::CoverageFailure]].GetString()),
|
||||
TestFailurePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TestFailure]].GetString()),
|
||||
IntegrityFailurePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::IntegrityFailure]].GetString()),
|
||||
TestShardingPolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TestSharding]].GetString()),
|
||||
TargetOutputCapturePolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TargetOutputCapture]].GetString())
|
||||
};
|
||||
}
|
||||
|
||||
SequencePolicyState DeserializePolicyStateMembers(const rapidjson::Value& serialPolicyState)
|
||||
{
|
||||
return { DeserializePolicyStateBaseMembers(serialPolicyState) };
|
||||
}
|
||||
|
||||
SafeImpactAnalysisSequencePolicyState DeserializeSafeImpactAnalysisPolicyStateMembers(const rapidjson::Value& serialPolicyState)
|
||||
{
|
||||
return
|
||||
{
|
||||
DeserializePolicyStateBaseMembers(serialPolicyState),
|
||||
TestPrioritizationPolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TestPrioritization]].GetString())
|
||||
};
|
||||
}
|
||||
|
||||
ImpactAnalysisSequencePolicyState DeserializeImpactAnalysisSequencePolicyStateMembers(const rapidjson::Value& serialPolicyState)
|
||||
{
|
||||
return
|
||||
{
|
||||
DeserializePolicyStateBaseMembers(serialPolicyState),
|
||||
TestPrioritizationPolicyFromString(serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::TestPrioritization]].GetString()),
|
||||
DynamicDependencyMapPolicyFromString(
|
||||
serialPolicyState[SequenceReportFields::Keys[SequenceReportFields::DynamicDependencyMap]].GetString())
|
||||
};
|
||||
}
|
||||
|
||||
template<typename PolicyStateType>
|
||||
PolicyStateType DeserializePolicyStateType(const rapidjson::Value& serialPolicyStateType)
|
||||
{
|
||||
if constexpr (AZStd::is_same_v<PolicyStateType, SequencePolicyState>)
|
||||
{
|
||||
return DeserializePolicyStateMembers(serialPolicyStateType);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<PolicyStateType, SafeImpactAnalysisSequencePolicyState>)
|
||||
{
|
||||
return DeserializeSafeImpactAnalysisPolicyStateMembers(serialPolicyStateType);
|
||||
}
|
||||
else if constexpr (AZStd::is_same_v<PolicyStateType, ImpactAnalysisSequencePolicyState>)
|
||||
{
|
||||
return DeserializeImpactAnalysisSequencePolicyStateMembers(serialPolicyStateType);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(false, "Template paramater must be a valid policy state type");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename SequenceReportBaseType>
|
||||
SequenceReportBaseType DeserialiseSequenceReportBase(const rapidjson::Value& serialSequenceReportBase)
|
||||
{
|
||||
const auto type = SequenceReportTypeFromString(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::Type]].GetString());
|
||||
AZ_TestImpact_Eval(
|
||||
type == SequenceReportBaseType::ReportType,
|
||||
SequenceReportException, AZStd::string::format(
|
||||
"The JSON sequence report type '%s' does not match the constructed report type",
|
||||
serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::Type]].GetString()));
|
||||
|
||||
const auto testTargetTimeout =
|
||||
serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::TestTargetTimeout]].GetUint64();
|
||||
const auto globalTimeout =
|
||||
serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::GlobalTimeout]].GetUint64();
|
||||
|
||||
return SequenceReportBaseType(
|
||||
serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::MaxConcurrency]].GetUint64(),
|
||||
testTargetTimeout ? AZStd::optional<AZStd::chrono::milliseconds>{ testTargetTimeout } : AZStd::nullopt,
|
||||
globalTimeout ? AZStd::optional<AZStd::chrono::milliseconds>{ globalTimeout } : AZStd::nullopt,
|
||||
DeserializePolicyStateType<SequenceReportBaseType::PolicyState>(serialSequenceReportBase),
|
||||
SuiteTypeFromString(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::Suite]].GetString()),
|
||||
DeserializeTestSelection(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::SelectedTestRuns]]),
|
||||
DeserializeTestRunReport(serialSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::SelectedTestRunReport]]));
|
||||
}
|
||||
|
||||
template<typename DerivedDraftingSequenceReportType>
|
||||
Client::DraftingSequenceReportBase<DerivedDraftingSequenceReportType::ReportType, typename DerivedDraftingSequenceReportType::PolicyState>
|
||||
DeserializeDraftingSequenceReportBase(const rapidjson::Value& serialDraftingSequenceReportBase)
|
||||
{
|
||||
AZStd::vector<AZStd::string> draftingTestRuns;
|
||||
draftingTestRuns.reserve(
|
||||
serialDraftingSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::DraftedTestRuns]].GetArray().Size());
|
||||
for (const auto& testRun :
|
||||
serialDraftingSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::DraftedTestRuns]].GetArray())
|
||||
{
|
||||
draftingTestRuns.emplace_back(testRun.GetString());
|
||||
}
|
||||
|
||||
using SequenceBase =
|
||||
Client::SequenceReportBase<DerivedDraftingSequenceReportType::ReportType, typename DerivedDraftingSequenceReportType::PolicyState>;
|
||||
using DraftingSequenceBase =
|
||||
Client::DraftingSequenceReportBase<DerivedDraftingSequenceReportType::ReportType, typename DerivedDraftingSequenceReportType::PolicyState>;
|
||||
|
||||
return DraftingSequenceBase(
|
||||
DeserialiseSequenceReportBase<SequenceBase>(serialDraftingSequenceReportBase),
|
||||
AZStd::move(draftingTestRuns),
|
||||
DeserializeTestRunReport(serialDraftingSequenceReportBase[SequenceReportFields::Keys[SequenceReportFields::DraftedTestRunReport]]));
|
||||
}
|
||||
|
||||
rapidjson::Document OpenSequenceReportJson(const AZStd::string& sequenceReportJson)
|
||||
{
|
||||
rapidjson::Document doc;
|
||||
|
||||
if (doc.Parse<0>(sequenceReportJson.c_str()).HasParseError())
|
||||
{
|
||||
throw SequenceReportException("Could not parse sequence report data");
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
Client::RegularSequenceReport DeserializeRegularSequenceReport(const AZStd::string& sequenceReportJson)
|
||||
{
|
||||
const auto doc = OpenSequenceReportJson(sequenceReportJson);
|
||||
return DeserialiseSequenceReportBase<Client::RegularSequenceReport>(doc);
|
||||
}
|
||||
|
||||
Client::SeedSequenceReport DeserializeSeedSequenceReport(const AZStd::string& sequenceReportJson)
|
||||
{
|
||||
const auto doc = OpenSequenceReportJson(sequenceReportJson);
|
||||
return DeserialiseSequenceReportBase<Client::SeedSequenceReport>(doc);
|
||||
}
|
||||
|
||||
Client::ImpactAnalysisSequenceReport DeserializeImpactAnalysisSequenceReport(const AZStd::string& sequenceReportJson)
|
||||
{
|
||||
const auto doc = OpenSequenceReportJson(sequenceReportJson);
|
||||
|
||||
AZStd::vector<AZStd::string> discardedTestRuns;
|
||||
discardedTestRuns.reserve(doc[SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRuns]].GetArray().Size());
|
||||
for (const auto& testRun : doc[SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRuns]].GetArray())
|
||||
{
|
||||
discardedTestRuns.emplace_back(testRun.GetString());
|
||||
}
|
||||
|
||||
return Client::ImpactAnalysisSequenceReport(
|
||||
DeserializeDraftingSequenceReportBase<Client::ImpactAnalysisSequenceReport>(doc), AZStd::move(discardedTestRuns));
|
||||
}
|
||||
|
||||
Client::SafeImpactAnalysisSequenceReport DeserializeSafeImpactAnalysisSequenceReport(const AZStd::string& sequenceReportJson)
|
||||
{
|
||||
const auto doc = OpenSequenceReportJson(sequenceReportJson);
|
||||
|
||||
return Client::SafeImpactAnalysisSequenceReport(
|
||||
DeserializeDraftingSequenceReportBase<Client::SafeImpactAnalysisSequenceReport>(doc),
|
||||
DeserializeTestSelection(doc[SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRuns]]),
|
||||
DeserializeTestRunReport(doc[SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRunReport]]));
|
||||
}
|
||||
} // namespace TestImpact
|
||||
|
||||
@@ -243,4 +243,256 @@ namespace TestImpact
|
||||
throw(Exception(AZStd::string::format("Unexpected client test case result: %u", aznumeric_cast<AZ::u32>(result))));
|
||||
}
|
||||
}
|
||||
|
||||
SuiteType SuiteTypeFromString(const AZStd::string& suiteType)
|
||||
{
|
||||
if (suiteType == SuiteTypeAsString(SuiteType::Main))
|
||||
{
|
||||
return SuiteType::Main;
|
||||
}
|
||||
else if (suiteType == SuiteTypeAsString(SuiteType::Periodic))
|
||||
{
|
||||
return SuiteType::Periodic;
|
||||
}
|
||||
else if (suiteType == SuiteTypeAsString(SuiteType::Sandbox))
|
||||
{
|
||||
return SuiteType::Sandbox;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected suite type: '%s'", suiteType.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Client::SequenceReportType SequenceReportTypeFromString(const AZStd::string& type)
|
||||
{
|
||||
if (type == SequenceReportTypeAsString(Client::SequenceReportType::ImpactAnalysisSequence))
|
||||
{
|
||||
return Client::SequenceReportType::ImpactAnalysisSequence;
|
||||
}
|
||||
else if (type == SequenceReportTypeAsString(Client::SequenceReportType::RegularSequence))
|
||||
{
|
||||
return Client::SequenceReportType::RegularSequence;
|
||||
}
|
||||
else if (type == SequenceReportTypeAsString(Client::SequenceReportType::SafeImpactAnalysisSequence))
|
||||
{
|
||||
return Client::SequenceReportType::SafeImpactAnalysisSequence;
|
||||
}
|
||||
else if (type == SequenceReportTypeAsString(Client::SequenceReportType::SeedSequence))
|
||||
{
|
||||
return Client::SequenceReportType::SeedSequence;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected sequence report type: '%s'", type.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Client::TestRunResult TestRunResultFromString(const AZStd::string& result)
|
||||
{
|
||||
if (result == TestRunResultAsString(Client::TestRunResult::AllTestsPass))
|
||||
{
|
||||
return Client::TestRunResult::AllTestsPass;
|
||||
}
|
||||
else if (result == TestRunResultAsString(Client::TestRunResult::FailedToExecute))
|
||||
{
|
||||
return Client::TestRunResult::FailedToExecute;
|
||||
}
|
||||
else if (result == TestRunResultAsString(Client::TestRunResult::NotRun))
|
||||
{
|
||||
return Client::TestRunResult::NotRun;
|
||||
}
|
||||
else if (result == TestRunResultAsString(Client::TestRunResult::TestFailures))
|
||||
{
|
||||
return Client::TestRunResult::TestFailures;
|
||||
}
|
||||
else if (result == TestRunResultAsString(Client::TestRunResult::Timeout))
|
||||
{
|
||||
return Client::TestRunResult::Timeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected client test run result: '%s'", result.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Client::TestResult TestResultFromString(const AZStd::string& result)
|
||||
{
|
||||
if (result == ClientTestResultAsString(Client::TestResult::Failed))
|
||||
{
|
||||
return Client::TestResult::Failed;
|
||||
}
|
||||
else if (result == ClientTestResultAsString(Client::TestResult::NotRun))
|
||||
{
|
||||
return Client::TestResult::NotRun;
|
||||
}
|
||||
else if (result == ClientTestResultAsString(Client::TestResult::Passed))
|
||||
{
|
||||
return Client::TestResult::Passed;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected client test result: '%s'", result.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
TestSequenceResult TestSequenceResultFromString(const AZStd::string& result)
|
||||
{
|
||||
if (result == TestSequenceResultAsString(TestSequenceResult::Failure))
|
||||
{
|
||||
return TestSequenceResult::Failure;
|
||||
}
|
||||
else if (result == TestSequenceResultAsString(TestSequenceResult::Success))
|
||||
{
|
||||
return TestSequenceResult::Success;
|
||||
}
|
||||
else if (result == TestSequenceResultAsString(TestSequenceResult::Timeout))
|
||||
{
|
||||
return TestSequenceResult::Timeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected test sequence result: '%s'", result.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Policy::ExecutionFailure ExecutionFailurePolicyFromString(const AZStd::string& executionFailurePolicy)
|
||||
{
|
||||
if (executionFailurePolicy == ExecutionFailurePolicyAsString(Policy::ExecutionFailure::Abort))
|
||||
{
|
||||
return Policy::ExecutionFailure::Abort;
|
||||
}
|
||||
else if (executionFailurePolicy == ExecutionFailurePolicyAsString(Policy::ExecutionFailure::Continue))
|
||||
{
|
||||
return Policy::ExecutionFailure::Continue;
|
||||
}
|
||||
else if (executionFailurePolicy == ExecutionFailurePolicyAsString(Policy::ExecutionFailure::Ignore))
|
||||
{
|
||||
return Policy::ExecutionFailure::Ignore;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected execution failure policy: '%s'", executionFailurePolicy.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Policy::FailedTestCoverage FailedTestCoveragePolicyFromString(const AZStd::string& failedTestCoveragePolicy)
|
||||
{
|
||||
if (failedTestCoveragePolicy == FailedTestCoveragePolicyAsString(Policy::FailedTestCoverage::Discard))
|
||||
{
|
||||
return Policy::FailedTestCoverage::Discard;
|
||||
}
|
||||
else if (failedTestCoveragePolicy == FailedTestCoveragePolicyAsString(Policy::FailedTestCoverage::Keep))
|
||||
{
|
||||
return Policy::FailedTestCoverage::Keep;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected failed test coverage policy: '%s'", failedTestCoveragePolicy.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Policy::TestPrioritization TestPrioritizationPolicyFromString(const AZStd::string& testPrioritizationPolicy)
|
||||
{
|
||||
if (testPrioritizationPolicy == TestPrioritizationPolicyAsString(Policy::TestPrioritization::DependencyLocality))
|
||||
{
|
||||
return Policy::TestPrioritization::DependencyLocality;
|
||||
}
|
||||
else if (testPrioritizationPolicy == TestPrioritizationPolicyAsString(Policy::TestPrioritization::None))
|
||||
{
|
||||
return Policy::TestPrioritization::None;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected test prioritization policy: '%s'", testPrioritizationPolicy.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Policy::TestFailure TestFailurePolicyFromString(const AZStd::string& testFailurePolicy)
|
||||
{
|
||||
if (testFailurePolicy == TestFailurePolicyAsString(Policy::TestFailure::Abort))
|
||||
{
|
||||
return Policy::TestFailure::Abort;
|
||||
}
|
||||
else if (testFailurePolicy == TestFailurePolicyAsString(Policy::TestFailure::Continue))
|
||||
{
|
||||
return Policy::TestFailure::Continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected test failure policy: '%s'", testFailurePolicy.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Policy::IntegrityFailure IntegrityFailurePolicyFromString(const AZStd::string& integrityFailurePolicy)
|
||||
{
|
||||
if (integrityFailurePolicy == IntegrityFailurePolicyAsString(Policy::IntegrityFailure::Abort))
|
||||
{
|
||||
return Policy::IntegrityFailure::Abort;
|
||||
}
|
||||
else if (integrityFailurePolicy == IntegrityFailurePolicyAsString(Policy::IntegrityFailure::Continue))
|
||||
{
|
||||
return Policy::IntegrityFailure::Continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected integration failure policy: '%s'", integrityFailurePolicy.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Policy::DynamicDependencyMap DynamicDependencyMapPolicyFromString(const AZStd::string& dynamicDependencyMapPolicy)
|
||||
{
|
||||
if (dynamicDependencyMapPolicy == DynamicDependencyMapPolicyAsString(Policy::DynamicDependencyMap::Discard))
|
||||
{
|
||||
return Policy::DynamicDependencyMap::Discard;
|
||||
}
|
||||
else if (dynamicDependencyMapPolicy == DynamicDependencyMapPolicyAsString(Policy::DynamicDependencyMap::Update))
|
||||
{
|
||||
return Policy::DynamicDependencyMap::Update;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected dynamic dependency map policy: '%s'", dynamicDependencyMapPolicy.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Policy::TestSharding TestShardingPolicyFromString(const AZStd::string& testShardingPolicy)
|
||||
{
|
||||
if (testShardingPolicy == TestShardingPolicyAsString(Policy::TestSharding::Always))
|
||||
{
|
||||
return Policy::TestSharding::Always;
|
||||
}
|
||||
else if (testShardingPolicy == TestShardingPolicyAsString(Policy::TestSharding::Never))
|
||||
{
|
||||
return Policy::TestSharding::Never;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected test sharding policy: '%s'", testShardingPolicy.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
Policy::TargetOutputCapture TargetOutputCapturePolicyFromString(const AZStd::string& targetOutputCapturePolicy)
|
||||
{
|
||||
if (targetOutputCapturePolicy == TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture::File))
|
||||
{
|
||||
return Policy::TargetOutputCapture::File;
|
||||
}
|
||||
else if (targetOutputCapturePolicy == TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture::None))
|
||||
{
|
||||
return Policy::TargetOutputCapture::None;
|
||||
}
|
||||
else if (targetOutputCapturePolicy == TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture::StdOut))
|
||||
{
|
||||
return Policy::TargetOutputCapture::StdOut;
|
||||
}
|
||||
else if (targetOutputCapturePolicy == TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture::StdOutAndFile))
|
||||
{
|
||||
return Policy::TargetOutputCapture::StdOutAndFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(AZStd::string::format("Unexpected target output capture policy: '%s'", targetOutputCapturePolicy.c_str()));
|
||||
}
|
||||
}
|
||||
} // namespace TestImpact
|
||||
|
||||
@@ -62,6 +62,7 @@ ly_add_target(
|
||||
3rdParty::Qt::Core
|
||||
3rdParty::Qt::Widgets
|
||||
3rdParty::Qt::Gui
|
||||
3rdParty::astc-encoder
|
||||
3rdParty::etc2comp
|
||||
3rdParty::PVRTexTool
|
||||
3rdParty::squish-ccr
|
||||
@@ -77,8 +78,6 @@ ly_add_target(
|
||||
Gem::Atom_RPI.Public
|
||||
Gem::Atom_RHI.Reflect
|
||||
Gem::Atom_Utils.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
3rdParty::ASTCEncoder
|
||||
|
||||
)
|
||||
ly_add_source_properties(
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* 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 <astcenc.h>
|
||||
|
||||
#include <AzCore/Jobs/JobCompletion.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
|
||||
#include <Atom/ImageProcessing/ImageObject.h>
|
||||
#include <Compressors/ASTCCompressor.h>
|
||||
#include <Processing/ImageFlags.h>
|
||||
#include <Processing/ImageToProcess.h>
|
||||
#include <Processing/PixelFormatInfo.h>
|
||||
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
bool ASTCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
|
||||
{
|
||||
return IsASTCFormat(fmt);
|
||||
}
|
||||
|
||||
bool ASTCCompressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
|
||||
{
|
||||
// astc encoder requires the compress input image or decompress output image to have four channels
|
||||
switch (fmt)
|
||||
{
|
||||
// uint 8
|
||||
case ePixelFormat_R8G8B8A8:
|
||||
case ePixelFormat_R8G8B8X8:
|
||||
// fp16
|
||||
case ePixelFormat_R16G16B16A16F:
|
||||
// fp32
|
||||
case ePixelFormat_R32G32B32A32F:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EPixelFormat ASTCCompressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const
|
||||
{
|
||||
if (IsUncompressedPixelFormatSupported(uncompressedfmt))
|
||||
{
|
||||
return uncompressedfmt;
|
||||
}
|
||||
|
||||
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(uncompressedfmt);
|
||||
switch (formatInfo->eSampleType)
|
||||
{
|
||||
case ESampleType::eSampleType_Half:
|
||||
return ePixelFormat_R16G16B16A16F;
|
||||
case ESampleType::eSampleType_Float:
|
||||
return ePixelFormat_R32G32B32A32F;
|
||||
}
|
||||
|
||||
return ePixelFormat_R8G8B8A8;
|
||||
}
|
||||
|
||||
ColorSpace ASTCCompressor::GetSupportedColorSpace([[maybe_unused]] EPixelFormat compressFormat) const
|
||||
{
|
||||
return ColorSpace::autoSelect;
|
||||
}
|
||||
|
||||
const char* ASTCCompressor::GetName() const
|
||||
{
|
||||
return "ASTCCompressor";
|
||||
}
|
||||
|
||||
bool ASTCCompressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
astcenc_profile GetAstcProfile(bool isSrgb, EPixelFormat pixelFormat)
|
||||
{
|
||||
// select profile depends on LDR or HDR, SRGB or Linear
|
||||
// ASTCENC_PRF_LDR
|
||||
// ASTCENC_PRF_LDR_SRGB
|
||||
// ASTCENC_PRF_HDR_RGB_LDR_A
|
||||
// ASTCENC_PRF_HDR
|
||||
|
||||
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
|
||||
bool isHDR = formatInfo->eSampleType == ESampleType::eSampleType_Half || formatInfo->eSampleType == ESampleType::eSampleType_Float;
|
||||
astcenc_profile profile;
|
||||
if (isHDR)
|
||||
{
|
||||
// HDR is not support in core vulkan 1.1 for android.
|
||||
// https://arm-software.github.io/vulkan-sdk/_a_s_t_c.html
|
||||
profile = isSrgb?ASTCENC_PRF_HDR_RGB_LDR_A:ASTCENC_PRF_HDR;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
profile = isSrgb?ASTCENC_PRF_LDR_SRGB:ASTCENC_PRF_LDR;
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
astcenc_type GetAstcDataType(EPixelFormat pixelFormat)
|
||||
{
|
||||
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
|
||||
astcenc_type dataType = ASTCENC_TYPE_U8;
|
||||
|
||||
switch (formatInfo->eSampleType)
|
||||
{
|
||||
case ESampleType::eSampleType_Uint8:
|
||||
dataType = ASTCENC_TYPE_U8;
|
||||
break;
|
||||
case ESampleType::eSampleType_Half:
|
||||
dataType = ASTCENC_TYPE_F16;
|
||||
break;
|
||||
case ESampleType::eSampleType_Float:
|
||||
dataType = ASTCENC_TYPE_F32;
|
||||
break;
|
||||
default:
|
||||
dataType = ASTCENC_TYPE_U8;
|
||||
AZ_Assert(false, "Unsupport uncompressed format %s", formatInfo->szName);
|
||||
break;
|
||||
}
|
||||
|
||||
return dataType;
|
||||
}
|
||||
|
||||
float GetAstcCompressQuality(ICompressor::EQuality quality)
|
||||
{
|
||||
switch (quality)
|
||||
{
|
||||
case ICompressor::EQuality::eQuality_Fast:
|
||||
return ASTCENC_PRE_FAST;
|
||||
case ICompressor::EQuality::eQuality_Slow:
|
||||
return ASTCENC_PRE_THOROUGH;
|
||||
case ICompressor::EQuality::eQuality_Preview:
|
||||
case ICompressor::EQuality::eQuality_Normal:
|
||||
default:
|
||||
return ASTCENC_PRE_MEDIUM;
|
||||
}
|
||||
}
|
||||
|
||||
IImageObjectPtr ASTCCompressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const
|
||||
{
|
||||
//validate input
|
||||
EPixelFormat fmtSrc = srcImage->GetPixelFormat();
|
||||
|
||||
//src format need to be uncompressed and dst format need to compressed.
|
||||
if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
astcenc_swizzle swizzle {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, compressOption->discardAlpha? ASTCENC_SWZ_1:ASTCENC_SWZ_A};
|
||||
AZ::u32 flags = 0;
|
||||
if (srcImage->HasImageFlags(EIF_RenormalizedTexture))
|
||||
{
|
||||
ImageToProcess imageToProcess(srcImage);
|
||||
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
|
||||
srcImage = imageToProcess.Get();
|
||||
fmtSrc = srcImage->GetPixelFormat();
|
||||
|
||||
flags = ASTCENC_FLG_MAP_NORMAL;
|
||||
swizzle = astcenc_swizzle{ ASTCENC_SWZ_R, ASTCENC_SWZ_R, ASTCENC_SWZ_R, ASTCENC_SWZ_G };
|
||||
}
|
||||
|
||||
auto dstFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtDst);
|
||||
|
||||
const float quality = GetAstcCompressQuality(compressOption->compressQuality);
|
||||
const astcenc_profile profile = GetAstcProfile(srcImage->HasImageFlags(EIF_SRGBRead), fmtSrc);
|
||||
|
||||
astcenc_config config;
|
||||
astcenc_error status;
|
||||
status = astcenc_config_init(profile, dstFormatInfo->blockWidth, dstFormatInfo->blockHeight, 1, quality, flags, &config);
|
||||
|
||||
//ASTCENC_FLG_MAP_NORMAL
|
||||
AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec config init failed: %s\n", astcenc_get_error_string(status));
|
||||
|
||||
// Create a context based on the configuration
|
||||
astcenc_context* context;
|
||||
AZ::u32 blockCount = ((srcImage->GetWidth(0)+ dstFormatInfo->blockWidth-1)/dstFormatInfo->blockWidth) * ((srcImage->GetHeight(0) + dstFormatInfo->blockHeight-1)/dstFormatInfo->blockHeight);
|
||||
AZ::u32 threadCount = AZStd::min(AZStd::thread::hardware_concurrency(), blockCount);
|
||||
status = astcenc_context_alloc(&config, threadCount, &context);
|
||||
AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(status));
|
||||
|
||||
const astcenc_type dataType =GetAstcDataType(fmtSrc);
|
||||
|
||||
// Compress the image for each mips
|
||||
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
|
||||
const AZ::u32 dstMips = dstImage->GetMipCount();
|
||||
for (AZ::u32 mip = 0; mip < dstMips; ++mip)
|
||||
{
|
||||
astcenc_image image;
|
||||
image.dim_x = srcImage->GetWidth(mip);
|
||||
image.dim_y = srcImage->GetHeight(mip);
|
||||
image.dim_z = 1;
|
||||
image.data_type = dataType;
|
||||
|
||||
AZ::u8* srcMem;
|
||||
AZ::u32 srcPitch;
|
||||
srcImage->GetImagePointer(mip, srcMem, srcPitch);
|
||||
image.data = reinterpret_cast<void**>(&srcMem);
|
||||
|
||||
AZ::u8* dstMem;
|
||||
AZ::u32 dstPitch;
|
||||
dstImage->GetImagePointer(mip, dstMem, dstPitch);
|
||||
AZ::u32 dataSize = dstImage->GetMipBufSize(mip);
|
||||
|
||||
// Create jobs for each compression thread
|
||||
auto completionJob = aznew AZ::JobCompletion();
|
||||
for (AZ::u32 threadIdx = 0; threadIdx < threadCount; threadIdx++)
|
||||
{
|
||||
const auto jobLambda = [&status, context, &image, &swizzle, dstMem, dataSize, threadIdx]()
|
||||
{
|
||||
astcenc_error error = astcenc_compress_image(context, &image, &swizzle, dstMem, dataSize, threadIdx);
|
||||
if (error != ASTCENC_SUCCESS)
|
||||
{
|
||||
status = error;
|
||||
}
|
||||
};
|
||||
|
||||
AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes
|
||||
simulationJob->SetDependent(completionJob);
|
||||
simulationJob->Start();
|
||||
}
|
||||
|
||||
if (completionJob)
|
||||
{
|
||||
completionJob->StartAndWaitForCompletion();
|
||||
delete completionJob;
|
||||
completionJob = nullptr;
|
||||
}
|
||||
|
||||
if (status != ASTCENC_SUCCESS)
|
||||
{
|
||||
AZ_Error("Image Processing", false, "ASTCCompressor::CompressImage failed: %s\n", astcenc_get_error_string(status));
|
||||
astcenc_context_free(context);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Need to reset to compress next mip
|
||||
astcenc_compress_reset(context);
|
||||
}
|
||||
astcenc_context_free(context);
|
||||
|
||||
return dstImage;
|
||||
}
|
||||
|
||||
IImageObjectPtr ASTCCompressor::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const
|
||||
{
|
||||
//validate input
|
||||
EPixelFormat fmtSrc = srcImage->GetPixelFormat(); //compressed
|
||||
auto srcFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtSrc);
|
||||
|
||||
if (!IsCompressedPixelFormatSupported(fmtSrc) || !IsUncompressedPixelFormatSupported(fmtDst))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const float quality = ASTCENC_PRE_MEDIUM;
|
||||
astcenc_swizzle swizzle {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A};
|
||||
if (srcImage->HasImageFlags(EIF_RenormalizedTexture))
|
||||
{
|
||||
swizzle = astcenc_swizzle{ASTCENC_SWZ_R, ASTCENC_SWZ_A, ASTCENC_SWZ_Z, ASTCENC_SWZ_1};
|
||||
}
|
||||
|
||||
astcenc_config config;
|
||||
astcenc_error status;
|
||||
astcenc_profile profile = GetAstcProfile(srcImage->HasImageFlags(EIF_SRGBRead), fmtDst);
|
||||
AZ::u32 flags = ASTCENC_FLG_DECOMPRESS_ONLY;
|
||||
status = astcenc_config_init(profile, srcFormatInfo->blockWidth, srcFormatInfo->blockHeight, 1, quality, flags, &config);
|
||||
|
||||
//ASTCENC_FLG_MAP_NORMAL
|
||||
AZ_Assert( status == ASTCENC_SUCCESS, "astcenc_config_init failed: %s\n", astcenc_get_error_string(status));
|
||||
|
||||
// Create a context based on the configuration
|
||||
const AZ::u32 threadCount = 1; // Decompress function doesn't support multiple threads
|
||||
astcenc_context* context;
|
||||
status = astcenc_context_alloc(&config, threadCount, &context);
|
||||
AZ_Assert( status == ASTCENC_SUCCESS, "astcenc_context_alloc failed: %s\n", astcenc_get_error_string(status));
|
||||
|
||||
astcenc_type dataType =GetAstcDataType(fmtDst);
|
||||
|
||||
// Decompress the image for each mips
|
||||
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
|
||||
const AZ::u32 dstMips = dstImage->GetMipCount();
|
||||
for (AZ::u32 mip = 0; mip < dstMips; ++mip)
|
||||
{
|
||||
astcenc_image image;
|
||||
image.dim_x = srcImage->GetWidth(mip);
|
||||
image.dim_y = srcImage->GetHeight(mip);
|
||||
image.dim_z = 1;
|
||||
image.data_type = dataType;
|
||||
|
||||
AZ::u8* srcMem;
|
||||
AZ::u32 srcPitch;
|
||||
srcImage->GetImagePointer(mip, srcMem, srcPitch);
|
||||
AZ::u32 srcDataSize = srcImage->GetMipBufSize(mip);
|
||||
|
||||
AZ::u8* dstMem;
|
||||
AZ::u32 dstPitch;
|
||||
dstImage->GetImagePointer(mip, dstMem, dstPitch);
|
||||
image.data = reinterpret_cast<void**>(&dstMem);
|
||||
|
||||
status = astcenc_decompress_image(context, srcMem, srcDataSize, &image, &swizzle, 0);
|
||||
|
||||
if (status != ASTCENC_SUCCESS)
|
||||
{
|
||||
AZ_Error("Image Processing", false, "ASTCCompressor::DecompressImage failed: %s\n", astcenc_get_error_string(status));
|
||||
astcenc_context_free(context);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
astcenc_context_free(context);
|
||||
|
||||
return dstImage;
|
||||
}
|
||||
} //namespace ImageProcessingAtom
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 <Compressors/Compressor.h>
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
class ASTCCompressor
|
||||
: public ICompressor
|
||||
{
|
||||
public:
|
||||
static bool IsCompressedPixelFormatSupported(EPixelFormat fmt);
|
||||
static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt);
|
||||
static bool DoesSupportDecompress(EPixelFormat fmtDst);
|
||||
|
||||
IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const override;
|
||||
IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const override;
|
||||
|
||||
EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const override;
|
||||
ColorSpace GetSupportedColorSpace(EPixelFormat compressFormat) const final;
|
||||
const char* GetName() const final;
|
||||
};
|
||||
} // namespace ImageProcessingAtom
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
#include <Compressors/ASTCCompressor.h>
|
||||
#include <Compressors/CTSquisher.h>
|
||||
#include <Compressors/PVRTC.h>
|
||||
#include <Compressors/ETC2.h>
|
||||
@@ -15,11 +16,8 @@
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
ICompressorPtr ICompressor::FindCompressor(EPixelFormat fmt, [[maybe_unused]] ColorSpace colorSpace, bool isCompressing)
|
||||
ICompressorPtr ICompressor::FindCompressor(EPixelFormat fmt, ColorSpace colorSpace, bool isCompressing)
|
||||
{
|
||||
// The ISPC texture compressor is able to compress BC1, BC3, BC6H and BC7 formats, and all of the ASTC formats.
|
||||
// Note: The ISPC texture compressor is only able to compress images that are a multiple of the compressed format's blocksize.
|
||||
// Another limitation is that the compressor requires LDR source images to be in sRGB colorspace.
|
||||
if (ISPCCompressor::IsCompressedPixelFormatSupported(fmt))
|
||||
{
|
||||
if ((isCompressing && ISPCCompressor::IsSourceColorSpaceSupported(colorSpace, fmt)) || (!isCompressing && ISPCCompressor::DoesSupportDecompress(fmt)))
|
||||
@@ -35,6 +33,14 @@ namespace ImageProcessingAtom
|
||||
return ICompressorPtr(new CTSquisher());
|
||||
}
|
||||
}
|
||||
|
||||
if (ASTCCompressor::IsCompressedPixelFormatSupported(fmt))
|
||||
{
|
||||
if (isCompressing || (!isCompressing && ASTCCompressor::DoesSupportDecompress(fmt)))
|
||||
{
|
||||
return ICompressorPtr(new ASTCCompressor());
|
||||
}
|
||||
}
|
||||
|
||||
// Both ETC2Compressor and PVRTCCompressor can process ETC formats
|
||||
// According to Mobile team, Etc2Com is faster than PVRTexLib, so we check with ETC2Compressor before PVRTCCompressor
|
||||
|
||||
@@ -38,8 +38,7 @@ namespace ImageProcessingAtom
|
||||
EQuality compressQuality = eQuality_Normal;
|
||||
//required for CTSquisher
|
||||
AZ::Vector3 rgbWeight = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
|
||||
//required for ISPC texture compressor
|
||||
bool ispcDiscardAlpha = false;
|
||||
bool discardAlpha = false;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
+13
-19
@@ -57,6 +57,12 @@ namespace ImageProcessingAtom
|
||||
|
||||
bool ISPCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
|
||||
{
|
||||
// Even though the ISPC compressor support ASTC formats. But it has restrictions
|
||||
// 1. Only supports LDR color profile
|
||||
// 2. Only supports a subset of 2D block sizes
|
||||
// Also it has overall lower quality compare to astc-encoder
|
||||
// So we won't add ASTC as part of supported formats here
|
||||
// Ref: https://solidpixel.github.io/2020/03/02/astc-compared.html
|
||||
switch (fmt)
|
||||
{
|
||||
case ePixelFormat_BC3:
|
||||
@@ -152,7 +158,7 @@ namespace ImageProcessingAtom
|
||||
if (compressOption)
|
||||
{
|
||||
quality = compressOption->compressQuality;
|
||||
discardAlpha = compressOption->ispcDiscardAlpha;
|
||||
discardAlpha = compressOption->discardAlpha;
|
||||
}
|
||||
|
||||
// Get the compression profile
|
||||
@@ -230,24 +236,12 @@ namespace ImageProcessingAtom
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (IsASTCFormat(destinationFormat))
|
||||
{
|
||||
const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo(destinationFormat);
|
||||
astc_enc_settings settings = {};
|
||||
|
||||
const auto setProfile = compressionProfile->GetASTC(discardAlpha);
|
||||
setProfile(&settings, info->blockWidth, info->blockHeight);
|
||||
|
||||
// Compress with ASTC
|
||||
CompressBlocksASTC(&sourceSurface, destinationImageData, &settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No valid pixel format
|
||||
AZ_Assert(false, "Unhandled pixel format %d", destinationFormat);
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
{
|
||||
// No valid pixel format
|
||||
AZ_Assert(false, "Unhandled pixel format %d", destinationFormat);
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
namespace ImageProcessingAtom
|
||||
{
|
||||
// Note: PVRTexLib supports ASTC formats, ETC formats, PVRTC formats and BC formats
|
||||
// Note: PVRTexLib supports ETC formats, PVRTC formats and BC formats
|
||||
// We haven't tested the performace to compress BC formats compare to CTSquisher
|
||||
// For PVRTC formats, we only added PVRTC 1 support for now
|
||||
// The compression for ePVRTPF_EAC_R11 and ePVRTPF_EAC_RG11 are very slow. It takes 7 and 14 minutes for a 2048x2048 texture.
|
||||
@@ -27,34 +27,6 @@ namespace ImageProcessingAtom
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case ePixelFormat_ASTC_4x4:
|
||||
return ePVRTPF_ASTC_4x4;
|
||||
case ePixelFormat_ASTC_5x4:
|
||||
return ePVRTPF_ASTC_5x4;
|
||||
case ePixelFormat_ASTC_5x5:
|
||||
return ePVRTPF_ASTC_5x5;
|
||||
case ePixelFormat_ASTC_6x5:
|
||||
return ePVRTPF_ASTC_6x5;
|
||||
case ePixelFormat_ASTC_6x6:
|
||||
return ePVRTPF_ASTC_6x6;
|
||||
case ePixelFormat_ASTC_8x5:
|
||||
return ePVRTPF_ASTC_8x5;
|
||||
case ePixelFormat_ASTC_8x6:
|
||||
return ePVRTPF_ASTC_8x6;
|
||||
case ePixelFormat_ASTC_8x8:
|
||||
return ePVRTPF_ASTC_8x8;
|
||||
case ePixelFormat_ASTC_10x5:
|
||||
return ePVRTPF_ASTC_10x5;
|
||||
case ePixelFormat_ASTC_10x6:
|
||||
return ePVRTPF_ASTC_10x6;
|
||||
case ePixelFormat_ASTC_10x8:
|
||||
return ePVRTPF_ASTC_10x8;
|
||||
case ePixelFormat_ASTC_10x10:
|
||||
return ePVRTPF_ASTC_10x10;
|
||||
case ePixelFormat_ASTC_12x10:
|
||||
return ePVRTPF_ASTC_12x10;
|
||||
case ePixelFormat_ASTC_12x12:
|
||||
return ePVRTPF_ASTC_12x12;
|
||||
case ePixelFormat_PVRTC2:
|
||||
return ePVRTPF_PVRTCI_2bpp_RGBA;
|
||||
case ePixelFormat_PVRTC4:
|
||||
@@ -156,26 +128,7 @@ namespace ImageProcessingAtom
|
||||
internalQuality = pvrtexture::eETCSlow;
|
||||
}
|
||||
}
|
||||
else if (IsASTCFormat(fmtDst))
|
||||
{
|
||||
if (quality == eQuality_Preview)
|
||||
{
|
||||
internalQuality = pvrtexture::eASTCVeryFast;
|
||||
}
|
||||
else if (quality == eQuality_Fast)
|
||||
{
|
||||
internalQuality = pvrtexture::eASTCFast;
|
||||
}
|
||||
else if (quality == eQuality_Normal)
|
||||
{
|
||||
internalQuality = pvrtexture::eASTCMedium;
|
||||
}
|
||||
else
|
||||
{
|
||||
internalQuality = pvrtexture::eASTCThorough;
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
if (quality == eQuality_Preview)
|
||||
{
|
||||
@@ -252,7 +205,7 @@ namespace ImageProcessingAtom
|
||||
|
||||
if (!isSuccess)
|
||||
{
|
||||
AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib. You may not have astcenc.exe for compressing ASTC formates");
|
||||
AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,11 @@ namespace ImageProcessingAtom
|
||||
}
|
||||
}
|
||||
|
||||
const ImageConvertProcessDescriptor* ImageConvertProcess::GetInputDesc() const
|
||||
{
|
||||
return m_input.get();
|
||||
}
|
||||
|
||||
ImageConvertProcess::ImageConvertProcess(AZStd::unique_ptr<ImageConvertProcessDescriptor>&& descriptor)
|
||||
: m_image(nullptr)
|
||||
, m_progressStep(0)
|
||||
@@ -554,12 +559,6 @@ namespace ImageProcessingAtom
|
||||
// pixel format conversion
|
||||
bool ImageConvertProcess::ConvertPixelformat()
|
||||
{
|
||||
//For ASTC compression we need to clear out the alpha to get accurate rgb compression.
|
||||
if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat))
|
||||
{
|
||||
m_image->Get()->Swizzle("rgb1");
|
||||
}
|
||||
|
||||
//set up compress option
|
||||
ICompressor::EQuality quality;
|
||||
if (m_input->m_isPreview)
|
||||
@@ -574,7 +573,14 @@ namespace ImageProcessingAtom
|
||||
// set the compression options
|
||||
m_image->GetCompressOption().compressQuality = quality;
|
||||
m_image->GetCompressOption().rgbWeight = m_input->m_presetSetting.GetColorWeight();
|
||||
m_image->GetCompressOption().ispcDiscardAlpha = m_input->m_presetSetting.m_discardAlpha;
|
||||
m_image->GetCompressOption().discardAlpha = m_input->m_presetSetting.m_discardAlpha;
|
||||
|
||||
//For ASTC compression we need to clear out the alpha to get accurate rgb compression.
|
||||
if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat))
|
||||
{
|
||||
m_image->GetCompressOption().discardAlpha = true;
|
||||
}
|
||||
|
||||
m_image->ConvertFormat(m_input->m_presetSetting.m_pixelFormat);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -122,6 +122,8 @@ namespace ImageProcessingAtom
|
||||
// Get output JobProducts and append them to the outProducts vector.
|
||||
void GetAppendOutputProducts(AZStd::vector<AssetBuilderSDK::JobProduct>& outProducts);
|
||||
|
||||
const ImageConvertProcessDescriptor* GetInputDesc() const;
|
||||
|
||||
private:
|
||||
//input image and settings
|
||||
AZStd::shared_ptr<ImageConvertProcessDescriptor> m_input;
|
||||
|
||||
@@ -12,8 +12,12 @@
|
||||
|
||||
#include <AzQtComponents/Utilities/QtPluginPaths.h>
|
||||
|
||||
#include <AzCore/AzCore_Traits_Platform.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetManagerComponent.h>
|
||||
#include <AzCore/Jobs/JobContext.h>
|
||||
#include <AzCore/Jobs/JobManager.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Memory/PoolAllocator.h>
|
||||
#include <AzCore/RTTI/ReflectionManager.h>
|
||||
@@ -123,6 +127,9 @@ namespace UnitTest
|
||||
AZStd::string m_outputRootFolder;
|
||||
AZStd::string m_outputFolder;
|
||||
|
||||
AZStd::unique_ptr<AZ::JobManager> m_jobManager;
|
||||
AZStd::unique_ptr<AZ::JobContext> m_jobContext;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
AllocatorsBase::SetupAllocator();
|
||||
@@ -159,6 +166,27 @@ namespace UnitTest
|
||||
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
|
||||
BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get());
|
||||
|
||||
// Setup job context for job system
|
||||
JobManagerDesc jobManagerDesc;
|
||||
JobManagerThreadDesc threadDesc;
|
||||
#if AZ_TRAIT_SET_JOB_PROCESSOR_ID
|
||||
threadDesc.m_cpuId = 0; // Don't set processors IDs on windows
|
||||
#endif
|
||||
|
||||
uint32_t numWorkerThreads = AZStd::thread::hardware_concurrency();
|
||||
|
||||
for (unsigned int i = 0; i < numWorkerThreads; ++i)
|
||||
{
|
||||
jobManagerDesc.m_workerThreads.push_back(threadDesc);
|
||||
#if AZ_TRAIT_SET_JOB_PROCESSOR_ID
|
||||
threadDesc.m_cpuId++;
|
||||
#endif
|
||||
}
|
||||
|
||||
m_jobManager = AZStd::make_unique<JobManager>(jobManagerDesc);
|
||||
m_jobContext = AZStd::make_unique<JobContext>(*m_jobManager);
|
||||
JobContext::SetGlobalContext(m_jobContext.get());
|
||||
|
||||
// Startup default local FileIO (hits OSAllocator) if not already setup.
|
||||
if (AZ::IO::FileIOBase::GetInstance() == nullptr)
|
||||
{
|
||||
@@ -192,6 +220,10 @@ namespace UnitTest
|
||||
delete AZ::IO::FileIOBase::GetInstance();
|
||||
AZ::IO::FileIOBase::SetInstance(nullptr);
|
||||
|
||||
JobContext::SetGlobalContext(nullptr);
|
||||
m_jobContext = nullptr;
|
||||
m_jobManager = nullptr;
|
||||
|
||||
m_jsonRegistrationContext->EnableRemoveReflection();
|
||||
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
|
||||
BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get());
|
||||
@@ -223,7 +255,7 @@ namespace UnitTest
|
||||
Image_512X288_RGB8_Tga,
|
||||
Image_1024X1024_RGB8_Tif,
|
||||
Image_UpperCase_Tga,
|
||||
Image_512x512_Normal_Tga, // QImage doesn't support loading this file.
|
||||
Image_1024x1024_normal_tiff,
|
||||
Image_128x128_Transparent_Tga,
|
||||
Image_237x177_RGB_Jpg,
|
||||
Image_GreyScale_Png,
|
||||
@@ -251,7 +283,7 @@ namespace UnitTest
|
||||
m_imagFileNameMap[Image_512X288_RGB8_Tga] = m_testFileFolder + "512x288_24bit.tga";
|
||||
m_imagFileNameMap[Image_1024X1024_RGB8_Tif] = m_testFileFolder + "1024x1024_24bit.tif";
|
||||
m_imagFileNameMap[Image_UpperCase_Tga] = m_testFileFolder + "uppercase.TGA";
|
||||
m_imagFileNameMap[Image_512x512_Normal_Tga] = m_testFileFolder + "512x512_RGB_N.tga";
|
||||
m_imagFileNameMap[Image_1024x1024_normal_tiff] = m_testFileFolder + "1024x1024_normal.tiff";
|
||||
m_imagFileNameMap[Image_128x128_Transparent_Tga] = m_testFileFolder + "128x128_RGBA8.tga";
|
||||
m_imagFileNameMap[Image_237x177_RGB_Jpg] = m_testFileFolder + "237x177_RGB.jpg";
|
||||
m_imagFileNameMap[Image_GreyScale_Png] = m_testFileFolder + "greyscale.png";
|
||||
@@ -801,8 +833,7 @@ namespace UnitTest
|
||||
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
|
||||
if (formatInfo->bCompressed)
|
||||
{
|
||||
// exclude astc formats until we add astc compressor to all platforms
|
||||
// exclude pvrtc formats (deprecating)
|
||||
// skip ASTC formats which are tested in TestConvertASTCCompressor
|
||||
if (!IsASTCFormat(pixelFormat)
|
||||
&& pixelFormat != ePixelFormat_PVRTC2 && pixelFormat != ePixelFormat_PVRTC4
|
||||
&& !IsETCFormat(pixelFormat)) // skip ETC since it's very slow
|
||||
@@ -830,32 +861,121 @@ namespace UnitTest
|
||||
continue;
|
||||
}
|
||||
|
||||
[[maybe_unused]] auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
|
||||
imageToProcess.Set(srcImage);
|
||||
imageToProcess.ConvertFormat(pixelFormat);
|
||||
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
|
||||
ColorSpace sourceColorSpace = srcImage->HasImageFlags(EIF_SRGBRead) ? ColorSpace::sRGB : ColorSpace::linear;
|
||||
ICompressorPtr compressor = ICompressor::FindCompressor(pixelFormat, sourceColorSpace, true);
|
||||
|
||||
if (!imageToProcess.Get())
|
||||
if (!compressor)
|
||||
{
|
||||
AZ_Warning("test", false, "unsupported format: %s", formatInfo->szName);
|
||||
continue;
|
||||
}
|
||||
|
||||
imageToProcess.Set(srcImage);
|
||||
imageToProcess.ConvertFormat(pixelFormat);
|
||||
|
||||
ASSERT_TRUE(imageToProcess.Get());
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat);
|
||||
|
||||
// Get compressor name
|
||||
ColorSpace sourceColorSpace = srcImage->HasImageFlags(EIF_SRGBRead) ? ColorSpace::sRGB : ColorSpace::linear;
|
||||
ICompressorPtr compressor = ICompressor::FindCompressor(pixelFormat, sourceColorSpace, true);
|
||||
//convert back to an uncompressed format and expect it will be successful
|
||||
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
|
||||
|
||||
//save the image to a file so we can check the visual result
|
||||
// Save the image to a file so we can check the visual result
|
||||
AZStd::string outputName = AZStd::string::format("%s_%s", imageName.c_str(), compressor->GetName());
|
||||
SaveImageToFile(imageToProcess.Get(), outputName, 1);
|
||||
|
||||
//convert back to an uncompressed format and expect it will be successful
|
||||
imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8);
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == ePixelFormat_R8G8B8A8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ImageProcessingTest, Test_ConvertAllAstc_Success)
|
||||
{
|
||||
// Compress/Decompress to all astc formats (LDR)
|
||||
auto imageIdx = Image_237x177_RGB_Jpg;
|
||||
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[imageIdx]));
|
||||
QFileInfo fi(m_imagFileNameMap[imageIdx].c_str());
|
||||
AZStd::string imageName = fi.baseName().toUtf8().constData();
|
||||
for (uint32 i = 0; i < ePixelFormat_Count; i++)
|
||||
{
|
||||
EPixelFormat pixelFormat = (EPixelFormat)i;
|
||||
if (IsASTCFormat(pixelFormat))
|
||||
{
|
||||
ImageToProcess imageToProcess(srcImage);
|
||||
imageToProcess.ConvertFormat(pixelFormat);
|
||||
|
||||
ASSERT_TRUE(imageToProcess.Get());
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat);
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == srcImage->GetWidth(0));
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetHeight(0) == srcImage->GetHeight(0));
|
||||
|
||||
// convert back to an uncompressed format and expect it will be successful
|
||||
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
|
||||
|
||||
// save the image to a file so we can check the visual result
|
||||
AZStd::string outputName = AZStd::string::format("ASTC_%s", imageName.c_str());
|
||||
SaveImageToFile(imageToProcess.Get(), outputName, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ImageProcessingTest, Test_ConvertHdrToAstc_Success)
|
||||
{
|
||||
// Compress/Decompress HDR
|
||||
auto imageIdx = Image_defaultprobe_cm_1536x256_64bits_tif;
|
||||
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[imageIdx]));
|
||||
|
||||
EPixelFormat dstFormat = ePixelFormat_ASTC_4x4;
|
||||
ImageToProcess imageToProcess(srcImage);
|
||||
imageToProcess.ConvertFormat(ePixelFormat_ASTC_4x4);
|
||||
|
||||
ASSERT_TRUE(imageToProcess.Get());
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == dstFormat);
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == srcImage->GetWidth(0));
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetHeight(0) == srcImage->GetHeight(0));
|
||||
|
||||
//convert back to an uncompressed format and expect it will be successful
|
||||
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
|
||||
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
|
||||
|
||||
//save the image to a file so we can check the visual result
|
||||
SaveImageToFile(imageToProcess.Get(), "ASTC_HDR", 1);
|
||||
}
|
||||
|
||||
TEST_F(ImageProcessingTest, Test_AstcNormalPreset_Success)
|
||||
{
|
||||
// Normal.preset which uses ASTC as output format
|
||||
// This test compress a normal texture and its mipmaps
|
||||
|
||||
auto outcome = BuilderSettingManager::Instance()->LoadConfigFromFolder(m_defaultSettingFolder);
|
||||
ASSERT_TRUE(outcome.IsSuccess());
|
||||
|
||||
AZStd::string inputFile;
|
||||
AZStd::vector<AssetBuilderSDK::JobProduct> outProducts;
|
||||
|
||||
inputFile = m_imagFileNameMap[Image_1024x1024_normal_tiff];
|
||||
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(inputFile));
|
||||
|
||||
ImageConvertProcess* process = CreateImageConvertProcess(inputFile, m_outputFolder, "ios", outProducts, m_context.get());
|
||||
|
||||
const PresetSettings* preset = &process->GetInputDesc()->m_presetSetting;
|
||||
|
||||
if (process != nullptr)
|
||||
{
|
||||
process->ProcessAll();
|
||||
|
||||
//get process result
|
||||
ASSERT_TRUE(process->IsSucceed());
|
||||
auto outputImage = process->GetOutputImage();
|
||||
ASSERT_TRUE(outputImage->GetPixelFormat() == preset->m_pixelFormat);
|
||||
ASSERT_TRUE(outputImage->GetWidth(0) == srcImage->GetWidth(0));
|
||||
ASSERT_TRUE(outputImage->GetHeight(0) == srcImage->GetHeight(0));
|
||||
|
||||
SaveImageToFile(outputImage, "ASTC_Normal", 10);
|
||||
|
||||
delete process;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ImageProcessingTest, DISABLED_TestImageFilter)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:120aaf43057b07fb3c784264eb48b899cc612f30917d316fe843bb839220dc22
|
||||
size 203062
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:646a9a9035cc3f4dfd57babc0055710d2f5bb8aee0a792f2b65d69b4fd6a94b3
|
||||
size 786450
|
||||
@@ -114,6 +114,8 @@ set(FILES
|
||||
../External/CubeMapGen/CImageSurface.cpp
|
||||
../External/CubeMapGen/CImageSurface.h
|
||||
../External/CubeMapGen/VectorMacros.h
|
||||
Source/Compressors/ASTCCompressor.cpp
|
||||
Source/Compressors/ASTCCompressor.h
|
||||
Source/Compressors/Compressor.h
|
||||
Source/Compressors/Compressor.cpp
|
||||
Source/Compressors/CTSquisher.h
|
||||
|
||||
@@ -1421,17 +1421,16 @@ namespace EMotionFX
|
||||
// extract sorted active items
|
||||
void Recorder::ExtractNodeHistoryItems(const ActorInstanceData& actorInstanceData, float timeValue, bool sort, EValueType valueType, AZStd::vector<ExtractedNodeHistoryItem>* outItems, AZStd::vector<size_t>* outMap) const
|
||||
{
|
||||
// clear the map array
|
||||
// Reinit the item array.
|
||||
const size_t maxIndex = CalcMaxNodeHistoryTrackIndex(actorInstanceData);
|
||||
outItems->resize(maxIndex + 1);
|
||||
for (size_t i = 0; i <= maxIndex; ++i)
|
||||
{
|
||||
ExtractedNodeHistoryItem item;
|
||||
item.m_trackIndex = i;
|
||||
item.m_value = 0.0f;
|
||||
ExtractedNodeHistoryItem& item = (*outItems)[i];
|
||||
item.m_nodeHistoryItem = nullptr;
|
||||
item.m_trackIndex = i;
|
||||
item.m_keyTrackSampleTime = 0.0f;
|
||||
item.m_nodeHistoryItem = nullptr;
|
||||
outItems->emplace(AZStd::next(begin(*outItems), i), AZStd::move(item));
|
||||
item.m_value = 0.0f;
|
||||
}
|
||||
|
||||
// find all node history items
|
||||
@@ -1440,7 +1439,7 @@ namespace EMotionFX
|
||||
{
|
||||
if (curItem->m_startTime <= timeValue && curItem->m_endTime > timeValue)
|
||||
{
|
||||
ExtractedNodeHistoryItem item;
|
||||
ExtractedNodeHistoryItem& item = (*outItems)[curItem->m_trackIndex];
|
||||
item.m_trackIndex = curItem->m_trackIndex;
|
||||
item.m_keyTrackSampleTime = timeValue - curItem->m_startTime;
|
||||
item.m_nodeHistoryItem = curItem;
|
||||
@@ -1463,8 +1462,6 @@ namespace EMotionFX
|
||||
MCORE_ASSERT(false); // unsupported mode
|
||||
item.m_value = curItem->m_globalWeights.GetValueAtTime(item.m_keyTrackSampleTime, nullptr, nullptr, m_recordSettings.m_interpolate);
|
||||
}
|
||||
|
||||
outItems->emplace(AZStd::next(begin(*outItems), curItem->m_trackIndex), item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1472,7 +1469,7 @@ namespace EMotionFX
|
||||
outMap->resize(maxIndex + 1);
|
||||
for (size_t i = 0; i <= maxIndex; ++i)
|
||||
{
|
||||
outMap->emplace(AZStd::next(begin(*outMap), i), i);
|
||||
(*outMap)[i] = i;
|
||||
}
|
||||
|
||||
// sort if desired
|
||||
@@ -1482,7 +1479,7 @@ namespace EMotionFX
|
||||
|
||||
for (size_t i = 0; i <= maxIndex; ++i)
|
||||
{
|
||||
outMap->emplace(AZStd::next(begin(*outMap), outItems->at(i).m_trackIndex), i);
|
||||
(*outMap)[outItems->at(i).m_trackIndex] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,10 +168,10 @@ namespace EMotionFX
|
||||
|
||||
struct EMFX_API ExtractedNodeHistoryItem
|
||||
{
|
||||
NodeHistoryItem* m_nodeHistoryItem;
|
||||
size_t m_trackIndex;
|
||||
float m_value;
|
||||
float m_keyTrackSampleTime;
|
||||
NodeHistoryItem* m_nodeHistoryItem = nullptr;
|
||||
size_t m_trackIndex = 0;
|
||||
float m_value = 0.0f;
|
||||
float m_keyTrackSampleTime = 0.0f;
|
||||
|
||||
friend bool operator< (const ExtractedNodeHistoryItem& a, const ExtractedNodeHistoryItem& b) { return (a.m_value > b.m_value); }
|
||||
friend bool operator==(const ExtractedNodeHistoryItem& a, const ExtractedNodeHistoryItem& b) { return (a.m_value == b.m_value); }
|
||||
|
||||
+6
-1
@@ -1461,10 +1461,15 @@ namespace EMStudio
|
||||
|
||||
// show the create window
|
||||
auto createWindow = new ParameterCreateRenameWindow("Create Group", "Please enter the group name:", uniqueGroupName.c_str(), "", invalidNames, this);
|
||||
connect(createWindow, &QDialog::finished, this, [this, createWindow]()
|
||||
connect(createWindow, &QDialog::finished, this, [this, createWindow](int resultCode)
|
||||
{
|
||||
createWindow->deleteLater();
|
||||
|
||||
if (resultCode == QDialog::Rejected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string command = AZStd::string::format("AnimGraphAddGroupParameter -animGraphID %i -name \"%s\"", m_animGraph->GetID(), createWindow->GetName().c_str());
|
||||
const EMotionFX::GroupParameter* parentGroup = nullptr;
|
||||
const EMotionFX::Parameter* selectedParameter = GetSingleSelectedParameter();
|
||||
|
||||
@@ -114,6 +114,16 @@ endif()
|
||||
# Tests
|
||||
################################################################################
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_target(
|
||||
NAME LmbrCentral.Mocks HEADERONLY
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
lmbrcentral_mocks_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
INTERFACE
|
||||
Mocks
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME LmbrCentral.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
@@ -131,6 +141,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
Legacy::CryCommon
|
||||
AZ::AzFramework
|
||||
Gem::LmbrCentral.Static
|
||||
Gem::LmbrCentral.Mocks
|
||||
)
|
||||
ly_add_googletest(
|
||||
NAME Gem::LmbrCentral.Tests
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 <gmock/gmock.h>
|
||||
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
|
||||
#include <LmbrCentral/Shape/ShapeComponentBus.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class MockBoxShapeComponentRequests
|
||||
: public LmbrCentral::BoxShapeComponentRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
MockBoxShapeComponentRequests(AZ::EntityId entityId)
|
||||
{
|
||||
LmbrCentral::BoxShapeComponentRequestsBus::Handler::BusConnect(entityId);
|
||||
}
|
||||
|
||||
~MockBoxShapeComponentRequests()
|
||||
{
|
||||
LmbrCentral::BoxShapeComponentRequestsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_METHOD0(GetBoxConfiguration, LmbrCentral::BoxShapeConfig());
|
||||
MOCK_METHOD0(GetBoxDimensions, AZ::Vector3());
|
||||
MOCK_METHOD1(SetBoxDimensions, void(const AZ::Vector3& newDimensions));
|
||||
};
|
||||
|
||||
class MockShapeComponentRequests
|
||||
: public LmbrCentral::ShapeComponentRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
MockShapeComponentRequests(AZ::EntityId entityId)
|
||||
{
|
||||
LmbrCentral::ShapeComponentRequestsBus::Handler::BusConnect(entityId);
|
||||
}
|
||||
|
||||
~MockShapeComponentRequests()
|
||||
{
|
||||
LmbrCentral::ShapeComponentRequestsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_METHOD0(GetShapeType, AZ::Crc32());
|
||||
MOCK_METHOD0(GetEncompassingAabb, AZ::Aabb());
|
||||
MOCK_METHOD2(GetTransformAndLocalBounds, void(AZ::Transform& transform, AZ::Aabb& bounds));
|
||||
MOCK_METHOD1(IsPointInside, bool(const AZ::Vector3& point));
|
||||
MOCK_METHOD1(DistanceSquaredFromPoint, float(const AZ::Vector3& point));
|
||||
MOCK_METHOD1(GenerateRandomPointInside, AZ::Vector3(AZ::RandomDistributionType randomDistribution));
|
||||
MOCK_METHOD3(IntersectRay, bool(const AZ::Vector3& src, const AZ::Vector3& dir, float& distance));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,4 +8,7 @@
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
// Include any public mocks here to ensure they get compiled as a part of the test project.
|
||||
#include <LmbrCentral/Shape/MockShapes.h>
|
||||
|
||||
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Mocks/LmbrCentral/Shape/MockShapes.h
|
||||
)
|
||||
@@ -82,4 +82,18 @@ namespace PhysX
|
||||
};
|
||||
|
||||
using EditorColliderComponentRequestBus = AZ::EBus<EditorColliderComponentRequests>;
|
||||
|
||||
/// <EditorColliderValidationRequests>
|
||||
/// This is a Bus in order to communicate the status of the meshes of the collider and avoid dependencies with the rigidbody
|
||||
/// </EditorColliderValidationRequests>
|
||||
class EditorColliderValidationRequests : public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
/// Checks if the the mesh in the collider is correct with the current state of the Rigidbody!
|
||||
virtual void ValidateRigidBodyMeshGeometryType() = 0;
|
||||
};
|
||||
|
||||
using EditorColliderValidationRequestBus = AZ::EBus<EditorColliderValidationRequests>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -118,11 +118,10 @@ namespace PhysX
|
||||
|
||||
AZ::u32 EditorProxyShapeConfig::OnShapeTypeChanged()
|
||||
{
|
||||
//reset the physics asset if the shape type was Physics Asset
|
||||
if (m_shapeType != Physics::ShapeType::PhysicsAsset &&
|
||||
m_lastShapeType == Physics::ShapeType::PhysicsAsset)
|
||||
// reset the physics asset if the shape type was Physics Asset
|
||||
if (m_shapeType != Physics::ShapeType::PhysicsAsset && m_lastShapeType == Physics::ShapeType::PhysicsAsset)
|
||||
{
|
||||
//clean up any reference to a physics assets, and re-initialize to an empty Pipeline::MeshAsset asset.
|
||||
// clean up any reference to a physics assets, and re-initialize to an empty Pipeline::MeshAsset asset.
|
||||
m_physicsAsset.m_pxAsset.Reset();
|
||||
m_physicsAsset.m_pxAsset = AZ::Data::Asset<Pipeline::MeshAsset>(AZ::Data::AssetLoadBehavior::QueueLoad);
|
||||
|
||||
@@ -212,6 +211,7 @@ namespace PhysX
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_shapeConfiguration, "Shape Configuration", "Configuration of the shape")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorColliderComponent::OnConfigurationChanged)
|
||||
->Attribute(AZ::Edit::Attributes::RemoveNotify, &EditorColliderComponent::ValidateRigidBodyMeshGeometryType)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_componentModeDelegate, "Component Mode", "Collider Component Mode")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &EditorColliderComponent::m_colliderDebugDraw,
|
||||
@@ -383,6 +383,7 @@ namespace PhysX
|
||||
ColliderShapeRequestBus::Handler::BusConnect(GetEntityId());
|
||||
AZ::Render::MeshComponentNotificationBus::Handler::BusConnect(GetEntityId());
|
||||
EditorColliderComponentRequestBus::Handler::BusConnect(AZ::EntityComponentIdPair(GetEntityId(), GetId()));
|
||||
EditorColliderValidationRequestBus::Handler::BusConnect(GetEntityId());
|
||||
m_nonUniformScaleChangedHandler = AZ::NonUniformScaleChangedEvent::Handler(
|
||||
[this](const AZ::Vector3& scale) {OnNonUniformScaleChanged(scale); });
|
||||
AZ::NonUniformScaleRequestBus::Event(GetEntityId(), &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent,
|
||||
@@ -427,6 +428,7 @@ namespace PhysX
|
||||
m_colliderDebugDraw.Disconnect();
|
||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
|
||||
m_nonUniformScaleChangedHandler.Disconnect();
|
||||
EditorColliderValidationRequestBus::Handler::BusDisconnect();
|
||||
EditorColliderComponentRequestBus::Handler::BusDisconnect();
|
||||
AZ::Render::MeshComponentNotificationBus::Handler::BusDisconnect();
|
||||
ColliderShapeRequestBus::Handler::BusDisconnect();
|
||||
@@ -466,6 +468,7 @@ namespace PhysX
|
||||
|
||||
UpdateShapeConfigurationScale();
|
||||
CreateStaticEditorCollider();
|
||||
ValidateRigidBodyMeshGeometryType();
|
||||
|
||||
m_colliderDebugDraw.ClearCachedGeometry();
|
||||
|
||||
@@ -768,15 +771,16 @@ namespace PhysX
|
||||
{
|
||||
m_componentWarnings.clear();
|
||||
m_configuration.m_materialSelection.SetMaterialSlots(Physics::MaterialSelection::SlotsArray());
|
||||
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
|
||||
}
|
||||
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
|
||||
}
|
||||
|
||||
void EditorColliderComponent::ValidateRigidBodyMeshGeometryType()
|
||||
{
|
||||
const PhysX::EditorRigidBodyComponent* entityRigidbody = m_entity->FindComponent<PhysX::EditorRigidBodyComponent>();
|
||||
|
||||
if (m_shapeConfiguration.m_physicsAsset.m_configuration.GetShapeType() == Physics::ShapeType::PhysicsAsset && entityRigidbody)
|
||||
if (m_shapeConfiguration.m_physicsAsset.m_pxAsset && (m_shapeConfiguration.m_shapeType == Physics::ShapeType::PhysicsAsset) && entityRigidbody)
|
||||
{
|
||||
AZStd::vector<AZStd::shared_ptr<Physics::Shape>> shapes;
|
||||
Utils::GetShapesFromAsset(m_shapeConfiguration.m_physicsAsset.m_configuration, m_configuration, m_hasNonUniformScale,
|
||||
@@ -784,17 +788,32 @@ namespace PhysX
|
||||
|
||||
if (shapes.empty())
|
||||
{
|
||||
m_componentWarnings.clear();
|
||||
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
|
||||
return;
|
||||
}
|
||||
|
||||
//We grab the first shape to check if it is a triangle mesh.
|
||||
auto shape = AZStd::rtti_pointer_cast<PhysX::Shape>(shapes[0]);
|
||||
//We check if the shapes are triangle meshes, if any mesh is a triangle mesh we activate the warning.
|
||||
bool shapeIsTriangleMesh = false;
|
||||
|
||||
if (shape &&
|
||||
shape->GetPxShape()->getGeometryType() == physx::PxGeometryType::eTRIANGLEMESH &&
|
||||
entityRigidbody->GetRigidBody() &&
|
||||
entityRigidbody->GetRigidBody()->IsKinematic() == false)
|
||||
for (const auto& shape : shapes)
|
||||
{
|
||||
auto current_shape = AZStd::rtti_pointer_cast<PhysX::Shape>(shape);
|
||||
if (current_shape &&
|
||||
current_shape->GetPxShape()->getGeometryType() == physx::PxGeometryType::eTRIANGLEMESH &&
|
||||
entityRigidbody->GetRigidBody() &&
|
||||
entityRigidbody->GetRigidBody()->IsKinematic() == false)
|
||||
{
|
||||
shapeIsTriangleMesh = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shapeIsTriangleMesh)
|
||||
{
|
||||
m_componentWarnings.clear();
|
||||
|
||||
AZStd::string assetPath = m_shapeConfiguration.m_physicsAsset.m_configuration.m_asset.GetHint().c_str();
|
||||
const size_t lastSlash = assetPath.rfind('/');
|
||||
if (lastSlash != AZStd::string::npos)
|
||||
@@ -816,6 +835,10 @@ namespace PhysX
|
||||
{
|
||||
m_componentWarnings.clear();
|
||||
}
|
||||
|
||||
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
|
||||
|
||||
}
|
||||
|
||||
void EditorColliderComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
||||
|
||||
@@ -58,8 +58,8 @@ namespace PhysX
|
||||
//! Proxy container for only displaying a specific shape configuration depending on the shapeType selected.
|
||||
struct EditorProxyShapeConfig
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR(PhysX::EditorProxyShapeConfig, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(PhysX::EditorProxyShapeConfig, "{531FB42A-42A9-4234-89BA-FD349EF83D0C}");
|
||||
AZ_CLASS_ALLOCATOR(EditorProxyShapeConfig, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(EditorProxyShapeConfig, "{531FB42A-42A9-4234-89BA-FD349EF83D0C}");
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
EditorProxyShapeConfig() = default;
|
||||
@@ -106,6 +106,7 @@ namespace PhysX
|
||||
, private PhysX::ColliderShapeRequestBus::Handler
|
||||
, private AZ::Render::MeshComponentNotificationBus::Handler
|
||||
, private PhysX::EditorColliderComponentRequestBus::Handler
|
||||
, private PhysX::EditorColliderValidationRequestBus::Handler
|
||||
, private AzPhysics::SimulatedBodyComponentRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
@@ -197,6 +198,9 @@ namespace PhysX
|
||||
void SetAssetScale(const AZ::Vector3& scale) override;
|
||||
AZ::Vector3 GetAssetScale() override;
|
||||
|
||||
// PhysX::EditorColliderValidationRequestBus overrides ...
|
||||
void ValidateRigidBodyMeshGeometryType() override;
|
||||
|
||||
AZ::Transform GetColliderLocalTransform() const;
|
||||
|
||||
EditorProxyShapeConfig m_shapeConfiguration;
|
||||
@@ -223,8 +227,6 @@ namespace PhysX
|
||||
|
||||
void BuildDebugDrawMesh() const;
|
||||
|
||||
void ValidateRigidBodyMeshGeometryType();
|
||||
|
||||
AZ::ComponentDescriptor::StringWarningArray GetComponentWarnings() const { return m_componentWarnings; };
|
||||
|
||||
using ComponentModeDelegate = AzToolsFramework::ComponentModeFramework::ComponentModeDelegate;
|
||||
|
||||
@@ -286,6 +286,9 @@ namespace PhysX
|
||||
}
|
||||
CreateEditorWorldRigidBody();
|
||||
|
||||
PhysX::EditorColliderValidationRequestBus::Event(
|
||||
GetEntityId(), &PhysX::EditorColliderValidationRequestBus::Events::ValidateRigidBodyMeshGeometryType);
|
||||
|
||||
AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusConnect(GetEntityId());
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,15 @@ endif()
|
||||
################################################################################
|
||||
# See if globally, tests are supported
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_target(
|
||||
NAME Terrain.Mocks HEADERONLY
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
terrain_mocks_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
INTERFACE
|
||||
Mocks
|
||||
)
|
||||
ly_add_target(
|
||||
NAME Terrain.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
@@ -97,6 +106,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
AZ::AzFramework
|
||||
Gem::LmbrCentral.Mocks
|
||||
Gem::Terrain.Mocks
|
||||
Gem::Terrain.Static
|
||||
)
|
||||
|
||||
@@ -120,6 +131,8 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Gem::LmbrCentral.Mocks
|
||||
Gem::Terrain.Mocks
|
||||
Gem::Terrain.Editor
|
||||
)
|
||||
|
||||
|
||||
+36
-50
@@ -11,59 +11,9 @@
|
||||
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
|
||||
#include <LmbrCentral/Shape/ShapeComponentBus.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
static const AZ::Uuid BoxShapeComponentTypeId = "{5EDF4B9E-0D3D-40B8-8C91-5142BCFC30A6}";
|
||||
|
||||
class MockBoxShapeComponent
|
||||
: public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(MockBoxShapeComponent, BoxShapeComponentTypeId)
|
||||
static void Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
}
|
||||
|
||||
void Activate() override
|
||||
{
|
||||
}
|
||||
|
||||
void Deactivate() override
|
||||
{
|
||||
}
|
||||
|
||||
bool ReadInConfig([[maybe_unused]] const AZ::ComponentConfig* baseConfig) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteOutConfig([[maybe_unused]] AZ::ComponentConfig* outBaseConfig) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static void GetProvidedServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC("ShapeService", 0xe86aa5fe));
|
||||
provided.push_back(AZ_CRC("BoxShapeService", 0x946a0032));
|
||||
provided.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService"));
|
||||
}
|
||||
|
||||
static void GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
}
|
||||
|
||||
static void GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
}
|
||||
|
||||
static void GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class MockTerrainSystemService : private Terrain::TerrainSystemServiceRequestBus::Handler
|
||||
{
|
||||
@@ -106,4 +56,40 @@ namespace UnitTest
|
||||
MOCK_METHOD2(OnTerrainDataChanged, void(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask));
|
||||
};
|
||||
|
||||
class MockTerrainAreaHeightRequests : public Terrain::TerrainAreaHeightRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
MockTerrainAreaHeightRequests(AZ::EntityId entityId)
|
||||
{
|
||||
Terrain::TerrainAreaHeightRequestBus::Handler::BusConnect(entityId);
|
||||
}
|
||||
|
||||
~MockTerrainAreaHeightRequests()
|
||||
{
|
||||
Terrain::TerrainAreaHeightRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_METHOD3(GetHeight, void(
|
||||
const AZ::Vector3& inPosition,
|
||||
AZ::Vector3& outPosition,
|
||||
bool& terrainExists));
|
||||
|
||||
};
|
||||
|
||||
class MockTerrainSpawnerRequests : public Terrain::TerrainSpawnerRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
MockTerrainSpawnerRequests(AZ::EntityId entityId)
|
||||
{
|
||||
Terrain::TerrainSpawnerRequestBus::Handler::BusConnect(entityId);
|
||||
}
|
||||
|
||||
~MockTerrainSpawnerRequests()
|
||||
{
|
||||
Terrain::TerrainSpawnerRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_METHOD2(GetPriority, void(AZ::u32& outLayer, AZ::u32& outPriority));
|
||||
MOCK_METHOD0(GetUseGroundPlane, bool());
|
||||
};
|
||||
}
|
||||
@@ -142,11 +142,15 @@ namespace Terrain
|
||||
return false;
|
||||
}
|
||||
|
||||
float TerrainHeightGradientListComponent::GetHeight(float x, float y)
|
||||
void TerrainHeightGradientListComponent::GetHeight(
|
||||
const AZ::Vector3& inPosition,
|
||||
AZ::Vector3& outPosition,
|
||||
bool& terrainExists)
|
||||
{
|
||||
float maxSample = 0.0f;
|
||||
terrainExists = false;
|
||||
|
||||
GradientSignal::GradientSampleParams params(AZ::Vector3(x, y, 0.0f));
|
||||
GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f));
|
||||
|
||||
// Right now, when the list contains multiple entries, we will use the highest point from each gradient.
|
||||
// This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value
|
||||
@@ -155,49 +159,20 @@ namespace Terrain
|
||||
// make this list a prioritized list from top to bottom for any points that overlap.
|
||||
for (auto& gradientId : m_configuration.m_gradientEntities)
|
||||
{
|
||||
// If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain
|
||||
// to *not* exist at a specific point.
|
||||
terrainExists = true;
|
||||
|
||||
float sample = 0.0f;
|
||||
GradientSignal::GradientRequestBus::EventResult(sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
|
||||
GradientSignal::GradientRequestBus::EventResult(
|
||||
sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
|
||||
maxSample = AZ::GetMax(maxSample, sample);
|
||||
}
|
||||
|
||||
const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample);
|
||||
|
||||
return AZ::GetClamp(height, m_cachedMinWorldHeight, m_cachedMaxWorldHeight);
|
||||
outPosition.SetZ(AZ::GetClamp(height, m_cachedMinWorldHeight, m_cachedMaxWorldHeight));
|
||||
}
|
||||
|
||||
void TerrainHeightGradientListComponent::GetHeight(
|
||||
const AZ::Vector3& inPosition,
|
||||
AZ::Vector3& outPosition,
|
||||
[[maybe_unused]] AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter =
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT)
|
||||
{
|
||||
const float height = GetHeight(inPosition.GetX(), inPosition.GetY());
|
||||
outPosition.SetZ(height);
|
||||
}
|
||||
|
||||
void TerrainHeightGradientListComponent::GetNormal(
|
||||
const AZ::Vector3& inPosition,
|
||||
AZ::Vector3& outNormal,
|
||||
[[maybe_unused]] AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter =
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT)
|
||||
{
|
||||
const float x = inPosition.GetX();
|
||||
const float y = inPosition.GetY();
|
||||
|
||||
if ((x >= m_cachedShapeBounds.GetMin().GetX()) && (x <= m_cachedShapeBounds.GetMax().GetX()) &&
|
||||
(y >= m_cachedShapeBounds.GetMin().GetY()) && (y <= m_cachedShapeBounds.GetMax().GetY()))
|
||||
{
|
||||
AZ::Vector2 fRange = (m_cachedHeightQueryResolution / 2.0f) + AZ::Vector2(0.05f);
|
||||
|
||||
AZ::Vector3 v1(x - fRange.GetX(), y - fRange.GetY(), GetHeight(x - fRange.GetX(), y - fRange.GetY()));
|
||||
AZ::Vector3 v2(x - fRange.GetX(), y + fRange.GetY(), GetHeight(x - fRange.GetX(), y + fRange.GetY()));
|
||||
AZ::Vector3 v3(x + fRange.GetX(), y - fRange.GetY(), GetHeight(x + fRange.GetX(), y - fRange.GetY()));
|
||||
AZ::Vector3 v4(x + fRange.GetX(), y + fRange.GetY(), GetHeight(x + fRange.GetX(), y + fRange.GetY()));
|
||||
outNormal = (v3 - v2).Cross(v4 - v1).GetNormalized();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TerrainHeightGradientListComponent::OnCompositionChanged()
|
||||
{
|
||||
RefreshMinMaxHeights();
|
||||
|
||||
@@ -64,14 +64,7 @@ namespace Terrain
|
||||
TerrainHeightGradientListComponent() = default;
|
||||
~TerrainHeightGradientListComponent() = default;
|
||||
|
||||
void GetHeight(
|
||||
const AZ::Vector3& inPosition,
|
||||
AZ::Vector3& outPosition,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter) override;
|
||||
void GetNormal(
|
||||
const AZ::Vector3& inPosition,
|
||||
AZ::Vector3& outNormal,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter) override;
|
||||
void GetHeight(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component interface implementation
|
||||
@@ -91,11 +84,7 @@ namespace Terrain
|
||||
private:
|
||||
TerrainHeightGradientListConfig m_configuration;
|
||||
|
||||
///////////////////////////////////////////
|
||||
void GetNormalSynchronous(float x, float y, AZ::Vector3& normal);
|
||||
|
||||
void RefreshMinMaxHeights();
|
||||
float GetHeight(float x, float y);
|
||||
|
||||
float m_cachedMinWorldHeight{ 0.0f };
|
||||
float m_cachedMaxWorldHeight{ 0.0f };
|
||||
|
||||
@@ -285,13 +285,13 @@ namespace Terrain
|
||||
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
z00, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists);
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists);
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
z01, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y1,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists);
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists);
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
z10, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x1, y,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT, &terrainExists);
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists);
|
||||
|
||||
sector.m_lineVertices.push_back(AZ::Vector3(x, y, z00));
|
||||
sector.m_lineVertices.push_back(AZ::Vector3(x1, y, z10));
|
||||
|
||||
@@ -152,27 +152,69 @@ AZ::Vector2 TerrainSystem::GetTerrainHeightQueryResolution() const
|
||||
return m_currentSettings.m_heightQueryResolution;
|
||||
}
|
||||
|
||||
void TerrainSystem::ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const
|
||||
{
|
||||
// Given an input position, clamp the values to our terrain grid, where it will always go to the terrain grid point
|
||||
// at a lower value, whether positive or negative. Ex: 3.3 -> 3, -3.3 -> -4
|
||||
// Also, return the normalized delta as a value of [0-1) describing what fraction of a grid point the value moved.
|
||||
|
||||
// Scale the position by the query resolution, so that integer values represent exact steps on the grid,
|
||||
// and fractional values are the amount in-between each grid point, in the range [0-1).
|
||||
AZ::Vector2 normalizedPosition = AZ::Vector2(x, y) / m_currentSettings.m_heightQueryResolution;
|
||||
normalizedDelta = AZ::Vector2(
|
||||
normalizedPosition.GetX() - floor(normalizedPosition.GetX()), normalizedPosition.GetY() - floor(normalizedPosition.GetY()));
|
||||
|
||||
// Remove the fractional part, then scale back down into world space.
|
||||
outPosition = (normalizedPosition - normalizedDelta) * m_currentSettings.m_heightQueryResolution;
|
||||
}
|
||||
|
||||
float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const
|
||||
{
|
||||
bool terrainExists = false;
|
||||
|
||||
AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ());
|
||||
AZ::Vector3 outPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ());
|
||||
float height = m_currentSettings.m_worldBounds.GetMin().GetZ();
|
||||
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
for (auto& [areaId, areaBounds] : m_registeredAreas)
|
||||
switch (sampler)
|
||||
{
|
||||
inPosition.SetZ(areaBounds.GetMin().GetZ());
|
||||
if (areaBounds.Contains(inPosition))
|
||||
// Get the value at the requested location, using the terrain grid to bilinear filter between sample grid points.
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::BILINEAR:
|
||||
{
|
||||
Terrain::TerrainAreaHeightRequestBus::Event(
|
||||
areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, sampler);
|
||||
// pos0 contains one corner of our grid square, pos1 contains the opposite corner, and normalizedDelta is the fractional
|
||||
// amount the position exists between those corners.
|
||||
// Ex: (3.3, 4.4) would have a pos0 of (3, 4), a pos1 of (4, 5), and a delta of (0.3, 0.4).
|
||||
AZ::Vector2 normalizedDelta;
|
||||
AZ::Vector2 pos0;
|
||||
ClampPosition(x, y, pos0, normalizedDelta);
|
||||
const AZ::Vector2 pos1 = pos0 + m_currentSettings.m_heightQueryResolution;
|
||||
|
||||
terrainExists = true;
|
||||
|
||||
break;
|
||||
const float heightX0Y0 = GetTerrainAreaHeight(pos0.GetX(), pos0.GetY(), terrainExists);
|
||||
const float heightX1Y0 = GetTerrainAreaHeight(pos1.GetX(), pos0.GetY(), terrainExists);
|
||||
const float heightX0Y1 = GetTerrainAreaHeight(pos0.GetX(), pos1.GetY(), terrainExists);
|
||||
const float heightX1Y1 = GetTerrainAreaHeight(pos1.GetX(), pos1.GetY(), terrainExists);
|
||||
const float heightXY0 = AZ::Lerp(heightX0Y0, heightX1Y0, normalizedDelta.GetX());
|
||||
const float heightXY1 = AZ::Lerp(heightX0Y1, heightX1Y1, normalizedDelta.GetX());
|
||||
height = AZ::Lerp(heightXY0, heightXY1, normalizedDelta.GetY());
|
||||
}
|
||||
break;
|
||||
|
||||
//! Clamp the input point to the terrain sample grid, then get the height at the given grid location.
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::CLAMP:
|
||||
{
|
||||
AZ::Vector2 normalizedDelta;
|
||||
AZ::Vector2 clampedPosition;
|
||||
ClampPosition(x, y, clampedPosition, normalizedDelta);
|
||||
|
||||
height = GetTerrainAreaHeight(clampedPosition.GetX(), clampedPosition.GetY(), terrainExists);
|
||||
}
|
||||
break;
|
||||
|
||||
//! Directly get the value at the location, regardless of terrain sample grid density.
|
||||
case AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
height = GetTerrainAreaHeight(x, y, terrainExists);
|
||||
break;
|
||||
}
|
||||
|
||||
if (terrainExistsPtr)
|
||||
@@ -181,7 +223,30 @@ float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, boo
|
||||
}
|
||||
|
||||
return AZ::GetClamp(
|
||||
outPosition.GetZ(), m_currentSettings.m_worldBounds.GetMin().GetZ(), m_currentSettings.m_worldBounds.GetMax().GetZ());
|
||||
height, m_currentSettings.m_worldBounds.GetMin().GetZ(), m_currentSettings.m_worldBounds.GetMax().GetZ());
|
||||
}
|
||||
|
||||
float TerrainSystem::GetTerrainAreaHeight(float x, float y, bool& terrainExists) const
|
||||
{
|
||||
AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ());
|
||||
float height = m_currentSettings.m_worldBounds.GetMin().GetZ();
|
||||
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
for (auto& [areaId, areaBounds] : m_registeredAreas)
|
||||
{
|
||||
inPosition.SetZ(areaBounds.GetMin().GetZ());
|
||||
if (areaBounds.Contains(inPosition))
|
||||
{
|
||||
AZ::Vector3 outPosition;
|
||||
Terrain::TerrainAreaHeightRequestBus::Event(
|
||||
areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists);
|
||||
height = outPosition.GetZ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
float TerrainSystem::GetHeight(AZ::Vector3 position, Sampler sampler, bool* terrainExistsPtr) const
|
||||
@@ -203,24 +268,24 @@ bool TerrainSystem::GetIsHoleFromFloats(float x, float y, Sampler sampler) const
|
||||
|
||||
AZ::Vector3 TerrainSystem::GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const
|
||||
{
|
||||
bool terrainExists = false;
|
||||
|
||||
AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ());
|
||||
AZ::Vector3 outNormal = AZ::Vector3::CreateAxisZ();
|
||||
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_areaMutex);
|
||||
|
||||
for (auto& [areaId, areaBounds] : m_registeredAreas)
|
||||
{
|
||||
inPosition.SetZ(areaBounds.GetMin().GetZ());
|
||||
if (areaBounds.Contains(inPosition))
|
||||
{
|
||||
Terrain::TerrainAreaHeightRequestBus::Event(
|
||||
areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetNormal, inPosition, outNormal, sampler);
|
||||
terrainExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool terrainExists = false;
|
||||
|
||||
AZ::Vector3 outNormal = AZ::Vector3::CreateAxisZ();
|
||||
|
||||
const AZ::Vector2 range = (m_currentSettings.m_heightQueryResolution / 2.0f);
|
||||
const AZ::Vector2 left (x - range.GetX(), y);
|
||||
const AZ::Vector2 right(x + range.GetX(), y);
|
||||
const AZ::Vector2 up (x, y - range.GetY());
|
||||
const AZ::Vector2 down (x, y + range.GetY());
|
||||
|
||||
AZ::Vector3 v1(up.GetX(), up.GetY(), GetHeightSynchronous(up.GetX(), up.GetY(), sampler, &terrainExists));
|
||||
AZ::Vector3 v2(left.GetX(), left.GetY(), GetHeightSynchronous(left.GetX(), left.GetY(), sampler, &terrainExists));
|
||||
AZ::Vector3 v3(right.GetX(), right.GetY(), GetHeightSynchronous(right.GetX(), right.GetY(), sampler, &terrainExists));
|
||||
AZ::Vector3 v4(down.GetX(), down.GetY(), GetHeightSynchronous(down.GetX(), down.GetY(), sampler, &terrainExists));
|
||||
|
||||
outNormal = (v3 - v2).Cross(v4 - v1).GetNormalized();
|
||||
|
||||
if (terrainExistsPtr)
|
||||
{
|
||||
@@ -469,43 +534,30 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
|
||||
{
|
||||
for (uint32_t x = 0; x < width; x++)
|
||||
{
|
||||
// Find the first terrain layer that covers this position. This will be the highest priority, so others can be ignored.
|
||||
for (auto& [areaId, areaBounds] : m_registeredAreas)
|
||||
{
|
||||
AZ::Vector3 inPosition(
|
||||
(x * m_currentSettings.m_heightQueryResolution.GetX()) + m_currentSettings.m_worldBounds.GetMin().GetX(),
|
||||
(y * m_currentSettings.m_heightQueryResolution.GetY()) + m_currentSettings.m_worldBounds.GetMin().GetY(),
|
||||
areaBounds.GetMin().GetZ());
|
||||
bool terrainExists;
|
||||
float terrainHeight = GetTerrainAreaHeight(
|
||||
(x * m_currentSettings.m_heightQueryResolution.GetX()) + m_currentSettings.m_worldBounds.GetMin().GetX(),
|
||||
(y * m_currentSettings.m_heightQueryResolution.GetY()) + m_currentSettings.m_worldBounds.GetMin().GetY(),
|
||||
terrainExists);
|
||||
|
||||
if (!areaBounds.Contains(inPosition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AZ::Vector3 outPosition;
|
||||
const AzFramework::Terrain::TerrainDataRequestBus::Events::Sampler sampleFilter =
|
||||
AzFramework::Terrain::TerrainDataRequestBus::Events::Sampler::DEFAULT;
|
||||
|
||||
Terrain::TerrainAreaHeightRequestBus::Event(
|
||||
areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, sampleFilter);
|
||||
|
||||
pixels[(y * width) + x] = (outPosition.GetZ() - m_currentSettings.m_worldBounds.GetMin().GetZ()) /
|
||||
m_currentSettings.m_worldBounds.GetExtents().GetZ();
|
||||
|
||||
break;
|
||||
}
|
||||
pixels[(y * width) + x] =
|
||||
(terrainHeight - m_currentSettings.m_worldBounds.GetMin().GetZ()) /
|
||||
m_currentSettings.m_worldBounds.GetExtents().GetZ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get();
|
||||
auto terrainFeatureProcessor = scene->GetFeatureProcessor<TerrainFeatureProcessor>();
|
||||
|
||||
AZ_Assert(terrainFeatureProcessor, "Unable to find a TerrainFeatureProcessor.");
|
||||
if (terrainFeatureProcessor)
|
||||
if (auto rpi = AZ::RPI::RPISystemInterface::Get(); rpi)
|
||||
{
|
||||
terrainFeatureProcessor->UpdateTerrainData(
|
||||
transform, m_currentSettings.m_worldBounds, m_currentSettings.m_heightQueryResolution.GetX(), width, height, pixels);
|
||||
if (auto defaultScene = rpi->GetDefaultScene(); defaultScene)
|
||||
{
|
||||
const AZ::RPI::Scene* scene = defaultScene.get();
|
||||
if (auto terrainFeatureProcessor = scene->GetFeatureProcessor<TerrainFeatureProcessor>(); terrainFeatureProcessor)
|
||||
{
|
||||
terrainFeatureProcessor->UpdateTerrainData(
|
||||
transform, m_currentSettings.m_worldBounds, m_currentSettings.m_heightQueryResolution.GetX(), width, height,
|
||||
pixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,8 +94,11 @@ namespace Terrain
|
||||
float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
|
||||
|
||||
private:
|
||||
void ClampPosition(float x, float y, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta) const;
|
||||
|
||||
float GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const;
|
||||
AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const;
|
||||
float GetTerrainAreaHeight(float x, float y, bool& terrainExists) const;
|
||||
AZ::Vector3 GetNormalSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const;
|
||||
|
||||
// AZ::TickBus::Handler overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
@@ -65,27 +65,8 @@ namespace Terrain
|
||||
|
||||
virtual ~TerrainAreaHeightRequests() = default;
|
||||
|
||||
enum SurfacePointDataMask
|
||||
{
|
||||
POSITION = 0x01,
|
||||
NORMAL = 0x02,
|
||||
SURFACE_WEIGHTS = 0x04,
|
||||
|
||||
DEFAULT = POSITION | NORMAL | SURFACE_WEIGHTS
|
||||
};
|
||||
|
||||
// Synchronous single input location. The Vector3 input position versions are defined to ignore the input Z value.
|
||||
|
||||
virtual void GetHeight(
|
||||
const AZ::Vector3& inPosition,
|
||||
AZ::Vector3& outPosition,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter =
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT) = 0;
|
||||
virtual void GetNormal(
|
||||
const AZ::Vector3& inPosition,
|
||||
AZ::Vector3& outNormal,
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter =
|
||||
AzFramework::Terrain::TerrainDataRequests::Sampler::DEFAULT) = 0;
|
||||
virtual void GetHeight(const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists) = 0;
|
||||
};
|
||||
|
||||
using TerrainAreaHeightRequestBus = AZ::EBus<TerrainAreaHeightRequests>;
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <TerrainMocks.h>
|
||||
#include <Terrain/MockTerrain.h>
|
||||
#include <MockAxisAlignedBoxShapeComponent.h>
|
||||
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::AtLeast;
|
||||
using ::testing::_;
|
||||
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::AtLeast;
|
||||
@@ -29,7 +34,7 @@ protected:
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> m_entity;
|
||||
Terrain::TerrainLayerSpawnerComponent* m_layerSpawnerComponent;
|
||||
UnitTest::MockBoxShapeComponent* m_shapeComponent;
|
||||
UnitTest::MockAxisAlignedBoxShapeComponent* m_shapeComponent;
|
||||
AZStd::unique_ptr<NiceMock<UnitTest::MockTerrainSystemService>> m_terrainSystem;
|
||||
|
||||
void SetUp() override
|
||||
@@ -67,7 +72,7 @@ protected:
|
||||
m_layerSpawnerComponent = m_entity->CreateComponent<Terrain::TerrainLayerSpawnerComponent>(config);
|
||||
m_app.RegisterComponentDescriptor(m_layerSpawnerComponent->CreateDescriptor());
|
||||
|
||||
m_shapeComponent = m_entity->CreateComponent<UnitTest::MockBoxShapeComponent>();
|
||||
m_shapeComponent = m_entity->CreateComponent<UnitTest::MockAxisAlignedBoxShapeComponent>();
|
||||
m_app.RegisterComponentDescriptor(m_shapeComponent->CreateDescriptor());
|
||||
|
||||
ASSERT_TRUE(m_layerSpawnerComponent);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 <gmock/gmock.h>
|
||||
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
|
||||
#include <LmbrCentral/Shape/ShapeComponentBus.h>
|
||||
#include <LmbrCentral/Shape/MockShapes.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class MockAxisAlignedBoxShapeComponent
|
||||
: public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(MockAxisAlignedBoxShapeComponent, "{77CBEED3-FAA3-4BC7-85A9-1A2BFC37BC2A}");
|
||||
|
||||
static void Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
}
|
||||
|
||||
void Activate() override
|
||||
{
|
||||
}
|
||||
|
||||
void Deactivate() override
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("ShapeService"));
|
||||
provided.push_back(AZ_CRC_CE("BoxShapeService"));
|
||||
provided.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService"));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -9,20 +9,23 @@
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Memory/MemoryComponent.h>
|
||||
|
||||
#include <TerrainSystem/TerrainSystem.h>
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <TerrainMocks.h>
|
||||
|
||||
#include <TerrainSystem/TerrainSystem.h>
|
||||
#include <Components/TerrainLayerSpawnerComponent.h>
|
||||
#include <Components/TerrainHeightGradientListComponent.h>
|
||||
|
||||
#include <Terrain/MockTerrain.h>
|
||||
#include <MockAxisAlignedBoxShapeComponent.h>
|
||||
|
||||
using ::testing::AtLeast;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
|
||||
class TerrainSystemTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
AZ::ComponentApplication m_app;
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> m_entity;
|
||||
AZStd::unique_ptr<Terrain::TerrainSystem> m_terrainSystem;
|
||||
|
||||
void SetUp() override
|
||||
@@ -41,28 +44,46 @@ protected:
|
||||
m_app.Destroy();
|
||||
}
|
||||
|
||||
void CreateEntity()
|
||||
AZStd::unique_ptr<AZ::Entity> CreateEntity()
|
||||
{
|
||||
m_entity = AZStd::make_unique<AZ::Entity>();
|
||||
m_entity->Init();
|
||||
|
||||
ASSERT_TRUE(m_entity);
|
||||
return AZStd::make_unique<AZ::Entity>();
|
||||
}
|
||||
|
||||
void ResetEntity()
|
||||
void ActivateEntity(AZ::Entity* entity)
|
||||
{
|
||||
m_entity->Deactivate();
|
||||
m_entity->Reset();
|
||||
entity->Init();
|
||||
EXPECT_EQ(AZ::Entity::State::Init, entity->GetState());
|
||||
|
||||
entity->Activate();
|
||||
EXPECT_EQ(AZ::Entity::State::Active, entity->GetState());
|
||||
}
|
||||
|
||||
template<typename Component, typename Configuration>
|
||||
AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config)
|
||||
{
|
||||
m_app.RegisterComponentDescriptor(Component::CreateDescriptor());
|
||||
return entity->CreateComponent<Component>(config);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
AZ::Component* CreateComponent(AZ::Entity* entity)
|
||||
{
|
||||
m_app.RegisterComponentDescriptor(Component::CreateDescriptor());
|
||||
return entity->CreateComponent<Component>();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TerrainSystemTest, TrivialCreateDestroy)
|
||||
{
|
||||
// Trivially verify that the terrain system can successfully be constructed and destructed without errors.
|
||||
|
||||
m_terrainSystem = AZStd::make_unique<Terrain::TerrainSystem>();
|
||||
}
|
||||
|
||||
TEST_F(TerrainSystemTest, TrivialActivateDeactivate)
|
||||
{
|
||||
// Verify that the terrain system can be activated and deactivated without errors.
|
||||
|
||||
m_terrainSystem = AZStd::make_unique<Terrain::TerrainSystem>();
|
||||
m_terrainSystem->Activate();
|
||||
m_terrainSystem->Deactivate();
|
||||
@@ -70,6 +91,8 @@ TEST_F(TerrainSystemTest, TrivialActivateDeactivate)
|
||||
|
||||
TEST_F(TerrainSystemTest, CreateEventsCalledOnActivation)
|
||||
{
|
||||
// Verify that when the terrain system is activated, the OnTerrainDataCreate* ebus notifications are generated.
|
||||
|
||||
NiceMock<UnitTest::MockTerrainDataNotificationListener> mockTerrainListener;
|
||||
EXPECT_CALL(mockTerrainListener, OnTerrainDataCreateBegin()).Times(AtLeast(1));
|
||||
EXPECT_CALL(mockTerrainListener, OnTerrainDataCreateEnd()).Times(AtLeast(1));
|
||||
@@ -80,6 +103,8 @@ TEST_F(TerrainSystemTest, CreateEventsCalledOnActivation)
|
||||
|
||||
TEST_F(TerrainSystemTest, DestroyEventsCalledOnDeactivation)
|
||||
{
|
||||
// Verify that when the terrain system is deactivated, the OnTerrainDataDestroy* ebus notifications are generated.
|
||||
|
||||
NiceMock<UnitTest::MockTerrainDataNotificationListener> mockTerrainListener;
|
||||
EXPECT_CALL(mockTerrainListener, OnTerrainDataDestroyBegin()).Times(AtLeast(1));
|
||||
EXPECT_CALL(mockTerrainListener, OnTerrainDataDestroyEnd()).Times(AtLeast(1));
|
||||
@@ -89,4 +114,115 @@ TEST_F(TerrainSystemTest, DestroyEventsCalledOnDeactivation)
|
||||
m_terrainSystem->Deactivate();
|
||||
}
|
||||
|
||||
TEST_F(TerrainSystemTest, TerrainDoesNotExistWhenNoTerrainLayerSpawnersAreRegistered)
|
||||
{
|
||||
// For the terrain system, terrain should only exist where terrain layer spawners are present.
|
||||
|
||||
// Verify that in the active terrain system, if there are no terrain layer spawners, any arbitrary point
|
||||
// will return false for terrainExists, returns a height equal to the min world bounds of the terrain system, and returns
|
||||
// a normal facing up the Z axis.
|
||||
|
||||
// Create the terrain system and give it one tick to fully initialize itself.
|
||||
m_terrainSystem = AZStd::make_unique<Terrain::TerrainSystem>();
|
||||
m_terrainSystem->Activate();
|
||||
AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{});
|
||||
|
||||
AZ::Aabb worldBounds = m_terrainSystem->GetTerrainAabb();
|
||||
|
||||
// Loop through several points within the world bounds, including on the edges, and verify that they all return false for
|
||||
// terrainExists with default heights and normals.
|
||||
for (float y = worldBounds.GetMin().GetY(); y <= worldBounds.GetMax().GetY(); y += (worldBounds.GetExtents().GetY() / 4.0f))
|
||||
{
|
||||
for (float x = worldBounds.GetMin().GetX(); x <= worldBounds.GetMax().GetX(); x += (worldBounds.GetExtents().GetX() / 4.0f))
|
||||
{
|
||||
AZ::Vector3 position(x, y, 0.0f);
|
||||
bool terrainExists = true;
|
||||
float height = m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists);
|
||||
EXPECT_FALSE(terrainExists);
|
||||
EXPECT_EQ(height, worldBounds.GetMin().GetZ());
|
||||
|
||||
terrainExists = true;
|
||||
AZ::Vector3 normal = m_terrainSystem->GetNormal(
|
||||
position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists);
|
||||
EXPECT_FALSE(terrainExists);
|
||||
EXPECT_EQ(normal, AZ::Vector3::CreateAxisZ());
|
||||
|
||||
bool isHole = m_terrainSystem->GetIsHoleFromFloats(
|
||||
position.GetX(), position.GetY(), AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT);
|
||||
EXPECT_TRUE(isHole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TerrainSystemTest, TerrainExistsOnlyWithinTerrainLayerSpawnerBounds)
|
||||
{
|
||||
// Verify that the presence of a TerrainLayerSpawner causes terrain to exist in (and *only* in) the box where the TerrainLayerSpawner
|
||||
// is defined.
|
||||
|
||||
// The terrain system should only query Heights from the TerrainAreaHeightRequest bus within the
|
||||
// TerrainLayerSpawner region, and so those values should only get returned from GetHeight for queries inside that region.
|
||||
|
||||
// Create the base entity with a mock Box Shape and a Terrain Layer Spawner.
|
||||
auto entity = CreateEntity();
|
||||
CreateComponent<UnitTest::MockAxisAlignedBoxShapeComponent>(entity.get());
|
||||
CreateComponent<Terrain::TerrainLayerSpawnerComponent>(entity.get());
|
||||
|
||||
// Set up the box shape to return a box from (0,0,5) to (10, 10, 15)
|
||||
AZ::Aabb spawnerBox = AZ::Aabb::CreateFromMinMaxValues(0.0f, 0.0f, 5.0f, 10.0f, 10.0f, 15.0f);
|
||||
NiceMock<UnitTest::MockBoxShapeComponentRequests> boxShapeRequests(entity->GetId());
|
||||
NiceMock<UnitTest::MockShapeComponentRequests> shapeRequests(entity->GetId());
|
||||
ON_CALL(shapeRequests, GetEncompassingAabb).WillByDefault(Return(spawnerBox));
|
||||
|
||||
// Set up a mock height provider that always returns 5.0 and a normal of Y-up.
|
||||
const float spawnerHeight = 5.0f;
|
||||
NiceMock<UnitTest::MockTerrainAreaHeightRequests> terrainAreaHeightRequests(entity->GetId());
|
||||
ON_CALL(terrainAreaHeightRequests, GetHeight)
|
||||
.WillByDefault(
|
||||
[spawnerHeight](const AZ::Vector3& inPosition, AZ::Vector3& outPosition, bool& terrainExists)
|
||||
{
|
||||
outPosition = inPosition;
|
||||
outPosition.SetZ(spawnerHeight);
|
||||
terrainExists = true;
|
||||
});
|
||||
|
||||
ActivateEntity(entity.get());
|
||||
|
||||
// Verify that terrain exists within the layer spawner bounds, and doesn't exist outside of it.
|
||||
|
||||
// Create the terrain system and give it one tick to fully initialize itself.
|
||||
m_terrainSystem = AZStd::make_unique<Terrain::TerrainSystem>();
|
||||
m_terrainSystem->Activate();
|
||||
AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{});
|
||||
|
||||
// Create a box that's twice as big as the layer spawner box. Loop through it and verify that points within the layer box contain
|
||||
// terrain and the expected height & normal values, and points outside the layer box don't contain terrain.
|
||||
const AZ::Aabb encompassingBox =
|
||||
AZ::Aabb::CreateFromMinMax(spawnerBox.GetMin() - (spawnerBox.GetExtents() / 2.0f),
|
||||
spawnerBox.GetMax() + (spawnerBox.GetExtents() / 2.0f));
|
||||
|
||||
for (float y = encompassingBox.GetMin().GetY(); y < encompassingBox.GetMax().GetY(); y += 1.0f)
|
||||
{
|
||||
for (float x = encompassingBox.GetMin().GetX(); x < encompassingBox.GetMax().GetX(); x += 1.0f)
|
||||
{
|
||||
AZ::Vector3 position(x, y, 0.0f);
|
||||
bool heightQueryTerrainExists = false;
|
||||
float height =
|
||||
m_terrainSystem->GetHeight(position, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &heightQueryTerrainExists);
|
||||
bool isHole = m_terrainSystem->GetIsHoleFromFloats(
|
||||
position.GetX(), position.GetY(), AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT);
|
||||
|
||||
if (spawnerBox.Contains(AZ::Vector3(position.GetX(), position.GetY(), spawnerBox.GetMin().GetZ())))
|
||||
{
|
||||
EXPECT_TRUE(heightQueryTerrainExists);
|
||||
EXPECT_FALSE(isHole);
|
||||
EXPECT_EQ(height, spawnerHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_FALSE(heightQueryTerrainExists);
|
||||
EXPECT_TRUE(isHole);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# 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
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Mocks/Terrain/MockTerrain.h
|
||||
)
|
||||
@@ -7,8 +7,8 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Tests/TerrainMocks.h
|
||||
Tests/TerrainTest.cpp
|
||||
Tests/TerrainSystemTest.cpp
|
||||
Tests/LayerSpawnerTests.cpp
|
||||
Tests/MockAxisAlignedBoxShapeComponent.h
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
# shared by other platforms:
|
||||
ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348)
|
||||
ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec)
|
||||
ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326)
|
||||
ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf)
|
||||
ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023)
|
||||
ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246)
|
||||
@@ -44,5 +43,6 @@ ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-linux
|
||||
ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev2-linux TARGETS azslc PACKAGE_HASH 1ba84d8321a566d35a1e9aa7400211ba8e6d1c11c08e4be3c93e6e74b8f7aef1)
|
||||
ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-linux TARGETS zlib PACKAGE_HASH 16f3b9e11cda525efb62144f354c1cfc30a5def9eff020dbe49cb00ee7d8234f)
|
||||
ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-linux TARGETS squish-ccr PACKAGE_HASH 85fecafbddc6a41a27c5f59ed4a5dfb123a94cb4666782cf26e63c0a4724c530)
|
||||
ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-linux TARGETS astc-encoder PACKAGE_HASH 2ba97a06474d609945f0ab4419af1f6bbffdd294ca6b869f5fcebec75c573c0f)
|
||||
ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-linux TARGETS ISPCTexComp PACKAGE_HASH 065fd12abe4247dde247330313763cf816c3375c221da030bdec35024947f259)
|
||||
ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-linux TARGETS lz4 PACKAGE_HASH 5de3dbd3e2a3537c6555d759b3c5bb98e5456cf85c74ff6d046f809b7087290d)
|
||||
|
||||
+1
-1
@@ -9,7 +9,6 @@
|
||||
# shared by other platforms:
|
||||
ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348)
|
||||
ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec)
|
||||
ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326)
|
||||
ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf)
|
||||
ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023)
|
||||
ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246)
|
||||
@@ -42,6 +41,7 @@ ly_associate_package(PACKAGE_NAME qt-5.15.2-rev5-mac
|
||||
ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe)
|
||||
ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-mac TARGETS zlib PACKAGE_HASH 21714e8a6de4f2523ee92a7f52d51fbee29c5f37ced334e00dc3c029115b472e)
|
||||
ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-mac TARGETS squish-ccr PACKAGE_HASH 155bfbfa17c19a9cd2ef025de14c5db598f4290045d5b0d83ab58cb345089a77)
|
||||
ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-mac TARGETS astc-encoder PACKAGE_HASH 96f6ea8c3e45ec7fe525230c7c53ca665c8300d8e28456cc19bb3159ce6f8dcc)
|
||||
ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-mac TARGETS ISPCTexComp PACKAGE_HASH 8a4e93277b8face6ea2fd57c6d017bdb55643ed3d6387110bc5f6b3b884dd169)
|
||||
ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-mac TARGETS lz4 PACKAGE_HASH 891ff630bf34f7ab1d8eaee2ea0a8f1fca89dbdc63fca41ee592703dd488a73b)
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
# shared by other platforms:
|
||||
ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348)
|
||||
ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec)
|
||||
ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326)
|
||||
ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf)
|
||||
ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023)
|
||||
ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246)
|
||||
@@ -49,5 +48,6 @@ ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows
|
||||
ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2)
|
||||
ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-windows TARGETS zlib PACKAGE_HASH 9afab1d67641ed8bef2fb38fc53942da47f2ab339d9e77d3d20704a48af2da0b)
|
||||
ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-windows TARGETS squish-ccr PACKAGE_HASH 5c3d9fa491e488ccaf802304ad23b932268a2b2846e383f088779962af2bfa84)
|
||||
ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-windows TARGETS astc-encoder PACKAGE_HASH 3addc6fc1a7eb0d6b7f3d530e962af967e6d92b3825ef485da243346357cf78e)
|
||||
ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-windows TARGETS ISPCTexComp PACKAGE_HASH b6fa6ea28a2808a9a5524c72c37789c525925e435770f2d94eb2d387360fa2d0)
|
||||
ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-windows TARGETS lz4 PACKAGE_HASH 4ea457b833cd8cfaf8e8e06ed6df601d3e6783b606bdbc44a677f77e19e0db16)
|
||||
|
||||
+2
-2
@@ -71,10 +71,10 @@ def SearchPaths(filename, paths=[]):
|
||||
return None
|
||||
|
||||
def ComputeOutputPath(inputFiles, projectDir, outputDir):
|
||||
commonInputPath = os.path.commonprefix(inputFiles) # If we've globbed many source files, this finds the common prefix
|
||||
commonInputPath = os.path.commonpath(inputFiles) # If we've globbed many source files, this finds the common path
|
||||
if os.path.isfile(commonInputPath): # If the commonInputPath resolves to an actual file, slice off the filename
|
||||
commonInputPath = os.path.dirname(commonInputPath)
|
||||
commonPath = os.path.commonprefix([commonInputPath, projectDir]) # Finds the common path between the data source files and our project directory (//depot/dev/Code/Framework/AzCore/)
|
||||
commonPath = os.path.commonpath([commonInputPath, projectDir]) # Finds the common path between the data source files and our project directory (//depot/dev/Code/Framework/AzCore/)
|
||||
inputRelativePath = os.path.relpath(commonInputPath, commonPath) # Computes the relative path for the project source directory (Code/Framework/AzCore/AutoGen/)
|
||||
return os.path.join(outputDir, inputRelativePath) # Returns a suitable output directory (//depot/dev/Generated/Code/Framework/AzCore/AutoGen/)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ else()
|
||||
endif()
|
||||
|
||||
# Set the default asset type for deployment
|
||||
set(LY_ASSET_DEPLOY_ASSET_TYPE "pc" CACHE STRING "Set the asset type for deployment.")
|
||||
set(LY_ASSET_DEPLOY_ASSET_TYPE "linux" CACHE STRING "Set the asset type for deployment.")
|
||||
|
||||
# Set the python cmd tool
|
||||
ly_set(LY_PYTHON_CMD ${CMAKE_CURRENT_SOURCE_DIR}/python/python.sh)
|
||||
|
||||
+17
-7
@@ -147,14 +147,24 @@ foreach(project ${LY_PROJECTS})
|
||||
cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE runtime_output_directory)
|
||||
set(install_engine_pak_template [=[
|
||||
if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$")
|
||||
set(install_output_folder "${CMAKE_INSTALL_PREFIX}/@runtime_output_directory@/@PAL_PLATFORM_NAME@/${CMAKE_INSTALL_CONFIG_NAME}")
|
||||
message(STATUS "Generating ${install_output_folder}/Engine.pak from @full_directory_path@/Cache")
|
||||
set(install_output_folder "${CMAKE_INSTALL_PREFIX}/@runtime_output_directory@/@PAL_PLATFORM_NAME@/${CMAKE_INSTALL_CONFIG_NAME}/@LY_BUILD_PERMUTATION@")
|
||||
if(NOT DEFINED LY_ASSET_DEPLOY_ASSET_TYPE)
|
||||
set(LY_ASSET_DEPLOY_ASSET_TYPE @LY_ASSET_DEPLOY_ASSET_TYPE@)
|
||||
endif()
|
||||
message(STATUS "Generating ${install_output_folder}/Engine.pak from @full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}")
|
||||
file(MAKE_DIRECTORY "${install_output_folder}")
|
||||
file(ARCHIVE_CREATE OUTPUT "${install_output_folder}/Engine.pak"
|
||||
PATHS "@full_directory_path@/Cache"
|
||||
FORMAT zip
|
||||
)
|
||||
message(STATUS "${install_output_folder}/Engine.pak generated")
|
||||
cmake_path(SET cache_product_path "@full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}")
|
||||
file(GLOB product_assets "${cache_product_path}/*")
|
||||
if(product_assets)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_output_folder}/Engine.pak" --format=zip -- ${product_assets}
|
||||
WORKING_DIRECTORY "${cache_product_path}"
|
||||
RESULT_VARIABLE archive_creation_result
|
||||
)
|
||||
if(archive_creation_result EQUAL 0)
|
||||
message(STATUS "${install_output_folder}/Engine.pak generated")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
]=])
|
||||
string(CONFIGURE "${install_engine_pak_template}" install_engine_pak_code @ONLY)
|
||||
|
||||
Reference in New Issue
Block a user