A new froxel grid vizualization for FilamentApp

FilamentApp now has debugging options to enable or disable the
camera and directional shadow frustums, as well as the new
"froxel grid" visualization.

"froxel grid" is automatically enabled when "froxel debugging" is
enabled in the debug gui in gltf_viewer.


New corresponding debugging APIs were added to View.
This commit is contained in:
Mathias Agopian
2025-08-15 16:48:04 -07:00
committed by Mathias Agopian
parent cea178a40c
commit 15bb295ec6
13 changed files with 458 additions and 27 deletions

View File

@@ -27,6 +27,7 @@
#include <utils/FixedCapacityVector.h>
#include <math/mathfwd.h>
#include <math/mat4.h>
#include <utility>
@@ -760,6 +761,27 @@ public:
//! debugging: enable or disable froxel visualisation for this view.
void setFroxelVizEnabled(bool enabled) noexcept;
//! debugging: returns information about the froxel configuration
struct FroxelConfigurationInfo {
uint8_t width;
uint8_t height;
uint8_t depth;
uint32_t viewportWidth;
uint32_t viewportHeight;
math::uint2 froxelDimension;
float zLightFar;
float linearizer;
math::mat4f p;
math::float4 clipTransform;
};
struct FroxelConfigurationInfoWithAge {
FroxelConfigurationInfo info;
uint32_t age;
};
FroxelConfigurationInfoWithAge getFroxelConfigurationInfo() const noexcept;
/** Result of a picking query */
struct PickingQueryResult {
utils::Entity renderable{}; //! RenderableManager Entity at the queried coordinates

View File

@@ -124,6 +124,20 @@ size_t Froxelizer::getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexc
return std::min(FROXEL_BUFFER_MAX_ENTRY_COUNT * sizeof(FroxelEntry), targetSize);
}
View::FroxelConfigurationInfo Froxelizer::getFroxelConfigurationInfo() const noexcept {
return { uint8_t(getFroxelCountX()),
uint8_t(getFroxelCountY()),
uint8_t(getFroxelCountZ()),
mViewport.width,
mViewport.height,
mFroxelDimension,
mZLightFar,
mLinearizer[0],
mProjection,
mClipTransform
};
}
Froxelizer::Froxelizer(FEngine& engine)
: mArena("froxel", PER_FROXELDATA_ARENA_SIZE),
mZLightNear(FROXEL_FIRST_SLICE_DEPTH),
@@ -198,10 +212,14 @@ void Froxelizer::setProjection(const mat4f& projection,
bool Froxelizer::prepare(
FEngine::DriverApi& driverApi, RootArenaScope& rootArenaScope,
filament::Viewport const& viewport,
const mat4f& projection, float const projectionNear, float const projectionFar) noexcept {
const mat4f& projection, float const projectionNear, float const projectionFar,
float4 const& clipTransform) noexcept {
setViewport(viewport);
setProjection(projection, projectionNear, projectionFar);
// Only for debugging
mClipTransform = clipTransform;
bool uniformsNeedUpdating = false;
if (UTILS_UNLIKELY(mDirtyFlags)) {
uniformsNeedUpdating = update();
@@ -361,8 +379,10 @@ bool Froxelizer::update() noexcept {
getFroxelBufferEntryCount(), viewport);
mFroxelDimension = froxelDimension;
mClipToFroxelX = (0.5f * float(viewport.width)) / float(froxelDimension.x);
mClipToFroxelY = (0.5f * float(viewport.height)) / float(froxelDimension.y);
// note: because froxelDimension is a power-of-two and viewport is an integer, mClipFroxel
// is an exact value (which is not true for 1/mClipToFroxelX, btw)
mClipToFroxelX = float(viewport.width) / float(2 * froxelDimension.x);
mClipToFroxelY = float(viewport.height) / float(2 * froxelDimension.y);
uniformsNeedUpdating = true;
@@ -408,8 +428,7 @@ bool Froxelizer::update() noexcept {
}
// for the inverse-transformation (view-space z to z-slice)
mLinearizer = 1.0f / linearizer;
mZLightFar = zLightFar;
mLinearizer = { linearizer, 1.0f / linearizer };
mParamsZ[0] = 0; // updated when camera changes
mParamsZ[1] = 0; // updated when camera changes
@@ -466,7 +485,7 @@ bool Froxelizer::update() noexcept {
// ==> i = log2(z_screen * (far/near)) * (-1/linearizer) + zcount
mParamsZ[0] = mZLightFar / Pw;
mParamsZ[1] = 0.0f;
mParamsZ[2] = -mLinearizer;
mParamsZ[2] = -mLinearizer[1];
} else {
// orthographic projection
// z_view = (1 - z_screen) * (near - far) - near
@@ -476,7 +495,7 @@ bool Froxelizer::update() noexcept {
// Pw = far / (far - near)
mParamsZ[0] = -1.0f / (Pz * mZLightFar); // -(far-near) / mZLightFar
mParamsZ[1] = Pw / (Pz * mZLightFar); // far / mZLightFar
mParamsZ[2] = mLinearizer;
mParamsZ[2] = mLinearizer[1];
}
uniformsNeedUpdating = true;
}
@@ -507,7 +526,7 @@ size_t Froxelizer::findSliceZ(float const z) const noexcept {
// This whole function is now branch-less.
int s = int( fast::log2(-z / mZLightFar) * mLinearizer + float(mFroxelCountZ) );
int s = int( fast::log2(-z / mZLightFar) * mLinearizer[1] + float(mFroxelCountZ) );
// there are cases where z can be negative here, e.g.:
// - the light is visible, but its center is behind the camera

View File

@@ -22,6 +22,7 @@
#include "details/Scene.h"
#include "details/Engine.h"
#include <filament/View.h>
#include <filament/Viewport.h>
#include <backend/Handle.h>
@@ -31,6 +32,7 @@
#include <utils/Slice.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec4.h>
namespace filament {
@@ -112,7 +114,8 @@ public:
* return true if updateUniforms() needs to be called
*/
bool prepare(backend::DriverApi& driverApi, RootArenaScope& rootArenaScope, Viewport const& viewport,
const math::mat4f& projection, float projectionNear, float projectionFar) noexcept;
const math::mat4f& projection, float projectionNear, float projectionFar,
math::float4 const& clipTransform) noexcept;
Froxel getFroxelAt(size_t x, size_t y, size_t z) const noexcept;
size_t getFroxelCountX() const noexcept { return mFroxelCountX; }
@@ -161,6 +164,8 @@ public:
static size_t getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexcept;
View::FroxelConfigurationInfo getFroxelConfigurationInfo() const noexcept;
private:
size_t getFroxelBufferEntryCount() const noexcept {
return mFroxelBufferEntryCount;
@@ -260,9 +265,10 @@ private:
uint16_t mFroxelCountZ = 0;
uint32_t mFroxelCount = 0;
math::uint2 mFroxelDimension = {};
math::float4 mClipTransform = { 1, 1, 0, 0 };
math::mat4f mProjection;
float mLinearizer = 0.0f;
math::float2 mLinearizer{};
float mClipToFroxelX = 0.0f;
float mClipToFroxelY = 0.0f;
backend::BufferObjectHandle mRecordsBuffer;

View File

@@ -81,6 +81,10 @@ void View::setFroxelVizEnabled(bool const enabled) noexcept {
downcast(this)->setFroxelVizEnabled(enabled);
}
View::FroxelConfigurationInfoWithAge View::getFroxelConfigurationInfo() const noexcept {
return downcast(this)->getFroxelConfigurationInfo();
}
void View::setShadowingEnabled(bool const enabled) noexcept {
downcast(this)->setShadowingEnabled(enabled);
}

View File

@@ -659,10 +659,12 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
if (hasDynamicLighting()) {
auto& froxelizer = mFroxelizer;
if (froxelizer.prepare(driver, rootArenaScope, viewport,
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) {
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf,
cameraInfo.clipTransform)) {
// TODO: might be more consistent to do this in prepareLighting(), but it's not
// strictly necessary
getColorPassDescriptorSet().prepareDynamicLights(mFroxelizer, mFroxelVizEnabled);
mFroxelConfigurationAge++;
}
// We need to pass viewMatrix by value here because it extends the scope of this
// function.
@@ -1434,6 +1436,10 @@ void FView::setStereoscopicOptions(const StereoscopicOptions& options) noexcept
mStereoscopicOptions = options;
}
View::FroxelConfigurationInfoWithAge FView::getFroxelConfigurationInfo() const noexcept {
return { mFroxelizer.getFroxelConfigurationInfo(), mFroxelConfigurationAge };
}
void FView::setMaterialGlobal(uint32_t const index, float4 const& value) {
FILAMENT_CHECK_PRECONDITION(index < 4)
<< "material global variable index (" << +index << ") out of range";

View File

@@ -244,6 +244,8 @@ public:
mFroxelVizEnabled = enabled;
}
FroxelConfigurationInfoWithAge getFroxelConfigurationInfo() const noexcept;
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
assert_invariant(!renderTarget || !mMultiSampleAntiAliasingOptions.enabled ||
!renderTarget->hasSampleableDepth());
@@ -557,6 +559,7 @@ private:
mutable Froxelizer mFroxelizer;
utils::JobSystem::Job* mFroxelizerSync = nullptr;
bool mFroxelVizEnabled = false;
uint32_t mFroxelConfigurationAge = 0;
Viewport mViewport;
bool mCulling = true;

View File

@@ -672,7 +672,7 @@ TEST(FilamentTest, FroxelData) {
Froxelizer froxelData(*engine);
froxelData.setOptions(5, 100);
froxelData.prepare(engine->getDriverApi(), scope, vp, p, 0.1, 100);
froxelData.prepare(engine->getDriverApi(), scope, vp, p, 0.1, 100, {1,1,0,0});
Froxel f = froxelData.getFroxelAt(0,0,0);

View File

@@ -20,6 +20,7 @@ set(MATERIAL_DIR "${GENERATION_ROOT}/generated/material")
set(PUBLIC_HDRS
include/filamentapp/Config.h
include/filamentapp/Cube.h
include/filamentapp/Grid.h
include/filamentapp/FilamentApp.h
include/filamentapp/IBL.h
include/filamentapp/IcoSphere.h
@@ -30,6 +31,7 @@ set(PUBLIC_HDRS
set(SRCS
src/Cube.cpp
src/Grid.cpp
src/FilamentApp.cpp
src/IBL.cpp
src/IcoSphere.cpp

View File

@@ -111,6 +111,15 @@ public:
void loadIBL(std::string_view path);
// debugging: enable/disable the froxel grid
void setCameraFrustumEnabled(bool enabled) noexcept;
void setDirectionalShadowFrustumEnabled(bool enabled) noexcept;
void setFroxelGridEnabled(bool enabled) noexcept;
bool isCameraFrustumEnabled() const noexcept;
bool isDirectionalShadowFrustumEnabled() const noexcept;
bool isFroxelGridEnabled() const noexcept;
FilamentApp(const FilamentApp& rhs) = delete;
FilamentApp(FilamentApp&& rhs) = delete;
FilamentApp& operator=(const FilamentApp& rhs) = delete;
@@ -262,6 +271,10 @@ private:
float mCameraNear = 0.1f;
float mCameraFar = 100.0f;
bool mReconfigureCameras = false;
uint8_t mFroxelInfoAge = 0x42;
uint8_t mFroxelGridEnabled = 0;
uint8_t mDirectionalShadowFrustumEnabled = 0x2;
uint8_t mCameraFrustumEnabled = 0x2;
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
filament::backend::VulkanPlatform* mVulkanPlatform = nullptr;

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_SAMPLE_GRID_H
#define TNT_FILAMENT_SAMPLE_GRID_H
#include <filament/Engine.h>
#include <filament/Box.h>
#include <filament/Camera.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <math/mat4.h>
#include <math/vec3.h>
#include <utils/Entity.h>
#include <functional>
class Grid {
public:
Grid(filament::Engine& engine, filament::Material const* material,
filament::math::float3 linearColor);
Grid(Grid const&) = delete;
Grid& operator=(Grid const&) = delete;
Grid(Grid&& rhs) noexcept;
utils::Entity getWireFrameRenderable() const {
return mWireFrameRenderable;
}
~Grid();
using Generator = std::function<float(int index)>;
void update(uint32_t width, uint32_t height, uint32_t depth);
void update(uint32_t width, uint32_t height, uint32_t depth,
Generator const& genWidth, Generator const& genHeight, Generator const& genDepth);
void mapFrustum(filament::Engine& engine, filament::Camera const* camera);
void mapFrustum(filament::Engine& engine, filament::math::mat4 const& transform);
void mapAabb(filament::Engine& engine, filament::Box const& box);
private:
filament::Engine& mEngine;
filament::VertexBuffer* mVertexBuffer = nullptr;
filament::IndexBuffer* mIndexBuffer = nullptr;
filament::Material const* mMaterial = nullptr;
filament::MaterialInstance* mMaterialInstanceWireFrame = nullptr;
utils::Entity mWireFrameRenderable{};
};
#endif // TNT_FILAMENT_SAMPLE_GRID_H

View File

@@ -55,6 +55,7 @@
#include <filagui/ImGuiHelper.h>
#include <filamentapp/Cube.h>
#include <filamentapp/Grid.h>
#include <filamentapp/NativeWindowHelper.h>
#include <stb_image.h>
@@ -179,6 +180,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
.build(*mEngine);
std::unique_ptr<Cube> cameraCube{ new Cube(*mEngine, mTransparentMaterial, { 1, 0, 0 }) };
std::unique_ptr<Grid> cameraGrid{ new Grid(*mEngine, mTransparentMaterial, { 1, 1, 0 }) };
// we can't cull the light-frustum because it's not applied a rigid transform
// and currently, filament assumes that for culling
@@ -195,18 +197,11 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
window->mMainView->getView()->setFroxelVizEnabled(true);
if (config.splitView) {
auto& rcm = mEngine->getRenderableManager();
rcm.setLayerMask(rcm.getInstance(cameraCube->getSolidRenderable()), 0x3, 0x2);
rcm.setLayerMask(rcm.getInstance(cameraCube->getWireFrameRenderable()), 0x3, 0x2);
mScene->addEntity(cameraCube->getWireFrameRenderable());
mScene->addEntity(cameraCube->getSolidRenderable());
mScene->addEntity(cameraCube->getWireFrameRenderable());
for (auto&& cube : lightmapCubes) {
rcm.setLayerMask(rcm.getInstance(cube.getSolidRenderable()), 0x3, 0x2);
rcm.setLayerMask(rcm.getInstance(cube.getWireFrameRenderable()), 0x3, 0x2);
mScene->addEntity(cube.getWireFrameRenderable());
mScene->addEntity(cube.getSolidRenderable());
mScene->addEntity(cube.getWireFrameRenderable());
}
window->mDepthView->getView()->setVisibleLayers(0x4, 0x4);
@@ -219,6 +214,9 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
window->mOrthoView->getView()->setShadowingEnabled(false);
}
// froxel debug grid always added (but hidden)
mScene->addEntity(cameraGrid->getWireFrameRenderable());
loadDirt(config);
loadIBL(config);
@@ -468,11 +466,60 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
filament::math::float3 eye, center, up;
window->mMainCameraMan->getLookAt(&eye, &center, &up);
window->mMainCamera->lookAt(eye, center, up);
window->mDebugCameraMan->getLookAt(&eye, &center, &up);
window->mDebugCamera->lookAt(eye, center, up);
window->mDebugCamera->setExposure(
window->mMainCamera->getAperture(),
window->mMainCamera->getShutterSpeed(),
window->mMainCamera->getSensitivity());
window->mOrthoCamera->setExposure(
window->mMainCamera->getAperture(),
window->mMainCamera->getShutterSpeed(),
window->mMainCamera->getSensitivity());
auto const fci = window->mMainView->getView()->getFroxelConfigurationInfo();
if (UTILS_UNLIKELY(fci.age != mFroxelInfoAge)) {
mFroxelInfoAge = fci.age;
auto width = fci.info.width;
auto height = fci.info.height;
auto depth = fci.info.depth;
auto froxelDimension = fci.info.froxelDimension;
auto viewportWidth = fci.info.viewportWidth;
auto viewportHeight = fci.info.viewportHeight;
auto zLightFar = fci.info.zLightFar;
auto linearizer = fci.info.linearizer;
auto p = fci.info.p;
auto ct = fci.info.clipTransform;
cameraGrid->update(width, height, depth,
[=](int const i) {
float x = float(2 * i * froxelDimension.x) / float(viewportWidth ) - 1.0f;
x = (x - ct.z) / ct.x;
return x;
},
[=](int const j) {
float y = float(2 * j * froxelDimension.y) / float(viewportHeight) - 1.0f;
y = (y - ct.w) / ct.y;
return y;
},
[=](int const k) {
float const z_view = -zLightFar * std::exp2(float(k - depth) * linearizer);
auto c = p * float4{ 0, 0, z_view, 1 };
float const z_clip_dx = k == 0 ? 1.0f : c.z / c.w;
float const z_clip_gl = (1 - z_clip_dx) * 2.0f - 1.0f;
return z_clip_gl;
});
}
auto& rcm = mEngine->getRenderableManager();
if (config.splitView) {
rcm.setLayerMask(rcm.getInstance(cameraCube->getSolidRenderable()), 0x3, mCameraFrustumEnabled);
rcm.setLayerMask(rcm.getInstance(cameraCube->getWireFrameRenderable()), 0x3, mCameraFrustumEnabled);
}
rcm.setLayerMask(rcm.getInstance(cameraGrid->getWireFrameRenderable()), 0x3, mFroxelGridEnabled);
// Update the cube distortion matrix used for frustum visualization.
auto& rcm = mEngine->getRenderableManager();
auto const csm = window->mMainView->getView()->getDirectionalShadowCameras();
// show/hide the cascades
for (size_t i = 0 ; i < 4; i++) {
@@ -484,15 +531,14 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
if (csm[i]) {
lightmapCubes[i].mapFrustum(*mEngine, csm[i]);
}
uint8_t const layer = csm[i] ? 0x2 : 0x0;
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getSolidRenderable()),
0x3, layer);
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getWireFrameRenderable()),
0x3, layer);
uint8_t const layer = csm[i] ? mDirectionalShadowFrustumEnabled : 0x0;
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getSolidRenderable()), 0x3, layer);
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getWireFrameRenderable()), 0x3, layer);
}
}
cameraCube->mapFrustum(*mEngine, window->mMainCamera);
cameraGrid->mapFrustum(*mEngine, window->mMainCamera);
// Delay rendering for roughly one monitor refresh interval
// TODO: Use SDL_GL_SetSwapInterval for proper vsync
@@ -550,6 +596,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
cleanupCallback(mEngine, window->mMainView->getView(), mScene);
cameraCube.reset();
cameraGrid.reset();
lightmapCubes.clear();
window.reset();
@@ -662,6 +709,30 @@ void FilamentApp::initSDL() {
FILAMENT_CHECK_POSTCONDITION(SDL_Init(SDL_INIT_EVENTS) == 0) << "SDL_Init Failure";
}
void FilamentApp::setCameraFrustumEnabled(bool enabled) noexcept {
mCameraFrustumEnabled = enabled ? 0x2 : 0x0;
}
void FilamentApp::setDirectionalShadowFrustumEnabled(bool enabled) noexcept {
mDirectionalShadowFrustumEnabled = enabled ? 0x2 : 0x0;
}
void FilamentApp::setFroxelGridEnabled(bool enabled) noexcept {
mFroxelGridEnabled = enabled ? 0x3 : 0x0;
}
bool FilamentApp::isCameraFrustumEnabled() const noexcept {
return !!mCameraFrustumEnabled;
}
bool FilamentApp::isDirectionalShadowFrustumEnabled() const noexcept {
return !!mDirectionalShadowFrustumEnabled;
}
bool FilamentApp::isFroxelGridEnabled() const noexcept {
return !!mFroxelGridEnabled;
}
// ------------------------------------------------------------------------------------------------
FilamentApp::Window::Window(FilamentApp* filamentApp,

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <filamentapp/Grid.h>
#include <filament/Box.h>
#include <filament/Camera.h>
#include <filament/Color.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/Material.h>
#include <filament/MaterialEnums.h>
#include <filament/MaterialInstance.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <utils/EntityManager.h>
#include <math/vec3.h>
#include <math/mat4.h>
#include <cstdint>
#include <cstddef>
#include <utility>
#include <vector>
#include <utils/Log.h>
using namespace filament;
using namespace filament::math;
using namespace filament;
Grid::Grid(Engine& engine, Material const* material, float3 linearColor)
: mEngine(engine),
mMaterial(material) {
if (mMaterial) {
mMaterialInstanceWireFrame = mMaterial->createInstance();
mMaterialInstanceWireFrame->setDepthCulling(true);
mMaterialInstanceWireFrame->setParameter("color", RgbaType::LINEAR,
LinearColorA{linearColor.r, linearColor.g, linearColor.b, 0.25f});
}
utils::EntityManager& em = utils::EntityManager::get();
mWireFrameRenderable = em.create();
RenderableManager::Builder(1)
.boundingBox({ { -1.0f, -1.0f, -1.0f }, { 1.0f, 1.0f, 1.0f } })
.material(0, mMaterialInstanceWireFrame)
.priority(6)
.culling(false)
.build(engine, mWireFrameRenderable);
}
Grid::Grid(Grid&& rhs) noexcept
: mEngine(rhs.mEngine) {
using std::swap;
swap(rhs.mVertexBuffer, mVertexBuffer);
swap(rhs.mIndexBuffer, mIndexBuffer);
swap(rhs.mMaterial, mMaterial);
swap(rhs.mMaterialInstanceWireFrame, mMaterialInstanceWireFrame);
swap(rhs.mWireFrameRenderable, mWireFrameRenderable);
}
Grid::~Grid() {
mEngine.destroy(mVertexBuffer);
mEngine.destroy(mIndexBuffer);
// We don't own the material, only instances
mEngine.destroy(mWireFrameRenderable);
// material instances must be destroyed after the renderables
mEngine.destroy(mMaterialInstanceWireFrame);
utils::EntityManager& em = utils::EntityManager::get();
em.destroy(mWireFrameRenderable);
}
void Grid::mapFrustum(Engine& engine, Camera const* camera) {
// the Camera far plane is at infinity, but we want it closer for display
const mat4 vm(camera->getModelMatrix());
mat4 const p(vm * inverse(camera->getProjectionMatrix()));
mapFrustum(engine, p);
}
void Grid::mapFrustum(Engine& engine, mat4 const& transform) {
// the Camera far plane is at infinity, but we want it closer for display
mat4f const p(transform);
auto& tcm = engine.getTransformManager();
tcm.setTransform(tcm.getInstance(mWireFrameRenderable), p);
}
void Grid::mapAabb(Engine& engine, Box const& box) {
mat4 const p = mat4::translation(box.center) * mat4::scaling(box.halfExtent);
mapFrustum(engine, p);
}
void Grid::update(uint32_t width, uint32_t height, uint32_t depth) {
// [-1, 1] range (default behavior)
update(width, height, depth,
[](int const index) { return float(index) * 2.0f - 1.0f; },
[](int const index) { return float(index) * 2.0f - 1.0f; },
[](int const index) { return float(index) * 2.0f - 1.0f; });
}
void Grid::update(uint32_t const width, uint32_t const height, uint32_t const depth,
Generator const& genWidth, Generator const& genHeight, Generator const& genDepth) {
// First, destroy the existing buffers.
mEngine.destroy(mVertexBuffer);
mEngine.destroy(mIndexBuffer);
std::vector<float3> vertices;
std::vector<uint32_t> indices;
size_t const verticeCount = (depth + 1) * (height + 1) * (width + 1);
vertices.reserve(verticeCount);
indices.reserve(verticeCount * 2);
// Generate vertices
// The vertices are generated to form a grid in the [-1, 1] range on all axes.
for (int k = 0; k <= depth; ++k) {
auto const z = genDepth(k);
for (int j = 0; j <= height; ++j) {
auto const y = genHeight(j);
for (int i = 0; i <= width; ++i) {
auto const x = genWidth(i);
vertices.push_back({ x, y, z });
}
}
}
// Generate indices for the lines
// The indices connect the vertices to form the grid lines.
auto getVertexIndex = [=](uint32_t const i, uint32_t const j, uint32_t const k) {
return k * (width + 1) * (height + 1) + j * (width + 1) + i;
};
// Lines along X axis
for (uint32_t k = 0; k <= depth; ++k) {
for (uint32_t j = 0; j <= height; ++j) {
indices.push_back(getVertexIndex(0, j, k));
indices.push_back(getVertexIndex(width, j, k));
}
}
// Lines along Y axis
for (uint32_t k = 0; k <= depth; ++k) {
for (uint32_t i = 0; i <= width; ++i) {
indices.push_back(getVertexIndex(i, 0, k));
indices.push_back(getVertexIndex(i, height, k));
}
}
// Lines along Z axis
for (uint32_t j = 0; j <= height; ++j) {
for (uint32_t i = 0; i <= width; ++i) {
indices.push_back(getVertexIndex(i, j, 0));
indices.push_back(getVertexIndex(i, j, depth));
}
}
const size_t vertexCount = vertices.size();
mVertexBuffer = VertexBuffer::Builder()
.vertexCount(vertexCount)
.bufferCount(1)
.attribute(POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
.build(mEngine);
auto vertexData = vertices.data();
mVertexBuffer->setBufferAt(mEngine, 0,
VertexBuffer::BufferDescriptor::make(
vertexData, vertexCount * sizeof(vertices[0]),
[v = std::move(vertices)](void*, size_t) {}));
const size_t indexCount = indices.size();
mIndexBuffer = IndexBuffer::Builder()
.indexCount(indexCount)
.build(mEngine);
auto indexData = indices.data();
mIndexBuffer->setBuffer(mEngine,
IndexBuffer::BufferDescriptor::make(
indexData, indexCount * sizeof(uint32_t),
[i = std::move(indices)](void*, size_t) {}));
auto& rcm = mEngine.getRenderableManager();
auto instance = rcm.getInstance(mWireFrameRenderable);
rcm.setGeometryAt(instance, 0, RenderableManager::PrimitiveType::LINES,
mVertexBuffer, mIndexBuffer, 0, indexCount);
}

View File

@@ -949,12 +949,22 @@ int main(int argc, char** argv) {
"d.shadowmap.display_shadow_texture_channel"), 0, 3);
ImGui::Unindent();
}
bool cameraFrustum = FilamentApp::get().isCameraFrustumEnabled();
ImGui::Checkbox("Show Camera Frustum", &cameraFrustum);
FilamentApp::get().setCameraFrustumEnabled(cameraFrustum);
bool shadowFrustum = FilamentApp::get().isDirectionalShadowFrustumEnabled();
ImGui::Checkbox("Show Shadow Frustum", &shadowFrustum);
FilamentApp::get().setDirectionalShadowFrustumEnabled(shadowFrustum);
bool debugFroxelVisualization;
if (debug.getProperty("d.lighting.debug_froxel_visualization",
&debugFroxelVisualization)) {
ImGui::Checkbox("Froxel Visualization", &debugFroxelVisualization);
debug.setProperty("d.lighting.debug_froxel_visualization",
debugFroxelVisualization);
FilamentApp::get().setFroxelGridEnabled(debugFroxelVisualization);
}
auto dataSource = debug.getDataSource("d.view.frame_info");