Removes ComponentPalette/CategoriesList and ComponentPalette/ComponentDataModel from Code/Editor/Plugins/ComponentEntityEditorPlugin

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-11-19 16:43:37 -08:00
parent cc79647654
commit fba00b0b2b
6 changed files with 0 additions and 1270 deletions
@@ -1,97 +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 "CategoriesList.h"
ComponentCategoryList::ComponentCategoryList(QWidget* parent /*= nullptr*/)
: QTreeWidget(parent)
{
}
void ComponentCategoryList::Init()
{
setColumnCount(1);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setDragDropMode(QAbstractItemView::DragDropMode::DragOnly);
setDragEnabled(true);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setAllColumnsShowFocus(true);
setStyleSheet("QTreeWidget { selection-background-color: rgba(255,255,255,0.2); }");
QStringList headers;
headers << tr("Categories");
setHeaderLabels(headers);
const QString parentCategoryIconPath = QString("Icons/PropertyEditor/Browse_on.png");
const QString categoryIconPath = QString("Icons/PropertyEditor/Browse.png");
QTreeWidgetItem* allCategory = new QTreeWidgetItem(this);
allCategory->setText(0, "All");
allCategory->setIcon(0, QIcon(categoryIconPath));
// Need this briefly to collect the list of available categories.
ComponentDataModel dataModel(this);
for (const auto& cat : dataModel.GetCategories())
{
QString categoryString = QString(cat.c_str());
QStringList categories = categoryString.split('/', Qt::SkipEmptyParts);
QTreeWidgetItem* parent = nullptr;
QTreeWidgetItem* categoryWidget = nullptr;
for (const auto& categoryName : categories)
{
if (parent)
{
categoryWidget = new QTreeWidgetItem(parent);
categoryWidget->setIcon(0, QIcon(categoryIconPath));
// Store the full category path in a user role because we'll need it to locate the actual category
categoryWidget->setData(0, Qt::UserRole, QVariant::fromValue(categoryString));
}
else
{
auto existingCategory = findItems(categoryName, Qt::MatchExactly);
if (existingCategory.empty())
{
categoryWidget = new QTreeWidgetItem(this);
categoryWidget->setIcon(0, QIcon(parentCategoryIconPath));
}
else
{
categoryWidget = static_cast<QTreeWidgetItem*>(existingCategory.first());
categoryWidget->setIcon(0, QIcon(parentCategoryIconPath));
}
}
parent = categoryWidget;
categoryWidget->setText(0, categoryName);
}
}
expandAll();
connect(this, &QTreeWidget::itemClicked, this, &ComponentCategoryList::OnItemClicked);
}
void ComponentCategoryList::OnItemClicked(QTreeWidgetItem* item, int /*column*/)
{
QVariant userData = item->data(0, Qt::UserRole);
if (userData.isValid())
{
// Send in the full category path, not just the child category name
emit OnCategoryChange(userData.value<QString>().toStdString().c_str());
}
else
{
emit OnCategoryChange(item->text(0).toStdString().c_str());
}
}
#include <UI/ComponentPalette/moc_CategoriesList.cpp>
@@ -1,39 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include "ComponentDataModel.h"
#include <QTreeWidget>
#endif
//! ComponentCategoryList
//! Provides a list of all reflected categories that users can select for quick
//! filtering the filtered component list.
class ComponentCategoryList : public QTreeWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ComponentCategoryList, AZ::SystemAllocator, 0);
explicit ComponentCategoryList(QWidget* parent = nullptr);
void Init();
Q_SIGNALS:
void OnCategoryChange(const char* category);
protected:
// Will emit OnCategoryChange signal
void OnItemClicked(QTreeWidgetItem* item, int column);
};
@@ -1,547 +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 "ComponentDataModel.h"
#include "Include/IObjectManager.h"
#include "Objects/SelectionGroup.h"
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Commands/EntityStateCommand.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <Editor/IEditor.h>
#include <Editor/Viewport.h>
#include <Editor/ViewManager.h>
#include <CryCommon/MathConversion.h>
#include <AzQtComponents/DragAndDrop/ViewportDragAndDrop.h>
#include <QMimeData>
namespace
{
// This is a helper function that given an object that derives from QAbstractItemModel,
// it will request the model's "ClassDataRole" class data for an entry and use that
// information to create a new entity with the selected components.
AZ::EntityId CreateEntityFromSelection(const QModelIndexList& selection, QAbstractItemModel* model)
{
AZ::Vector3 position = AZ::Vector3::CreateZero();
CViewport *view = GetIEditor()->GetViewManager()->GetGameViewport();
int width, height;
view->GetDimensions(&width, &height);
position = LYVec3ToAZVec3(view->ViewToWorld(QPoint(width / 2, height / 2)));
AZ::EntityId newEntityId;
EBUS_EVENT_RESULT(newEntityId, AzToolsFramework::EditorRequests::Bus, CreateNewEntityAtPosition, position, AZ::EntityId());
if (newEntityId.IsValid())
{
// Add all the selected components.
AZ::ComponentTypeList componentsToAdd;
for (auto index : selection)
{
// We only need to consider the first column, it's important that the data() function that
// returns ComponentDataModel::ClassDataRole also does so for the first column.
if (index.column() != 0)
{
continue;
}
QVariant classDataVariant = model->data(index, ComponentDataModel::ClassDataRole);
if (classDataVariant.isValid())
{
const AZ::SerializeContext::ClassData* classData = reinterpret_cast<const AZ::SerializeContext::ClassData*>(classDataVariant.value<void*>());
componentsToAdd.push_back(classData->m_typeId);
}
}
AzToolsFramework::EntityCompositionRequestBus::Broadcast(&AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, AzToolsFramework::EntityIdList{ newEntityId }, componentsToAdd);
return newEntityId;
}
return AZ::EntityId();
}
}
namespace ComponentDataUtilities
{
// This is a helper function to add the specified components to the selected entities, it relies on the provided
// QAbstractItemModel to determine the appropriate ClassData to use to create the components (given that some widgets
// may provide proxy models that alter the order).
void AddComponentsToSelectedEntities(const QModelIndexList& selectedComponents, QAbstractItemModel* model)
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
if (selectedEntities.empty())
{
return;
}
// Add all the selected components.
AZ::ComponentTypeList componentsToAdd;
for (auto index : selectedComponents)
{
// We only need to consider the first column, it's important that the data() function that
// returns ComponentDataModel::ClassDataRole also does so for the first column.
if (index.column() != 0)
{
continue;
}
QVariant classDataVariant = model->data(index, ComponentDataModel::ClassDataRole);
if (classDataVariant.isValid())
{
const AZ::SerializeContext::ClassData* classData = reinterpret_cast<const AZ::SerializeContext::ClassData*>(classDataVariant.value<void*>());
componentsToAdd.push_back(classData->m_typeId);
}
}
AzToolsFramework::EntityCompositionRequestBus::Broadcast(&AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, selectedEntities, componentsToAdd);
}
}
// ComponentDataModel
//////////////////////////////////////////////////////////////////////////
ComponentDataModel::ComponentDataModel(QObject* parent)
: QAbstractTableModel(parent)
{
AZ::SerializeContext* serializeContext = nullptr;
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
serializeContext->EnumerateDerived<AZ::Component>([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
{
bool allowed = false;
bool hidden = false;
AZStd::string category = "Miscellaneous";
if (classData->m_editData)
{
for (const AZ::Edit::ElementData& element : classData->m_editData->m_elements)
{
if (element.m_elementId == AZ::Edit::ClassElements::EditorData)
{
AZStd::string iconPath;
AzToolsFramework::EditorRequestBus::BroadcastResult(iconPath, &AzToolsFramework::EditorRequests::GetComponentTypeEditorIcon, classData->m_typeId);
if (!iconPath.empty())
{
m_componentIcons[classData->m_typeId] = QIcon(iconPath.c_str());
}
for (const AZ::Edit::AttributePair& attribPair : element.m_attributes)
{
if (attribPair.first == AZ::Edit::Attributes::AppearsInAddComponentMenu)
{
if (auto data = azdynamic_cast<AZ::Edit::AttributeData<AZ::Crc32>*>(attribPair.second))
{
if (data->Get(nullptr) == AZ_CRC("Game"))
{
allowed = true;
}
}
}
else if (attribPair.first == AZ::Edit::Attributes::AddableByUser)
{
// skip this component if user is not allowed to add it directly
if (auto data = azdynamic_cast<AZ::Edit::AttributeData<bool>*>(attribPair.second))
{
if (!data->Get(nullptr))
{
hidden = true;
}
}
}
else if (attribPair.first == AZ::Edit::Attributes::Category)
{
if (auto data = azdynamic_cast<AZ::Edit::AttributeData<const char*>*>(attribPair.second))
{
category = data->Get(nullptr);
}
}
}
break;
}
}
}
if (allowed && !hidden)
{
m_componentList.push_back(classData);
m_componentMap[category].push_back(classData);
m_categories.insert(category);
}
return true;
});
// we'd like viewport events
AzQtComponents::DragAndDropEventsBus::Handler::BusConnect(AzQtComponents::DragAndDropContexts::EditorViewport);
}
ComponentDataModel::~ComponentDataModel()
{
AzQtComponents::DragAndDropEventsBus::Handler::BusDisconnect();
}
Qt::ItemFlags ComponentDataModel::flags([[maybe_unused]] const QModelIndex &index) const
{
return Qt::ItemFlags(
Qt::ItemIsEnabled |
Qt::ItemIsDragEnabled |
Qt::ItemIsDropEnabled |
Qt::ItemIsSelectable);
}
const AZ::SerializeContext::ClassData* ComponentDataModel::GetClassData(const QModelIndex& index) const
{
int row = index.row();
if (row < 0 || row >= m_componentList.size())
{
return nullptr;
}
return m_componentList[row];
}
const char* ComponentDataModel::GetCategory(const AZ::SerializeContext::ClassData* classData)
{
if (classData)
{
if (auto editorDataElement = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
{
if (auto categoryAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::Category))
{
if (auto categoryData = azdynamic_cast<AZ::Edit::AttributeData<const char*>*>(categoryAttribute))
{
const char* result = categoryData->Get(nullptr);
if (result)
{
return result;
}
}
}
}
}
return "";
}
QModelIndex ComponentDataModel::index(int row, int column, const QModelIndex &parent /*= QModelIndex()*/) const
{
if (row >= rowCount(parent) || column >= columnCount(parent))
{
return QModelIndex();
}
return createIndex(row, column, (void*)(m_componentList[row]));
}
QModelIndex ComponentDataModel::parent([[maybe_unused]] const QModelIndex &child) const
{
return QModelIndex();
}
int ComponentDataModel::rowCount([[maybe_unused]] const QModelIndex &parent /*= QModelIndex()*/) const
{
return static_cast<int>(m_componentList.size());
}
int ComponentDataModel::columnCount([[maybe_unused]] const QModelIndex &parent /*= QModelIndex()*/) const
{
return ColumnIndex::Count;
}
QVariant ComponentDataModel::data(const QModelIndex &index, int role /*= Qt::DisplayRole*/) const
{
if (index.isValid())
{
const AZ::SerializeContext::ClassData* classData = m_componentList[index.row()];
if (!classData)
{
return QVariant();
}
switch (role)
{
case ClassDataRole:
if (index.column() == 0) // Only get data for one column
{
return QVariant::fromValue<void*>(reinterpret_cast<void*>(const_cast<AZ::SerializeContext::ClassData*>(classData)));
}
break;
case Qt::DisplayRole:
{
if (index.column() == ColumnIndex::Name)
{
return QVariant(classData->m_editData->m_name);
}
else
if (index.column() == ColumnIndex::Category)
{
if (auto editorDataElement = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
{
if (auto categoryAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::Category))
{
if (auto categoryData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(categoryAttribute))
{
return QVariant(categoryData->Get(nullptr));
}
}
}
}
}
break;
case Qt::ToolTipRole:
{
return QVariant(classData->m_editData->m_description);
}
case Qt::DecorationRole:
{
if (index.column() == ColumnIndex::Icon)
{
auto iconIterator = m_componentIcons.find(classData->m_typeId);
if (iconIterator != m_componentIcons.end())
{
return iconIterator->second;
}
}
}
break;
default:
break;
}
}
return QVariant();
}
QMimeData* ComponentDataModel::mimeData(const QModelIndexList& indices) const
{
QModelIndexList list;
// Filter out columns we are not interested in.
for (const QModelIndex& index : indices)
{
if (index.column() == 0)
{
list.push_back(index);
}
}
AZStd::vector<const AZ::SerializeContext::ClassData*> sortedList;
for (QModelIndex index : list)
{
QVariant classDataVariant = index.data(ComponentDataModel::ClassDataRole);
if (classDataVariant.isValid())
{
const AZ::SerializeContext::ClassData* classData = reinterpret_cast<const AZ::SerializeContext::ClassData*>(classDataVariant.value<void*>());
sortedList.push_back(classData);
}
}
QMimeData* mimeData = nullptr;
if (!sortedList.empty())
{
mimeData = AzToolsFramework::ComponentTypeMimeData::Create(sortedList).release();
}
return mimeData;
}
bool ComponentDataModel::CanAcceptDragAndDropEvent(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context) const
{
using namespace AzToolsFramework;
using namespace AzQtComponents;
// if a listener with a higher priority already claimed this event, do not touch it.
if ((!event) || (event->isAccepted()) || (!event->mimeData()))
{
return false;
}
ViewportDragContext* contextVP = azrtti_cast<ViewportDragContext*>(&context);
if (!contextVP)
{
// not a viewport event. This is for some other GUI such as the main window itself.
return false;
}
AZStd::vector<const AZ::SerializeContext::ClassData*> componentClassDataList;
return AzToolsFramework::ComponentTypeMimeData::Get(event->mimeData(), componentClassDataList);
}
void ComponentDataModel::DragEnter(QDragEnterEvent* event, AzQtComponents::DragAndDropContextBase& context)
{
if (CanAcceptDragAndDropEvent(event, context))
{
event->setDropAction(Qt::CopyAction);
event->setAccepted(true);
// opportunities to show special highlights, or ghosted entities or previews here.
}
}
void ComponentDataModel::DragMove(QDragMoveEvent* event, AzQtComponents::DragAndDropContextBase& context)
{
if (CanAcceptDragAndDropEvent(event, context))
{
event->setDropAction(Qt::CopyAction);
event->setAccepted(true);
// opportunities to update special highlights, or ghosted entities or previews here.
}
}
void ComponentDataModel::DragLeave(QDragLeaveEvent* /*event*/)
{
// opportunities to remove ghosted entities or previews here.
}
void ComponentDataModel::Drop(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context)
{
using namespace AzToolsFramework;
using namespace AzQtComponents;
// ALWAYS CHECK - you are not the only one connected to this bus, and someone else may have already
// handled the event or accepted the drop - it might not contain types relevant to you.
// you still get informed about the drop event in case you did some stuff in your gui and need to clean it up.
if (!CanAcceptDragAndDropEvent(event, context))
{
return;
}
// note that the above call already checks all the pointers such as event, or whether context is a VP context, mimetype, etc
ViewportDragContext* contextVP = azrtti_cast<ViewportDragContext*>(&context);
// we don't get given this action by Qt unless we already returned accepted from one of the other ones (such as drag move of drag enter)
event->setDropAction(Qt::CopyAction);
event->setAccepted(true);
AzToolsFramework::ScopedUndoBatch undo("Create entity from components");
const AZStd::string name = AZStd::string::format("Entity%d", GetIEditor()->GetObjectManager()->GetObjectCount());
AZ::Entity* newEntity = aznew AZ::Entity(name.c_str());
if (newEntity)
{
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequests::AddRequiredComponents, *newEntity);
auto* transformComponent = newEntity->FindComponent<AzToolsFramework::Components::TransformComponent>();
if (transformComponent)
{
transformComponent->SetWorldTM(AZ::Transform::CreateTranslation(contextVP->m_hitLocation));
}
// Add the entity to the editor context, which activates it and creates the sandbox object.
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequests::AddEditorEntity, newEntity);
// Prepare undo command last so it captures the final state of the entity.
AzToolsFramework::EntityCreateCommand* command = aznew AzToolsFramework::EntityCreateCommand(static_cast<AZ::u64>(newEntity->GetId()));
command->Capture(newEntity);
command->SetParent(undo.GetUndoBatch());
// Only need to add components to the new entity
AzToolsFramework::EntityIdList entities = { newEntity->GetId() };
AZStd::vector<const AZ::SerializeContext::ClassData*> componentClassDataList;
AzToolsFramework::ComponentTypeMimeData::Get(event->mimeData(), componentClassDataList);
AZ::ComponentTypeList componentsToAdd;
for (auto classData : componentClassDataList)
{
if (!classData)
{
continue;
}
componentsToAdd.push_back(classData->m_typeId);
}
AzToolsFramework::EntityCompositionRequests::AddComponentsOutcome addedComponentsResult = AZ::Failure(AZStd::string("Failed to call AddComponentsToEntities on EntityCompositionRequestBus"));
AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(addedComponentsResult, &AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, entities, componentsToAdd);
ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::AddDirtyEntity, newEntity->GetId());
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::SetSelectedEntities, entities);
}
}
AZ::EntityId ComponentDataProxyModel::NewEntityFromSelection(const QModelIndexList& selection)
{
return CreateEntityFromSelection(selection, this);
}
AZ::EntityId ComponentDataModel::NewEntityFromSelection(const QModelIndexList& selection)
{
return CreateEntityFromSelection(selection, this);
}
bool ComponentDataProxyModel::filterAcceptsRow(int sourceRow, [[maybe_unused]] const QModelIndex &sourceParent) const
{
if (m_selectedCategory.empty() && !filterRegExp().isValid())
return true;
ComponentDataModel* dataModel = static_cast<ComponentDataModel*>(sourceModel());
if (sourceRow < 0 || sourceRow >= dataModel->GetComponents().size())
{
return false;
}
const AZ::SerializeContext::ClassData* classData = dataModel->GetComponents()[sourceRow];
if (!classData)
{
return false;
}
// Get Category
if (!m_selectedCategory.empty())
{
AZStd::string currentCateogry = ComponentDataModel::GetCategory(classData);
if (AzFramework::StringFunc::Find(currentCateogry.c_str(), m_selectedCategory.c_str()))
{
return false;
}
}
if (filterRegExp().isValid())
{
QString componentName = QString::fromUtf8(classData->m_editData->m_name);
return componentName.contains(filterRegExp());
}
return true;
}
void ComponentDataProxyModel::SetSelectedCategory(const AZStd::string& category)
{
m_selectedCategory = category;
invalidate();
}
void ComponentDataProxyModel::ClearSelectedCategory()
{
m_selectedCategory.clear();
invalidate();
}
#include "UI/ComponentPalette/moc_ComponentDataModel.cpp"
@@ -1,128 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/containers/vector.h>
#include <AzQtComponents/Buses/DragAndDrop.h>
#endif
namespace ComponentDataUtilities
{
// Given a list of selected components, use the provided model to get the components to add to any selected entities.
void AddComponentsToSelectedEntities(const QModelIndexList& selectedComponents, QAbstractItemModel* model);
}
class CViewport;
//! ComponentDataModel
//! Holds the data required to display components in a table, this includes component name, categories, icons.
class ComponentDataModel
: public QAbstractTableModel
, protected AzQtComponents::DragAndDropEventsBus::Handler // its okay if more than one of these is installed, the first one gets it.
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ComponentDataModel, AZ::SystemAllocator, 0);
using ComponentClassList = AZStd::vector<const AZ::SerializeContext::ClassData*>;
using ComponentCategorySet = AZStd::set<AZStd::string>;
using ComponentClassMap = AZStd::unordered_map<AZStd::string, AZStd::vector<const AZ::SerializeContext::ClassData*>>;
using ComponentIconMap = AZStd::unordered_map<AZ::Uuid, QIcon>;
enum ColumnIndex
{
Icon,
Category,
Name,
Count
};
enum CustomRoles
{
ClassDataRole = Qt::UserRole + 1
};
ComponentDataModel(QObject* parent = nullptr);
~ComponentDataModel() override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QMimeData* mimeData(const QModelIndexList& indexes) const override;
const AZ::SerializeContext::ClassData* GetClassData(const QModelIndex&) const;
AZ::EntityId NewEntityFromSelection(const QModelIndexList& selection);
static const char* GetCategory(const AZ::SerializeContext::ClassData* classData);
ComponentClassList& GetComponents() { return m_componentList; }
ComponentCategorySet& GetCategories() { return m_categories; }
protected:
//////////////////////////////////////////////////////////////////////////
// AzQtComponents::DragAndDropEventsBus::Handler
//////////////////////////////////////////////////////////////////////////
void DragEnter(QDragEnterEvent* event, AzQtComponents::DragAndDropContextBase& context) override;
void DragMove(QDragMoveEvent* event, AzQtComponents::DragAndDropContextBase& context) override;
void DragLeave(QDragLeaveEvent* event) override;
void Drop(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context) override;
bool CanAcceptDragAndDropEvent(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context) const;
ComponentClassList m_componentList;
ComponentClassMap m_componentMap;
ComponentIconMap m_componentIcons;
ComponentCategorySet m_categories;
};
//! ComponentDataProxyModel
//! FilterProxy for the ComponentDataModel is used along with the search criteria to filter the
//! list of components based on tags and/or selected category.
class ComponentDataProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ComponentDataProxyModel, AZ::SystemAllocator, 0);
ComponentDataProxyModel(QObject* parent = nullptr)
: QSortFilterProxyModel(parent)
{}
// Creates a new entity and adds the selected components to it.
// It is specialized here to ensure it uses the correct indices according to the sorted data.
AZ::EntityId NewEntityFromSelection(const QModelIndexList& selection);
// Filters rows according to the specifed tags and/or selected category
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
// Set the category to filter by.
void SetSelectedCategory(const AZStd::string& category);
void ClearSelectedCategory();
protected:
AZStd::string m_selectedCategory;
};
@@ -20,10 +20,6 @@ set(FILES
UI/QComponentEntityEditorOutlinerWindow.cpp
UI/AssetCatalogModel.h
UI/AssetCatalogModel.cpp
UI/ComponentPalette/CategoriesList.h
UI/ComponentPalette/CategoriesList.cpp
UI/ComponentPalette/ComponentDataModel.h
UI/ComponentPalette/ComponentDataModel.cpp
UI/ComponentPalette/ComponentPaletteSettings.h
UI/Outliner/OutlinerDisplayOptionsMenu.h
UI/Outliner/OutlinerDisplayOptionsMenu.cpp
-455
View File
@@ -1,455 +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
*
*/
/* BEGIN CONTENTS OF README.TXT -------------------------------------
The ConvexDecomposition library was written by John W. Ratcliff mailto:jratcliffscarab@gmail.com
What is Convex Decomposition?
Convex Decomposition is when you take an arbitrarily complex triangle mesh and sub-divide it into
a collection of discrete compound pieces (each represented as a convex hull) to approximate
the original shape of the objet.
This is required since few physics engines can treat aribtrary triangle mesh objects as dynamic
objects. Even those engines which can handle this use case incurr a huge performance and memory
penalty to do so.
By breaking a complex triangle mesh up into a discrete number of convex components you can greatly
improve performance for dynamic simulations.
--------------------------------------------------------------------------------
This code is released under the MIT license.
The code is functional but could use the following improvements:
(1) The convex hull generator, originally written by Stan Melax, could use some major code cleanup.
(2) The code to remove T-junctions appears to have a bug in it. This code was working fine before,
but I haven't had time to debug why it stopped working.
(3) Island generation once the mesh has been split is currently disabled due to the fact that the
Remove Tjunctions functionality has a bug in it.
(4) The code to perform a raycast against a triangle mesh does not currently use any acceleration
data structures.
(5) When a split is performed, the surface that got split is not 'capped'. This causes a problem
if you use a high recursion depth on your convex decomposition. It will cause the object to
be modelled as if it had a hollow interior. A lot of work was done to solve this problem, but
it hasn't been integrated into this code drop yet.
*/// ---------- END CONTENTS OF README.TXT ----------------------------
// a set of routines that let you do common 3d math
// operations without any vector, matrix, or quaternion
// classes or templates.
//
// a vector (or point) is a 'NxF32 *' to 3 floating point numbers.
// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL
// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w
//
//
/*!
**
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
**
** Portions of this source has been released with the PhysXViewer application, as well as
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
**
** If you find this code useful or you are feeling particularily generous I would
** ask that you please go to http://www.amillionpixels.us and make a donation
** to Troy DeMolay.
**
** DeMolay is a youth group for young men between the ages of 12 and 21.
** It teaches strong moral principles, as well as leadership skills and
** public speaking. The donations page uses the 'pay for pixels' paradigm
** where, in this case, a pixel is only a single penny. Donations can be
** made for as small as $4 or as high as a $100 block. Each person who donates
** will get a link to their own site as well as acknowledgement on the
** donations blog located here http://www.amillionpixels.blogspot.com/
**
** If you wish to contact me you can use the following methods:
**
** Skype ID: jratcliff63367
** Yahoo: jratcliff63367
** AOL: jratcliff1961
** email: jratcliffscarab@gmail.com
**
**
** The MIT license:
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is furnished
** to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in all
** copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
class TVec
{
public:
TVec(NxF64 _x, NxF64 _y, NxF64 _z) { x = _x; y = _y; z = _z; };
TVec(void) { };
NxF64 x;
NxF64 y;
NxF64 z;
};
class CTriangulator
{
public:
/// Default constructor
CTriangulator();
/// Default destructor
virtual ~CTriangulator();
/// Returns the given point in the triangulator array
inline TVec get(const TU32 id) { return mPoints[id]; }
virtual void reset(void)
{
mInputPoints.clear();
mPoints.clear();
mIndices.clear();
}
virtual void addPoint(NxF64 x, NxF64 y, NxF64 z)
{
TVec v(x, y, z);
// update bounding box...
if (mInputPoints.empty())
{
mMin = v;
mMax = v;
}
else
{
if (x < mMin.x)
{
mMin.x = x;
}
if (y < mMin.y)
{
mMin.y = y;
}
if (z < mMin.z)
{
mMin.z = z;
}
if (x > mMax.x)
{
mMax.x = x;
}
if (y > mMax.y)
{
mMax.y = y;
}
if (z > mMax.z)
{
mMax.z = z;
}
}
mInputPoints.push_back(v);
}
// Triangulation happens in 2d. We could inverse transform the polygon around the normal direction, or we just use the two most signficant axes
// Here we find the two longest axes and use them to triangulate. Inverse transforming them would introduce more doubleing point error and isn't worth it.
virtual NxU32* triangulate(NxU32& tcount, NxF64 epsilon)
{
NxU32* ret = 0;
tcount = 0;
mEpsilon = epsilon;
if (!mInputPoints.empty())
{
mPoints.clear();
NxF64 dx = mMax.x - mMin.x; // locate the first, second and third longest edges and store them in i1, i2, i3
NxF64 dy = mMax.y - mMin.y;
NxF64 dz = mMax.z - mMin.z;
NxU32 i1, i2, i3;
if (dx > dy && dx > dz)
{
i1 = 0;
if (dy > dz)
{
i2 = 1;
i3 = 2;
}
else
{
i2 = 2;
i3 = 1;
}
}
else if (dy > dx && dy > dz)
{
i1 = 1;
if (dx > dz)
{
i2 = 0;
i3 = 2;
}
else
{
i2 = 2;
i3 = 0;
}
}
else
{
i1 = 2;
if (dx > dy)
{
i2 = 0;
i3 = 1;
}
else
{
i2 = 1;
i3 = 0;
}
}
NxU32 pcount = (NxU32)mInputPoints.size();
const NxF64* points = &mInputPoints[0].x;
for (NxU32 i = 0; i < pcount; i++)
{
TVec v(points[i1], points[i2], points[i3]);
mPoints.push_back(v);
points += 3;
}
mIndices.clear();
triangulate(mIndices);
tcount = (NxU32)mIndices.size() / 3;
if (tcount)
{
ret = &mIndices[0];
}
}
return ret;
}
virtual const NxF64* getPoint(NxU32 index)
{
return &mInputPoints[index].x;
}
private:
NxF64 mEpsilon;
TVec mMin;
TVec mMax;
TVecVector mInputPoints;
TVecVector mPoints;
TU32Vector mIndices;
/// Tests if a point is inside the given triangle
bool _insideTriangle(const TVec& A, const TVec& B, const TVec& C, const TVec& P);
/// Returns the area of the contour
NxF64 _area();
bool _snip(NxI32 u, NxI32 v, NxI32 w, NxI32 n, NxI32* V);
/// Processes the triangulation
void _process(TU32Vector& indices);
/// Triangulates the contour
void triangulate(TU32Vector& indices);
};
/// Default constructor
CTriangulator::CTriangulator(void)
{
}
/// Default destructor
CTriangulator::~CTriangulator()
{
}
/// Triangulates the contour
void CTriangulator::triangulate(TU32Vector& indices)
{
_process(indices);
}
/// Processes the triangulation
void CTriangulator::_process(TU32Vector& indices)
{
const NxI32 n = (const NxI32)mPoints.size();
if (n < 3)
{
return;
}
NxI32* V = (NxI32*)MEMALLOC_MALLOC(sizeof(NxI32) * n);
bool flipped = false;
if (0.0f < _area())
{
for (NxI32 v = 0; v < n; v++)
{
V[v] = v;
}
}
else
{
flipped = true;
for (NxI32 v = 0; v < n; v++)
{
V[v] = (n - 1) - v;
}
}
NxI32 nv = n;
NxI32 count = 2 * nv;
for (NxI32 m = 0, v = nv - 1; nv > 2; )
{
if (0 >= (count--))
{
return;
}
NxI32 u = v;
if (nv <= u)
{
u = 0;
}
v = u + 1;
if (nv <= v)
{
v = 0;
}
NxI32 w = v + 1;
if (nv <= w)
{
w = 0;
}
if (_snip(u, v, w, nv, V))
{
NxI32 a, b, c, s, t;
a = V[u];
b = V[v];
c = V[w];
if (flipped)
{
indices.push_back(a);
indices.push_back(b);
indices.push_back(c);
}
else
{
indices.push_back(c);
indices.push_back(b);
indices.push_back(a);
}
m++;
for (s = v, t = v + 1; t < nv; s++, t++)
{
V[s] = V[t];
}
nv--;
count = 2 * nv;
}
}
MEMALLOC_FREE(V);
}
/// Returns the area of the contour
NxF64 CTriangulator::_area()
{
NxI32 n = (NxU32)mPoints.size();
NxF64 A = 0.0f;
for (NxI32 p = n - 1, q = 0; q < n; p = q++)
{
const TVec& pval = mPoints[p];
const TVec& qval = mPoints[q];
A += pval.x * qval.y - qval.x * pval.y;
}
A *= 0.5f;
return A;
}
bool CTriangulator::_snip(NxI32 u, NxI32 v, NxI32 w, NxI32 n, NxI32* V)
{
NxI32 p;
const TVec& A = mPoints[V[u]];
const TVec& B = mPoints[V[v]];
const TVec& C = mPoints[V[w]];
if (mEpsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))))
{
return false;
}
for (p = 0; p < n; p++)
{
if ((p == u) || (p == v) || (p == w))
{
continue;
}
const TVec& P = mPoints[V[p]];
if (_insideTriangle(A, B, C, P))
{
return false;
}
}
return true;
}
/// Tests if a point is inside the given triangle
bool CTriangulator::_insideTriangle(const TVec& A, const TVec& B, const TVec& C, const TVec& P)
{
NxF64 ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
NxF64 cCROSSap, bCROSScp, aCROSSbp;
ax = C.x - B.x;
ay = C.y - B.y;
bx = A.x - C.x;
by = A.y - C.y;
cx = B.x - A.x;
cy = B.y - A.y;
apx = P.x - A.x;
apy = P.y - A.y;
bpx = P.x - B.x;
bpy = P.y - B.y;
cpx = P.x - C.x;
cpy = P.y - C.y;
aCROSSbp = ax * bpy - ay * bpx;
cCROSSap = cx * apy - cy * apx;
bCROSScp = bx * cpy - by * cpx;
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
}