Skip to content

Commit

Permalink
Fixed world transformation for bounding boxes.
Browse files Browse the repository at this point in the history
Added render stats and current room info.
  • Loading branch information
DronCode committed Dec 23, 2023
1 parent 2e58a97 commit 214e386
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 47 deletions.
13 changes: 11 additions & 2 deletions BMEdit/Editor/Include/Widgets/SceneRenderWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ namespace widgets
RM_DEFAULT = RM_TEXTURE | RM_NON_ALPHA_OBJECTS | RM_ALPHA_OBJECTS, ///< Render in texture mode with alpha/non-alpha objects
};

struct RenderStats
{
QString currentRoom {};
int allowedObjects { 0 };
int rejectedObjects { 0 };
float fFrameTime { .0f }; // how much time used for render this frame
};

class SceneRenderWidget : public QOpenGLWidget
{
Q_OBJECT
Expand Down Expand Up @@ -73,6 +81,7 @@ namespace widgets
signals:
void resourcesReady();
void resourceLoadFailed(const QString& reason);
void frameReady(const RenderStats& stats);

public slots:
void onRedrawRequested();
Expand All @@ -98,8 +107,8 @@ namespace widgets
void doPrepareInvalidatedResources(QOpenGLFunctions_3_3_Core* glFunctions);
[[nodiscard]] glm::ivec2 getViewportSize() const;

void collectRenderList(const render::Camera& camera, const gamelib::scene::SceneObject* pRootGeom, render::RenderEntriesList& entries, bool bIgnoreVisibility);
void collectRenderEntriesIntoRenderList(const gamelib::scene::SceneObject* pRootGeom, render::RenderEntriesList& entries, bool bIgnoreVisibility);
void collectRenderList(const render::Camera& camera, const gamelib::scene::SceneObject* pRootGeom, render::RenderEntriesList& entries, RenderStats& stats, bool bIgnoreVisibility);
void collectRenderEntriesIntoRenderList(const gamelib::scene::SceneObject* pRootGeom, render::RenderEntriesList& entries, RenderStats& stats, bool bIgnoreVisibility);
void performRender(QOpenGLFunctions_3_3_Core* glFunctions, const render::RenderEntriesList& entries, const render::Camera& camera, const std::function<bool(const render::RenderEntry&)>& filter);

void invalidateRenderList();
Expand Down
78 changes: 43 additions & 35 deletions BMEdit/Editor/Source/Widgets/SceneRenderWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include <unordered_set>
#include <string_view>
#include <algorithm>
#include <optional>
#include <chrono>
#include <set>


Expand Down Expand Up @@ -123,6 +123,10 @@ namespace widgets

void SceneRenderWidget::paintGL()
{
RenderStats renderStats {};

auto renderStartTime = std::chrono::high_resolution_clock::now();

auto funcs = QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_3_3_Core>(QOpenGLContext::currentContext());
if (!funcs) {
qFatal("Could not obtain required OpenGL context version");
Expand Down Expand Up @@ -207,7 +211,7 @@ namespace widgets

if (m_renderList.empty())
{
collectRenderList(m_camera, pRoot, m_renderList, bIgnoreVisibility);
collectRenderList(m_camera, pRoot, m_renderList, renderStats, bIgnoreVisibility);
}

if (!m_renderList.empty())
Expand All @@ -226,6 +230,15 @@ namespace widgets
{
performRender(funcs, m_renderList, m_camera, onlyAlpha);
}

// Submit stats
if (!m_renderList.empty())
{
auto renderEndTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> elapsed = renderEndTime - renderStartTime;
renderStats.fFrameTime = elapsed.count();
emit frameReady(renderStats);
}
}
}
break;
Expand Down Expand Up @@ -978,15 +991,15 @@ namespace widgets
return { QWidget::width(), QWidget::height() };
}

void SceneRenderWidget::collectRenderList(const render::Camera& camera, const gamelib::scene::SceneObject* pRootGeom, render::RenderEntriesList& entries, bool bIgnoreVisibility)
void SceneRenderWidget::collectRenderList(const render::Camera& camera, const gamelib::scene::SceneObject* pRootGeom, render::RenderEntriesList& entries, RenderStats& stats, bool bIgnoreVisibility)
{
if (m_pLevel->getSceneObjects().empty())
return;

if (pRootGeom != m_pLevel->getSceneObjects()[0].get() || m_rooms.empty() /* on some levels m_rooms cache could be not presented! */)
{
// Render from specific node
collectRenderEntriesIntoRenderList(pRootGeom, entries, bIgnoreVisibility);
collectRenderEntriesIntoRenderList(pRootGeom, entries, stats, bIgnoreVisibility);
}
else
{
Expand All @@ -995,11 +1008,19 @@ namespace widgets
// Render static
for (const auto& sRoomDef : m_rooms)
{
if (sRoomDef.vBoundingBox.contains(m_camera.getPosition()) || m_camera.canSeeObject(sRoomDef.vBoundingBox.min, sRoomDef.vBoundingBox.max))
const bool bCameraInsideRoom = sRoomDef.vBoundingBox.contains(m_camera.getPosition());

if (bCameraInsideRoom || m_camera.canSeeObject(sRoomDef.vBoundingBox.min, sRoomDef.vBoundingBox.max))
{
if (auto pRoom = sRoomDef.rRoom.lock())
{
collectRenderEntriesIntoRenderList(pRoom.get(), entries, bIgnoreVisibility);
if (bCameraInsideRoom)
{
// Store new room name
stats.currentRoom = QString::fromStdString(pRoom->getName());
}

collectRenderEntriesIntoRenderList(pRoom.get(), entries, stats, bIgnoreVisibility);
}

acceptedRooms.emplace_back(sRoomDef);
Expand All @@ -1018,12 +1039,12 @@ namespace widgets
const gamelib::scene::SceneObject* pDynRoot = it->get();

using R = gamelib::scene::SceneObject::EVisitResult;
pDynRoot->visitChildren([&entries, this](const gamelib::scene::SceneObject::Ptr& pObject) -> R {
pDynRoot->visitChildren([&entries, &stats, this](const gamelib::scene::SceneObject::Ptr& pObject) -> R {
if (pObject->getName().ends_with("_LOCATIONS.zip"))
return R::VR_NEXT;

// Collect everything inside
collectRenderEntriesIntoRenderList(pObject.get(), entries, false);
collectRenderEntriesIntoRenderList(pObject.get(), entries, stats, false);
return R::VR_NEXT;
});
}
Expand All @@ -1039,7 +1060,7 @@ namespace widgets
});
}

void SceneRenderWidget::collectRenderEntriesIntoRenderList(const gamelib::scene::SceneObject* geom, render::RenderEntriesList& entries, bool bIgnoreVisibility) // NOLINT(*-no-recursion)
void SceneRenderWidget::collectRenderEntriesIntoRenderList(const gamelib::scene::SceneObject* geom, render::RenderEntriesList& entries, RenderStats& stats, bool bIgnoreVisibility) // NOLINT(*-no-recursion)
{
const bool bInvisible = geom->getProperties().getObject<bool>("Invisible", false);
const auto vPosition = geom->getPosition();
Expand Down Expand Up @@ -1111,14 +1132,9 @@ namespace widgets

// Get model
const Model& model = m_resources->m_models[m_resources->m_modelsCache[primId]];
gamelib::BoundingBox modelWorldBoundingBox = gamelib::BoundingBox::toWorld(model.boundingBox, mWorldTransform);

glm::vec4 vModelBboxMin { model.boundingBox.min, 1.0f };
glm::vec4 vModelBboxMax { model.boundingBox.max, 1.0f };

vModelBboxMin = vModelBboxMin * mWorldTransform;
vModelBboxMax = vModelBboxMax * mWorldTransform;

if (m_camera.canSeeObject(glm::vec3(vModelBboxMin), glm::vec3(vModelBboxMax))) {
if (m_camera.canSeeObject(glm::vec3(modelWorldBoundingBox.min), glm::vec3(modelWorldBoundingBox.max))) {
// Add bounding box to render list
if (geom == m_pSelectedSceneObject && model.boundingBoxMesh.has_value()) {
// Need to add mesh
Expand All @@ -1142,6 +1158,9 @@ namespace widgets
material.pShader = &m_resources->m_shaders[m_resources->m_iGizmoShaderIdx];
}

// increase allowed objects count
stats.allowedObjects++;

// Add each 'mesh' into render list
for (int iMeshIdx = 0; iMeshIdx < model.meshes.size(); iMeshIdx++) {
const auto &mesh = model.meshes[iMeshIdx];
Expand Down Expand Up @@ -1250,14 +1269,19 @@ namespace widgets
}
}
}
else
{
// Increase rejected objects
stats.rejectedObjects++;
}
}

// Visit others
for (const auto& child : geom->getChildren())
{
if (auto g = child.lock())
{
collectRenderEntriesIntoRenderList(g.get(), entries, bIgnoreVisibility);
collectRenderEntriesIntoRenderList(g.get(), entries, stats, bIgnoreVisibility);
}
}
}
Expand Down Expand Up @@ -1442,15 +1466,7 @@ namespace widgets
{
// Nice, collision mesh was found! Just use it as source for bbox of ZROOM
auto sBoundingBox = m_resources->m_models[m_resources->m_modelsCache[iPrimId]].boundingBox;

glm::vec4 vMin { sBoundingBox.min, 1.0f };
glm::vec4 vMax { sBoundingBox.max, 1.0f };
glm::mat4 mWorldTransform = pCollisionMesh->getWorldTransform();

vMin = vMin * mWorldTransform;
vMax = vMax * mWorldTransform;

d.vBoundingBox = gamelib::BoundingBox(glm::vec3(vMin), glm::vec3(vMax));
d.vBoundingBox = gamelib::BoundingBox::toWorld(sBoundingBox, pCollisionMesh->getWorldTransform());
return;
}
}
Expand All @@ -1469,15 +1485,7 @@ namespace widgets
if (m_resources->m_modelsCache.contains(iPrimId))
{
auto sBoundingBox = m_resources->m_models[m_resources->m_modelsCache[iPrimId]].boundingBox;

glm::vec4 vMin { sBoundingBox.min, 1.0f };
glm::vec4 vMax { sBoundingBox.max, 1.0f };
glm::mat4 mWorldTransform = pObject->getWorldTransform();

vMin = vMin * mWorldTransform;
vMax = vMax * mWorldTransform;

gamelib::BoundingBox vWorldBoundingBox = { glm::vec3(vMin.x, vMin.y, vMin.z), glm::vec3(vMax.x, vMax.y, vMax.z) };
gamelib::BoundingBox vWorldBoundingBox = gamelib::BoundingBox::toWorld(sBoundingBox, pObject->getWorldTransform());

if (!bBboxInited)
{
Expand Down
4 changes: 4 additions & 0 deletions BMEdit/Editor/UI/Include/BMEditMainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include <GameLib/IO/AssetKind.h>

#include <Widgets/SceneRenderWidget.h>

#include "LoadSceneProgressDialog.h"
#include "ViewTexturesDialog.h"

Expand Down Expand Up @@ -84,6 +86,7 @@ public slots:
void onLevelAssetsLoadFailed(const QString& reason);
void onSceneObjectPropertyChanged(const gamelib::scene::SceneObject* geom);
void onTextureChanged(uint32_t textureIndex);
void onSceneFramePresented(const widgets::RenderStats& stats);

private:
// UI
Expand All @@ -92,6 +95,7 @@ public slots:
// Custom
QLabel* m_operationLabel;
QLabel* m_operationCommentLabel;
QLabel* m_renderStatsLabel;
QProgressBar* m_operationProgress;

// Models
Expand Down
15 changes: 15 additions & 0 deletions BMEdit/Editor/UI/Source/BMEditMainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,14 @@ void BMEditMainWindow::initStatusBar()
m_operationProgress = new QProgressBar(statusBar());
m_operationLabel = new QLabel(statusBar());
m_operationCommentLabel = new QLabel(statusBar());
m_renderStatsLabel = new QLabel(statusBar());

resetStatusToDefault();

statusBar()->insertWidget(0, m_operationLabel);
statusBar()->insertWidget(1, m_operationProgress);
statusBar()->insertWidget(2, m_operationCommentLabel);
statusBar()->insertWidget(3, m_renderStatsLabel);
}

void BMEditMainWindow::initSearchInput()
Expand Down Expand Up @@ -528,6 +530,17 @@ void BMEditMainWindow::onTextureChanged(uint32_t textureIndex)
ui->sceneGLView->reloadTexture(textureIndex);
}

void BMEditMainWindow::onSceneFramePresented(const widgets::RenderStats& stats)
{
const int iApproxFPS = static_cast<int>(std::floorf(1.f / stats.fFrameTime));

m_renderStatsLabel->setText(QString("ROOM: %1 | Visible objects: %2 | Rejected objects: %3 | FPS: %4")
.arg(stats.currentRoom)
.arg(stats.allowedObjects)
.arg(stats.rejectedObjects)
.arg(iApproxFPS));
}

void BMEditMainWindow::loadTypesDataBase()
{
m_operationProgress->setValue(OperationToProgress::DISCOVER_TYPES_DATABASE);
Expand Down Expand Up @@ -631,6 +644,7 @@ void BMEditMainWindow::resetStatusToDefault()
{
m_operationLabel->setText("Progress: ");
m_operationCommentLabel->setText("(No active operation)");
m_renderStatsLabel->setText("[No render stats]");
m_operationProgress->setValue(0);
}

Expand Down Expand Up @@ -667,6 +681,7 @@ void BMEditMainWindow::initSceneTree()

connect(ui->sceneGLView, &widgets::SceneRenderWidget::resourcesReady, this, &BMEditMainWindow::onLevelAssetsLoaded);
connect(ui->sceneGLView, &widgets::SceneRenderWidget::resourceLoadFailed, this, &BMEditMainWindow::onLevelAssetsLoadFailed);
connect(ui->sceneGLView, &widgets::SceneRenderWidget::frameReady, this, &BMEditMainWindow::onSceneFramePresented);
}

void BMEditMainWindow::initProperties()
Expand Down
16 changes: 6 additions & 10 deletions BMEdit/Editor/UI/UI/BMEditMainWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,13 @@
<string notr="true">BMEdit</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="widgets::SceneRenderWidget" name="sceneGLView">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
</widget>
</item>
</layout>
<widget class="widgets::SceneRenderWidget" name="sceneGLView">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
</widget>
</item>
</layout>
</widget>
Expand Down
3 changes: 3 additions & 0 deletions BMEdit/GameLib/Include/GameLib/BoundingBox.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <glm/vec3.hpp>
#include <glm/mat4x4.hpp>


namespace gamelib
Expand All @@ -17,5 +18,7 @@ namespace gamelib

void expand(const BoundingBox& another);
bool contains(const glm::vec3& vPoint) const;

static BoundingBox toWorld(const BoundingBox& source, const glm::mat4& mTransform);
};
}
29 changes: 29 additions & 0 deletions BMEdit/GameLib/Source/GameLib/BoundingBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,33 @@ bool BoundingBox::contains(const glm::vec3& vPoint) const
return vPoint.x >= min.x && vPoint.x <= max.x &&
vPoint.y >= min.y && vPoint.y <= max.y &&
vPoint.z >= min.z && vPoint.z <= max.z;
}

BoundingBox BoundingBox::toWorld(const BoundingBox& source, const glm::mat4& mTransform)
{
glm::vec3 vMin = source.min;
glm::vec3 vMax = source.max;

glm::vec3 avVertices[8];
avVertices[0] = vMin;
avVertices[1] = glm::vec3(vMax.x, vMin.y, vMin.z);
avVertices[2] = glm::vec3(vMin.x, vMax.y, vMin.z);
avVertices[3] = glm::vec3(vMax.x, vMax.y, vMin.z);
avVertices[4] = glm::vec3(vMin.x, vMin.y, vMax.z);
avVertices[5] = glm::vec3(vMax.x, vMin.y, vMax.z);
avVertices[6] = glm::vec3(vMin.x, vMax.y, vMax.z);
avVertices[7] = vMax;

BoundingBox result {};
result.min = glm::vec3(mTransform * glm::vec4(avVertices[0], 1.f));
result.max = result.min;

for (int i = 1; i < 8; i++)
{
glm::vec3 vTransformed = glm::vec3(mTransform * glm::vec4(avVertices[i], 1.f));
result.min = glm::min(result.min, vTransformed);
result.max = glm::max(result.max, vTransformed);
}

return result;
}

0 comments on commit 214e386

Please sign in to comment.