Skip to content

Commit

Permalink
Provide a camera if there's none in the scene
Browse files Browse the repository at this point in the history
Allow loading scenes without a camera. If there is no camera then
provide one that the user can interact with. As someone validating GLTF
support, I find this preferable to a static camera so that I can look at
the scene from any angle at runtime.
  • Loading branch information
footballhead committed Dec 6, 2024
1 parent 5aa9f65 commit 68819e4
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 2 deletions.
83 changes: 81 additions & 2 deletions projects/gltf_basic_materials/GltfBasicMaterials.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "ppx/scene/scene_gltf_loader.h"
#include "ppx/graphics_util.h"

namespace {

using namespace ppx;

#if defined(USE_DX12)
Expand All @@ -24,6 +26,36 @@ const grfx::Api kApi = grfx::API_DX_12_0;
const grfx::Api kApi = grfx::API_VK_1_1;
#endif

// Calculates a world space bounding box for the mesh. May be bigger than the actual bounding box (especially if rotation is applied) since the node's bounding box is the starting point for transformation (not the individual vertices).
ppx::AABB GetMeshNodeBoundingBox(const scene::MeshNode& meshNode)
{
float3 obbVertices[8] = {};
meshNode.GetMesh()->GetBoundingBox().Transform(meshNode.GetEvaluatedMatrix(), obbVertices);
ppx::AABB transformedBoundingBox;
transformedBoundingBox.Expand(obbVertices[0]);
transformedBoundingBox.Expand(obbVertices[1]);
transformedBoundingBox.Expand(obbVertices[2]);
transformedBoundingBox.Expand(obbVertices[3]);
transformedBoundingBox.Expand(obbVertices[4]);
transformedBoundingBox.Expand(obbVertices[5]);
transformedBoundingBox.Expand(obbVertices[6]);
transformedBoundingBox.Expand(obbVertices[7]);
return transformedBoundingBox;
}

ppx::AABB GetSceneBoundingBox(const scene::Scene& scene)
{
ppx::AABB sceneBoundingBox;
for (uint32_t i = 0; i < scene.GetMeshNodeCount(); ++i) {
const ppx::AABB transformedMeshNodeBoundingBox = GetMeshNodeBoundingBox(*scene.GetMeshNode(i));
sceneBoundingBox.Expand(transformedMeshNodeBoundingBox.GetMax());
sceneBoundingBox.Expand(transformedMeshNodeBoundingBox.GetMin());
}
return sceneBoundingBox;
}

}

void GltfBasicMaterialsApp::Config(ppx::ApplicationSettings& settings)
{
settings.appName = "gltf_basic_materials";
Expand Down Expand Up @@ -63,7 +95,16 @@ void GltfBasicMaterialsApp::Setup()
PPX_CHECKED_CALL(scene::GltfLoader::Create(GetAssetPath(mSceneAssetKnob->GetValue()), /*pMaterialSelector=*/nullptr, &pLoader));

PPX_CHECKED_CALL(pLoader->LoadScene(GetDevice(), 0, &mScene));
PPX_ASSERT_MSG((mScene->GetCameraNodeCount() > 0), "scene doesn't have camera nodes");
if (mScene->GetCameraNodeCount() == 0) {
PPX_LOG_WARN("Scene doesn't have a camera node. Using a default camera");
mDefaultCamera = ArcballCamera();
mDefaultCamera->SetPerspective(60.0f, GetWindowAspect());
ppx::AABB boundingBox = GetSceneBoundingBox(*mScene);
// Bias FitToBoundingBox to keep the camera view straight-on the Z axis by placing the camera right in front of the scene on the Z axis. This tends to work well for most Khronos glTF-Sample-Assets.
float3 center = (boundingBox.GetMin() + boundingBox.GetMax()) / 2.0f;
mDefaultCamera->LookAt(center + float3(0, 0, 1), center);
mDefaultCamera->FitToBoundingBox(boundingBox.GetMin(), boundingBox.GetMax());
}
PPX_ASSERT_MSG((mScene->GetMeshNodeCount() > 0), "scene doesn't have mesh nodes");

delete pLoader;
Expand Down Expand Up @@ -196,6 +237,7 @@ void GltfBasicMaterialsApp::Setup()
}
}


void GltfBasicMaterialsApp::Shutdown()
{
delete mScene;
Expand All @@ -218,7 +260,8 @@ void GltfBasicMaterialsApp::Render()
PPX_CHECKED_CALL(frame.imageAcquiredFence->WaitAndReset());

// Update camera params
mPipelineArgs->SetCameraParams(mScene->GetCameraNode(0)->GetCamera());
const ppx::Camera& camera = mDefaultCamera.has_value() ? *mDefaultCamera : *mScene->GetCameraNode(0)->GetCamera();
mPipelineArgs->SetCameraParams(&camera);

// Update instance params
{
Expand Down Expand Up @@ -331,3 +374,39 @@ void GltfBasicMaterialsApp::InitKnobs()
GetKnobManager().InitKnob(&mSceneAssetKnob, "gltf-scene-asset", "scene_renderer/scenes/tests/gltf_test_basic_materials.glb");
mSceneAssetKnob->SetFlagDescription("GLTF asset to load and render");
}

void GltfBasicMaterialsApp::MouseMove(int32_t x, int32_t y, int32_t dx, int32_t dy, uint32_t buttons)
{
if (!mDefaultCamera) {
return;
}

if (buttons & ppx::MOUSE_BUTTON_LEFT) {
int32_t prevX = x - dx;
int32_t prevY = y - dy;

float2 prevPos = GetNormalizedDeviceCoordinates(prevX, prevY);
float2 curPos = GetNormalizedDeviceCoordinates(x, y);

mDefaultCamera->Rotate(prevPos, curPos);
}
else if (buttons & ppx::MOUSE_BUTTON_RIGHT) {
int32_t prevX = x - dx;
int32_t prevY = y - dy;

float2 prevPos = GetNormalizedDeviceCoordinates(prevX, prevY);
float2 curPos = GetNormalizedDeviceCoordinates(x, y);
float2 delta = curPos - prevPos;

mDefaultCamera->Pan(delta);
}
}

void GltfBasicMaterialsApp::Scroll(float dx, float dy)
{
if (!mDefaultCamera) {
return;
}
constexpr float kZoomSpeed = 0.5f;
mDefaultCamera->Zoom(dy * kZoomSpeed);
}
5 changes: 5 additions & 0 deletions projects/gltf_basic_materials/GltfBasicMaterials.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class GltfBasicMaterialsApp
void Shutdown() override;
void Render() override;
void InitKnobs() override;
void MouseMove(int32_t x, int32_t y, int32_t dx, int32_t dy, uint32_t buttons) override;
void Scroll(float dx, float dy) override;

private:
struct PerFrame
Expand Down Expand Up @@ -60,6 +62,9 @@ class GltfBasicMaterialsApp
ppx::grfx::TexturePtr mIBLEnvMap;

std::shared_ptr<ppx::KnobFlag<std::string>> mSceneAssetKnob;

// Contains a value only if the GLTF scene doesn't have a camera.
std::optional<ppx::ArcballCamera> mDefaultCamera;
};

#endif // GLTF_BASIC_MATERIALS_H

0 comments on commit 68819e4

Please sign in to comment.