mirror of
https://github.com/liuk7071/ChonkyStation.git
synced 2025-04-02 10:52:38 -04:00
514 lines
17 KiB
C++
514 lines
17 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2021 PCSX-Redux authors *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU 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 General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
|
|
***************************************************************************/
|
|
|
|
#include "shader-editor.h"
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <istream>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <streambuf>
|
|
|
|
#include <glad/glad.h>
|
|
|
|
static const GLchar* const c_defaultVertexShader = R"(
|
|
#version 430 core
|
|
// The Vertex Shader isn't necessarily very useful, but is still provided here.
|
|
precision highp float;
|
|
layout (location = 0) in vec2 Position;
|
|
layout (location = 1) in vec2 UV;
|
|
layout (location = 2) in vec4 Color;
|
|
uniform mat4 u_projMatrix;
|
|
out vec2 Frag_UV;
|
|
out vec4 Frag_Color;
|
|
void main() {
|
|
Frag_UV = UV;
|
|
Frag_Color = Color;
|
|
gl_Position = u_projMatrix * vec4(Position.xy, 0, 1);
|
|
}
|
|
)";
|
|
|
|
static const GLchar* const c_defaultPixelShader = R"(
|
|
#version 430 core
|
|
// The Pixel Shader is most likely what the user will want to change.
|
|
precision highp float;
|
|
uniform sampler2D Texture;
|
|
in vec2 Frag_UV;
|
|
in vec4 Frag_Color;
|
|
layout (location = 0) out vec4 Out_Color;
|
|
void main() {
|
|
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
|
|
Out_Color.a = 1.0;
|
|
}
|
|
)";
|
|
|
|
static const char* const c_defaultLuaInvoker = R"(
|
|
-- All of this code is sandboxed, as in any global variable it
|
|
-- creates will be attached to a local environment. The global
|
|
-- environment is still accessible as normal.
|
|
-- Note that the environment is wiped every time the shader is
|
|
-- recompiled one way or another.
|
|
|
|
-- The environment will have the `shaderProgramID` variable set
|
|
-- before being evaluated, meaning this code is perfectly valid:
|
|
function Constructor(shaderProgramID)
|
|
-- Cache some Shader Attributes locations
|
|
print('Shader compiled: program ID = ' .. shaderProgramID)
|
|
end
|
|
Constructor(shaderProgramID)
|
|
|
|
-- This function is called to issue an ImGui::Image when it's time
|
|
-- to display the video output of the emulated screen. It can
|
|
-- prepare some values to later attach to the shader program.
|
|
--
|
|
-- This function won't be called for non-ImGui renders, such as
|
|
-- the offscreen render of the vram.
|
|
function Image(textureID, srcSizeX, srcSizeY, dstSizeX, dstSizeY)
|
|
imgui.Image(textureID, dstSizeX, dstSizeY, 0, 0, 1, 1)
|
|
end
|
|
|
|
-- This function is called to draw some UI, at the same time
|
|
-- as the shader editor, but regardless of the status of the
|
|
-- shader editor window. Its purpose is to potentially display
|
|
-- a piece of UI to let the user interact with the shader program.
|
|
|
|
-- Returning true from it will cause the environment to be saved.
|
|
function Draw()
|
|
end
|
|
|
|
-- This function is called just before executing the shader program,
|
|
-- to give it a chance to bind some attributes to it, that'd come
|
|
-- from either the global state, or the locally computed attributes
|
|
-- from the two functions above.
|
|
--
|
|
-- The last six parameters will only exist for non-ImGui renders.
|
|
function BindAttributes(textureID, shaderProgramID, srcLocX, srcLocY, srcSizeX, srcSizeY, dstSizeX, dstSizeY)
|
|
end
|
|
)";
|
|
|
|
ShaderEditor::ShaderEditor(const std::string& base, const std::string_view& dVS,
|
|
const std::string_view& dPS, const std::string_view& dL)
|
|
: m_baseFilename(base) {
|
|
std::filesystem::path f = base;
|
|
{
|
|
f.replace_extension("vert");
|
|
std::ifstream in(f, std::ifstream::in);
|
|
if (in) {
|
|
std::ostringstream code;
|
|
code << in.rdbuf();
|
|
in.close();
|
|
m_vertexShaderEditor.setText(code.str());
|
|
}
|
|
else {
|
|
m_vertexShaderEditor.setText(c_defaultVertexShader);
|
|
}
|
|
}
|
|
{
|
|
f.replace_extension("frag");
|
|
std::ifstream in(f, std::ifstream::in);
|
|
if (in) {
|
|
std::ostringstream code;
|
|
code << in.rdbuf();
|
|
in.close();
|
|
m_pixelShaderEditor.setText(code.str());
|
|
}
|
|
else {
|
|
m_pixelShaderEditor.setText(c_defaultPixelShader);
|
|
}
|
|
}
|
|
{
|
|
f.replace_extension("lua");
|
|
std::ifstream in(f, std::ifstream::in);
|
|
if (in) {
|
|
std::ostringstream code;
|
|
code << in.rdbuf();
|
|
in.close();
|
|
m_luaEditor.setText(code.str());
|
|
}
|
|
else {
|
|
m_luaEditor.setText(c_defaultLuaInvoker);
|
|
}
|
|
}
|
|
}
|
|
|
|
ShaderEditor::~ShaderEditor() {
|
|
glDeleteVertexArrays(1, &m_vao);
|
|
glDeleteBuffers(1, &m_vbo);
|
|
if (m_shaderProgram != 0) {
|
|
glDeleteProgram(m_shaderProgram);
|
|
}
|
|
}
|
|
|
|
void ShaderEditor::setDefaults() {
|
|
m_vertexShaderEditor.setText(c_defaultVertexShader);
|
|
m_pixelShaderEditor.setText(c_defaultPixelShader);
|
|
m_luaEditor.setText(c_defaultLuaInvoker);
|
|
}
|
|
|
|
void ShaderEditor::init() {
|
|
glGenVertexArrays(1, &m_vao);
|
|
glGenBuffers(1, &m_vbo);
|
|
}
|
|
|
|
std::optional<GLuint> ShaderEditor::compile(
|
|
const std::vector<std::string_view>& mandatoryAttributes) {
|
|
m_setupVAO = true;
|
|
m_shaderProjMtxLoc = -1;
|
|
GLint status = 0;
|
|
|
|
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
|
auto VS = getVertexText();
|
|
const char* VSv = VS.data();
|
|
glShaderSource(vertexShader, 1, &VSv, 0);
|
|
glCompileShader(vertexShader);
|
|
|
|
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);
|
|
if (status == 0) {
|
|
GLint maxLength;
|
|
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
|
|
char* log = (char*)malloc(maxLength);
|
|
glGetShaderInfoLog(vertexShader, maxLength, &maxLength, log);
|
|
|
|
m_errorMessage = format("Vertex Shader compilation error: %s\n", log);
|
|
|
|
free(log);
|
|
glDeleteShader(vertexShader);
|
|
return std::nullopt;
|
|
}
|
|
|
|
GLuint pixelShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
auto PS = getPixelText();
|
|
const char* PSv = PS.data();
|
|
glShaderSource(pixelShader, 1, &PSv, 0);
|
|
glCompileShader(pixelShader);
|
|
|
|
glGetShaderiv(pixelShader, GL_COMPILE_STATUS, &status);
|
|
if (status == 0) {
|
|
GLint maxLength;
|
|
glGetShaderiv(pixelShader, GL_INFO_LOG_LENGTH, &maxLength);
|
|
char* log = (char*)malloc(maxLength);
|
|
|
|
glGetShaderInfoLog(pixelShader, maxLength, &maxLength, log);
|
|
|
|
m_errorMessage = format("Pixel Shader compilation error: %s\n", log);
|
|
|
|
free(log);
|
|
glDeleteShader(vertexShader);
|
|
glDeleteShader(pixelShader);
|
|
return std::nullopt;
|
|
}
|
|
|
|
GLuint shaderProgram = glCreateProgram();
|
|
glAttachShader(shaderProgram, vertexShader);
|
|
glAttachShader(shaderProgram, pixelShader);
|
|
|
|
glLinkProgram(shaderProgram);
|
|
|
|
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &status);
|
|
if (status == 0) {
|
|
GLint maxLength;
|
|
glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &maxLength);
|
|
char* log = (char*)malloc(maxLength);
|
|
|
|
glGetProgramInfoLog(shaderProgram, maxLength, &maxLength, log);
|
|
|
|
m_errorMessage = format("Link error: %s\n", log);
|
|
|
|
free(log);
|
|
glDeleteProgram(shaderProgram);
|
|
glDeleteShader(vertexShader);
|
|
glDeleteShader(pixelShader);
|
|
return std::nullopt;
|
|
}
|
|
|
|
for (auto attrib : mandatoryAttributes) {
|
|
int loc = glGetAttribLocation(shaderProgram, attrib.data());
|
|
if (loc == -1) {
|
|
m_errorMessage = format(("Missing attribute %s in shader program"), attrib);
|
|
glDeleteProgram(shaderProgram);
|
|
glDeleteShader(vertexShader);
|
|
glDeleteShader(pixelShader);
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
glDeleteShader(vertexShader);
|
|
glDeleteShader(pixelShader);
|
|
|
|
m_errorMessage.clear();
|
|
|
|
std::filesystem::path f = m_baseFilename;
|
|
|
|
if (m_autosave) {
|
|
{
|
|
f.replace_extension("vert");
|
|
std::ofstream out(f, std::ofstream::out);
|
|
out << m_vertexShaderEditor.getText();
|
|
}
|
|
{
|
|
f.replace_extension("frag");
|
|
std::ofstream out(f, std::ofstream::out);
|
|
out << m_pixelShaderEditor.getText();
|
|
}
|
|
{
|
|
f.replace_extension("lua");
|
|
std::ofstream out(f, std::ofstream::out);
|
|
out << m_luaEditor.getText();
|
|
}
|
|
}
|
|
|
|
if (m_shaderProgram != 0) {
|
|
glDeleteProgram(m_shaderProgram);
|
|
}
|
|
m_shaderProgram = shaderProgram;
|
|
m_shaderProjMtxLoc = glGetUniformLocation(m_shaderProgram, "u_projMatrix");
|
|
return shaderProgram;
|
|
}
|
|
|
|
bool ShaderEditor::draw(const char* title) {
|
|
if (!m_show) return false;
|
|
if (!ImGui::Begin(title, &m_show)) return false;
|
|
ImGui::Checkbox("Auto reload", &m_autoreload);
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Auto save", &m_autosave);
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Show all", &m_showAll);
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Configure shader")) {
|
|
setConfigure();
|
|
}
|
|
auto contents = ImGui::GetContentRegionAvail();
|
|
ImGuiStyle& style = ImGui::GetStyle();
|
|
const float heightSeparator = style.ItemSpacing.y;
|
|
float footerHeight = heightSeparator * 2 + 5 * ImGui::GetTextLineHeightWithSpacing();
|
|
float width = contents.x / 3 - style.ItemInnerSpacing.x;
|
|
if (m_showAll) {
|
|
ImVec2 size = { width, contents.y - footerHeight };
|
|
ImGui::BeginChild("VertexShaderEditor", size);
|
|
m_vertexShaderEditor.draw();
|
|
ImGui::EndChild();
|
|
ImGui::SameLine();
|
|
ImGui::BeginChild("PixelShaderEditor", size);
|
|
m_pixelShaderEditor.draw();
|
|
ImGui::EndChild();
|
|
ImGui::SameLine();
|
|
ImGui::BeginChild("LuaInvoker", size);
|
|
m_luaEditor.draw();
|
|
ImGui::EndChild();
|
|
}
|
|
else {
|
|
if (ImGui::BeginTabBar("MyTabBar")) {
|
|
ImVec2 size = { contents.x, contents.y - footerHeight };
|
|
if (ImGui::BeginTabItem("Vertex Shader")) {
|
|
ImGui::BeginChild("VertexShaderEditor", size);
|
|
m_vertexShaderEditor.draw();
|
|
ImGui::EndChild();
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("Pixel Shader")) {
|
|
ImGui::BeginChild("PixelShaderEditor", size);
|
|
m_pixelShaderEditor.draw();
|
|
ImGui::EndChild();
|
|
ImGui::EndTabItem();
|
|
}
|
|
ImGui::EndTabBar();
|
|
}
|
|
}
|
|
//ImGui::PopFont();
|
|
ImGui::BeginChild("Errors", ImVec2(0, 0), true);
|
|
ImGui::Text("%s", m_errorMessage.c_str());
|
|
if (m_displayError) {
|
|
for (auto& msg : m_lastLuaErrors) {
|
|
ImGui::TextUnformatted(msg.c_str());
|
|
}
|
|
}
|
|
ImGui::EndChild();
|
|
|
|
ImGui::End();
|
|
|
|
return m_vertexShaderEditor.hasTextChanged() || m_pixelShaderEditor.hasTextChanged() ||
|
|
m_luaEditor.hasTextChanged();
|
|
}
|
|
|
|
void ShaderEditor::setConfigure(bool configure) {
|
|
|
|
}
|
|
|
|
void ShaderEditor::configure(GUI* gui) {
|
|
|
|
}
|
|
|
|
void ShaderEditor::reset(GUI* gui) {
|
|
|
|
}
|
|
|
|
|
|
void ShaderEditor::renderWithImgui(GUI* gui, ImTextureID textureID, const ImVec2& srcSize,
|
|
const ImVec2& dstSize) {
|
|
if (m_shaderProgram == 0) {
|
|
compile();
|
|
}
|
|
if (m_shaderProgram == 0) {
|
|
ImGui::Image(textureID, dstSize, { 0, 0 }, { 1, 1 });
|
|
return;
|
|
}
|
|
|
|
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
|
m_cachedGui = gui;
|
|
drawList->AddCallback(
|
|
[](const ImDrawList* parentList, const ImDrawCmd* cmd) {
|
|
ShaderEditor* that = reinterpret_cast<ShaderEditor*>(cmd->UserCallbackData);
|
|
that->imguiCB(parentList, cmd);
|
|
},
|
|
this);
|
|
|
|
|
|
drawList->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
|
|
}
|
|
|
|
void ShaderEditor::imguiCB(const ImDrawList* parentList, const ImDrawCmd* cmd) {
|
|
GLuint textureID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(cmd->TextureId));
|
|
|
|
GLfloat projMtx[4][4];
|
|
if (m_imguiProjMtxLoc == -1) {
|
|
glGetIntegerv(GL_CURRENT_PROGRAM, &m_imguiProgram);
|
|
m_imguiProjMtxLoc = glGetUniformLocation(m_imguiProgram, "ProjMtx");
|
|
}
|
|
|
|
// Get projection matrix from the Imgui program
|
|
glUseProgram(m_shaderProgram);
|
|
glGetUniformfv(m_imguiProgram, m_imguiProjMtxLoc, &projMtx[0][0]);
|
|
|
|
// Send projection matrix to our shader
|
|
glUniformMatrix4fv(m_shaderProjMtxLoc, 1, GL_FALSE, &projMtx[0][0]);
|
|
//glBindTexture(GL_TEXTURE_2D, textureID);
|
|
}
|
|
|
|
void ShaderEditor::render(GUI* gui, GLuint textureID, const ImVec2& srcLoc, const ImVec2& srcSize,
|
|
const ImVec2& dstSize) {
|
|
if (m_shaderProgram == 0) {
|
|
compile();
|
|
}
|
|
if (m_shaderProgram == 0) {
|
|
return;
|
|
}
|
|
|
|
glBindVertexArray(m_vao);
|
|
glUseProgram(m_shaderProgram);
|
|
struct VertexData {
|
|
float positions[3];
|
|
float textures[2];
|
|
float color[4];
|
|
};
|
|
|
|
VertexData quadVertices[4];
|
|
|
|
quadVertices[0].positions[0] = -1.0;
|
|
quadVertices[0].positions[1] = -1.0;
|
|
quadVertices[0].positions[2] = 0.0;
|
|
quadVertices[0].textures[0] = srcLoc.x;
|
|
quadVertices[0].textures[1] = srcLoc.y;
|
|
quadVertices[0].color[0] = 1.0;
|
|
quadVertices[0].color[1] = 1.0;
|
|
quadVertices[0].color[2] = 1.0;
|
|
quadVertices[0].color[3] = 1.0;
|
|
|
|
quadVertices[1].positions[0] = 1.0;
|
|
quadVertices[1].positions[1] = -1.0;
|
|
quadVertices[1].positions[2] = 0.0;
|
|
quadVertices[1].textures[0] = srcLoc.x + srcSize.x;
|
|
quadVertices[1].textures[1] = srcLoc.y;
|
|
quadVertices[1].color[0] = 1.0;
|
|
quadVertices[1].color[1] = 1.0;
|
|
quadVertices[1].color[2] = 1.0;
|
|
quadVertices[1].color[3] = 1.0;
|
|
|
|
quadVertices[2].positions[0] = -1.0;
|
|
quadVertices[2].positions[1] = 1.0;
|
|
quadVertices[2].positions[2] = 0.0;
|
|
quadVertices[2].textures[0] = srcLoc.x;
|
|
quadVertices[2].textures[1] = srcLoc.y + srcSize.y;
|
|
quadVertices[2].color[0] = 1.0;
|
|
quadVertices[2].color[1] = 1.0;
|
|
quadVertices[2].color[2] = 1.0;
|
|
quadVertices[2].color[3] = 1.0;
|
|
|
|
quadVertices[3].positions[0] = 1.0;
|
|
quadVertices[3].positions[1] = 1.0;
|
|
quadVertices[3].positions[2] = 0.0;
|
|
quadVertices[3].textures[0] = srcLoc.x + srcSize.x;
|
|
quadVertices[3].textures[1] = srcLoc.y + srcSize.y;
|
|
quadVertices[3].color[0] = 1.0;
|
|
quadVertices[3].color[1] = 1.0;
|
|
quadVertices[3].color[2] = 1.0;
|
|
quadVertices[3].color[3] = 1.0;
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData) * 4, &quadVertices[0], GL_STATIC_DRAW);
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
if (m_setupVAO) {
|
|
m_setupVAO = false;
|
|
int loc = glGetAttribLocation(m_shaderProgram, "Position");
|
|
if (loc >= 0) {
|
|
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData),
|
|
(void*)&((VertexData*)nullptr)->positions);
|
|
glEnableVertexAttribArray(loc);
|
|
}
|
|
|
|
loc = glGetAttribLocation(m_shaderProgram, "UV");
|
|
if (loc >= 0) {
|
|
glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData),
|
|
(void*)&((VertexData*)nullptr)->textures);
|
|
glEnableVertexAttribArray(loc);
|
|
}
|
|
|
|
loc = glGetAttribLocation(m_shaderProgram, "Color");
|
|
if (loc >= 0) {
|
|
glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, sizeof(VertexData),
|
|
(void*)&((VertexData*)nullptr)->color);
|
|
glEnableVertexAttribArray(loc);
|
|
}
|
|
}
|
|
|
|
GLfloat currentProjection[4][4];
|
|
|
|
currentProjection[0][0] = 1.0f;
|
|
currentProjection[0][1] = 0.0f;
|
|
currentProjection[0][2] = 0.0f;
|
|
currentProjection[0][3] = 0.0f;
|
|
currentProjection[1][0] = 0.0f;
|
|
currentProjection[1][1] = 1.0f;
|
|
currentProjection[1][2] = 0.0f;
|
|
currentProjection[1][3] = 0.0f;
|
|
currentProjection[2][0] = 0.0f;
|
|
currentProjection[2][1] = 0.0f;
|
|
currentProjection[2][2] = 1.0f;
|
|
currentProjection[2][3] = 0.0f;
|
|
currentProjection[3][0] = 0.0f;
|
|
currentProjection[3][1] = 0.0f;
|
|
currentProjection[3][2] = 0.0f;
|
|
currentProjection[3][3] = 1.0f;
|
|
glUniformMatrix4fv(m_shaderProjMtxLoc, 1, GL_FALSE, ¤tProjection[0][0]);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureID);
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
}
|