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:
committed by
Mathias Agopian
parent
cea178a40c
commit
15bb295ec6
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
71
libs/filamentapp/include/filamentapp/Grid.h
Normal file
71
libs/filamentapp/include/filamentapp/Grid.h
Normal 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
|
||||
@@ -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, ¢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,
|
||||
|
||||
204
libs/filamentapp/src/Grid.cpp
Normal file
204
libs/filamentapp/src/Grid.cpp
Normal 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);
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user