370 lines
11 KiB
C++
370 lines
11 KiB
C++
// clang-format off
|
|
// glad must be included before any other OpenGL libraries
|
|
#include <glad/gl.h>
|
|
// clang-format on
|
|
|
|
#include <GLFW/glfw3.h>
|
|
#include <GLFW/glfw3native.h>
|
|
#include <X11/Xlib.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#include "backends/imgui_impl_glfw.h"
|
|
#include "backends/imgui_impl_opengl3.h"
|
|
#include "imgui.h"
|
|
#include "utils.h"
|
|
#include "srender.h"
|
|
#include "simulator.h"
|
|
|
|
GLFWwindow* gWindow = nullptr;
|
|
GuiInputState* gGuiInputState = nullptr;
|
|
|
|
// Rendering
|
|
srndr* gRndr = nullptr;
|
|
srview* gView = nullptr;
|
|
srcmdbuf* gRndrCmds = nullptr;
|
|
|
|
double mouse_scroll_x = 0.;
|
|
double mouse_scroll_y = 0.;
|
|
|
|
using namespace std;
|
|
|
|
static void error_callback(int error, const char* description) {
|
|
fprintf(stderr, "Error (%d): %s\n", error, description);
|
|
}
|
|
|
|
static void opengl_error_callback(
|
|
GLenum source,
|
|
GLenum type,
|
|
GLuint id,
|
|
GLenum severity,
|
|
GLsizei length,
|
|
const GLchar* message,
|
|
const void* userParam) {
|
|
gLog(
|
|
"OpenGL Error: %s type %0x%x, severity = 0x%x, message = %s",
|
|
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
|
|
type,
|
|
severity,
|
|
message);
|
|
}
|
|
|
|
static void
|
|
key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
|
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
|
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
|
}
|
|
|
|
void signal_handler(int signo) { gLog("Received signal %d", signo); }
|
|
|
|
void mouse_scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
|
|
mouse_scroll_x += xoffset;
|
|
mouse_scroll_y += yoffset;
|
|
}
|
|
|
|
void handle_mouse() {
|
|
if (!glfwGetWindowAttrib(gWindow, GLFW_FOCUSED)) {
|
|
return;
|
|
}
|
|
|
|
double mouse_x, mouse_y;
|
|
glfwGetCursorPos(gWindow, &mouse_x, &mouse_y);
|
|
|
|
if (gGuiInputState->mouseButton) {
|
|
gGuiInputState->mousedX = mouse_x - gGuiInputState->mouseX;
|
|
gGuiInputState->mousedY = mouse_y - gGuiInputState->mouseY;
|
|
} else {
|
|
gGuiInputState->mousedX = 0;
|
|
gGuiInputState->mousedY = 0;
|
|
}
|
|
gGuiInputState->mouseX = mouse_x;
|
|
gGuiInputState->mouseY = mouse_y;
|
|
gGuiInputState->mouseScroll = mouse_scroll_y;
|
|
|
|
gGuiInputState->mouseButton = glfwGetMouseButton(gWindow, 0)
|
|
+ (glfwGetMouseButton(gWindow, 1) << 1)
|
|
+ (glfwGetMouseButton(gWindow, 2) << 2);
|
|
}
|
|
|
|
void ShowDockspace(bool open) {
|
|
static bool opt_fullscreen = true;
|
|
static bool opt_padding = false;
|
|
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
(void)io;
|
|
|
|
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
|
|
// because it would be confusing to have two docking targets within each others.
|
|
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking;
|
|
if (opt_fullscreen) {
|
|
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
ImGui::SetNextWindowPos(viewport->GetWorkPos());
|
|
ImGui::SetNextWindowSize(viewport->GetWorkSize());
|
|
ImGui::SetNextWindowViewport(viewport->ID);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
|
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse
|
|
| ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
|
|
window_flags |=
|
|
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
|
} else {
|
|
dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
|
|
}
|
|
|
|
// When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background
|
|
// and handle the pass-thru hole, so we ask Begin() to not render a background.
|
|
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
|
|
window_flags |= ImGuiWindowFlags_NoBackground;
|
|
|
|
// Important: note that we proceed even if Begin() returns false (aka window is collapsed).
|
|
// This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
|
|
// all active windows docked into it will lose their parent and become undocked.
|
|
// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
|
|
// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
|
|
if (!opt_padding)
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
|
ImGui::Begin("DockSpace Demo", &open, window_flags);
|
|
if (!opt_padding) ImGui::PopStyleVar();
|
|
|
|
if (opt_fullscreen) ImGui::PopStyleVar(2);
|
|
|
|
// DockSpace
|
|
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) {
|
|
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
|
|
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
|
|
} else {
|
|
gLog("Error: no docking not enabled");
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
void DoRender() {
|
|
// Render Output
|
|
ImGui::Begin("Render Output");
|
|
|
|
// Update the view
|
|
const ImVec2 content_avail = ImGui::GetContentRegionAvail();
|
|
srview_set_size(gView, content_avail.x, content_avail.y);
|
|
simd4x4f view;
|
|
simd4x4f proj;
|
|
simd4x4f_translation(&view, 0.1f, 0.1f, -0.5f);
|
|
simd4f eye = simd4f_create (0.f, 1.f, 10.f, 1.f);
|
|
simd4x4f_lookat(
|
|
&view,
|
|
eye,
|
|
simd4f_create(0.f, 0.f, 0.f, 1.f),
|
|
simd4f_create(0.f, 1.f, 0.f, 1.f));
|
|
|
|
float view_width = content_avail.x / 50.f;
|
|
float view_height = content_avail.y / 50.f;
|
|
|
|
simd4x4f_ortho(
|
|
&proj,
|
|
-0.5f * view_width,
|
|
0.5f * view_width,
|
|
-0.5f * view_height,
|
|
0.5f * view_height,
|
|
-50.0f,
|
|
50.0f);
|
|
simd4x4f_perspective(&proj,
|
|
70.f * (M_PI / 180.f), view_width / view_height, 0.1f, 50.f);
|
|
srview_set_view(gView, view);
|
|
srview_set_proj(gView, proj);
|
|
|
|
// Populate render commands
|
|
srcmdbuf_clear(gRndrCmds);
|
|
|
|
srcmd rcmd;
|
|
srcmd_clear(&rcmd);
|
|
|
|
rcmd.type = SRndrCmdTypeFrame;
|
|
srcmdbuf_add(gRndrCmds, &rcmd);
|
|
|
|
simulator_draw(gRndrCmds);
|
|
|
|
// Perform the actual render
|
|
srndr_render(gRndr, gView, gRndrCmds);
|
|
GLuint view_texture;
|
|
srview_get_output_texture(gView, &view_texture);
|
|
|
|
ImGui::Image(
|
|
(void*)view_texture,
|
|
content_avail,
|
|
ImVec2(0.0f, 1.0f),
|
|
ImVec2(1.0f, 0.0f));
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
int main(void) {
|
|
gTimerInit();
|
|
|
|
LoggingInit();
|
|
|
|
if (signal(SIGUSR1, signal_handler) == SIG_ERR) {
|
|
gLog("Error registering signal handler!");
|
|
}
|
|
|
|
// Initialize GLFW
|
|
glfwSetErrorCallback(error_callback);
|
|
glfwInit();
|
|
|
|
const char* glsl_version = "#version 130";
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
|
glfwWindowHint(GLFW_SAMPLES, 16);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
|
|
#if __APPLE__
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
|
#endif
|
|
|
|
gWindow = glfwCreateWindow(800, 600, "ViSimir", NULL, NULL);
|
|
assert(gWindow != NULL);
|
|
glfwMakeContextCurrent(gWindow);
|
|
glfwSwapInterval(1);
|
|
|
|
// Initialize OpenGL loader
|
|
gladLoadGL(glfwGetProcAddress);
|
|
|
|
glfwSetKeyCallback(gWindow, key_callback);
|
|
glfwSetScrollCallback(gWindow, mouse_scroll_callback);
|
|
|
|
std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << endl;
|
|
std::cout << "GLSL Version : " << glGetString(GL_SHADING_LANGUAGE_VERSION)
|
|
<< endl;
|
|
|
|
// During init, enable debug output
|
|
// glEnable ( GL_DEBUG_OUTPUT );
|
|
// glDebugMessageCallback( (GLDEBUGPROC) opengl_error_callback, 0 );
|
|
|
|
// imgui initialization.
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
(void)io;
|
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
|
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
|
|
|
GuiInputState gui_input_state;
|
|
gGuiInputState = &gui_input_state;
|
|
|
|
ImGui_ImplGlfw_InitForOpenGL(gWindow, true);
|
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
|
|
|
// Setup Style
|
|
ImGui::StyleColorsDark();
|
|
//ImGui::StyleColorsClassic();
|
|
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
style.WindowRounding =
|
|
0.0f; // When viewports are enabled it is preferable to disable WinodwRounding
|
|
style.Colors[ImGuiCol_WindowBg].w =
|
|
1.0f; // When viewports are enabled it is preferable to disable WindowBg alpha
|
|
|
|
double frame_time_last = glfwGetTime();
|
|
double frame_time_current = frame_time_last;
|
|
double frame_delta_time = 0.0;
|
|
uint64_t frame_counter = 0;
|
|
|
|
bool show_demo_window = true;
|
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
|
|
|
gRndr = srndr_create();
|
|
gView = srview_create();
|
|
gRndrCmds = srcmdbuf_create(1024);
|
|
simulator_init();
|
|
|
|
while (!glfwWindowShouldClose(gWindow)) {
|
|
frame_counter++;
|
|
|
|
// Start the imgui frame such that widgets can be submitted
|
|
handle_mouse();
|
|
|
|
glfwPollEvents();
|
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
ImGui_ImplGlfw_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
frame_time_last = frame_time_current;
|
|
frame_time_current = glfwGetTime();
|
|
frame_delta_time = frame_time_current - frame_time_last;
|
|
|
|
gTimer->mFrameTime = (float)(frame_delta_time);
|
|
if (!gTimer->mPaused) {
|
|
gTimer->mDeltaTime = gTimer->mFrameTime;
|
|
gTimer->mCurrentTime = gTimer->mCurrentTime + gTimer->mDeltaTime;
|
|
} else {
|
|
gTimer->mDeltaTime = 0.0f;
|
|
}
|
|
|
|
assert(gTimer->mDeltaTime >= 0.0f);
|
|
int width, height;
|
|
ImGui::BeginMainMenuBar();
|
|
|
|
if (ImGui::BeginMenu("Dialogs")) {
|
|
ImGui::Checkbox("ImGui Demo", &show_demo_window);
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
float menu_bar_height = ImGui::GetWindowHeight();
|
|
ImGui::EndMainMenuBar();
|
|
|
|
if (show_demo_window) ImGui::ShowDemoWindow();
|
|
|
|
ShowDockspace(true);
|
|
|
|
DoRender();
|
|
|
|
simulator_update(gTimer->mDeltaTime);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
// Rendering
|
|
ImGui::Render();
|
|
int display_w, display_h;
|
|
glfwMakeContextCurrent(gWindow);
|
|
glfwGetFramebufferSize(gWindow, &display_w, &display_h);
|
|
glViewport(0, 0, display_w, display_h);
|
|
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
ImDrawData* draw_data = ImGui::GetDrawData();
|
|
assert(draw_data != NULL);
|
|
ImGui_ImplOpenGL3_RenderDrawData(draw_data);
|
|
|
|
// Update and Render additional Platform Windows
|
|
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
|
ImGui::UpdatePlatformWindows();
|
|
ImGui::RenderPlatformWindowsDefault();
|
|
}
|
|
|
|
glfwMakeContextCurrent(gWindow);
|
|
glfwSwapBuffers(gWindow);
|
|
|
|
// Send the application to sleep if we have some time left for this frame
|
|
double frame_target_time = 1.0 / 60.0;
|
|
if (frame_delta_time < frame_target_time) {
|
|
usleep((frame_target_time - frame_delta_time) * 1000000 * 0.98);
|
|
}
|
|
}
|
|
|
|
gLog("Exiting application");
|
|
|
|
srview_destroy(gView);
|
|
srndr_destroy(gRndr);
|
|
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
ImGui_ImplGlfw_Shutdown();
|
|
ImGui::DestroyContext();
|
|
glfwTerminate();
|
|
}
|