From 15bb295ec644246fd7defbda8ab8d4e49a8c7995 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 15 Aug 2025 16:48:04 -0700 Subject: [PATCH] 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. --- filament/include/filament/View.h | 22 ++ filament/src/Froxelizer.cpp | 35 ++- filament/src/Froxelizer.h | 10 +- filament/src/View.cpp | 4 + filament/src/details/View.cpp | 8 +- filament/src/details/View.h | 3 + filament/test/filament_test.cpp | 2 +- libs/filamentapp/CMakeLists.txt | 2 + .../include/filamentapp/FilamentApp.h | 13 ++ libs/filamentapp/include/filamentapp/Grid.h | 71 ++++++ libs/filamentapp/src/FilamentApp.cpp | 101 +++++++-- libs/filamentapp/src/Grid.cpp | 204 ++++++++++++++++++ samples/gltf_viewer.cpp | 10 + 13 files changed, 458 insertions(+), 27 deletions(-) create mode 100644 libs/filamentapp/include/filamentapp/Grid.h create mode 100644 libs/filamentapp/src/Grid.cpp diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index 3876c41c62..3c4dc5b4ca 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -27,6 +27,7 @@ #include #include +#include #include @@ -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 diff --git a/filament/src/Froxelizer.cpp b/filament/src/Froxelizer.cpp index 9608968fef..73f9c4d67b 100644 --- a/filament/src/Froxelizer.cpp +++ b/filament/src/Froxelizer.cpp @@ -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 diff --git a/filament/src/Froxelizer.h b/filament/src/Froxelizer.h index 440ded442f..72dae5bc96 100644 --- a/filament/src/Froxelizer.h +++ b/filament/src/Froxelizer.h @@ -22,6 +22,7 @@ #include "details/Scene.h" #include "details/Engine.h" +#include #include #include @@ -31,6 +32,7 @@ #include #include +#include #include 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; diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 0ca0c9d95c..c874926b47 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -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); } diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index ba85066571..c9c205fdc0 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -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"; diff --git a/filament/src/details/View.h b/filament/src/details/View.h index be862de9bb..48b77b72fa 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -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; diff --git a/filament/test/filament_test.cpp b/filament/test/filament_test.cpp index 7bd3d3445a..fa83060a4d 100644 --- a/filament/test/filament_test.cpp +++ b/filament/test/filament_test.cpp @@ -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); diff --git a/libs/filamentapp/CMakeLists.txt b/libs/filamentapp/CMakeLists.txt index 47af478783..2af83c3f18 100644 --- a/libs/filamentapp/CMakeLists.txt +++ b/libs/filamentapp/CMakeLists.txt @@ -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 diff --git a/libs/filamentapp/include/filamentapp/FilamentApp.h b/libs/filamentapp/include/filamentapp/FilamentApp.h index 9f94872044..701775834d 100644 --- a/libs/filamentapp/include/filamentapp/FilamentApp.h +++ b/libs/filamentapp/include/filamentapp/FilamentApp.h @@ -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; diff --git a/libs/filamentapp/include/filamentapp/Grid.h b/libs/filamentapp/include/filamentapp/Grid.h new file mode 100644 index 0000000000..d5d927435b --- /dev/null +++ b/libs/filamentapp/include/filamentapp/Grid.h @@ -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 +#include +#include +#include +#include + +#include +#include + +#include + +#include + +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; + + 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 diff --git a/libs/filamentapp/src/FilamentApp.cpp b/libs/filamentapp/src/FilamentApp.cpp index 150abeefca..406a711a31 100644 --- a/libs/filamentapp/src/FilamentApp.cpp +++ b/libs/filamentapp/src/FilamentApp.cpp @@ -55,6 +55,7 @@ #include #include +#include #include #include @@ -179,6 +180,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback, .build(*mEngine); std::unique_ptr cameraCube{ new Cube(*mEngine, mTransparentMaterial, { 1, 0, 0 }) }; + std::unique_ptr 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, ¢er, &up); window->mMainCamera->lookAt(eye, center, up); + window->mDebugCameraMan->getLookAt(&eye, ¢er, &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, diff --git a/libs/filamentapp/src/Grid.cpp b/libs/filamentapp/src/Grid.cpp new file mode 100644 index 0000000000..055592c629 --- /dev/null +++ b/libs/filamentapp/src/Grid.cpp @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +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 vertices; + std::vector 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); +} diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 55c12bdfb9..5172709f49 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -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");