17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-08 05:10:41 +01:00

Merge pull request #240 from miloszunio/fix/gl46-editor-cursor-depth

fix(gl46): editor cursor depth read in the biased depth pass (port of 7078edf)
This commit is contained in:
2026-11-07 19:58:31 +00:00
committed by GitHub
2 changed files with 86 additions and 27 deletions

View File

@@ -407,7 +407,7 @@ bool opengl46_renderer::Init(GLFWwindow *Window)
if (Global.iMultisampling)
{
m_depth_pointer_rb = std::make_unique<gl::renderbuffer>();
m_depth_pointer_rb->alloc(depth_pointer_format, 1, 1);
m_depth_pointer_rb->alloc(depth_pointer_format, 3, 3); // the pixel under the cursor and the ones around it
m_depth_pointer_fb = std::make_unique<gl::framebuffer>();
m_depth_pointer_fb->attach(*m_depth_pointer_rb, GL_DEPTH_ATTACHMENT);
@@ -5497,53 +5497,100 @@ void opengl46_renderer::Pick_Node_Callback(std::function<void(scene::basic_node
m_node_pick_requests.push_back(callback);
}
// the position of the cursor of the editor in the scene: the depth around it read back from an earlier frame, worked out in
// the view that frame was drawn in (the cursor and the camera may have moved since), and kept in world space
glm::dvec3 opengl46_renderer::Update_Mouse_Position()
{
if (!m_depth_pointer_pbo->is_busy())
{
// determine point to examine
glm::vec2 mousepos = Global.cursor_pos;
mousepos.y = Global.window_size.y - mousepos.y; // cursor coordinates are flipped compared to opengl
// the depths asked for before, around where the cursor was then
auto const count{m_cursorblocksize.x * m_cursorblocksize.y};
std::array<float, 9> depths{};
if ((count > 0) && (count <= static_cast<int>(depths.size())) && m_depth_pointer_pbo->read_data(m_cursorblocksize.x, m_cursorblocksize.y, depths.data(), 4))
{
auto const centre{m_cursorposition - m_cursorblockcorner};
auto const depth_at = [&](glm::ivec2 const Pixel) {
if ((Pixel.x < 0) || (Pixel.y < 0) || (Pixel.x >= m_cursorblocksize.x) || (Pixel.y >= m_cursorblocksize.y))
{
return 0.f;
}
return depths[static_cast<std::size_t>(Pixel.y * m_cursorblocksize.x + Pixel.x)];
};
float pointdepth{depth_at(centre)};
if (m_cursorbiased && (pointdepth > 0.f))
{
// read in the depth pass of the scene (Render_Scene_Depth()), which draws everything a little farther than it lies
// (glPolygonOffset(-1, -1)), so the colour pass passes on the same surface. the slope of the depth grows towards the
// horizon, and the ground seen at a grazing angle came out a pixel's worth of ground farther than it lies, metres off
// some way from the camera. the slope is taken back as in the vulkan renderer: the pixels next to it on the same triangle
// are moved by the same bias, so their difference is the slope; of the two sides the smaller, which is less likely
// to lie on another surface, and the larger of the two axes, the one implementations use for m. then the unit, r
auto const slope = [&](glm::ivec2 const Step) {
auto result{-1.f};
for (auto const side : {-1, 1})
{
auto const other{depth_at(centre + Step * side)};
if (other > 0.f)
{
auto const difference{std::abs(other - pointdepth)};
result = (result < 0.f ? difference : std::min(result, difference));
}
}
return std::max(result, 0.f);
};
pointdepth += std::max(slope({1, 0}), slope({0, 1})) + std::ldexp(1.f, std::ilogb(pointdepth) - 23);
}
m_mousehit = pointdepth > 0.0f;
if (m_mousehit)
{
auto const offset{glm::unProjectZO(glm::vec3(m_cursorposition, pointdepth), m_cursorview, m_cursorprojection, m_cursorviewport)};
m_worldmouseposition = m_cursorcamera + glm::dvec3{offset};
m_mouseknown = true;
}
}
glm::ivec2 bufferpos;
bufferpos = glm::ivec2(mousepos * glm::vec2(Global.gfx_framebuffer_width, Global.gfx_framebuffer_height) / glm::vec2(Global.window_size));
bufferpos = glm::clamp(bufferpos, glm::ivec2(0, 0), glm::ivec2(Global.gfx_framebuffer_width - 1, Global.gfx_framebuffer_height - 1));
float pointdepth = std::numeric_limits<float>::max();
m_depth_pointer_pbo->read_data(1, 1, &pointdepth, 4);
// ask for the depths around where the cursor is now
glm::vec2 mousepos = Global.cursor_pos;
mousepos.y = Global.window_size.y - mousepos.y; // cursor coordinates are flipped compared to opengl
glm::ivec2 const extent{Global.gfx_framebuffer_width, Global.gfx_framebuffer_height};
auto bufferpos{glm::ivec2(mousepos * glm::vec2(extent) / glm::max(glm::vec2(Global.window_size), glm::vec2(1.f)))};
bufferpos = glm::clamp(bufferpos, glm::ivec2(0, 0), extent - 1);
// the pixels around the one under the cursor too, for the slope of the depth there
auto const blocksize{glm::clamp(extent, glm::ivec2(1), glm::ivec2(3))};
auto const blockcorner{glm::clamp(bufferpos - 1, glm::ivec2(0), extent - blocksize)};
if (!Global.iMultisampling)
{
m_depth_pointer_pbo->request_read(bufferpos.x, bufferpos.y, 1, 1, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_depth_pointer_pbo->request_read(blockcorner.x, blockcorner.y, blocksize.x, blocksize.y, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
}
else if (Global.gfx_skippipeline)
{
gl::framebuffer::blit(nullptr, m_depth_pointer_fb.get(), bufferpos.x, bufferpos.y, 1, 1, GL_DEPTH_BUFFER_BIT, 0);
gl::framebuffer::blit(nullptr, m_depth_pointer_fb.get(), blockcorner.x, blockcorner.y, blocksize.x, blocksize.y, GL_DEPTH_BUFFER_BIT, 0);
m_depth_pointer_fb->bind();
m_depth_pointer_pbo->request_read(0, 0, 1, 1, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_depth_pointer_pbo->request_read(0, 0, blocksize.x, blocksize.y, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_depth_pointer_fb->unbind();
}
else
{
gl::framebuffer::blit(m_viewports.front()->msaa_fb.get(), m_depth_pointer_fb.get(), bufferpos.x, bufferpos.y, 1, 1, GL_DEPTH_BUFFER_BIT, 0);
gl::framebuffer::blit(m_viewports.front()->msaa_fb.get(), m_depth_pointer_fb.get(), blockcorner.x, blockcorner.y, blocksize.x, blocksize.y, GL_DEPTH_BUFFER_BIT, 0);
m_depth_pointer_fb->bind();
m_depth_pointer_pbo->request_read(0, 0, 1, 1, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_depth_pointer_pbo->request_read(0, 0, blocksize.x, blocksize.y, 4, GL_DEPTH_COMPONENT, GL_FLOAT);
m_viewports.front()->msaa_fb->bind();
}
if (pointdepth != std::numeric_limits<float>::max())
{
m_mousehit = pointdepth > 0.0f;
if (pointdepth > 0.0f)
m_worldmousecoordinates = glm::unProjectZO(glm::vec3(bufferpos, pointdepth), glm::mat4(glm::mat3(m_colorpass.pass_camera.modelview())), m_colorpass.pass_camera.projection(),
glm::vec4(0, 0, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height));
}
// and the view it's drawn in, for when the depths come back
m_cursorposition = bufferpos;
m_cursorblockcorner = blockcorner;
m_cursorblocksize = blocksize;
m_cursorbiased = m_scenedepthpass;
m_cursorview = glm::mat4(glm::mat3(m_colorpass.pass_camera.modelview()));
m_cursorprojection = m_colorpass.pass_camera.projection();
m_cursorcamera = m_colorpass.pass_camera.position();
m_cursorviewport = glm::vec4(0, 0, extent.x, extent.y);
}
return m_colorpass.pass_camera.position() + glm::dvec3{m_worldmousecoordinates};
return m_colorpass.pass_camera.position() + Mouse_Position();
}
void opengl46_renderer::Update(double const Deltatime)

View File

@@ -150,7 +150,7 @@ class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
void
Forget_Node( scene::basic_node const *Node ) override;
glm::dvec3
Mouse_Position() const override { return m_worldmousecoordinates; }
Mouse_Position() const override { return m_mouseknown ? m_worldmouseposition - m_colorpass.pass_camera.position() : glm::dvec3 { 0.0 }; }
bool
Mouse_Hit() const override { return m_mousehit; }
glm::mat4
@@ -748,8 +748,20 @@ class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
std::array<renderpass_config, 3> m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages
TSubModel const *m_pickcontrolitem{nullptr};
scene::basic_node *m_picksceneryitem{nullptr};
glm::vec3 m_worldmousecoordinates { 0.f };
// the point of the scene under the cursor of the editor, in world space (Update_Mouse_Position())
glm::dvec3 m_worldmouseposition { 0.0 };
bool m_mouseknown { false }; // whether the cursor hit anything yet
bool m_mousehit { false };
// the depths around the cursor asked of the gpu: where they lie in the image, whether they come from the depth pass of the
// scene (drawn a little farther than it lies), and the view they were drawn in
glm::ivec2 m_cursorposition { 0 };
glm::ivec2 m_cursorblockcorner { 0 };
glm::ivec2 m_cursorblocksize { 0 };
bool m_cursorbiased { false };
glm::mat4 m_cursorview { 1.f };
glm::mat4 m_cursorprojection { 1.f };
glm::dvec3 m_cursorcamera { 0.0 };
glm::vec4 m_cursorviewport { 0.f };
#ifdef EU07_USE_DEBUG_CAMERA
renderpass_config m_worldcamera; // debug item
#endif