2021-11-11 21:22:24 +01:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// imgui-glfw
|
|
|
|
// Demonstrates basic integration with Dear Imgui (without custom
|
|
|
|
// texture or custom font support).
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include "imgui.h"
|
2022-02-11 16:51:18 +01:00
|
|
|
#include "imnodes.h"
|
2021-11-11 21:22:24 +01:00
|
|
|
#define SOKOL_IMPL
|
|
|
|
#define SOKOL_GLCORE33
|
|
|
|
#include "sokol_gfx.h"
|
|
|
|
#include "sokol_time.h"
|
|
|
|
#define SOKOL_GL_IMPL
|
|
|
|
#include "util/sokol_gl.h"
|
|
|
|
#define GLFW_INCLUDE_NONE
|
|
|
|
#include <iostream>
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
#include "3rdparty/json/json.hpp"
|
2021-11-12 11:00:39 +01:00
|
|
|
#include "Camera.h"
|
2023-03-25 23:30:13 +01:00
|
|
|
#include "GLFW/glfw3.h"
|
2021-11-12 12:48:56 +01:00
|
|
|
#include "SkinnedMesh.h"
|
2023-03-26 13:25:44 +02:00
|
|
|
#include "SkinnedMeshResource.h"
|
2022-03-25 11:46:44 +01:00
|
|
|
#include "src/AnimGraph/AnimGraphEditor.h"
|
2021-11-11 21:22:24 +01:00
|
|
|
|
|
|
|
const int Width = 1024;
|
|
|
|
const int Height = 768;
|
|
|
|
const int MaxVertices = (1 << 16);
|
|
|
|
const int MaxIndices = MaxVertices * 3;
|
|
|
|
|
|
|
|
uint64_t last_time = 0;
|
|
|
|
|
|
|
|
sg_pass_action pass_action;
|
|
|
|
sg_pipeline pip;
|
|
|
|
sg_bindings bind;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
ImVec2 disp_size;
|
|
|
|
} vs_params_t;
|
|
|
|
|
|
|
|
static void draw_imgui(ImDrawData*);
|
|
|
|
|
|
|
|
#define HANDMADE_MATH_IMPLEMENTATION
|
|
|
|
#define HANDMADE_MATH_NO_SSE
|
|
|
|
#include "HandmadeMath.h"
|
|
|
|
#include "camera.h"
|
|
|
|
|
|
|
|
// ozz-animation headers
|
2023-03-25 23:30:13 +01:00
|
|
|
#include <cmath> // fmodf
|
|
|
|
#include <fstream>
|
2021-11-11 21:22:24 +01:00
|
|
|
#include <memory> // std::unique_ptr, std::make_unique
|
|
|
|
|
2021-11-16 18:15:56 +01:00
|
|
|
#include "SkinnedMeshRenderer.h"
|
2021-11-11 21:22:24 +01:00
|
|
|
#include "ozz/animation/runtime/animation.h"
|
|
|
|
#include "ozz/animation/runtime/local_to_model_job.h"
|
|
|
|
#include "ozz/animation/runtime/sampling_job.h"
|
|
|
|
#include "ozz/animation/runtime/skeleton.h"
|
|
|
|
#include "ozz/base/containers/vector.h"
|
|
|
|
#include "ozz/base/io/archive.h"
|
|
|
|
#include "ozz/base/io/stream.h"
|
|
|
|
#include "ozz/base/log.h"
|
|
|
|
#include "ozz/base/maths/soa_transform.h"
|
|
|
|
#include "ozz/base/maths/vec_float.h"
|
|
|
|
|
|
|
|
// wrapper struct for managed ozz-animation C++ objects, mUST BE DEleted
|
|
|
|
// before shutdown, otherwise ozz-animation will report a memory leak
|
|
|
|
typedef struct {
|
|
|
|
ozz::animation::Skeleton skeleton;
|
|
|
|
ozz::animation::Animation animation;
|
2023-03-26 11:44:29 +02:00
|
|
|
ozz::animation::SamplingJob::Context sampling_context;
|
2021-11-11 21:22:24 +01:00
|
|
|
ozz::vector<ozz::math::SoaTransform> local_matrices;
|
|
|
|
ozz::vector<ozz::math::Float4x4> model_matrices;
|
|
|
|
} ozz_t;
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
std::unique_ptr<ozz_t> ozz;
|
|
|
|
sg_pass_action pass_action;
|
2021-11-12 11:00:39 +01:00
|
|
|
Camera camera;
|
2021-11-11 21:22:24 +01:00
|
|
|
struct {
|
|
|
|
bool skeleton;
|
|
|
|
bool animation;
|
|
|
|
bool failed;
|
|
|
|
} loaded;
|
|
|
|
struct {
|
|
|
|
double frame;
|
|
|
|
double absolute;
|
|
|
|
uint64_t laptime;
|
|
|
|
float factor;
|
|
|
|
float anim_ratio;
|
|
|
|
bool anim_ratio_ui_override;
|
|
|
|
bool paused;
|
|
|
|
} time;
|
|
|
|
} state;
|
|
|
|
|
2021-11-12 11:00:39 +01:00
|
|
|
typedef struct {
|
|
|
|
int32_t mousedX;
|
|
|
|
int32_t mousedY;
|
|
|
|
int32_t mouseX;
|
|
|
|
int32_t mouseY;
|
|
|
|
uint8_t mouseButton;
|
|
|
|
int32_t mouseScroll;
|
|
|
|
char key;
|
|
|
|
} GuiInputState;
|
|
|
|
|
|
|
|
GuiInputState gGuiInputState = {0, 0, 0, 0, 0, 0, 0};
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
enum class ControlMode { ControlModeNone, ControlModeFPS };
|
2021-11-12 11:00:39 +01:00
|
|
|
|
|
|
|
ControlMode gControlMode = ControlMode::ControlModeNone;
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
struct ApplicationConfig {
|
|
|
|
int window_position[2] = {100, 30};
|
|
|
|
int window_size[2] = {1000, 600};
|
|
|
|
|
|
|
|
struct GraphEditor {
|
|
|
|
bool visible = false;
|
|
|
|
int position[2] = {20, 20};
|
|
|
|
int size[2] = {800, 500};
|
|
|
|
};
|
|
|
|
GraphEditor graph_editor;
|
|
|
|
|
2023-03-26 00:13:19 +01:00
|
|
|
struct SkinnedMeshWidget {
|
|
|
|
bool visible = false;
|
|
|
|
int position[2] = {20, 20};
|
|
|
|
int size[2] = {800, 500};
|
|
|
|
};
|
|
|
|
SkinnedMeshWidget skinned_mesh_widget;
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
bool show_imgui_demo_window = false;
|
|
|
|
bool show_another_window = false;
|
|
|
|
bool show_camera_widget = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
void to_json(nlohmann::json& j, const ApplicationConfig& config) {
|
|
|
|
std::cerr << "converting to json" << std::endl;
|
|
|
|
j["type"] = "AnimTestbedConfig";
|
|
|
|
|
|
|
|
j["main_window"]["position"][0] = config.window_position[0];
|
|
|
|
j["main_window"]["position"][1] = config.window_position[1];
|
|
|
|
|
|
|
|
j["main_window"]["size"][0] = config.window_size[0];
|
|
|
|
j["main_window"]["size"][1] = config.window_size[1];
|
|
|
|
|
|
|
|
j["graph_editor"]["visible"] = config.graph_editor.visible;
|
|
|
|
j["graph_editor"]["position"][0] = config.graph_editor.position[0];
|
|
|
|
j["graph_editor"]["position"][1] = config.graph_editor.position[1];
|
|
|
|
j["graph_editor"]["size"][0] = config.graph_editor.size[0];
|
|
|
|
j["graph_editor"]["size"][1] = config.graph_editor.size[1];
|
|
|
|
|
2023-03-26 00:13:19 +01:00
|
|
|
j["skinned_mesh_widget"]["visible"] = config.skinned_mesh_widget.visible;
|
|
|
|
j["skinned_mesh_widget"]["position"][0] = config.skinned_mesh_widget.position[0];
|
|
|
|
j["skinned_mesh_widget"]["position"][1] = config.skinned_mesh_widget.position[1];
|
|
|
|
j["skinned_mesh_widget"]["size"][0] = config.skinned_mesh_widget.size[0];
|
|
|
|
j["skinned_mesh_widget"]["size"][1] = config.skinned_mesh_widget.size[1];
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
j["camera_widget"]["visible"] = config.show_camera_widget;
|
|
|
|
}
|
|
|
|
|
|
|
|
void from_json(const nlohmann::json& j, ApplicationConfig& config) {
|
|
|
|
std::cerr << "converting from json" << std::endl;
|
|
|
|
|
|
|
|
if (j.contains("main_window")) {
|
|
|
|
if (j["main_window"].contains("position")
|
|
|
|
and j["main_window"]["position"].size() == 2) {
|
|
|
|
config.window_position[0] = j["main_window"]["position"].at(0);
|
|
|
|
config.window_position[1] = j["main_window"]["position"].at(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j["main_window"].contains("size")
|
|
|
|
and j["main_window"]["size"].size() == 2) {
|
|
|
|
config.window_size[0] = j["main_window"]["size"].at(0);
|
|
|
|
config.window_size[1] = j["main_window"]["size"].at(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j.contains("graph_editor")) {
|
|
|
|
if (j["graph_editor"].contains("visible")) {
|
|
|
|
config.graph_editor.visible = j["graph_editor"]["visible"];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j["graph_editor"].contains("position") and j["graph_editor"]["position"].size() == 2) {
|
|
|
|
config.graph_editor.position[0] = j["graph_editor"]["position"].at(0);
|
|
|
|
config.graph_editor.position[1] = j["graph_editor"]["position"].at(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j["graph_editor"].contains("size") and j["graph_editor"]["size"].size() == 2) {
|
|
|
|
config.graph_editor.size[0] = j["graph_editor"]["size"].at(0);
|
|
|
|
config.graph_editor.size[1] = j["graph_editor"]["size"].at(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-26 00:13:19 +01:00
|
|
|
|
|
|
|
if (j.contains("skinned_mesh_widget")) {
|
|
|
|
if (j["skinned_mesh_widget"].contains("visible")) {
|
|
|
|
config.skinned_mesh_widget.visible = j["skinned_mesh_widget"]["visible"];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j["skinned_mesh_widget"].contains("position") and j["skinned_mesh_widget"]["position"].size() == 2) {
|
|
|
|
config.skinned_mesh_widget.position[0] = j["skinned_mesh_widget"]["position"].at(0);
|
|
|
|
config.skinned_mesh_widget.position[1] = j["skinned_mesh_widget"]["position"].at(1);
|
|
|
|
}
|
2023-03-25 23:30:13 +01:00
|
|
|
|
2023-03-26 00:13:19 +01:00
|
|
|
if (j["skinned_mesh_widget"].contains("size") and j["skinned_mesh_widget"]["size"].size() == 2) {
|
|
|
|
config.skinned_mesh_widget.size[0] = j["skinned_mesh_widget"]["size"].at(0);
|
|
|
|
config.skinned_mesh_widget.size[1] = j["skinned_mesh_widget"]["size"].at(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
if (j.contains("camera_widget") and j["camera_widget"].contains("visible")) {
|
|
|
|
config.show_camera_widget = j["camera_widget"]["visible"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ApplicationConfig gApplicationConfig;
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
// io buffers for skeleton and animation data files, we know the max file size upfront
|
|
|
|
static uint8_t skel_data_buffer[4 * 1024];
|
|
|
|
static uint8_t anim_data_buffer[32 * 1024];
|
|
|
|
|
2021-11-12 11:00:39 +01:00
|
|
|
static void draw_grid(void);
|
2021-11-11 21:22:24 +01:00
|
|
|
static void draw_ui(void);
|
|
|
|
// static void skeleton_data_loaded(const sfetch_response_t* response);
|
|
|
|
// static void animation_data_loaded(const sfetch_response_t* response);
|
|
|
|
static void frame(void);
|
|
|
|
|
2021-11-12 11:00:39 +01:00
|
|
|
void handle_mouse(GLFWwindow* w, GuiInputState* io_input_state) {
|
|
|
|
if (!glfwGetWindowAttrib(w, GLFW_FOCUSED)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
double mouse_x, mouse_y;
|
|
|
|
glfwGetCursorPos(w, &mouse_x, &mouse_y);
|
|
|
|
|
|
|
|
if (io_input_state->mouseButton) {
|
|
|
|
io_input_state->mousedX = int32_t(mouse_x) - io_input_state->mouseX;
|
|
|
|
io_input_state->mousedY = int32_t(mouse_y) - io_input_state->mouseY;
|
|
|
|
} else {
|
|
|
|
io_input_state->mousedX = 0;
|
|
|
|
io_input_state->mousedY = 0;
|
|
|
|
}
|
|
|
|
io_input_state->mouseX = int32_t(mouse_x);
|
|
|
|
io_input_state->mouseY = int32_t(mouse_y);
|
|
|
|
|
|
|
|
io_input_state->mouseButton = glfwGetMouseButton(w, 0)
|
|
|
|
+ (glfwGetMouseButton(w, 1) << 1)
|
|
|
|
+ (glfwGetMouseButton(w, 2) << 2);
|
|
|
|
}
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
void load_application_config(const char* filename) {
|
|
|
|
if (std::filesystem::exists(filename)) {
|
|
|
|
std::ifstream input_file;
|
|
|
|
input_file.open(filename);
|
|
|
|
std::stringstream buffer;
|
|
|
|
buffer << input_file.rdbuf();
|
|
|
|
nlohmann::json application_config =
|
|
|
|
nlohmann::json::parse(buffer.str(), nullptr, false);
|
|
|
|
|
|
|
|
if (application_config.is_discarded()) {
|
|
|
|
std::cerr << "Error parsing json of file '" << filename << "'."
|
|
|
|
<< std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (application_config.value("type", "undefined") != "AnimTestbedConfig") {
|
|
|
|
std::cerr
|
|
|
|
<< "Invalid json object. Expected type 'AnimTestbedConfig' but got '"
|
|
|
|
<< application_config["type"] << "'." << std::endl;
|
|
|
|
application_config["type"] = "AnimTestbedConfig";
|
|
|
|
}
|
|
|
|
|
|
|
|
gApplicationConfig = application_config.get<ApplicationConfig>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void save_application_config(const char* filename) {
|
|
|
|
std::ofstream output_file;
|
|
|
|
|
|
|
|
nlohmann::json application_config = gApplicationConfig;
|
|
|
|
|
|
|
|
output_file.open(filename);
|
|
|
|
output_file << to_string(application_config) << std::endl;
|
|
|
|
output_file.close();
|
|
|
|
}
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
int main() {
|
|
|
|
// window and GL context via GLFW and flextGL
|
|
|
|
glfwInit();
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
|
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
|
|
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
|
2021-11-12 11:23:20 +01:00
|
|
|
glfwWindowHint(GLFW_SAMPLES, 16);
|
2021-11-12 11:00:39 +01:00
|
|
|
GLFWwindow* w = glfwCreateWindow(Width, Height, "AnimTestbed", 0, 0);
|
2021-11-11 21:22:24 +01:00
|
|
|
glfwMakeContextCurrent(w);
|
|
|
|
glfwSwapInterval(1);
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
load_application_config("animtestbed_config.json");
|
|
|
|
if (gApplicationConfig.window_position[0] != 0
|
|
|
|
|| gApplicationConfig.window_position[1] != 0) {
|
|
|
|
glfwSetWindowPos(
|
|
|
|
w,
|
|
|
|
gApplicationConfig.window_position[0],
|
|
|
|
gApplicationConfig.window_position[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gApplicationConfig.window_size[0] != 0
|
|
|
|
|| gApplicationConfig.window_size[1] != 0) {
|
|
|
|
glfwSetWindowSize(
|
|
|
|
w,
|
|
|
|
gApplicationConfig.window_size[0],
|
|
|
|
gApplicationConfig.window_size[1]);
|
|
|
|
}
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
// GLFW to ImGui input forwarding
|
|
|
|
glfwSetMouseButtonCallback(
|
|
|
|
w,
|
|
|
|
[](GLFWwindow*, int btn, int action, int /*mods*/) {
|
|
|
|
if ((btn >= 0) && (btn < 3)) {
|
|
|
|
ImGui::GetIO().MouseDown[btn] = (action == GLFW_PRESS);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
glfwSetCursorPosCallback(w, [](GLFWwindow*, double pos_x, double pos_y) {
|
|
|
|
ImGui::GetIO().MousePos.x = float(pos_x);
|
|
|
|
ImGui::GetIO().MousePos.y = float(pos_y);
|
|
|
|
});
|
|
|
|
glfwSetScrollCallback(w, [](GLFWwindow*, double /*pos_x*/, double pos_y) {
|
|
|
|
ImGui::GetIO().MouseWheel = float(pos_y);
|
|
|
|
});
|
|
|
|
glfwSetKeyCallback(
|
|
|
|
w,
|
|
|
|
[](GLFWwindow*, int key, int /*scancode*/, int action, int mods) {
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
if ((key >= 0) && (key < 512)) {
|
|
|
|
io.KeysDown[key] = (action == GLFW_PRESS) || (action == GLFW_REPEAT);
|
|
|
|
}
|
|
|
|
io.KeyCtrl = (0 != (mods & GLFW_MOD_CONTROL));
|
|
|
|
io.KeyAlt = (0 != (mods & GLFW_MOD_ALT));
|
|
|
|
io.KeyShift = (0 != (mods & GLFW_MOD_SHIFT));
|
|
|
|
});
|
|
|
|
glfwSetCharCallback(w, [](GLFWwindow*, unsigned int codepoint) {
|
|
|
|
ImGui::GetIO().AddInputCharacter((ImWchar)codepoint);
|
|
|
|
});
|
|
|
|
|
|
|
|
// setup sokol_gfx and sokol_time
|
|
|
|
stm_setup();
|
|
|
|
sg_desc desc = {};
|
|
|
|
sg_setup(&desc);
|
|
|
|
assert(sg_isvalid());
|
|
|
|
|
|
|
|
// setup sokol-gl
|
|
|
|
sgl_desc_t sgldesc = {};
|
|
|
|
sgldesc.sample_count = 0;
|
|
|
|
sgl_setup(&sgldesc);
|
|
|
|
|
2023-03-26 13:25:44 +02:00
|
|
|
SkinnedMeshResource skinned_mesh_resource;
|
|
|
|
skinned_mesh_resource.loadFromFile("../media/SampleSkinnedMesh.json");
|
|
|
|
|
2021-11-12 20:10:56 +01:00
|
|
|
SkinnedMesh skinned_mesh;
|
2023-03-26 13:25:44 +02:00
|
|
|
skinned_mesh_resource.createInstance(skinned_mesh);
|
2021-11-19 12:40:14 +01:00
|
|
|
|
2021-11-12 20:10:56 +01:00
|
|
|
skinned_mesh.SetCurrentAnimation(0);
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
state.time.factor = 1.0f;
|
|
|
|
|
2021-11-12 11:00:39 +01:00
|
|
|
Camera_Init(&state.camera);
|
2021-11-11 21:22:24 +01:00
|
|
|
|
|
|
|
// setup Dear Imgui
|
|
|
|
ImGui::CreateContext();
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
io.IniFilename = nullptr;
|
|
|
|
io.Fonts->AddFontDefault();
|
|
|
|
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
|
|
|
|
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
|
|
|
|
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
|
|
|
|
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
|
|
|
|
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
|
|
|
|
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
|
|
|
|
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
|
|
|
|
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
|
|
|
|
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
|
|
|
|
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
|
|
|
|
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
|
|
|
|
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
|
|
|
|
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
|
|
|
|
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
|
|
|
|
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
|
|
|
|
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
|
|
|
|
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
|
|
|
|
|
2022-02-11 16:51:18 +01:00
|
|
|
// ImNodes
|
|
|
|
ImNodes::CreateContext();
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
// dynamic vertex- and index-buffers for imgui-generated geometry
|
|
|
|
sg_buffer_desc vbuf_desc = {};
|
|
|
|
vbuf_desc.usage = SG_USAGE_STREAM;
|
|
|
|
vbuf_desc.size = MaxVertices * sizeof(ImDrawVert);
|
|
|
|
bind.vertex_buffers[0] = sg_make_buffer(&vbuf_desc);
|
|
|
|
|
|
|
|
sg_buffer_desc ibuf_desc = {};
|
|
|
|
ibuf_desc.type = SG_BUFFERTYPE_INDEXBUFFER;
|
|
|
|
ibuf_desc.usage = SG_USAGE_STREAM;
|
|
|
|
ibuf_desc.size = MaxIndices * sizeof(ImDrawIdx);
|
|
|
|
bind.index_buffer = sg_make_buffer(&ibuf_desc);
|
|
|
|
|
|
|
|
// font texture for imgui's default font
|
|
|
|
unsigned char* font_pixels;
|
|
|
|
int font_width, font_height;
|
|
|
|
io.Fonts->GetTexDataAsRGBA32(&font_pixels, &font_width, &font_height);
|
|
|
|
sg_image_desc img_desc = {};
|
|
|
|
img_desc.width = font_width;
|
|
|
|
img_desc.height = font_height;
|
|
|
|
img_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
|
|
|
|
img_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE;
|
|
|
|
img_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
|
|
|
|
img_desc.data.subimage[0][0] =
|
|
|
|
sg_range{font_pixels, size_t(font_width * font_height * 4)};
|
|
|
|
bind.fs_images[0] = sg_make_image(&img_desc);
|
|
|
|
|
|
|
|
// shader object for imgui rendering
|
|
|
|
sg_shader_desc shd_desc = {};
|
|
|
|
auto& ub = shd_desc.vs.uniform_blocks[0];
|
|
|
|
ub.size = sizeof(vs_params_t);
|
|
|
|
ub.uniforms[0].name = "disp_size";
|
|
|
|
ub.uniforms[0].type = SG_UNIFORMTYPE_FLOAT2;
|
|
|
|
shd_desc.vs.source =
|
|
|
|
"#version 330\n"
|
|
|
|
"uniform vec2 disp_size;\n"
|
|
|
|
"layout(location=0) in vec2 position;\n"
|
|
|
|
"layout(location=1) in vec2 texcoord0;\n"
|
|
|
|
"layout(location=2) in vec4 color0;\n"
|
|
|
|
"out vec2 uv;\n"
|
|
|
|
"out vec4 color;\n"
|
|
|
|
"void main() {\n"
|
|
|
|
" gl_Position = vec4(((position/disp_size)-0.5)*vec2(2.0,-2.0), 0.5, "
|
|
|
|
"1.0);\n"
|
|
|
|
" uv = texcoord0;\n"
|
|
|
|
" color = color0;\n"
|
|
|
|
"}\n";
|
|
|
|
shd_desc.fs.images[0].name = "tex";
|
|
|
|
shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D;
|
|
|
|
shd_desc.fs.source =
|
|
|
|
"#version 330\n"
|
|
|
|
"uniform sampler2D tex;\n"
|
|
|
|
"in vec2 uv;\n"
|
|
|
|
"in vec4 color;\n"
|
|
|
|
"out vec4 frag_color;\n"
|
|
|
|
"void main() {\n"
|
|
|
|
" frag_color = texture(tex, uv) * color;\n"
|
|
|
|
"}\n";
|
|
|
|
sg_shader shd = sg_make_shader(&shd_desc);
|
|
|
|
|
|
|
|
// pipeline object for imgui rendering
|
|
|
|
sg_pipeline_desc pip_desc = {};
|
|
|
|
pip_desc.layout.buffers[0].stride = sizeof(ImDrawVert);
|
|
|
|
auto& attrs = pip_desc.layout.attrs;
|
|
|
|
attrs[0].format = SG_VERTEXFORMAT_FLOAT2;
|
|
|
|
attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
|
|
|
|
attrs[2].format = SG_VERTEXFORMAT_UBYTE4N;
|
|
|
|
pip_desc.shader = shd;
|
|
|
|
pip_desc.index_type = SG_INDEXTYPE_UINT16;
|
|
|
|
pip_desc.colors[0].blend.enabled = true;
|
|
|
|
pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
|
|
|
|
pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
|
|
|
|
pip_desc.colors[0].write_mask = SG_COLORMASK_RGB;
|
|
|
|
pip = sg_make_pipeline(&pip_desc);
|
|
|
|
|
|
|
|
// initial clear color
|
|
|
|
pass_action.colors[0].action = SG_ACTION_CLEAR;
|
2021-11-12 11:00:39 +01:00
|
|
|
pass_action.colors[0].value = {0.1f, 0.1f, 0.1f, 1.0f};
|
2021-11-11 21:22:24 +01:00
|
|
|
|
|
|
|
// draw loop
|
|
|
|
while (!glfwWindowShouldClose(w)) {
|
2023-03-25 23:30:13 +01:00
|
|
|
glfwGetWindowPos(
|
|
|
|
w,
|
|
|
|
&gApplicationConfig.window_position[0],
|
|
|
|
&gApplicationConfig.window_position[1]);
|
|
|
|
|
|
|
|
glfwGetWindowSize(
|
|
|
|
w,
|
|
|
|
&gApplicationConfig.window_size[0],
|
|
|
|
&gApplicationConfig.window_size[1]);
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
state.time.frame = stm_sec(
|
|
|
|
stm_round_to_common_refresh_rate(stm_laptime(&state.time.laptime)));
|
|
|
|
|
2021-11-12 20:10:56 +01:00
|
|
|
state.time.absolute += state.time.frame * state.time.factor;
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
int cur_width, cur_height;
|
|
|
|
glfwGetFramebufferSize(w, &cur_width, &cur_height);
|
|
|
|
|
|
|
|
// this is standard ImGui demo code
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
io.DisplaySize = ImVec2(float(cur_width), float(cur_height));
|
|
|
|
io.DeltaTime = (float)stm_sec(stm_laptime(&last_time));
|
|
|
|
ImGui::NewFrame();
|
|
|
|
|
2021-11-12 11:00:39 +01:00
|
|
|
// handle input
|
2023-03-25 23:30:13 +01:00
|
|
|
handle_mouse(w, &gGuiInputState);
|
2021-11-12 11:00:39 +01:00
|
|
|
|
|
|
|
if (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_RIGHT)) {
|
|
|
|
if (gControlMode == ControlMode::ControlModeNone) {
|
|
|
|
gControlMode = ControlMode::ControlModeFPS;
|
|
|
|
Camera_CalcFromMatrix(&state.camera, &state.camera.mtxView[0]);
|
|
|
|
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
gControlMode = ControlMode::ControlModeNone;
|
|
|
|
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
|
|
|
Camera_Update(
|
|
|
|
&state.camera,
|
|
|
|
cur_width,
|
|
|
|
cur_height,
|
|
|
|
state.time.frame,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gControlMode == ControlMode::ControlModeFPS) {
|
|
|
|
float camera_accel[3] = {0.f, 0.f, 0.f};
|
|
|
|
float accel_scale = 100.0;
|
|
|
|
|
|
|
|
if (glfwGetKey(w, GLFW_KEY_LEFT_SHIFT)) {
|
|
|
|
accel_scale *= 3.;
|
|
|
|
} else if (glfwGetKey(w, GLFW_KEY_LEFT_CONTROL)) {
|
|
|
|
accel_scale /= 3.;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (glfwGetKey(w, GLFW_KEY_W)) {
|
|
|
|
camera_accel[0] -= accel_scale;
|
|
|
|
}
|
|
|
|
if (glfwGetKey(w, GLFW_KEY_S)) {
|
|
|
|
camera_accel[0] += accel_scale;
|
|
|
|
}
|
|
|
|
if (glfwGetKey(w, GLFW_KEY_C)) {
|
|
|
|
camera_accel[1] -= accel_scale;
|
|
|
|
}
|
|
|
|
if (glfwGetKey(w, GLFW_KEY_SPACE)) {
|
|
|
|
camera_accel[1] += accel_scale;
|
|
|
|
}
|
|
|
|
if (glfwGetKey(w, GLFW_KEY_A)) {
|
|
|
|
camera_accel[2] -= accel_scale;
|
|
|
|
}
|
|
|
|
if (glfwGetKey(w, GLFW_KEY_D)) {
|
|
|
|
camera_accel[2] += accel_scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
Camera_Update(
|
|
|
|
&state.camera,
|
|
|
|
cur_width,
|
|
|
|
cur_height,
|
|
|
|
state.time.frame,
|
|
|
|
gGuiInputState.mousedX,
|
|
|
|
gGuiInputState.mousedY,
|
|
|
|
camera_accel);
|
|
|
|
}
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
if (ImGui::BeginMainMenuBar()) {
|
2023-03-25 23:30:13 +01:00
|
|
|
if (ImGui::BeginMenu("File")) {
|
2023-03-16 23:51:49 +01:00
|
|
|
if (ImGui::MenuItem("Quit")) {
|
|
|
|
glfwSetWindowShouldClose(w, true);
|
|
|
|
}
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
if (ImGui::BeginMenu("View")) {
|
|
|
|
ImGui::Checkbox("Graph Editor", &gApplicationConfig.graph_editor.visible);
|
|
|
|
ImGui::Checkbox(
|
|
|
|
"Camera Settings",
|
|
|
|
&gApplicationConfig.show_camera_widget);
|
|
|
|
ImGui::Checkbox(
|
|
|
|
"Skinned Mesh",
|
2023-03-26 00:13:19 +01:00
|
|
|
&gApplicationConfig.skinned_mesh_widget.visible);
|
2023-03-16 23:51:49 +01:00
|
|
|
ImGui::Separator();
|
2023-03-25 23:30:13 +01:00
|
|
|
ImGui::Checkbox(
|
|
|
|
"ImGui Demo",
|
|
|
|
&gApplicationConfig.show_imgui_demo_window);
|
2023-03-16 23:51:49 +01:00
|
|
|
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
ImGui::EndMainMenuBar();
|
|
|
|
}
|
|
|
|
|
2022-03-25 11:46:44 +01:00
|
|
|
// Animation Runtime
|
|
|
|
{
|
2023-03-25 23:30:13 +01:00
|
|
|
if (gApplicationConfig.show_camera_widget) {
|
2023-03-16 23:51:49 +01:00
|
|
|
ImGui::SetNextWindowPos(ImVec2(600, 30), ImGuiCond_FirstUseEver);
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(300, 170), ImGuiCond_FirstUseEver);
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
ImGui::Begin("Camera", &gApplicationConfig.show_camera_widget);
|
2023-03-16 23:51:49 +01:00
|
|
|
ImGui::SliderFloat3("pos", state.camera.pos, -100.f, 100.f);
|
|
|
|
ImGui::SliderFloat("near", &state.camera.near, 0.001f, 10.f);
|
|
|
|
ImGui::SliderFloat("far", &state.camera.far, 1.0f, 10000.f);
|
|
|
|
ImGui::SliderFloat("heading", &state.camera.heading, -180.0f, 180.f);
|
|
|
|
ImGui::SliderFloat("pitch", &state.camera.pitch, -179.0f, 179.f);
|
|
|
|
ImGui::End();
|
|
|
|
}
|
2022-02-11 16:51:18 +01:00
|
|
|
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(600, 60), ImGuiCond_FirstUseEver);
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(300, 400), ImGuiCond_FirstUseEver);
|
|
|
|
|
|
|
|
draw_grid();
|
2023-03-16 23:51:49 +01:00
|
|
|
|
2023-03-26 00:13:19 +01:00
|
|
|
if (gApplicationConfig.skinned_mesh_widget.visible) {
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.skinned_mesh_widget.position[0], gApplicationConfig.skinned_mesh_widget.position[1]), ImGuiCond_FirstUseEver);
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.skinned_mesh_widget.size[0], gApplicationConfig.skinned_mesh_widget.size[1]), ImGuiCond_FirstUseEver);
|
|
|
|
|
|
|
|
ImGui::Begin("SkinnedMesh", &gApplicationConfig.skinned_mesh_widget.visible);
|
|
|
|
|
|
|
|
ImVec2 skinned_mesh_widget_position = ImGui::GetWindowPos();
|
|
|
|
gApplicationConfig.skinned_mesh_widget.position[0] = skinned_mesh_widget_position.x;
|
|
|
|
gApplicationConfig.skinned_mesh_widget.position[1] = skinned_mesh_widget_position.y;
|
|
|
|
|
|
|
|
ImVec2 skinned_mesh_widget_size = ImGui::GetWindowSize();
|
|
|
|
gApplicationConfig.skinned_mesh_widget.size[0] = skinned_mesh_widget_size.x;
|
|
|
|
gApplicationConfig.skinned_mesh_widget.size[1] = skinned_mesh_widget_size.y;
|
|
|
|
|
2023-03-16 23:51:49 +01:00
|
|
|
skinned_mesh.DrawDebugUi();
|
2023-03-26 00:13:19 +01:00
|
|
|
|
|
|
|
ImGui::End();
|
2023-03-16 23:51:49 +01:00
|
|
|
}
|
2022-02-11 16:51:18 +01:00
|
|
|
|
2022-03-25 11:46:44 +01:00
|
|
|
// TODO: add AnimGraph to calculate pose
|
|
|
|
// skinned_mesh.CalcModelMatrices();
|
2022-02-11 16:51:18 +01:00
|
|
|
|
|
|
|
sgl_defaults();
|
|
|
|
sgl_matrix_mode_projection();
|
|
|
|
sgl_load_matrix((const float*)&state.camera.mtxProj);
|
|
|
|
sgl_matrix_mode_modelview();
|
|
|
|
sgl_load_matrix((const float*)&state.camera.mtxView);
|
|
|
|
RenderSkinnedMesh(skinned_mesh);
|
2022-03-25 11:46:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Animation Graph Editor
|
2023-03-25 23:30:13 +01:00
|
|
|
if (gApplicationConfig.graph_editor.visible) {
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.graph_editor.position[0], gApplicationConfig.graph_editor.position[1]), ImGuiCond_FirstUseEver);
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.graph_editor.size[0], gApplicationConfig.graph_editor.size[1]), ImGuiCond_FirstUseEver);
|
2022-02-11 16:51:18 +01:00
|
|
|
|
2022-03-25 11:46:44 +01:00
|
|
|
ImGui::Begin(
|
|
|
|
"Graph Editor",
|
2023-03-25 23:30:13 +01:00
|
|
|
&gApplicationConfig.graph_editor.visible,
|
2022-03-25 11:46:44 +01:00
|
|
|
ImGuiWindowFlags_MenuBar);
|
2023-03-25 23:30:13 +01:00
|
|
|
|
|
|
|
ImVec2 graph_editor_position = ImGui::GetWindowPos();
|
|
|
|
gApplicationConfig.graph_editor.position[0] = graph_editor_position.x;
|
|
|
|
gApplicationConfig.graph_editor.position[1] = graph_editor_position.y;
|
|
|
|
|
|
|
|
ImVec2 graph_editor_size = ImGui::GetWindowSize();
|
|
|
|
gApplicationConfig.graph_editor.size[0] = graph_editor_size.x;
|
|
|
|
gApplicationConfig.graph_editor.size[1] = graph_editor_size.y;
|
|
|
|
|
2022-02-11 16:51:18 +01:00
|
|
|
AnimGraphEditorUpdate();
|
2023-03-25 23:30:13 +01:00
|
|
|
|
2022-02-11 16:51:18 +01:00
|
|
|
ImGui::End();
|
2021-11-19 12:40:14 +01:00
|
|
|
}
|
2021-11-11 21:22:24 +01:00
|
|
|
|
|
|
|
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow()
|
2023-03-25 23:30:13 +01:00
|
|
|
if (gApplicationConfig.show_imgui_demo_window) {
|
2021-11-11 21:22:24 +01:00
|
|
|
ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver);
|
|
|
|
ImGui::ShowDemoWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
// the sokol_gfx draw pass
|
|
|
|
sg_begin_default_pass(&pass_action, cur_width, cur_height);
|
|
|
|
sgl_draw();
|
|
|
|
ImGui::Render();
|
|
|
|
draw_imgui(ImGui::GetDrawData());
|
|
|
|
sg_end_pass();
|
|
|
|
sg_commit();
|
|
|
|
glfwSwapBuffers(w);
|
|
|
|
glfwPollEvents();
|
|
|
|
}
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
save_application_config("animtestbed_config.json");
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
/* cleanup */
|
2022-02-11 16:51:18 +01:00
|
|
|
ImNodes::DestroyContext();
|
2021-11-11 21:22:24 +01:00
|
|
|
ImGui::DestroyContext();
|
|
|
|
sgl_shutdown();
|
|
|
|
sg_shutdown();
|
|
|
|
glfwTerminate();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LoadSkeleton(const char* _filename, ozz::animation::Skeleton* _skeleton) {
|
|
|
|
assert(_filename && _skeleton);
|
|
|
|
ozz::log::Out() << "Loading skeleton archive " << _filename << "."
|
|
|
|
<< std::endl;
|
|
|
|
ozz::io::File file(_filename, "rb");
|
|
|
|
if (!file.opened()) {
|
|
|
|
ozz::log::Err() << "Failed to open skeleton file " << _filename << "."
|
|
|
|
<< std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ozz::io::IArchive archive(&file);
|
|
|
|
if (!archive.TestTag<ozz::animation::Skeleton>()) {
|
|
|
|
ozz::log::Err() << "Failed to load skeleton instance from file "
|
|
|
|
<< _filename << "." << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Once the tag is validated, reading cannot fail.
|
|
|
|
archive >> *_skeleton;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LoadAnimation(
|
|
|
|
const char* _filename,
|
|
|
|
ozz::animation::Animation* _animation) {
|
|
|
|
assert(_filename && _animation);
|
|
|
|
ozz::log::Out() << "Loading animation archive: " << _filename << "."
|
|
|
|
<< std::endl;
|
|
|
|
ozz::io::File file(_filename, "rb");
|
|
|
|
if (!file.opened()) {
|
|
|
|
ozz::log::Err() << "Failed to open animation file " << _filename << "."
|
|
|
|
<< std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ozz::io::IArchive archive(&file);
|
|
|
|
if (!archive.TestTag<ozz::animation::Animation>()) {
|
|
|
|
ozz::log::Err() << "Failed to load animation instance from file "
|
|
|
|
<< _filename << "." << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Once the tag is validated, reading cannot fail.
|
|
|
|
archive >> *_animation;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_vec(const ozz::math::SimdFloat4& vec) {
|
|
|
|
sgl_v3f(ozz::math::GetX(vec), ozz::math::GetY(vec), ozz::math::GetZ(vec));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_line(
|
|
|
|
const ozz::math::SimdFloat4& v0,
|
|
|
|
const ozz::math::SimdFloat4& v1) {
|
|
|
|
draw_vec(v0);
|
|
|
|
draw_vec(v1);
|
|
|
|
}
|
|
|
|
|
2021-11-12 11:00:39 +01:00
|
|
|
static void draw_grid(void) {
|
|
|
|
sgl_defaults();
|
|
|
|
sgl_matrix_mode_projection();
|
|
|
|
sgl_load_matrix((const float*)&state.camera.mtxProj);
|
|
|
|
sgl_matrix_mode_modelview();
|
|
|
|
sgl_load_matrix((const float*)&state.camera.mtxView);
|
|
|
|
|
|
|
|
const int grid_size = 10;
|
|
|
|
|
|
|
|
sgl_begin_lines();
|
|
|
|
sgl_c3f(0.4f, 0.4f, 0.4f);
|
|
|
|
for (int i = -grid_size; i <= grid_size; i++) {
|
|
|
|
if (i == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-03-25 23:30:13 +01:00
|
|
|
ozz::math::SimdFloat4 p0 =
|
|
|
|
ozz::math::simd_float4::Load(i * 1.0f, 0.f, -grid_size * 1.0f, 1.f);
|
|
|
|
ozz::math::SimdFloat4 p1 =
|
|
|
|
ozz::math::simd_float4::Load(i * 1.0f, 0.f, grid_size * 1.0f, 1.f);
|
2021-11-12 11:00:39 +01:00
|
|
|
draw_line(p0, p1);
|
|
|
|
|
|
|
|
p0 = ozz::math::simd_float4::Load(-grid_size * 1.0f, 0.f, i * 1.0f, 1.f);
|
|
|
|
p1 = ozz::math::simd_float4::Load(grid_size * 1.0f, 0.f, i * 1.0f, 1.f);
|
|
|
|
draw_line(p0, p1);
|
|
|
|
}
|
2023-03-25 23:30:13 +01:00
|
|
|
sgl_c3f(0.7f, 0.4f, 0.2f);
|
|
|
|
ozz::math::SimdFloat4 p0 =
|
|
|
|
ozz::math::simd_float4::Load(0, 0.f, -grid_size * 1.0f, 1.f);
|
|
|
|
ozz::math::SimdFloat4 p1 =
|
|
|
|
ozz::math::simd_float4::Load(0, 0.f, grid_size * 1.0f, 1.f);
|
2021-11-12 11:00:39 +01:00
|
|
|
draw_line(p0, p1);
|
|
|
|
|
2023-03-25 23:30:13 +01:00
|
|
|
sgl_c3f(0.2f, 0.4f, 0.7f);
|
2021-11-12 11:00:39 +01:00
|
|
|
p0 = ozz::math::simd_float4::Load(-grid_size * 1.0f, 0.f, 0.f, 1.f);
|
|
|
|
p1 = ozz::math::simd_float4::Load(grid_size * 1.0f, 0.f, 0.f, 1.f);
|
|
|
|
draw_line(p0, p1);
|
|
|
|
|
|
|
|
sgl_end();
|
|
|
|
}
|
|
|
|
|
2021-11-11 21:22:24 +01:00
|
|
|
// draw ImGui draw lists via sokol-gfx
|
|
|
|
void draw_imgui(ImDrawData* draw_data) {
|
|
|
|
assert(draw_data);
|
|
|
|
if (draw_data->CmdListsCount == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// render the command list
|
|
|
|
sg_apply_pipeline(pip);
|
|
|
|
vs_params_t vs_params;
|
|
|
|
vs_params.disp_size.x = ImGui::GetIO().DisplaySize.x;
|
|
|
|
vs_params.disp_size.y = ImGui::GetIO().DisplaySize.y;
|
|
|
|
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE(vs_params));
|
|
|
|
for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++) {
|
|
|
|
const ImDrawList* cl = draw_data->CmdLists[cl_index];
|
|
|
|
|
|
|
|
// append vertices and indices to buffers, record start offsets in resource binding struct
|
|
|
|
const uint32_t vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
|
|
|
|
const uint32_t idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
|
|
|
|
const uint32_t vb_offset = sg_append_buffer(
|
|
|
|
bind.vertex_buffers[0],
|
|
|
|
{&cl->VtxBuffer.front(), vtx_size});
|
|
|
|
const uint32_t ib_offset =
|
|
|
|
sg_append_buffer(bind.index_buffer, {&cl->IdxBuffer.front(), idx_size});
|
|
|
|
/* don't render anything if the buffer is in overflow state (this is also
|
|
|
|
checked internally in sokol_gfx, draw calls that attempt from
|
|
|
|
overflowed buffers will be silently dropped)
|
|
|
|
*/
|
|
|
|
if (sg_query_buffer_overflow(bind.vertex_buffers[0])
|
|
|
|
|| sg_query_buffer_overflow(bind.index_buffer)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bind.vertex_buffer_offsets[0] = vb_offset;
|
|
|
|
bind.index_buffer_offset = ib_offset;
|
|
|
|
sg_apply_bindings(&bind);
|
|
|
|
|
|
|
|
int base_element = 0;
|
|
|
|
for (const ImDrawCmd& pcmd : cl->CmdBuffer) {
|
|
|
|
if (pcmd.UserCallback) {
|
|
|
|
pcmd.UserCallback(cl, &pcmd);
|
|
|
|
} else {
|
|
|
|
const int scissor_x = (int)(pcmd.ClipRect.x);
|
|
|
|
const int scissor_y = (int)(pcmd.ClipRect.y);
|
|
|
|
const int scissor_w = (int)(pcmd.ClipRect.z - pcmd.ClipRect.x);
|
|
|
|
const int scissor_h = (int)(pcmd.ClipRect.w - pcmd.ClipRect.y);
|
|
|
|
sg_apply_scissor_rect(scissor_x, scissor_y, scissor_w, scissor_h, true);
|
|
|
|
sg_draw(base_element, pcmd.ElemCount, 1);
|
|
|
|
}
|
|
|
|
base_element += pcmd.ElemCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|