summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/bgfx/examples/30-picking/picking.cpp
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2016-08-06 14:20:16 +0200
committer Miodrag Milanovic <mmicko@gmail.com>2016-08-06 14:20:16 +0200
commit3917850b6197950e5671f4d3b9cb0b9c921b798d (patch)
tree503b9690868a7c238ba10d73b5c6bece7ca8c338 /3rdparty/bgfx/examples/30-picking/picking.cpp
parent057474989c1a92e9e935dbc8cb9d233ba3a4ea3a (diff)
Update BX and BGFX (nw)
Diffstat (limited to '3rdparty/bgfx/examples/30-picking/picking.cpp')
-rw-r--r--3rdparty/bgfx/examples/30-picking/picking.cpp423
1 files changed, 423 insertions, 0 deletions
diff --git a/3rdparty/bgfx/examples/30-picking/picking.cpp b/3rdparty/bgfx/examples/30-picking/picking.cpp
new file mode 100644
index 00000000000..b6419a884bc
--- /dev/null
+++ b/3rdparty/bgfx/examples/30-picking/picking.cpp
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "common.h"
+#include "bgfx_utils.h"
+#include "imgui/imgui.h"
+#include <bx/rng.h>
+#include <map>
+
+#define RENDER_PASS_SHADING 0 // Default forward rendered geo with simple shading
+#define RENDER_PASS_ID 1 // ID buffer for picking
+#define RENDER_PASS_BLIT 2 // Blit GPU render target to CPU texture
+
+#define ID_DIM 8 // Size of the ID buffer
+
+class ExamplePicking : public entry::AppI
+{
+ void init(int _argc, char** _argv) BX_OVERRIDE
+ {
+ Args args(_argc, _argv);
+
+ m_width = 1280;
+ m_height = 720;
+ m_debug = BGFX_DEBUG_TEXT;
+ m_reset = BGFX_RESET_VSYNC;
+
+ bgfx::init(args.m_type, args.m_pciId);
+
+ bgfx::reset(m_width, m_height, m_reset);
+
+ // Enable debug text.
+ bgfx::setDebug(m_debug);
+
+ // Set up screen clears
+ bgfx::setViewClear(RENDER_PASS_SHADING
+ , BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH
+ , 0x303030ff
+ , 1.0f
+ , 0
+ );
+
+ // ID buffer clears to black, which represnts clicking on nothing (background)
+ bgfx::setViewClear(RENDER_PASS_ID
+ , BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH
+ , 0x000000ff
+ , 1.0f
+ , 0
+ );
+
+ // Create uniforms
+ u_tint = bgfx::createUniform("u_tint", bgfx::UniformType::Vec4); // Tint for when you click on items
+ u_id = bgfx::createUniform("u_id", bgfx::UniformType::Vec4); // ID for drawing into ID buffer
+
+ // Create program from shaders.
+ m_shadingProgram = loadProgram("vs_picking_shaded", "fs_picking_shaded"); // Blinn shading
+ m_idProgram = loadProgram("vs_picking_shaded", "fs_picking_id"); // Shader for drawing into ID buffer
+
+ static const char* meshPaths[] =
+ {
+ "meshes/orb.bin",
+ "meshes/column.bin",
+ "meshes/bunny.bin",
+ "meshes/cube.bin",
+ "meshes/tree.bin",
+ "meshes/hollowcube.bin",
+ };
+
+ static const float meshScale[] =
+ {
+ 0.5f,
+ 0.05f,
+ 0.5f,
+ 0.25f,
+ 0.05f,
+ 0.05f,
+ };
+
+ m_highlighted = UINT32_MAX;
+ m_reading = 0;
+ m_currFrame = UINT32_MAX;
+ m_fov = 3.0f;
+ m_cameraSpin = false;
+
+ bx::RngMwc mwc; // Random number generator
+ for (uint32_t ii = 0; ii < 12; ++ii)
+ {
+ m_meshes[ii] = meshLoad(meshPaths[ii % BX_COUNTOF(meshPaths)]);
+ m_meshScale[ii] = meshScale[ii % BX_COUNTOF(meshPaths)];
+ // For the sake of this example, we'll give each mesh a random color, so the debug output looks colorful.
+ // In an actual app, you'd probably just want to count starting from 1
+ uint32_t rr = mwc.gen() % 256;
+ uint32_t gg = mwc.gen() % 256;
+ uint32_t bb = mwc.gen() % 256;
+ m_idsF[ii][0] = rr / 255.0f;
+ m_idsF[ii][1] = gg / 255.0f;
+ m_idsF[ii][2] = bb / 255.0f;
+ m_idsF[ii][3] = 1.0f;
+ m_idsU[ii] = rr + (gg << 8) + (bb << 16) + (255u << 24);
+ }
+
+ m_timeOffset = bx::getHPCounter();
+
+ // Set up ID buffer, which has a color target and depth buffer
+ m_pickingRT = bgfx::createTexture2D(ID_DIM, ID_DIM, 1, bgfx::TextureFormat::RGBA8, 0
+ | BGFX_TEXTURE_RT
+ | BGFX_TEXTURE_MIN_POINT
+ | BGFX_TEXTURE_MAG_POINT
+ | BGFX_TEXTURE_MIP_POINT
+ | BGFX_TEXTURE_U_CLAMP
+ | BGFX_TEXTURE_V_CLAMP
+ );
+ m_pickingRTDepth = bgfx::createTexture2D(ID_DIM, ID_DIM, 1, bgfx::TextureFormat::D24S8, 0
+ | BGFX_TEXTURE_RT
+ | BGFX_TEXTURE_MIN_POINT
+ | BGFX_TEXTURE_MAG_POINT
+ | BGFX_TEXTURE_MIP_POINT
+ | BGFX_TEXTURE_U_CLAMP
+ | BGFX_TEXTURE_V_CLAMP
+ );
+
+ // CPU texture for blitting to and reading ID buffer so we can see what was clicked on.
+ // Impossible to read directly from a render target, you *must* blit to a CPU texture
+ // first. Algorithm Overview: Render on GPU -> Blit to CPU texture -> Read from CPU
+ // texture.
+ m_blitTex = bgfx::createTexture2D(ID_DIM, ID_DIM, 1, bgfx::TextureFormat::RGBA8, 0
+ | BGFX_TEXTURE_BLIT_DST
+ | BGFX_TEXTURE_READ_BACK
+ | BGFX_TEXTURE_MIN_POINT
+ | BGFX_TEXTURE_MAG_POINT
+ | BGFX_TEXTURE_MIP_POINT
+ | BGFX_TEXTURE_U_CLAMP
+ | BGFX_TEXTURE_V_CLAMP
+ );
+
+ bgfx::TextureHandle rt[2] =
+ {
+ m_pickingRT,
+ m_pickingRTDepth
+ };
+ m_pickingFB = bgfx::createFrameBuffer(BX_COUNTOF(rt), rt, true);
+
+ imguiCreate();
+ }
+
+ int shutdown() BX_OVERRIDE
+ {
+ for (uint32_t ii = 0; ii < 12; ++ii)
+ {
+ meshUnload(m_meshes[ii]);
+ }
+
+ // Cleanup.
+ bgfx::destroyProgram(m_shadingProgram);
+ bgfx::destroyProgram(m_idProgram);
+
+ bgfx::destroyUniform(u_tint);
+ bgfx::destroyUniform(u_id);
+
+ bgfx::destroyFrameBuffer(m_pickingFB);
+ bgfx::destroyTexture(m_pickingRT);
+ bgfx::destroyTexture(m_pickingRTDepth);
+ bgfx::destroyTexture(m_blitTex);
+
+ imguiDestroy();
+
+ // Shutdown bgfx.
+ bgfx::shutdown();
+
+ return 0;
+ }
+
+ bool update() BX_OVERRIDE
+ {
+ if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
+ {
+ bgfx::setViewFrameBuffer(RENDER_PASS_ID, m_pickingFB);
+
+ int64_t now = bx::getHPCounter();
+ static int64_t last = now;
+ const int64_t frameTime = now - last;
+ last = now;
+ const double freq = double(bx::getHPFrequency());
+ const double toMs = 1000.0 / freq;
+ float time = (float)( (bx::getHPCounter() - m_timeOffset) / double(bx::getHPFrequency() ) );
+
+ // Use debug font to print information about this example.
+ bgfx::dbgTextClear();
+ bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/30-picking");
+ bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Mouse picking via GPU texture readback.");
+ bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);
+
+ // Set up matrices for basic forward renderer
+ const float camSpeed = 0.25;
+ float cameraSpin = (float)m_cameraSpin;
+ float eyeDist = 2.5f;
+ float eye[3] =
+ {
+ -eyeDist * bx::fsin(time*cameraSpin*camSpeed),
+ 0.0f,
+ -eyeDist * bx::fcos(time*cameraSpin*camSpeed),
+ };
+ float at[3] = { 0.0f, 0.0f, 0.0f };
+
+ float view[16];
+ bx::mtxLookAt(view, eye, at);
+
+ float proj[16];
+ bx::mtxProj(proj, 60.0f, float(m_width) / float(m_height), 0.1f, 100.0f);
+
+ // Set up view rect and transform for the shaded pass
+ bgfx::setViewRect(RENDER_PASS_SHADING, 0, 0, uint16_t(m_width), uint16_t(m_height) );
+ bgfx::setViewTransform(RENDER_PASS_SHADING, view, proj);
+
+ // Set up picking pass
+ float pickView[16];
+ float pickAt[4]; // Need to inversly project the mouse pointer to determin what we're looking at
+ float pickEye[3] = { eye[0], eye[1], eye[2] }; // Eye is same location as before
+ float viewProj[16];
+ bx::mtxMul(viewProj, view, proj);
+ float invViewProj[16];
+ bx::mtxInverse(invViewProj, viewProj);
+ // Mouse coord in NDC
+ float mouseXNDC = (m_mouseState.m_mx / (float)m_width) * 2.0f - 1.0f;
+ float mouseYNDC = ((m_height - m_mouseState.m_my) / (float)m_height) * 2.0f - 1.0f;
+ float mousePosNDC[4] = { mouseXNDC, mouseYNDC, 0, 1.0f };
+ // Unproject and perspective divide
+ bx::vec4MulMtx(pickAt, mousePosNDC, invViewProj);
+ pickAt[3] = 1.0f / pickAt[3];
+ pickAt[0] *= pickAt[3];
+ pickAt[1] *= pickAt[3];
+ pickAt[2] *= pickAt[3];
+
+ // Look at our unprojected point
+ bx::mtxLookAt(pickView, pickEye, pickAt);
+ float pickProj[16];
+
+ // Tight FOV is best for picking
+ bx::mtxProj(pickProj, m_fov, 1, 0.1f, 100.0f);
+ // View rect and transforms for picking pass
+ bgfx::setViewRect(RENDER_PASS_ID, 0, 0, ID_DIM, ID_DIM);
+ bgfx::setViewTransform(RENDER_PASS_ID, pickView, pickProj);
+
+ // Now that our passes are set up, we can finally draw each mesh
+
+ // Picking highlights a mesh so we'll set up this tint color
+ const float tintBasic[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ const float tintHighlighted[4] = { 0.3f, 0.3f, 2.0f, 1.0f };
+
+ for (uint32_t mesh = 0; mesh < 12; ++mesh)
+ {
+ const float scale = m_meshScale[mesh];
+
+ // Set up transform matrix for each mesh
+ float mtx[16];
+ bx::mtxSRT(mtx
+ , scale, scale, scale
+ , 0.0f
+ , time*0.37f*(mesh % 2 ? 1.0f : -1.0f)
+ , 0.0f
+ , (mesh % 4) - 1.5f
+ , (mesh / 4) - 1.25f
+ , 0.0f
+ );
+
+ // Submit mesh to both of our render passes
+ // Set uniform based on if this is the highlighted mesh
+ bgfx::setUniform(u_tint
+ , mesh == m_highlighted
+ ? tintHighlighted
+ : tintBasic
+ );
+ meshSubmit(m_meshes[mesh], RENDER_PASS_SHADING, m_shadingProgram, mtx);
+
+ // Submit ID pass based on mesh ID
+ bgfx::setUniform(u_id, m_idsF[mesh]);
+ meshSubmit(m_meshes[mesh], RENDER_PASS_ID, m_idProgram, mtx);
+ }
+
+ // If the user previously clicked, and we're done reading data from GPU, look at ID buffer on CPU
+ // Whatever mesh has the most pixels in the ID buffer is the one the user clicked on.
+ if (m_reading == m_currFrame)
+ {
+ m_reading = 0;
+ std::map<uint32_t, uint32_t> ids; // This contains all the IDs found in the buffer
+ uint32_t maxAmount = 0;
+ for (uint8_t *x = m_blitData; x < m_blitData + ID_DIM * ID_DIM * 4;)
+ {
+ uint8_t rr = *x++;
+ uint8_t gg = *x++;
+ uint8_t bb = *x++;
+ uint8_t aa = *x++;
+
+ const bgfx::Caps* caps = bgfx::getCaps();
+ if (bgfx::RendererType::Direct3D9 == caps->rendererType)
+ {
+ // Comes back as BGRA
+ uint8_t temp = rr;
+ rr = bb;
+ bb = temp;
+ }
+
+ if (0 == (rr|gg|bb) ) // Skip background
+ {
+ continue;
+ }
+
+ uint32_t hashKey = rr + (gg << 8) + (bb << 16) + (aa << 24);
+ std::map<uint32_t, uint32_t>::iterator mapIter = ids.find(hashKey);
+ uint32_t amount = 1;
+ if (mapIter != ids.end() )
+ {
+ amount = mapIter->second + 1;
+ }
+
+ ids[hashKey] = amount; // Amount of times this ID (color) has been clicked on in buffer
+ maxAmount = maxAmount > amount
+ ? maxAmount
+ : amount
+ ;
+ }
+
+ uint32_t idKey = 0;
+ m_highlighted = UINT32_MAX;
+ if (maxAmount)
+ {
+ for (std::map<uint32_t, uint32_t>::iterator mapIter = ids.begin(); mapIter != ids.end(); mapIter++)
+ {
+ if (mapIter->second == maxAmount)
+ {
+ idKey = mapIter->first;
+ break;
+ }
+ }
+
+ for (uint32_t ii = 0; ii < 12; ++ii)
+ {
+ if (m_idsU[ii] == idKey)
+ {
+ m_highlighted = ii;
+ break;
+ }
+ }
+ }
+ }
+
+ // Start a new readback?
+ if (!m_reading
+ && m_mouseState.m_buttons[entry::MouseButton::Left])
+ {
+ // Blit and read
+ bgfx::blit(RENDER_PASS_BLIT, m_blitTex, 0, 0, m_pickingRT);
+ m_reading = bgfx::readTexture(m_blitTex, m_blitData);
+ }
+
+ // Draw UI
+ imguiBeginFrame(m_mouseState.m_mx
+ , m_mouseState.m_my
+ , (m_mouseState.m_buttons[entry::MouseButton::Left] ? IMGUI_MBUT_LEFT : 0)
+ | (m_mouseState.m_buttons[entry::MouseButton::Right] ? IMGUI_MBUT_RIGHT : 0)
+ | (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
+ , m_mouseState.m_mz
+ , m_width
+ , m_height
+ );
+
+ imguiBeginArea("Picking Render Target:", 10, 100, 300, 400);
+ imguiImage(m_pickingRT, 1.0f, 1.0f, 1.0f);
+ imguiSlider("FOV", m_fov, 1.0f, 60.0f, 1.0f);
+
+ if (imguiCheck("Spin Camera", m_cameraSpin))
+ {
+ m_cameraSpin = !m_cameraSpin;
+ }
+
+ imguiEndArea();
+ imguiEndFrame();
+
+ // Advance to next frame. Rendering thread will be kicked to
+ // process submitted rendering primitives.
+ m_currFrame = bgfx::frame();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ uint32_t m_width;
+ uint32_t m_height;
+ uint32_t m_debug;
+ uint32_t m_reset;
+ int64_t m_timeOffset;
+
+ entry::MouseState m_mouseState;
+
+ Mesh* m_meshes[12];
+ float m_meshScale[12];
+ float m_idsF[12][4];
+ uint32_t m_idsU[12];
+ uint32_t m_highlighted;
+
+ // Resource handles
+ bgfx::ProgramHandle m_shadingProgram;
+ bgfx::ProgramHandle m_idProgram;
+ bgfx::UniformHandle u_tint;
+ bgfx::UniformHandle u_id;
+ bgfx::TextureHandle m_pickingRT;
+ bgfx::TextureHandle m_pickingRTDepth;
+ bgfx::TextureHandle m_blitTex;
+ bgfx::FrameBufferHandle m_pickingFB;
+
+ uint8_t m_blitData[ID_DIM*ID_DIM * 4]; // Read blit into this
+
+ uint32_t m_reading;
+ uint32_t m_currFrame;
+
+ float m_fov;
+ bool m_cameraSpin;
+};
+
+ENTRY_IMPLEMENT_MAIN(ExamplePicking);