mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-20 10:25:40 +00:00
Initial commit of current work on Prime World Editor
This commit is contained in:
491
UI/CWorldEditor.cpp
Normal file
491
UI/CWorldEditor.cpp
Normal file
@@ -0,0 +1,491 @@
|
||||
#include "CWorldEditor.h"
|
||||
#include "ui_CWorldEditor.h"
|
||||
#include "CEditorGLWidget.h"
|
||||
#include <gtc/matrix_transform.hpp>
|
||||
#include <Core/CDrawUtil.h>
|
||||
#include <iostream>
|
||||
#include <QOpenGLContext>
|
||||
#include <QFontMetrics>
|
||||
#include <Core/Log.h>
|
||||
|
||||
#include "WorldEditor/CLayerEditor.h"
|
||||
#include "WorldEditor/WModifyTab.h"
|
||||
#include "WorldEditor/WInstancesTab.h"
|
||||
|
||||
CWorldEditor::CWorldEditor(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::CWorldEditor)
|
||||
{
|
||||
Log::Write("Creating World Editor");
|
||||
ui->setupUi(this);
|
||||
|
||||
mpRenderer = new CRenderer();
|
||||
mpRenderer->SetClearColor(CColor::skBlack);
|
||||
QSize ViewSize = ui->MainViewport->size();
|
||||
mpRenderer->SetViewportSize(ViewSize.width(), ViewSize.height());
|
||||
|
||||
mpSceneManager = new CSceneManager();
|
||||
|
||||
mpArea = nullptr;
|
||||
mpWorld = nullptr;
|
||||
mpHoverNode = nullptr;
|
||||
//mpInstanceModel = new CInstanceModel(this);
|
||||
//ui->InstancesTreeView->setModel(mpInstanceModel);
|
||||
mDrawSky = true;
|
||||
|
||||
mFrameCount = 0;
|
||||
mFPSTimer.Start();
|
||||
|
||||
// Create blank title bar with some space to allow for dragging the dock
|
||||
QWidget *pOldTitleBar = ui->MainDock->titleBarWidget();
|
||||
|
||||
QWidget *pNewTitleBar = new QWidget(ui->MainDock);
|
||||
QVBoxLayout *pTitleLayout = new QVBoxLayout(pNewTitleBar);
|
||||
pTitleLayout->setSpacing(10);
|
||||
pNewTitleBar->setLayout(pTitleLayout);
|
||||
ui->MainDock->setTitleBarWidget(pNewTitleBar);
|
||||
|
||||
delete pOldTitleBar;
|
||||
|
||||
ResetHover();
|
||||
|
||||
ui->ModifyTabContents->SetEditor(this);
|
||||
ui->InstancesTabContents->SetEditor(this, mpSceneManager);
|
||||
ui->MainDock->installEventFilter(this);
|
||||
connect(ui->MainViewport, SIGNAL(PreRender()), this, SLOT(ViewportPreRender()));
|
||||
connect(ui->MainViewport, SIGNAL(Render(CCamera&)), this, SLOT(ViewportRender(CCamera&)));
|
||||
connect(ui->MainViewport, SIGNAL(ViewportResized(int,int)), this, SLOT(SetViewportSize(int,int)));
|
||||
connect(ui->MainViewport, SIGNAL(frameSwapped()), this, SLOT(ViewportPostRender()));
|
||||
connect(ui->MainViewport, SIGNAL(MouseClick(QMouseEvent*)), this, SLOT(ViewportMouseClick(QMouseEvent*)));
|
||||
}
|
||||
|
||||
CWorldEditor::~CWorldEditor()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool CWorldEditor::eventFilter(QObject *pObj, QEvent *pEvent)
|
||||
{
|
||||
if (pObj == ui->MainDock)
|
||||
{
|
||||
if (pEvent->type() == QEvent::Resize)
|
||||
{
|
||||
UpdateSelectionUI();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea)
|
||||
{
|
||||
ResetHover();
|
||||
ClearSelection();
|
||||
ui->ModifyTabContents->ClearUI();
|
||||
ui->ModifyTabContents->ClearCachedEditors();
|
||||
ui->InstancesTabContents->SetMaster(nullptr);
|
||||
ui->InstancesTabContents->SetArea(pArea);
|
||||
|
||||
// Clear old area - hack until better world/area loader is implemented
|
||||
if ((mpArea) && (pArea != mpArea))
|
||||
mpArea->ClearScriptLayers();
|
||||
|
||||
// Load new area
|
||||
mpArea = pArea;
|
||||
mpWorld = pWorld;
|
||||
mAreaToken = CToken(pArea);
|
||||
mWorldToken = CToken(pWorld);
|
||||
|
||||
mpSceneManager->SetActiveWorld(pWorld);
|
||||
mpSceneManager->SetActiveArea(pArea);
|
||||
|
||||
// Snap camera to location of area
|
||||
CTransform4f AreaTransform = pArea->GetTransform();
|
||||
CVector3f AreaPosition(AreaTransform[0][3], AreaTransform[1][3], AreaTransform[2][3]);
|
||||
ui->MainViewport->Camera().Snap(AreaPosition);
|
||||
|
||||
// Default bloom to ON for Metroid Prime 3; disable for other games
|
||||
if (mpWorld->Version() == eCorruption)
|
||||
{
|
||||
ui->menuBloom->setEnabled(true);
|
||||
on_ActionBloom_triggered();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ui->menuBloom->setEnabled(false);
|
||||
on_ActionNoBloom_triggered();
|
||||
}
|
||||
|
||||
// Set up sidebar tabs
|
||||
CMasterTemplate *pMaster = CMasterTemplate::GetMasterForGame(mpWorld->Version());
|
||||
ui->InstancesTabContents->SetMaster(pMaster);
|
||||
}
|
||||
|
||||
void CWorldEditor::ViewportRayCast(CRay Ray)
|
||||
{
|
||||
if (!ui->MainViewport->IsMouseInputActive())
|
||||
{
|
||||
SRayIntersection Result = mpSceneManager->SceneRayCast(Ray);
|
||||
|
||||
if (Result.Hit)
|
||||
{
|
||||
if (mpHoverNode)
|
||||
mpHoverNode->SetMouseHovering(false);
|
||||
|
||||
mpHoverNode = Result.pNode;
|
||||
mpHoverNode->SetMouseHovering(true);
|
||||
|
||||
mHoverPoint = Ray.PointOnRay(Result.Distance);
|
||||
}
|
||||
else
|
||||
ResetHover();
|
||||
}
|
||||
else
|
||||
ResetHover();
|
||||
}
|
||||
|
||||
CRenderer* CWorldEditor::Renderer()
|
||||
{
|
||||
return mpRenderer;
|
||||
}
|
||||
|
||||
CSceneManager* CWorldEditor::Scene()
|
||||
{
|
||||
return mpSceneManager;
|
||||
}
|
||||
|
||||
CGameArea* CWorldEditor::ActiveArea()
|
||||
{
|
||||
return mpArea;
|
||||
}
|
||||
|
||||
// ************ SELECTION ************
|
||||
void CWorldEditor::SelectNode(CSceneNode *pNode)
|
||||
{
|
||||
if (!pNode->IsSelected())
|
||||
{
|
||||
pNode->SetSelected(true);
|
||||
mSelectedNodes.push_back(pNode);
|
||||
mSelectionAABox.ExpandBounds(pNode->AABox());
|
||||
|
||||
if (pNode->NodeType() == eScriptNode)
|
||||
{
|
||||
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
|
||||
if (pScript->HasPreviewVolume())
|
||||
mSelectionAABox.ExpandBounds(pScript->PreviewVolumeAABox());
|
||||
}
|
||||
}
|
||||
|
||||
UpdateSelectionUI();
|
||||
}
|
||||
|
||||
void CWorldEditor::DeselectNode(CSceneNode *pNode)
|
||||
{
|
||||
if (pNode->IsSelected())
|
||||
{
|
||||
pNode->SetSelected(false);
|
||||
|
||||
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
|
||||
{
|
||||
if (*it == pNode)
|
||||
{
|
||||
mSelectedNodes.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RecalculateSelectionBounds();
|
||||
UpdateSelectionUI();
|
||||
}
|
||||
|
||||
void CWorldEditor::ClearSelection()
|
||||
{
|
||||
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
|
||||
(*it)->SetSelected(false);
|
||||
|
||||
mSelectedNodes.clear();
|
||||
mSelectionAABox = CAABox::skInfinite;
|
||||
UpdateSelectionUI();
|
||||
}
|
||||
|
||||
// ************ SLOTS ************
|
||||
void CWorldEditor::ViewportMouseDrag(QMouseEvent *pEvent)
|
||||
{
|
||||
// todo: gizmo translate/rotate/scale implementation
|
||||
}
|
||||
|
||||
void CWorldEditor::ViewportMouseClick(QMouseEvent *pEvent)
|
||||
{
|
||||
// Process left click (button press)
|
||||
if (pEvent->button() == Qt::LeftButton)
|
||||
{
|
||||
bool ValidNode = ((mpHoverNode) && (mpHoverNode->NodeType() != eStaticNode));
|
||||
bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0);
|
||||
bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0);
|
||||
|
||||
// Alt pressed - deselect object
|
||||
if (AltPressed)
|
||||
{
|
||||
// No valid node selected - do nothing
|
||||
if (!ValidNode)
|
||||
return;
|
||||
|
||||
DeselectNode(mpHoverNode);
|
||||
}
|
||||
|
||||
// Other - select object
|
||||
else
|
||||
{
|
||||
// Control not pressed - clear existing selection
|
||||
if (!CtrlPressed)
|
||||
ClearSelection();
|
||||
|
||||
// Add hover node to selection
|
||||
if (ValidNode)
|
||||
SelectNode(mpHoverNode);
|
||||
}
|
||||
|
||||
UpdateSelectionUI();
|
||||
}
|
||||
|
||||
// Later, possibly expand to context menu creation for right-click
|
||||
}
|
||||
|
||||
// ************ SLOTS ************
|
||||
void CWorldEditor::ViewportPreRender()
|
||||
{
|
||||
// Perform raycast
|
||||
if (ui->MainViewport->underMouse())
|
||||
ViewportRayCast(ui->MainViewport->CastRay());
|
||||
else
|
||||
ResetHover();
|
||||
|
||||
// Start frame
|
||||
mFrameTimer.Start();
|
||||
mpRenderer->BeginFrame();
|
||||
}
|
||||
|
||||
void CWorldEditor::ViewportRender(CCamera& Camera)
|
||||
{
|
||||
mpSceneManager->AddSceneToRenderer(mpRenderer);
|
||||
|
||||
if (mDrawSky)
|
||||
{
|
||||
CModel *pSky = mpSceneManager->GetActiveSkybox();
|
||||
if (pSky) mpRenderer->RenderSky(pSky, Camera.Position());
|
||||
}
|
||||
|
||||
mpRenderer->RenderScene(Camera);
|
||||
mpRenderer->EndFrame();
|
||||
mFrameTimer.Stop();
|
||||
mFrameCount++;
|
||||
}
|
||||
|
||||
void CWorldEditor::ViewportPostRender()
|
||||
{
|
||||
// Update UI with raycast results
|
||||
UpdateCursor();
|
||||
UpdateStatusBar();
|
||||
}
|
||||
|
||||
void CWorldEditor::SetViewportSize(int Width, int Height)
|
||||
{
|
||||
mpRenderer->SetViewportSize(Width, Height);
|
||||
}
|
||||
|
||||
// ************ PRIVATE ************
|
||||
void CWorldEditor::RecalculateSelectionBounds()
|
||||
{
|
||||
mSelectionAABox = CAABox::skInfinite;
|
||||
|
||||
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
|
||||
{
|
||||
mSelectionAABox.ExpandBounds( (*it)->AABox() );
|
||||
|
||||
if ((*it)->NodeType() == eScriptNode)
|
||||
{
|
||||
CScriptNode *pScript = static_cast<CScriptNode*>(*it);
|
||||
if (pScript->HasPreviewVolume())
|
||||
mSelectionAABox.ExpandBounds(pScript->PreviewVolumeAABox());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CWorldEditor::ResetHover()
|
||||
{
|
||||
if (mpHoverNode) mpHoverNode->SetMouseHovering(false);
|
||||
mpHoverNode = nullptr;
|
||||
mHoverPoint = CVector3f::skZero;
|
||||
}
|
||||
|
||||
void CWorldEditor::UpdateCursor()
|
||||
{
|
||||
if (ui->MainViewport->IsCursorVisible())
|
||||
{
|
||||
if ((mpHoverNode) && (mpHoverNode->NodeType() != eStaticNode))
|
||||
ui->MainViewport->SetCursorState(Qt::PointingHandCursor);
|
||||
else
|
||||
ui->MainViewport->SetCursorState(Qt::ArrowCursor);
|
||||
}
|
||||
}
|
||||
|
||||
void CWorldEditor::UpdateStatusBar()
|
||||
{
|
||||
// Would be cool to do more frequent status bar updates with more info. Unfortunately, this causes lag.
|
||||
QString StatusText = "";
|
||||
|
||||
if (mpHoverNode)
|
||||
{
|
||||
if (mpHoverNode->NodeType() != eStaticNode)
|
||||
StatusText = QString::fromStdString(mpHoverNode->Name());
|
||||
}
|
||||
|
||||
if (ui->statusbar->currentMessage() != StatusText)
|
||||
ui->statusbar->showMessage(StatusText);
|
||||
}
|
||||
|
||||
void CWorldEditor::UpdateSelectionUI()
|
||||
{
|
||||
// Update sidebar
|
||||
ui->ModifyTabContents->GenerateUI(mSelectedNodes);
|
||||
|
||||
// Update selection info text
|
||||
QString SelectionText;
|
||||
|
||||
if (mSelectedNodes.size() == 1)
|
||||
SelectionText = QString::fromStdString(mSelectedNodes.front()->Name());
|
||||
else if (mSelectedNodes.size() > 1)
|
||||
SelectionText = QString("%1 objects selected").arg(mSelectedNodes.size());
|
||||
|
||||
QFontMetrics Metrics(ui->SelectionInfoLabel->font());
|
||||
SelectionText = Metrics.elidedText(SelectionText, Qt::ElideRight, ui->SelectionInfoFrame->width() - 10);
|
||||
ui->SelectionInfoLabel->setText(SelectionText);
|
||||
}
|
||||
|
||||
// ************ ACTIONS ************
|
||||
// These functions are from "Go to slot" in the designer
|
||||
void CWorldEditor::on_ActionDrawWorld_triggered()
|
||||
{
|
||||
mpSceneManager->SetWorld(ui->ActionDrawWorld->isChecked());
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionDrawCollision_triggered()
|
||||
{
|
||||
mpSceneManager->SetCollision(ui->ActionDrawCollision->isChecked());
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionDrawObjects_triggered()
|
||||
{
|
||||
mpSceneManager->SetObjects(ui->ActionDrawObjects->isChecked());
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionDrawLights_triggered()
|
||||
{
|
||||
mpSceneManager->SetLights(ui->ActionDrawLights->isChecked());
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionDrawSky_triggered()
|
||||
{
|
||||
mDrawSky = ui->ActionDrawSky->isChecked();
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionNoLighting_triggered()
|
||||
{
|
||||
CGraphics::sLightMode = CGraphics::NoLighting;
|
||||
ui->ActionNoLighting->setChecked(true);
|
||||
ui->ActionBasicLighting->setChecked(false);
|
||||
ui->ActionWorldLighting->setChecked(false);
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionBasicLighting_triggered()
|
||||
{
|
||||
CGraphics::sLightMode = CGraphics::BasicLighting;
|
||||
ui->ActionNoLighting->setChecked(false);
|
||||
ui->ActionBasicLighting->setChecked(true);
|
||||
ui->ActionWorldLighting->setChecked(false);
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionWorldLighting_triggered()
|
||||
{
|
||||
CGraphics::sLightMode = CGraphics::WorldLighting;
|
||||
ui->ActionNoLighting->setChecked(false);
|
||||
ui->ActionBasicLighting->setChecked(false);
|
||||
ui->ActionWorldLighting->setChecked(true);
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionNoBloom_triggered()
|
||||
{
|
||||
mpRenderer->SetBloom(CRenderer::eNoBloom);
|
||||
ui->ActionNoBloom->setChecked(true);
|
||||
ui->ActionBloomMaps->setChecked(false);
|
||||
ui->ActionBloom->setChecked(false);
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionBloomMaps_triggered()
|
||||
{
|
||||
mpRenderer->SetBloom(CRenderer::eBloomMaps);
|
||||
ui->ActionNoBloom->setChecked(false);
|
||||
ui->ActionBloomMaps->setChecked(true);
|
||||
ui->ActionBloom->setChecked(false);
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionBloom_triggered()
|
||||
{
|
||||
mpRenderer->SetBloom(CRenderer::eBloom);
|
||||
ui->ActionNoBloom->setChecked(false);
|
||||
ui->ActionBloomMaps->setChecked(false);
|
||||
ui->ActionBloom->setChecked(true);
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionZoomOnSelection_triggered()
|
||||
{
|
||||
static const float skDistScale = 2.5f;
|
||||
static const float skAreaDistScale = 0.8f;
|
||||
|
||||
CCamera& Camera = ui->MainViewport->Camera();
|
||||
CVector3f CamDir = Camera.GetDirection();
|
||||
CVector3f NewPos;
|
||||
|
||||
// Zoom on selection
|
||||
if (mSelectedNodes.size() != 0)
|
||||
{
|
||||
CVector3f Min = mSelectionAABox.Min();
|
||||
CVector3f Max = mSelectionAABox.Max();
|
||||
float Dist = ((Max.x - Min.x) + (Max.y - Min.y) + (Max.z - Min.z)) / 3.f;
|
||||
//float Dist = mSelectionAABox.Min().Distance(mSelectionAABox.Max());
|
||||
NewPos = mSelectionAABox.Center() + (CamDir * -(Dist * skDistScale));
|
||||
}
|
||||
|
||||
// Zoom on area
|
||||
else
|
||||
{
|
||||
CAABox AreaBox = mpArea->AABox();
|
||||
CVector3f Min = AreaBox.Min();
|
||||
CVector3f Max = AreaBox.Max();
|
||||
float Dist = ((Max.x - Min.x) + (Max.y - Min.y) + (Max.z - Min.z)) / 3.f;
|
||||
//float Dist = AreaBox.Min().Distance(AreaBox.Max());
|
||||
NewPos = AreaBox.Center() + (CamDir * -(Dist * skAreaDistScale));
|
||||
}
|
||||
|
||||
Camera.SetPosition(NewPos);
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionDisableBackfaceCull_triggered()
|
||||
{
|
||||
mpRenderer->ToggleBackfaceCull(!ui->ActionDisableBackfaceCull->isChecked());
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionDisableAlpha_triggered()
|
||||
{
|
||||
mpRenderer->ToggleAlphaDisabled(ui->ActionDisableAlpha->isChecked());
|
||||
}
|
||||
|
||||
void CWorldEditor::on_ActionEditLayers_triggered()
|
||||
{
|
||||
// Launch layer editor
|
||||
CLayerEditor Editor(this);
|
||||
Editor.SetArea(mpArea);
|
||||
Editor.exec();
|
||||
}
|
||||
Reference in New Issue
Block a user