/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2018 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*/
#pragma once
#include
#include
#include
#include
#include
#include
#include
namespace {
const char* render_view_normals = R"(
#version 330
#pragma vp_function oeui_render_view_normals, fragment_lighting, last
in vec3 vp_Normal;
void oeui_render_view_normals(inout vec4 color) {
color = vec4((vp_Normal+1.0)*0.5, 1);
}
)";
const char* render_model_normals = R"(
#version 330
#pragma vp_function oeui_render_model_normals_vs, vertex_model, last
out vec3 vp_Normal;
out vec3 oeui_model_normal;
void oeui_render_model_normals_vs(inout vec4 vertex) {
oeui_model_normal = vp_Normal;
}
[break]
#version 330
#pragma vp_function oeui_render_view_normals_fs, fragment_lighting, last
in vec3 oeui_model_normal;
void oeui_render_view_normals_fs(inout vec4 color) {
color = vec4( (normalize(oeui_model_normal)+1.0)*0.5, 1);
}
)";
const char* render_fb_normals = R"(
#version 330
#pragma vp_function oeui_render_fb_normals, fragment_lighting, last
in vec3 vp_Normal;
void oeui_render_fb_normals(inout vec4 color) {
float a = step(0.5, color.a);
float nz = normalize(vp_Normal).z;
color.rgb = mix(vec3(0,0,0), vec3(1,1,1), (nz+1.0)*0.5);
color = vec4(color.rgb, a);
}
)";
const char* render_winding = R"(
#version 450
#extension GL_NV_fragment_shader_barycentric : enable
#pragma vp_function oeui_render_winding_fs, fragment_lighting, last
void oeui_render_winding_fs(inout vec4 color) {
color.rgb = gl_FrontFacing ? vec3(0,0.75,0) : vec3(1,0,0);
float b = min(gl_BaryCoordNV.x, min(gl_BaryCoordNV.y, gl_BaryCoordNV.z))*28.0;
color = vec4(mix(vec3(1), color.rgb, clamp(b,0,1)), 1.0);
}
)";
const char* render_outlines = R"(
#version 450
#extension GL_NV_fragment_shader_barycentric : enable
#pragma vp_function oeui_render_outlines, fragment_lighting, last
#define VP_STAGE_FRAGMENT
void oeui_render_outlines(inout vec4 color) {
float b = min(gl_BaryCoordNV.x, min(gl_BaryCoordNV.y, gl_BaryCoordNV.z))*32.0;
float mono = dot(color.rgb, vec3(0.299, 0.587, 0.114));
mono = mod(mono + 0.25, 1.0);
color = vec4(mix(vec3(mono), color.rgb, clamp(b,0,1)), color.a);
}
)";
const char* render_ao = R"(
#version 330
#pragma vp_function oeui_render_ao, fragment_lighting, last
struct OE_PBR { float displacement, roughness, ao, metal; } oe_pbr;
void oeui_render_ao(inout vec4 color) {
color = vec4(oe_pbr.ao, oe_pbr.ao, oe_pbr.ao, 1);
}
)";
const char* render_roughness = R"(
#version 330
#pragma vp_function oeui_render_roughness, fragment_lighting, last
struct OE_PBR { float displacement, roughness, ao, metal; } oe_pbr;
void oeui_render_roughness(inout vec4 color) {
color = vec4(oe_pbr.roughness, oe_pbr.roughness, oe_pbr.roughness, 1);
}
)";
const char* render_metal = R"(
#version 330
#pragma vp_function oeui_render_metal, fragment_lighting, last
struct OE_PBR { float displacement, roughness, ao, metal; } oe_pbr;
void oeui_render_metal(inout vec4 color) {
color = vec4(oe_pbr.metal, oe_pbr.metal, oe_pbr.metal, 1);
}
)";
const char* render_displacement = R"(
#version 330
#pragma vp_function oeui_render_metal, fragment_lighting, last
struct OE_PBR { float displacement, roughness, ao, metal; } oe_pbr;
void oeui_render_metal(inout vec4 color) {
color = vec4(oe_pbr.displacement, oe_pbr.displacement, oe_pbr.displacement, 1);
}
)";
const char* render_draw_id = R"(
#version 460
#pragma vp_function oeui_render_drawid_vs, vertex_model
flat out int oeui_drawid;
void oeui_render_drawid_vs(inout vec4 vertex) {
oeui_drawid = gl_DrawID;
}
[break]
#version 460
#pragma vp_function oeui_render_drawid, fragment_lighting, last
flat in int oeui_drawid;
const vec3 colors[32] = { // thanks, GPT
vec3(1.0, 0.0, 0.0), // Red
vec3(0.0, 1.0, 0.0), // Green
vec3(0.0, 0.0, 1.0), // Blue
vec3(1.0, 1.0, 0.0), // Yellow
vec3(1.0, 0.0, 1.0), // Magenta
vec3(0.0, 1.0, 1.0), // Cyan
vec3(1.0, 0.5, 0.0), // Orange
vec3(0.5, 1.0, 0.0), // Lime
vec3(0.0, 0.5, 1.0), // Sky Blue
vec3(0.5, 0.0, 1.0), // Purple
vec3(1.0, 0.5, 0.5), // Light Red
vec3(0.5, 1.0, 0.5), // Light Green
vec3(0.5, 0.5, 1.0), // Light Blue
vec3(1.0, 1.0, 0.5), // Light Yellow
vec3(1.0, 0.5, 1.0), // Light Magenta
vec3(0.5, 1.0, 1.0), // Light Cyan
vec3(0.8, 0.2, 0.2), // Dark Red
vec3(0.2, 0.8, 0.2), // Dark Green
vec3(0.2, 0.2, 0.8), // Dark Blue
vec3(0.8, 0.8, 0.2), // Dark Yellow
vec3(0.8, 0.2, 0.8), // Dark Magenta
vec3(0.2, 0.8, 0.8), // Dark Cyan
vec3(0.8, 0.5, 0.2), // Brown
vec3(0.5, 0.8, 0.2), // Olive Green
vec3(0.2, 0.5, 0.8), // Steel Blue
vec3(0.5, 0.2, 0.8), // Indigo
vec3(0.8, 0.5, 0.5), // Salmon
vec3(0.5, 0.8, 0.5), // Light Olive Green
vec3(0.5, 0.5, 0.8), // Cornflower Blue
vec3(0.8, 0.8, 0.5), // Light Khaki
vec3(0.8, 0.5, 0.8), // Orchid
vec3(0.5, 0.8, 0.8) // Light Slate Gray
};
void oeui_render_drawid(inout vec4 color) {
color.rgb = colors[oeui_drawid % 32];
}
)";
const char* render_elevation_marker = R"(
#version 330
#pragma vp_function oeui_render_elevation_marker_vs, vertex_view
out vec4 oeui_color;
flat out int oe_terrain_vertexMarker;
void oeui_render_elevation_marker_vs(inout vec4 vertex) {
oeui_color = vec4(0);
int marker = oe_terrain_vertexMarker;
if ((marker & 4) > 0) // HAS_ELEVATION
oeui_color = vec4(1,0,0,1);
else if ((marker & 16) > 0) // CONSTRAINT
oeui_color = vec4(1,1,0,1);
}
[break]
#pragma vp_function oeui_render_elevation_marker_fs, fragment_lighting, last
in vec4 oeui_color;
void oeui_render_elevation_marker_fs(inout vec4 color) {
color.rgb = mix(color.rgb, oeui_color.rgb, oeui_color.a);
}
)";
}
namespace osgEarth
{
using namespace osgEarth::Threading;
class RenderingGUI : public ImGuiPanel
{
private:
osg::observer_ptr _mapNode;
using time_point = std::chrono::time_point;
time_point _lastFrame;
std::queue _times;
int _time_accum;
int _frameCounter;
int _fps;
std::string _renderMode;
bool _renderViewNormals;
bool _renderModelNormals;
bool _renderWinding;
bool _renderOutlines;
public:
RenderingGUI() : ImGuiPanel("Rendering"),
_frameCounter(0), _time_accum(0),
_renderViewNormals(false), _renderModelNormals(false),
_renderWinding(false), _renderOutlines(false) { }
void load(const Config& conf) override
{
}
void save(Config& conf) override
{
}
void setRenderMode(const std::string& mode, osg::RenderInfo& ri)
{
auto* vp = VirtualProgram::getOrCreate(stateset(ri));
if (!_renderMode.empty())
ShaderLoader::unload(vp, _renderMode);
_renderMode = mode;
if (!_renderMode.empty())
ShaderLoader::load(vp, _renderMode);
}
void draw(osg::RenderInfo& ri) override
{
if (!isVisible())
return;
if (!findNodeOrHide(_mapNode, ri))
return;
ImGui::Begin(name(), visible());
{
if (ImGuiLTable::Begin("LOD"))
{
float sse = _mapNode->getScreenSpaceError();
if (ImGuiLTable::SliderFloat("SSE", &sse, 1.0f, 200.0f))
_mapNode->setScreenSpaceError(sse);
float lod_scale = camera(ri)->getLODScale();
if (ImGuiLTable::SliderFloat("LOD Scale", &lod_scale, 0.1f, 4.0f))
camera(ri)->setLODScale(lod_scale);
ImGuiLTable::End();
}
ImGui::Separator();
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Overlays");
static int s_renderMode = 0;
int m = 0;
if (ImGui::RadioButton("Off", &s_renderMode, m++)) {
setRenderMode("", ri);
}
if (ImGui::RadioButton("Wireframe overlay", &s_renderMode, m++)) {
setRenderMode(render_outlines, ri);
}
if (ImGui::RadioButton("Front/backfacing triangles", &s_renderMode, m++)) {
setRenderMode(render_winding, ri);
}
if (ImGui::RadioButton("Normals (front/back)", &s_renderMode, m++)) {
setRenderMode(render_fb_normals, ri);
}
if (ImGui::RadioButton("Normals (view space)", &s_renderMode, m++)) {
setRenderMode(render_view_normals, ri);
}
if (ImGui::RadioButton("Normals (model space)", &s_renderMode, m++)) {
setRenderMode(render_model_normals, ri);
}
if (ImGui::RadioButton("Metal (PBR)", &s_renderMode, m++)) {
setRenderMode(render_metal, ri);
}
if (ImGui::RadioButton("Roughness (PBR)", &s_renderMode, m++)) {
setRenderMode(render_roughness, ri);
}
if (ImGui::RadioButton("AO (PBR)", &s_renderMode, m++)) {
setRenderMode(render_ao, ri);
}
if (ImGui::RadioButton("Displacement (PBR)", &s_renderMode, m++)) {
setRenderMode(render_displacement, ri);
}
if (ImGui::RadioButton("Draw ID", &s_renderMode, m++)) {
setRenderMode(render_draw_id, ri);
}
if (ImGui::RadioButton("Elevation markers", &s_renderMode, m++)) {
setRenderMode(render_elevation_marker, ri);
}
if (GLUtils::useNVGL())
{
static bool s_gpuculldebug = false;
if (ImGui::Checkbox("GPU cull debug view", &s_gpuculldebug)) {
stateset(ri)->removeDefine("OE_GPUCULL_DEBUG");
if (s_gpuculldebug)
stateset(ri)->setDefine("OE_GPUCULL_DEBUG", "1");
else
stateset(ri)->setDefine("OE_GPUCULL_DEBUG", "0");
}
}
ImGui::Separator();
const osgViewer::ViewerBase::ThreadingModel models[] = {
osgViewer::ViewerBase::SingleThreaded,
osgViewer::ViewerBase::DrawThreadPerContext,
osgViewer::ViewerBase::CullDrawThreadPerContext,
osgViewer::ViewerBase::CullThreadPerCameraDrawThreadPerContext
};
const std::string modelNames[] = {
"SingleThreaded",
"DrawThreadPerContext",
"CullDrawThreadPerContext",
"CullThreadPerCameraDrawThreadPerContext"
};
auto vb = view(ri)->getViewerBase();
int tmi;
for (tmi = 0; tmi < 4; ++tmi)
if (models[tmi] == vb->getThreadingModel())
break;
ImGui::Text("OSG Threading Model: ");
ImGui::SameLine();
if (ImGui::Button(modelNames[tmi].c_str())) {
auto new_tm = models[(tmi + 1) % 4];
vb->addUpdateOperation(new OneTimer([vb, new_tm]() {
vb->setThreadingModel(new_tm); }));
}
}
ImGui::End();
}
};
class NVGLInspectorGUI : public ImGuiPanel
{
public:
NVGLInspectorGUI() : ImGuiPanel("NVGL Inspector")
{
}
void load(const Config& conf) override
{
}
void save(Config& conf) override
{
}
void draw(osg::RenderInfo& ri) override
{
if (!isVisible())
return;
ImGui::Begin(name(), visible());
{
if (!GLUtils::useNVGL())
{
ImGui::TextColored(ImVec4(1, 0, 0, 1), "NVGL not enabled");
}
else
{
auto pools = GLObjectPool::getAll();
for (auto& iter : pools)
{
auto cxid = iter.first;
auto glpool = iter.second;
auto globjects = glpool->objects();
if (pools.size() > 1)
ImGui::Text("Context %d", cxid);
double glmem = (double)glpool->totalBytes() / 1048576.;
ImGui::TextColored(ImVec4(1, 1, 0, 1), "NVGL Memory: %.1lf MB", glmem);
ImGui::SameLine();
static bool sort_by_size = false;
ImGui::Checkbox("Sort by size", &sort_by_size);
std::map> categories;
for (auto& obj : globjects) {
categories[obj->category()].emplace_back(obj);
}
for (auto& cat : categories)
{
unsigned total = 0;
for (auto& obj : cat.second)
total += obj->size();
char header[128];
sprintf(header, "%s (%d @ %.1lf MB)###%s", cat.first.c_str(), (int)cat.second.size(), (double)total / 1048576., cat.first.c_str());
if (sort_by_size)
{
cat.second.sort([](const auto& lhs, const auto& rhs) {
return lhs->size() > rhs->size();
});
}
if (ImGui::TreeNode(header))
{
ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders;
flags &= ~ImGuiTableFlags_BordersOuter;
if (ImGui::BeginTable("globj", 4, flags))
{
for (auto& obj : cat.second)
{
ImGui::TableNextColumn();
ImGui::Text("%6.1lf KB", (double)obj->size() / (double)1024.);
ImGui::TableNextColumn();
ImGui::Text("%d", obj->recycles());
ImGui::TableNextColumn();
ImGui::Text("%s", obj->uid().c_str());
ImGui::TableNextColumn();
ImGui::Text("(%d)", obj->name());
}
ImGui::EndTable();
}
ImGui::TreePop();
}
}
if (pools.size() > 1)
ImGui::Separator();
ImGui::Separator();
auto recycle_attempts = glpool->recycleHits() + glpool->recycleMisses();
if (recycle_attempts > 0)
{
float re = (float)glpool->recycleHits() / (float)recycle_attempts;
ImGui::Text("Recycle Efficiency: %.0f%%", 100.0f * re);
}
}
ImGui::Separator();
int kb_per_frame = GLObjectPool::getBytesToDeletePerFrame() / 1024;
if (ImGuiLTable::Begin("Settings"))
{
if (ImGuiLTable::SliderInt("Rel KB per frame", &kb_per_frame, 128, 1024))
GLObjectPool::setBytesToDeletePerFrame(kb_per_frame * 1024);
ImGuiLTable::End();
}
}
}
ImGui::End();
}
};
}