feat: re-implement grid overlay

This commit is contained in:
Nick Fisher
2024-12-17 14:00:41 +08:00
parent 852cb58ba9
commit 1b979252db
9 changed files with 2403 additions and 2165 deletions

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

View File

@@ -1,57 +1,118 @@
material {
name : Grid,
parameters : [
{
type : float,
name : maxDistance
type: float,
name: distance
},
{
type : float3,
name : color
type: float,
name: lineSize
}
],
depthWrite : false,
depthWrite : true,
depthCulling : true,
doubleSided: false,
shadingModel : unlit,
blending: opaque,
variantFilter : [ skinning, shadowReceiver, vsm ],
culling: none,
instanced: false,
vertexDomain: object
blending : transparent,
variantFilter : [ dynamicLighting, directionalLighting, shadowReceiver, skinning, ssr, stereo ],
culling : none,
instanced : false,
vertexDomain : object
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
vec4 modelSpace = getPosition();
vec4 worldSpace = getWorldFromModelMatrix() * modelSpace;
vec4 clipSpace = getClipFromWorldMatrix() * worldSpace;
clipSpace.z = 0.02f;
material.worldPosition = getWorldFromClipMatrix() * clipSpace;
vec3 position = getPosition().xyz;
position.xz *= materialParams.distance;
material.worldPosition.xz = position.xz;
}
}
fragment {
#include "shared.h"
/* the below has been adapted from Blender's overlay_grid_frag.glsl */
#define _1_DIV_SQRTPI 0.5641895835477563
#define RADIUS (_1_DIV_SQRTPI * 1.05)
#define GRID_START (0.5 + RADIUS)
#define GRID_END (0.5 - RADIUS)
#define GRID_STEP(dist) smoothstep(GRID_START, GRID_END, dist)
float getGrid(vec2 point, vec2 fwidthCos, vec2 gridScale, float lineSize) {
vec2 halfSize = gridScale / 2.0;
vec2 gridDomain = abs(mod(point + halfSize, gridScale) - halfSize);
gridDomain /= fwidthCos;
float lineDist = min(gridDomain.x, gridDomain.y);
return GRID_STEP(lineDist - lineSize);
}
vec3 getAxes(vec3 point, vec3 fwidthCos, float line_size)
{
vec3 axes_domain = abs(point);
axes_domain /= fwidthCos;
return GRID_STEP(axes_domain - (line_size + materialParams.lineSize));
}
void material(inout MaterialInputs material) {
prepareMaterial(material);
vec3 P = getWorldPosition().xyz;
vec3 dFdxPos = dFdx(P);
vec3 dFdyPos = dFdy(P);
vec3 fwidthPos = abs(dFdxPos) + abs(dFdyPos);
P += mulMat4x4Float3(getUserWorldFromWorldMatrix(), getWorldCameraPosition()).xyz;
vec3 V = getWorldPosition().xyz;
float dist = length(V);
V /= dist;
// Convert world position to view space
float4 viewPos = getViewFromWorldMatrix() * float4(getWorldPosition(), 1.0);
float angle = V.y;
angle = 1.0 - abs(angle);
angle *= angle;
float fade = 1.0 - angle;
fade *= 0.5 - smoothstep(0.0, materialParams.distance, dist - materialParams.distance);
float gridA = getGrid(P.xz, fwidthPos.xz, vec2(1.0, 1.0), materialParams.lineSize);
vec3 planeAxes = vec3(1.0f, 0.0f, 1.0f);
vec3 distanceToAxes = vec3(
dot(P.yz, planeAxes.yz),
0.0f,
dot(P.xy, planeAxes.xy)
);
vec3 dAxes = vec3(
dot(fwidthPos.yz, planeAxes.yz),
0.0f,
dot(fwidthPos.xy, planeAxes.xy)
);
vec3 axes = getAxes(distanceToAxes, dAxes, 0.1);
vec4 color = vec4(
0.1f,
0.1f,
0.1f,
gridA
);
color.a = max(color.a, axes.x);
color.rgb = (axes.x < 1e-8) ? color.rgb : AXIS_COLOR_X;
color.a = max(color.a, axes.z);
color.rgb = (axes.z < 1e-8) ? color.rgb : AXIS_COLOR_Z;
color.a *= fade;
material.baseColor = color;
// Calculate distance in view space (camera is at 0,0,0 in view space)
float distance = length(viewPos.xz);
// Discard fragment if it's too far from the camera
if (distance > materialParams.maxDistance) {
material.baseColor = float4(0.0);
} else {
material.baseColor = float4(materialParams.color, 1.0);
// Optional: fade out as we approach maxDistance
float fadeStart = materialParams.maxDistance * 0.8;
if (distance > fadeStart) {
float fade = 1.0 - (distance - fadeStart) / (materialParams.maxDistance - fadeStart);
material.baseColor.a = fade;
}
}
}
}

4
materials/shared.h Normal file
View File

@@ -0,0 +1,4 @@
#define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0))
#define AXIS_COLOR_X vec3(1.0f, 0.0f, 0.0f)
#define AXIS_COLOR_Y vec3(0.0f, 1.0f, 0.0f)
#define AXIS_COLOR_Z vec3(0.0f, 0.0f, 1.0f)

View File

@@ -8,5 +8,5 @@ GRID_PACKAGE:
GRID_GRID_OFFSET:
.int 0
GRID_GRID_SIZE:
.int 44564
.int 47596

View File

@@ -8,5 +8,5 @@ _GRID_PACKAGE:
_GRID_GRID_OFFSET:
.int 0
_GRID_GRID_SIZE:
.int 44564
.int 47596

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,8 @@
#include <filament/MaterialInstance.h>
#include <utils/Entity.h>
#include "scene/SceneAsset.hpp"
#include "material/grid.h"
namespace thermion {
@@ -15,7 +17,7 @@ using namespace filament;
class GridOverlay : public SceneAsset {
public:
GridOverlay(Engine& engine);
GridOverlay(Engine& engine, Material* material);
~GridOverlay();
SceneAssetType getType() override { return SceneAsset::SceneAssetType::Gizmo; }

View File

@@ -1,17 +1,14 @@
#include "scene/GridOverlay.hpp"
#include "scene/SceneManager.hpp"
#include "material/grid.h"
#include "Log.hpp"
namespace thermion
{
GridOverlay::GridOverlay(Engine &engine) : _engine(engine)
GridOverlay::GridOverlay(Engine &engine, Material *material) : _engine(engine), _material(material)
{
createGrid();
createSphere();
_childEntities[0] = _gridEntity;
_childEntities[1] = _sphereEntity;
}
GridOverlay::~GridOverlay()
@@ -31,88 +28,98 @@ namespace thermion
void GridOverlay::createGrid()
{
const int gridSize = 100;
const float gridSpacing = 1.0f;
int vertexCount = (gridSize + 1) * 4; // 2 axes, 2 vertices per line
const float stepSize = 0.25f;
const int gridSize = 8; // Number of grid cells in each direction (-1 to 1 with 0.25 step = 8 cells)
const int vertexCount = gridSize * gridSize * 4; // 4 vertices per grid cell
const int indexCount = gridSize * gridSize * 6; // 6 indices (2 triangles) per grid cell
float *gridVertices = new float[vertexCount * 3];
int index = 0;
std::vector<math::float3> *vertices = new std::vector<math::float3>();
std::vector<uint32_t> *indices = new std::vector<uint32_t>();
vertices->reserve(vertexCount);
indices->reserve(indexCount);
// Create grid lines
for (int i = 0; i <= gridSize; ++i)
// Generate grid vertices and indices
for (float x = -1.0f; x < 1.0f; x += stepSize)
{
float pos = i * gridSpacing - (gridSize * gridSpacing / 2);
for (float z = -1.0f; z < 1.0f; z += stepSize)
{
uint32_t baseIndex = vertices->size();
// X-axis lines
gridVertices[index++] = pos;
gridVertices[index++] = 0;
gridVertices[index++] = -(gridSize * gridSpacing / 2);
// Add four vertices for this grid cell
vertices->push_back({x, 0.0f, z}); // Bottom-left
vertices->push_back({x, 0.0f, z + stepSize}); // Top-left
vertices->push_back({x + stepSize, 0.0f, z + stepSize}); // Top-right
vertices->push_back({x + stepSize, 0.0f, z}); // Bottom-right
gridVertices[index++] = pos;
gridVertices[index++] = 0;
gridVertices[index++] = (gridSize * gridSpacing / 2);
// Z-axis lines
gridVertices[index++] = -(gridSize * gridSpacing / 2);
gridVertices[index++] = 0;
gridVertices[index++] = pos;
gridVertices[index++] = (gridSize * gridSpacing / 2);
gridVertices[index++] = 0;
gridVertices[index++] = pos;
// Add indices for two triangles
indices->push_back(baseIndex);
indices->push_back(baseIndex + 1);
indices->push_back(baseIndex + 2);
indices->push_back(baseIndex + 2);
indices->push_back(baseIndex + 3);
indices->push_back(baseIndex);
}
}
auto vb = VertexBuffer::Builder()
.vertexCount(vertexCount)
.vertexCount(vertices->size())
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
.build(_engine);
vb->setBufferAt(_engine, 0, VertexBuffer::BufferDescriptor(gridVertices, vertexCount * sizeof(math::float3), [](void *buffer, size_t size, void *)
{ delete[] static_cast<float *>(buffer); }));
uint32_t *gridIndices = new uint32_t[vertexCount];
for (uint32_t i = 0; i < vertexCount; ++i)
{
gridIndices[i] = i;
}
vb->setBufferAt(_engine, 0,
VertexBuffer::BufferDescriptor(
vertices->data(),
vertices->size() * sizeof(math::float3),
[](void *buffer, size_t size, void *user) {
delete static_cast<std::vector<math::float3>*>(user);
}, vertices));
auto ib = IndexBuffer::Builder()
.indexCount(vertexCount)
.indexCount(indices->size())
.bufferType(IndexBuffer::IndexType::UINT)
.build(_engine);
ib->setBuffer(_engine, IndexBuffer::BufferDescriptor(
gridIndices,
vertexCount * sizeof(uint32_t),
[](void *buffer, size_t size, void *)
{ delete[] static_cast<uint32_t *>(buffer); }));
ib->setBuffer(_engine,
IndexBuffer::BufferDescriptor(
indices->data(),
indices->size() * sizeof(uint32_t),
[](void *buffer, size_t size, void *user) {
delete static_cast<std::vector<uint32_t>*>(user);
}, indices));
_gridEntity = utils::EntityManager::get().create();
_material = Material::Builder()
.package(GRID_PACKAGE, GRID_GRID_SIZE)
.build(_engine);
_materialInstance = _material->createInstance();
_materialInstance->setParameter("maxDistance", 50.0f);
_materialInstance->setParameter("color", math::float3{0.05f, 0.05f, 0.05f});
// Set material parameters to match Dart implementation
_materialInstance->setParameter("distance", 10000.0f);
_materialInstance->setParameter("lineSize", 0.01f);
_materialInstance->setCullingMode(MaterialInstance::CullingMode::NONE);
RenderableManager::Builder(1)
.boundingBox({{-gridSize * gridSpacing / 2, 0, -gridSize * gridSpacing / 2},
{gridSize * gridSpacing / 2, 0, gridSize * gridSpacing / 2}})
.boundingBox({{-1.0f, -1.0f, -1.0f}, // Min point
{1.0f, 1.0f, 1.0f}}) // Max point
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, indices->size())
.material(0, _materialInstance)
.geometry(0, RenderableManager::PrimitiveType::LINES, vb, ib, 0, vertexCount)
.priority(7)
.priority(1)
.layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY)
.culling(true)
/*
We disable culling here because we calculate the quad's world-space coordinates
manually in the shader (see grid.mat). Without this, the quad would be culled before
rendered.
*/
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(_engine, _gridEntity);
_childEntities[0] = _gridEntity;
}
void GridOverlay::createSphere()
{
const float sphereRadius = 0.05f;
const float sphereRadius = 1.05f;
const int sphereSegments = 16;
const int sphereRings = 16;
@@ -195,14 +202,15 @@ namespace thermion
.receiveShadows(false)
.castShadows(false)
.build(_engine, _sphereEntity);
_childEntities[1] = _sphereEntity;
}
SceneAsset *GridOverlay::createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount)
{
auto instance = std::make_unique<GridOverlay>(_engine);
auto instance = std::make_unique<GridOverlay>(_engine, _material);
auto *raw = instance.get();
_instances.push_back(std::move(instance));
return reinterpret_cast<SceneAsset*>(raw);
return reinterpret_cast<SceneAsset *>(raw);
}
void GridOverlay::addAllEntities(Scene *scene)