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");