1
0
mirror of synced 2024-12-20 11:35:54 +01:00
ImHex/plugins/visualizers/source/content/pl_visualizers/3d_model.cpp

926 lines
38 KiB
C++
Raw Normal View History

#include <algorithm>
#include <hex/helpers/utils.hpp>
#include <content/visualizer_helpers.hpp>
#include <fonts/codicons_font.h>
#include <fonts/blendericons_font.h>
#include <imgui.h>
#include <imgui_internal.h>
#include <hex/helpers/opengl.hpp>
#include <opengl_support.h>
#include <numbers>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization_manager.hpp>
#include <romfs/romfs.hpp>
#include <numeric>
namespace hex::plugin::visualizers {
namespace {
enum class IndexType : u8 {
U8,
U16,
U32,
Undefined,
};
template<class T>
struct Vectors {
std::vector<float> vertices;
std::vector<float> normals;
std::vector<float> colors;
std::vector<float> uv;
std::vector<T> indices;
};
template <class T>
struct LineVectors {
std::vector<float> vertices;
std::vector<float> colors;
std::vector<T> indices;
};
template<class T>
struct Buffers {
gl::Buffer<float> vertices;
gl::Buffer<float> normals;
gl::Buffer<float> colors;
gl::Buffer<float> uv;
gl::Buffer<T> indices;
};
template<class T>
struct LineBuffers {
gl::Buffer<float> vertices;
gl::Buffer<float> colors;
gl::Buffer<T> indices;
};
ImVec2 s_renderingWindowSize;
int s_drawMode = GL_TRIANGLES;
float s_nearLimit = 0.9F;
float s_farLimit = 100.0F;
float s_scaling = 1.0F;
float s_max;
bool s_isPerspective = true;
bool s_drawAxes = true;
bool s_drawGrid = true;
bool s_drawLightSource = true;
bool s_drawTexture = false;
bool s_shouldReset = false;
bool s_shouldUpdateLightSource = true;
bool s_shouldUpdateTexture = false;
std::vector<u32> s_badIndices;
IndexType s_indexType;
ImGuiExt::Texture s_modelTexture;
gl::Vector<float, 3> s_translation = { { 0.0F, 0.0F, -3.0F } };
gl::Vector<float, 3> s_rotation = { { 0.0F, 0.0F, 0.0F } };
gl::Vector<float, 3> s_lightPosition = { { -0.7F, 0.0F, 0.0F } };
gl::Vector<float, 4> s_lightBrightness = { { 0.5F, 0.5F, 0.5F, 32.0F } };
2024-02-24 23:34:29 +01:00
gl::Vector<float, 3> s_lightColor = { { 1.0F, 1.0F, 1.0F } };
gl::Matrix<float, 4, 4> s_rotate = gl::Matrix<float, 4, 4>::identity();
ImGuiExt::Texture s_texture;
std::fs::path s_texturePath;
u32 s_vertexCount;
const auto isIndexInRange = [](auto index) {
if (index >= s_vertexCount)
s_badIndices.push_back(index);
return index < s_vertexCount;
};
template<typename T>
void indicesForLines(std::vector<T> &vertexIndices) {
std::vector<u32> indices;
u32 vertexCount = vertexIndices.size() / 3;
indices.resize(vertexCount * 6);
for (u32 i = 0; i < vertexCount; i += 1) {
indices[i * 6] = vertexIndices[3 * i];
indices[(i * 6) + 1] = vertexIndices[(3 * i) + 1];
indices[(i * 6) + 2] = vertexIndices[(3 * i) + 1];
indices[(i * 6) + 3] = vertexIndices[(3 * i) + 2];
indices[(i * 6) + 4] = vertexIndices[(3 * i) + 2];
indices[(i * 6) + 5] = vertexIndices[3 * i];
}
vertexIndices.resize(indices.size());
for (u32 i = 0; i < indices.size(); ++i)
vertexIndices[i] = indices[i];
}
float getBoundingBox(const std::vector<float> &vertices) {
gl::Vector<float, 4> minWorld(std::numeric_limits<float>::infinity()), maxWorld(-std::numeric_limits<float>::infinity());
for (u32 i = 0; i < vertices.size(); i += 3) {
minWorld[0] = std::min(vertices[i], minWorld[0]);
minWorld[1] = std::min(vertices[i + 1], minWorld[1]);
minWorld[2] = std::min(vertices[i + 2], minWorld[2]);
maxWorld[0] = std::max(vertices[i], maxWorld[0]);
maxWorld[1] = std::max(vertices[i + 1], maxWorld[1]);
maxWorld[2] = std::max(vertices[i + 2], maxWorld[2]);
}
minWorld[3] = 1;
maxWorld[3] = 1;
gl::Vector<float, 4> minCamera = minWorld, maxCamera = maxWorld;
if (maxCamera[3] != 0)
2024-02-24 23:34:29 +01:00
maxCamera = maxCamera * (1.0F / maxCamera[3]);
if (minCamera[3] != 0)
2024-02-24 23:34:29 +01:00
minCamera = minCamera * (1.0F / minCamera[3]);
float max_X = std::max(std::fabs(minCamera[0]), std::fabs(maxCamera[0]));
float max_Y = std::max(std::fabs(minCamera[1]), std::fabs(maxCamera[1]));
return std::max(max_X, max_Y);
}
void setDefaultUVs(std::vector<float> &uv, size_t size) {
uv.resize(size / 3 * 2);
for (u32 i = 0; i < uv.size(); i += 2) {
uv[i ] = 0.0F;
uv[i + 1] = 0.0F;
}
}
void setDefaultColors(std::vector<float> &colors, size_t size, u32 color) {
colors.resize(size / 3 * 4);
float red = float((color >> 0) & 0xFF) / 255.0F;
float green = float((color >> 8) & 0xFF) / 255.0F;
float blue = float((color >> 16) & 0xFF) / 255.0F;
float alpha = float((color >> 24) & 0xFF) / 255.0F;
for (u32 i = 0; i < colors.size(); i += 4) {
colors[i] = red;
colors[i + 1] = green;
colors[i + 2] = blue;
colors[i + 3] = alpha;
}
}
void setNormals(const std::vector<float> &vertices, std::vector<float> &normals) {
for (u32 i = 0; i < normals.size(); i += 9) {
auto v1 = gl::Vector<float, 3>({vertices[i], vertices[i + 1], vertices[i + 2]});
auto v2 = gl::Vector<float, 3>({vertices[i + 3], vertices[i + 4], vertices[i + 5]});
auto v3 = gl::Vector<float, 3>({vertices[i + 6], vertices[i + 7], vertices[i + 8]});
auto normal = ((v2 - v1).cross(v3 - v1));
normals[i] += normal[0];
normals[i + 1] += normal[1];
normals[i + 2] += normal[2];
normals[i + 3] += normal[0];
normals[i + 4] += normal[1];
normals[i + 5] += normal[2];
normals[i + 6] += normal[0];
normals[i + 7] += normal[1];
normals[i + 8] += normal[2];
}
for (u32 i = 0; i < normals.size(); i += 3) {
auto normal = gl::Vector<float, 3>({normals[i], normals[i + 1], normals[i + 2]});
normal.normalize();
normals[i] = normal[0];
normals[i + 1] = normal[1];
normals[i + 2] = normal[2];
}
}
void setNormalsWithIndices(const std::vector<float> &vertices, std::vector<float> &normals, const std::vector<u32> &indices) {
for (u32 i = 0; i < indices.size(); i += 3) {
auto idx = indices[i];
auto idx1 = indices[i + 1];
auto idx2 = indices[i + 2];
auto v1 = gl::Vector<float, 3>({vertices[3 * idx], vertices[(3 * idx) + 1], vertices[(3 * idx) + 2]});
auto v2 = gl::Vector<float, 3>(
{vertices[3 * idx1], vertices[(3 * idx1) + 1], vertices[(3 * idx1) + 2]});
auto v3 = gl::Vector<float, 3>(
{vertices[3 * idx2], vertices[(3 * idx2) + 1], vertices[(3 * idx2) + 2]});
auto weighted = ((v2 - v1).cross(v3 - v1));
normals[3 * idx] += weighted[0];
normals[(3 * idx) + 1] += weighted[1];
normals[(3 * idx) + 2] += weighted[2];
normals[(3 * idx1)] += weighted[0];
normals[(3 * idx1) + 1] += weighted[1];
normals[(3 * idx1) + 2] += weighted[2];
normals[(3 * idx2)] += weighted[0];
normals[(3 * idx2) + 1] += weighted[1];
normals[(3 * idx2) + 2] += weighted[2];
}
for (u32 i = 0; i < normals.size(); i += 3) {
auto normal = gl::Vector<float, 3>({normals[i], normals[i + 1], normals[i + 2]});
auto mag = normal.magnitude();
if (mag > 0.001F) {
normals[i] = normal[0] / mag;
normals[i + 1] = normal[1] / mag;
normals[i + 2] = normal[2] / mag;
}
}
}
template<class T>
void loadVectors(Vectors<T> &vectors, IndexType indexType) {
s_max = getBoundingBox(vectors.vertices);
if (s_drawTexture)
setDefaultColors(vectors.colors, vectors.vertices.size(), 0x00000000);
else if (vectors.colors.empty())
setDefaultColors(vectors.colors, vectors.vertices.size(), 0xFF337FFF);
if (vectors.uv.empty())
setDefaultUVs(vectors.uv, vectors.vertices.size());
if (vectors.normals.empty()) {
vectors.normals.resize(vectors.vertices.size());
if (vectors.indices.empty() || (indexType == IndexType::Undefined)) {
setNormals(vectors.vertices, vectors.normals);
} else {
std::vector<u32> indices;
indices.resize(vectors.indices.size());
for (u32 i = 0; i < vectors.indices.size(); i += 1)
indices[i] = vectors.indices[i];
setNormalsWithIndices(vectors.vertices, vectors.normals, indices);
}
}
}
template<class T>
void loadLineVectors(LineVectors<T> &lineVectors, IndexType indexType) {
s_max = getBoundingBox(lineVectors.vertices);
if (lineVectors.colors.empty())
setDefaultColors(lineVectors.colors, lineVectors.vertices.size(), 0xFF337FFF);
if (indexType != IndexType::Undefined)
indicesForLines(lineVectors.indices);
}
void processKeyEvent(ImGuiKey key, float &variable, float increment, float acceleration) {
2024-04-12 22:56:10 +02:00
if (ImGui::IsKeyPressed(key)) {
auto temp = variable + (increment * acceleration);
if (variable * temp < 0.0F)
variable = 0.0F;
else
variable = temp;
}
}
void processInputEvents(gl::Vector<float, 3> &rotation, gl::Vector<float, 3> &translation, float &scaling, float &nearLimit, float &farLimit) {
auto accel = 1.0F;
2024-04-12 22:56:10 +02:00
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) ||
ImGui::IsKeyDown(ImGuiKey_RightShift))
accel = 10.0F;
auto dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Middle);
if (dragDelta.x != 0) {
rotation[1] += dragDelta.x * 0.0075F * accel;
}
if (dragDelta.y != 0) {
rotation[0] += dragDelta.y * 0.0075F * accel;
}
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Middle);
dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
translation[0] += dragDelta.x * 0.0075F * accel;
translation[1] -= dragDelta.y * 0.0075F * accel;
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Right);
auto scrollDelta = ImGui::GetIO().MouseWheel;
scaling += scrollDelta * 0.1F * accel;
scaling = std::max(scaling, 0.01F);
processKeyEvent(ImGuiKey_Keypad4, translation[0], -0.1F, accel);
processKeyEvent(ImGuiKey_Keypad6, translation[0], 0.1F, accel);
processKeyEvent(ImGuiKey_Keypad8, translation[1], 0.1F, accel);
processKeyEvent(ImGuiKey_Keypad2, translation[1], -0.1F, accel);
processKeyEvent(ImGuiKey_Keypad1, translation[2], 0.1F, accel);
processKeyEvent(ImGuiKey_Keypad7, translation[2], -0.1F, accel);
processKeyEvent(ImGuiKey_Keypad9, nearLimit, -0.01F, accel);
processKeyEvent(ImGuiKey_Keypad3, nearLimit, 0.01F, accel);
if (ImHexApi::System::isDebugBuild()) {
processKeyEvent(ImGuiKey_KeypadDivide, farLimit, -1.0F, accel);
processKeyEvent(ImGuiKey_KeypadMultiply, farLimit, 1.0F, accel);
}
processKeyEvent(ImGuiKey_KeypadAdd, rotation[2], -0.075F, accel);
processKeyEvent(ImGuiKey_KeypadSubtract, rotation[2], 0.075F, accel);
rotation[2] = std::fmod(rotation[2], 2 * std::numbers::pi_v<float>);
}
bool validateVector(const std::vector<float> &vector, u32 vertexCount, u32 divisor, const std::string &name,std::string &errorMessage) {
if (!vector.empty()) {
if (vector.size() % divisor != 0) {
errorMessage = name + " must be a multiple of " + std::to_string(divisor);
return false;
}
} else {
errorMessage = name + " can't be empty";
return false;
}
auto vectorCount = vector.size()/divisor;
if (vectorCount != vertexCount) {
errorMessage = hex::format("Expected {} colors, got {}", vertexCount, vectorCount);
return false;
}
return true;
}
template <class T>
void bindBuffers(Buffers<T> &buffers, const gl::VertexArray &vertexArray, Vectors<T> vectors, IndexType indexType) {
buffers.vertices = {};
buffers.normals = {};
buffers.colors = {};
buffers.uv = {};
buffers.indices = {};
vertexArray.bind();
u32 vertexCount = vectors.vertices.size() / 3;
std::string errorMessage;
if (indexType != IndexType::Undefined && !vectors.indices.empty())
buffers.indices = gl::Buffer<T>(gl::BufferType::Index, vectors.indices);
if (validateVector(vectors.vertices, vertexCount, 3, "Positions", errorMessage)) {
if ((indexType == IndexType::Undefined || vectors.indices.empty()) && vertexCount % 3 != 0)
throw std::runtime_error("Without indices vertices must be a multiple of 3");
else
buffers.vertices = gl::Buffer<float>(gl::BufferType::Vertex, vectors.vertices);
} else
throw std::runtime_error(errorMessage);
if (validateVector(vectors.colors, vertexCount, 4, "Colors", errorMessage))
buffers.colors = gl::Buffer<float>(gl::BufferType::Vertex, vectors.colors);
else
throw std::runtime_error(errorMessage);
if (validateVector(vectors.normals, vertexCount, 3, "Normals", errorMessage))
buffers.normals = gl::Buffer<float>(gl::BufferType::Vertex, vectors.normals);
else
throw std::runtime_error(errorMessage);
if (validateVector(vectors.uv, vertexCount, 2, "UV coordinates", errorMessage))
buffers.uv = gl::Buffer<float>(gl::BufferType::Vertex, vectors.uv);
else
throw std::runtime_error(errorMessage);
vertexArray.addBuffer(0, buffers.vertices);
vertexArray.addBuffer(1, buffers.colors, 4);
vertexArray.addBuffer(2, buffers.normals);
vertexArray.addBuffer(3, buffers.uv, 2);
buffers.vertices.unbind();
buffers.colors.unbind();
buffers.normals.unbind();
buffers.uv.unbind();
if (indexType != IndexType::Undefined)
buffers.indices.unbind();
vertexArray.unbind();
}
template <class T>
void bindLineBuffers(LineBuffers<T> &lineBuffers, const gl::VertexArray &vertexArray, const LineVectors<T> &lineVectors, IndexType indexType) {
lineBuffers.vertices = {};
lineBuffers.colors = {};
lineBuffers.indices = {};
u32 vertexCount = lineVectors.vertices.size() / 3;
vertexArray.bind();
std::string errorMessage;
if (indexType != IndexType::Undefined)
lineBuffers.indices = gl::Buffer<T>(gl::BufferType::Index, lineVectors.indices);
if (validateVector(lineVectors.vertices, vertexCount, 3, "Positions", errorMessage)) {
if ((indexType == IndexType::Undefined || lineVectors.indices.empty()) && vertexCount % 3 != 0)
throw std::runtime_error("Without indices vertices must be a multiple of 3");
else
lineBuffers.vertices = gl::Buffer<float>(gl::BufferType::Vertex, lineVectors.vertices);
} else
throw std::runtime_error(errorMessage);
if (validateVector(lineVectors.colors, vertexCount, 4, "Colors", errorMessage))
lineBuffers.colors = gl::Buffer<float>(gl::BufferType::Vertex, lineVectors.colors);
else
throw std::runtime_error(errorMessage);
vertexArray.addBuffer(0, lineBuffers.vertices);
vertexArray.addBuffer(1, lineBuffers.colors, 4);
lineBuffers.vertices.unbind();
lineBuffers.colors.unbind();
if (indexType != IndexType::Undefined)
lineBuffers.indices.unbind();
vertexArray.unbind();
}
void drawWindow(const ImGuiExt::Texture &texture, ImVec2 &renderingWindowSize, const gl::Matrix<float, 4, 4> &mvp) {
auto textureSize = texture.getSize();
auto textureWidth = textureSize.x;
auto textureHeight = textureSize.y;
ImVec2 screenPos = ImGui::GetCursorScreenPos();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowSizeConstraints(scaled({ 350, 350 }), ImVec2(FLT_MAX, FLT_MAX));
if (ImGui::BeginChild("##image", textureSize, ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_Border, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
renderingWindowSize = ImGui::GetContentRegionAvail();
ImGui::Image(texture, textureSize, ImVec2(0, 1), ImVec2(1, 0));
if (s_drawAxes) {
gl::Matrix<float, 4, 4> axes = gl::Matrix<float, 4, 4>::identity();
2024-02-24 23:34:29 +01:00
axes(0, 3) = 1.0F;
axes(1, 3) = 1.0F;
axes(2, 3) = 1.0F;
axes = axes * mvp;
2024-02-24 23:34:29 +01:00
bool showX = axes(0, 3) > 0.0F;
bool showY = axes(1, 3) > 0.0F;
bool showZ = axes(2, 3) > 0.0F;
2024-02-24 23:34:29 +01:00
axes.updateRow(0, axes.getRow(0) * (1.0F / axes(0, 3)));
axes.updateRow(1, axes.getRow(1) * (1.0F / axes(1, 3)));
axes.updateRow(2, axes.getRow(2) * (1.0F / axes(2, 3)));
2024-02-24 23:34:29 +01:00
auto axesPosx = (axes.getColumn(0) + 1.0F) * (textureWidth / 2.0F);
auto axesPosy = (axes.getColumn(1) + 1.0F) * (-textureHeight / 2.0F) + textureHeight;
ImDrawList *drawList = ImGui::GetWindowDrawList();
if (showX)
drawList->AddText(ImVec2(axesPosx[0], axesPosy[0]) + screenPos, IM_COL32(255, 0, 0, 255), "X");
if (showY)
drawList->AddText(ImVec2(axesPosx[1], axesPosy[1]) + screenPos, IM_COL32(0, 255, 0, 255), "Y");
if (showZ)
drawList->AddText(ImVec2(axesPosx[2], axesPosy[2]) + screenPos, IM_COL32(0, 0, 255, 255), "Z");
}
if (ImHexApi::System::isDebugBuild()) {
auto mousePos = ImClamp(ImGui::GetMousePos() - screenPos, { 0, 0 }, textureSize);
ImDrawList *drawList = ImGui::GetWindowDrawList();
drawList->AddText(
screenPos + scaled({ 5, 5 }),
ImGui::GetColorU32(ImGuiCol_Text),
hex::format("X: {:.5}\nY: {:.5}", mousePos.x, mousePos.y).c_str());
}
}
ImGui::EndChild();
ImGui::PopStyleVar();
// Draw axis arrows toggle
{
ImGui::PushID(1);
if (ImGuiExt::DimmedIconToggle(ICON_BI_EMPTY_ARROWS, &s_drawAxes))
s_shouldReset = true;
ImGui::PopID();
}
ImGui::SameLine();
// Draw grid toggle
{
ImGui::PushID(2);
if (ImGuiExt::DimmedIconToggle(s_isPerspective ? ICON_BI_GRID : ICON_VS_SYMBOL_NUMBER, &s_drawGrid))
s_shouldReset = true;
ImGui::PopID();
}
ImGui::SameLine();
// Draw light source toggle
{
ImGui::PushID(3);
if (ImGuiExt::DimmedIconToggle(ICON_VS_LIGHTBULB, &s_drawLightSource))
s_shouldReset = true;
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
ImGui::OpenPopup("LightSettings");
}
if (ImGui::BeginPopup("LightSettings")) {
if (ImGui::DragFloat3("hex.visualizers.pl_visualizer.3d.light_position"_lang, s_lightPosition.data(), 0.05F)) {
s_shouldUpdateLightSource = true;
}
ImGui::SliderFloat("hex.visualizers.pl_visualizer.3d.ambient_brightness"_lang, &s_lightBrightness.data()[0], 0, 2);
ImGui::SliderFloat("hex.visualizers.pl_visualizer.3d.diffuse_brightness"_lang, &s_lightBrightness.data()[1], 0, 2);
ImGui::SliderFloat("hex.visualizers.pl_visualizer.3d.specular_brightness"_lang, &s_lightBrightness.data()[2], 0, 2);
ImGui::SliderFloat("hex.visualizers.pl_visualizer.3d.object_reflectiveness"_lang, &s_lightBrightness.data()[3], 0, 64);
if (ImGui::ColorEdit3("hex.visualizers.pl_visualizer.3d.light_color"_lang, s_lightColor.data()))
s_shouldUpdateLightSource = true;
ImGui::EndPopup();
}
ImGui::PopID();
}
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
// Draw projection toggle
{
ImGui::PushID(4);
if (ImGuiExt::DimmedIconToggle(ICON_BI_VIEW_PERSPECTIVE, ICON_BI_VIEW_ORTHO, &s_isPerspective)) {
s_shouldReset = true;
}
ImGui::PopID();
}
ImGui::SameLine();
// Draw solid / line mode toggle
{
ImGui::PushID(4);
bool isSolid = s_drawMode == GL_TRIANGLES;
if (ImGuiExt::DimmedIconToggle(ICON_BI_MOD_SOLIDIFY, ICON_BI_CUBE , &isSolid)) {
s_shouldReset = true;
s_drawMode = isSolid ? GL_TRIANGLES : GL_LINES;
}
ImGui::PopID();
}
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
if (ImGuiExt::DimmedButton("hex.ui.common.reset"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
s_translation = { { 0.0F, 0.0F, -3.0F } };
s_rotation = { { 0.0F, 0.0F, 0.0F } };
s_scaling = 1.0F;
}
// Draw more settings
if (ImGui::CollapsingHeader("hex.visualizers.pl_visualizer.3d.more_settings"_lang)) {
if (ImGuiExt::InputFilePicker("hex.visualizers.pl_visualizer.3d.texture_file"_lang, s_texturePath, {}))
s_shouldUpdateTexture = true;
}
}
}
template <class T>
void processRendering(std::shared_ptr<pl::ptrn::Pattern> verticesPattern, std::shared_ptr<pl::ptrn::Pattern> indicesPattern,
std::shared_ptr<pl::ptrn::Pattern> normalsPattern, std::shared_ptr<pl::ptrn::Pattern> colorsPattern,
std::shared_ptr<pl::ptrn::Pattern> uvPattern) {
static gl::LightSourceVectors sourceVectors(20);
2023-12-27 16:33:49 +01:00
static gl::VertexArray sourceVertexArray = {};
static gl::LightSourceBuffers sourceBuffers(sourceVertexArray, sourceVectors);
2023-12-27 16:33:49 +01:00
static gl::VertexArray gridVertexArray = {};
static gl::GridVectors gridVectors(9);
static gl::GridBuffers gridBuffers(gridVertexArray, gridVectors);
2023-12-27 16:33:49 +01:00
static gl::VertexArray axesVertexArray = {};
static gl::AxesVectors axesVectors;
static gl::AxesBuffers axesBuffers(axesVertexArray, axesVectors);
2023-12-27 16:33:49 +01:00
static gl::VertexArray vertexArray = {};
if (s_renderingWindowSize.x <= 0 || s_renderingWindowSize.y <= 0)
s_renderingWindowSize = {350_scaled, 350_scaled};
gl::Matrix<float, 4, 4> mvp(0);
static Buffers<T> buffers;
static LineBuffers<T> lineBuffers;
if (s_shouldReset) {
s_shouldReset = false;
s_shouldUpdateLightSource = true;
if (s_drawMode == GL_TRIANGLES) {
Vectors<T> vectors;
vectors.vertices = patternToArray<float>(verticesPattern.get());
s_vertexCount = vectors.vertices.size() / 3;
if (s_indexType != IndexType::Undefined) {
vectors.indices = patternToArray<T>(indicesPattern.get());
s_badIndices.clear();
auto indexCount = vectors.indices.size();
if (indexCount < 3 || indexCount % 3 != 0) {
throw std::runtime_error("Index count must be a multiple of 3");
}
auto booleans = std::views::transform(vectors.indices,isIndexInRange);
if (!std::accumulate(std::begin(booleans), std::end(booleans), true, std::logical_and<>())) {
std::string badIndicesStr = "Invalid indices: ";
for (auto badIndex : s_badIndices)
badIndicesStr += std::to_string(badIndex) + ", ";
badIndicesStr.pop_back();
badIndicesStr.pop_back();
badIndicesStr += hex::format(" for {} vertices",s_vertexCount);
throw std::runtime_error(badIndicesStr);
}
}
if (colorsPattern != nullptr)
vectors.colors = patternToArray<float>(colorsPattern.get());
if (normalsPattern != nullptr)
vectors.normals = patternToArray<float>(normalsPattern.get());
if (uvPattern != nullptr)
vectors.uv = patternToArray<float>(uvPattern.get());
loadVectors(vectors, s_indexType);
bindBuffers(buffers, vertexArray, vectors, s_indexType);
} else {
LineVectors<T> lineVectors;
lineVectors.vertices = patternToArray<float>(verticesPattern.get());
s_vertexCount = lineVectors.vertices.size() / 3;
if (s_indexType != IndexType::Undefined) {
lineVectors.indices = patternToArray<T>(indicesPattern.get());
auto indexCount = lineVectors.indices.size();
if (indexCount < 3 || indexCount % 3 != 0) {
throw std::runtime_error("Index count must be a multiple of 3");
}
s_badIndices.clear();
if (!std::ranges::all_of(lineVectors.indices,isIndexInRange)) {
std::string badIndicesStr = "Invalid indices: ";
for (auto badIndex : s_badIndices)
badIndicesStr += std::to_string(badIndex) + ", ";
badIndicesStr.pop_back();
badIndicesStr.pop_back();
badIndicesStr += hex::format(" for {} vertices",s_vertexCount);
throw std::runtime_error(badIndicesStr);
}
}
if (colorsPattern != nullptr)
lineVectors.colors = patternToArray<float>(colorsPattern.get());
loadLineVectors(lineVectors, s_indexType);
bindLineBuffers(lineBuffers, vertexArray, lineVectors, s_indexType);
}
}
if (s_shouldUpdateLightSource) {
s_shouldUpdateLightSource = false;
sourceVectors.moveTo(s_lightPosition);
sourceVectors.setColor(s_lightColor[0], s_lightColor[1], s_lightColor[2]);
sourceBuffers.moveVertices(sourceVertexArray, sourceVectors);
sourceBuffers.updateColors(sourceVertexArray, sourceVectors);
}
{
gl::Matrix<float, 4, 4> model(0);
gl::Matrix<float, 4, 4> scaledModel(0);
gl::Matrix<float, 4, 4> view(0);
gl::Matrix<float, 4, 4> projection(0);
unsigned width = std::floor(s_renderingWindowSize.x);
unsigned height = std::floor(s_renderingWindowSize.y);
gl::FrameBuffer frameBuffer(width, height);
gl::Texture renderTexture(width, height);
frameBuffer.attachTexture(renderTexture);
frameBuffer.bind();
s_rotate = gl::getRotationMatrix<float>(s_rotation, true, gl::RotationSequence::ZYX);
gl::Matrix<float, 4, 4> scale = gl::Matrix<float, 4, 4>::identity();
gl::Matrix<float, 4, 4> scaleForVertices = gl::Matrix<float, 4, 4>::identity();
gl::Matrix<float, 4, 4> translate = gl::Matrix<float, 4, 4>::identity();
float totalScale;
2024-02-24 23:34:29 +01:00
float viewWidth = s_renderingWindowSize.x / 500.0F;
float viewHeight = s_renderingWindowSize.y / 500.0F;
glViewport(0,0 , GLsizei(renderTexture.getWidth()), GLsizei(renderTexture.getHeight()));
glDepthRangef(s_nearLimit, s_farLimit);
glClearColor(0.00F, 0.00F, 0.00F, 0.00F);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
if (!s_isPerspective) {
projection = gl::GetOrthographicMatrix(viewWidth, viewHeight, s_nearLimit, s_farLimit, false);
totalScale = s_scaling / (std::fabs(s_translation[2]));
scale(0, 0) = totalScale;
scale(1, 1) = totalScale;
scale(2, 2) = totalScale;
translate(3, 0) = s_translation[0] / std::fabs(s_translation[2]);
translate(3, 1) = s_translation[1] / std::fabs(s_translation[2]);
translate(3, 2) = s_translation[2];
} else {
projection = gl::GetPerspectiveMatrix(viewWidth, viewHeight, s_nearLimit, s_farLimit, false);
totalScale = s_scaling;
scale(0, 0) = totalScale;
scale(1, 1) = totalScale;
scale(2, 2) = totalScale;
translate(3, 0) = s_translation[0];
translate(3, 1) = s_translation[1];
translate(3, 2) = s_translation[2];
}
2024-02-24 23:34:29 +01:00
totalScale /= (3.0F * s_max);
scaleForVertices(0, 0) = totalScale;
scaleForVertices(1, 1) = totalScale;
scaleForVertices(2, 2) = totalScale;
model = s_rotate * scale;
scaledModel = s_rotate * scaleForVertices;
view = translate;
mvp = model * view * projection;
if (s_drawMode == GL_TRIANGLES) {
static gl::Shader shader = gl::Shader(
romfs::get("shaders/default/vertex.glsl").string(),
romfs::get("shaders/default/fragment.glsl").string()
);
shader.bind();
shader.setUniform("modelScale", scaledModel);
shader.setUniform("modelMatrix", model);
shader.setUniform("viewMatrix", view);
shader.setUniform("projectionMatrix",projection);
shader.setUniform("lightPosition", s_lightPosition);
shader.setUniform("lightBrightness", s_lightBrightness);
shader.setUniform("lightColor", s_lightColor);
vertexArray.bind();
if (s_shouldUpdateTexture) {
s_shouldUpdateTexture = false;
s_modelTexture = ImGuiExt::Texture::fromImage(s_texturePath, ImGuiExt::Texture::Filter::Nearest);
if (s_modelTexture.isValid()) {
s_drawTexture = true;
}
}
if (s_drawTexture)
glBindTexture(GL_TEXTURE_2D, s_modelTexture);
buffers.indices.bind();
if (buffers.indices.getSize() == 0)
buffers.vertices.draw(s_drawMode);
else
buffers.indices.draw(s_drawMode);
buffers.indices.unbind();
} else {
static gl::Shader lineShader = gl::Shader(
romfs::get("shaders/default/lineVertex.glsl").string(),
romfs::get("shaders/default/lineFragment.glsl").string()
);
lineShader.bind();
lineShader.setUniform("modelMatrix", scaledModel);
lineShader.setUniform("viewMatrix", view);
lineShader.setUniform("projectionMatrix", projection);
vertexArray.bind();
lineBuffers.indices.bind();
if (lineBuffers.indices.getSize() == 0)
lineBuffers.vertices.draw(s_drawMode);
else
lineBuffers.indices.draw(s_drawMode);
lineBuffers.indices.unbind();
}
if (s_drawGrid || s_drawAxes) {
static auto gridAxesShader = gl::Shader(
romfs::get("shaders/default/lineVertex.glsl").string(),
romfs::get("shaders/default/lineFragment.glsl").string()
);
gridAxesShader.bind();
gridAxesShader.setUniform("modelMatrix", model);
gridAxesShader.setUniform("viewMatrix", view);
gridAxesShader.setUniform("projectionMatrix", projection);
if (s_drawGrid) {
gridVertexArray.bind();
gridBuffers.getIndices().bind();
gridBuffers.getIndices().draw(GL_LINES);
gridBuffers.getIndices().unbind();
gridVertexArray.unbind();
}
if (s_drawAxes) {
axesVertexArray.bind();
axesBuffers.getIndices().bind();
axesBuffers.getIndices().draw(GL_LINES);
axesBuffers.getIndices().unbind();
axesVertexArray.unbind();
}
gridAxesShader.unbind();
}
if (s_drawLightSource) {
static auto sourceShader = gl::Shader(
romfs::get("shaders/default/lightVertex.glsl").string(),
romfs::get("shaders/default/lightFragment.glsl").string()
);
sourceShader.bind();
sourceShader.setUniform("modelMatrix", model);
sourceShader.setUniform("viewMatrix", view);
sourceShader.setUniform("projectionMatrix", projection);
sourceVertexArray.bind();
sourceBuffers.getIndices().bind();
sourceBuffers.getIndices().draw(GL_TRIANGLES);
sourceBuffers.getIndices().unbind();
sourceVertexArray.unbind();
sourceShader.unbind();
}
vertexArray.unbind();
frameBuffer.unbind();
s_texture = ImGuiExt::Texture::fromGLTexture(renderTexture.release(), GLsizei(renderTexture.getWidth()), GLsizei(renderTexture.getHeight()));
drawWindow(s_texture, s_renderingWindowSize, mvp);
}
}
void draw3DVisualizer(pl::ptrn::Pattern &, bool shouldReset, std::span<const pl::core::Token::Literal> arguments) {
std::shared_ptr<pl::ptrn::Pattern> verticesPattern = arguments[0].toPattern();
std::shared_ptr<pl::ptrn::Pattern> indicesPattern = arguments[1].toPattern();
std::shared_ptr<pl::ptrn::Pattern> normalsPattern = nullptr;
std::shared_ptr<pl::ptrn::Pattern> colorsPattern = nullptr;
std::shared_ptr<pl::ptrn::Pattern> uvPattern = nullptr;
std::string textureFile;
if (arguments.size() > 2) {
normalsPattern = arguments[2].toPattern();
if (arguments.size() > 3) {
colorsPattern = arguments[3].toPattern();
if (arguments.size() > 4) {
uvPattern = arguments[4].toPattern();
if (arguments.size() > 5)
textureFile = arguments[5].toString();
}
}
}
s_texturePath = textureFile;
s_drawTexture = !textureFile.empty();
if (shouldReset)
s_shouldReset = true;
processInputEvents(s_rotation, s_translation, s_scaling, s_nearLimit, s_farLimit);
auto *iterable = dynamic_cast<pl::ptrn::IIterable*>(indicesPattern.get());
if (iterable != nullptr && iterable->getEntryCount() > 0) {
const auto &content = iterable->getEntry(0);
if (content->getSize() == 1) {
s_indexType = IndexType::U8;
processRendering<u8>(verticesPattern, indicesPattern, normalsPattern, colorsPattern, uvPattern);
} else if (content->getSize() == 2) {
s_indexType = IndexType::U16;
processRendering<u16>(verticesPattern, indicesPattern, normalsPattern, colorsPattern, uvPattern);
} else if (content->getSize() == 4) {
s_indexType = IndexType::U32;
processRendering<u32>(verticesPattern, indicesPattern, normalsPattern, colorsPattern, uvPattern);
}
} else {
s_indexType = IndexType::Undefined;
processRendering<u8>(verticesPattern, indicesPattern, normalsPattern, colorsPattern, uvPattern);
}
}
}