| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- /*
- * Copyright 2016 Joseph Cherlin. All rights reserved.
- * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
- */
- #include "common.h"
- #include "bgfx_utils.h"
- #include "imgui/imgui.h"
- #include <bx/rng.h>
- #include <map>
- namespace
- {
- #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
- {
- public:
- ExamplePicking(const char* _name, const char* _description, const char* _url)
- : entry::AppI(_name, _description, _url)
- {
- }
- void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
- {
- Args args(_argc, _argv);
- m_width = _width;
- m_height = _height;
- m_debug = BGFX_DEBUG_NONE;
- m_reset = BGFX_RESET_VSYNC;
- bgfx::Init init;
- init.type = args.m_type;
- init.vendorId = args.m_pciId;
- init.platformData.nwh = entry::getNativeWindowHandle(entry::kDefaultWindowHandle);
- init.platformData.ndt = entry::getNativeDisplayHandle();
- init.platformData.type = entry::getNativeWindowHandleType(entry::kDefaultWindowHandle);
- init.resolution.width = m_width;
- init.resolution.height = m_height;
- init.resolution.reset = m_reset;
- bgfx::init(init);
- // 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 represents 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, false, 1, bgfx::TextureFormat::RGBA8, 0
- | BGFX_TEXTURE_RT
- | BGFX_SAMPLER_MIN_POINT
- | BGFX_SAMPLER_MAG_POINT
- | BGFX_SAMPLER_MIP_POINT
- | BGFX_SAMPLER_U_CLAMP
- | BGFX_SAMPLER_V_CLAMP
- );
- m_pickingRTDepth = bgfx::createTexture2D(ID_DIM, ID_DIM, false, 1, bgfx::TextureFormat::D32F, 0
- | BGFX_TEXTURE_RT
- | BGFX_SAMPLER_MIN_POINT
- | BGFX_SAMPLER_MAG_POINT
- | BGFX_SAMPLER_MIP_POINT
- | BGFX_SAMPLER_U_CLAMP
- | BGFX_SAMPLER_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, false, 1, bgfx::TextureFormat::RGBA8, 0
- | BGFX_TEXTURE_BLIT_DST
- | BGFX_TEXTURE_READ_BACK
- | BGFX_SAMPLER_MIN_POINT
- | BGFX_SAMPLER_MAG_POINT
- | BGFX_SAMPLER_MIP_POINT
- | BGFX_SAMPLER_U_CLAMP
- | BGFX_SAMPLER_V_CLAMP
- );
- bgfx::TextureHandle rt[2] =
- {
- m_pickingRT,
- m_pickingRTDepth
- };
- m_pickingFB = bgfx::createFrameBuffer(BX_COUNTOF(rt), rt, true);
- imguiCreate();
- }
- int shutdown() override
- {
- for (uint32_t ii = 0; ii < 12; ++ii)
- {
- meshUnload(m_meshes[ii]);
- }
- // Cleanup.
- bgfx::destroy(m_shadingProgram);
- bgfx::destroy(m_idProgram);
- bgfx::destroy(u_tint);
- bgfx::destroy(u_id);
- bgfx::destroy(m_pickingFB);
- bgfx::destroy(m_pickingRT);
- bgfx::destroy(m_pickingRTDepth);
- bgfx::destroy(m_blitTex);
- imguiDestroy();
- // Shutdown bgfx.
- bgfx::shutdown();
- return 0;
- }
- bool update() override
- {
- if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
- {
- // 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
- , uint16_t(m_width)
- , uint16_t(m_height)
- );
- const bgfx::Caps* caps = bgfx::getCaps();
- bool blitSupport = 0 != (caps->supported & BGFX_CAPS_TEXTURE_BLIT);
- showExampleDialog(this
- , !blitSupport
- ? "BGFX_CAPS_TEXTURE_BLIT is not supported."
- : NULL
- );
- if (blitSupport)
- {
- ImGui::SetNextWindowPos(
- ImVec2(m_width - m_width / 5.0f - 10.0f, 10.0f)
- , ImGuiCond_FirstUseEver
- );
- ImGui::SetNextWindowSize(
- ImVec2(m_width / 5.0f, m_height / 2.0f)
- , ImGuiCond_FirstUseEver
- );
- ImGui::Begin("Settings"
- , NULL
- , 0
- );
- ImGui::Image(m_pickingRT, ImVec2(m_width / 5.0f - 16.0f, m_width / 5.0f - 16.0f) );
- ImGui::SliderFloat("Field of view", &m_fov, 1.0f, 60.0f);
- ImGui::Checkbox("Spin Camera", &m_cameraSpin);
- ImGui::End();
- bgfx::setViewFrameBuffer(RENDER_PASS_ID, m_pickingFB);
- float time = (float)( (bx::getHPCounter() - m_timeOffset) / double(bx::getHPFrequency() ) );
- // Set up matrices for basic forward renderer
- const float camSpeed = 0.25;
- float cameraSpin = (float)m_cameraSpin;
- float eyeDist = 2.5f;
- const bx::Vec3 at = { 0.0f, 0.0f, 0.0f };
- const bx::Vec3 eye =
- {
- -eyeDist * bx::sin(time*cameraSpin*camSpeed),
- 0.0f,
- -eyeDist * bx::cos(time*cameraSpin*camSpeed),
- };
- 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, caps->homogeneousDepth);
- // 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 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;
- const bx::Vec3 pickEye = bx::mulH({ mouseXNDC, mouseYNDC, 0.0f }, invViewProj);
- const bx::Vec3 pickAt = bx::mulH({ mouseXNDC, mouseYNDC, 1.0f }, invViewProj);
- // Look at our unprojected point
- float pickView[16];
- bx::mtxLookAt(pickView, pickEye, pickAt);
- // Tight FOV is best for picking
- float pickProj[16];
- bx::mtxProj(pickProj, m_fov, 1, 0.1f, 100.0f, caps->homogeneousDepth);
- // 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++;
- 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);
- }
- }
- imguiEndFrame();
- // Advance to next frame. Rendering thread will be kicked to
- // process submitted rendering primitives.
- m_currFrame = bgfx::frame();
- return true;
- }
- return false;
- }
- entry::MouseState m_mouseState;
- uint32_t m_width;
- uint32_t m_height;
- uint32_t m_debug;
- uint32_t m_reset;
- int64_t m_timeOffset;
- 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;
- };
- } // namespace
- ENTRY_IMPLEMENT_MAIN(
- ExamplePicking
- , "30-picking"
- , "Mouse picking via GPU texture readback."
- , "https://bkaradzic.github.io/bgfx/examples.html#picking"
- );
|