Merge branch 'development' of https://github.com/o3de/o3de into sc-editor-asset-redux
This commit is contained in:
@@ -6,10 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "EditorDefs.h"
|
||||
|
||||
#include "AboutDialog.h"
|
||||
|
||||
// Qt
|
||||
@@ -47,14 +44,17 @@ CAboutDialog::CAboutDialog(QString versionText, QString richTextCopyrightNotice,
|
||||
CAboutDialog > QLabel#link { text-decoration: underline; color: #94D2FF; }");
|
||||
|
||||
// Prepare background image
|
||||
m_backgroundImage = AzQtComponents::ScalePixmapForScreenDpi(
|
||||
QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_developer_preview.jpg")),
|
||||
screen(),
|
||||
QSize(m_enforcedWidth, m_enforcedHeight),
|
||||
QPixmap image = AzQtComponents::ScalePixmapForScreenDpi(
|
||||
QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_2021_11.jpg")),
|
||||
screen(), QSize(m_imageWidth, m_imageHeight),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation
|
||||
);
|
||||
|
||||
// Crop image to cut out transparent border
|
||||
QRect cropRect((m_imageWidth - m_enforcedWidth) / 2, (m_imageHeight - m_enforcedHeight) / 2, m_enforcedWidth, m_enforcedHeight);
|
||||
m_backgroundImage = AzQtComponents::CropPixmapForScreenDpi(image, screen(), cropRect);
|
||||
|
||||
// Draw the Open 3D Engine logo from svg
|
||||
m_ui->m_logo->load(QStringLiteral(":/StartupLogoDialog/o3de_logo.svg"));
|
||||
|
||||
|
||||
@@ -38,7 +38,9 @@ private:
|
||||
QScopedPointer<Ui::CAboutDialog> m_ui;
|
||||
QPixmap m_backgroundImage;
|
||||
|
||||
int m_enforcedWidth = 600;
|
||||
int m_enforcedHeight = 400;
|
||||
const int m_imageWidth = 668;
|
||||
const int m_imageHeight = 368;
|
||||
const int m_enforcedWidth = 600;
|
||||
const int m_enforcedHeight = 300;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>360</height>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -19,13 +19,13 @@
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>360</height>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>360</height>
|
||||
<width>608</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -69,7 +69,7 @@
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>12</number>
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>12</number>
|
||||
@@ -125,7 +125,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Developer Preview</string>
|
||||
<string>General Availability</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
|
||||
@@ -96,10 +96,6 @@ AzAssetBrowserWindow::AzAssetBrowserWindow(QWidget* parent)
|
||||
connect(
|
||||
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
|
||||
&AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
|
||||
|
||||
connect(
|
||||
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
|
||||
&AzAssetBrowserWindow::UpdateTableModelAfterFilter);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
|
||||
&AzAssetBrowserWindow::SelectionChangedSlot);
|
||||
@@ -251,24 +247,6 @@ void AzAssetBrowserWindow::SetExpandedAssetBrowserMode()
|
||||
|
||||
m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::ExpandedMode;
|
||||
|
||||
disconnect(
|
||||
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
|
||||
&AzAssetBrowserWindow::UpdateTableModelAfterFilter);
|
||||
disconnect(
|
||||
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
|
||||
&AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
|
||||
|
||||
disconnect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
|
||||
&AzAssetBrowserWindow::SelectionChangedSlot);
|
||||
disconnect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
|
||||
disconnect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearStringFilter);
|
||||
disconnect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
|
||||
|
||||
if (m_ui->m_assetBrowserTableViewWidget->isVisible())
|
||||
{
|
||||
m_ui->m_assetBrowserTableViewWidget->setVisible(false);
|
||||
@@ -281,37 +259,9 @@ void AzAssetBrowserWindow::SetDefaultAssetBrowserMode()
|
||||
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
|
||||
|
||||
m_assetBrowserDisplayState = AzAssetBrowser::AssetBrowserDisplayState::DefaultMode;
|
||||
|
||||
connect(
|
||||
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
|
||||
&AzAssetBrowserWindow::SetTableViewVisibleAfterFilter);
|
||||
|
||||
connect(
|
||||
m_filterModel.data(), &AzAssetBrowser::AssetBrowserFilterModel::filterChanged, this,
|
||||
&AzAssetBrowserWindow::UpdateTableModelAfterFilter);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::selectionChangedSignal, this,
|
||||
&AzAssetBrowserWindow::SelectionChangedSlot);
|
||||
connect(m_ui->m_assetBrowserTableViewWidget, &QAbstractItemView::doubleClicked, this, &AzAssetBrowserWindow::DoubleClickedItem);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearStringFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearStringFilter);
|
||||
connect(
|
||||
m_ui->m_assetBrowserTableViewWidget, &AzAssetBrowser::AssetBrowserTableView::ClearTypeFilter, m_ui->m_searchWidget,
|
||||
&AzAssetBrowser::SearchWidget::ClearTypeFilter);
|
||||
|
||||
//If the filter is not empty we want to switch views and Update the model
|
||||
UpdateTableModelAfterFilter();
|
||||
SetTableViewVisibleAfterFilter();
|
||||
}
|
||||
|
||||
void AzAssetBrowserWindow::UpdateTableModelAfterFilter()
|
||||
{
|
||||
if (!m_ui->m_searchWidget->GetFilterString().isEmpty())
|
||||
{
|
||||
m_tableModel->UpdateTableModelMaps();
|
||||
}
|
||||
}
|
||||
|
||||
void AzAssetBrowserWindow::SetTableViewVisibleAfterFilter()
|
||||
{
|
||||
@@ -389,8 +339,8 @@ void AzAssetBrowserWindow::SelectionChangedSlot(const QItemSelection& /*selected
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
// while its tempting to use Activated here, we dont actually want it to count as activation
|
||||
// just becuase on some OS clicking once is activation.
|
||||
// while its tempting to use Activated here, we don't actually want it to count as activation
|
||||
// just because on some OS clicking once is activation.
|
||||
void AzAssetBrowserWindow::DoubleClickedItem([[maybe_unused]] const QModelIndex& element)
|
||||
{
|
||||
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
|
||||
|
||||
@@ -68,7 +68,6 @@ protected slots:
|
||||
void CreateSwitchViewMenu();
|
||||
void SetExpandedAssetBrowserMode();
|
||||
void SetDefaultAssetBrowserMode();
|
||||
void UpdateTableModelAfterFilter();
|
||||
void SetTableViewVisibleAfterFilter();
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,923 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "EditorDefs.h"
|
||||
|
||||
#include "ColorGradientCtrl.h"
|
||||
|
||||
// Qt
|
||||
#include <QPainter>
|
||||
#include <QToolTip>
|
||||
|
||||
// AzQtComponents
|
||||
#include <AzQtComponents/Components/Widgets/ColorPicker.h>
|
||||
|
||||
|
||||
#define MIN_TIME_EPSILON 0.01f
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CColorGradientCtrl::CColorGradientCtrl(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_nActiveKey = -1;
|
||||
m_nHitKeyIndex = -1;
|
||||
m_nKeyDrawRadius = 3;
|
||||
m_bTracking = false;
|
||||
m_pSpline = nullptr;
|
||||
m_fMinTime = -1;
|
||||
m_fMaxTime = 1;
|
||||
m_fMinValue = -1;
|
||||
m_fMaxValue = 1;
|
||||
m_fTooltipScaleX = 1;
|
||||
m_fTooltipScaleY = 1;
|
||||
m_bNoTimeMarker = true;
|
||||
m_bLockFirstLastKey = false;
|
||||
m_bNoZoom = true;
|
||||
|
||||
ClearSelection();
|
||||
|
||||
m_bSelectedKeys.reserve(0);
|
||||
|
||||
m_fTimeMarker = -10;
|
||||
|
||||
m_grid.zoom.x = 100;
|
||||
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
CColorGradientCtrl::~CColorGradientCtrl()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// QColorGradientCtrl message handlers
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
|
||||
QRect rc(QPoint(0, 0), event->size());
|
||||
m_rcGradient = rc;
|
||||
m_rcGradient.setHeight(m_rcGradient.height() - 11);
|
||||
//m_rcGradient.DeflateRect(4,4);
|
||||
|
||||
m_grid.rect = m_rcGradient;
|
||||
if (m_bNoZoom)
|
||||
{
|
||||
m_grid.zoom.x = static_cast<f32>(m_grid.rect.width());
|
||||
}
|
||||
|
||||
m_rcKeys = rc;
|
||||
m_rcKeys.setTop(m_rcKeys.bottom() - 10);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::SetZoom(float fZoom)
|
||||
{
|
||||
m_grid.zoom.x = fZoom;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::SetOrigin(float fOffset)
|
||||
{
|
||||
m_grid.origin.x = fOffset;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QPoint CColorGradientCtrl::KeyToPoint(int nKey)
|
||||
{
|
||||
if (nKey >= 0)
|
||||
{
|
||||
return TimeToPoint(m_pSpline->GetKeyTime(nKey));
|
||||
}
|
||||
return QPoint(0, 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QPoint CColorGradientCtrl::TimeToPoint(float time)
|
||||
{
|
||||
return QPoint(m_grid.WorldToClient(Vec2(time, 0)).x(), m_rcGradient.height() / 2);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZ::Color CColorGradientCtrl::TimeToColor(float time)
|
||||
{
|
||||
ISplineInterpolator::ValueType val;
|
||||
m_pSpline->Interpolate(time, val);
|
||||
const AZ::Color col = ValueToColor(val);
|
||||
return col;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::PointToTimeValue(QPoint point, float& time, ISplineInterpolator::ValueType& val)
|
||||
{
|
||||
time = XOfsToTime(point.x());
|
||||
ColorToValue(TimeToColor(time), val);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
float CColorGradientCtrl::XOfsToTime(int x)
|
||||
{
|
||||
return m_grid.ClientToWorld(QPoint(x, 0)).x;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QPoint CColorGradientCtrl::XOfsToPoint(int x)
|
||||
{
|
||||
return TimeToPoint(XOfsToTime(x));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZ::Color CColorGradientCtrl::XOfsToColor(int x)
|
||||
{
|
||||
return TimeToColor(XOfsToTime(x));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::paintEvent(QPaintEvent* e)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
QRect rcClient = rect();
|
||||
|
||||
if (m_pSpline)
|
||||
{
|
||||
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
|
||||
}
|
||||
{
|
||||
if (!isEnabled())
|
||||
{
|
||||
painter.setBrush(palette().button());
|
||||
painter.drawRect(rcClient);
|
||||
return;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Fill keys backgound.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QRect rcKeys = m_rcKeys.intersected(e->rect());
|
||||
painter.setBrush(palette().button());
|
||||
painter.drawRect(rcKeys);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//Draw Keys and Curve
|
||||
if (m_pSpline)
|
||||
{
|
||||
DrawGradient(e, &painter);
|
||||
DrawKeys(e, &painter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::DrawGradient(QPaintEvent* e, QPainter* painter)
|
||||
{
|
||||
//Draw Curve
|
||||
// create and select a thick, white pen
|
||||
painter->setPen(QPen(QColor(128, 255, 128), 1, Qt::SolidLine));
|
||||
|
||||
const QRect rcClip = e->rect().intersected(m_rcGradient);
|
||||
const int right = rcClip.left() + rcClip.width();
|
||||
for (int x = rcClip.left(); x < right; x++)
|
||||
{
|
||||
const AZ::Color col = XOfsToColor(x);
|
||||
QPen pen(QColor(col.GetR8(), col.GetG8(), col.GetR8(), col.GetA8()), 1, Qt::SolidLine);
|
||||
painter->setPen(pen);
|
||||
painter->drawLine(x, m_rcGradient.top(), x, m_rcGradient.top() + m_rcGradient.height());
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::DrawKeys(QPaintEvent* e, QPainter* painter)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// create and select a white pen
|
||||
painter->setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine));
|
||||
|
||||
QRect rcClip = e->rect();
|
||||
|
||||
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
|
||||
|
||||
for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
|
||||
{
|
||||
float time = m_pSpline->GetKeyTime(i);
|
||||
QPoint pt = TimeToPoint(time);
|
||||
|
||||
if (pt.x() < rcClip.left() - 8 || pt.x() > rcClip.left() + rcClip.width() + 8)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const AZ::Color clr = TimeToColor(time);
|
||||
QBrush brush(QColor(clr.GetR8(), clr.GetG8(), clr.GetB8(), clr.GetA8()));
|
||||
painter->setBrush(brush);
|
||||
|
||||
// Find the midpoints of the top, right, left, and bottom
|
||||
// of the client area. They will be the vertices of our polygon.
|
||||
QPoint pts[3];
|
||||
pts[0].rx() = pt.x();
|
||||
pts[0].ry() = m_rcKeys.top() + 1;
|
||||
pts[1].rx() = pt.x() - 5;
|
||||
pts[1].ry() = m_rcKeys.top() + 8;
|
||||
pts[2].rx() = pt.x() + 5;
|
||||
pts[2].ry() = m_rcKeys.top() + 8;
|
||||
painter->drawPolygon(pts, 3);
|
||||
|
||||
if (m_bSelectedKeys[i])
|
||||
{
|
||||
QPen pen(QColor(200, 0, 0), 1, Qt::SolidLine);
|
||||
QPen oldPen = painter->pen();
|
||||
painter->setPen(pen);
|
||||
painter->drawPolygon(pts, 3);
|
||||
painter->setPen(oldPen);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_bNoTimeMarker)
|
||||
{
|
||||
QPen timePen(QColor(255, 0, 255), 1, Qt::SolidLine);
|
||||
painter->setPen(timePen);
|
||||
QPoint pt = TimeToPoint(m_fTimeMarker);
|
||||
painter->drawLine(pt.x(), m_rcGradient.top() + 1, pt.x(), m_rcGradient.bottom() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void CColorGradientCtrl::UpdateTooltip(QPoint pos)
|
||||
{
|
||||
if (m_nHitKeyIndex >= 0)
|
||||
{
|
||||
float time = m_pSpline->GetKeyTime(m_nHitKeyIndex);
|
||||
ISplineInterpolator::ValueType val;
|
||||
m_pSpline->GetKeyValue(m_nHitKeyIndex, val);
|
||||
|
||||
AZ::Color col = TimeToColor(time);
|
||||
int cont_s = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_IN_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
|
||||
int cont_d = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_OUT_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
|
||||
|
||||
QString tipText(tr("%1 : %2,%3,%4 [%5,%6]").arg(time * m_fTooltipScaleX, 0, 'f', 2).arg(col.GetR8()).arg(col.GetG8()).arg(col.GetB8()).arg(cont_s).arg(cont_d));
|
||||
const QPoint globalPos = mapToGlobal(pos);
|
||||
QToolTip::showText(mapToGlobal(pos), tipText, this, QRect(globalPos, QSize(1, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//Mouse Message Handlers
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
OnLButtonDown(event);
|
||||
}
|
||||
else if (event->button() == Qt::RightButton)
|
||||
{
|
||||
OnRButtonDown(event);
|
||||
}
|
||||
}
|
||||
|
||||
void CColorGradientCtrl::OnLButtonDown([[maybe_unused]] QMouseEvent* event)
|
||||
{
|
||||
if (m_bTracking)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setFocus();
|
||||
|
||||
switch (m_hitCode)
|
||||
{
|
||||
case HIT_KEY:
|
||||
StartTracking();
|
||||
SetActiveKey(m_nHitKeyIndex);
|
||||
break;
|
||||
|
||||
/*
|
||||
case HIT_SPLINE:
|
||||
{
|
||||
// Cycle the spline slope of the nearest key.
|
||||
int flags = m_pSpline->GetKeyFlags(m_nHitKeyIndex);
|
||||
if (m_nHitKeyDist < 0)
|
||||
// Toggle left side.
|
||||
flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_IN_SHIFT;
|
||||
if (m_nHitKeyDist > 0)
|
||||
// Toggle right side.
|
||||
flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_OUT_SHIFT;
|
||||
m_pSpline->SetKeyFlags(m_nHitKeyIndex, flags);
|
||||
m_pSpline->Update();
|
||||
|
||||
SetActiveKey(-1);
|
||||
SendNotifyEvent( CLRGRDN_CHANGE );
|
||||
if (m_updateCallback)
|
||||
m_updateCallback(this);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
case HIT_NOTHING:
|
||||
SetActiveKey(-1);
|
||||
break;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::OnRButtonDown([[maybe_unused]] QMouseEvent* event)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() != Qt::LeftButton)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_hitCode)
|
||||
{
|
||||
case HIT_SPLINE:
|
||||
{
|
||||
int iIndex = InsertKey(event->pos());
|
||||
SetActiveKey(iIndex);
|
||||
EditKey(iIndex);
|
||||
|
||||
update();
|
||||
}
|
||||
break;
|
||||
case HIT_KEY:
|
||||
{
|
||||
EditKey(m_nHitKeyIndex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_bTracking)
|
||||
{
|
||||
switch (HitTest(event->pos()))
|
||||
{
|
||||
case HIT_SPLINE:
|
||||
{
|
||||
setCursor(CMFCUtils::LoadCursor(IDC_ARRWHITE));
|
||||
} break;
|
||||
case HIT_KEY:
|
||||
{
|
||||
setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCK));
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bTracking)
|
||||
{
|
||||
TrackKey(event->pos());
|
||||
}
|
||||
|
||||
if (m_bTracking || m_nHitKeyIndex >= 0)
|
||||
{
|
||||
UpdateTooltip(event->pos());
|
||||
}
|
||||
else
|
||||
{
|
||||
QToolTip::hideText();
|
||||
}
|
||||
}
|
||||
|
||||
void CColorGradientCtrl::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
OnLButtonUp(event);
|
||||
}
|
||||
else if (event->button() == Qt::RightButton)
|
||||
{
|
||||
OnRButtonUp(event);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::OnLButtonUp(QMouseEvent* event)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_bTracking)
|
||||
{
|
||||
StopTracking(event->pos());
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::OnRButtonUp([[maybe_unused]] QMouseEvent* event)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::SetActiveKey(int nIndex)
|
||||
{
|
||||
ClearSelection();
|
||||
|
||||
//Activate New Key
|
||||
if (nIndex >= 0)
|
||||
{
|
||||
m_bSelectedKeys[nIndex] = true;
|
||||
}
|
||||
m_nActiveKey = nIndex;
|
||||
update();
|
||||
|
||||
SendNotifyEvent(CLRGRDN_ACTIVE_KEY_CHANGE);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::SetSpline(ISplineInterpolator* pSpline, bool bRedraw)
|
||||
{
|
||||
if (pSpline != m_pSpline)
|
||||
{
|
||||
//if (pSpline && pSpline->GetNumDimensions() != 3)
|
||||
//return;
|
||||
m_pSpline = pSpline;
|
||||
m_nActiveKey = -1;
|
||||
}
|
||||
|
||||
ClearSelection();
|
||||
|
||||
if (bRedraw)
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ISplineInterpolator* CColorGradientCtrl::GetSpline()
|
||||
{
|
||||
return m_pSpline;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
bool bProcessed = false;
|
||||
|
||||
if (m_nActiveKey != -1 && m_pSpline)
|
||||
{
|
||||
switch (event->key())
|
||||
{
|
||||
case Qt::Key_Delete:
|
||||
{
|
||||
RemoveKey(m_nActiveKey);
|
||||
bProcessed = true;
|
||||
} break;
|
||||
case Qt::Key_Up:
|
||||
{
|
||||
CUndo undo("Move Spline Key");
|
||||
QPoint point = KeyToPoint(m_nActiveKey);
|
||||
point.rx() -= 1;
|
||||
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
|
||||
TrackKey(point);
|
||||
bProcessed = true;
|
||||
} break;
|
||||
case Qt::Key_Down:
|
||||
{
|
||||
CUndo undo("Move Spline Key");
|
||||
QPoint point = KeyToPoint(m_nActiveKey);
|
||||
point.rx() += 1;
|
||||
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
|
||||
TrackKey(point);
|
||||
bProcessed = true;
|
||||
} break;
|
||||
case Qt::Key_Left:
|
||||
{
|
||||
CUndo undo("Move Spline Key");
|
||||
QPoint point = KeyToPoint(m_nActiveKey);
|
||||
point.rx() -= 1;
|
||||
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
|
||||
TrackKey(point);
|
||||
bProcessed = true;
|
||||
} break;
|
||||
case Qt::Key_Right:
|
||||
{
|
||||
CUndo undo("Move Spline Key");
|
||||
QPoint point = KeyToPoint(m_nActiveKey);
|
||||
point.rx() += 1;
|
||||
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
|
||||
TrackKey(point);
|
||||
bProcessed = true;
|
||||
} break;
|
||||
|
||||
default:
|
||||
break; //do nothing
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
event->setAccepted(bProcessed);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
CColorGradientCtrl::EHitCode CColorGradientCtrl::HitTest(QPoint point)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return HIT_NOTHING;
|
||||
}
|
||||
|
||||
ISplineInterpolator::ValueType val;
|
||||
float time;
|
||||
PointToTimeValue(point, time, val);
|
||||
|
||||
QRect rc = rect();
|
||||
|
||||
m_nHitKeyIndex = -1;
|
||||
|
||||
if (rc.contains(point))
|
||||
{
|
||||
m_nHitKeyDist = 0xFFFF;
|
||||
m_hitCode = HIT_SPLINE;
|
||||
|
||||
for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
|
||||
{
|
||||
QPoint splinePt = TimeToPoint(m_pSpline->GetKeyTime(i));
|
||||
if (abs(point.x() - splinePt.x()) < abs(m_nHitKeyDist))
|
||||
{
|
||||
m_nHitKeyIndex = i;
|
||||
m_nHitKeyDist = point.x() - splinePt.x();
|
||||
}
|
||||
}
|
||||
if (abs(m_nHitKeyDist) < 4)
|
||||
{
|
||||
m_hitCode = HIT_KEY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hitCode = HIT_NOTHING;
|
||||
}
|
||||
|
||||
return m_hitCode;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::StartTracking()
|
||||
{
|
||||
m_bTracking = true;
|
||||
|
||||
GetIEditor()->BeginUndo();
|
||||
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
|
||||
|
||||
setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCKCROSS));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::TrackKey(QPoint point)
|
||||
{
|
||||
if (point.x() < m_rcGradient.left() || point.y() > m_rcGradient.right())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int nKey = m_nHitKeyIndex;
|
||||
|
||||
if (nKey >= 0)
|
||||
{
|
||||
ISplineInterpolator::ValueType val;
|
||||
float time;
|
||||
PointToTimeValue(point, time, val);
|
||||
|
||||
// Clamp to min/max time.
|
||||
if (time < m_fMinTime || time > m_fMaxTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
|
||||
{
|
||||
// Switch to next key.
|
||||
if ((m_pSpline->GetKeyTime(i) < time && i > nKey) ||
|
||||
(m_pSpline->GetKeyTime(i) > time && i < nKey))
|
||||
{
|
||||
m_pSpline->SetKeyTime(nKey, time);
|
||||
m_pSpline->Update();
|
||||
SetActiveKey(i);
|
||||
m_nHitKeyIndex = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_bLockFirstLastKey || (nKey != 0 && nKey != m_pSpline->GetKeyCount() - 1))
|
||||
{
|
||||
m_pSpline->SetKeyTime(nKey, time);
|
||||
m_pSpline->Update();
|
||||
}
|
||||
|
||||
SendNotifyEvent(CLRGRDN_CHANGE);
|
||||
if (m_updateCallback)
|
||||
{
|
||||
m_updateCallback(this);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::StopTracking(QPoint point)
|
||||
{
|
||||
if (!m_bTracking)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetIEditor()->AcceptUndo("Spline Move");
|
||||
|
||||
if (m_nHitKeyIndex >= 0)
|
||||
{
|
||||
QRect rc = rect();
|
||||
rc = rc.marginsAdded(QMargins(100, 100, 100, 100));
|
||||
if (!rc.contains(point))
|
||||
{
|
||||
RemoveKey(m_nHitKeyIndex);
|
||||
}
|
||||
}
|
||||
|
||||
m_bTracking = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::EditKey(int nKey)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (nKey < 0 || nKey >= m_pSpline->GetKeyCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetActiveKey(nKey);
|
||||
|
||||
ISplineInterpolator::ValueType val;
|
||||
m_pSpline->GetKeyValue(nKey, val);
|
||||
|
||||
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
|
||||
|
||||
AzQtComponents::ColorPicker dlg(AzQtComponents::ColorPicker::Configuration::RGB);
|
||||
dlg.setCurrentColor(ValueToColor(val));
|
||||
dlg.setSelectedColor(ValueToColor(val));
|
||||
connect(&dlg, &AzQtComponents::ColorPicker::currentColorChanged, this, &CColorGradientCtrl::OnKeyColorChanged);
|
||||
if (dlg.exec() == QDialog::Accepted)
|
||||
{
|
||||
CUndo undo("Modify Gradient Color");
|
||||
OnKeyColorChanged(dlg.selectedColor());
|
||||
}
|
||||
else
|
||||
{
|
||||
OnKeyColorChanged(ValueToColor(val));
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::OnKeyColorChanged(const AZ::Color& color)
|
||||
{
|
||||
int nKey = m_nActiveKey;
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (nKey < 0 || nKey >= m_pSpline->GetKeyCount())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ISplineInterpolator::ValueType val;
|
||||
ColorToValue(color, val);
|
||||
m_pSpline->SetKeyValue(nKey, val);
|
||||
update();
|
||||
|
||||
if (m_bLockFirstLastKey)
|
||||
{
|
||||
if (nKey == 0)
|
||||
{
|
||||
m_pSpline->SetKeyValue(m_pSpline->GetKeyCount() - 1, val);
|
||||
}
|
||||
else if (nKey == m_pSpline->GetKeyCount() - 1)
|
||||
{
|
||||
m_pSpline->SetKeyValue(0, val);
|
||||
}
|
||||
}
|
||||
m_pSpline->Update();
|
||||
SendNotifyEvent(CLRGRDN_CHANGE);
|
||||
if (m_updateCallback)
|
||||
{
|
||||
m_updateCallback(this);
|
||||
}
|
||||
|
||||
GetIEditor()->UpdateViews(eRedrawViewports);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::RemoveKey(int nKey)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (m_bLockFirstLastKey)
|
||||
{
|
||||
if (nKey == 0 || nKey == m_pSpline->GetKeyCount() - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CUndo undo("Remove Spline Key");
|
||||
|
||||
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
|
||||
m_nActiveKey = -1;
|
||||
m_nHitKeyIndex = -1;
|
||||
if (m_pSpline)
|
||||
{
|
||||
m_pSpline->RemoveKey(nKey);
|
||||
m_pSpline->Update();
|
||||
}
|
||||
SendNotifyEvent(CLRGRDN_CHANGE);
|
||||
if (m_updateCallback)
|
||||
{
|
||||
m_updateCallback(this);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int CColorGradientCtrl::InsertKey(QPoint point)
|
||||
{
|
||||
CUndo undo("Spline Insert Key");
|
||||
|
||||
ISplineInterpolator::ValueType val;
|
||||
|
||||
float time;
|
||||
PointToTimeValue(point, time, val);
|
||||
|
||||
if (time < m_fMinTime || time > m_fMaxTime)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
|
||||
{
|
||||
// Skip if any key already have time that is very close.
|
||||
if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
|
||||
|
||||
m_pSpline->InsertKey(time, val);
|
||||
m_pSpline->Interpolate(time, val);
|
||||
ClearSelection();
|
||||
update();
|
||||
|
||||
SendNotifyEvent(CLRGRDN_CHANGE);
|
||||
if (m_updateCallback)
|
||||
{
|
||||
m_updateCallback(this);
|
||||
}
|
||||
|
||||
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
|
||||
{
|
||||
// Find key with added time.
|
||||
if (m_pSpline->GetKeyTime(i) == time)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::ClearSelection()
|
||||
{
|
||||
m_nActiveKey = -1;
|
||||
if (m_pSpline)
|
||||
{
|
||||
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
|
||||
}
|
||||
for (int i = 0; i < (int)m_bSelectedKeys.size(); i++)
|
||||
{
|
||||
m_bSelectedKeys[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::SetTimeMarker(float fTime)
|
||||
{
|
||||
if (!m_pSpline)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QPoint pt = TimeToPoint(m_fTimeMarker);
|
||||
QRect rc = QRect(pt.x(), m_rcGradient.top(), 0, m_rcGradient.bottom() - m_rcGradient.top()).normalized();
|
||||
rc += QMargins(1, 0, 1, 0);
|
||||
update(rc);
|
||||
}
|
||||
{
|
||||
QPoint pt = TimeToPoint(fTime);
|
||||
QRect rc = QRect(pt.x(), m_rcGradient.top(), 0, m_rcGradient.bottom() - m_rcGradient.top()).normalized();
|
||||
rc += QMargins(1, 0, 1, 0);
|
||||
update(rc);
|
||||
}
|
||||
m_fTimeMarker = fTime;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::SendNotifyEvent(int nEvent)
|
||||
{
|
||||
switch (nEvent)
|
||||
{
|
||||
case CLRGRDN_BEFORE_CHANGE:
|
||||
emit beforeChange();
|
||||
break;
|
||||
case CLRGRDN_CHANGE:
|
||||
emit change();
|
||||
break;
|
||||
case CLRGRDN_ACTIVE_KEY_CHANGE:
|
||||
emit activeKeyChange();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZ::Color CColorGradientCtrl::ValueToColor(ISplineInterpolator::ValueType val)
|
||||
{
|
||||
const AZ::Color color(val[0], val[1], val[2], 1.0);
|
||||
return color.LinearToGamma();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CColorGradientCtrl::ColorToValue(const AZ::Color& col, ISplineInterpolator::ValueType& val)
|
||||
{
|
||||
const AZ::Color colLin = col.GammaToLinear();
|
||||
val[0] = colLin.GetR();
|
||||
val[1] = colLin.GetG();
|
||||
val[2] = colLin.GetB();
|
||||
val[3] = 0;
|
||||
}
|
||||
|
||||
void CColorGradientCtrl::SetNoTimeMarker(bool noTimeMarker)
|
||||
{
|
||||
m_bNoTimeMarker = noTimeMarker;
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
#include <Controls/moc_ColorGradientCtrl.cpp>
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H
|
||||
#define CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QWidget>
|
||||
#include <ISplines.h>
|
||||
#include "Controls/WndGridHelper.h"
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class Color;
|
||||
}
|
||||
|
||||
// Notify event sent when spline is being modified.
|
||||
#define CLRGRDN_CHANGE (0x0001)
|
||||
// Notify event sent just before when spline is modified.
|
||||
#define CLRGRDN_BEFORE_CHANGE (0x0002)
|
||||
// Notify event sent when the active key changes
|
||||
#define CLRGRDN_ACTIVE_KEY_CHANGE (0x0003)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Spline control.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CColorGradientCtrl
|
||||
: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CColorGradientCtrl(QWidget* parent = nullptr);
|
||||
virtual ~CColorGradientCtrl();
|
||||
|
||||
//Key functions
|
||||
int GetActiveKey() { return m_nActiveKey; };
|
||||
void SetActiveKey(int nIndex);
|
||||
int InsertKey(QPoint point);
|
||||
|
||||
// Turns on/off zooming and scroll support.
|
||||
void SetNoZoom([[maybe_unused]] bool bNoZoom) { m_bNoZoom = false; };
|
||||
|
||||
void SetTimeRange(float tmin, float tmax) { m_fMinTime = tmin; m_fMaxTime = tmax; }
|
||||
void SetValueRange(float tmin, float tmax) { m_fMinValue = tmin; m_fMaxValue = tmax; }
|
||||
void SetTooltipValueScale(float x, float y) { m_fTooltipScaleX = x; m_fTooltipScaleY = y; };
|
||||
// Lock value of first and last key to be the same.
|
||||
void LockFirstAndLastKeys(bool bLock) { m_bLockFirstLastKey = bLock; }
|
||||
|
||||
void SetSpline(ISplineInterpolator* pSpline, bool bRedraw = false);
|
||||
ISplineInterpolator* GetSpline();
|
||||
|
||||
void SetTimeMarker(float fTime);
|
||||
|
||||
// Zoom in pixels per time unit.
|
||||
void SetZoom(float fZoom);
|
||||
void SetOrigin(float fOffset);
|
||||
|
||||
typedef AZStd::function<void(CColorGradientCtrl*)> UpdateCallback;
|
||||
void SetUpdateCallback(const UpdateCallback& cb) { m_updateCallback = cb; };
|
||||
|
||||
void SetNoTimeMarker(bool noTimeMarker);
|
||||
|
||||
signals:
|
||||
void change();
|
||||
void beforeChange();
|
||||
void activeKeyChange();
|
||||
|
||||
protected:
|
||||
enum EHitCode
|
||||
{
|
||||
HIT_NOTHING,
|
||||
HIT_KEY,
|
||||
HIT_SPLINE,
|
||||
};
|
||||
|
||||
void paintEvent(QPaintEvent* e) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void OnLButtonDown(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void OnLButtonUp(QMouseEvent* event);
|
||||
void OnRButtonUp(QMouseEvent* event);
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
void OnRButtonDown(QMouseEvent* event);
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
// Drawing functions
|
||||
void DrawGradient(QPaintEvent* e, QPainter* painter);
|
||||
void DrawKeys(QPaintEvent* e, QPainter* painter);
|
||||
void UpdateTooltip(QPoint pos);
|
||||
|
||||
EHitCode HitTest(QPoint point);
|
||||
|
||||
//Tracking support helper functions
|
||||
void StartTracking();
|
||||
void TrackKey(QPoint point);
|
||||
void StopTracking(QPoint point);
|
||||
void RemoveKey(int nKey);
|
||||
void EditKey(int nKey);
|
||||
|
||||
QPoint KeyToPoint(int nKey);
|
||||
QPoint TimeToPoint(float time);
|
||||
void PointToTimeValue(QPoint point, float& time, ISplineInterpolator::ValueType& val);
|
||||
float XOfsToTime(int x);
|
||||
QPoint XOfsToPoint(int x);
|
||||
|
||||
AZ::Color XOfsToColor(int x);
|
||||
AZ::Color TimeToColor(float time);
|
||||
|
||||
void ClearSelection();
|
||||
|
||||
void SendNotifyEvent(int nEvent);
|
||||
|
||||
AZ::Color ValueToColor(ISplineInterpolator::ValueType val);
|
||||
void ColorToValue(const AZ::Color& col, ISplineInterpolator::ValueType& val);
|
||||
|
||||
|
||||
private:
|
||||
void OnKeyColorChanged(const AZ::Color& color);
|
||||
|
||||
private:
|
||||
ISplineInterpolator* m_pSpline;
|
||||
|
||||
bool m_bNoZoom;
|
||||
|
||||
QRect m_rcClipRect;
|
||||
QRect m_rcGradient;
|
||||
QRect m_rcKeys;
|
||||
|
||||
QPoint m_hitPoint;
|
||||
EHitCode m_hitCode;
|
||||
int m_nHitKeyIndex;
|
||||
int m_nHitKeyDist;
|
||||
QPoint m_curvePoint;
|
||||
|
||||
float m_fTimeMarker;
|
||||
|
||||
int m_nActiveKey;
|
||||
int m_nKeyDrawRadius;
|
||||
|
||||
bool m_bTracking;
|
||||
|
||||
float m_fMinTime, m_fMaxTime;
|
||||
float m_fMinValue, m_fMaxValue;
|
||||
float m_fTooltipScaleX, m_fTooltipScaleY;
|
||||
|
||||
bool m_bLockFirstLastKey;
|
||||
|
||||
bool m_bNoTimeMarker;
|
||||
|
||||
std::vector<int> m_bSelectedKeys;
|
||||
|
||||
UpdateCallback m_updateCallback;
|
||||
|
||||
CWndGridHelper m_grid;
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H
|
||||
@@ -27,7 +27,6 @@ void RegisterReflectedVarHandlers()
|
||||
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew LocalStringPropertyHandler());
|
||||
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew LightAnimationPropertyHandler());
|
||||
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew UserPopupWidgetHandler());
|
||||
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew ColorCurveHandler());
|
||||
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew FloatCurveHandler());
|
||||
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew MotionPropertyWidgetHandler());
|
||||
}
|
||||
|
||||
@@ -170,30 +170,3 @@ bool FloatCurveHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, CSpline
|
||||
GUI->SetSpline(reinterpret_cast<ISplineInterpolator*>(instance.m_spline));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QWidget* ColorCurveHandler::CreateGUI(QWidget *pParent)
|
||||
{
|
||||
CColorGradientCtrl* gradientCtrl = new CColorGradientCtrl(pParent);
|
||||
//connect(gradientCtrl, &CColorGradientCtrl::change, [gradientCtrl]()
|
||||
//{
|
||||
// EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, gradientCtrl);
|
||||
//});
|
||||
gradientCtrl->SetTimeRange(0, 1);
|
||||
gradientCtrl->setFixedHeight(36);
|
||||
return gradientCtrl;
|
||||
|
||||
}
|
||||
|
||||
void ColorCurveHandler::ConsumeAttribute(CColorGradientCtrl*, AZ::u32, AzToolsFramework::PropertyAttributeReader*, const char*)
|
||||
{}
|
||||
|
||||
void ColorCurveHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, [[maybe_unused]] CColorGradientCtrl* GUI, [[maybe_unused]] property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
|
||||
{}
|
||||
|
||||
bool ColorCurveHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, CColorGradientCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
|
||||
{
|
||||
GUI->SetSpline(reinterpret_cast<ISplineInterpolator*>(instance.m_spline));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
|
||||
#include "ReflectedVar.h"
|
||||
#include "Util/VariablePropertyType.h"
|
||||
#include "Controls/ColorGradientCtrl.h"
|
||||
#include "Controls/SplineCtrl.h"
|
||||
#include <QWidget>
|
||||
#endif
|
||||
@@ -82,17 +81,4 @@ public:
|
||||
void OnSplineChange(CSplineCtrl*);
|
||||
};
|
||||
|
||||
class ColorCurveHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarSpline, CColorGradientCtrl>
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ColorCurveHandler, AZ::SystemAllocator, 0);
|
||||
bool IsDefaultHandler() const override { return false; }
|
||||
QWidget* CreateGUI(QWidget *pParent) override;
|
||||
|
||||
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("ePropertyColorCurve", 0xa30da4ec); }
|
||||
|
||||
void ConsumeAttribute(CColorGradientCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
|
||||
void WriteGUIValuesIntoProperty(size_t index, CColorGradientCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
|
||||
bool ReadValuesIntoGUI(size_t index, CColorGradientCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
|
||||
};
|
||||
#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
|
||||
|
||||
@@ -16,18 +16,11 @@
|
||||
#include <QScopedValueRollback>
|
||||
#include <QToolBar>
|
||||
#include <QLoggingCategory>
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
#include <QtGui/qpa/qplatformnativeinterface.h>
|
||||
#include <QtGui/private/qhighdpiscaling_p.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
// AzFramework
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
# include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
|
||||
#endif // defined(AZ_PLATFORM_WINDOWS)
|
||||
|
||||
// AzQtComponents
|
||||
#include <AzQtComponents/Components/GlobalEventFilter.h>
|
||||
@@ -39,7 +32,6 @@
|
||||
#include "Settings.h"
|
||||
#include "CryEdit.h"
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
// in milliseconds
|
||||
@@ -241,7 +233,6 @@ namespace Editor
|
||||
|
||||
EditorQtApplication::EditorQtApplication(int& argc, char** argv)
|
||||
: AzQtApplication(argc, argv)
|
||||
, m_inWinEventFilter(false)
|
||||
, m_stylesheet(new AzQtComponents::O3DEStylesheet(this))
|
||||
, m_idleTimer(new QTimer(this))
|
||||
{
|
||||
@@ -368,86 +359,10 @@ namespace Editor
|
||||
UninstallEditorTranslators();
|
||||
}
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long* result)
|
||||
EditorQtApplication* EditorQtApplication::instance()
|
||||
{
|
||||
MSG* msg = (MSG*)message;
|
||||
|
||||
if (msg->message == WM_MOVING || msg->message == WM_SIZING)
|
||||
{
|
||||
m_isMovingOrResizing = true;
|
||||
}
|
||||
else if (msg->message == WM_EXITSIZEMOVE)
|
||||
{
|
||||
m_isMovingOrResizing = false;
|
||||
}
|
||||
|
||||
// Prevent the user from being able to move the window in game mode.
|
||||
// This is done during the hit test phase to bypass the native window move messages. If the window
|
||||
// decoration wrapper title bar contains the cursor, set the result to HTCLIENT instead of
|
||||
// HTCAPTION.
|
||||
if (msg->message == WM_NCHITTEST && GetIEditor()->IsInGameMode())
|
||||
{
|
||||
const LRESULT defWinProcResult = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
|
||||
if (defWinProcResult == 1)
|
||||
{
|
||||
if (QWidget* widget = QWidget::find((WId)msg->hwnd))
|
||||
{
|
||||
if (auto wrapper = qobject_cast<const AzQtComponents::WindowDecorationWrapper *>(widget))
|
||||
{
|
||||
AzQtComponents::TitleBar* titleBar = wrapper->titleBar();
|
||||
const short global_x = static_cast<short>(LOWORD(msg->lParam));
|
||||
const short global_y = static_cast<short>(HIWORD(msg->lParam));
|
||||
|
||||
const QPoint globalPos = QHighDpi::fromNativePixels(QPoint(global_x, global_y), widget->window()->windowHandle());
|
||||
const QPoint local = titleBar->mapFromGlobal(globalPos);
|
||||
if (titleBar->draggableRect().contains(local) && !titleBar->isTopResizeArea(globalPos))
|
||||
{
|
||||
*result = HTCLIENT;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system.
|
||||
// These events are only broadcast in game mode. In Editor mode, RenderViewportWidget creates synthetic
|
||||
// keyboard and mouse events via Qt.
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
if (msg->message == WM_INPUT)
|
||||
{
|
||||
UINT rawInputSize;
|
||||
const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER);
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, nullptr, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
AZStd::array<BYTE, sizeof(RAWINPUT)> rawInputBytesArray;
|
||||
LPBYTE rawInputBytes = rawInputBytesArray.data();
|
||||
|
||||
[[maybe_unused]] const UINT bytesCopied = GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize);
|
||||
CRY_ASSERT(bytesCopied == rawInputSize);
|
||||
|
||||
RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes;
|
||||
CRY_ASSERT(rawInput);
|
||||
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputEvent, *rawInput);
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (msg->message == WM_DEVICECHANGE)
|
||||
{
|
||||
if (msg->wParam == 0x0007) // DBT_DEVNODES_CHANGED
|
||||
{
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputDeviceChangeEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return static_cast<EditorQtApplication*>(QApplication::instance());
|
||||
}
|
||||
#endif
|
||||
|
||||
void EditorQtApplication::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
{
|
||||
@@ -505,11 +420,6 @@ namespace Editor
|
||||
return m_stylesheet->GetColorByName(name);
|
||||
}
|
||||
|
||||
EditorQtApplication* EditorQtApplication::instance()
|
||||
{
|
||||
return static_cast<EditorQtApplication*>(QApplication::instance());
|
||||
}
|
||||
|
||||
bool EditorQtApplication::IsActive()
|
||||
{
|
||||
return applicationState() == Qt::ApplicationActive;
|
||||
@@ -613,42 +523,6 @@ namespace Editor
|
||||
case QEvent::KeyRelease:
|
||||
m_pressedKeys.remove(reinterpret_cast<QKeyEvent*>(event)->key());
|
||||
break;
|
||||
#ifdef AZ_PLATFORM_WINDOWS
|
||||
case QEvent::Leave:
|
||||
{
|
||||
// if we receive a leave event for a toolbar on Windows
|
||||
// check first whether we really left it. If we didn't: start checking
|
||||
// for the tool bar under the mouse by timer to check when we really left.
|
||||
// Synthesize a new leave event then. Workaround for LY-69788
|
||||
auto toolBarAt = [](const QPoint& pos) -> QToolBar* {
|
||||
QWidget* widget = qApp->widgetAt(pos);
|
||||
while (widget != nullptr)
|
||||
{
|
||||
if (QToolBar* tb = qobject_cast<QToolBar*>(widget))
|
||||
{
|
||||
return tb;
|
||||
}
|
||||
widget = widget->parentWidget();
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
if (object == toolBarAt(QCursor::pos()))
|
||||
{
|
||||
QTimer* t = new QTimer(object);
|
||||
t->start(100);
|
||||
connect(t, &QTimer::timeout, object, [t, object, toolBarAt]() {
|
||||
if (object != toolBarAt(QCursor::pos()))
|
||||
{
|
||||
QEvent event(QEvent::Leave);
|
||||
qApp->sendEvent(object, &event);
|
||||
t->deleteLater();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -72,14 +72,12 @@ namespace Editor
|
||||
////
|
||||
|
||||
static EditorQtApplication* instance();
|
||||
static EditorQtApplication* newInstance(int& argc, char** argv);
|
||||
|
||||
static bool IsActive();
|
||||
|
||||
bool isMovingOrResizing() const;
|
||||
|
||||
// QAbstractNativeEventFilter:
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
|
||||
|
||||
// IEditorNotifyListener:
|
||||
void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
|
||||
|
||||
@@ -100,6 +98,10 @@ namespace Editor
|
||||
signals:
|
||||
void skinChanged();
|
||||
|
||||
protected:
|
||||
|
||||
bool m_isMovingOrResizing = false;
|
||||
|
||||
private:
|
||||
enum TimerResetFlag
|
||||
{
|
||||
@@ -116,8 +118,6 @@ namespace Editor
|
||||
|
||||
AzQtComponents::O3DEStylesheet* m_stylesheet;
|
||||
|
||||
bool m_inWinEventFilter = false;
|
||||
|
||||
// Translators
|
||||
void InstallEditorTranslators();
|
||||
void UninstallEditorTranslators();
|
||||
@@ -127,7 +127,6 @@ namespace Editor
|
||||
QTranslator* m_editorTranslator = nullptr;
|
||||
QTranslator* m_assetBrowserTranslator = nullptr;
|
||||
QTimer* const m_idleTimer = nullptr;
|
||||
bool m_isMovingOrResizing = false;
|
||||
|
||||
AZ::UserSettingsProvider m_localUserSettings;
|
||||
|
||||
|
||||
+25
-26
@@ -287,21 +287,22 @@ bool CCryDocManager::DoPromptFileName(QString& fileName, [[maybe_unused]] UINT n
|
||||
|
||||
return false;
|
||||
}
|
||||
CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAddToMRU)
|
||||
CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions)
|
||||
{
|
||||
assert(lpszFileName != nullptr);
|
||||
assert(filename != nullptr);
|
||||
|
||||
const bool reopenIfSame = openSameLevelOptions == COpenSameLevelOptions::ReopenLevelIfSame;
|
||||
// find the highest confidence
|
||||
auto pos = m_templateList.begin();
|
||||
CCrySingleDocTemplate::Confidence bestMatch = CCrySingleDocTemplate::noAttempt;
|
||||
CCrySingleDocTemplate* pBestTemplate = nullptr;
|
||||
CCryEditDoc* pOpenDocument = nullptr;
|
||||
|
||||
if (lpszFileName[0] == '\"')
|
||||
if (filename[0] == '\"')
|
||||
{
|
||||
++lpszFileName;
|
||||
++filename;
|
||||
}
|
||||
QString szPath = QString::fromUtf8(lpszFileName);
|
||||
QString szPath = QString::fromUtf8(filename);
|
||||
if (szPath.endsWith('"'))
|
||||
{
|
||||
szPath.remove(szPath.length() - 1, 1);
|
||||
@@ -325,7 +326,7 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAd
|
||||
}
|
||||
}
|
||||
|
||||
if (pOpenDocument != nullptr)
|
||||
if (!reopenIfSame && pOpenDocument != nullptr)
|
||||
{
|
||||
return pOpenDocument;
|
||||
}
|
||||
@@ -336,7 +337,7 @@ CCryEditDoc* CCryDocManager::OpenDocumentFile(const char* lpszFileName, bool bAd
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), bAddToMRU, false);
|
||||
return pBestTemplate->OpenDocumentFile(szPath.toUtf8().data(), addToMostRecentFileList, false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -513,7 +514,7 @@ public:
|
||||
QString m_appRoot;
|
||||
QString m_logFile;
|
||||
QString m_pythonArgs;
|
||||
QString m_pythontTestCase;
|
||||
QString m_pythonTestCase;
|
||||
QString m_execFile;
|
||||
QString m_execLineCmd;
|
||||
|
||||
@@ -562,7 +563,7 @@ public:
|
||||
const std::vector<std::pair<CommandLineStringOption, QString&> > stringOptions = {
|
||||
{{"logfile", "File name of the log file to write out to.", "logfile"}, m_logFile},
|
||||
{{"runpythonargs", "Command-line argument string to pass to the python script if --runpython or --runpythontest was used.", "runpythonargs"}, m_pythonArgs},
|
||||
{{"pythontestcase", "Test case name of python test script if --runpythontest was used.", "pythontestcase"}, m_pythontTestCase},
|
||||
{{"pythontestcase", "Test case name of python test script if --runpythontest was used.", "pythontestcase"}, m_pythonTestCase},
|
||||
{{"exec", "cfg file to run on startup, used for systems like automation", "exec"}, m_execFile},
|
||||
{{"rhi", "Command-line argument to force which rhi to use", "dummyString"}, dummyString },
|
||||
{{"rhi-device-validation", "Command-line argument to configure rhi validation", "dummyString"}, dummyString },
|
||||
@@ -818,7 +819,7 @@ CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, b
|
||||
return OpenDocumentFile(lpszPathName, true, bMakeVisible);
|
||||
}
|
||||
|
||||
CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool bAddToMRU, [[maybe_unused]] bool bMakeVisible)
|
||||
CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, [[maybe_unused]] bool bMakeVisible)
|
||||
{
|
||||
CCryEditDoc* pCurDoc = GetIEditor()->GetDocument();
|
||||
|
||||
@@ -848,7 +849,7 @@ CCryEditDoc* CCrySingleDocTemplate::OpenDocumentFile(const char* lpszPathName, b
|
||||
{
|
||||
pCurDoc->OnOpenDocument(lpszPathName);
|
||||
pCurDoc->SetPathName(lpszPathName);
|
||||
if (bAddToMRU)
|
||||
if (addToMostRecentFileList)
|
||||
{
|
||||
CCryEditApp::instance()->AddToRecentFileList(lpszPathName);
|
||||
}
|
||||
@@ -1535,11 +1536,12 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
|
||||
{
|
||||
// Multiple testcases can be specified them with ';', these should match the files to run
|
||||
AZStd::vector<AZStd::string_view> testcaseList;
|
||||
QByteArray pythonTestCase = cmdInfo.m_pythonTestCase.toUtf8();
|
||||
testcaseList.resize(fileList.size());
|
||||
{
|
||||
int i = 0;
|
||||
AzFramework::StringFunc::TokenizeVisitor(
|
||||
fileStr.constData(),
|
||||
pythonTestCase.constData(),
|
||||
[&i, &testcaseList](AZStd::string_view elem)
|
||||
{
|
||||
testcaseList[i++] = (elem);
|
||||
@@ -2630,7 +2632,7 @@ void CCryEditApp::OnShowHelpers()
|
||||
void CCryEditApp::OnEditLevelData()
|
||||
{
|
||||
auto dir = QFileInfo(GetIEditor()->GetDocument()->GetLevelPathName()).dir();
|
||||
CFileUtil::EditTextFile(dir.absoluteFilePath("LevelData.xml").toUtf8().data());
|
||||
CFileUtil::EditTextFile(dir.absoluteFilePath("leveldata.xml").toUtf8().data());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -3364,7 +3366,7 @@ void CCryEditApp::OnOpenSlice()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* lpszFileName)
|
||||
CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions)
|
||||
{
|
||||
if (m_openingLevel)
|
||||
{
|
||||
@@ -3404,9 +3406,9 @@ CCryEditDoc* CCryEditApp::OpenDocumentFile(const char* lpszFileName)
|
||||
openDocTraceHandler.SetShowWindow(false);
|
||||
}
|
||||
|
||||
// in this case, we set bAddToMRU to always be true because adding files to the MRU list
|
||||
// in this case, we set addToMostRecentFileList to always be true because adding files to the MRU list
|
||||
// automatically culls duplicate and normalizes paths anyway
|
||||
m_pDocManager->OpenDocumentFile(lpszFileName, true);
|
||||
m_pDocManager->OpenDocumentFile(filename, addToMostRecentFileList, openSameLevelOptions);
|
||||
|
||||
if (openDocTraceHandler.HasAnyErrors())
|
||||
{
|
||||
@@ -4133,9 +4135,9 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
|
||||
Editor::EditorQtApplication::InstallQtLogHandler();
|
||||
|
||||
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
|
||||
Editor::EditorQtApplication app(argc, argv);
|
||||
Editor::EditorQtApplication* app = Editor::EditorQtApplication::newInstance(argc, argv);
|
||||
|
||||
if (app.arguments().contains("-autotest_mode"))
|
||||
if (app->arguments().contains("-autotest_mode"))
|
||||
{
|
||||
// Nullroute all stdout to null for automated tests, this way we make sure
|
||||
// that the test result output is not polluted with unrelated output data.
|
||||
@@ -4171,12 +4173,7 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::NotifyQtApplicationAvailable, &app);
|
||||
|
||||
#if defined(AZ_PLATFORM_MAC)
|
||||
// Native menu bars do not work on macOS due to all the tool dialogs
|
||||
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
|
||||
#endif
|
||||
AzToolsFramework::EditorEvents::Bus::Broadcast(&AzToolsFramework::EditorEvents::NotifyQtApplicationAvailable, app);
|
||||
|
||||
int exitCode = 0;
|
||||
|
||||
@@ -4187,9 +4184,9 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
|
||||
|
||||
if (didCryEditStart)
|
||||
{
|
||||
app.EnableOnIdle();
|
||||
app->EnableOnIdle();
|
||||
|
||||
ret = app.exec();
|
||||
ret = app->exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4200,6 +4197,8 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[])
|
||||
|
||||
}
|
||||
|
||||
delete app;
|
||||
|
||||
gSettings.Disconnect();
|
||||
|
||||
return ret;
|
||||
|
||||
+11
-3
@@ -85,6 +85,12 @@ public:
|
||||
|
||||
using EditorIdleProcessingBus = AZ::EBus<EditorIdleProcessing>;
|
||||
|
||||
enum class COpenSameLevelOptions
|
||||
{
|
||||
ReopenLevelIfSame,
|
||||
NotReopenIfSame
|
||||
};
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
class SANDBOX_API CCryEditApp
|
||||
@@ -174,7 +180,9 @@ public:
|
||||
virtual bool InitInstance();
|
||||
virtual int ExitInstance(int exitCode = 0);
|
||||
virtual bool OnIdle(LONG lCount);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszFileName);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* filename,
|
||||
bool addToMostRecentFileList=true,
|
||||
COpenSameLevelOptions openSameLevelOptions = COpenSameLevelOptions::NotReopenIfSame);
|
||||
|
||||
CCryDocManager* GetDocManager() { return m_pDocManager; }
|
||||
|
||||
@@ -448,7 +456,7 @@ public:
|
||||
~CCrySingleDocTemplate() {};
|
||||
// avoid creating another CMainFrame
|
||||
// close other type docs before opening any things
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool bAddToMRU, bool bMakeVisible);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool addToMostRecentFileList, bool bMakeVisible);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszPathName, bool bMakeVisible = TRUE);
|
||||
virtual Confidence MatchDocType(const char* lpszPathName, CCryEditDoc*& rpDocMatch);
|
||||
|
||||
@@ -468,7 +476,7 @@ public:
|
||||
virtual void OnFileNew();
|
||||
virtual bool DoPromptFileName(QString& fileName, UINT nIDSTitle,
|
||||
DWORD lFlags, bool bOpenFileDialog, CDocTemplate* pTemplate);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* lpszFileName, bool bAddToMRU);
|
||||
virtual CCryEditDoc* OpenDocumentFile(const char* filename, bool addToMostRecentFileList, COpenSameLevelOptions openSameLevelOptions = COpenSameLevelOptions::NotReopenIfSame);
|
||||
|
||||
QVector<CCrySingleDocTemplate*> m_templateList;
|
||||
};
|
||||
|
||||
+11
-13
@@ -143,20 +143,11 @@ namespace
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const bool addToMostRecentFileList = false;
|
||||
auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.toUtf8().data(),
|
||||
addToMostRecentFileList, COpenSameLevelOptions::ReopenLevelIfSame);
|
||||
|
||||
auto previousDocument = GetIEditor()->GetDocument();
|
||||
QString previousPathName = (previousDocument != nullptr) ? previousDocument->GetLevelPathName() : "";
|
||||
auto newDocument = CCryEditApp::instance()->OpenDocumentFile(levelPath.toUtf8().data());
|
||||
|
||||
// the underlying document pointer doesn't change, so we can't check that; use the path name's instead
|
||||
|
||||
bool result = true;
|
||||
if (newDocument == nullptr || newDocument->IsLevelLoadFailed() || (newDocument->GetLevelPathName() == previousPathName))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
return newDocument != nullptr && !newDocument->IsLevelLoadFailed();
|
||||
}
|
||||
|
||||
bool PyOpenLevelNoPrompt(const char* pLevelName)
|
||||
@@ -407,6 +398,11 @@ inline namespace Commands
|
||||
{
|
||||
return AZ::Debug::Trace::WaitForDebugger(timeoutSeconds);
|
||||
}
|
||||
|
||||
AZStd::string PyGetFileAlias(AZStd::string alias)
|
||||
{
|
||||
return AZ::IO::FileIOBase::GetInstance()->GetAlias(alias.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
namespace AzToolsFramework
|
||||
@@ -457,6 +453,8 @@ namespace AzToolsFramework
|
||||
addLegacyGeneral(behaviorContext->Method("attach_debugger", PyAttachDebugger, nullptr, "Prompts for attaching the debugger"));
|
||||
addLegacyGeneral(behaviorContext->Method("wait_for_debugger", PyWaitForDebugger, behaviorContext->MakeDefaultValues(-1.f), "Pauses this thread execution until the debugger has been attached"));
|
||||
|
||||
addLegacyGeneral(behaviorContext->Method("get_file_alias", PyGetFileAlias, nullptr, "Retrieves path for IO alias"));
|
||||
|
||||
// this will put these methods into the 'azlmbr.legacy.checkout_dialog' module
|
||||
auto addCheckoutDialog = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
|
||||
{
|
||||
|
||||
@@ -75,7 +75,10 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
->Field("CaptureCursorLook", &CameraMovementSettings::m_captureCursorLook)
|
||||
->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
|
||||
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
|
||||
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY);
|
||||
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY)
|
||||
->Field("DefaultPositionX", &CameraMovementSettings::m_defaultCameraPositionX)
|
||||
->Field("DefaultPositionY", &CameraMovementSettings::m_defaultCameraPositionY)
|
||||
->Field("DefaultPositionZ", &CameraMovementSettings::m_defaultCameraPositionZ);
|
||||
|
||||
serialize.Class<CameraInputSettings>()
|
||||
->Version(2)
|
||||
@@ -154,7 +157,16 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
"Invert direction of pan in local Y axis")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_captureCursorLook, "Camera Capture Look Cursor",
|
||||
"Should the cursor be captured (hidden) while performing free look");
|
||||
"Should the cursor be captured (hidden) while performing free look")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionX, "Default X Camera Position",
|
||||
"Default X Camera Position when a level is opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionY, "Default Y Camera Position",
|
||||
"Default Y Camera Position when a level is opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionZ, "Default Z Camera Position",
|
||||
"Default Z Camera Position when a level is opened");
|
||||
|
||||
editContext->Class<CameraInputSettings>("Camera Input Settings", "")
|
||||
->DataElement(
|
||||
@@ -271,6 +283,12 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
|
||||
SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
|
||||
SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX);
|
||||
SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY);
|
||||
SandboxEditor::SetDefaultCameraEditorPosition(
|
||||
AZ::Vector3(
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ
|
||||
));
|
||||
|
||||
SandboxEditor::SetCameraTranslateForwardChannelId(m_cameraInputSettings.m_translateForwardChannelId);
|
||||
SandboxEditor::SetCameraTranslateBackwardChannelId(m_cameraInputSettings.m_translateBackwardChannelId);
|
||||
@@ -308,6 +326,11 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
|
||||
m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX();
|
||||
m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY();
|
||||
|
||||
AZ::Vector3 defaultCameraPosition = SandboxEditor::DefaultEditorCameraPosition();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX = defaultCameraPosition.GetX();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY = defaultCameraPosition.GetY();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ = defaultCameraPosition.GetZ();
|
||||
|
||||
m_cameraInputSettings.m_translateForwardChannelId = SandboxEditor::CameraTranslateForwardChannelId().GetName();
|
||||
m_cameraInputSettings.m_translateBackwardChannelId = SandboxEditor::CameraTranslateBackwardChannelId().GetName();
|
||||
m_cameraInputSettings.m_translateLeftChannelId = SandboxEditor::CameraTranslateLeftChannelId().GetName();
|
||||
|
||||
@@ -57,6 +57,9 @@ private:
|
||||
bool m_orbitYawRotationInverted;
|
||||
bool m_panInvertedX;
|
||||
bool m_panInvertedY;
|
||||
float m_defaultCameraPositionX;
|
||||
float m_defaultCameraPositionY;
|
||||
float m_defaultCameraPositionZ;
|
||||
|
||||
AZ::Crc32 RotateSmoothingVisibility() const
|
||||
{
|
||||
|
||||
@@ -52,6 +52,9 @@ namespace SandboxEditor
|
||||
constexpr AZStd::string_view CameraOrbitDollyIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitDollyId";
|
||||
constexpr AZStd::string_view CameraOrbitPanIdSetting = "/Amazon/Preferences/Editor/Camera/OrbitPanId";
|
||||
constexpr AZStd::string_view CameraFocusIdSetting = "/Amazon/Preferences/Editor/Camera/FocusId";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionX = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/x";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionY = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/y";
|
||||
constexpr AZStd::string_view CameraDefaultStartingPositionZ = "/Amazon/Preferences/Editor/Camera/DefaultStartingPosition/z";
|
||||
|
||||
template<typename T>
|
||||
void SetRegistry(const AZStd::string_view setting, T&& value)
|
||||
@@ -111,6 +114,21 @@ namespace SandboxEditor
|
||||
return AZStd::make_unique<EditorViewportSettingsCallbacksImpl>();
|
||||
}
|
||||
|
||||
AZ::Vector3 DefaultEditorCameraPosition()
|
||||
{
|
||||
float xPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionX, 0.0));
|
||||
float yPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionY, -10.0));
|
||||
float zPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionZ, 4.0));
|
||||
return AZ::Vector3(xPosition, yPosition, zPosition);
|
||||
}
|
||||
|
||||
void SetDefaultCameraEditorPosition(const AZ::Vector3 defaultCameraPosition)
|
||||
{
|
||||
SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX());
|
||||
SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY());
|
||||
SetRegistry(CameraDefaultStartingPositionZ, defaultCameraPosition.GetZ());
|
||||
}
|
||||
|
||||
AZ::u64 MaxItemsShownInAssetBrowserSearch()
|
||||
{
|
||||
return GetRegistry(AssetBrowserMaxItemsShownInSearchSetting, aznumeric_cast<AZ::u64>(50));
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzFramework/Input/Channels/InputChannelId.h>
|
||||
|
||||
namespace SandboxEditor
|
||||
@@ -32,6 +33,9 @@ namespace SandboxEditor
|
||||
//! event will fire when a value in the settings registry (editorpreferences.setreg) is modified.
|
||||
SANDBOX_API AZStd::unique_ptr<EditorViewportSettingsCallbacks> CreateEditorViewportSettingsCallbacks();
|
||||
|
||||
SANDBOX_API AZ::Vector3 DefaultEditorCameraPosition();
|
||||
SANDBOX_API void SetDefaultCameraEditorPosition(AZ::Vector3 defaultCameraPosition);
|
||||
|
||||
SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch();
|
||||
SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown);
|
||||
|
||||
|
||||
@@ -112,7 +112,6 @@ void StartFixedCursorMode(QObject *viewport);
|
||||
|
||||
#define RENDER_MESH_TEST_DISTANCE (0.2f)
|
||||
#define CURSOR_FONT_HEIGHT 8.0f
|
||||
|
||||
namespace AZ::ViewportHelpers
|
||||
{
|
||||
static const char TextCantCreateCameraNoLevel[] = "Cannot create camera when no level is loaded.";
|
||||
@@ -623,16 +622,10 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
|
||||
float sx = terrainAabb.GetXExtent();
|
||||
float sy = terrainAabb.GetYExtent();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
// Initial camera will be at middle of the map at the height of 2
|
||||
// meters above the terrain (default terrain height is 32)
|
||||
viewTM.SetTranslation(Vec3(sx * 0.5f, sy * 0.5f, 34.0f));
|
||||
|
||||
viewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(viewTM);
|
||||
|
||||
UpdateScene();
|
||||
@@ -647,16 +640,10 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
AZ::Aabb terrainAabb = AZ::Aabb::CreateFromPoint(AZ::Vector3::CreateZero());
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(terrainAabb, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb);
|
||||
float sx = terrainAabb.GetXExtent();
|
||||
float sy = terrainAabb.GetYExtent();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
// Initial camera will be at middle of the map at the height of 2
|
||||
// meters above the terrain (default terrain height is 32)
|
||||
viewTM.SetTranslation(Vec3(sx * 0.5f, sy * 0.5f, 34.0f));
|
||||
|
||||
viewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(viewTM);
|
||||
}
|
||||
break;
|
||||
@@ -1345,10 +1332,6 @@ void EditorViewportWidget::keyPressEvent(QKeyEvent* event)
|
||||
|
||||
void EditorViewportWidget::SetViewTM(const Matrix34& tm)
|
||||
{
|
||||
if (m_viewSourceType == ViewSourceType::None)
|
||||
{
|
||||
m_defaultViewTM = tm;
|
||||
}
|
||||
SetViewTM(tm, false);
|
||||
}
|
||||
|
||||
@@ -1445,6 +1428,10 @@ void EditorViewportWidget::SetViewTM(const Matrix34& camMatrix, bool bMoveOnly)
|
||||
"Please report this as a bug."
|
||||
);
|
||||
}
|
||||
else if (shouldUpdateObject == ShouldUpdateObject::No)
|
||||
{
|
||||
GetCurrentAtomView()->SetCameraTransform(LYTransformToAZMatrix3x4(camMatrix));
|
||||
}
|
||||
|
||||
if (m_pressedKeyState == KeyPressedState::PressedThisFrame)
|
||||
{
|
||||
@@ -2028,6 +2015,9 @@ void EditorViewportWidget::SetDefaultCamera()
|
||||
m_viewSourceType = ViewSourceType::None;
|
||||
GetViewManager()->SetCameraObjectId(GUID_NULL);
|
||||
SetName(m_defaultViewName);
|
||||
|
||||
// Set the default Editor Camera position.
|
||||
m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(m_defaultViewTM);
|
||||
|
||||
// Synchronize the configured editor viewport FOV to the default camera
|
||||
@@ -2530,6 +2520,11 @@ bool EditorViewportSettings::StickySelectEnabled() const
|
||||
return SandboxEditor::StickySelectEnabled();
|
||||
}
|
||||
|
||||
AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const
|
||||
{
|
||||
return SandboxEditor::DefaultEditorCameraPosition();
|
||||
}
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
|
||||
|
||||
bool EditorViewportWidget::ShouldPreviewFullscreen() const
|
||||
@@ -2641,5 +2636,4 @@ void EditorViewportWidget::StopFullscreenPreview()
|
||||
// Show the main window
|
||||
MainWindow::instance()->show();
|
||||
}
|
||||
|
||||
#include <moc_EditorViewportWidget.cpp>
|
||||
|
||||
@@ -78,6 +78,7 @@ struct EditorViewportSettings : public AzToolsFramework::ViewportInteraction::Vi
|
||||
float ManipulatorLineBoundWidth() const override;
|
||||
float ManipulatorCircleBoundWidth() const override;
|
||||
bool StickySelectEnabled() const override;
|
||||
AZ::Vector3 DefaultEditorCameraPosition() const override;
|
||||
};
|
||||
|
||||
// EditorViewportWidget window
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#define MUSIC_LEVEL_LIBRARY_FILE "Music.xml"
|
||||
#define MATERIAL_LEVEL_LIBRARY_FILE "Materials.xml"
|
||||
#define RESOURCE_LIST_FILE "ResourceList.txt"
|
||||
#define USED_RESOURCE_LIST_FILE "UsedResourceList.txt"
|
||||
#define SHADER_LIST_FILE "ShadersList.txt"
|
||||
#define MUSIC_LEVEL_LIBRARY_FILE "music.xml"
|
||||
#define MATERIAL_LEVEL_LIBRARY_FILE "materials.xml"
|
||||
#define RESOURCE_LIST_FILE "resourcelist.txt"
|
||||
#define USED_RESOURCE_LIST_FILE "usedresourcelist.txt"
|
||||
#define SHADER_LIST_FILE "shaderslist.txt"
|
||||
|
||||
#define GetAValue(rgb) ((BYTE)((rgb) >> 24))
|
||||
|
||||
@@ -185,9 +185,9 @@ bool CGameExporter::Export(unsigned int flags, [[maybe_unused]] EEndian eExportE
|
||||
ExportOcclusionMesh(sLevelPath.toUtf8().data());
|
||||
|
||||
//! Export Level data.
|
||||
CLogFile::WriteLine("Exporting LevelData.xml");
|
||||
CLogFile::WriteLine("Exporting leveldata.xml");
|
||||
ExportLevelData(sLevelPath);
|
||||
CLogFile::WriteLine("Exporting LevelData.xml done.");
|
||||
CLogFile::WriteLine("Exporting leveldata.xml done.");
|
||||
|
||||
ExportLevelInfo(sLevelPath);
|
||||
|
||||
@@ -266,26 +266,26 @@ void CGameExporter::ExportOcclusionMesh(const char* pszGamePath)
|
||||
void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/)
|
||||
{
|
||||
IEditor* pEditor = GetIEditor();
|
||||
pEditor->SetStatusText(QObject::tr("Exporting LevelData.xml..."));
|
||||
pEditor->SetStatusText(QObject::tr("Exporting leveldata.xml..."));
|
||||
|
||||
char versionString[256];
|
||||
pEditor->GetFileVersion().ToString(versionString);
|
||||
|
||||
XmlNodeRef root = XmlHelpers::CreateXmlNode("LevelData");
|
||||
XmlNodeRef root = XmlHelpers::CreateXmlNode("leveldata");
|
||||
root->setAttr("SandboxVersion", versionString);
|
||||
XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("LevelDataAction");
|
||||
XmlNodeRef rootAction = XmlHelpers::CreateXmlNode("leveldataaction");
|
||||
rootAction->setAttr("SandboxVersion", versionString);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Save Level Data XML
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QString levelDataFile = path + "LevelData.xml";
|
||||
QString levelDataFile = path + "leveldata.xml";
|
||||
XmlString xmlData = root->getXML();
|
||||
CCryMemFile file;
|
||||
file.Write(xmlData.c_str(), static_cast<unsigned int>(xmlData.length()));
|
||||
m_levelPak.m_pakFile.UpdateFile(levelDataFile.toUtf8().data(), file);
|
||||
|
||||
QString levelDataActionFile = path + "LevelDataAction.xml";
|
||||
QString levelDataActionFile = path + "leveldataaction.xml";
|
||||
XmlString xmlDataAction = rootAction->getXML();
|
||||
CCryMemFile fileAction;
|
||||
fileAction.Write(xmlDataAction.c_str(), static_cast<unsigned int>(xmlDataAction.length()));
|
||||
@@ -298,7 +298,7 @@ void CGameExporter::ExportLevelData(const QString& path, bool /*bExportMission*/
|
||||
if (savedEntities)
|
||||
{
|
||||
QString entitiesFile;
|
||||
entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "Mission0");
|
||||
entitiesFile = QStringLiteral("%1%2.entities_xml").arg(path, "mission0");
|
||||
m_levelPak.m_pakFile.UpdateFile(entitiesFile.toUtf8().data(), entitySaveBuffer.begin(), static_cast<int>(entitySaveBuffer.size()));
|
||||
}
|
||||
}
|
||||
@@ -326,7 +326,7 @@ void CGameExporter::ExportLevelInfo(const QString& path)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Save LevelInfo file.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QString filename = path + "LevelInfo.xml";
|
||||
QString filename = path + "levelinfo.xml";
|
||||
XmlString xmlData = root->getXML();
|
||||
|
||||
CCryMemFile file;
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace UnitTest
|
||||
void BeginCursorCapture() override;
|
||||
void EndCursorCapture() override;
|
||||
bool IsMouseOver() const override;
|
||||
void SetOverrideCursor(AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride) override;
|
||||
void ClearOverrideCursor() override;
|
||||
|
||||
private:
|
||||
AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr;
|
||||
@@ -58,6 +60,17 @@ namespace UnitTest
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViewportMouseCursorRequestImpl::SetOverrideCursor(
|
||||
[[maybe_unused]] AzToolsFramework::ViewportInteraction::CursorStyleOverride cursorStyleOverride)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void ViewportMouseCursorRequestImpl::ClearOverrideCursor()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
class ModularViewportCameraControllerFixture : public AllocatorsTestFixture
|
||||
{
|
||||
public:
|
||||
|
||||
+11
-2
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "QtEditorApplication.h"
|
||||
#include "QtEditorApplication_linux.h"
|
||||
|
||||
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
#include <AzFramework/XcbEventHandler.h>
|
||||
@@ -14,7 +14,16 @@
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*)
|
||||
EditorQtApplication* EditorQtApplication::newInstance(int& argc, char** argv)
|
||||
{
|
||||
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
return new EditorQtApplicationXcb(argc, argv);
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool EditorQtApplicationXcb::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*)
|
||||
{
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Editor/Core/QtEditorApplication.h>
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
class EditorQtApplicationXcb : public EditorQtApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EditorQtApplicationXcb(int& argc, char** argv)
|
||||
: EditorQtApplication(argc, argv)
|
||||
{
|
||||
}
|
||||
|
||||
// QAbstractNativeEventFilter:
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
|
||||
};
|
||||
} // namespace Editor
|
||||
@@ -7,6 +7,6 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
../../Core/QtEditorApplication_linux.cpp
|
||||
Editor/Core/QtEditorApplication_linux.cpp
|
||||
../Common/Unimplemented/Util/Mailer_Unimplemented.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Editor/Core/QtEditorApplication.h>
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
class EditorQtApplicationMac : public EditorQtApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EditorQtApplicationMac(int& argc, char** argv)
|
||||
: EditorQtApplication(argc, argv)
|
||||
{
|
||||
}
|
||||
|
||||
// QAbstractNativeEventFilter:
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
|
||||
};
|
||||
} // namespace Editor
|
||||
+9
-2
@@ -9,7 +9,7 @@
|
||||
#import <AppKit/NSEvent.h>
|
||||
|
||||
#include "EditorDefs.h"
|
||||
#include "QtEditorApplication.h"
|
||||
#include "QtEditorApplication_mac.h"
|
||||
|
||||
// AzFramework
|
||||
#include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
|
||||
@@ -19,7 +19,14 @@
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
bool EditorQtApplication::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
|
||||
EditorQtApplication* EditorQtApplication::newInstance(int& argc, char** argv)
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
|
||||
|
||||
return new EditorQtApplicationMac(argc, argv);
|
||||
}
|
||||
|
||||
bool EditorQtApplicationMac::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
|
||||
{
|
||||
NSEvent* event = (NSEvent*)message;
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
../../Core/QtEditorApplication_mac.mm
|
||||
Editor/Core/QtEditorApplication_mac.mm
|
||||
../../LogFile_mac.mm
|
||||
../../WindowObserver_mac.h
|
||||
../../WindowObserver_mac.mm
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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 "QtEditorApplication_windows.h"
|
||||
|
||||
// Qt
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QScopedValueRollback>
|
||||
#include <QToolBar>
|
||||
#include <QLoggingCategory>
|
||||
#include <QTimer>
|
||||
|
||||
#include <QtGui/private/qhighdpiscaling_p.h>
|
||||
#include <QtGui/qpa/qplatformnativeinterface.h>
|
||||
|
||||
// AzQtComponents
|
||||
#include <AzQtComponents/Components/Titlebar.h>
|
||||
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
|
||||
|
||||
// AzFramework
|
||||
#include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
EditorQtApplication* EditorQtApplication::newInstance(int& argc, char** argv)
|
||||
{
|
||||
return new EditorQtApplicationWindows(argc, argv);
|
||||
}
|
||||
|
||||
bool EditorQtApplicationWindows::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long* result)
|
||||
{
|
||||
MSG* msg = (MSG*)message;
|
||||
|
||||
if (msg->message == WM_MOVING || msg->message == WM_SIZING)
|
||||
{
|
||||
m_isMovingOrResizing = true;
|
||||
}
|
||||
else if (msg->message == WM_EXITSIZEMOVE)
|
||||
{
|
||||
m_isMovingOrResizing = false;
|
||||
}
|
||||
|
||||
// Prevent the user from being able to move the window in game mode.
|
||||
// This is done during the hit test phase to bypass the native window move messages. If the window
|
||||
// decoration wrapper title bar contains the cursor, set the result to HTCLIENT instead of
|
||||
// HTCAPTION.
|
||||
if (msg->message == WM_NCHITTEST && GetIEditor()->IsInGameMode())
|
||||
{
|
||||
const LRESULT defWinProcResult = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
|
||||
if (defWinProcResult == 1)
|
||||
{
|
||||
if (QWidget* widget = QWidget::find((WId)msg->hwnd))
|
||||
{
|
||||
if (auto wrapper = qobject_cast<const AzQtComponents::WindowDecorationWrapper*>(widget))
|
||||
{
|
||||
AzQtComponents::TitleBar* titleBar = wrapper->titleBar();
|
||||
const short global_x = static_cast<short>(LOWORD(msg->lParam));
|
||||
const short global_y = static_cast<short>(HIWORD(msg->lParam));
|
||||
|
||||
const QPoint globalPos = QHighDpi::fromNativePixels(QPoint(global_x, global_y), widget->window()->windowHandle());
|
||||
const QPoint local = titleBar->mapFromGlobal(globalPos);
|
||||
if (titleBar->draggableRect().contains(local) && !titleBar->isTopResizeArea(globalPos))
|
||||
{
|
||||
*result = HTCLIENT;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system.
|
||||
// These events are only broadcast in game mode. In Editor mode, RenderViewportWidget creates synthetic
|
||||
// keyboard and mouse events via Qt.
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
if (msg->message == WM_INPUT)
|
||||
{
|
||||
UINT rawInputSize;
|
||||
const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER);
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, nullptr, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
AZStd::array<BYTE, sizeof(RAWINPUT)> rawInputBytesArray;
|
||||
LPBYTE rawInputBytes = rawInputBytesArray.data();
|
||||
|
||||
[[maybe_unused]] const UINT bytesCopied =
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize);
|
||||
CRY_ASSERT(bytesCopied == rawInputSize);
|
||||
|
||||
RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes;
|
||||
CRY_ASSERT(rawInput);
|
||||
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(
|
||||
&AzFramework::RawInputNotificationsWindows::OnRawInputEvent, *rawInput);
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (msg->message == WM_DEVICECHANGE)
|
||||
{
|
||||
if (msg->wParam == 0x0007) // DBT_DEVNODES_CHANGED
|
||||
{
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(
|
||||
&AzFramework::RawInputNotificationsWindows::OnRawInputDeviceChangeEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorQtApplicationWindows::eventFilter(QObject* object, QEvent* event)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::Leave:
|
||||
{
|
||||
// if we receive a leave event for a toolbar on Windows
|
||||
// check first whether we really left it. If we didn't: start checking
|
||||
// for the tool bar under the mouse by timer to check when we really left.
|
||||
// Synthesize a new leave event then. Workaround for LY-69788
|
||||
auto toolBarAt = [](const QPoint& pos) -> QToolBar*
|
||||
{
|
||||
QWidget* widget = qApp->widgetAt(pos);
|
||||
while (widget != nullptr)
|
||||
{
|
||||
if (QToolBar* tb = qobject_cast<QToolBar*>(widget))
|
||||
{
|
||||
return tb;
|
||||
}
|
||||
widget = widget->parentWidget();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (object == toolBarAt(QCursor::pos()))
|
||||
{
|
||||
QTimer* t = new QTimer(object);
|
||||
t->start(100);
|
||||
connect(
|
||||
t, &QTimer::timeout, object,
|
||||
[t, object, toolBarAt]()
|
||||
{
|
||||
if (object != toolBarAt(QCursor::pos()))
|
||||
{
|
||||
QEvent event(QEvent::Leave);
|
||||
qApp->sendEvent(object, &event);
|
||||
t->deleteLater();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return EditorQtApplication::eventFilter(object, event);
|
||||
}
|
||||
} // namespace Editor
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 <Editor/Core/QtEditorApplication.h>
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
class EditorQtApplicationWindows : public EditorQtApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EditorQtApplicationWindows(int& argc, char** argv)
|
||||
: EditorQtApplication(argc, argv)
|
||||
{
|
||||
}
|
||||
|
||||
// QAbstractNativeEventFilter:
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
|
||||
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
};
|
||||
} // namespace Editor
|
||||
@@ -7,5 +7,6 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Editor/Core/QtEditorApplication_windows.cpp
|
||||
Util/Mailer_Windows.cpp
|
||||
)
|
||||
|
||||
@@ -1788,6 +1788,11 @@ AZStd::string SandboxIntegrationManager::GetComponentEditorIcon(const AZ::Uuid&
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
AZStd::string SandboxIntegrationManager::GetComponentTypeEditorIcon(const AZ::Uuid& componentType)
|
||||
{
|
||||
return GetComponentEditorIcon(componentType, nullptr);
|
||||
}
|
||||
|
||||
AZStd::string SandboxIntegrationManager::GetComponentIconPath(const AZ::Uuid& componentType,
|
||||
AZ::Crc32 componentIconAttrib, AZ::Component* component)
|
||||
{
|
||||
|
||||
@@ -233,6 +233,7 @@ private:
|
||||
}
|
||||
|
||||
AZStd::string GetComponentEditorIcon(const AZ::Uuid& componentType, AZ::Component* component) override;
|
||||
AZStd::string GetComponentTypeEditorIcon(const AZ::Uuid& componentType) override;
|
||||
AZStd::string GetComponentIconPath(const AZ::Uuid& componentType, AZ::Crc32 componentIconAttrib, AZ::Component* component) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+1
-1
@@ -139,7 +139,7 @@ ComponentDataModel::ComponentDataModel(QObject* parent)
|
||||
if (element.m_elementId == AZ::Edit::ClassElements::EditorData)
|
||||
{
|
||||
AZStd::string iconPath;
|
||||
EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr);
|
||||
AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId);
|
||||
if (!iconPath.empty())
|
||||
{
|
||||
m_componentIcons[classData->m_typeId] = QIcon(iconPath.c_str());
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace AZ
|
||||
else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_ProviderIsDown)
|
||||
{
|
||||
message = "Failed to put entries/dependencies into source control as the provider is not available.\n";
|
||||
reportAsWarning = true;
|
||||
}
|
||||
else if (info.m_status == AzToolsFramework::SourceControlStatus::SCS_CertificateInvalid)
|
||||
{
|
||||
|
||||
@@ -34,11 +34,12 @@ CStartupLogoDialog::CStartupLogoDialog(QString versionText, QString richTextCopy
|
||||
m_ui->setupUi(this);
|
||||
|
||||
s_pLogoWindow = this;
|
||||
setFixedSize(QSize(600, 300));
|
||||
setFixedSize(QSize(m_enforcedWidth, m_enforcedHeight));
|
||||
setAttribute(Qt::WA_TranslucentBackground, true);
|
||||
|
||||
// Prepare background image
|
||||
m_backgroundImage = AzQtComponents::ScalePixmapForScreenDpi(
|
||||
QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_developer_preview.jpg")),
|
||||
QPixmap(QStringLiteral(":/StartupLogoDialog/splashscreen_background_2021_11.jpg")),
|
||||
screen(),
|
||||
QSize(m_enforcedWidth, m_enforcedHeight),
|
||||
Qt::IgnoreAspectRatio,
|
||||
|
||||
@@ -50,7 +50,7 @@ private:
|
||||
QScopedPointer<Ui::StartupLogoDialog> m_ui;
|
||||
|
||||
QPixmap m_backgroundImage;
|
||||
const int m_enforcedWidth = 600;
|
||||
const int m_enforcedHeight = 300;
|
||||
const int m_enforcedWidth = 668;
|
||||
const int m_enforcedHeight = 368;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/StartupLogoDialog">
|
||||
<file>o3de_logo.svg</file>
|
||||
<file>splashscreen_background_developer_preview.jpg</file>
|
||||
<file>splashscreen_background_2021_11.jpg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -6,13 +6,22 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>300</height>
|
||||
<width>668</width>
|
||||
<height>368</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>42</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>42</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
<number>42</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>10</number>
|
||||
@@ -29,7 +38,7 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<width>300</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -53,7 +62,7 @@
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>28</number>
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>24</number>
|
||||
@@ -94,7 +103,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Developer Preview</string>
|
||||
<string>General Availability</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -153,20 +162,28 @@
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>290</width>
|
||||
<height>0</height>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>290</width>
|
||||
<height>16777215</height>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Starting Editor...</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -408,7 +408,7 @@ CTrackViewNodesCtrl::CTrackViewNodesCtrl(QWidget* hParentWnd, CTrackViewDialog*
|
||||
serializeContext->EnumerateDerived<AZ::Component>([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
|
||||
{
|
||||
AZStd::string iconPath;
|
||||
EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr);
|
||||
AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId);
|
||||
if (!iconPath.empty())
|
||||
{
|
||||
m_componentTypeToIconMap[classData->m_typeId] = QIcon(iconPath.c_str());
|
||||
|
||||
@@ -330,8 +330,6 @@ set(FILES
|
||||
Commands/CommandManager.h
|
||||
Controls/BitmapToolTip.cpp
|
||||
Controls/BitmapToolTip.h
|
||||
Controls/ColorGradientCtrl.cpp
|
||||
Controls/ColorGradientCtrl.h
|
||||
Controls/ConsoleSCB.cpp
|
||||
Controls/ConsoleSCB.h
|
||||
Controls/ConsoleSCB.ui
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ffcb7614bed0790bf58a2bb7b2d70958289cb2edf562acc8fc841f4c50a55445
|
||||
size 2974586
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7105ec99477f124a8ac8d588f2dfc4ee7bb54f39386c8131b7703c86754c0cb8
|
||||
size 248690
|
||||
@@ -65,8 +65,35 @@ namespace AZ
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides - Application is a singleton
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
typedef AZStd::recursive_mutex MutexType;
|
||||
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
|
||||
static constexpr bool EnableEventQueue = true;
|
||||
using EventQueueMutexType = AZStd::mutex;
|
||||
struct PostThreadDispatchInvoker
|
||||
{
|
||||
~PostThreadDispatchInvoker();
|
||||
};
|
||||
|
||||
template <typename DispatchMutex>
|
||||
struct ThreadDispatchLockGuard
|
||||
{
|
||||
ThreadDispatchLockGuard(DispatchMutex& contextMutex)
|
||||
: m_lock{ contextMutex }
|
||||
{}
|
||||
ThreadDispatchLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
|
||||
: m_lock{ contextMutex, adopt_lock }
|
||||
{}
|
||||
ThreadDispatchLockGuard(const ThreadDispatchLockGuard&) = delete;
|
||||
ThreadDispatchLockGuard& operator=(const ThreadDispatchLockGuard&) = delete;
|
||||
private:
|
||||
PostThreadDispatchInvoker m_threadPolicyInvoker;
|
||||
using LockType = AZStd::conditional_t<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
|
||||
LockType m_lock;
|
||||
};
|
||||
|
||||
template <typename DispatchMutex, bool>
|
||||
using DispatchLockGuard = ThreadDispatchLockGuard<DispatchMutex>;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual ~AssetCatalogRequests() = default;
|
||||
@@ -200,6 +227,17 @@ namespace AZ
|
||||
|
||||
using AssetCatalogRequestBus = AZ::EBus<AssetCatalogRequests>;
|
||||
|
||||
inline AssetCatalogRequests::PostThreadDispatchInvoker::~PostThreadDispatchInvoker()
|
||||
{
|
||||
if (!AssetCatalogRequestBus::IsInDispatchThisThread())
|
||||
{
|
||||
if (AssetCatalogRequestBus::QueuedEventCount())
|
||||
{
|
||||
AssetCatalogRequestBus::ExecuteQueuedEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Events that AssetManager listens for
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Component/ComponentApplicationLifecycle.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
|
||||
#include <AzCore/Debug/LocalFileEventLogger.h>
|
||||
@@ -44,8 +45,6 @@
|
||||
#include <AzCore/Module/Module.h>
|
||||
#include <AzCore/Module/ModuleManager.h>
|
||||
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
#include <AzCore/Driller/Driller.h>
|
||||
@@ -506,6 +505,16 @@ namespace AZ
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
|
||||
|
||||
// The /O3DE/Application/LifecycleEvents array contains a valid set of lifecycle events
|
||||
// Those lifecycle events are normally read from the <engine-root>/Registry
|
||||
// which isn't merged until ComponentApplication::Create invokes MergeSettingsToRegistry
|
||||
// So pre-populate the valid lifecycle even entries
|
||||
ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SystemAllocatorCreated");
|
||||
ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SettingsRegistryAvailable");
|
||||
ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "ConsoleAvailable");
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorCreated", R"({})");
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryAvailable", R"({})");
|
||||
|
||||
// Create the Module Manager
|
||||
m_moduleManager = AZStd::make_unique<ModuleManager>();
|
||||
|
||||
@@ -520,6 +529,7 @@ namespace AZ
|
||||
m_ownsConsole = true;
|
||||
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
|
||||
m_settingsRegistryConsoleFunctors = AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_settingsRegistry, *m_console);
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleAvailable", R"({})");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,6 +561,7 @@ namespace AZ
|
||||
{
|
||||
AZ::Interface<AZ::IConsole>::Unregister(m_console);
|
||||
delete m_console;
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleUnavailable", R"({})");
|
||||
}
|
||||
|
||||
m_moduleManager.reset();
|
||||
@@ -558,6 +569,8 @@ namespace AZ
|
||||
if (AZ::SettingsRegistry::Get() == m_settingsRegistry.get())
|
||||
{
|
||||
SettingsRegistry::Unregister(m_settingsRegistry.get());
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryUnavailable", R"({})");
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorPendingDestruction", R"({})");
|
||||
}
|
||||
m_settingsRegistry.reset();
|
||||
|
||||
@@ -672,6 +685,8 @@ namespace AZ
|
||||
ReflectionEnvironment::GetReflectionManager()->Reflect(azrtti_typeid(this), [this](ReflectContext* context) {Reflect(context); });
|
||||
|
||||
RegisterCoreComponents();
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerAvailable", R"({})");
|
||||
|
||||
TickBus::AllowFunctionQueuing(true);
|
||||
SystemTickBus::AllowFunctionQueuing(true);
|
||||
|
||||
@@ -691,6 +706,7 @@ namespace AZ
|
||||
|
||||
// Load the actual modules
|
||||
LoadModules();
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsLoaded", R"({})");
|
||||
|
||||
// Execute user.cfg after modules have been loaded but before processing any command-line overrides
|
||||
AZ::IO::FixedMaxPath platformCachePath;
|
||||
@@ -756,12 +772,14 @@ namespace AZ
|
||||
m_entities.rehash(0); // force free all memory
|
||||
|
||||
DestroyReflectionManager();
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerUnavailable", R"({})");
|
||||
|
||||
static_cast<SettingsRegistryImpl*>(m_settingsRegistry.get())->ClearNotifiers();
|
||||
static_cast<SettingsRegistryImpl*>(m_settingsRegistry.get())->ClearMergeEvents();
|
||||
|
||||
// Uninit and unload any dynamic modules.
|
||||
m_moduleManager->UnloadModules();
|
||||
ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsUnloaded", R"({})");
|
||||
|
||||
NameDictionary::Destroy();
|
||||
|
||||
|
||||
@@ -175,6 +175,8 @@ namespace AZ
|
||||
bool m_loadDynamicModules = true;
|
||||
//! Used by test fixtures to ensure reflection occurs to edit context.
|
||||
bool m_createEditContext = false;
|
||||
//! Indicates whether the AssetCatalog.xml should be loaded by default in Application::StartCommon
|
||||
bool m_loadAssetCatalog = true;
|
||||
};
|
||||
|
||||
ComponentApplication();
|
||||
@@ -356,7 +358,7 @@ namespace AZ
|
||||
/// Calculates the root directory of the engine.
|
||||
void CalculateEngineRoot();
|
||||
|
||||
/// Calculates the directory where the bootstrap.cfg file resides.
|
||||
/// Deprecated: The term "AppRoot" has no meaning
|
||||
void CalculateAppRoot();
|
||||
|
||||
template<typename Iterator>
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationLifecycle.h>
|
||||
#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
|
||||
|
||||
namespace AZ::ComponentApplicationLifecycle
|
||||
{
|
||||
bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName)
|
||||
{
|
||||
using FixedValueString = SettingsRegistryInterface::FixedValueString;
|
||||
using Type = SettingsRegistryInterface::Type;
|
||||
FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey };
|
||||
eventRegistrationKey += '/';
|
||||
eventRegistrationKey += eventName;
|
||||
return settingsRegistry.GetType(eventRegistrationKey) == Type::Object;
|
||||
}
|
||||
|
||||
bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue)
|
||||
{
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
using Format = AZ::SettingsRegistryInterface::Format;
|
||||
|
||||
if (!ValidateEvent(settingsRegistry, eventName))
|
||||
{
|
||||
AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot signal event %.*s. Name does is not a field of object "%.*s".)"
|
||||
R"( Please make sure the entry exists in the '<engine-root>/Registry/application_lifecycle_events.setreg")"
|
||||
" or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
|
||||
return false;
|
||||
}
|
||||
auto eventRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey),
|
||||
AZ_STRING_ARG(eventName));
|
||||
|
||||
return settingsRegistry.MergeSettings(eventValue, Format::JsonMergePatch, eventRegistrationKey);
|
||||
}
|
||||
|
||||
bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName)
|
||||
{
|
||||
using FixedValueString = SettingsRegistryInterface::FixedValueString;
|
||||
using Format = AZ::SettingsRegistryInterface::Format;
|
||||
|
||||
if (!ValidateEvent(settingsRegistry, eventName))
|
||||
{
|
||||
FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey };
|
||||
eventRegistrationKey += '/';
|
||||
eventRegistrationKey += eventName;
|
||||
return settingsRegistry.MergeSettings(R"({})", Format::JsonMergePatch, eventRegistrationKey);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
|
||||
AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName)
|
||||
{
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
using Type = AZ::SettingsRegistryInterface::Type;
|
||||
using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler;
|
||||
|
||||
if (!ValidateEvent(settingsRegistry, eventName))
|
||||
{
|
||||
AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot register event %.*s. Name does is not a field of object "%.*s".)"
|
||||
R"( Please make sure the entry exists in the '<engine-root>/Registry/application_lifecycle_events.setreg")"
|
||||
" or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
|
||||
return false;
|
||||
}
|
||||
auto eventNameRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey),
|
||||
AZ_STRING_ARG(eventName));
|
||||
auto lifecycleCallback = [callback = AZStd::move(callback), eventNameRegistrationKey](AZStd::string_view path, Type type)
|
||||
{
|
||||
if (path == eventNameRegistrationKey)
|
||||
{
|
||||
callback(path, type);
|
||||
}
|
||||
};
|
||||
|
||||
handler = NotifyEventHandler(AZStd::move(lifecycleCallback));
|
||||
settingsRegistry.RegisterNotifier(handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
namespace AZ::ComponentApplicationLifecycle
|
||||
{
|
||||
//! Root Key where lifecycle events should be registered under
|
||||
inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Runtime/Application/LifecycleEvents";
|
||||
|
||||
|
||||
//! Validates that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
|
||||
//! @param settingsRegistry registry where @eventName will be searched
|
||||
//! @param eventName name of key that validated that exists as an element in the ApplicationLifecycleEventRegistrationKey array
|
||||
//! @return true if the @eventName was found in the ApplicationLifecycleEventRegistrationKey array
|
||||
bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName);
|
||||
|
||||
//! Wrapper around setting a value underneath the ApplicationLifecycleEventRegistrationKey
|
||||
//! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array
|
||||
//! It then appends the @eventName to the ApplicationLifecycleEventRegistrationKey merges the @eventValue into
|
||||
//! the SettingsRegistry at that key
|
||||
//! NOTE: This function should only be invoked from ComponentApplication and its derived classes
|
||||
//! @param settingsRegistry registry where eventName should be set
|
||||
//! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to signal
|
||||
//! @param eventValue JSON Object that will be merged into the SettingsRegistry at <ApplicationLifecycleEventRootKey>/<eventName>
|
||||
//! @return true if the eventValue was successfully merged at the <ApplicationLifecycleEventRootKey>/<eventName>
|
||||
bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue);
|
||||
|
||||
//! Register that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
|
||||
//! @param settingsRegistry registry where @eventName will be searched
|
||||
//! @param eventName name of key that will be stored in the ApplicationLifecycleEventRegistrationKey array
|
||||
//! @return true if the event passed validation or the eventName was stored in the ApplicationLifecycleEventRegistrationKey array
|
||||
bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName);
|
||||
|
||||
//! Wrapper around registering the NotifyEventHandler with the SettingsRegistry for the specified event
|
||||
//! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array and if
|
||||
//! so moves the @callback into @handler and then registers the handler with the SettingsRegistry NotifyEvent
|
||||
//! @param settingsRegistry registry where handler will be registered
|
||||
//! @param handler handler where callback will be moved into and then registered with the SettingsRegistry
|
||||
//! if the specified @eventName passes validation
|
||||
//! @param callback will be moved into the handler if the specified @eventName is valid
|
||||
//! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to register
|
||||
//! @return true if the handler was registered with the SettingsRegistry NotifyEvent
|
||||
bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
|
||||
AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName);
|
||||
}
|
||||
@@ -262,6 +262,6 @@ static constexpr AZ::ThreadSafety ConsoleThreadSafety<_TYPE, std::enable_if_t<st
|
||||
//! @param _FLAGS a set of AzFramework::ConsoleFunctorFlags used to mutate behaviour
|
||||
//! @param _DESC a description of the cvar
|
||||
#define AZ_CONSOLEFREEFUNC_4(_NAME, _FUNCTION, _FLAGS, _DESC) \
|
||||
inline AZ::ConsoleFunctor<void, false> Functor##_FUNCTION(#_FUNCTION, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
|
||||
inline AZ::ConsoleFunctor<void, false> Functor##_FUNCTION(_NAME, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
|
||||
|
||||
#define AZ_CONSOLEFREEFUNC(...) AZ_MACRO_SPECIALIZE(AZ_CONSOLEFREEFUNC_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__))
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/DomVisitor.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
const char* VisitorError::CodeToString(VisitorErrorCode code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case VisitorErrorCode::UnsupportedOperation:
|
||||
return "operation not supported";
|
||||
case VisitorErrorCode::InvalidData:
|
||||
return "invalid data specified";
|
||||
case VisitorErrorCode::InternalError:
|
||||
return "internal error";
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
VisitorError::VisitorError(VisitorErrorCode code)
|
||||
: m_code(code)
|
||||
{
|
||||
}
|
||||
|
||||
VisitorError::VisitorError(VisitorErrorCode code, AZStd::string additionalInfo)
|
||||
: m_code(code)
|
||||
, m_additionalInfo(AZStd::move(additionalInfo))
|
||||
{
|
||||
}
|
||||
|
||||
VisitorErrorCode VisitorError::GetCode() const
|
||||
{
|
||||
return m_code;
|
||||
}
|
||||
|
||||
const AZStd::string& VisitorError::GetAdditionalInfo() const
|
||||
{
|
||||
return m_additionalInfo;
|
||||
}
|
||||
|
||||
AZStd::string VisitorError::FormatVisitorErrorMessage() const
|
||||
{
|
||||
if (m_additionalInfo.empty())
|
||||
{
|
||||
return AZStd::string::format("VisitorError: %s.", CodeToString(m_code));
|
||||
}
|
||||
return AZStd::string::format("VisitorError: %s. %s.", CodeToString(m_code), m_additionalInfo.c_str());
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code)
|
||||
{
|
||||
return AZ::Failure(VisitorError(code));
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo)
|
||||
{
|
||||
return AZ::Failure(VisitorError(code, AZStd::move(additionalInfo)));
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::VisitorFailure(VisitorError error)
|
||||
{
|
||||
return AZ::Failure(error);
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::VisitorSuccess()
|
||||
{
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Null()
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Bool([[maybe_unused]] bool value)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Int64([[maybe_unused]] AZ::s64 value)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Uint64([[maybe_unused]] AZ::u64 value)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Double([[maybe_unused]] double value)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::String([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::OpaqueValue([[maybe_unused]] const OpaqueType& value, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
if (!SupportsOpaqueValues())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Opaque values are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::RawValue([[maybe_unused]] AZStd::string_view value, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
if (!SupportsRawValues())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw values are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::StartObject()
|
||||
{
|
||||
if (!SupportsObjects())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::EndObject([[maybe_unused]] AZ::u64 attributeCount)
|
||||
{
|
||||
if (!SupportsObjects())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Objects are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::Key([[maybe_unused]] AZ::Name key)
|
||||
{
|
||||
if (!SupportsObjects() && !SupportsNodes())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Keys are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::RawKey(AZStd::string_view key, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
if (!SupportsRawKeys())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Raw keys are not supported by this visitor");
|
||||
}
|
||||
return Key(AZ::Name(key));
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::StartArray()
|
||||
{
|
||||
if (!SupportsArrays())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::EndArray([[maybe_unused]] AZ::u64 elementCount)
|
||||
{
|
||||
if (!SupportsArrays())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Arrays are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::StartNode([[maybe_unused]] AZ::Name name)
|
||||
{
|
||||
if (!SupportsNodes())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::RawStartNode(AZStd::string_view name, [[maybe_unused]] Lifetime lifetime)
|
||||
{
|
||||
return StartNode(AZ::Name(name));
|
||||
}
|
||||
|
||||
Visitor::Result Visitor::EndNode([[maybe_unused]] AZ::u64 attributeCount, [[maybe_unused]] AZ::u64 elementCount)
|
||||
{
|
||||
if (!SupportsNodes())
|
||||
{
|
||||
return VisitorFailure(VisitorErrorCode::UnsupportedOperation, "Nodes are not supported by this visitor");
|
||||
}
|
||||
return VisitorSuccess();
|
||||
}
|
||||
|
||||
VisitorFlags Visitor::GetVisitorFlags() const
|
||||
{
|
||||
// By default support raw keys (promoting them to AZ::Name) and support Array / Object / Node
|
||||
// We leave Opaque type support and Raw Values to more specialized, implementation-specific cases
|
||||
return VisitorFlags::SupportsRawKeys | VisitorFlags::SupportsArrays | VisitorFlags::SupportsObjects | VisitorFlags::SupportsNodes;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsRawValues() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsRawValues) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsRawKeys() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsRawKeys) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsObjects() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsObjects) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsArrays() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsArrays) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsNodes() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsNodes) != VisitorFlags::Null;
|
||||
}
|
||||
|
||||
bool Visitor::SupportsOpaqueValues() const
|
||||
{
|
||||
return (GetVisitorFlags() & VisitorFlags::SupportsOpaqueValues) != VisitorFlags::Null;
|
||||
}
|
||||
} // namespace AZ::DOM
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/std/any.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AZ::DOM
|
||||
{
|
||||
//
|
||||
// Lifetime enum
|
||||
//
|
||||
//! Specifies the period in which a reference value will still be alive and safe to read.
|
||||
enum class Lifetime
|
||||
{
|
||||
//! Specifies that the value is safe to read and will remain so indefinitely.
|
||||
//! This implies that the value will not be mutated for the duration of this storage.
|
||||
Persistent,
|
||||
//! Specifies that the value may change or be deallocated, and must be copied to be safely stored.
|
||||
Temporary,
|
||||
};
|
||||
|
||||
//
|
||||
// VisitorErrorCode enum
|
||||
//
|
||||
//! Error code specifying the reason a Visitor operation failed.
|
||||
enum class VisitorErrorCode
|
||||
{
|
||||
//! Set when a Visitor doesn't have an implementation for a given attribute type.
|
||||
//! A pure-JSON serializer might reject a Node attribute, for example, and serialization visitors
|
||||
//! can forbid non-serializable Opaque types.
|
||||
UnsupportedOperation,
|
||||
//! Set when a Visitor has received malformed or invalid data.
|
||||
//! Potential sources include mismatching Begin/End call pairs or invalid attribute or element counts
|
||||
//! being sent to End methods.
|
||||
InvalidData,
|
||||
//! The Visitor failed for some other reason not caused by invalid input.
|
||||
//! If returning a custom error with this code, it's preferrable to also provide supplemental info
|
||||
//! in the form of an explanatory string.
|
||||
InternalError
|
||||
};
|
||||
|
||||
//
|
||||
// VisitorError class
|
||||
//
|
||||
//! Details of the reason for failure within a VisitorInterface operation.
|
||||
class VisitorError final
|
||||
{
|
||||
public:
|
||||
explicit VisitorError(VisitorErrorCode code);
|
||||
VisitorError(VisitorErrorCode code, AZStd::string additionalInfo);
|
||||
|
||||
//! Gets the error code associated with this error.
|
||||
VisitorErrorCode GetCode() const;
|
||||
//! Gets a supplemental error info string from the error.
|
||||
//! Returns an empty string if no additional information was provided to the error.
|
||||
const AZStd::string& GetAdditionalInfo() const;
|
||||
//! Provides a formatted, human-readable error description that can be used for logging purposes.
|
||||
AZStd::string FormatVisitorErrorMessage() const;
|
||||
|
||||
//! Helper method, translates a VisitorErrorCode to a human readable string.
|
||||
static const char* CodeToString(VisitorErrorCode code);
|
||||
|
||||
private:
|
||||
VisitorErrorCode m_code;
|
||||
AZStd::string m_additionalInfo;
|
||||
};
|
||||
|
||||
//! A type alias for opaque DOM types that aren't meant to be serializable.
|
||||
//! /see VisitorInterface::OpaqueValue
|
||||
using OpaqueType = AZStd::any;
|
||||
|
||||
//
|
||||
// VisitorFlags enum
|
||||
//
|
||||
//! Flags representning capabilities of a \ref Visitor.
|
||||
enum class VisitorFlags : AZ::u16
|
||||
{
|
||||
//! No flags are set. This can be used in conjunction with bitwise operators to check a flag.
|
||||
Null = 0,
|
||||
//! If set, this Visitor interface supports raw strings in place of specific value types.
|
||||
//! Visitors with this flag accept RawValue calls in lieu of more specific value calls such as Int64 or String.
|
||||
SupportsRawValues = (1 << 1),
|
||||
//! If set, this Visitor interface supports raw strings in place of Name types for keys and Node names.
|
||||
//! Visitors with this flag accept RawKey and RawStartNode in lieu of Key and StartNode calls.
|
||||
SupportsRawKeys = (1 << 2),
|
||||
//! If set, this Visitor interface supports Object types described via BeginObject and EndObject.
|
||||
SupportsObjects = (1 << 3),
|
||||
//! If set, this Visitor interface supports Array types described via BeginArray and EndArray.
|
||||
SupportsArrays = (1 << 4),
|
||||
//! If set, this Visitor interface supports Node types described BeginNode and EndNode.
|
||||
SupportsNodes = (1 << 4),
|
||||
//! If set, this Visitor interface supports opaque values described via OpaqueValue.
|
||||
SupportsOpaqueValues = (1 << 5),
|
||||
};
|
||||
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(VisitorFlags);
|
||||
|
||||
//
|
||||
// Visitor class
|
||||
//
|
||||
//! An interface for performing operations on elements of a generic DOM (Document Object Model).
|
||||
//! A Document Object Model is defined here as a tree structure comprised of one of the following values:
|
||||
//! - Primitives: plain data types, including
|
||||
//! - \ref Int64: 64 bit signed integer
|
||||
//! - \ref Uint64: 64 bit unsigned integer
|
||||
//! - \ref Bool: boolean value
|
||||
//! - \ref Double: 64 bit double precision float
|
||||
//! - \ref Null: sentinel "empty" type with no value representation
|
||||
//! - \ref String: UTF8 encoded string
|
||||
//! - \ref Object: an ordered container of key/value pairs where keys are AZ::Names and values may be any DOM type
|
||||
//! (including Object)
|
||||
//! - \ref Array: an ordered container of values, in which values are any DOM value type (including Array)
|
||||
//! - \ref Node: a container
|
||||
//! - \ref OpaqueValue: An arbitrary value stored in an AZStd::any. This is a non-serializable representation of an
|
||||
//! entry useful for in-memory options. This is intended to be used as an intermediate value over the course of DOM
|
||||
//! transformation and as a proxy to pass through types of which the DOM has no knowledge to other systems.
|
||||
//!
|
||||
//! Opaque values are rejected by the default VisitorInterface implementation.
|
||||
//!
|
||||
//! Care should be ensured that DOMs representing opaque types are only visited by consumers that understand them.
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
virtual ~Visitor() = default;
|
||||
|
||||
//! The result of a Visitor operation.
|
||||
//! A failure indicates a non-recoverable issue and signals that no further visit calls may be made in the
|
||||
//! current state.
|
||||
using Result = AZ::Outcome<void, VisitorError>;
|
||||
|
||||
//! Returns a set of flags representing the operations this Visitor supports.
|
||||
//! The base implementation supports raw keys (\see VisitorFlags::SupportsRawKeys) and
|
||||
//! arrays (\see VisitorFlags::SupportsArrays), objects (\see VisitorFlags::SupportsObjects), and
|
||||
//! nodes (\see VisitorFlags::SupportsNodes).
|
||||
//! Raw (\see VisitorFlags::SupportsRawValues) and opaque values (\see VisitorFlags::SupportsOpaqueValues)
|
||||
//! are disallowed by default, as their handling is intended to be implementation-specific.
|
||||
virtual VisitorFlags GetVisitorFlags() const;
|
||||
//! /see VisitorFlags::SupportsRawValues
|
||||
bool SupportsRawValues() const;
|
||||
//! /see VisitorFlags::SupportsRawKeys
|
||||
bool SupportsRawKeys() const;
|
||||
//! /see VisitorFlags::SupportsObjects
|
||||
bool SupportsObjects() const;
|
||||
//! /see VisitorFlags::SupportsArrays
|
||||
bool SupportsArrays() const;
|
||||
//! /see VisitorFlags::SupportsNodes
|
||||
bool SupportsNodes() const;
|
||||
//! /see VisitorFlags::SupportsOpaqueValues
|
||||
bool SupportsOpaqueValues() const;
|
||||
|
||||
//! Operates on an empty null value.
|
||||
virtual Result Null();
|
||||
//! Operates on a bool value.
|
||||
virtual Result Bool(bool value);
|
||||
//! Operates on a signed, 64 bit integer value.
|
||||
virtual Result Int64(AZ::s64 value);
|
||||
//! Operates on an unsigned, 64 bit integer value.
|
||||
virtual Result Uint64(AZ::u64 value);
|
||||
//! Operates on a double precision, 64 bit floating point value.
|
||||
virtual Result Double(double value);
|
||||
//! Operates on a string value. As strings are a reference type.
|
||||
//! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy.
|
||||
virtual Result String(AZStd::string_view value, Lifetime lifetime);
|
||||
//! Operates on an opaque value. As opaque values are a reference type, storage semantics are provided to
|
||||
//! indicate where the value may be stored persistently or requires a copy.
|
||||
//! The base implementation of OpaqueValue rejects the operation, as opaque values are meant for special
|
||||
//! cases with specific implementations, not generic usage.
|
||||
//! Storage semantics are provided to indicate where the value may be stored persistently or requires a copy.
|
||||
virtual Result OpaqueValue(const OpaqueType& value, Lifetime lifetime);
|
||||
//! Operates on a raw value encoded as a UTF-8 string that hasn't had its type deduced.
|
||||
//! Visitors that support raw values (\see VisitorFlags::SupportsRawValues) may parse the raw value and
|
||||
//! forward it to the corresponding value call or calls of their choice.
|
||||
//! The base implementation of RawValue rejects the operation, as raw values are meant to be handled on
|
||||
//! a per-implementation basis.
|
||||
virtual Result RawValue(AZStd::string_view value, Lifetime lifetime);
|
||||
|
||||
//! Operates on an Object.
|
||||
//! Callers may make any number of Key calls, followed by calls representing a value (including a nested
|
||||
//! StartObject call) and then must call EndObject.
|
||||
virtual Result StartObject();
|
||||
//! Finishes operating on an Object.
|
||||
//! Callers must provide the number of attributes that were provided to the object, i.e. the number of key
|
||||
//! and value calls made within the direct context of this object (but not any nested objects / nodes).
|
||||
virtual Result EndObject(AZ::u64 attributeCount);
|
||||
|
||||
//! Specifies a key for a key/value pair.
|
||||
//! Key must be called subsequent to a call to \ref StartObject or \ref StartNode and immediately followed by
|
||||
//! calls representing the key's associated value.
|
||||
virtual Result Key(AZ::Name key);
|
||||
//! Specifies a key for a key/value pair using a raw string instead of \ref AZ::Name.
|
||||
//! \see Key
|
||||
virtual Result RawKey(AZStd::string_view key, Lifetime lifetime);
|
||||
|
||||
//! Operates on an Array.
|
||||
//! Callers may make any number of subsequent value calls to represent the elements of the array, and then must
|
||||
//! call EndArray.
|
||||
virtual Result StartArray();
|
||||
//! Finishes operating on an Array.
|
||||
//! Callers must provide the number of elements that were provided to the array, i.e. the number of value calls
|
||||
//! made within the direct context of this array (but not any nested arrays / nodes).
|
||||
virtual Result EndArray(AZ::u64 elementCount);
|
||||
|
||||
//! Operates on a Node.
|
||||
//! Callers may make any number of Key calls followed by value calls or value calls not prefixed with a Key
|
||||
//! call, and then must call EndNode. See \ref StartObject and \ref StartArray as Node types combine the
|
||||
//! functionality of both structures into a named Node structure.
|
||||
virtual Result StartNode(AZ::Name name);
|
||||
//! Operates on a Node using a raw string instead of \ref AZ::Name.
|
||||
//! \see StartNode
|
||||
virtual Result RawStartNode(AZStd::string_view name, Lifetime lifetime);
|
||||
//! Finishes operating on a Node.
|
||||
//! Callers must provide both the number of attributes the were provided and the number of elements that were
|
||||
//! provided to the node, attributes being values prefaced by a call to Key.
|
||||
virtual Result EndNode(AZ::u64 attributeCount, AZ::u64 elementCount);
|
||||
|
||||
protected:
|
||||
Visitor() = default;
|
||||
|
||||
//! Helper method, constructs a failure \ref Result with the specified code.
|
||||
static Result VisitorFailure(VisitorErrorCode code);
|
||||
//! Helper method, constructs a failure \ref Result with the specified code and supplemental info.
|
||||
static Result VisitorFailure(VisitorErrorCode code, AZStd::string additionalInfo);
|
||||
//! Helper method, constructs a failure \ref Result with the specified error.
|
||||
static Result VisitorFailure(VisitorError error);
|
||||
//! Helper method, constructs a success \ref Result.
|
||||
static Result VisitorSuccess();
|
||||
};
|
||||
} // namespace AZ::DOM
|
||||
@@ -577,7 +577,9 @@ namespace AZ
|
||||
}
|
||||
|
||||
azstrcat(lines[i], AZ_ARRAY_SIZE(lines[i]), "\n");
|
||||
AZ_Printf(window, "%s", lines[i]); // feed back into the trace system so that listeners can get it.
|
||||
// Use Output instead of AZ_Printf to be consistent with the exception output code and avoid
|
||||
// this accidentally being suppressed as a normal message
|
||||
Output(window, lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,13 @@
|
||||
#include <AzCore/Module/Internal/ModuleManagerSearchPathTool.h>
|
||||
|
||||
#include <AzCore/Module/Module.h>
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/ComponentApplicationLifecycle.h>
|
||||
#include <AzCore/NativeUI/NativeUIRequests.h>
|
||||
#include <AzCore/Script/ScriptSystemBus.h>
|
||||
#include <AzCore/Script/ScriptContext.h>
|
||||
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
@@ -221,11 +218,16 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string componentNamesArray = R"({ "SystemComponents":[)";
|
||||
const char* comma = "";
|
||||
// For all system components, deactivate
|
||||
for (auto componentIt = m_systemComponents.rbegin(); componentIt != m_systemComponents.rend(); ++componentIt)
|
||||
{
|
||||
ModuleEntity::DeactivateComponent(**componentIt);
|
||||
componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, (*componentIt)->RTTI_GetTypeName());
|
||||
comma = ", ";
|
||||
}
|
||||
componentNamesArray += R"(]})";
|
||||
|
||||
// For all modules that we created an entity for, set them to "Init" (meaning not Activated)
|
||||
for (auto& moduleData : m_ownedModules)
|
||||
@@ -239,6 +241,13 @@ namespace AZ
|
||||
|
||||
// Since the system components have been deactivated clear out the vector.
|
||||
m_systemComponents.clear();
|
||||
|
||||
// Signal that the System Components have deactivated
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsDeactivated", componentNamesArray);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
@@ -284,7 +293,11 @@ namespace AZ
|
||||
{
|
||||
// Split the tag list
|
||||
AZStd::vector<AZStd::string_view> tagList;
|
||||
AZStd::tokenize<AZStd::string_view>(tags, ",", tagList);
|
||||
auto TokenizeTags = [&tagList](AZStd::string_view token)
|
||||
{
|
||||
tagList.push_back(token);
|
||||
};
|
||||
AZ::StringFunc::TokenizeVisitor(tags, TokenizeTags, ',');
|
||||
|
||||
m_systemComponentTags.resize(tagList.size());
|
||||
AZStd::transform(tagList.begin(), tagList.end(), m_systemComponentTags.begin(), [](const AZStd::string_view& tag)
|
||||
@@ -737,11 +750,17 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string componentNamesArray = R"({ "SystemComponents":[)";
|
||||
const char* comma = "";
|
||||
// Activate the entities in the appropriate order
|
||||
for (Component* component : componentsToActivate)
|
||||
{
|
||||
ModuleEntity::ActivateComponent(*component);
|
||||
|
||||
componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, component->RTTI_GetTypeName());
|
||||
comma = ", ";
|
||||
}
|
||||
componentNamesArray += R"(]})";
|
||||
|
||||
// Done activating; set state to active
|
||||
for (auto& moduleData : modulesToInit)
|
||||
@@ -755,5 +774,12 @@ namespace AZ
|
||||
|
||||
// Save the activated components for deactivation later
|
||||
m_systemComponents.insert(m_systemComponents.end(), componentsToActivate.begin(), componentsToActivate.end());
|
||||
|
||||
// Signal that the System Components are activated
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsActivated",
|
||||
componentNamesArray);
|
||||
}
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonImporter.h>
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
JsonSerializationResult::ResultCode JsonImportResolver::ResolveNestedImports(rapidjson::Value& jsonDoc,
|
||||
rapidjson::Document::AllocatorType& allocator, ImportPathStack& importPathStack,
|
||||
JsonImportSettings& settings, const AZ::IO::FixedMaxPath& importPath, StackedString& element)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
for (auto& path : importPathStack)
|
||||
{
|
||||
if (importPath == path)
|
||||
{
|
||||
return settings.m_reporting(
|
||||
AZStd::string::format("'%s' was already imported in this chain. This indicates a cyclic dependency.", importPath.c_str()),
|
||||
ResultCode(Tasks::Import, Outcomes::Catastrophic), element);
|
||||
}
|
||||
}
|
||||
|
||||
importPathStack.push_back(importPath);
|
||||
AZ::StackedString importElement(AZ::StackedString::Format::JsonPointer);
|
||||
JsonImportSettings nestedImportSettings;
|
||||
nestedImportSettings.m_importer = settings.m_importer;
|
||||
nestedImportSettings.m_reporting = settings.m_reporting;
|
||||
nestedImportSettings.m_resolveFlags = ImportTracking::Dependencies;
|
||||
ResultCode result = ResolveImports(jsonDoc, allocator, importPathStack, nestedImportSettings, importElement);
|
||||
importPathStack.pop_back();
|
||||
|
||||
if (result.GetOutcome() == Outcomes::Catastrophic)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return ResultCode(Tasks::Import, Outcomes::Success);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonImportResolver::ResolveImports(rapidjson::Value& jsonDoc,
|
||||
rapidjson::Document::AllocatorType& allocator, ImportPathStack& importPathStack,
|
||||
JsonImportSettings& settings, StackedString& element)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
if (jsonDoc.IsObject())
|
||||
{
|
||||
for (auto& field : jsonDoc.GetObject())
|
||||
{
|
||||
if(strncmp(field.name.GetString(), JsonSerialization::ImportDirectiveIdentifier, field.name.GetStringLength()) == 0)
|
||||
{
|
||||
const rapidjson::Value& importDirective = field.value;
|
||||
AZ::IO::FixedMaxPath importAbsPath = importPathStack.back();
|
||||
importAbsPath.RemoveFilename();
|
||||
AZStd::string importName;
|
||||
if (importDirective.IsObject())
|
||||
{
|
||||
auto filenameField = importDirective.FindMember("filename");
|
||||
if (filenameField != importDirective.MemberEnd())
|
||||
{
|
||||
importName = AZStd::string(filenameField->value.GetString(), filenameField->value.GetStringLength());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
importName = AZStd::string(importDirective.GetString(), importDirective.GetStringLength());
|
||||
}
|
||||
importAbsPath.Append(importName);
|
||||
|
||||
rapidjson::Value patch;
|
||||
ResultCode resolveResult = settings.m_importer->ResolveImport(&jsonDoc, patch, importDirective, importAbsPath, allocator);
|
||||
if (resolveResult.GetOutcome() == Outcomes::Catastrophic)
|
||||
{
|
||||
return resolveResult;
|
||||
}
|
||||
|
||||
if ((settings.m_resolveFlags & ImportTracking::Imports) == ImportTracking::Imports)
|
||||
{
|
||||
rapidjson::Pointer path(element.Get().data(), element.Get().size());
|
||||
settings.m_importer->AddImportDirective(path, importName);
|
||||
}
|
||||
if ((settings.m_resolveFlags & ImportTracking::Dependencies) == ImportTracking::Dependencies)
|
||||
{
|
||||
settings.m_importer->AddImportedFile(importAbsPath.String());
|
||||
}
|
||||
|
||||
ResultCode result = ResolveNestedImports(jsonDoc, allocator, importPathStack, settings, importAbsPath, element);
|
||||
if (result.GetOutcome() == Outcomes::Catastrophic)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
settings.m_importer->ApplyPatch(jsonDoc, patch, allocator);
|
||||
}
|
||||
else if (field.value.IsObject() || field.value.IsArray())
|
||||
{
|
||||
ScopedStackedString entryName(element, AZStd::string_view(field.name.GetString(), field.name.GetStringLength()));
|
||||
ResultCode result = ResolveImports(field.value, allocator, importPathStack, settings, element);
|
||||
if (result.GetOutcome() == Outcomes::Catastrophic)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(jsonDoc.IsArray())
|
||||
{
|
||||
int index = 0;
|
||||
for (rapidjson::Value::ValueIterator elem = jsonDoc.Begin(); elem != jsonDoc.End(); ++elem, ++index)
|
||||
{
|
||||
if (!elem->IsObject() && !elem->IsArray())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ScopedStackedString entryName(element, index);
|
||||
ResultCode result = ResolveImports(*elem, allocator, importPathStack, settings, element);
|
||||
if (result.GetOutcome() == Outcomes::Catastrophic)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ResultCode(Tasks::Import, Outcomes::Success);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonImportResolver::RestoreImports(rapidjson::Value& jsonDoc,
|
||||
rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
if (jsonDoc.IsObject() || jsonDoc.IsArray())
|
||||
{
|
||||
const BaseJsonImporter::ImportDirectivesList& importDirectives = settings.m_importer->GetImportDirectives();
|
||||
for (auto& import : importDirectives)
|
||||
{
|
||||
rapidjson::Pointer importPtr = import.first;
|
||||
rapidjson::Value* currentValue = importPtr.Get(jsonDoc);
|
||||
|
||||
rapidjson::Value importedValue(rapidjson::kObjectType);
|
||||
importedValue.AddMember(rapidjson::StringRef(JsonSerialization::ImportDirectiveIdentifier), rapidjson::StringRef(import.second.c_str()), allocator);
|
||||
ResultCode resolveResult = JsonSerialization::ResolveImports(importedValue, allocator, settings);
|
||||
if (resolveResult.GetOutcome() == Outcomes::Catastrophic)
|
||||
{
|
||||
return resolveResult;
|
||||
}
|
||||
|
||||
rapidjson::Value patch;
|
||||
settings.m_importer->CreatePatch(patch, importedValue, *currentValue, allocator);
|
||||
settings.m_importer->RestoreImport(currentValue, patch, allocator, import.second);
|
||||
}
|
||||
}
|
||||
|
||||
return ResultCode(Tasks::Import, Outcomes::Success);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonImporter::ResolveImport(rapidjson::Value* importPtr,
|
||||
rapidjson::Value& patch, const rapidjson::Value& importDirective,
|
||||
const AZ::IO::FixedMaxPath& importedFilePath, rapidjson::Document::AllocatorType& allocator)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
auto importedObject = JsonSerializationUtils::ReadJsonFile(importedFilePath.Native());
|
||||
if (importedObject.IsSuccess())
|
||||
{
|
||||
rapidjson::Value& importedDoc = importedObject.GetValue();
|
||||
|
||||
if (importDirective.IsObject())
|
||||
{
|
||||
auto patchField = importDirective.FindMember("patch");
|
||||
if (patchField != importDirective.MemberEnd())
|
||||
{
|
||||
patch.CopyFrom(patchField->value, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
importPtr->CopyFrom(importedDoc, allocator);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ResultCode(Tasks::Import, Outcomes::Catastrophic);
|
||||
}
|
||||
|
||||
return ResultCode(Tasks::Import, Outcomes::Success);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonImporter::RestoreImport(rapidjson::Value* importPtr,
|
||||
rapidjson::Value& patch, rapidjson::Document::AllocatorType& allocator, const AZStd::string& importFilename)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
importPtr->SetObject();
|
||||
if ((patch.IsObject() && patch.MemberCount() > 0) || (patch.IsArray() && !patch.Empty()))
|
||||
{
|
||||
rapidjson::Value importDirective(rapidjson::kObjectType);
|
||||
importDirective.AddMember(rapidjson::StringRef("filename"), rapidjson::StringRef(importFilename.c_str()), allocator);
|
||||
importDirective.AddMember(rapidjson::StringRef("patch"), patch, allocator);
|
||||
importPtr->AddMember(rapidjson::StringRef(JsonSerialization::ImportDirectiveIdentifier), importDirective, allocator);
|
||||
}
|
||||
else
|
||||
{
|
||||
importPtr->AddMember(rapidjson::StringRef(JsonSerialization::ImportDirectiveIdentifier), rapidjson::StringRef(importFilename.c_str()), allocator);
|
||||
}
|
||||
|
||||
return ResultCode(Tasks::Import, Outcomes::Success);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonImporter::ApplyPatch(rapidjson::Value& target,
|
||||
const rapidjson::Value& patch, rapidjson::Document::AllocatorType& allocator)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
if ((patch.IsObject() && patch.MemberCount() > 0) || (patch.IsArray() && !patch.Empty()))
|
||||
{
|
||||
return AZ::JsonSerialization::ApplyPatch(target, allocator, patch, JsonMergeApproach::JsonMergePatch);
|
||||
}
|
||||
|
||||
return ResultCode(Tasks::Import, Outcomes::Success);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode BaseJsonImporter::CreatePatch(rapidjson::Value& patch,
|
||||
const rapidjson::Value& source, const rapidjson::Value& target,
|
||||
rapidjson::Document::AllocatorType& allocator)
|
||||
{
|
||||
return JsonSerialization::CreatePatch(patch, allocator, source, target, JsonMergeApproach::JsonMergePatch);
|
||||
}
|
||||
|
||||
void BaseJsonImporter::AddImportDirective(const rapidjson::Pointer& jsonPtr, AZStd::string importFile)
|
||||
{
|
||||
m_importDirectives.emplace_back(jsonPtr, AZStd::move(importFile));
|
||||
}
|
||||
|
||||
void BaseJsonImporter::AddImportedFile(AZStd::string importedFile)
|
||||
{
|
||||
m_importedFiles.insert(AZStd::move(importedFile));
|
||||
}
|
||||
|
||||
const BaseJsonImporter::ImportDirectivesList& BaseJsonImporter::GetImportDirectives()
|
||||
{
|
||||
return m_importDirectives;
|
||||
}
|
||||
|
||||
const BaseJsonImporter::ImportedFilesList& BaseJsonImporter::GetImportedFiles()
|
||||
{
|
||||
return m_importedFiles;
|
||||
}
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include<AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/JSON/pointer.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
|
||||
#include <AzCore/Serialization/Json/StackedString.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
struct JsonImportSettings;
|
||||
|
||||
class BaseJsonImporter
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(BaseJsonImporter, "{7B225807-7B43-430F-8B11-C794DCF5ACA5}");
|
||||
|
||||
using ImportDirectivesList = AZStd::vector<AZStd::pair<rapidjson::Pointer, AZStd::string>>;
|
||||
using ImportedFilesList = AZStd::unordered_set<AZStd::string>;
|
||||
|
||||
virtual JsonSerializationResult::ResultCode ResolveImport(rapidjson::Value* importPtr,
|
||||
rapidjson::Value& patch, const rapidjson::Value& importDirective,
|
||||
const AZ::IO::FixedMaxPath& importedFilePath, rapidjson::Document::AllocatorType& allocator);
|
||||
|
||||
virtual JsonSerializationResult::ResultCode RestoreImport(rapidjson::Value* importPtr,
|
||||
rapidjson::Value& patch, rapidjson::Document::AllocatorType& allocator,
|
||||
const AZStd::string& importFilename);
|
||||
|
||||
virtual JsonSerializationResult::ResultCode ApplyPatch(rapidjson::Value& target,
|
||||
const rapidjson::Value& patch, rapidjson::Document::AllocatorType& allocator);
|
||||
|
||||
virtual JsonSerializationResult::ResultCode CreatePatch(rapidjson::Value& patch,
|
||||
const rapidjson::Value& source, const rapidjson::Value& target,
|
||||
rapidjson::Document::AllocatorType& allocator);
|
||||
|
||||
void AddImportDirective(const rapidjson::Pointer& jsonPtr, AZStd::string importFile);
|
||||
const ImportDirectivesList& GetImportDirectives();
|
||||
|
||||
void AddImportedFile(AZStd::string importedFile);
|
||||
const ImportedFilesList& GetImportedFiles();
|
||||
|
||||
virtual ~BaseJsonImporter() = default;
|
||||
|
||||
protected:
|
||||
|
||||
ImportDirectivesList m_importDirectives;
|
||||
ImportedFilesList m_importedFiles;
|
||||
};
|
||||
|
||||
enum class ImportTracking : AZ::u8
|
||||
{
|
||||
None = 0,
|
||||
Dependencies = (1<<0),
|
||||
Imports = (1<<1),
|
||||
All = (Dependencies | Imports)
|
||||
};
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(ImportTracking);
|
||||
|
||||
class JsonImportResolver final
|
||||
{
|
||||
public:
|
||||
|
||||
using ImportPathStack = AZStd::vector<AZ::IO::FixedMaxPath>;
|
||||
|
||||
JsonImportResolver() = delete;
|
||||
JsonImportResolver& operator=(const JsonImportResolver& rhs) = delete;
|
||||
JsonImportResolver& operator=(JsonImportResolver&& rhs) = delete;
|
||||
JsonImportResolver(const JsonImportResolver& rhs) = delete;
|
||||
JsonImportResolver(JsonImportResolver&& rhs) = delete;
|
||||
~JsonImportResolver() = delete;
|
||||
|
||||
static JsonSerializationResult::ResultCode ResolveImports(rapidjson::Value& jsonDoc,
|
||||
rapidjson::Document::AllocatorType& allocator, ImportPathStack& importPathStack,
|
||||
JsonImportSettings& settings, StackedString& element);
|
||||
|
||||
static JsonSerializationResult::ResultCode RestoreImports(rapidjson::Value& jsonDoc,
|
||||
rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings);
|
||||
|
||||
private:
|
||||
|
||||
static JsonSerializationResult::ResultCode ResolveNestedImports(rapidjson::Value& jsonDoc,
|
||||
rapidjson::Document::AllocatorType& allocator, ImportPathStack& importPathStack,
|
||||
JsonImportSettings& settings, const AZ::IO::FixedMaxPath& importPath, StackedString& element);
|
||||
};
|
||||
|
||||
|
||||
struct JsonImportSettings final
|
||||
{
|
||||
JsonSerializationResult::JsonIssueCallback m_reporting;
|
||||
|
||||
BaseJsonImporter* m_importer = nullptr;
|
||||
|
||||
ImportTracking m_resolveFlags = ImportTracking::All;
|
||||
|
||||
AZ::IO::FixedMaxPath m_loadedJsonPath;
|
||||
};
|
||||
} // namespace AZ
|
||||
@@ -706,7 +706,7 @@ namespace AZ
|
||||
rapidjson::Value(rapidjson::kNullType), field.value, element, settings);
|
||||
}
|
||||
|
||||
if (result.GetOutcome() == Outcomes::Success)
|
||||
if (result.GetOutcome() == Outcomes::Success || result.GetOutcome() == Outcomes::PartialDefaults)
|
||||
{
|
||||
rapidjson::Value name;
|
||||
name.CopyFrom(field.name, allocator, true);
|
||||
@@ -717,6 +717,10 @@ namespace AZ
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultCode.Combine(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Do an extra pass to find all the fields that are removed.
|
||||
@@ -751,7 +755,7 @@ namespace AZ
|
||||
rapidjson::Value value;
|
||||
ResultCode result = CreateMergePatchInternal(value, allocator,
|
||||
rapidjson::Value(rapidjson::kNullType), field.value, element, settings);
|
||||
if (result.GetOutcome() == Outcomes::Success)
|
||||
if (result.GetOutcome() == Outcomes::Success || result.GetOutcome() == Outcomes::PartialDefaults)
|
||||
{
|
||||
rapidjson::Value name;
|
||||
name.CopyFrom(field.name, allocator, true);
|
||||
@@ -762,11 +766,20 @@ namespace AZ
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultCode.Combine(result);
|
||||
}
|
||||
}
|
||||
|
||||
if (target.MemberCount() == 0)
|
||||
{
|
||||
resultCode.Combine(settings.m_reporting("Added empty object to JSON Merge Patch.",
|
||||
ResultCode(Tasks::CreatePatch, Outcomes::Success), element));
|
||||
}
|
||||
}
|
||||
|
||||
patch = AZStd::move(resultValue);
|
||||
resultCode.Combine(ResultCode(Tasks::CreatePatch, Outcomes::Success));
|
||||
return resultCode;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
|
||||
#include <AzCore/Serialization/Json/JsonDeserializer.h>
|
||||
#include <AzCore/Serialization/Json/JsonImporter.h>
|
||||
#include <AzCore/Serialization/Json/JsonMerger.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerializer.h>
|
||||
@@ -19,11 +20,6 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
const char* JsonSerialization::TypeIdFieldIdentifier = "$type";
|
||||
const char* JsonSerialization::DefaultStringIdentifier = "{}";
|
||||
const char* JsonSerialization::KeyFieldIdentifier = "Key";
|
||||
const char* JsonSerialization::ValueFieldIdentifier = "Value";
|
||||
|
||||
namespace JsonSerializationInternal
|
||||
{
|
||||
template<typename T>
|
||||
@@ -394,6 +390,60 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonSerialization::ResolveImports(
|
||||
rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
if (settings.m_importer == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Importer object needs to be provided");
|
||||
return ResultCode(Tasks::Import, Outcomes::Catastrophic);
|
||||
}
|
||||
|
||||
AZStd::string scratchBuffer;
|
||||
auto issueReportingCallback = [&scratchBuffer](AZStd::string_view message, ResultCode result, AZStd::string_view target) -> ResultCode
|
||||
{
|
||||
return JsonSerialization::DefaultIssueReporter(scratchBuffer, message, result, target);
|
||||
};
|
||||
if (!settings.m_reporting)
|
||||
{
|
||||
settings.m_reporting = issueReportingCallback;
|
||||
}
|
||||
|
||||
JsonImportResolver::ImportPathStack importPathStack;
|
||||
importPathStack.push_back(settings.m_loadedJsonPath);
|
||||
StackedString element(StackedString::Format::JsonPointer);
|
||||
|
||||
return JsonImportResolver::ResolveImports(jsonDoc, allocator, importPathStack, settings, element);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonSerialization::RestoreImports(
|
||||
rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings)
|
||||
{
|
||||
using namespace JsonSerializationResult;
|
||||
|
||||
if (settings.m_importer == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Importer object needs to be provided");
|
||||
return ResultCode(Tasks::Import, Outcomes::Catastrophic);
|
||||
}
|
||||
|
||||
AZStd::string scratchBuffer;
|
||||
auto issueReportingCallback = [&scratchBuffer](AZStd::string_view message, ResultCode result, AZStd::string_view target) -> ResultCode
|
||||
{
|
||||
return JsonSerialization::DefaultIssueReporter(scratchBuffer, message, result, target);
|
||||
};
|
||||
if (!settings.m_reporting)
|
||||
{
|
||||
settings.m_reporting = issueReportingCallback;
|
||||
}
|
||||
|
||||
settings.m_resolveFlags = ImportTracking::None;
|
||||
|
||||
return JsonImportResolver::RestoreImports(jsonDoc, allocator, settings);
|
||||
}
|
||||
|
||||
JsonSerializationResult::ResultCode JsonSerialization::DefaultIssueReporter(AZStd::string& scratchBuffer,
|
||||
AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view path)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
namespace AZ
|
||||
{
|
||||
class BaseJsonSerializer;
|
||||
|
||||
struct JsonImportSettings;
|
||||
|
||||
enum class JsonMergeApproach
|
||||
{
|
||||
@@ -51,10 +53,11 @@ namespace AZ
|
||||
class JsonSerialization final
|
||||
{
|
||||
public:
|
||||
static const char* TypeIdFieldIdentifier;
|
||||
static const char* DefaultStringIdentifier;
|
||||
static const char* KeyFieldIdentifier;
|
||||
static const char* ValueFieldIdentifier;
|
||||
static constexpr const char* TypeIdFieldIdentifier = "$type";
|
||||
static constexpr const char* DefaultStringIdentifier = "{}";
|
||||
static constexpr const char* KeyFieldIdentifier = "Key";
|
||||
static constexpr const char* ValueFieldIdentifier = "Value";
|
||||
static constexpr const char* ImportDirectiveIdentifier = "$import";
|
||||
|
||||
//! Merges two json values together by applying "patch" to "target" using the selected merge algorithm.
|
||||
//! This version of ApplyPatch is destructive to "target". If the patch can't be correctly applied it will
|
||||
@@ -284,6 +287,22 @@ namespace AZ
|
||||
//! @return An enum containing less, equal or greater. In case of an error, the value for the enum will "error".
|
||||
static JsonSerializerCompareResult Compare(const rapidjson::Value& lhs, const rapidjson::Value& rhs);
|
||||
|
||||
//! Resolves all import directives, including nested imports, in the given document. An importer object needs to be passed
|
||||
//! in through the settings.
|
||||
//! @param jsonDoc The json document in which to resolve imports.
|
||||
//! @param allocator The allocator associated with the json document.
|
||||
//! @param settings Additional settings that control the way the imports are resolved.
|
||||
static JsonSerializationResult::ResultCode ResolveImports(
|
||||
rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings);
|
||||
|
||||
//! Restores all import directives that were present in the json document. The same importer object that was
|
||||
//! passed into ResolveImports through the settings needs to be passed here through settings as well.
|
||||
//! @param jsonDoc The json document in which to restore imports.
|
||||
//! @param allocator The allocator associated with the json document.
|
||||
//! @param settings Additional settings that control the way the imports are restored.
|
||||
static JsonSerializationResult::ResultCode RestoreImports(
|
||||
rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings);
|
||||
|
||||
private:
|
||||
JsonSerialization() = delete;
|
||||
~JsonSerialization() = delete;
|
||||
|
||||
@@ -69,6 +69,9 @@ namespace AZ
|
||||
case Tasks::CreatePatch:
|
||||
target.append("a create patch operation ");
|
||||
break;
|
||||
case Tasks::Import:
|
||||
target.append("an import operation");
|
||||
break;
|
||||
default:
|
||||
target.append("an unknown operation ");
|
||||
break;
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace AZ
|
||||
ReadField, //!< Task to read a field from JSON to a value.
|
||||
WriteValue, //!< Task to write a value to a JSON field.
|
||||
Merge, //!< Task to merge two JSON values/documents together.
|
||||
CreatePatch //!< Task to create a patch to transform one value/document to another.
|
||||
CreatePatch, //!< Task to create a patch to transform one value/document to another.
|
||||
Import //!< Task to import a JSON document.
|
||||
};
|
||||
|
||||
//! Describes how the task was processed.
|
||||
|
||||
@@ -15,20 +15,20 @@
|
||||
|
||||
namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
{
|
||||
static void RegisterScriptProxyForNotify(SettingsRegistryScriptProxy& settingsRegistryProxy)
|
||||
static void RegisterScriptProxyForNotify(SettingsRegistryInterface* settingsRegistry,
|
||||
SettingsRegistryScriptProxy::NotifyEventProxy* notifyEventProxy)
|
||||
{
|
||||
if (settingsRegistryProxy.IsValid())
|
||||
if (settingsRegistry != nullptr)
|
||||
{
|
||||
auto ForwardSettingsUpdateToProxyEvent = [&settingsRegistryProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
auto ForwardSettingsUpdateToProxyEvent = [notifyEventProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
if (settingsRegistryProxy.m_notifyEventProxy)
|
||||
if (notifyEventProxy)
|
||||
{
|
||||
settingsRegistryProxy.m_notifyEventProxy->m_scriptNotifyEvent.Signal(path);
|
||||
notifyEventProxy->m_scriptNotifyEvent.Signal(path);
|
||||
}
|
||||
};
|
||||
// Register the forwarding function with the BehaviorContext
|
||||
settingsRegistryProxy.m_notifyEventProxy->m_settingsUpdatedHandler =
|
||||
settingsRegistryProxy.m_settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
|
||||
notifyEventProxy->m_settingsUpdatedHandler = settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
: m_settingsRegistry(AZStd::move(settingsRegistry))
|
||||
, m_notifyEventProxy(AZStd::make_shared<NotifyEventProxy>())
|
||||
{
|
||||
RegisterScriptProxyForNotify(*this);
|
||||
RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
|
||||
}
|
||||
|
||||
// Raw AZ::SettingsRegistryInterface pointer is not owned by the proxy, so it's deleter is a no-op
|
||||
@@ -45,7 +45,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
: m_settingsRegistry(settingsRegistry, [](AZ::SettingsRegistryInterface*) {})
|
||||
, m_notifyEventProxy(AZStd::make_shared<NotifyEventProxy>())
|
||||
{
|
||||
RegisterScriptProxyForNotify(*this);
|
||||
RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
|
||||
}
|
||||
|
||||
// SettingsRegistryScriptProxy function that determines if the SettingsRegistry object is valid
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace AZ::Utils
|
||||
AZ::Outcome<void, AZStd::string> WriteFile(AZStd::string_view content, AZStd::string_view filePath)
|
||||
{
|
||||
AZ::IO::FixedMaxPath filePathFixed = filePath; // Because FileIOStream requires a null-terminated string
|
||||
AZ::IO::FileIOStream stream(filePathFixed.c_str(), AZ::IO::OpenMode::ModeWrite);
|
||||
AZ::IO::FileIOStream stream(filePathFixed.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath);
|
||||
|
||||
bool success = false;
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ set(FILES
|
||||
Component/ComponentApplication.cpp
|
||||
Component/ComponentApplication.h
|
||||
Component/ComponentApplicationBus.h
|
||||
Component/ComponentApplicationLifecycle.cpp
|
||||
Component/ComponentApplicationLifecycle.h
|
||||
Component/ComponentBus.cpp
|
||||
Component/ComponentBus.h
|
||||
Component/ComponentExport.h
|
||||
@@ -121,6 +123,8 @@ set(FILES
|
||||
Debug/TraceMessagesDrillerBus.h
|
||||
Debug/TraceReflection.cpp
|
||||
Debug/TraceReflection.h
|
||||
DOM/DomVisitor.cpp
|
||||
DOM/DomVisitor.h
|
||||
Driller/DefaultStringPool.h
|
||||
Driller/Driller.cpp
|
||||
Driller/Driller.h
|
||||
@@ -522,6 +526,8 @@ set(FILES
|
||||
Serialization/Json/IntSerializer.cpp
|
||||
Serialization/Json/JsonDeserializer.h
|
||||
Serialization/Json/JsonDeserializer.cpp
|
||||
Serialization/Json/JsonImporter.cpp
|
||||
Serialization/Json/JsonImporter.h
|
||||
Serialization/Json/JsonMerger.h
|
||||
Serialization/Json/JsonMerger.cpp
|
||||
Serialization/Json/JsonSerialization.h
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonImporter.h>
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
#include <Tests/Serialization/Json/JsonSerializationTests.h>
|
||||
|
||||
namespace JsonSerializationTests
|
||||
{
|
||||
class JsonImportingTests;
|
||||
|
||||
class JsonImporterCustom
|
||||
: public AZ::BaseJsonImporter
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonImporterCustom, "{003F5896-71E0-4A50-A14F-08C319B06AD0}");
|
||||
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode ResolveImport(rapidjson::Value* importPtr,
|
||||
rapidjson::Value& patch, const rapidjson::Value& importDirective,
|
||||
const AZ::IO::FixedMaxPath& importedFilePath, rapidjson::Document::AllocatorType& allocator) override;
|
||||
|
||||
JsonImporterCustom(JsonImportingTests* tests)
|
||||
{
|
||||
testClass = tests;
|
||||
}
|
||||
|
||||
private:
|
||||
JsonImportingTests* testClass;
|
||||
};
|
||||
|
||||
class JsonImportingTests
|
||||
: public BaseJsonSerializerFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
BaseJsonSerializerFixture::SetUp();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
BaseJsonSerializerFixture::TearDown();
|
||||
}
|
||||
|
||||
void GetTestDocument(const AZStd::string& docName, rapidjson::Document& out)
|
||||
{
|
||||
const char *objectJson = R"({
|
||||
"field_1" : "value_1",
|
||||
"field_2" : "value_2",
|
||||
"field_3" : "value_3"
|
||||
})";
|
||||
|
||||
const char *arrayJson = R"([
|
||||
{ "element_1" : "value_1" },
|
||||
{ "element_2" : "value_2" },
|
||||
{ "element_3" : "value_3" }
|
||||
])";
|
||||
|
||||
const char *nestedImportJson = R"({
|
||||
"desc" : "Nested Import",
|
||||
"obj" : {"$import" : "object.json"}
|
||||
})";
|
||||
|
||||
const char *nestedImportCycle1Json = R"({
|
||||
"desc" : "Nested Import Cycle 1",
|
||||
"obj" : {"$import" : "nested_import_c2.json"}
|
||||
})";
|
||||
|
||||
const char *nestedImportCycle2Json = R"({
|
||||
"desc" : "Nested Import Cycle 2",
|
||||
"obj" : {"$import" : "nested_import_c1.json"}
|
||||
})";
|
||||
|
||||
if (docName.compare("object.json") == 0)
|
||||
{
|
||||
out.Parse(objectJson);
|
||||
ASSERT_FALSE(out.HasParseError());
|
||||
}
|
||||
else if (docName.compare("array.json") == 0)
|
||||
{
|
||||
out.Parse(arrayJson);
|
||||
ASSERT_FALSE(out.HasParseError());
|
||||
}
|
||||
else if (docName.compare("nested_import.json") == 0)
|
||||
{
|
||||
out.Parse(nestedImportJson);
|
||||
ASSERT_FALSE(out.HasParseError());
|
||||
}
|
||||
else if (docName.compare("nested_import_c1.json") == 0)
|
||||
{
|
||||
out.Parse(nestedImportCycle1Json);
|
||||
ASSERT_FALSE(out.HasParseError());
|
||||
}
|
||||
else if (docName.compare("nested_import_c2.json") == 0)
|
||||
{
|
||||
out.Parse(nestedImportCycle2Json);
|
||||
ASSERT_FALSE(out.HasParseError());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void TestImportLoadStore(const char* input, const char* expectedImportedValue)
|
||||
{
|
||||
m_jsonDocument->Parse(input);
|
||||
ASSERT_FALSE(m_jsonDocument->HasParseError());
|
||||
|
||||
JsonImporterCustom* importerObj = new JsonImporterCustom(this);
|
||||
|
||||
rapidjson::Document expectedOutcome;
|
||||
expectedOutcome.Parse(expectedImportedValue);
|
||||
ASSERT_FALSE(expectedOutcome.HasParseError());
|
||||
|
||||
TestResolveImports(importerObj);
|
||||
|
||||
Expect_DocStrEq(m_jsonDocument->GetObject(), expectedOutcome.GetObject());
|
||||
|
||||
rapidjson::Document originalInput;
|
||||
originalInput.Parse(input);
|
||||
ASSERT_FALSE(originalInput.HasParseError());
|
||||
|
||||
TestRestoreImports(importerObj);
|
||||
|
||||
Expect_DocStrEq(m_jsonDocument->GetObject(), originalInput.GetObject());
|
||||
|
||||
m_jsonDocument->SetObject();
|
||||
delete importerObj;
|
||||
}
|
||||
|
||||
void TestImportCycle(const char* input)
|
||||
{
|
||||
m_jsonDocument->Parse(input);
|
||||
ASSERT_FALSE(m_jsonDocument->HasParseError());
|
||||
|
||||
JsonImporterCustom* importerObj = new JsonImporterCustom(this);
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode result = TestResolveImports(importerObj);
|
||||
|
||||
EXPECT_EQ(result.GetOutcome(), AZ::JsonSerializationResult::Outcomes::Catastrophic);
|
||||
|
||||
m_jsonDocument->SetObject();
|
||||
delete importerObj;
|
||||
}
|
||||
|
||||
void TestInsertNewImport(const char* input, const char* expectedRestoredValue)
|
||||
{
|
||||
m_jsonDocument->Parse(input);
|
||||
ASSERT_FALSE(m_jsonDocument->HasParseError());
|
||||
|
||||
JsonImporterCustom* importerObj = new JsonImporterCustom(this);
|
||||
|
||||
TestResolveImports(importerObj);
|
||||
|
||||
importerObj->AddImportDirective(rapidjson::Pointer("/object_2"), "object.json");
|
||||
|
||||
rapidjson::Document expectedOutput;
|
||||
expectedOutput.Parse(expectedRestoredValue);
|
||||
ASSERT_FALSE(expectedOutput.HasParseError());
|
||||
|
||||
TestRestoreImports(importerObj);
|
||||
|
||||
Expect_DocStrEq(m_jsonDocument->GetObject(), expectedOutput.GetObject());
|
||||
|
||||
m_jsonDocument->SetObject();
|
||||
delete importerObj;
|
||||
}
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode TestResolveImports(JsonImporterCustom* importerObj)
|
||||
{
|
||||
AZ::JsonImportSettings settings;
|
||||
settings.m_importer = importerObj;
|
||||
|
||||
return AZ::JsonSerialization::ResolveImports(m_jsonDocument->GetObject(), m_jsonDocument->GetAllocator(), settings);
|
||||
}
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode TestRestoreImports(JsonImporterCustom* importerObj)
|
||||
{
|
||||
AZ::JsonImportSettings settings;
|
||||
settings.m_importer = importerObj;
|
||||
|
||||
return AZ::JsonSerialization::RestoreImports(m_jsonDocument->GetObject(), m_jsonDocument->GetAllocator(), settings);
|
||||
}
|
||||
};
|
||||
|
||||
AZ::JsonSerializationResult::ResultCode JsonImporterCustom::ResolveImport(rapidjson::Value* importPtr,
|
||||
rapidjson::Value& patch, const rapidjson::Value& importDirective, const AZ::IO::FixedMaxPath& importedFilePath,
|
||||
rapidjson::Document::AllocatorType& allocator)
|
||||
{
|
||||
AZ::JsonSerializationResult::ResultCode resultCode(AZ::JsonSerializationResult::Tasks::Import);
|
||||
|
||||
rapidjson::Document importedDoc;
|
||||
testClass->GetTestDocument(importedFilePath.String(), importedDoc);
|
||||
|
||||
if (importDirective.IsObject())
|
||||
{
|
||||
auto patchField = importDirective.FindMember("patch");
|
||||
if (patchField != importDirective.MemberEnd())
|
||||
{
|
||||
patch.CopyFrom(patchField->value, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
importPtr->CopyFrom(importedDoc, allocator);
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
// Test Cases
|
||||
|
||||
TEST_F(JsonImportingTests, ImportSimpleObjectTest)
|
||||
{
|
||||
const char* inputFile = R"(
|
||||
{
|
||||
"name" : "simple_object_import",
|
||||
"object": {"$import" : "object.json"}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* expectedOutput = R"(
|
||||
{
|
||||
"name" : "simple_object_import",
|
||||
"object": {
|
||||
"field_1" : "value_1",
|
||||
"field_2" : "value_2",
|
||||
"field_3" : "value_3"
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
TestImportLoadStore(inputFile, expectedOutput);
|
||||
}
|
||||
|
||||
TEST_F(JsonImportingTests, ImportSimpleObjectPatchTest)
|
||||
{
|
||||
const char* inputFile = R"(
|
||||
{
|
||||
"name" : "simple_object_import",
|
||||
"object": {
|
||||
"$import" : {
|
||||
"filename" : "object.json",
|
||||
"patch" : { "field_2" : "patched_value" }
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* expectedOutput = R"(
|
||||
{
|
||||
"name" : "simple_object_import",
|
||||
"object": {
|
||||
"field_1" : "value_1",
|
||||
"field_2" : "patched_value",
|
||||
"field_3" : "value_3"
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
TestImportLoadStore(inputFile, expectedOutput);
|
||||
}
|
||||
|
||||
TEST_F(JsonImportingTests, ImportSimpleArrayTest)
|
||||
{
|
||||
const char* inputFile = R"(
|
||||
{
|
||||
"name" : "simple_array_import",
|
||||
"object": {"$import" : "array.json"}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* expectedOutput = R"(
|
||||
{
|
||||
"name" : "simple_array_import",
|
||||
"object": [
|
||||
{ "element_1" : "value_1" },
|
||||
{ "element_2" : "value_2" },
|
||||
{ "element_3" : "value_3" }
|
||||
]
|
||||
}
|
||||
)";
|
||||
|
||||
TestImportLoadStore(inputFile, expectedOutput);
|
||||
}
|
||||
|
||||
TEST_F(JsonImportingTests, ImportSimpleArrayPatchTest)
|
||||
{
|
||||
const char* inputFile = R"(
|
||||
{
|
||||
"name" : "simple_array_import",
|
||||
"object": {
|
||||
"$import" : {
|
||||
"filename" : "array.json",
|
||||
"patch" : [ { "element_1" : "patched_value" } ]
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* expectedOutput = R"(
|
||||
{
|
||||
"name" : "simple_array_import",
|
||||
"object": [
|
||||
{ "element_1" : "patched_value" }
|
||||
]
|
||||
}
|
||||
)";
|
||||
|
||||
TestImportLoadStore(inputFile, expectedOutput);
|
||||
}
|
||||
|
||||
TEST_F(JsonImportingTests, NestedImportTest)
|
||||
{
|
||||
const char* inputFile = R"(
|
||||
{
|
||||
"name" : "nested_import",
|
||||
"object": {"$import" : "nested_import.json"}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* expectedOutput = R"(
|
||||
{
|
||||
"name" : "nested_import",
|
||||
"object": {
|
||||
"desc" : "Nested Import",
|
||||
"obj" : {
|
||||
"field_1" : "value_1",
|
||||
"field_2" : "value_2",
|
||||
"field_3" : "value_3"
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
TestImportLoadStore(inputFile, expectedOutput);
|
||||
}
|
||||
|
||||
TEST_F(JsonImportingTests, NestedImportPatchTest)
|
||||
{
|
||||
const char* inputFile = R"(
|
||||
{
|
||||
"name" : "nested_import",
|
||||
"object": {
|
||||
"$import" : {
|
||||
"filename" : "nested_import.json",
|
||||
"patch" : { "obj" : { "field_3" : "patched_value" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* expectedOutput = R"(
|
||||
{
|
||||
"name" : "nested_import",
|
||||
"object": {
|
||||
"desc" : "Nested Import",
|
||||
"obj" : {
|
||||
"field_1" : "value_1",
|
||||
"field_2" : "value_2",
|
||||
"field_3" : "patched_value"
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
TestImportLoadStore(inputFile, expectedOutput);
|
||||
}
|
||||
|
||||
TEST_F(JsonImportingTests, NestedImportCycleTest)
|
||||
{
|
||||
const char* inputFile = R"(
|
||||
{
|
||||
"name" : "nested_import_cycle",
|
||||
"object": {"$import" : "nested_import_c1.json"}
|
||||
}
|
||||
)";
|
||||
|
||||
TestImportCycle(inputFile);
|
||||
}
|
||||
|
||||
TEST_F(JsonImportingTests, InsertNewImportTest)
|
||||
{
|
||||
const char* inputFile = R"(
|
||||
{
|
||||
"name" : "simple_object_import",
|
||||
"object_1": {"$import" : "object.json"},
|
||||
"object_2": {
|
||||
"field_1" : "other_value",
|
||||
"field_2" : "value_2",
|
||||
"field_3" : "value_3"
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
const char* expectedOutput = R"(
|
||||
{
|
||||
"name" : "simple_object_import",
|
||||
"object_1": {"$import" : "object.json"},
|
||||
"object_2": {
|
||||
"$import" : {
|
||||
"filename" : "object.json",
|
||||
"patch" : { "field_1" : "other_value" }
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
TestInsertNewImport(inputFile, expectedOutput);
|
||||
}
|
||||
}
|
||||
@@ -121,6 +121,7 @@ set(FILES
|
||||
Serialization/Json/TestCases_Classes.cpp
|
||||
Serialization/Json/TestCases_Compare.cpp
|
||||
Serialization/Json/TestCases_Enum.cpp
|
||||
Serialization/Json/TestCases_Importing.cpp
|
||||
Serialization/Json/TestCases_Patching.cpp
|
||||
Serialization/Json/TestCases_Pointers.h
|
||||
Serialization/Json/TestCases_Pointers.cpp
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Math/Crc.h>
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/Component/ComponentApplicationLifecycle.h>
|
||||
#include <AzCore/Component/NonUniformScaleBus.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Memory/MemoryComponent.h>
|
||||
@@ -58,6 +59,7 @@
|
||||
#include <AzFramework/Script/ScriptComponent.h>
|
||||
#include <AzFramework/Spawnable/SpawnableSystemComponent.h>
|
||||
#include <AzFramework/StreamingInstall/StreamingInstall.h>
|
||||
#include <AzFramework/SurfaceData/SurfaceData.h>
|
||||
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
#include <AzFramework/Metrics/MetricsPlainTextNameRegistration.h>
|
||||
@@ -119,6 +121,11 @@ namespace AzFramework
|
||||
m_archiveFileIO = AZStd::make_unique<AZ::IO::ArchiveFileIO>(m_archive.get());
|
||||
AZ::IO::FileIOBase::SetInstance(m_archiveFileIO.get());
|
||||
SetFileIOAliases();
|
||||
// The FileIOAvailable event needs to be registered here as this event is sent out
|
||||
// before the settings registry has merged the .setreg files from the <engine-root>
|
||||
// (That happens in MergeSettingsToRegistry
|
||||
AZ::ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "FileIOAvailable");
|
||||
AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOAvailable", R"({})");
|
||||
}
|
||||
|
||||
if (auto nativeUI = AZ::Interface<AZ::NativeUI::NativeUIRequests>::Get(); nativeUI == nullptr)
|
||||
@@ -171,6 +178,8 @@ namespace AzFramework
|
||||
// Archive classes relies on the FileIOBase DirectInstance to close
|
||||
// files properly
|
||||
m_directFileIO.reset();
|
||||
|
||||
AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOUnavailable", R"({})");
|
||||
}
|
||||
|
||||
void Application::Start(const Descriptor& descriptor, const StartupParameters& startupParameters)
|
||||
@@ -195,7 +204,25 @@ namespace AzFramework
|
||||
systemEntity->Activate();
|
||||
AZ_Assert(systemEntity->GetState() == AZ::Entity::State::Active, "System Entity failed to activate.");
|
||||
|
||||
m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active);
|
||||
|
||||
if (m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active); m_isStarted)
|
||||
{
|
||||
if (m_startupParameters.m_loadAssetCatalog)
|
||||
{
|
||||
// Start Monitoring Asset changes over the network and load the AssetCatalog
|
||||
auto StartMonitoringAssetsAndLoadCatalog = [this](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath assetCatalogPath;
|
||||
m_settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
|
||||
{
|
||||
assetCatalogPath /= "assetcatalog.xml";
|
||||
assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str());
|
||||
}
|
||||
};
|
||||
using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus;
|
||||
AssetCatalogBus::Broadcast(AZStd::move(StartMonitoringAssetsAndLoadCatalog));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::PreModuleLoad()
|
||||
@@ -209,6 +236,17 @@ namespace AzFramework
|
||||
{
|
||||
if (m_isStarted)
|
||||
{
|
||||
if (m_startupParameters.m_loadAssetCatalog)
|
||||
{
|
||||
// Stop Monitoring Assets changes
|
||||
auto StopMonitoringAssets = [](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
|
||||
{
|
||||
assetCatalogRequests->StopMonitoringAssets();
|
||||
};
|
||||
using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus;
|
||||
AssetCatalogBus::Broadcast(AZStd::move(StopMonitoringAssets));
|
||||
}
|
||||
|
||||
ApplicationLifecycleEvents::Bus::Broadcast(&ApplicationLifecycleEvents::OnApplicationAboutToStop);
|
||||
|
||||
m_pimpl.reset();
|
||||
@@ -278,6 +316,8 @@ namespace AzFramework
|
||||
AzFramework::RemoteStorageDriveConfig::Reflect(context);
|
||||
|
||||
Physics::ReflectionUtils::ReflectPhysicsApi(context);
|
||||
AzFramework::SurfaceData::SurfaceTagWeight::Reflect(context);
|
||||
AzFramework::SurfaceData::SurfacePoint::Reflect(context);
|
||||
AzFramework::Terrain::TerrainDataRequests::Reflect(context);
|
||||
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
|
||||
@@ -565,7 +565,7 @@ namespace AzFramework
|
||||
|
||||
if (!bytes.empty())
|
||||
{
|
||||
AZStd::shared_ptr < AzFramework::AssetRegistry> prevRegistry;
|
||||
AZStd::shared_ptr<AzFramework::AssetRegistry> prevRegistry;
|
||||
if (!m_initialized)
|
||||
{
|
||||
// First time initialization may have updates already processed which we want to apply
|
||||
@@ -589,7 +589,6 @@ namespace AzFramework
|
||||
AZ_TracePrintf("AssetCatalog", "Loaded registry containing %u assets.\n", m_registry->m_assetIdToInfo.size());
|
||||
|
||||
// It's currently possible in tools for us to have received updates from AP which were applied before the catalog was ready to load
|
||||
// due to CryPak and CrySystem coming online later than our components
|
||||
if (!m_initialized)
|
||||
{
|
||||
ApplyDeltaCatalog(prevRegistry);
|
||||
@@ -611,12 +610,13 @@ namespace AzFramework
|
||||
// the mutex. If the listener tries to perform a blocking asset load via GetAsset() / BlockUntilLoadComplete(), the spawned asset
|
||||
// thread will make a call to the AssetCatalogRequestBus and block on the held mutex. This would cause a deadlock, since the listener
|
||||
// won't free the mutex until the load is complete.
|
||||
// So instead, queue the notification until the next tick, so that it doesn't occur within the AssetCatalogRequestBus mutex, and also
|
||||
// So instead, queue the notification until after the AssetCatalogRequestBus mutex is unlocked for the current thread, and also
|
||||
// so that the entire AssetCatalog initialization is complete.
|
||||
AZ::TickBus::QueueFunction([catalogRegistryString = AZStd::string(catalogRegistryFile)]()
|
||||
{
|
||||
AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
|
||||
});
|
||||
auto OnCatalogLoaded = [catalogRegistryString = AZStd::string(catalogRegistryFile)]()
|
||||
{
|
||||
AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
|
||||
};
|
||||
AZ::Data::AssetCatalogRequestBus::QueueFunction(AZStd::move(OnCatalogLoaded));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -978,6 +978,7 @@ namespace AzFramework
|
||||
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_registryMutex);
|
||||
|
||||
m_registry->Clear();
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace AzFramework
|
||||
//=========================================================================
|
||||
void AssetRegistry::Clear()
|
||||
{
|
||||
m_assetDependencies = {};
|
||||
m_assetIdToInfo = AssetIdToInfoMap();
|
||||
m_assetPathToId = AssetPathToIdMap();
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/string/wildcard.h>
|
||||
#include <AzCore/std/string/regex.h>
|
||||
#include <AzCore/std/string/conversions.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/XML/rapidxml.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzFramework/Asset/FileTagAsset.h>
|
||||
@@ -89,19 +89,19 @@ namespace AzFramework
|
||||
bool FileTagManager::Save(FileTagType fileTagType, const AZStd::string& destinationFilePath = AZStd::string())
|
||||
{
|
||||
AzFramework::FileTag::FileTagAsset* fileTagAsset = GetFileTagAsset(fileTagType);
|
||||
AZStd::string filePathToSave = destinationFilePath;
|
||||
AZ::IO::Path filePathToSave = destinationFilePath;
|
||||
if (filePathToSave.empty())
|
||||
{
|
||||
filePathToSave = FileTagQueryManager::GetDefaultFileTagFilePath(fileTagType);
|
||||
}
|
||||
|
||||
if (!AzFramework::StringFunc::EndsWith(filePathToSave, AzFramework::FileTag::FileTagAsset::Extension()))
|
||||
if (!filePathToSave.Extension().Native().ends_with(AzFramework::FileTag::FileTagAsset::Extension()))
|
||||
{
|
||||
AZ_Error("FileTag", false, "Unable to save tag file (%s). Invalid file extension, file tag can only have (%s) extension.\n", filePathToSave.c_str(), AzFramework::FileTag::FileTagAsset::Extension());
|
||||
return false;
|
||||
}
|
||||
|
||||
return AZ::Utils::SaveObjectToFile(filePathToSave, AZ::DataStream::StreamType::ST_XML, fileTagAsset);
|
||||
return AZ::Utils::SaveObjectToFile(filePathToSave.Native(), AZ::DataStream::StreamType::ST_XML, fileTagAsset);
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> FileTagManager::AddTagsInternal(AZStd::string filePath, FileTagType fileTagType, AZStd::vector<AZStd::string> fileTags, AzFramework::FileTag::FilePatternType filePatternType)
|
||||
@@ -239,17 +239,22 @@ namespace AzFramework
|
||||
QueryFileTagsEventBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
AZStd::string FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType)
|
||||
AZ::IO::Path FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType)
|
||||
{
|
||||
auto destinationFilePath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / EngineAssetSourceRelPath;
|
||||
AZ::IO::Path destinationFilePath;
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
settingsRegistry->Get(destinationFilePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
||||
}
|
||||
destinationFilePath /= EngineAssetSourceRelPath;
|
||||
destinationFilePath /= fileTagType == FileTagType::Exclude ? ExcludeFileName : IncludeFileName;
|
||||
destinationFilePath.ReplaceExtension(AzFramework::FileTag::FileTagAsset::Extension());
|
||||
return destinationFilePath.String();
|
||||
return destinationFilePath;
|
||||
}
|
||||
|
||||
bool FileTagQueryManager::Load(const AZStd::string& filePath)
|
||||
{
|
||||
AZStd::string fileToLoad = filePath;
|
||||
AZ::IO::Path fileToLoad = filePath;
|
||||
if (fileToLoad.empty())
|
||||
{
|
||||
fileToLoad = GetDefaultFileTagFilePath(m_fileTagType);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/std/containers/map.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzFramework/FileTag/FileTagBus.h>
|
||||
|
||||
namespace AzFramework
|
||||
@@ -88,7 +89,7 @@ namespace AzFramework
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AZStd::string GetDefaultFileTagFilePath(FileTagType fileTagType);
|
||||
static AZ::IO::Path GetDefaultFileTagFilePath(FileTagType fileTagType);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <AzCore/std/string/wildcard.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/XML/rapidxml.h>
|
||||
|
||||
namespace AzFramework
|
||||
@@ -66,7 +67,8 @@ namespace AzFramework
|
||||
m_excludeFileQueryManager.reset(aznew FileTagQueryManager(FileTagType::Exclude));
|
||||
if (!m_excludeFileQueryManager.get()->Load())
|
||||
{
|
||||
AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n", FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str());
|
||||
AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n",
|
||||
FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str());
|
||||
}
|
||||
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace AzFramework
|
||||
class IMatchmakingAsyncRequests
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ISessionAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}");
|
||||
AZ_RTTI(IMatchmakingAsyncRequests, "{53513480-2D02-493C-B44E-96AA27F42429}");
|
||||
|
||||
IMatchmakingAsyncRequests() = default;
|
||||
virtual ~IMatchmakingAsyncRequests() = default;
|
||||
@@ -60,4 +60,31 @@ namespace AzFramework
|
||||
// @param stopMatchmakingRequest The request of StopMatchmaking operation
|
||||
virtual void StopMatchmakingAsync(const StopMatchmakingRequest& stopMatchmakingRequest) = 0;
|
||||
};
|
||||
|
||||
//! MatchmakingAsyncRequestNotifications
|
||||
//! The notifications correspond to matchmaking async requests
|
||||
class MatchmakingAsyncRequestNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
// Safeguard handler for multi-threaded use case
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
|
||||
virtual void OnAcceptMatchAsyncComplete() = 0;
|
||||
|
||||
// OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
|
||||
// @param matchmakingTicketId The unique identifier for the matchmaking ticket
|
||||
virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0;
|
||||
|
||||
// OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
|
||||
virtual void OnStopMatchmakingAsyncComplete() = 0;
|
||||
};
|
||||
using MatchmakingAsyncRequestNotificationBus = AZ::EBus<MatchmakingAsyncRequestNotifications>;
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -13,36 +13,10 @@
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
//! MatchmakingAsyncRequestNotifications
|
||||
//! The notifications correspond to matchmaking async requests
|
||||
class MatchmakingAsyncRequestNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
// Safeguard handler for multi-threaded use case
|
||||
using MutexType = AZStd::recursive_mutex;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OnAcceptMatchAsyncComplete is fired once AcceptMatchAsync completes
|
||||
virtual void OnAcceptMatchAsyncComplete() = 0;
|
||||
|
||||
// OnStartMatchmakingAsyncComplete is fired once StartMatchmakingAsync completes
|
||||
// @param matchmakingTicketId The unique identifier for the matchmaking ticket
|
||||
virtual void OnStartMatchmakingAsyncComplete(const AZStd::string& matchmakingTicketId) = 0;
|
||||
|
||||
// OnStopMatchmakingAsyncComplete is fired once StopMatchmakingAsync completes
|
||||
virtual void OnStopMatchmakingAsyncComplete() = 0;
|
||||
};
|
||||
using MatchmakingAsyncRequestNotificationBus = AZ::EBus<MatchmakingAsyncRequestNotifications>;
|
||||
|
||||
//! MatchmakingNotifications
|
||||
//! The matchmaking notifications to listen for performing required operations
|
||||
class MatchAcceptanceNotifications
|
||||
//! based on matchmaking ticket event
|
||||
class MatchmakingNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
@@ -55,8 +29,18 @@ namespace AzFramework
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OnMatchAcceptance is fired when DescribeMatchmaking ticket status is REQUIRES_ACCEPTANCE
|
||||
// OnMatchAcceptance is fired when match is found and pending on acceptance
|
||||
// Use this notification to accept found match
|
||||
virtual void OnMatchAcceptance() = 0;
|
||||
|
||||
// OnMatchComplete is fired when match is complete
|
||||
virtual void OnMatchComplete() = 0;
|
||||
|
||||
// OnMatchError is fired when match is processed with error
|
||||
virtual void OnMatchError() = 0;
|
||||
|
||||
// OnMatchFailure is fired when match is failed to complete
|
||||
virtual void OnMatchFailure() = 0;
|
||||
};
|
||||
using MatchAcceptanceNotificationBus = AZ::EBus<MatchAcceptanceNotifications>;
|
||||
using MatchmakingNotificationBus = AZ::EBus<MatchmakingNotifications>;
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Component/ComponentBus.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzFramework/Physics/Material.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
//! The QuadMeshType specifies the property of the heightfield quad.
|
||||
enum class QuadMeshType : uint8_t
|
||||
{
|
||||
SubdivideUpperLeftToBottomRight, //!< Subdivide the quad, from upper left to bottom right |\|, into two triangles.
|
||||
SubdivideBottomLeftToUpperRight, //!< Subdivide the quad, from bottom left to upper right |/|, into two triangles.
|
||||
Hole //!< The quad should be treated as a hole in the heightfield.
|
||||
};
|
||||
|
||||
struct HeightMaterialPoint
|
||||
{
|
||||
float m_height{ 0.0f }; //!< Holds the height of this point in the heightfield relative to the heightfield entity location.
|
||||
QuadMeshType m_quadMeshType{ QuadMeshType::SubdivideUpperLeftToBottomRight }; //!< By default, create two triangles like this |\|, where this point is in the upper left corner.
|
||||
uint8_t m_materialIndex{ 0 }; //!< The surface material index for the upper left corner of this quad.
|
||||
uint16_t m_padding{ 0 }; //!< available for future use.
|
||||
};
|
||||
|
||||
//! An interface to provide heightfield values.
|
||||
class HeightfieldProviderRequests
|
||||
: public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
//! Returns the distance between each height in the map.
|
||||
//! @return Vector containing Column Spacing, Rows Spacing.
|
||||
virtual AZ::Vector2 GetHeightfieldGridSpacing() const = 0;
|
||||
|
||||
//! Returns the height field gridsize.
|
||||
//! @param numColumns contains the size of the grid in the x direction.
|
||||
//! @param numRows contains the size of the grid in the y direction.
|
||||
virtual void GetHeightfieldGridSize(int32_t& numColumns, int32_t& numRows) const = 0;
|
||||
|
||||
//! Returns the height field min and max height bounds.
|
||||
//! @param minHeightBounds contains the minimum height that the heightfield can contain.
|
||||
//! @param maxHeightBounds contains the maximum height that the heightfield can contain.
|
||||
virtual void GetHeightfieldHeightBounds(float& minHeightBounds, float& maxHeightBounds) const = 0;
|
||||
|
||||
//! Returns the AABB of the heightfield.
|
||||
//! This is provided separately from the shape AABB because the heightfield might choose to modify the AABB bounds.
|
||||
//! @return AABB of the heightfield.
|
||||
virtual AZ::Aabb GetHeightfieldAabb() const = 0;
|
||||
|
||||
//! Returns the world transform for the heightfield.
|
||||
//! This is provided separately from the entity transform because the heightfield might want to clear out the rotation or scale.
|
||||
//! @return world transform that should be used with the heightfield data.
|
||||
virtual AZ::Transform GetHeightfieldTransform() const = 0;
|
||||
|
||||
//! Returns the list of materials used by the height field.
|
||||
//! @return returns a vector of all materials.
|
||||
virtual AZStd::vector<MaterialId> GetMaterialList() const = 0;
|
||||
|
||||
//! Returns the list of heights used by the height field.
|
||||
//! @return the rows*columns vector of the heights.
|
||||
virtual AZStd::vector<float> GetHeights() const = 0;
|
||||
|
||||
//! Returns the list of heights and materials used by the height field.
|
||||
//! @return the rows*columns vector of the heights and materials.
|
||||
virtual AZStd::vector<Physics::HeightMaterialPoint> GetHeightsAndMaterials() const = 0;
|
||||
};
|
||||
|
||||
using HeightfieldProviderRequestsBus = AZ::EBus<HeightfieldProviderRequests>;
|
||||
|
||||
//! Broadcasts notifications when heightfield data changes - heightfield providers implement HeightfieldRequests bus.
|
||||
class HeightfieldProviderNotifications
|
||||
: public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
|
||||
//! Called whenever the heightfield data changes.
|
||||
//! @param the AABB of the area of data that changed.
|
||||
virtual void OnHeightfieldDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
~HeightfieldProviderNotifications() = default;
|
||||
};
|
||||
|
||||
using HeightfieldProviderNotificationBus = AZ::EBus<HeightfieldProviderNotifications>;
|
||||
} // namespace Physics
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 <AzFramework/Physics/HeightfieldProviderBus.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class MockHeightfieldProviderNotificationBusListener
|
||||
: private Physics::HeightfieldProviderNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
MockHeightfieldProviderNotificationBusListener(AZ::EntityId entityid)
|
||||
{
|
||||
Physics::HeightfieldProviderNotificationBus::Handler::BusConnect(entityid);
|
||||
}
|
||||
|
||||
~MockHeightfieldProviderNotificationBusListener()
|
||||
{
|
||||
Physics::HeightfieldProviderNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_METHOD1(OnHeightfieldDataChanged, void(const AZ::Aabb&));
|
||||
};
|
||||
} // namespace UnitTest
|
||||
@@ -37,6 +37,7 @@ namespace Physics
|
||||
REFLECT_SHAPETYPE_ENUM_VALUE(Sphere);
|
||||
REFLECT_SHAPETYPE_ENUM_VALUE(Cylinder);
|
||||
REFLECT_SHAPETYPE_ENUM_VALUE(PhysicsAsset);
|
||||
REFLECT_SHAPETYPE_ENUM_VALUE(Heightfield);
|
||||
|
||||
#undef REFLECT_SHAPETYPE_ENUM_VALUE
|
||||
}
|
||||
@@ -285,12 +286,17 @@ namespace Physics
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void* CookedMeshShapeConfiguration::GetCachedNativeMesh() const
|
||||
const void* CookedMeshShapeConfiguration::GetCachedNativeMesh() const
|
||||
{
|
||||
return m_cachedNativeMesh;
|
||||
}
|
||||
|
||||
void CookedMeshShapeConfiguration::SetCachedNativeMesh(void* cachedNativeMesh) const
|
||||
void* CookedMeshShapeConfiguration::GetCachedNativeMesh()
|
||||
{
|
||||
return m_cachedNativeMesh;
|
||||
}
|
||||
|
||||
void CookedMeshShapeConfiguration::SetCachedNativeMesh(void* cachedNativeMesh)
|
||||
{
|
||||
m_cachedNativeMesh = cachedNativeMesh;
|
||||
}
|
||||
@@ -305,4 +311,131 @@ namespace Physics
|
||||
m_cachedNativeMesh = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldShapeConfiguration::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext
|
||||
->RegisterGenericType<AZStd::shared_ptr<HeightfieldShapeConfiguration>>();
|
||||
|
||||
serializeContext->Class<HeightfieldShapeConfiguration, ShapeConfiguration>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
HeightfieldShapeConfiguration::~HeightfieldShapeConfiguration()
|
||||
{
|
||||
SetCachedNativeHeightfield(nullptr);
|
||||
}
|
||||
|
||||
HeightfieldShapeConfiguration::HeightfieldShapeConfiguration(const HeightfieldShapeConfiguration& other)
|
||||
: ShapeConfiguration(other)
|
||||
, m_gridResolution(other.m_gridResolution)
|
||||
, m_numColumns(other.m_numColumns)
|
||||
, m_numRows(other.m_numRows)
|
||||
, m_samples(other.m_samples)
|
||||
, m_minHeightBounds(other.m_minHeightBounds)
|
||||
, m_maxHeightBounds(other.m_maxHeightBounds)
|
||||
, m_cachedNativeHeightfield(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
HeightfieldShapeConfiguration& HeightfieldShapeConfiguration::operator=(const HeightfieldShapeConfiguration& other)
|
||||
{
|
||||
ShapeConfiguration::operator=(other);
|
||||
|
||||
m_gridResolution = other.m_gridResolution;
|
||||
m_numColumns = other.m_numColumns;
|
||||
m_numRows = other.m_numRows;
|
||||
m_samples = other.m_samples;
|
||||
m_minHeightBounds = other.m_minHeightBounds;
|
||||
m_maxHeightBounds = other.m_maxHeightBounds;
|
||||
|
||||
// Prevent raw pointer from being copied
|
||||
m_cachedNativeHeightfield = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const void* HeightfieldShapeConfiguration::GetCachedNativeHeightfield() const
|
||||
{
|
||||
return m_cachedNativeHeightfield;
|
||||
}
|
||||
|
||||
void* HeightfieldShapeConfiguration::GetCachedNativeHeightfield()
|
||||
{
|
||||
return m_cachedNativeHeightfield;
|
||||
}
|
||||
|
||||
void HeightfieldShapeConfiguration::SetCachedNativeHeightfield(void* cachedNativeHeightfield)
|
||||
{
|
||||
if (m_cachedNativeHeightfield)
|
||||
{
|
||||
Physics::SystemRequestBus::Broadcast(&Physics::SystemRequests::ReleaseNativeHeightfieldObject, m_cachedNativeHeightfield);
|
||||
}
|
||||
|
||||
m_cachedNativeHeightfield = cachedNativeHeightfield;
|
||||
}
|
||||
|
||||
AZ::Vector2 HeightfieldShapeConfiguration::GetGridResolution() const
|
||||
{
|
||||
return m_gridResolution;
|
||||
}
|
||||
|
||||
void HeightfieldShapeConfiguration::SetGridResolution(const AZ::Vector2& gridResolution)
|
||||
{
|
||||
m_gridResolution = gridResolution;
|
||||
}
|
||||
|
||||
int32_t HeightfieldShapeConfiguration::GetNumColumns() const
|
||||
{
|
||||
return m_numColumns;
|
||||
}
|
||||
|
||||
void HeightfieldShapeConfiguration::SetNumColumns(int32_t numColumns)
|
||||
{
|
||||
m_numColumns = numColumns;
|
||||
}
|
||||
|
||||
int32_t HeightfieldShapeConfiguration::GetNumRows() const
|
||||
{
|
||||
return m_numRows;
|
||||
}
|
||||
|
||||
void HeightfieldShapeConfiguration::SetNumRows(int32_t numRows)
|
||||
{
|
||||
m_numRows = numRows;
|
||||
}
|
||||
|
||||
const AZStd::vector<Physics::HeightMaterialPoint>& HeightfieldShapeConfiguration::GetSamples() const
|
||||
{
|
||||
return m_samples;
|
||||
}
|
||||
|
||||
void HeightfieldShapeConfiguration::SetSamples(const AZStd::vector<Physics::HeightMaterialPoint>& samples)
|
||||
{
|
||||
m_samples = samples;
|
||||
}
|
||||
|
||||
float HeightfieldShapeConfiguration::GetMinHeightBounds() const
|
||||
{
|
||||
return m_minHeightBounds;
|
||||
}
|
||||
|
||||
void HeightfieldShapeConfiguration::SetMinHeightBounds(float minBounds)
|
||||
{
|
||||
m_minHeightBounds = minBounds;
|
||||
}
|
||||
|
||||
float HeightfieldShapeConfiguration::GetMaxHeightBounds() const
|
||||
{
|
||||
return m_maxHeightBounds;
|
||||
}
|
||||
|
||||
void HeightfieldShapeConfiguration::SetMaxHeightBounds(float maxBounds)
|
||||
{
|
||||
m_maxHeightBounds = maxBounds;
|
||||
}
|
||||
} // namespace Physics
|
||||
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Physics/HeightfieldProviderBus.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
/// Used to identify shape configuration type from base class.
|
||||
@@ -27,6 +30,7 @@ namespace Physics
|
||||
Native, ///< Native shape configuration if user wishes to bypass generic shape configurations.
|
||||
PhysicsAsset, ///< Shapes configured in the asset.
|
||||
CookedMesh, ///< Stores a blob of mesh data cooked for the specific engine.
|
||||
Heightfield ///< Interacts with the physics system heightfield
|
||||
};
|
||||
|
||||
class ShapeConfiguration
|
||||
@@ -183,8 +187,9 @@ namespace Physics
|
||||
|
||||
MeshType GetMeshType() const;
|
||||
|
||||
void* GetCachedNativeMesh() const;
|
||||
void SetCachedNativeMesh(void* cachedNativeMesh) const;
|
||||
void* GetCachedNativeMesh();
|
||||
const void* GetCachedNativeMesh() const;
|
||||
void SetCachedNativeMesh(void* cachedNativeMesh);
|
||||
|
||||
private:
|
||||
void ReleaseCachedNativeMesh();
|
||||
@@ -193,7 +198,56 @@ namespace Physics
|
||||
MeshType m_type = MeshType::TriangleMesh;
|
||||
|
||||
//! Cached native mesh object (e.g. PxConvexMesh or PxTriangleMesh). This data is not serialized.
|
||||
mutable void* m_cachedNativeMesh = nullptr;
|
||||
void* m_cachedNativeMesh = nullptr;
|
||||
};
|
||||
|
||||
class HeightfieldShapeConfiguration
|
||||
: public ShapeConfiguration
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(HeightfieldShapeConfiguration, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(HeightfieldShapeConfiguration, "{8DF47C83-D2A9-4E7C-8620-5E173E43C0B3}", ShapeConfiguration);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
HeightfieldShapeConfiguration() = default;
|
||||
HeightfieldShapeConfiguration(const HeightfieldShapeConfiguration&);
|
||||
HeightfieldShapeConfiguration& operator=(const HeightfieldShapeConfiguration&);
|
||||
~HeightfieldShapeConfiguration();
|
||||
|
||||
ShapeType GetShapeType() const override
|
||||
{
|
||||
return ShapeType::Heightfield;
|
||||
}
|
||||
|
||||
const void* GetCachedNativeHeightfield() const;
|
||||
void* GetCachedNativeHeightfield();
|
||||
void SetCachedNativeHeightfield(void* cachedNativeHeightfield);
|
||||
AZ::Vector2 GetGridResolution() const;
|
||||
void SetGridResolution(const AZ::Vector2& gridSpacing);
|
||||
int32_t GetNumColumns() const;
|
||||
void SetNumColumns(int32_t numColumns);
|
||||
int32_t GetNumRows() const;
|
||||
void SetNumRows(int32_t numRows);
|
||||
const AZStd::vector<Physics::HeightMaterialPoint>& GetSamples() const;
|
||||
void SetSamples(const AZStd::vector<Physics::HeightMaterialPoint>& samples);
|
||||
float GetMinHeightBounds() const;
|
||||
void SetMinHeightBounds(float minBounds);
|
||||
float GetMaxHeightBounds() const;
|
||||
void SetMaxHeightBounds(float maxBounds);
|
||||
|
||||
private:
|
||||
//! The number of meters between each heightfield sample.
|
||||
AZ::Vector2 m_gridResolution{ 1.0f };
|
||||
//! The number of columns in the heightfield sample grid.
|
||||
int32_t m_numColumns{ 0 };
|
||||
//! The number of rows in the heightfield sample grid.
|
||||
int32_t m_numRows{ 0 };
|
||||
//! The minimum and maximum heights that can be used by this heightfield.
|
||||
//! This can be used by the physics system to choose a more optimal heightfield data type internally (ex: int16, uint8)
|
||||
float m_minHeightBounds{AZStd::numeric_limits<float>::lowest()};
|
||||
float m_maxHeightBounds{AZStd::numeric_limits<float>::max()};
|
||||
//! The grid of sample points for the heightfield.
|
||||
AZStd::vector<Physics::HeightMaterialPoint> m_samples;
|
||||
//! An optional storage pointer for the physics system to cache its native heightfield representation.
|
||||
void* m_cachedNativeHeightfield{ nullptr };
|
||||
};
|
||||
} // namespace Physics
|
||||
|
||||
@@ -132,6 +132,10 @@ namespace Physics
|
||||
|
||||
virtual AZStd::shared_ptr<Material> CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) = 0;
|
||||
|
||||
/// Releases the height field object created by the physics backend.
|
||||
/// @param nativeHeightfieldObject Pointer to the height field object.
|
||||
virtual void ReleaseNativeHeightfieldObject(void* nativeHeightfieldObject) = 0;
|
||||
|
||||
/// Releases the mesh object created by the physics backend.
|
||||
/// @param nativeMeshObject Pointer to the mesh object.
|
||||
virtual void ReleaseNativeMeshObject(void* nativeMeshObject) = 0;
|
||||
|
||||
@@ -107,6 +107,7 @@ namespace Physics
|
||||
PhysicsAssetShapeConfiguration::Reflect(context);
|
||||
NativeShapeConfiguration::Reflect(context);
|
||||
CookedMeshShapeConfiguration::Reflect(context);
|
||||
HeightfieldShapeConfiguration::Reflect(context);
|
||||
AzPhysics::SystemInterface::Reflect(context);
|
||||
AzPhysics::Scene::Reflect(context);
|
||||
AzPhysics::CollisionLayer::Reflect(context);
|
||||
|
||||
@@ -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/MockHeightfieldProviderBus.h
|
||||
)
|
||||
@@ -22,7 +22,7 @@ namespace AzFramework
|
||||
AZStd::scoped_ptr<ProcessWatcher> pWatcher(LaunchProcess(processLaunchInfo, communicationType));
|
||||
if (!pWatcher)
|
||||
{
|
||||
AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: Unable to launch process '%s %s'", processLaunchInfo.m_processExecutableString.c_str(), processLaunchInfo.m_commandlineParameters.c_str());
|
||||
AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: Unable to launch process '%s %s'\n", processLaunchInfo.m_processExecutableString.c_str(), processLaunchInfo.m_commandlineParameters.c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -31,7 +31,7 @@ namespace AzFramework
|
||||
ProcessCommunicator* pCommunicator = pWatcher->GetCommunicator();
|
||||
if (!pCommunicator || !pCommunicator->IsValid())
|
||||
{
|
||||
AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: No communicator for watcher's process (%s %s)!", processLaunchInfo.m_processExecutableString.c_str(), processLaunchInfo.m_commandlineParameters.c_str());
|
||||
AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: No communicator for watcher's process (%s %s)!\n", processLaunchInfo.m_processExecutableString.c_str(), processLaunchInfo.m_commandlineParameters.c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 <AzFramework/SurfaceData/SurfaceData.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
namespace AzFramework::SurfaceData
|
||||
{
|
||||
void SurfaceTagWeight::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<SurfaceTagWeight>()
|
||||
->Field("m_surfaceType", &SurfaceTagWeight::m_surfaceType)
|
||||
->Field("m_weight", &SurfaceTagWeight::m_weight)
|
||||
;
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<SurfaceTagWeight>()
|
||||
->Attribute(AZ::Script::Attributes::Category, "SurfaceData")
|
||||
->Constructor()
|
||||
->Property("surfaceType", BehaviorValueProperty(&SurfaceTagWeight::m_surfaceType))
|
||||
->Property("weight", BehaviorValueProperty(&SurfaceTagWeight::m_weight))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void SurfacePoint::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<SurfacePoint>()
|
||||
->Field("m_position", &SurfacePoint::m_position)
|
||||
->Field("m_normal", &SurfacePoint::m_normal)
|
||||
->Field("m_surfaceTags", &SurfacePoint::m_surfaceTags)
|
||||
;
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<SurfacePoint>("AzFramework::SurfaceData::SurfacePoint")
|
||||
->Attribute(AZ::Script::Attributes::Category, "SurfaceData")
|
||||
->Constructor()
|
||||
->Property("position", BehaviorValueProperty(&SurfacePoint::m_position))
|
||||
->Property("normal", BehaviorValueProperty(&SurfacePoint::m_normal))
|
||||
->Property("surfaceTags", BehaviorValueProperty(&SurfacePoint::m_surfaceTags))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AzFramework::SurfaceData
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Crc.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
namespace AzFramework::SurfaceData
|
||||
{
|
||||
namespace Constants
|
||||
{
|
||||
static constexpr const char* s_unassignedTagName = "(unassigned)";
|
||||
}
|
||||
|
||||
struct SurfaceTagWeight
|
||||
{
|
||||
AZ_TYPE_INFO(SurfaceTagWeight, "{EA14018E-E853-4BF5-8E13-D83BB99A54CC}");
|
||||
SurfaceTagWeight() = default;
|
||||
SurfaceTagWeight(AZ::Crc32 surfaceType, float weight)
|
||||
: m_surfaceType(surfaceType)
|
||||
, m_weight(weight)
|
||||
{
|
||||
}
|
||||
|
||||
AZ::Crc32 m_surfaceType = AZ::Crc32(Constants::s_unassignedTagName);
|
||||
float m_weight = 0.0f; //! A Value in the range [0.0f .. 1.0f]
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
};
|
||||
|
||||
struct SurfaceTagWeightComparator
|
||||
{
|
||||
bool operator()(const SurfaceTagWeight& tagWeight1, const SurfaceTagWeight& tagWeight2) const
|
||||
{
|
||||
// Return a deterministic sort order for surface tags from highest to lowest weight, with the surface types sorted
|
||||
// in a predictable order when the weights are equal. The surface type sort order is meaningless since it is sorting CRC
|
||||
// values, it's really just important for it to be stable.
|
||||
// For the floating-point weight comparisons we use exact instead of IsClose value comparisons for a similar reason - we
|
||||
// care about being sorted highest to lowest, but there's no inherent meaning in sorting surface types with *similar* weights
|
||||
// together.
|
||||
|
||||
if (tagWeight1.m_weight != tagWeight2.m_weight)
|
||||
{
|
||||
return tagWeight1.m_weight > tagWeight2.m_weight;
|
||||
}
|
||||
else
|
||||
{
|
||||
return tagWeight1.m_surfaceType > tagWeight2.m_surfaceType;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using SurfaceTagWeightList = AZStd::vector<SurfaceTagWeight>;
|
||||
|
||||
struct SurfacePoint final
|
||||
{
|
||||
AZ_TYPE_INFO(SurfacePoint, "{331A3D0E-BB1D-47BF-96A2-249FAA0D720D}");
|
||||
|
||||
AZ::Vector3 m_position;
|
||||
AZ::Vector3 m_normal;
|
||||
SurfaceTagWeightList m_surfaceTags;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
};
|
||||
} // namespace AzFramework::SurfaceData
|
||||
@@ -8,56 +8,33 @@
|
||||
|
||||
#include "TerrainDataRequestBus.h"
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
namespace AzFramework
|
||||
namespace AzFramework::Terrain
|
||||
{
|
||||
namespace SurfaceData
|
||||
void TerrainDataRequests::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
void SurfaceTagWeight::Reflect(AZ::ReflectContext* context)
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<SurfaceTagWeight>()
|
||||
->Field("m_surfaceType", &SurfaceTagWeight::m_surfaceType)
|
||||
->Field("m_weight", &SurfaceTagWeight::m_weight)
|
||||
;
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->Class<SurfaceTagWeight>("SurfaceTagWeight")
|
||||
->Property("m_surfaceType", BehaviorValueProperty(&SurfaceTagWeight::m_surfaceType))
|
||||
->Property("m_weight", BehaviorValueProperty(&SurfaceTagWeight::m_weight))
|
||||
;
|
||||
}
|
||||
}
|
||||
} //namespace SurfaceData
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
void TerrainDataRequests::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AzFramework::SurfaceData::SurfaceTagWeight::Reflect(context);
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->EBus<AzFramework::Terrain::TerrainDataRequestBus>("TerrainDataRequestBus")
|
||||
->Attribute(AZ::Script::Attributes::Category, "Terrain")
|
||||
->Event("GetHeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeight)
|
||||
->Event("GetHeightFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeightFromFloats)
|
||||
->Event("GetMaxSurfaceWeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeight)
|
||||
->Event("GetMaxSurfaceWeightFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeightFromFloats)
|
||||
->Event("GetIsHoleFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetIsHoleFromFloats)
|
||||
->Event("GetNormal", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetNormal)
|
||||
->Event("GetNormalFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetNormalFromFloats)
|
||||
->Event("GetTerrainAabb", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainAabb)
|
||||
->Event("GetTerrainHeightQueryResolution",
|
||||
&AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainHeightQueryResolution)
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
behaviorContext->EBus<AzFramework::Terrain::TerrainDataRequestBus>("TerrainDataRequestBus")
|
||||
->Attribute(AZ::Script::Attributes::Category, "Terrain")
|
||||
->Event("GetHeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetHeight)
|
||||
->Event("GetNormal", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetNormal)
|
||||
->Event("GetMaxSurfaceWeight", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeight)
|
||||
->Event("GetMaxSurfaceWeightFromVector2",
|
||||
&AzFramework::Terrain::TerrainDataRequestBus::Events::GetMaxSurfaceWeightFromVector2)
|
||||
->Event("GetSurfaceWeights", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfaceWeights)
|
||||
->Event("GetSurfaceWeightsFromVector2",
|
||||
&AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfaceWeightsFromVector2)
|
||||
->Event("GetIsHoleFromFloats", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetIsHoleFromFloats)
|
||||
->Event("GetSurfacePoint", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfacePoint)
|
||||
->Event("GetSurfacePointFromVector2",
|
||||
&AzFramework::Terrain::TerrainDataRequestBus::Events::GetSurfacePointFromVector2)
|
||||
->Event("GetTerrainAabb", &AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainAabb)
|
||||
->Event("GetTerrainHeightQueryResolution",
|
||||
&AzFramework::Terrain::TerrainDataRequestBus::Events::GetTerrainHeightQueryResolution)
|
||||
;
|
||||
}
|
||||
|
||||
} //namespace Terrain
|
||||
} // namespace AzFramework
|
||||
}
|
||||
} // namespace AzFramework::Terrain
|
||||
|
||||
@@ -8,50 +8,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzFramework/SurfaceData/SurfaceData.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
namespace SurfaceData
|
||||
{
|
||||
namespace Constants
|
||||
{
|
||||
static const char* s_unassignedTagName = "(unassigned)";
|
||||
}
|
||||
|
||||
struct SurfaceTagWeight
|
||||
{
|
||||
AZ_TYPE_INFO(SurfaceTagWeight, "{EA14018E-E853-4BF5-8E13-D83BB99A54CC}");
|
||||
|
||||
AZ::Crc32 m_surfaceType = AZ::Crc32(Constants::s_unassignedTagName);
|
||||
float m_weight = 0.0f; //! A Value in the range [0.0f .. 1.0f]
|
||||
|
||||
//! Don't call this directly. TerrainDataRequests::Reflect is doing it already.
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
};
|
||||
|
||||
struct SurfaceTagWeightComparator
|
||||
{
|
||||
bool operator()(const SurfaceTagWeight& tagWeight1, const SurfaceTagWeight& tagWeight2) const
|
||||
{
|
||||
if (!AZ::IsClose(tagWeight1.m_weight, tagWeight2.m_weight))
|
||||
{
|
||||
return tagWeight1.m_weight > tagWeight2.m_weight;
|
||||
}
|
||||
else
|
||||
{
|
||||
return tagWeight1.m_surfaceType > tagWeight2.m_surfaceType;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using OrderedSurfaceTagWeightSet = AZStd::set<SurfaceTagWeight, SurfaceTagWeightComparator>;
|
||||
} //namespace SurfaceData
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
|
||||
@@ -91,49 +54,82 @@ namespace AzFramework
|
||||
//! Returns terrains height in meters at location x,y.
|
||||
//! @terrainExistsPtr: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain HOLE then *terrainExistsPtr will become false,
|
||||
//! otherwise *terrainExistsPtr will become true.
|
||||
virtual float GetHeight(AZ::Vector3 position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual float GetHeightFromFloats(float x, float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual float GetHeight(const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual float GetHeightFromVector2(
|
||||
const AZ::Vector2& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual float GetHeightFromFloats(
|
||||
float x, float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
|
||||
//! Returns true if there's a hole at location x,y.
|
||||
//! Also returns true if there's no terrain data at location x,y.
|
||||
virtual bool GetIsHole(const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR) const = 0;
|
||||
virtual bool GetIsHoleFromVector2(const AZ::Vector2& position, Sampler sampleFilter = Sampler::BILINEAR) const = 0;
|
||||
virtual bool GetIsHoleFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR) const = 0;
|
||||
|
||||
// Given an XY coordinate, return the surface normal.
|
||||
//! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a
|
||||
//! terrain HOLE then *terrainExistsPtr will be set to false,
|
||||
//! otherwise *terrainExistsPtr will be set to true.
|
||||
virtual AZ::Vector3 GetNormal(
|
||||
const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual AZ::Vector3 GetNormalFromVector2(
|
||||
const AZ::Vector2& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual AZ::Vector3 GetNormalFromFloats(
|
||||
float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
|
||||
//! Given an XY coordinate, return the max surface type and weight.
|
||||
//! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain HOLE then *terrainExistsPtr will be set to false,
|
||||
//! otherwise *terrainExistsPtr will be set to true.
|
||||
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight(AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2(const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight(
|
||||
const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2(
|
||||
const AZ::Vector2& inPosition, Sampler sampleFilter = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats(
|
||||
float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
|
||||
//! Given an XY coordinate, return the set of surface types and weights. The Vector3 input position version is defined to ignore
|
||||
//! the input Z value.
|
||||
virtual void GetSurfaceWeights(
|
||||
const AZ::Vector3& inPosition,
|
||||
SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights,
|
||||
SurfaceData::SurfaceTagWeightList& outSurfaceWeights,
|
||||
Sampler sampleFilter = Sampler::DEFAULT,
|
||||
bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual void GetSurfaceWeightsFromVector2(
|
||||
const AZ::Vector2& inPosition,
|
||||
SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights,
|
||||
SurfaceData::SurfaceTagWeightList& outSurfaceWeights,
|
||||
Sampler sampleFilter = Sampler::DEFAULT,
|
||||
bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual void GetSurfaceWeightsFromFloats(
|
||||
float x,
|
||||
float y,
|
||||
SurfaceData::OrderedSurfaceTagWeightSet& outSurfaceWeights,
|
||||
SurfaceData::SurfaceTagWeightList& outSurfaceWeights,
|
||||
Sampler sampleFilter = Sampler::DEFAULT,
|
||||
bool* terrainExistsPtr = nullptr) const = 0;
|
||||
|
||||
//! Convenience function for low level systems that can't do a reverse lookup from Crc to string. Everyone else should use GetMaxSurfaceWeight or GetMaxSurfaceWeightFromFloats.
|
||||
//! Not available in the behavior context.
|
||||
//! Returns nullptr if the position is inside a hole or outside of the terrain boundaries.
|
||||
virtual const char * GetMaxSurfaceName(AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual const char* GetMaxSurfaceName(
|
||||
const AZ::Vector3& position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
|
||||
//! Returns true if there's a hole at location x,y.
|
||||
//! Also returns true if there's no terrain data at location x,y.
|
||||
virtual bool GetIsHoleFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR) const = 0;
|
||||
|
||||
// Given an XY coordinate, return the surface normal.
|
||||
//! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain HOLE then *terrainExistsPtr will be set to false,
|
||||
//! otherwise *terrainExistsPtr will be set to true.
|
||||
virtual AZ::Vector3 GetNormal(AZ::Vector3 position, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual AZ::Vector3 GetNormalFromFloats(float x, float y, Sampler sampleFilter = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const = 0;
|
||||
//! Given an XY coordinate, return all terrain information at that location. The Vector3 input position version is defined
|
||||
//! to ignore the input Z value.
|
||||
virtual void GetSurfacePoint(
|
||||
const AZ::Vector3& inPosition,
|
||||
SurfaceData::SurfacePoint& outSurfacePoint,
|
||||
Sampler sampleFilter = Sampler::DEFAULT,
|
||||
bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual void GetSurfacePointFromVector2(
|
||||
const AZ::Vector2& inPosition,
|
||||
SurfaceData::SurfacePoint& outSurfacePoint,
|
||||
Sampler sampleFilter = Sampler::DEFAULT,
|
||||
bool* terrainExistsPtr = nullptr) const = 0;
|
||||
virtual void GetSurfacePointFromFloats(
|
||||
float x,
|
||||
float y,
|
||||
SurfaceData::SurfacePoint& outSurfacePoint,
|
||||
Sampler sampleFilter = Sampler::DEFAULT,
|
||||
bool* terrainExistsPtr = nullptr) const = 0;
|
||||
};
|
||||
using TerrainDataRequestBus = AZ::EBus<TerrainDataRequests>;
|
||||
|
||||
@@ -169,6 +165,10 @@ namespace AzFramework
|
||||
}
|
||||
};
|
||||
using TerrainDataNotificationBus = AZ::EBus<TerrainDataNotifications>;
|
||||
|
||||
} //namespace Terrain
|
||||
} // namespace Terrain
|
||||
} // namespace AzFramework
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_TYPE_INFO_SPECIALIZE(AzFramework::Terrain::TerrainDataRequests::Sampler, "{D29BB6D7-3006-4114-858D-355EAA256B86}");
|
||||
} // namespace AZ
|
||||
|
||||
@@ -14,9 +14,8 @@ namespace AzFramework
|
||||
{
|
||||
AZ_CVAR(bool, bg_octreeUseQuadtree, false, nullptr, AZ::ConsoleFunctorFlags::ReadOnly, "If set to true, the visibility octrees will degenerate to a quadtree split along the X/Y plane");
|
||||
AZ_CVAR(float, bg_octreeMaxWorldExtents, 16384.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum supported world size by the world octreeSystemComponent");
|
||||
AZ_CVAR(uint32_t, bg_octreeNodeMaxEntries, 64, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of entries to allow in any node before forcing a split");
|
||||
AZ_CVAR(uint32_t, bg_octreeNodeMinEntries, 32, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of entries to allow in a node resulting from a merge operation");
|
||||
|
||||
AZ_CVAR(uint32_t, bg_octreeNodeMaxEntries, 64, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of entries to allow in any node before forcing a split");
|
||||
AZ_CVAR(uint32_t, bg_octreeNodeMinEntries, 32, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of entries to allow in a node resulting from a merge operation");
|
||||
|
||||
static uint32_t GetChildNodeCount()
|
||||
{
|
||||
@@ -25,14 +24,12 @@ namespace AzFramework
|
||||
return (bg_octreeUseQuadtree) ? QuadtreeNodeChildCount : OctreeNodeChildCount;
|
||||
}
|
||||
|
||||
|
||||
OctreeNode::OctreeNode(const AZ::Aabb& bounds)
|
||||
: m_bounds(bounds)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
OctreeNode::OctreeNode(OctreeNode&& rhs)
|
||||
: m_bounds(rhs.m_bounds)
|
||||
, m_parent(rhs.m_parent)
|
||||
@@ -46,7 +43,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OctreeNode& OctreeNode::operator=(OctreeNode&& rhs)
|
||||
{
|
||||
m_bounds = rhs.m_bounds;
|
||||
@@ -63,7 +59,6 @@ namespace AzFramework
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::Insert(OctreeScene& octreeScene, VisibilityEntry* entry)
|
||||
{
|
||||
AZ_Assert(entry->m_internalNode == nullptr, "Double-insertion: Insert invoked for an entry already bound to the OctreeScene");
|
||||
@@ -98,7 +93,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::Update(OctreeScene& octreeScene, VisibilityEntry* entry)
|
||||
{
|
||||
AZ_Assert(entry->m_internalNode == this, "Update invoked for an entry bound to a different OctreeNode");
|
||||
@@ -129,7 +123,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::Remove(OctreeScene& octreeScene, VisibilityEntry* entry)
|
||||
{
|
||||
AZ_Assert(entry->m_internalNode == this, "Remove invoked for an entry bound to a different OctreeNode");
|
||||
@@ -152,25 +145,30 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
EnumerateHelper(aabb, callback);
|
||||
if (AZ::ShapeIntersection::Overlaps(aabb, m_bounds))
|
||||
{
|
||||
EnumerateHelper(aabb, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
EnumerateHelper(sphere, callback);
|
||||
if (AZ::ShapeIntersection::Overlaps(sphere, m_bounds))
|
||||
{
|
||||
EnumerateHelper(sphere, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
EnumerateHelper(frustum, callback);
|
||||
if (AZ::ShapeIntersection::Overlaps(frustum, m_bounds))
|
||||
{
|
||||
EnumerateHelper(frustum, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
// Invoke the callback for the current node
|
||||
@@ -190,25 +188,21 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const AZStd::vector<VisibilityEntry*>& OctreeNode::GetEntries() const
|
||||
{
|
||||
return m_entries;
|
||||
}
|
||||
|
||||
|
||||
OctreeNode* OctreeNode::GetChildren() const
|
||||
{
|
||||
return m_children;
|
||||
}
|
||||
|
||||
|
||||
bool OctreeNode::IsLeaf() const
|
||||
{
|
||||
return m_children == nullptr;
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::TryMerge(OctreeScene& octreeScene)
|
||||
{
|
||||
if (IsLeaf())
|
||||
@@ -236,7 +230,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void OctreeNode::EnumerateHelper(const T& boundingVolume, const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
@@ -262,7 +255,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::Split(OctreeScene& octreeScene)
|
||||
{
|
||||
AZ_Assert(m_children == nullptr, "Split invoked on an octreeScene node that has already been split");
|
||||
@@ -312,7 +304,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeNode::Merge(OctreeScene& octreeScene)
|
||||
{
|
||||
AZ_Assert(m_children != nullptr, "Merge invoked on an octreeScene node that does not have children");
|
||||
@@ -371,7 +362,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeScene::RemoveEntry(VisibilityEntry& entry)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sharedMutex);
|
||||
@@ -382,35 +372,30 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeScene::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
|
||||
m_root.Enumerate(aabb, callback);
|
||||
}
|
||||
|
||||
|
||||
void OctreeScene::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
|
||||
m_root.Enumerate(sphere, callback);
|
||||
}
|
||||
|
||||
|
||||
void OctreeScene::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
|
||||
m_root.Enumerate(frustum, callback);
|
||||
}
|
||||
|
||||
|
||||
void OctreeScene::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const
|
||||
{
|
||||
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
|
||||
m_root.EnumerateNoCull(callback);
|
||||
}
|
||||
|
||||
|
||||
uint32_t OctreeScene::GetEntryCount() const
|
||||
{
|
||||
return m_entryCount;
|
||||
@@ -421,26 +406,22 @@ namespace AzFramework
|
||||
return m_nodeCount;
|
||||
}
|
||||
|
||||
|
||||
uint32_t OctreeScene::GetFreeNodeCount() const
|
||||
{
|
||||
// Each entry represents GetChildNodeCount() nodes
|
||||
return aznumeric_cast<uint32_t>(m_freeOctreeNodes.size() * GetChildNodeCount());
|
||||
}
|
||||
|
||||
|
||||
uint32_t OctreeScene::GetPageCount() const
|
||||
{
|
||||
return aznumeric_cast<uint32_t>(m_nodeCache.size());
|
||||
}
|
||||
|
||||
|
||||
uint32_t OctreeScene::GetChildNodeCount() const
|
||||
{
|
||||
return AzFramework::GetChildNodeCount();
|
||||
}
|
||||
|
||||
|
||||
void OctreeScene::DumpStats()
|
||||
{
|
||||
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::EntryCount = %u", GetName().GetCStr(), GetEntryCount());
|
||||
@@ -450,21 +431,18 @@ namespace AzFramework
|
||||
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::ChildNodeCount = %u", GetName().GetCStr(), GetChildNodeCount());
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t CreateNodeIndex(uint32_t page, uint32_t offset)
|
||||
{
|
||||
AZ_Assert(page <= 0xFFFF && offset <= 0xFFFF, "Out of range values passed to CreateNodeIndex");
|
||||
return (page << 16) | offset;
|
||||
}
|
||||
|
||||
|
||||
static inline void ExtractPageAndOffsetFromIndex(uint32_t index, uint32_t& page, uint32_t& offset)
|
||||
{
|
||||
offset = index & 0x0000FFFF;
|
||||
page = index >> 16;
|
||||
}
|
||||
|
||||
|
||||
uint32_t OctreeScene::AllocateChildNodes()
|
||||
{
|
||||
const uint32_t childCount = GetChildNodeCount();
|
||||
@@ -508,14 +486,12 @@ namespace AzFramework
|
||||
return CreateNodeIndex(nextChildPage, nextChildOffset);
|
||||
}
|
||||
|
||||
|
||||
void OctreeScene::ReleaseChildNodes(uint32_t nodeIndex)
|
||||
{
|
||||
m_nodeCount -= GetChildNodeCount();
|
||||
m_freeOctreeNodes.push(nodeIndex);
|
||||
}
|
||||
|
||||
|
||||
OctreeNode* OctreeScene::GetChildNodesAtIndex(uint32_t nodeIndex) const
|
||||
{
|
||||
uint32_t childPage;
|
||||
@@ -524,7 +500,6 @@ namespace AzFramework
|
||||
return &(*m_nodeCache[childPage])[childOffset];
|
||||
}
|
||||
|
||||
|
||||
void OctreeSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
@@ -534,19 +509,16 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OctreeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC("OctreeService"));
|
||||
}
|
||||
|
||||
|
||||
void OctreeSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC("OctreeService"));
|
||||
}
|
||||
|
||||
|
||||
OctreeSystemComponent::OctreeSystemComponent()
|
||||
{
|
||||
AZ::Interface<IVisibilitySystem>::Register(this);
|
||||
@@ -555,7 +527,6 @@ namespace AzFramework
|
||||
m_defaultScene = aznew OctreeScene(AZ::Name("DefaultVisibilityScene"));
|
||||
}
|
||||
|
||||
|
||||
OctreeSystemComponent::~OctreeSystemComponent()
|
||||
{
|
||||
AZ_Assert(m_scenes.empty(), "All IVisibilityScenes must be destroyed before shutdown");
|
||||
@@ -566,13 +537,11 @@ namespace AzFramework
|
||||
AZ::Interface<IVisibilitySystem>::Unregister(this);
|
||||
}
|
||||
|
||||
|
||||
void OctreeSystemComponent::Activate()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void OctreeSystemComponent::Deactivate()
|
||||
{
|
||||
;
|
||||
@@ -591,7 +560,6 @@ namespace AzFramework
|
||||
return newScene;
|
||||
}
|
||||
|
||||
|
||||
void OctreeSystemComponent::DestroyVisibilityScene(IVisibilityScene* visScene)
|
||||
{
|
||||
for (auto iter = m_scenes.begin(); iter != m_scenes.end(); ++iter)
|
||||
@@ -606,7 +574,6 @@ namespace AzFramework
|
||||
AZ_Assert(false, "visScene[\"%s\"] not found in the OctreeSystemComponent", visScene->GetName().GetCStr());
|
||||
}
|
||||
|
||||
|
||||
IVisibilityScene* OctreeSystemComponent::FindVisibilityScene(const AZ::Name& sceneName)
|
||||
{
|
||||
for (OctreeScene* scene : m_scenes)
|
||||
@@ -619,7 +586,6 @@ namespace AzFramework
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void OctreeSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
for (OctreeScene* scene : m_scenes)
|
||||
|
||||
@@ -228,6 +228,7 @@ set(FILES
|
||||
Physics/Configuration/SimulatedBodyConfiguration.cpp
|
||||
Physics/Configuration/SystemConfiguration.h
|
||||
Physics/Configuration/SystemConfiguration.cpp
|
||||
Physics/HeightfieldProviderBus.h
|
||||
Physics/SimulatedBodies/RigidBody.h
|
||||
Physics/SimulatedBodies/RigidBody.cpp
|
||||
Physics/SimulatedBodies/StaticRigidBody.h
|
||||
@@ -299,6 +300,8 @@ set(FILES
|
||||
Spawnable/SpawnableMonitor.cpp
|
||||
Spawnable/SpawnableSystemComponent.h
|
||||
Spawnable/SpawnableSystemComponent.cpp
|
||||
SurfaceData/SurfaceData.h
|
||||
SurfaceData/SurfaceData.cpp
|
||||
Terrain/TerrainDataRequestBus.h
|
||||
Terrain/TerrainDataRequestBus.cpp
|
||||
Thermal/ThermalInfo.h
|
||||
|
||||
@@ -42,6 +42,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
Tests/framework_shared_tests_files.cmake
|
||||
AzFramework/Physics/physics_mock_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
Tests
|
||||
@@ -53,7 +54,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
AZ::AzTest
|
||||
AZ::AzTestShared
|
||||
)
|
||||
|
||||
|
||||
if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
|
||||
ly_add_target(
|
||||
|
||||
@@ -258,10 +258,17 @@ namespace AzFramework
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void XcbNativeWindow::SetWindowTitle(const AZStd::string& title)
|
||||
{
|
||||
// Set the title of both the window and the task bar by using
|
||||
// a buffer to hold the title twice, separated by a null-terminator
|
||||
auto doubleTitleSize = (title.size() + 1) * 2;
|
||||
AZStd::string doubleTitle(doubleTitleSize, '\0');
|
||||
azstrncpy(doubleTitle.data(), doubleTitleSize, title.c_str(), title.size());
|
||||
azstrncpy(&doubleTitle.data()[title.size() + 1], title.size(), title.c_str(), title.size());
|
||||
|
||||
xcb_void_cookie_t xcbCheckResult;
|
||||
xcbCheckResult = xcb_change_property(
|
||||
m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, static_cast<uint32_t>(title.size()),
|
||||
title.c_str());
|
||||
m_xcbConnection, XCB_PROP_MODE_REPLACE, m_xcbWindow, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, static_cast<uint32_t>(doubleTitle.size()),
|
||||
doubleTitle.c_str());
|
||||
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set window title.");
|
||||
}
|
||||
|
||||
@@ -308,7 +315,7 @@ namespace AzFramework
|
||||
event.data.data32[2] = 0;
|
||||
event.data.data32[3] = 1;
|
||||
event.data.data32[4] = 0;
|
||||
xcb_void_cookie_t xcbCheckResult = xcb_send_event(
|
||||
[[maybe_unused]] xcb_void_cookie_t xcbCheckResult = xcb_send_event(
|
||||
m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
|
||||
(const char*)&event);
|
||||
AZ_Assert(ValidateXcbResult(xcbCheckResult), "Failed to set _NET_WM_STATE_FULLSCREEN");
|
||||
@@ -333,7 +340,7 @@ namespace AzFramework
|
||||
event.data.data32[2] = 0;
|
||||
event.data.data32[3] = 0;
|
||||
event.data.data32[4] = 0;
|
||||
xcb_void_cookie_t xcbCheckResult = xcb_send_event(
|
||||
[[maybe_unused]] xcb_void_cookie_t xcbCheckResult = xcb_send_event(
|
||||
m_xcbConnection, 1, m_xcbRootScreen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
|
||||
(const char*)&event);
|
||||
AZ_Assert(
|
||||
|
||||
+18
@@ -7,17 +7,35 @@
|
||||
*/
|
||||
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
#include <AzFramework/XcbApplication.h>
|
||||
#endif
|
||||
|
||||
constexpr rlim_t g_minimumOpenFileHandles = 65536L;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace AzFramework
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Application::Implementation* Application::Implementation::Create()
|
||||
{
|
||||
// The default open file limit for processes may not be enough for O3DE applications.
|
||||
// We will need to increase to the recommended value if the current open file limit
|
||||
// is not sufficient.
|
||||
rlimit currentLimit;
|
||||
int get_limit_result = getrlimit(RLIMIT_NOFILE, ¤tLimit);
|
||||
AZ_Warning("Application", get_limit_result == 0, "Unable to read current ulimit open file limits");
|
||||
if ((get_limit_result == 0) && (currentLimit.rlim_cur < g_minimumOpenFileHandles || currentLimit.rlim_max < g_minimumOpenFileHandles))
|
||||
{
|
||||
rlimit newLimit;
|
||||
newLimit.rlim_cur = g_minimumOpenFileHandles; // Soft Limit
|
||||
newLimit.rlim_max = g_minimumOpenFileHandles; // Hard Limit
|
||||
[[maybe_unused]] int set_limit_result = setrlimit(RLIMIT_NOFILE, &newLimit);
|
||||
AZ_Assert(set_limit_result == 0, "Unable to update open file limits");
|
||||
}
|
||||
|
||||
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
return aznew XcbApplication();
|
||||
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
|
||||
|
||||
+71
-24
@@ -91,31 +91,42 @@ namespace AzFramework
|
||||
return processId == 0;
|
||||
}
|
||||
|
||||
/*! Executes a command in the child process after the fork operation has been executed.
|
||||
* This function will never return. If the execvp command fails this will call _exit with
|
||||
* the errno value as the return value since continuing execution after a execvp command
|
||||
* is invalid (it will be running the parent's code and in its address space and will
|
||||
* cause many issues).
|
||||
/*! Executes a command in the child process after the fork operation
|
||||
* has been executed. This function will never return. If the execvpe
|
||||
* command fails this will call _exit since continuing execution after
|
||||
* a execvpe command is invalid (it will be running the parent's code
|
||||
* and in its address space and will cause many issues).
|
||||
*
|
||||
* This function runs after a `fork()` call. `fork()` creates a copy of
|
||||
* the current process, including the current state of the process's
|
||||
* memory, at the time the call is made. However, it only creates a
|
||||
* copy of the one thread that called `fork()`. This means that if any
|
||||
* mutexes are locked by other threads at the time of `fork()`, those
|
||||
* mutexes will remain locked in the child process, with no way to
|
||||
* unlock them. So this function needs to ensure that it does as little
|
||||
* work as possible.
|
||||
*
|
||||
* \param commandAndArgs - Array of strings that has the command to execute in index 0 with any args for the command following. Last element must be a null pointer.
|
||||
* \param envionrmentVariables - Array of strings that contains environment variables that command should use. Last element must be a null pointer.
|
||||
* \param environmentVariables - Array of strings that contains environment variables that command should use. Last element must be a null pointer.
|
||||
* \param processLaunchInfo - struct containing information about luanching the command
|
||||
* \param startupInfo - struct containing information needed to startup the command
|
||||
* \param errorPipe - a pipe file descriptor used to communicate a failed execvpe call's error code to the parent process
|
||||
*/
|
||||
void ExecuteCommandAsChild(char** commandAndArgs, char** environmentVariables, const ProcessLauncher::ProcessLaunchInfo& processLaunchInfo, StartupInfo& startupInfo)
|
||||
[[noreturn]] static void ExecuteCommandAsChild(char** commandAndArgs, char** environmentVariables, const ProcessLauncher::ProcessLaunchInfo& processLaunchInfo, StartupInfo& startupInfo, const AZStd::array<int, 2>& errorPipe)
|
||||
{
|
||||
close(errorPipe[0]);
|
||||
|
||||
if (!processLaunchInfo.m_workingDirectory.empty())
|
||||
{
|
||||
int res = chdir(processLaunchInfo.m_workingDirectory.c_str());
|
||||
if (res != 0)
|
||||
{
|
||||
std::cerr << strerror(errno) << std::endl;
|
||||
AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: Unable to change the launched process' directory to '%s'.", processLaunchInfo.m_workingDirectory.c_str());
|
||||
write(errorPipe[1], &errno, sizeof(int));
|
||||
// We *have* to _exit as we are the child process and simply
|
||||
// returning at this point would mean we would start running
|
||||
// the code from our parent process and that will just wreck
|
||||
// havoc.
|
||||
_exit(errno);
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,15 +146,17 @@ namespace AzFramework
|
||||
|
||||
startupInfo.SetupHandlesForChildProcess();
|
||||
|
||||
execve(commandAndArgs[0], commandAndArgs, environmentVariables);
|
||||
execvpe(commandAndArgs[0], commandAndArgs, environmentVariables);
|
||||
const int errval = errno;
|
||||
|
||||
// If we get here then execve failed to run the requested program and
|
||||
// If we get here then execvpe failed to run the requested program and
|
||||
// we have an error. In this case we need to exit the child process
|
||||
// to stop it from continuing to run as a clone of the parent
|
||||
AZ_TracePrintf("Process Watcher", "ProcessWatcher::LaunchProcessAndRetrieveOutput: Unable to launch process %s : errno = %s ", commandAndArgs[0], strerror(errno));
|
||||
std::cerr << strerror(errno) << std::endl;
|
||||
// to stop it from continuing to run as a clone of the parent.
|
||||
// Communicate the error code back to the parent via a pipe for the
|
||||
// parent to read.
|
||||
write(errorPipe[1], &errval, sizeof(errval));
|
||||
|
||||
_exit(errno);
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,9 +225,8 @@ namespace AzFramework
|
||||
|
||||
AZStd::string outputString;
|
||||
bool inQuotes = false;
|
||||
for (size_t pos = 0; pos < processLaunchInfo.m_commandlineParameters.size(); ++pos)
|
||||
for (const char currentChar : processLaunchInfo.m_commandlineParameters)
|
||||
{
|
||||
char currentChar = processLaunchInfo.m_commandlineParameters[pos];
|
||||
if (currentChar == '"')
|
||||
{
|
||||
inQuotes = !inQuotes;
|
||||
@@ -231,7 +243,7 @@ namespace AzFramework
|
||||
outputString.push_back(currentChar);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!outputString.empty())
|
||||
{
|
||||
commandTokens.push_back(outputString);
|
||||
@@ -249,10 +261,10 @@ namespace AzFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
// Because of the way execve is defined we need to copy the strings from
|
||||
// Because of the way execvpe is defined we need to copy the strings from
|
||||
// AZ::string (using c_str() returns a const char*) into a non-const char*
|
||||
|
||||
// Need to add one more as exec requires the array's last element to be a null pointer
|
||||
// Need to add one more as execvpe requires the array's last element to be a null pointer
|
||||
char** commandAndArgs = new char*[commandTokens.size() + 1];
|
||||
for (int i = 0; i < commandTokens.size(); ++i)
|
||||
{
|
||||
@@ -275,7 +287,7 @@ namespace AzFramework
|
||||
azstrcat(environmentVariable.get(), envVarString.size() + 1, envVarString.c_str());
|
||||
environmentVariablesVector.emplace_back(environmentVariable.get());
|
||||
}
|
||||
// Adding one more as exec expects the array to have a nullptr as the last element
|
||||
// Adding one more as execvpe expects the array to have a nullptr as the last element
|
||||
environmentVariablesVector.emplace_back(nullptr);
|
||||
environmentVariables = environmentVariablesVector.data();
|
||||
}
|
||||
@@ -288,15 +300,50 @@ namespace AzFramework
|
||||
AZ_Assert(environmentVariables, "Environment variables for current process not available\n");
|
||||
}
|
||||
|
||||
// Set up a pipe to communicate the error code from the subprocess's execvpe call
|
||||
AZStd::array<int, 2> childErrorPipeFds{};
|
||||
pipe(childErrorPipeFds.data());
|
||||
|
||||
// This configures the write end of the pipe to close on calls to `exec`
|
||||
fcntl(childErrorPipeFds[1], F_SETFD, fcntl(childErrorPipeFds[1], F_GETFD) | FD_CLOEXEC);
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (IsIdChildProcess(child_pid))
|
||||
{
|
||||
ExecuteCommandAsChild(commandAndArgs, environmentVariables, processLaunchInfo, processData.m_startupInfo);
|
||||
ExecuteCommandAsChild(commandAndArgs, environmentVariables, processLaunchInfo, processData.m_startupInfo, childErrorPipeFds);
|
||||
}
|
||||
processData.m_childProcessId = child_pid;
|
||||
|
||||
// Close these handles as they are only to be used by the child process
|
||||
processData.m_startupInfo.CloseAllHandles();
|
||||
close(childErrorPipeFds[1]);
|
||||
|
||||
{
|
||||
int errorCodeFromChild = 0;
|
||||
int count = 0;
|
||||
// Read from the error pipe.
|
||||
// * If the child's call to execvpe succeeded, then the pipe will
|
||||
// be closed due to setting FD_CLOEXEC on the write end of the
|
||||
// pipe. `read()` will return 0.
|
||||
// * If the child's call to execvpe failed, the child will have
|
||||
// written the error code to the pipe. `read()` will return >0, and
|
||||
// the data to be read is the error code from execvpe.
|
||||
while ((count = read(childErrorPipeFds[0], &errorCodeFromChild, sizeof(errorCodeFromChild))) == -1)
|
||||
{
|
||||
if (errno != EAGAIN && errno != EINTR)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
{
|
||||
AZ_TracePrintf("Process Watcher", "ProcessLauncher::LaunchProcess: Unable to launch process %s : errno = %s\n", commandAndArgs[0], strerror(errorCodeFromChild));
|
||||
processData.m_childProcessIsDone = true;
|
||||
child_pid = -1;
|
||||
}
|
||||
}
|
||||
close(childErrorPipeFds[0]);
|
||||
|
||||
processData.m_childProcessId = child_pid;
|
||||
|
||||
for (int i = 0; i < commandTokens.size(); i++)
|
||||
{
|
||||
|
||||
@@ -308,7 +308,9 @@ namespace UnitTest
|
||||
registry->Set(projectPathKey, "AutomatedTesting");
|
||||
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
|
||||
m_app->Start(desc);
|
||||
AZ::ComponentApplication::StartupParameters startupParameters;
|
||||
startupParameters.m_loadAssetCatalog = false;
|
||||
m_app->Start(desc, startupParameters);
|
||||
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
|
||||
+2
@@ -46,6 +46,8 @@ namespace AzManipulatorTestFramework
|
||||
virtual void UpdateVisibility() = 0;
|
||||
//! Set if sticky select is enabled or not.
|
||||
virtual void SetStickySelect(bool enabled) = 0;
|
||||
//! Get default Editor Camera Position.
|
||||
virtual AZ::Vector3 DefaultEditorCameraPosition() const = 0;
|
||||
};
|
||||
|
||||
//! This interface is used to simulate the manipulator manager while the manipulators are under test.
|
||||
|
||||
+1
@@ -36,6 +36,7 @@ namespace AzManipulatorTestFramework
|
||||
int GetViewportId() const override;
|
||||
void UpdateVisibility() override;
|
||||
void SetStickySelect(bool enabled) override;
|
||||
AZ::Vector3 DefaultEditorCameraPosition() const override;
|
||||
|
||||
// ViewportInteractionRequestBus overrides ...
|
||||
AzFramework::CameraState GetCameraState() override;
|
||||
|
||||
@@ -120,6 +120,11 @@ namespace AzManipulatorTestFramework
|
||||
m_stickySelect = enabled;
|
||||
}
|
||||
|
||||
AZ::Vector3 ViewportInteraction::DefaultEditorCameraPosition() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void ViewportInteraction::SetGridSize(float size)
|
||||
{
|
||||
m_gridSize = size;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace AzNetworking
|
||||
m_timeoutItemMap.erase(timeoutId);
|
||||
}
|
||||
|
||||
void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts)
|
||||
void TimeoutQueue::UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts)
|
||||
{
|
||||
int32_t numTimeouts = 0;
|
||||
if (maxTimeouts < 0)
|
||||
@@ -103,7 +103,7 @@ namespace AzNetworking
|
||||
|
||||
// By this point, the item is definitely timed out
|
||||
// Invoke the timeout function to see how to proceed
|
||||
const TimeoutResult result = timeoutHandler.HandleTimeout(mapItem);
|
||||
const TimeoutResult result = timeoutHandler(mapItem);
|
||||
|
||||
if (result == TimeoutResult::Refresh)
|
||||
{
|
||||
@@ -122,4 +122,10 @@ namespace AzNetworking
|
||||
m_timeoutItemMap.erase(itemTimeoutId);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts)
|
||||
{
|
||||
TimeoutHandler handler([&timeoutHandler](TimeoutQueue::TimeoutItem& item) { return timeoutHandler.HandleTimeout(item); });
|
||||
UpdateTimeouts(handler, maxTimeouts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,12 @@ namespace AzNetworking
|
||||
//! @param timeoutId the identifier of the item to remove
|
||||
void RemoveItem(TimeoutId timeoutId);
|
||||
|
||||
//! Updates timeouts for all items, invokes the provided timeout functor if required.
|
||||
//! @param timeoutHandler lambda to invoke for all timeouts
|
||||
//! @param maxTimeouts the maximum number of timeouts to process before breaking iteration
|
||||
using TimeoutHandler = AZStd::function<TimeoutResult(TimeoutQueue::TimeoutItem&)>;
|
||||
void UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1);
|
||||
|
||||
//! Updates timeouts for all items, invokes timeout handlers if required.
|
||||
//! @param timeoutHandler listener instance to call back on for timeouts
|
||||
//! @param maxTimeouts the maximum number of timeouts to process before breaking iteration
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace AzNetworking
|
||||
: m_delta(delta)
|
||||
, m_dataSerializer(m_delta.GetBufferPtr(), m_delta.GetBufferCapacity())
|
||||
{
|
||||
m_namePrefix.reserve(128);
|
||||
;
|
||||
}
|
||||
|
||||
DeltaSerializerCreate::~DeltaSerializerCreate()
|
||||
@@ -73,7 +73,7 @@ namespace AzNetworking
|
||||
// Delete any left over records that might be hanging around
|
||||
for (auto iter : m_records)
|
||||
{
|
||||
delete iter.second;
|
||||
delete iter;
|
||||
}
|
||||
m_records.clear();
|
||||
}
|
||||
@@ -160,28 +160,13 @@ namespace AzNetworking
|
||||
return SerializeHelper(buffer, bufferCapacity, isString, outSize, name);
|
||||
}
|
||||
|
||||
AZStd::string DeltaSerializerCreate::GetNextObjectName(const char* name)
|
||||
bool DeltaSerializerCreate::BeginObject([[maybe_unused]] const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
AZStd::string objectName = name;
|
||||
objectName += ".";
|
||||
objectName += AZStd::to_string(m_objectCounter);
|
||||
++m_objectCounter;
|
||||
return objectName;
|
||||
}
|
||||
|
||||
bool DeltaSerializerCreate::BeginObject(const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
m_nameLengthStack.push_back(m_namePrefix.length());
|
||||
m_namePrefix += GetNextObjectName(name);
|
||||
m_namePrefix += ".";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeltaSerializerCreate::EndObject([[maybe_unused]] const char* name, [[maybe_unused]] const char* typeName)
|
||||
{
|
||||
const size_t prevLen = m_nameLengthStack.back();
|
||||
m_nameLengthStack.pop_back();
|
||||
m_namePrefix.resize(prevLen);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -205,25 +190,15 @@ namespace AzNetworking
|
||||
{
|
||||
typedef AbstractValue::ValueT<T> ValueType;
|
||||
|
||||
const size_t prevLen = m_namePrefix.length();
|
||||
m_namePrefix += GetNextObjectName(name);
|
||||
|
||||
const AZ::HashValue32 nameHash = AZ::TypeHash32(m_namePrefix.c_str());
|
||||
|
||||
m_namePrefix.resize(prevLen);
|
||||
|
||||
AbstractValue::BaseValue*& baseValue = m_records[nameHash];
|
||||
AbstractValue::BaseValue* baseValue = m_records.size() > m_objectCounter ? m_records[m_objectCounter] : nullptr;
|
||||
++m_objectCounter;
|
||||
|
||||
// If we are in the gather records phase, just save off the value records
|
||||
if (m_gatheringRecords)
|
||||
{
|
||||
if (baseValue != nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Duplicate name encountered in delta serializer. This will cause data to be serialized incorrectly.");
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_Assert(baseValue == nullptr, "Expected to create a new record but found a pre-existing one at index %d", m_objectCounter - 1);
|
||||
baseValue = new ValueType(value);
|
||||
m_records.push_back(baseValue);
|
||||
}
|
||||
else // If we are not gathering records, then we are comparing them
|
||||
{
|
||||
|
||||
@@ -90,8 +90,6 @@ namespace AzNetworking
|
||||
DeltaSerializerCreate(const DeltaSerializerCreate&) = delete;
|
||||
DeltaSerializerCreate& operator=(const DeltaSerializerCreate&) = delete;
|
||||
|
||||
AZStd::string GetNextObjectName(const char* name);
|
||||
|
||||
template <typename T>
|
||||
bool SerializeHelper(T& value, uint32_t bufferCapacity, bool isString, uint32_t& outSize, const char* name);
|
||||
|
||||
@@ -105,9 +103,7 @@ namespace AzNetworking
|
||||
|
||||
bool m_gatheringRecords = false;
|
||||
uint32_t m_objectCounter = 0;
|
||||
AZStd::string m_namePrefix;
|
||||
AZStd::vector<size_t> m_nameLengthStack;
|
||||
AZStd::unordered_map<AZ::HashValue32, AbstractValue::BaseValue*> m_records;
|
||||
AZStd::vector<AbstractValue::BaseValue*> m_records;
|
||||
NetworkInputSerializer m_dataSerializer;
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user