mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-21 10:49:23 +00:00
455 lines
14 KiB
C++
455 lines
14 KiB
C++
#include "CPoiMapSidebar.h"
|
|
#include "ui_CPoiMapSidebar.h"
|
|
#include "CWorldEditor.h"
|
|
#include "Editor/UICommon.h"
|
|
|
|
#include <Core/Resource/Cooker/CPoiToWorldCooker.h>
|
|
#include <Core/Resource/Scan/CScan.h>
|
|
#include <Core/Resource/Script/CGameTemplate.h>
|
|
#include <Core/Resource/Script/NGameList.h>
|
|
#include <Core/ScriptExtra/CPointOfInterestExtra.h>
|
|
|
|
#include <QMouseEvent>
|
|
#include <QMessageBox>
|
|
|
|
constexpr CColor skNormalColor(0.137255f, 0.184314f, 0.776471f, 0.5f);
|
|
constexpr CColor skImportantColor(0.721569f, 0.066667f, 0.066667f, 0.5f);
|
|
constexpr CColor skHoverColor(0.047059f, 0.2f, 0.003922f, 0.5f);
|
|
|
|
CPoiMapSidebar::CPoiMapSidebar(CWorldEditor *pEditor)
|
|
: CWorldEditorSidebar(pEditor)
|
|
, ui(std::make_unique<Ui::CPoiMapSidebar>())
|
|
, mSourceModel(pEditor, this)
|
|
{
|
|
mModel.setSourceModel(&mSourceModel);
|
|
mModel.sort(0);
|
|
|
|
ui->setupUi(this);
|
|
ui->ListView->setModel(&mModel);
|
|
ui->ListView->selectionModel()->select(mModel.index(0,0), QItemSelectionModel::Select | QItemSelectionModel::Current);
|
|
SetHighlightSelected();
|
|
|
|
connect(ui->HighlightSelectedButton, &QPushButton::pressed, this, &CPoiMapSidebar::SetHighlightSelected);
|
|
connect(ui->HighlightAllButton, &QPushButton::pressed, this, &CPoiMapSidebar::SetHighlightAll);
|
|
connect(ui->HighlightNoneButton, &QPushButton::pressed, this, &CPoiMapSidebar::SetHighlightNone);
|
|
connect(ui->ListView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
|
this, &CPoiMapSidebar::OnSelectionChanged);
|
|
connect(ui->ListView, &QListView::doubleClicked, this, &CPoiMapSidebar::OnItemDoubleClick);
|
|
connect(ui->MapMeshesButton, &QPushButton::clicked, this, &CPoiMapSidebar::OnPickButtonClicked);
|
|
connect(ui->UnmapMeshesButton, &QPushButton::clicked, this, &CPoiMapSidebar::OnPickButtonClicked);
|
|
connect(ui->UnmapAllButton, &QPushButton::clicked, this, &CPoiMapSidebar::OnUnmapAllPressed);
|
|
connect(ui->AddPoiFromViewportButton, &QPushButton::clicked, this, &CPoiMapSidebar::OnPickButtonClicked);
|
|
connect(ui->AddPoiFromInstanceListButton, &QPushButton::clicked, this, &CPoiMapSidebar::OnInstanceListButtonClicked);
|
|
connect(ui->RemovePoiButton, &QPushButton::clicked, this, &CPoiMapSidebar::OnRemovePoiButtonClicked);
|
|
}
|
|
|
|
CPoiMapSidebar::~CPoiMapSidebar() = default;
|
|
|
|
void CPoiMapSidebar::SidebarOpen()
|
|
{
|
|
Editor()->SetRenderingMergedWorld(false);
|
|
UpdateModelHighlights();
|
|
}
|
|
|
|
void CPoiMapSidebar::SidebarClose()
|
|
{
|
|
// Clear model highlights
|
|
if (mHighlightMode != EHighlightMode::HighlightNone)
|
|
{
|
|
EHighlightMode OldHighlightMode = mHighlightMode;
|
|
SetHighlightNone();
|
|
mHighlightMode = OldHighlightMode;
|
|
}
|
|
|
|
// Stop picking
|
|
if (mPickType != EPickType::NotPicking)
|
|
StopPicking();
|
|
|
|
// Disable unmerged world rendering
|
|
Editor()->SetRenderingMergedWorld(true);
|
|
}
|
|
|
|
void CPoiMapSidebar::HighlightPoiModels(const QModelIndex& rkIndex)
|
|
{
|
|
// Get POI and models
|
|
const QModelIndex SourceIndex = mModel.mapToSource(rkIndex);
|
|
const QList<CModelNode*>& rkModels = mSourceModel.GetPoiMeshList(SourceIndex);
|
|
bool Important = IsImportant(SourceIndex);
|
|
|
|
// Highlight the meshes
|
|
for (auto& model : rkModels)
|
|
{
|
|
model->SetScanOverlayEnabled(true);
|
|
model->SetScanOverlayColor(Important ? skImportantColor : skNormalColor);
|
|
}
|
|
}
|
|
|
|
void CPoiMapSidebar::UnhighlightPoiModels(const QModelIndex& rkIndex)
|
|
{
|
|
const QModelIndex SourceIndex = mModel.mapToSource(rkIndex);
|
|
const QList<CModelNode*>& rkModels = mSourceModel.GetPoiMeshList(SourceIndex);
|
|
|
|
for (const auto& model : rkModels)
|
|
RevertModelOverlay(model);
|
|
}
|
|
|
|
void CPoiMapSidebar::HighlightModel(const QModelIndex& rkIndex, CModelNode *pNode)
|
|
{
|
|
const bool Important = IsImportant(rkIndex);
|
|
pNode->SetScanOverlayEnabled(true);
|
|
pNode->SetScanOverlayColor(Important ? skImportantColor : skNormalColor);
|
|
}
|
|
|
|
void CPoiMapSidebar::UnhighlightModel(CModelNode *pNode)
|
|
{
|
|
pNode->SetScanOverlayEnabled(false);
|
|
}
|
|
|
|
void CPoiMapSidebar::RevertModelOverlay(CModelNode *pModel)
|
|
{
|
|
if (!pModel)
|
|
return;
|
|
|
|
if (mHighlightMode == EHighlightMode::HighlightAll)
|
|
{
|
|
// Prioritize the selected POI over others.
|
|
const QModelIndex Selected = GetSelectedRow();
|
|
|
|
if (mSourceModel.IsModelMapped(Selected, pModel))
|
|
{
|
|
HighlightModel(Selected, pModel);
|
|
}
|
|
else // If it's not mapped to the selected POI, then check whether it's mapped to any others.
|
|
{
|
|
for (int iRow = 0; iRow < mSourceModel.rowCount(QModelIndex()); iRow++)
|
|
{
|
|
const QModelIndex Index = mSourceModel.index(iRow, 0);
|
|
|
|
if (mSourceModel.IsModelMapped(Index, pModel))
|
|
{
|
|
HighlightModel(Index, pModel);
|
|
return;
|
|
}
|
|
}
|
|
|
|
UnhighlightModel(pModel);
|
|
}
|
|
}
|
|
else if (mHighlightMode == EHighlightMode::HighlightSelected)
|
|
{
|
|
const QModelIndex Index = GetSelectedRow();
|
|
|
|
if (mSourceModel.IsModelMapped(Index, pModel))
|
|
HighlightModel(Index, pModel);
|
|
else
|
|
UnhighlightModel(pModel);
|
|
}
|
|
else
|
|
{
|
|
UnhighlightModel(pModel);
|
|
}
|
|
}
|
|
|
|
CPoiMapSidebar::EPickType CPoiMapSidebar::GetRealPickType(bool AltPressed) const
|
|
{
|
|
if (!AltPressed)
|
|
return mPickType;
|
|
|
|
if (mPickType == EPickType::AddMeshes)
|
|
return EPickType::RemoveMeshes;
|
|
|
|
return EPickType::AddMeshes;
|
|
}
|
|
|
|
bool CPoiMapSidebar::IsImportant(const QModelIndex& rkIndex)
|
|
{
|
|
CScriptNode *pPOI = mSourceModel.PoiNodePointer(rkIndex);
|
|
|
|
bool Important = false;
|
|
TResPtr<CScan> pScan = static_cast<CPointOfInterestExtra*>(pPOI->Extra())->GetScan();
|
|
|
|
if (pScan)
|
|
Important = pScan->IsCriticalPropertyRef();
|
|
|
|
return Important;
|
|
}
|
|
|
|
QModelIndex CPoiMapSidebar::GetSelectedRow() const
|
|
{
|
|
const QModelIndexList Indices = ui->ListView->selectionModel()->selectedRows();
|
|
return Indices.isEmpty() ? QModelIndex() : mModel.mapToSource(Indices.front());
|
|
}
|
|
|
|
void CPoiMapSidebar::UpdateModelHighlights()
|
|
{
|
|
const QItemSelection kSelection = ui->ListView->selectionModel()->selection();
|
|
QList<QModelIndex> SelectedIndices;
|
|
QList<QModelIndex> UnselectedIndices;
|
|
|
|
for (int iRow = 0; iRow < mModel.rowCount(QModelIndex()); iRow++)
|
|
{
|
|
const QModelIndex Index = mModel.index(iRow, 0);
|
|
|
|
switch (mHighlightMode)
|
|
{
|
|
case EHighlightMode::HighlightSelected:
|
|
if (kSelection.contains(Index))
|
|
SelectedIndices.push_back(Index);
|
|
else
|
|
UnselectedIndices.push_back(Index);
|
|
break;
|
|
|
|
case EHighlightMode::HighlightAll:
|
|
SelectedIndices.push_back(Index);
|
|
break;
|
|
|
|
case EHighlightMode::HighlightNone:
|
|
UnselectedIndices.push_back(Index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const QModelIndex& rkIndex : UnselectedIndices)
|
|
UnhighlightPoiModels(rkIndex);
|
|
|
|
for (const QModelIndex& rkIndex : SelectedIndices)
|
|
HighlightPoiModels(rkIndex);
|
|
}
|
|
|
|
void CPoiMapSidebar::SetHighlightSelected()
|
|
{
|
|
mHighlightMode = EHighlightMode::HighlightSelected;
|
|
UpdateModelHighlights();
|
|
}
|
|
|
|
void CPoiMapSidebar::SetHighlightAll()
|
|
{
|
|
mHighlightMode = EHighlightMode::HighlightAll;
|
|
UpdateModelHighlights();
|
|
|
|
// Call HighlightPoiModels again on the selected index to prioritize it over the non-selected POIs.
|
|
if (ui->ListView->selectionModel()->hasSelection())
|
|
HighlightPoiModels(ui->ListView->selectionModel()->selectedRows().front());
|
|
}
|
|
|
|
void CPoiMapSidebar::SetHighlightNone()
|
|
{
|
|
mHighlightMode = EHighlightMode::HighlightNone;
|
|
UpdateModelHighlights();
|
|
}
|
|
|
|
void CPoiMapSidebar::OnSelectionChanged(const QItemSelection& rkSelected, const QItemSelection& rkDeselected)
|
|
{
|
|
if (mHighlightMode == EHighlightMode::HighlightSelected)
|
|
{
|
|
// Clear highlight on deselected models
|
|
const QModelIndexList DeselectedIndices = rkDeselected.indexes();
|
|
for (const auto& index : DeselectedIndices)
|
|
UnhighlightPoiModels(index);
|
|
|
|
// Highlight newly selected models
|
|
const QModelIndexList SelectedIndices = rkSelected.indexes();
|
|
for (const auto& index : SelectedIndices)
|
|
HighlightPoiModels(index);
|
|
}
|
|
}
|
|
|
|
void CPoiMapSidebar::OnItemDoubleClick(const QModelIndex& Index)
|
|
{
|
|
const QModelIndex SourceIndex = mModel.mapToSource(Index);
|
|
CScriptNode *pPOI = mSourceModel.PoiNodePointer(SourceIndex);
|
|
Editor()->ClearAndSelectNode(pPOI);
|
|
}
|
|
|
|
void CPoiMapSidebar::OnUnmapAllPressed()
|
|
{
|
|
const QModelIndex Index = GetSelectedRow();
|
|
const QList<CModelNode*> ModelList = mSourceModel.GetPoiMeshList(Index);
|
|
|
|
for (CModelNode *pModel : ModelList)
|
|
{
|
|
mSourceModel.RemoveMapping(Index, pModel);
|
|
RevertModelOverlay(pModel);
|
|
}
|
|
}
|
|
|
|
void CPoiMapSidebar::OnPickButtonClicked()
|
|
{
|
|
auto* pButton = qobject_cast<QPushButton*>(sender());
|
|
|
|
if (pButton == ui->AddPoiFromViewportButton)
|
|
{
|
|
Editor()->EnterPickMode(ENodeType::Script, true, false, false);
|
|
connect(Editor(), &CWorldEditor::PickModeExited, this, &CPoiMapSidebar::StopPicking);
|
|
connect(Editor(), &CWorldEditor::PickModeClick, this, &CPoiMapSidebar::OnPoiPicked);
|
|
|
|
pButton->setChecked(true);
|
|
ui->MapMeshesButton->setChecked(false);
|
|
ui->UnmapMeshesButton->setChecked(false);
|
|
|
|
mPickType = EPickType::AddPOIs;
|
|
}
|
|
else
|
|
{
|
|
if (!pButton->isChecked())
|
|
{
|
|
Editor()->ExitPickMode();
|
|
}
|
|
else
|
|
{
|
|
Editor()->EnterPickMode(ENodeType::Model, false, false, true);
|
|
connect(Editor(), &CWorldEditor::PickModeExited, this, &CPoiMapSidebar::StopPicking);
|
|
connect(Editor(), &CWorldEditor::PickModeHoverChanged, this, &CPoiMapSidebar::OnModelHover);
|
|
pButton->setChecked(true);
|
|
|
|
if (pButton == ui->MapMeshesButton)
|
|
{
|
|
mPickType = EPickType::AddMeshes;
|
|
ui->UnmapMeshesButton->setChecked(false);
|
|
}
|
|
else if (pButton == ui->UnmapMeshesButton)
|
|
{
|
|
mPickType = EPickType::RemoveMeshes;
|
|
ui->MapMeshesButton->setChecked(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPoiMapSidebar::StopPicking()
|
|
{
|
|
ui->MapMeshesButton->setChecked(false);
|
|
ui->UnmapMeshesButton->setChecked(false);
|
|
ui->AddPoiFromViewportButton->setChecked(false);
|
|
mPickType = EPickType::NotPicking;
|
|
|
|
RevertModelOverlay(mpHoverModel);
|
|
mpHoverModel = nullptr;
|
|
|
|
Editor()->ExitPickMode();
|
|
disconnect(Editor(), &CWorldEditor::PickModeExited, this, nullptr);
|
|
disconnect(Editor(), &CWorldEditor::PickModeHoverChanged, this, nullptr);
|
|
disconnect(Editor(), &CWorldEditor::PickModeClick, this, nullptr);
|
|
}
|
|
|
|
void CPoiMapSidebar::OnInstanceListButtonClicked()
|
|
{
|
|
const EGame Game = Editor()->CurrentGame();
|
|
CScriptTemplate *pPoiTemplate = NGameList::GetGameTemplate(Game)->TemplateByID("POIN");
|
|
|
|
CPoiListDialog Dialog(pPoiTemplate, &mSourceModel, Editor()->Scene(), this);
|
|
Dialog.exec();
|
|
|
|
const QList<CScriptNode*>& rkSelection = Dialog.Selection();
|
|
|
|
if (!rkSelection.empty())
|
|
{
|
|
for (CScriptNode *pNode : rkSelection)
|
|
mSourceModel.AddPOI(pNode);
|
|
|
|
mModel.sort(0);
|
|
}
|
|
}
|
|
|
|
void CPoiMapSidebar::OnRemovePoiButtonClicked()
|
|
{
|
|
if (ui->ListView->selectionModel()->hasSelection())
|
|
{
|
|
QModelIndex Index = ui->ListView->selectionModel()->selectedRows().front();
|
|
UnhighlightPoiModels(Index);
|
|
Index = mModel.mapToSource(Index);
|
|
mSourceModel.RemovePOI(Index);
|
|
}
|
|
}
|
|
|
|
void CPoiMapSidebar::OnPoiPicked(const SRayIntersection& rkIntersect, const QMouseEvent* pEvent)
|
|
{
|
|
auto* pPOI = static_cast<CScriptNode*>(rkIntersect.pNode);
|
|
if (pPOI->Instance()->ObjectTypeID() != CFourCC("POIN").ToLong())
|
|
return;
|
|
|
|
mSourceModel.AddPOI(pPOI);
|
|
mModel.sort(0);
|
|
|
|
// Exit pick mode unless the user is holding the Ctrl key
|
|
if (!(pEvent->modifiers() & Qt::ControlModifier))
|
|
Editor()->ExitPickMode();
|
|
}
|
|
|
|
void CPoiMapSidebar::OnModelPicked(const SRayIntersection& rkRayIntersect, const QMouseEvent* pEvent)
|
|
{
|
|
if (!rkRayIntersect.pNode)
|
|
return;
|
|
|
|
// Check for valid selection
|
|
const QModelIndexList Indices = ui->ListView->selectionModel()->selectedRows();
|
|
if (Indices.isEmpty())
|
|
return;
|
|
|
|
// Map selection to source model
|
|
QModelIndexList SourceIndices;
|
|
SourceIndices.reserve(Indices.size());
|
|
for (const auto& index : Indices)
|
|
SourceIndices.push_back(mModel.mapToSource(index));
|
|
|
|
// If alt is pressed, invert the pick mode
|
|
auto* pModel = static_cast<CModelNode*>(rkRayIntersect.pNode);
|
|
|
|
const bool AltPressed = (pEvent->modifiers() & Qt::AltModifier) != 0;
|
|
const EPickType PickType = GetRealPickType(AltPressed);
|
|
|
|
// Add meshes
|
|
if (PickType == EPickType::AddMeshes)
|
|
{
|
|
for (const auto& index : SourceIndices)
|
|
mSourceModel.AddMapping(index, pModel);
|
|
|
|
if (mHighlightMode != EHighlightMode::HighlightNone)
|
|
HighlightModel(SourceIndices.front(), pModel);
|
|
}
|
|
// Remove meshes
|
|
else if (PickType == EPickType::RemoveMeshes)
|
|
{
|
|
for (const auto& index : SourceIndices)
|
|
mSourceModel.RemoveMapping(index, pModel);
|
|
|
|
if (mHighlightMode != EHighlightMode::HighlightNone)
|
|
RevertModelOverlay(mpHoverModel);
|
|
else
|
|
UnhighlightModel(pModel);
|
|
}
|
|
}
|
|
|
|
void CPoiMapSidebar::OnModelHover(const SRayIntersection& rkIntersect, const QMouseEvent *pEvent)
|
|
{
|
|
// Restore old hover model to correct overlay color, and set new hover model
|
|
if (mpHoverModel)
|
|
RevertModelOverlay(mpHoverModel);
|
|
|
|
mpHoverModel = static_cast<CModelNode*>(rkIntersect.pNode);
|
|
|
|
// If the left mouse button is pressed, treat this as a click.
|
|
if ((pEvent->buttons() & Qt::LeftButton) != 0)
|
|
{
|
|
OnModelPicked(rkIntersect, pEvent);
|
|
}
|
|
else // Otherwise, process as a mouseover
|
|
{
|
|
const QModelIndex Index = GetSelectedRow();
|
|
|
|
// Process new hover model
|
|
if (mpHoverModel)
|
|
{
|
|
const bool AltPressed = (pEvent->modifiers() & Qt::AltModifier) != 0;
|
|
const EPickType PickType = GetRealPickType(AltPressed);
|
|
|
|
if ( ((PickType == EPickType::AddMeshes) && !mSourceModel.IsModelMapped(Index, mpHoverModel)) ||
|
|
((PickType == EPickType::RemoveMeshes) && mSourceModel.IsModelMapped(Index, mpHoverModel)) )
|
|
{
|
|
mpHoverModel->SetScanOverlayEnabled(true);
|
|
mpHoverModel->SetScanOverlayColor(skHoverColor);
|
|
}
|
|
}
|
|
}
|
|
}
|