diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index b0d82dc..dbfec87 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -12,10 +12,6 @@ #include "Prism/Renderer/FrameBuffer.h" #include "Prism/Renderer/RendererAPI.h" -#define GLFW_EXPOSE_NATIVE_WIN32 -#include -#include - #include "tinyfiledialogs.h" namespace Prism @@ -106,6 +102,7 @@ namespace Prism std::string Application::OpenFile(const std::string& filter) const { + // TODO: will move it to other folder // 处理过滤器 std::vector filterPatterns; std::vector patternStorage; @@ -162,29 +159,6 @@ namespace Prism return result ? std::string(result) : std::string(); - /* - OPENFILENAMEA ofn; // common dialog box structure - CHAR szFile[260] = {0}; // if using TCHAR macros - - // Initialize OPENFILENAME - ZeroMemory(&ofn, sizeof(OPENFILENAME)); - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = glfwGetWin32Window((GLFWwindow*)m_Window->GetNativeWindow()); - ofn.lpstrFile = szFile; - ofn.nMaxFile = sizeof(szFile); - ofn.lpstrFilter = "All\0*.*\0"; - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = NULL; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = NULL; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - - if (GetOpenFileNameA(&ofn) == TRUE) - { - return ofn.lpstrFile; - } - return std::string(); - */ } void Application::PushLayer(Layer* layer) diff --git a/Prism/src/Prism/Core/Core.h b/Prism/src/Prism/Core/Core.h index 043db96..da8ba6c 100644 --- a/Prism/src/Prism/Core/Core.h +++ b/Prism/src/Prism/Core/Core.h @@ -42,8 +42,13 @@ namespace Prism #define PM_DEBUGBREAK() asm("int3") #endif - #define PM_ASSERT(x, ...) { if(!(x)) { PM_ERROR("Assertion Failed: {0}", __VA_ARGS__); PM_DEBUGBREAK(); } } - #define PM_CORE_ASSERT(x, ...) { if(!(x)) { PM_CORE_ERROR("Assertion Failed: {0}", __VA_ARGS__); PM_DEBUGBREAK(); } } + #define PM_ASSERT_NO_MESSAGE(condition) { if(!(condition)) { PM_CORE_ERROR("Assertion Failed!"); PM_DEBUGBREAK(); } } + #define PM_ASSERT_MESSAGE(condition, ...) { if(!(condition)) { PM_CORE_ERROR("Assertion Failed: {0}", __VA_ARGS__); PM_DEBUGBREAK(); } } + + #define PM_ASSERT_RESOLVE(arg1, arg2, macro, ...) macro + + #define PM_ASSERT(...) PM_ASSERT_RESOLVE(__VA_ARGS__, PM_ASSERT_MESSAGE, PM_ASSERT_NO_MESSAGE)(__VA_ARGS__) + #define PM_CORE_ASSERT(...) PM_ASSERT_RESOLVE(__VA_ARGS__, PM_ASSERT_MESSAGE, PM_ASSERT_NO_MESSAGE)(__VA_ARGS__) #else #define PM_ASSERT(x, ...) #define PM_CORE_ASSERT(x, ...) diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp index d07c3fc..9913887 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp @@ -20,12 +20,25 @@ namespace Prism void OpenGLShader::Reload() { - ReadShaderFromFile(m_AssetPath); + const std::string source = ReadShaderFromFile(m_AssetPath); + m_ShaderSource = PreProcess(source); + Parse(); + PM_RENDER_S({ if (self->m_RendererID) glDeleteShader(self->m_RendererID); self->CompileAndUploadShader(); + self->ResolveUniforms(); + self->ValidateUniforms(); + + if (self->m_Loaded) + { + for (auto& callback : self->m_ShaderReloadedCallbacks) + callback(); + } + + self->m_Loaded = true; }); } @@ -80,6 +93,11 @@ namespace Prism } } + void OpenGLShader::AddShaderReloadedCallback(const ShaderReloadedCallback& callback) + { + m_ShaderReloadedCallbacks.push_back(callback); + } + void OpenGLShader::SetFloat(const std::string& name, float value) { PM_RENDER_S2(name, value, { @@ -94,78 +112,485 @@ namespace Prism }); } + void OpenGLShader::SetVSMaterialUniformBuffer(Buffer buffer) + { + PM_RENDER_S1(buffer, { + glUseProgram(self->m_RendererID); + self->ResolveAndSetUniforms(self->m_VSMaterialUniformBuffer, buffer); + }); + } + + void OpenGLShader::SetPSMaterialUniformBuffer(Buffer buffer) + { + PM_RENDER_S1(buffer, { + glUseProgram(self->m_RendererID); + self->ResolveAndSetUniforms(self->m_PSMaterialUniformBuffer, buffer); + }); + } + + void OpenGLShader::SetMat4FromRenderThread(const std::string& name, const glm::mat4& value) + { + UploadUniformMat4(name, value); + } + const std::string& OpenGLShader::GetName() const { return m_Name; } - void OpenGLShader::ReadShaderFromFile(const std::string& filepath) + std::string OpenGLShader::ReadShaderFromFile(const std::string& filepath) const { + std::string result; std::ifstream file(filepath, std::ios::in | std::ios::binary); if (file) { file.seekg(0, std::ios::end); - m_ShaderSource.resize(file.tellg()); + result.resize(file.tellg()); file.seekg(0, std::ios::beg); - file.read(&m_ShaderSource[0], m_ShaderSource.size()); + file.read(&result[0], result.size()); file.close(); } else { PM_CORE_WARN("Could not read shader file {0}", filepath); } + + return result; } - void OpenGLShader::CompileAndUploadShader() + std::unordered_map OpenGLShader::PreProcess(const std::string& source) { - std::istringstream ss(m_ShaderSource); - std::string token; - /* - while (ss >> token) - { - if (token == "#type") - { - std::string type; - ss >> type; - PM_CORE_TRACE("Type={0}", type); - } - - PM_CORE_TRACE("{}", token.c_str()); - } - */ - - std::unordered_map shaderSources; + std::unordered_map shaderSources; - const char* typeToken = "#type"; - size_t typeTokenLength = strlen(typeToken); - size_t pos = m_ShaderSource.find(typeToken, 0); - while (pos != std::string::npos) + const char* typeToken = "#type"; + size_t typeTokenLength = strlen(typeToken); + size_t pos = source.find(typeToken, 0); + while (pos != std::string::npos) + { + size_t eol = source.find("\r\n", pos); + if (eol == std::string::npos) { + eol = source.find('\n', pos); + if (eol == std::string::npos) { + eol = source.find('\r', pos); + } + } + + PM_CORE_ASSERT(eol != std::string::npos, "Syntax error"); + size_t begin = pos + typeTokenLength + 1; + std::string type = source.substr(begin, eol - begin); + PM_CORE_ASSERT(type == "vertex" || type == "fragment" || type == "pixel", "Invalid shader type"); + + size_t nextLinePos = source.find_first_not_of("\r\n", eol); + pos = source.find(typeToken, nextLinePos); + shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? m_ShaderSource.size() - 1 : nextLinePos)); + } + + return shaderSources; + } + const char* FindToken(const char* str, const std::string& token) + { + const char* t = str; + while (t = strstr(t, token.c_str())) + { + bool left = str == t || isspace(t[-1]); + bool right = !t[token.size()] || isspace(t[token.size()]); + if (left && right) + return t; + + t += token.size(); + } + return nullptr; + } + + const char* FindToken(const std::string& string, const std::string& token) + { + return FindToken(string.c_str(), token); + } + + std::vector SplitString(const std::string& string, const std::string& delimiters) + { + size_t start = 0; + size_t end = string.find_first_of(delimiters); + + std::vector result; + + while (end <= std::string::npos) + { + std::string token = string.substr(start, end - start); + if (!token.empty()) + result.push_back(token); + + if (end == std::string::npos) + break; + + start = end + 1; + end = string.find_first_of(delimiters, start); + } + + return result; + } + std::vector SplitString(const std::string& string, const char delimiter) + { + return SplitString(string, std::string(1, delimiter)); + } + + std::vector Tokenize(const std::string& string) + { + return SplitString(string, " \t\n"); + } + + std::vector GetLines(const std::string& string) + { + return SplitString(string, "\n"); + } + + std::string GetBlock(const char* str, const char** outPosition) + { + const char* end = strstr(str, "}"); + if (!end) + return str; + + if (outPosition) + *outPosition = end; + const uint32_t length = end - str + 1; + return std::string(str, length); + } + + std::string GetStatement(const char* str, const char** outPosition) + { + const char* end = strstr(str, ";"); + if (!end) + return str; + + if (outPosition) + *outPosition = end; + const uint32_t length = end - str + 1; + return std::string(str, length); + } + + bool StartsWith(const std::string& string, const std::string& start) + { + return string.find(start) == 0; + } + + void OpenGLShader::Parse() + { + const char* token; + const char* vstr; + const char* fstr; + + m_Resources.clear(); + m_Structs.clear(); + m_VSMaterialUniformBuffer.reset(); + m_PSMaterialUniformBuffer.reset(); + + auto& vertexSource = m_ShaderSource[GL_VERTEX_SHADER]; + auto& fragmentSource = m_ShaderSource[GL_FRAGMENT_SHADER]; + + // Vertex Shader + vstr = vertexSource.c_str(); + while ((token = FindToken(vstr, "struct"))) + ParseUniformStruct(GetBlock(token, &vstr), ShaderDomain::Vertex); + + vstr = vertexSource.c_str(); + while ((token = FindToken(vstr, "uniform"))) + ParseUniform(GetStatement(token, &vstr), ShaderDomain::Vertex); + + // Fragment Shader + fstr = fragmentSource.c_str(); + while ((token = FindToken(fstr, "struct"))) + ParseUniformStruct(GetBlock(token, &fstr), ShaderDomain::Pixel); + + fstr = fragmentSource.c_str(); + while ((token = FindToken(fstr, "uniform"))) + ParseUniform(GetStatement(token, &fstr), ShaderDomain::Pixel); + } + + + static bool IsTypeStringResource(const std::string& type) + { + if (type == "sampler2D") return true; + if (type == "samplerCube") return true; + if (type == "sampler2DShadow") return true; + return false; + } + + + ShaderStruct* OpenGLShader::FindStruct(const std::string& name) + { + for (ShaderStruct* s : m_Structs) + { + if (s->GetName() == name) + return s; + } + return nullptr; + } + + int32_t OpenGLShader::GetUniformLocation(const std::string& name) const + { + int32_t result = glGetUniformLocation(m_RendererID, name.c_str()); + if (result == -1) + PM_CORE_WARN("Could not find uniform '{0}' in shader", name); + + return result; + } + + + void OpenGLShader::ParseUniform(const std::string& statement, ShaderDomain domain) + { + std::vector tokens = Tokenize(statement); + uint32_t index = 0; + + index++; // "uniform" + std::string typeString = tokens[index++]; + std::string name = tokens[index++]; + // Strip ; from name if present + if (const char* s = strstr(name.c_str(), ";")) + name = std::string(name.c_str(), s - name.c_str()); + + std::string n(name); + int32_t count = 1; + const char* namestr = n.c_str(); + if (const char* s = strstr(namestr, "[")) { - size_t eol = m_ShaderSource.find("\r\n", pos); - if (eol == std::string::npos) { - eol = m_ShaderSource.find('\n', pos); - if (eol == std::string::npos) { - eol = m_ShaderSource.find('\r', pos); - } + name = std::string(namestr, s - namestr); + + const char* end = strstr(namestr, "]"); + std::string c(s + 1, end - s); + count = atoi(c.c_str()); + } + + if (IsTypeStringResource(typeString)) + { + ShaderResourceDeclaration* declaration = new OpenGLShaderResourceDeclaration(OpenGLShaderResourceDeclaration::StringToType(typeString), name, count); + m_Resources.push_back(declaration); + } + else + { + OpenGLShaderUniformDeclaration::Type t = OpenGLShaderUniformDeclaration::StringToType(typeString); + OpenGLShaderUniformDeclaration* declaration = nullptr; + + if (t == OpenGLShaderUniformDeclaration::Type::NONE) + { + // Find struct + ShaderStruct* s = FindStruct(typeString); + PM_CORE_ASSERT(s, ""); + declaration = new OpenGLShaderUniformDeclaration(domain, s, name, count); + } + else + { + declaration = new OpenGLShaderUniformDeclaration(domain, t, name, count); } - PM_CORE_ASSERT(eol != std::string::npos, "Syntax error"); - size_t begin = pos + typeTokenLength + 1; - std::string type = m_ShaderSource.substr(begin, eol - begin); - PM_CORE_ASSERT(type == "vertex" || type == "fragment" || type == "pixel", "Invalid shader type"); + if (StartsWith(name, "r_")) + { + if (domain == ShaderDomain::Vertex) + ((OpenGLShaderUniformBufferDeclaration*)m_VSRendererUniformBuffers.front())->PushUniform(declaration); + else if (domain == ShaderDomain::Pixel) + ((OpenGLShaderUniformBufferDeclaration*)m_PSRendererUniformBuffers.front())->PushUniform(declaration); + } + else + { + if (domain == ShaderDomain::Vertex) + { + if (!m_VSMaterialUniformBuffer) + m_VSMaterialUniformBuffer.reset(new OpenGLShaderUniformBufferDeclaration("", domain)); - size_t nextLinePos = m_ShaderSource.find_first_not_of("\r\n", eol); - pos = m_ShaderSource.find(typeToken, nextLinePos); - shaderSources[ShaderTypeFromString(type)] = m_ShaderSource.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? m_ShaderSource.size() - 1 : nextLinePos)); + m_VSMaterialUniformBuffer->PushUniform(declaration); + } + else if (domain == ShaderDomain::Pixel) + { + if (!m_PSMaterialUniformBuffer) + m_PSMaterialUniformBuffer.reset(new OpenGLShaderUniformBufferDeclaration("", domain)); + + m_PSMaterialUniformBuffer->PushUniform(declaration); + } + } } + } + + void OpenGLShader::ParseUniformStruct(const std::string& block, ShaderDomain domain) + { + std::vector tokens = Tokenize(block); + + uint32_t index = 0; + index++; // struct + std::string name = tokens[index++]; + ShaderStruct* uniformStruct = new ShaderStruct(name); + index++; // { + while (index < tokens.size()) + { + if (tokens[index] == "}") + break; + + std::string type = tokens[index++]; + std::string name = tokens[index++]; + + // Strip ; from name if present + if (const char* s = strstr(name.c_str(), ";")) + name = std::string(name.c_str(), s - name.c_str()); + + uint32_t count = 1; + const char* namestr = name.c_str(); + if (const char* s = strstr(namestr, "[")) + { + name = std::string(namestr, s - namestr); + + const char* end = strstr(namestr, "]"); + std::string c(s + 1, end - s); + count = atoi(c.c_str()); + } + ShaderUniformDeclaration* field = new OpenGLShaderUniformDeclaration(domain, OpenGLShaderUniformDeclaration::StringToType(type), name, count); + uniformStruct->AddField(field); + } + m_Structs.push_back(uniformStruct); + } + + void OpenGLShader::ResolveUniforms() + { + glUseProgram(m_RendererID); + + for (size_t i = 0; i < m_VSRendererUniformBuffers.size(); i++) + { + OpenGLShaderUniformBufferDeclaration* decl = (OpenGLShaderUniformBufferDeclaration*)m_VSRendererUniformBuffers[i]; + const ShaderUniformList& uniforms = decl->GetUniformDeclarations(); + for (size_t j = 0; j < uniforms.size(); j++) + { + OpenGLShaderUniformDeclaration* uniform = (OpenGLShaderUniformDeclaration*)uniforms[j]; + if (uniform->GetType() == OpenGLShaderUniformDeclaration::Type::STRUCT) + { + const ShaderStruct& s = uniform->GetShaderUniformStruct(); + const auto& fields = s.GetFields(); + for (size_t k = 0; k < fields.size(); k++) + { + OpenGLShaderUniformDeclaration* field = (OpenGLShaderUniformDeclaration*)fields[k]; + field->m_Location = GetUniformLocation(uniform->m_Name + "." + field->m_Name); + } + } + else + { + uniform->m_Location = GetUniformLocation(uniform->m_Name); + } + } + } + + for (size_t i = 0; i < m_PSRendererUniformBuffers.size(); i++) + { + OpenGLShaderUniformBufferDeclaration* decl = (OpenGLShaderUniformBufferDeclaration*)m_PSRendererUniformBuffers[i]; + const ShaderUniformList& uniforms = decl->GetUniformDeclarations(); + for (size_t j = 0; j < uniforms.size(); j++) + { + OpenGLShaderUniformDeclaration* uniform = (OpenGLShaderUniformDeclaration*)uniforms[j]; + if (uniform->GetType() == OpenGLShaderUniformDeclaration::Type::STRUCT) + { + const ShaderStruct& s = uniform->GetShaderUniformStruct(); + const auto& fields = s.GetFields(); + for (size_t k = 0; k < fields.size(); k++) + { + OpenGLShaderUniformDeclaration* field = (OpenGLShaderUniformDeclaration*)fields[k]; + field->m_Location = GetUniformLocation(uniform->m_Name + "." + field->m_Name); + } + } + else + { + uniform->m_Location = GetUniformLocation(uniform->m_Name); + } + } + } + + { + const auto& decl = m_VSMaterialUniformBuffer; + if (decl) + { + const ShaderUniformList& uniforms = decl->GetUniformDeclarations(); + for (size_t j = 0; j < uniforms.size(); j++) + { + OpenGLShaderUniformDeclaration* uniform = (OpenGLShaderUniformDeclaration*)uniforms[j]; + if (uniform->GetType() == OpenGLShaderUniformDeclaration::Type::STRUCT) + { + const ShaderStruct& s = uniform->GetShaderUniformStruct(); + const auto& fields = s.GetFields(); + for (size_t k = 0; k < fields.size(); k++) + { + OpenGLShaderUniformDeclaration* field = (OpenGLShaderUniformDeclaration*)fields[k]; + field->m_Location = GetUniformLocation(uniform->m_Name + "." + field->m_Name); + } + } + else + { + uniform->m_Location = GetUniformLocation(uniform->m_Name); + } + } + } + } + + { + const auto& decl = m_PSMaterialUniformBuffer; + if (decl) + { + const ShaderUniformList& uniforms = decl->GetUniformDeclarations(); + for (size_t j = 0; j < uniforms.size(); j++) + { + OpenGLShaderUniformDeclaration* uniform = (OpenGLShaderUniformDeclaration*)uniforms[j]; + if (uniform->GetType() == OpenGLShaderUniformDeclaration::Type::STRUCT) + { + const ShaderStruct& s = uniform->GetShaderUniformStruct(); + const auto& fields = s.GetFields(); + for (size_t k = 0; k < fields.size(); k++) + { + OpenGLShaderUniformDeclaration* field = (OpenGLShaderUniformDeclaration*)fields[k]; + field->m_Location = GetUniformLocation(uniform->m_Name + "." + field->m_Name); + } + } + else + { + uniform->m_Location = GetUniformLocation(uniform->m_Name); + } + } + } + } + + uint32_t sampler = 0; + for (size_t i = 0; i < m_Resources.size(); i++) + { + OpenGLShaderResourceDeclaration* resource = (OpenGLShaderResourceDeclaration*)m_Resources[i]; + int32_t location = GetUniformLocation(resource->m_Name); + + if (resource->GetCount() == 1) + { + resource->m_Register = sampler; + if (location != -1) + UploadUniformInt(location, sampler); + + sampler++; + } + else if (resource->GetCount() > 1) + { + resource->m_Register = 0; + uint32_t count = resource->GetCount(); + int* samplers = new int[count]; + for (uint32_t s = 0; s < count; s++) + samplers[s] = s; + UploadUniformIntArray(resource->GetName(), samplers, count); + delete[] samplers; + } + } + } + + void OpenGLShader::ValidateUniforms() + { + } + + void OpenGLShader::CompileAndUploadShader() + { std::vector shaderRendererIDs; GLuint program = glCreateProgram(); - for (const auto& kv : shaderSources) + for (const auto& kv : m_ShaderSource) { GLenum type = kv.first; const std::string& source = kv.second; @@ -219,20 +644,6 @@ namespace Prism glDetachShader(program, id); m_RendererID = program; - - // Bind default texture unit - UploadUniformInt("u_Texture", 0); - - // PBR shader textures - UploadUniformInt("u_AlbedoTexture", 1); - UploadUniformInt("u_NormalTexture", 2); - UploadUniformInt("u_MetalnessTexture", 3); - UploadUniformInt("u_RoughnessTexture", 4); - - UploadUniformInt("u_EnvRadianceTex", 10); - UploadUniformInt("u_EnvIrradianceTex", 11); - - UploadUniformInt("u_BRDFLUTTexture", 15); } GLenum OpenGLShader::ShaderTypeFromString(const std::string& type) @@ -304,4 +715,185 @@ namespace Prism else PM_CORE_WARN("Uniform '{0}' not found!", name); } + + void OpenGLShader::ResolveAndSetUniforms(const Scope& decl, Buffer buffer) + { + const ShaderUniformList& uniforms = decl->GetUniformDeclarations(); + for (size_t i = 0; i < uniforms.size(); i++) + { + OpenGLShaderUniformDeclaration* uniform = (OpenGLShaderUniformDeclaration*)uniforms[i]; + if (uniform->IsArray()) + ResolveAndSetUniformArray(uniform, buffer); + else + ResolveAndSetUniform(uniform, buffer); + } + } + + void OpenGLShader::ResolveAndSetUniform(OpenGLShaderUniformDeclaration* uniform, Buffer buffer) + { + if (uniform->GetLocation() == -1) + return; + + PM_CORE_ASSERT(uniform->GetLocation() != -1, "Uniform has invalid location!"); + + uint32_t offset = uniform->GetOffset(); + switch (uniform->GetType()) + { + case OpenGLShaderUniformDeclaration::Type::FLOAT32: + UploadUniformFloat(uniform->GetLocation(), *(float*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::INT32: + UploadUniformInt(uniform->GetLocation(), *(int32_t*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC2: + UploadUniformFloat2(uniform->GetLocation(), *(glm::vec2*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC3: + UploadUniformFloat3(uniform->GetLocation(), *(glm::vec3*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC4: + UploadUniformFloat4(uniform->GetLocation(), *(glm::vec4*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::MAT3: + UploadUniformMat3(uniform->GetLocation(), *(glm::mat3*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::MAT4: + UploadUniformMat4(uniform->GetLocation(), *(glm::mat4*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::STRUCT: + UploadUniformStruct(uniform, buffer.Data, offset); + break; + default: + PM_CORE_ASSERT(false, "Unknown uniform type!"); + } + } + + void OpenGLShader::ResolveAndSetUniformArray(OpenGLShaderUniformDeclaration* uniform, Buffer buffer) + { + //HZ_CORE_ASSERT(uniform->GetLocation() != -1, "Uniform has invalid location!"); + + uint32_t offset = uniform->GetOffset(); + switch (uniform->GetType()) + { + case OpenGLShaderUniformDeclaration::Type::FLOAT32: + UploadUniformFloat(uniform->GetLocation(), *(float*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::INT32: + UploadUniformInt(uniform->GetLocation(), *(int32_t*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC2: + UploadUniformFloat2(uniform->GetLocation(), *(glm::vec2*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC3: + UploadUniformFloat3(uniform->GetLocation(), *(glm::vec3*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC4: + UploadUniformFloat4(uniform->GetLocation(), *(glm::vec4*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::MAT3: + UploadUniformMat3(uniform->GetLocation(), *(glm::mat3*)&buffer.Data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::MAT4: + UploadUniformMat4Array(uniform->GetLocation(), *(glm::mat4*)&buffer.Data[offset], uniform->GetCount()); + break; + case OpenGLShaderUniformDeclaration::Type::STRUCT: + UploadUniformStruct(uniform, buffer.Data, offset); + break; + default: + PM_CORE_ASSERT(false, "Unknown uniform type!"); + } + } + + void OpenGLShader::ResolveAndSetUniformField(const OpenGLShaderUniformDeclaration& field, byte* data, int32_t offset) + { + switch (field.GetType()) + { + case OpenGLShaderUniformDeclaration::Type::FLOAT32: + UploadUniformFloat(field.GetLocation(), *(float*)&data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::INT32: + UploadUniformInt(field.GetLocation(), *(int32_t*)&data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC2: + UploadUniformFloat2(field.GetLocation(), *(glm::vec2*)&data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC3: + UploadUniformFloat3(field.GetLocation(), *(glm::vec3*)&data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::VEC4: + UploadUniformFloat4(field.GetLocation(), *(glm::vec4*)&data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::MAT3: + UploadUniformMat3(field.GetLocation(), *(glm::mat3*)&data[offset]); + break; + case OpenGLShaderUniformDeclaration::Type::MAT4: + UploadUniformMat4(field.GetLocation(), *(glm::mat4*)&data[offset]); + break; + default: + PM_CORE_ASSERT(false, "Unknown uniform type!"); + } + } + + void OpenGLShader::UploadUniformInt(uint32_t location, int32_t value) + { + glUniform1i(location, value); + } + + void OpenGLShader::UploadUniformIntArray(uint32_t location, int32_t* values, int32_t count) + { + glUniform1iv(location, count, values); + } + + void OpenGLShader::UploadUniformFloat(uint32_t location, float value) + { + glUniform1f(location, value); + } + + void OpenGLShader::UploadUniformFloat2(uint32_t location, const glm::vec2& value) + { + glUniform2f(location, value.x, value.y); + } + + void OpenGLShader::UploadUniformFloat3(uint32_t location, const glm::vec3& value) + { + glUniform3f(location, value.x, value.y, value.z); + } + + void OpenGLShader::UploadUniformFloat4(uint32_t location, const glm::vec4& value) + { + glUniform4f(location, value.x, value.y, value.z, value.w); + } + + void OpenGLShader::UploadUniformMat3(uint32_t location, const glm::mat3& values) + { + glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(values)); + } + + void OpenGLShader::UploadUniformMat4(uint32_t location, const glm::mat4& values) + { + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(values)); + } + + void OpenGLShader::UploadUniformMat4Array(uint32_t location, const glm::mat4& values, uint32_t count) + { + glUniformMatrix4fv(location, count, GL_FALSE, glm::value_ptr(values)); + } + + void OpenGLShader::UploadUniformStruct(OpenGLShaderUniformDeclaration* uniform, byte* buffer, uint32_t offset) + { + const ShaderStruct& s = uniform->GetShaderUniformStruct(); + const auto& fields = s.GetFields(); + for (size_t k = 0; k < fields.size(); k++) + { + OpenGLShaderUniformDeclaration* field = (OpenGLShaderUniformDeclaration*)fields[k]; + ResolveAndSetUniformField(*field, buffer, offset); + offset += field->m_Size; + } + } + + void OpenGLShader::UploadUniformIntArray(const std::string& name, int32_t* values, int32_t count) + { + int32_t location = GetUniformLocation(name); + glUniform1iv(location, count, values); + } } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h index dd12697..cffae5c 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h @@ -4,6 +4,7 @@ #ifndef OPENGLSHADER_H #define OPENGLSHADER_H +#include "OpenGLShaderUniform.h" #include "glad/glad.h" #include "Prism/Renderer/RendererAPI.h" #include "Prism/Renderer/Shader.h" @@ -20,14 +21,33 @@ namespace Prism virtual void Bind() override; virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) override; + virtual void AddShaderReloadedCallback(const ShaderReloadedCallback& callback) override; virtual void SetFloat(const std::string& name, float value) override; virtual void SetMat4(const std::string& name, const glm::mat4& value) override; + + virtual void SetVSMaterialUniformBuffer(Buffer buffer) override; + virtual void SetPSMaterialUniformBuffer(Buffer buffer) override; + + virtual void SetMat4FromRenderThread(const std::string& name, const glm::mat4& value) override; + const std::string& GetName() const override; private: - void ReadShaderFromFile(const std::string& filepath); + std::string ReadShaderFromFile(const std::string& filepath) const; + std::unordered_map PreProcess(const std::string& source); + void Parse(); + void ParseUniform(const std::string& statement, ShaderDomain domain); + void ParseUniformStruct(const std::string& block, ShaderDomain domain); + ShaderStruct* FindStruct(const std::string& name); + + int32_t GetUniformLocation(const std::string& name) const; + + void ResolveUniforms(); + void ValidateUniforms(); + + void CompileAndUploadShader(); static GLenum ShaderTypeFromString(const std::string& type); @@ -38,11 +58,47 @@ namespace Prism void UploadUniformFloat3(const std::string& name, const glm::vec3& values) const; void UploadUniformFloat4(const std::string& name, const glm::vec4& values) const; void UploadUniformMat4(const std::string& name, const glm::mat4& values) const; + + void ResolveAndSetUniforms(const Scope& decl, Buffer buffer); + void ResolveAndSetUniform(OpenGLShaderUniformDeclaration* uniform, Buffer buffer); + void ResolveAndSetUniformArray(OpenGLShaderUniformDeclaration* uniform, Buffer buffer); + void ResolveAndSetUniformField(const OpenGLShaderUniformDeclaration& field, byte* data, int32_t offset); + + void UploadUniformInt(uint32_t location, int32_t value); + void UploadUniformIntArray(uint32_t location, int32_t* values, int32_t count); + void UploadUniformFloat(uint32_t location, float value); + void UploadUniformFloat2(uint32_t location, const glm::vec2& value); + void UploadUniformFloat3(uint32_t location, const glm::vec3& value); + void UploadUniformFloat4(uint32_t location, const glm::vec4& value); + void UploadUniformMat3(uint32_t location, const glm::mat3& values); + void UploadUniformMat4(uint32_t location, const glm::mat4& values); + void UploadUniformMat4Array(uint32_t location, const glm::mat4& values, uint32_t count); + + void UploadUniformStruct(OpenGLShaderUniformDeclaration* uniform, byte* buffer, uint32_t offset); + void UploadUniformIntArray(const std::string& name, int32_t* values, int32_t count); + + inline const ShaderUniformBufferList& GetVSRendererUniforms() const override { return m_VSRendererUniformBuffers; } + inline const ShaderUniformBufferList& GetPSRendererUniforms() const override { return m_PSRendererUniformBuffers; } + inline const ShaderUniformBufferDeclaration& GetVSMaterialUniformBuffer() const override { return *m_VSMaterialUniformBuffer; } + inline const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const override { return *m_PSMaterialUniformBuffer; } + inline const ShaderResourceList& GetResources() const override { return m_Resources; } private: RendererID m_RendererID; + bool m_Loaded = false; + std::string m_Name, m_AssetPath; - std::string m_ShaderSource; + std::unordered_map m_ShaderSource; + + std::vector m_ShaderReloadedCallbacks; + + ShaderUniformBufferList m_VSRendererUniformBuffers; + ShaderUniformBufferList m_PSRendererUniformBuffers; + Scope m_VSMaterialUniformBuffer; + Scope m_PSMaterialUniformBuffer; + ShaderResourceList m_Resources; + ShaderStructList m_Structs; + }; } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.cpp new file mode 100644 index 0000000..9f2d908 --- /dev/null +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.cpp @@ -0,0 +1,130 @@ +// +// Created by sfd on 25-11-27. +// + +#include "OpenGLShaderUniform.h" + + +namespace Prism +{ + OpenGLShaderUniformDeclaration::OpenGLShaderUniformDeclaration(ShaderDomain domain, Type type, const std::string& name, uint32_t count) + : m_Type(type), m_Struct(nullptr), m_Domain(domain) + { + m_Name = name; + m_Count = count; + m_Size = SizeOfUniformType(type) * count; + } + + OpenGLShaderUniformDeclaration::OpenGLShaderUniformDeclaration(ShaderDomain domain, ShaderStruct* uniformStruct, const std::string& name, uint32_t count) + : m_Struct(uniformStruct), m_Type(OpenGLShaderUniformDeclaration::Type::STRUCT), m_Domain(domain) + { + m_Name = name; + m_Count = count; + m_Size = m_Struct->GetSize() * count; + } + + void OpenGLShaderUniformDeclaration::SetOffset(uint32_t offset) + { + if (m_Type == OpenGLShaderUniformDeclaration::Type::STRUCT) + m_Struct->SetOffset(offset); + + m_Offset = offset; + } + + uint32_t OpenGLShaderUniformDeclaration::SizeOfUniformType(Type type) + { + switch (type) + { + case OpenGLShaderUniformDeclaration::Type::INT32: return 4; + case OpenGLShaderUniformDeclaration::Type::FLOAT32: return 4; + case OpenGLShaderUniformDeclaration::Type::VEC2: return 4 * 2; + case OpenGLShaderUniformDeclaration::Type::VEC3: return 4 * 3; + case OpenGLShaderUniformDeclaration::Type::VEC4: return 4 * 4; + case OpenGLShaderUniformDeclaration::Type::MAT3: return 4 * 3 * 3; + case OpenGLShaderUniformDeclaration::Type::MAT4: return 4 * 4 * 4; + } + return 0; + } + + OpenGLShaderUniformDeclaration::Type OpenGLShaderUniformDeclaration::StringToType(const std::string& type) + { + if (type == "int32") return Type::INT32; + if (type == "float") return Type::FLOAT32; + if (type == "vec2") return Type::VEC2; + if (type == "vec3") return Type::VEC3; + if (type == "vec4") return Type::VEC4; + if (type == "mat3") return Type::MAT3; + if (type == "mat4") return Type::MAT4; + + return Type::NONE; + } + + std::string OpenGLShaderUniformDeclaration::TypeToString(Type type) + { + switch (type) + { + case OpenGLShaderUniformDeclaration::Type::INT32: return "int32"; + case OpenGLShaderUniformDeclaration::Type::FLOAT32: return "float"; + case OpenGLShaderUniformDeclaration::Type::VEC2: return "vec2"; + case OpenGLShaderUniformDeclaration::Type::VEC3: return "vec3"; + case OpenGLShaderUniformDeclaration::Type::VEC4: return "vec4"; + case OpenGLShaderUniformDeclaration::Type::MAT3: return "mat3"; + case OpenGLShaderUniformDeclaration::Type::MAT4: return "mat4"; + } + return "Invalid Type"; + } + + OpenGLShaderUniformBufferDeclaration::OpenGLShaderUniformBufferDeclaration(const std::string& name, ShaderDomain domain) + : m_Name(name), m_Domain(domain), m_Size(0), m_Register(0) + { + } + + void OpenGLShaderUniformBufferDeclaration::PushUniform(OpenGLShaderUniformDeclaration* uniform) + { + uint32_t offset = 0; + if (m_Uniforms.size()) + { + OpenGLShaderUniformDeclaration* previous = (OpenGLShaderUniformDeclaration*)m_Uniforms.back(); + offset = previous->m_Offset + previous->m_Size; + } + uniform->SetOffset(offset); + m_Size += uniform->GetSize(); + m_Uniforms.push_back(uniform); + } + + ShaderUniformDeclaration* OpenGLShaderUniformBufferDeclaration::FindUniform(const std::string& name) + { + for (ShaderUniformDeclaration* uniform : m_Uniforms) + { + if (uniform->GetName() == name) + return uniform; + } + return nullptr; + } + + OpenGLShaderResourceDeclaration::OpenGLShaderResourceDeclaration(Type type, const std::string& name, uint32_t count) + : m_Type(type), m_Name(name), m_Count(count) + { + m_Name = name; + m_Count = count; + } + + OpenGLShaderResourceDeclaration::Type OpenGLShaderResourceDeclaration::StringToType(const std::string& type) + { + if (type == "sampler2D") return Type::TEXTURE2D; + if (type == "samplerCube") return Type::TEXTURECUBE; + + return Type::NONE; + } + + std::string OpenGLShaderResourceDeclaration::TypeToString(Type type) + { + switch (type) + { + case Type::TEXTURE2D: return "sampler2D"; + case Type::TEXTURECUBE: return "samplerCube"; + } + return "Invalid Type"; + } + +} \ No newline at end of file diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.h b/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.h new file mode 100644 index 0000000..444491d --- /dev/null +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShaderUniform.h @@ -0,0 +1,118 @@ +// +// Created by sfd on 25-11-27. +// + +#ifndef OPENGLSHADERUNIFORM_H +#define OPENGLSHADERUNIFORM_H +#include "Prism/Renderer/ShaderUniform.h" + + +namespace Prism +{ + class OpenGLShaderResourceDeclaration : public ShaderResourceDeclaration + { + public: + enum class Type + { + NONE, TEXTURE2D, TEXTURECUBE + }; + private: + friend class OpenGLShader; + private: + std::string m_Name; + uint32_t m_Register = 0; + uint32_t m_Count; + Type m_Type; + public: + OpenGLShaderResourceDeclaration(Type type, const std::string& name, uint32_t count); + + inline const std::string& GetName() const override { return m_Name; } + inline uint32_t GetRegister() const override { return m_Register; } + inline uint32_t GetCount() const override { return m_Count; } + + inline Type GetType() const { return m_Type; } + public: + static Type StringToType(const std::string& type); + static std::string TypeToString(Type type); + }; + + class OpenGLShaderUniformDeclaration : public ShaderUniformDeclaration + { + private: + friend class OpenGLShader; + friend class OpenGLShaderUniformBufferDeclaration; + public: + enum class Type + { + NONE, FLOAT32, VEC2, VEC3, VEC4, MAT3, MAT4, INT32, STRUCT + }; + private: + std::string m_Name; + uint32_t m_Size; + uint32_t m_Count; + uint32_t m_Offset; + ShaderDomain m_Domain; + + Type m_Type; + ShaderStruct* m_Struct; + mutable int32_t m_Location; + public: + OpenGLShaderUniformDeclaration(ShaderDomain domain, Type type, const std::string& name, uint32_t count = 1); + OpenGLShaderUniformDeclaration(ShaderDomain domain, ShaderStruct* uniformStruct, const std::string& name, uint32_t count = 1); + + inline const std::string& GetName() const override { return m_Name; } + inline uint32_t GetSize() const override { return m_Size; } + inline uint32_t GetCount() const override { return m_Count; } + inline uint32_t GetOffset() const override { return m_Offset; } + inline uint32_t GetAbsoluteOffset() const { return m_Struct ? m_Struct->GetOffset() + m_Offset : m_Offset; } + inline ShaderDomain GetDomain() const { return m_Domain; } + + int32_t GetLocation() const { return m_Location; } + inline Type GetType() const { return m_Type; } + inline bool IsArray() const { return m_Count > 1; } + inline const ShaderStruct& GetShaderUniformStruct() const { PM_CORE_ASSERT(m_Struct, ""); return *m_Struct; } + protected: + void SetOffset(uint32_t offset) override; + public: + static uint32_t SizeOfUniformType(Type type); + static Type StringToType(const std::string& type); + static std::string TypeToString(Type type); + }; + + struct GLShaderUniformField + { + OpenGLShaderUniformDeclaration::Type type; + std::string name; + uint32_t count; + mutable uint32_t size; + mutable int32_t location; + }; + + class OpenGLShaderUniformBufferDeclaration : public ShaderUniformBufferDeclaration + { + private: + friend class Shader; + private: + std::string m_Name; + ShaderUniformList m_Uniforms; + uint32_t m_Register; + uint32_t m_Size; + ShaderDomain m_Domain; + public: + OpenGLShaderUniformBufferDeclaration(const std::string& name, ShaderDomain domain); + + void PushUniform(OpenGLShaderUniformDeclaration* uniform); + + inline const std::string& GetName() const override { return m_Name; } + inline uint32_t GetRegister() const override { return m_Register; } + inline uint32_t GetSize() const override { return m_Size; } + virtual ShaderDomain GetDomain() const { return m_Domain; } + inline const ShaderUniformList& GetUniformDeclarations() const override { return m_Uniforms; } + + ShaderUniformDeclaration* FindUniform(const std::string& name); + }; + +} + + +#endif //OPENGLSHADERUNIFORM_H diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp index faf4cd5..f1342cd 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp @@ -33,18 +33,19 @@ namespace Prism // Texture2D // ****************************************** - OpenGLTexture2D::OpenGLTexture2D(TextureFormat format, unsigned int width, unsigned int height) - : m_Format(format), m_Width(width), m_Height(height) + OpenGLTexture2D::OpenGLTexture2D(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap) + : m_Format(format), m_Width(width), m_Height(height), m_Wrap(wrap) { auto self = this; PM_RENDER_1(self, { glGenTextures(1, &self->m_RendererID); glBindTexture(GL_TEXTURE_2D, self->m_RendererID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + GLenum wrap = self->m_Wrap == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); glTextureParameterf(self->m_RendererID, GL_TEXTURE_MAX_ANISOTROPY, RendererAPI::GetCapabilities().MaxAnisotropy); glTexImage2D(GL_TEXTURE_2D, 0, PrismToOpenGLTextureFormat(self->m_Format), self->m_Width, self->m_Height, 0, PrismToOpenGLTextureFormat(self->m_Format), GL_UNSIGNED_BYTE, nullptr); @@ -55,12 +56,13 @@ namespace Prism } + OpenGLTexture2D::OpenGLTexture2D(const std::string& path, bool srgb) : m_FilePath(path) { int width, height, channels; PM_CORE_INFO("Loading texture {0}, srgb={1}", path, srgb); - m_ImageData = stbi_load(path.c_str(), &width, &height, &channels, srgb ? STBI_rgb : STBI_rgb_alpha); + m_ImageData.Data = stbi_load(path.c_str(), &width, &height, &channels, srgb ? STBI_rgb : STBI_rgb_alpha); m_Width = width; m_Height = height; @@ -77,7 +79,7 @@ namespace Prism glTextureParameteri(self->m_RendererID, GL_TEXTURE_MIN_FILTER, levels > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); glTextureParameteri(self->m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTextureSubImage2D(self->m_RendererID, 0, 0, 0, self->m_Width, self->m_Height, GL_RGB, GL_UNSIGNED_BYTE, self->m_ImageData); + glTextureSubImage2D(self->m_RendererID, 0, 0, 0, self->m_Width, self->m_Height, GL_RGB, GL_UNSIGNED_BYTE, self->m_ImageData.Data); glGenerateTextureMipmap(self->m_RendererID); } else @@ -90,30 +92,58 @@ namespace Prism glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, PrismToOpenGLTextureFormat(self->m_Format), self->m_Width, self->m_Height, 0, srgb ? GL_SRGB8 : PrismToOpenGLTextureFormat(self->m_Format), GL_UNSIGNED_BYTE, self->m_ImageData); + glTexImage2D(GL_TEXTURE_2D, 0, PrismToOpenGLTextureFormat(self->m_Format), self->m_Width, self->m_Height, 0, srgb ? GL_SRGB8 : PrismToOpenGLTextureFormat(self->m_Format), GL_UNSIGNED_BYTE, self->m_ImageData.Data); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } - stbi_image_free(self->m_ImageData); + stbi_image_free(self->m_ImageData.Data); }); } OpenGLTexture2D::~OpenGLTexture2D() { - auto self = this; - PM_RENDER_1(self, { + PM_RENDER_S({ glDeleteTextures(1, &self->m_RendererID); }); } - void OpenGLTexture2D::Bind(unsigned int slot) const + void OpenGLTexture2D::Bind(uint32_t slot) const { PM_RENDER_S1(slot, { glBindTextureUnit(slot, self->m_RendererID); }); } + void OpenGLTexture2D::Lock() + { + m_Locked = true; + } + + void OpenGLTexture2D::Unlock() + { + m_Locked = false; + PM_RENDER_S({ + glTextureSubImage2D(self->m_RendererID, 0, 0, 0, self->m_Width, self->m_Height, PrismToOpenGLTextureFormat(self->m_Format), GL_UNSIGNED_BYTE, self->m_ImageData.Data); + }); + } + + void OpenGLTexture2D::Resize(uint32_t width, uint32_t height) + { + PM_CORE_ASSERT(m_Locked, "Texture must be locked!"); + + m_ImageData.Allocate(width * height * Texture::GetBPP(m_Format)); +#if PM_DEBUG + m_ImageData.ZeroInitialize(); +#endif + } + + Buffer OpenGLTexture2D::GetWriteableBuffer() + { + PM_CORE_ASSERT(m_Locked, "Texture must be locked!"); + return m_ImageData; + } + // ****************************************** // TextureCube @@ -214,11 +244,10 @@ namespace Prism }); } - void OpenGLTextureCube::Bind(unsigned int slot) const + void OpenGLTextureCube::Bind(uint32_t slot) const { PM_RENDER_S1(slot, { - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_CUBE_MAP, self->m_RendererID); + glBindTextureUnit(slot, self->m_RendererID); }); } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h index a2cbec4..039062b 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.h @@ -13,15 +13,22 @@ namespace Prism class OpenGLTexture2D : public Texture2D { public: - OpenGLTexture2D(TextureFormat format, unsigned int width, unsigned int height); + OpenGLTexture2D(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap); OpenGLTexture2D(const std::string& path, bool srgb); virtual ~OpenGLTexture2D(); - virtual void Bind(unsigned int slot = 0) const override; + virtual void Bind(uint32_t slot = 0) const override; + + virtual TextureFormat GetFormat() const override { return m_Format; } + virtual uint32_t GetWidth() const override { return m_Width; } + virtual uint32_t GetHeight() const override { return m_Height; } + + virtual void Lock() override; + virtual void Unlock() override; + + virtual void Resize(uint32_t width, uint32_t height) override; + virtual Buffer GetWriteableBuffer() override; - virtual TextureFormat GetFormat() const { return m_Format; } - virtual unsigned int GetWidth() const { return m_Width; } - virtual unsigned int GetHeight() const { return m_Height; } virtual const std::string& GetPath() const override { return m_FilePath; } @@ -30,8 +37,11 @@ namespace Prism RendererID m_RendererID; TextureFormat m_Format; unsigned int m_Width, m_Height; + TextureWrap m_Wrap = TextureWrap::Clamp; - unsigned char* m_ImageData; + bool m_Locked = false; + + Buffer m_ImageData; std::string m_FilePath; }; diff --git a/Prism/src/Prism/Renderer/Camera.cpp b/Prism/src/Prism/Renderer/Camera.cpp index f27b680..9a37237 100644 --- a/Prism/src/Prism/Renderer/Camera.cpp +++ b/Prism/src/Prism/Renderer/Camera.cpp @@ -12,7 +12,6 @@ #include "glm/gtx/quaternion.hpp" #include "Prism/Core/Input.h" -#define M_PI 3.14159f namespace Prism { diff --git a/Prism/src/Prism/Renderer/Material.cpp b/Prism/src/Prism/Renderer/Material.cpp new file mode 100644 index 0000000..5f7ed81 --- /dev/null +++ b/Prism/src/Prism/Renderer/Material.cpp @@ -0,0 +1,178 @@ +// +// Created by sfd on 25-11-27. +// + +#include "Material.h" + +namespace Prism +{ + Material::Material(const Ref& shader) + : m_Shader(shader) + { + m_Shader->AddShaderReloadedCallback(std::bind(&Material::OnShaderReloaded, this)); + AllocateStorage(); + } + + Material::~Material() + { + } + + + + void Material::AllocateStorage() + { + const auto& vsBuffer = m_Shader->GetVSMaterialUniformBuffer(); + m_VSUniformStorageBuffer.Allocate(vsBuffer.GetSize()); + m_VSUniformStorageBuffer.ZeroInitialize(); + + const auto& psBuffer = m_Shader->GetPSMaterialUniformBuffer(); + m_PSUniformStorageBuffer.Allocate(psBuffer.GetSize()); + m_PSUniformStorageBuffer.ZeroInitialize(); + } + + void Material::OnShaderReloaded() + { + AllocateStorage(); + + for (auto mi : m_MaterialInstances) + mi->OnShaderReloaded(); + } + + ShaderUniformDeclaration* Material::FindUniformDeclaration(const std::string& name) + { + if (m_VSUniformStorageBuffer) + { + auto& declarations = m_Shader->GetVSMaterialUniformBuffer().GetUniformDeclarations(); + for (ShaderUniformDeclaration* uniform : declarations) + { + if (uniform->GetName() == name) + return uniform; + } + } + + if (m_PSUniformStorageBuffer) + { + auto& declarations = m_Shader->GetPSMaterialUniformBuffer().GetUniformDeclarations(); + for (ShaderUniformDeclaration* uniform : declarations) + { + if (uniform->GetName() == name) + return uniform; + } + } + return nullptr; + } + + ShaderResourceDeclaration* Material::FindResourceDeclaration(const std::string& name) + { + auto& resources = m_Shader->GetResources(); + for (ShaderResourceDeclaration* resource : resources) + { + if (resource->GetName() == name) + return resource; + } + return nullptr; + } + + Buffer& Material::GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration) + { + switch (uniformDeclaration->GetDomain()) + { + case ShaderDomain::Vertex: return m_VSUniformStorageBuffer; + case ShaderDomain::Pixel: return m_PSUniformStorageBuffer; + } + + PM_CORE_ASSERT(false, "Invalid uniform declaration domain! Material does not support this shader type."); + return m_VSUniformStorageBuffer; + } + + void Material::Bind() const + { + m_Shader->Bind(); + + if (m_VSUniformStorageBuffer) + m_Shader->SetVSMaterialUniformBuffer(m_VSUniformStorageBuffer); + + if (m_PSUniformStorageBuffer) + m_Shader->SetPSMaterialUniformBuffer(m_PSUniformStorageBuffer); + + BindTextures(); + } + + void Material::BindTextures() const + { + for (uint32_t i = 0; i < m_Textures.size(); i++) + { + auto& texture = m_Textures[i]; + if (texture) + texture->Bind(i); + } + } + + MaterialInstance::MaterialInstance(const Ref& material) + : m_Material(material) + { + m_Material->m_MaterialInstances.insert(this); + AllocateStorage(); + } + + MaterialInstance::~MaterialInstance() + { + m_Material->m_MaterialInstances.erase(this); + } + + void MaterialInstance::OnShaderReloaded() + { + AllocateStorage(); + m_OverriddenValues.clear(); + } + + void MaterialInstance::AllocateStorage() + { + const auto& vsBuffer = m_Material->m_Shader->GetVSMaterialUniformBuffer(); + m_VSUniformStorageBuffer.Allocate(vsBuffer.GetSize()); + memcpy(m_VSUniformStorageBuffer.Data, m_Material->m_VSUniformStorageBuffer.Data, vsBuffer.GetSize()); + + const auto& psBuffer = m_Material->m_Shader->GetPSMaterialUniformBuffer(); + m_PSUniformStorageBuffer.Allocate(psBuffer.GetSize()); + memcpy(m_PSUniformStorageBuffer.Data, m_Material->m_PSUniformStorageBuffer.Data, psBuffer.GetSize()); + } + + void MaterialInstance::OnMaterialValueUpdated(ShaderUniformDeclaration* decl) + { + if (m_OverriddenValues.find(decl->GetName()) == m_OverriddenValues.end()) + { + auto& buffer = GetUniformBufferTarget(decl); + auto& materialBuffer = m_Material->GetUniformBufferTarget(decl); + buffer.Write(materialBuffer.Data + decl->GetOffset(), decl->GetSize(), decl->GetOffset()); + } + } + + Buffer& MaterialInstance::GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration) + { + switch (uniformDeclaration->GetDomain()) + { + case ShaderDomain::Vertex: return m_VSUniformStorageBuffer; + case ShaderDomain::Pixel: return m_PSUniformStorageBuffer; + } + + PM_CORE_ASSERT(false, "Invalid uniform declaration domain! Material does not support this shader type."); + return m_VSUniformStorageBuffer; + } + + void MaterialInstance::Bind() const + { + if (m_VSUniformStorageBuffer) + m_Material->m_Shader->SetVSMaterialUniformBuffer(m_VSUniformStorageBuffer); + + if (m_PSUniformStorageBuffer) + m_Material->m_Shader->SetPSMaterialUniformBuffer(m_PSUniformStorageBuffer); + + m_Material->BindTextures(); + for (uint32_t i = 0; i < m_Textures.size(); i++) + { + auto& texture = m_Textures[i]; + if (texture) + texture->Bind(i); + } + } +} \ No newline at end of file diff --git a/Prism/src/Prism/Renderer/Material.h b/Prism/src/Prism/Renderer/Material.h new file mode 100644 index 0000000..ebfa659 --- /dev/null +++ b/Prism/src/Prism/Renderer/Material.h @@ -0,0 +1,141 @@ +// +// Created by sfd on 25-11-27. +// + +#ifndef MATERIAL_H +#define MATERIAL_H +#include + +#include "Shader.h" +#include "Texture.h" + +namespace Prism +{ + +#ifndef _MSC_VER + class MaterialInstance; +#endif + + class PRISM_API Material + { + friend class MaterialInstance; + public: + Material(const Ref& shader); + virtual ~Material(); + + void Bind() const; + + template + void Set(const std::string& name, const T& value); + + + void Set(const std::string& name, const Ref& texture) + { + auto decl = FindResourceDeclaration(name); + uint32_t slot = decl->GetRegister(); + if (m_Textures.size() <= slot) + m_Textures.resize((size_t)slot + 1); + m_Textures[slot] = texture; + } + + void Set(const std::string& name, const Ref& texture) + { + Set(name, (const Ref&)texture); + } + + void Set(const std::string& name, const Ref& texture) + { + Set(name, (const Ref&)texture); + } + private: + void AllocateStorage(); + void OnShaderReloaded(); + void BindTextures() const; + + ShaderUniformDeclaration* FindUniformDeclaration(const std::string& name); + ShaderResourceDeclaration* FindResourceDeclaration(const std::string& name); + Buffer& GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration); + private: + Ref m_Shader; + std::unordered_set m_MaterialInstances; + + Buffer m_VSUniformStorageBuffer; + Buffer m_PSUniformStorageBuffer; + std::vector> m_Textures; + + int32_t m_RenderFlags = 0; + }; + + + + class PRISM_API MaterialInstance + { + friend class Material; + public: + MaterialInstance(const Ref& material); + virtual ~MaterialInstance(); + + template + void Set(const std::string& name, const T& value) + { + auto decl = m_Material->FindUniformDeclaration(name); + // HZ_CORE_ASSERT(decl, "Could not find uniform with name '{0}'", name); + PM_CORE_ASSERT(decl, "Could not find uniform with name 'x'"); + auto& buffer = GetUniformBufferTarget(decl); + buffer.Write((byte*)& value, decl->GetSize(), decl->GetOffset()); + + m_OverriddenValues.insert(name); + } + + void Set(const std::string& name, const Ref& texture) + { + auto decl = m_Material->FindResourceDeclaration(name); + uint32_t slot = decl->GetRegister(); + if (m_Textures.size() <= slot) + m_Textures.resize((size_t)slot + 1); + m_Textures[slot] = texture; + } + + void Set(const std::string& name, const Ref& texture) + { + Set(name, (const Ref&)texture); + } + + void Set(const std::string& name, const Ref& texture) + { + Set(name, (const Ref&)texture); + } + + void Bind() const; + private: + void AllocateStorage(); + void OnShaderReloaded(); + Buffer& GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration); + void OnMaterialValueUpdated(ShaderUniformDeclaration* decl); + private: + Ref m_Material; + + Buffer m_VSUniformStorageBuffer; + Buffer m_PSUniformStorageBuffer; + std::vector> m_Textures; + + // TODO: This is temporary; come up with a proper system to track overrides + std::unordered_set m_OverriddenValues; + }; + + template + void Material::Set(const std::string& name, const T& value) + { + auto decl = FindUniformDeclaration(name); + // HZ_CORE_ASSERT(decl, "Could not find uniform with name '{0}'", name); + PM_CORE_ASSERT(decl, "Could not find uniform with name 'x'"); + auto& buffer = GetUniformBufferTarget(decl); + buffer.Write((byte*)&value, decl->GetSize(), decl->GetOffset()); + + for (auto mi : m_MaterialInstances) + mi->OnMaterialValueUpdated(decl); + } +} + + +#endif //MATERIAL_H diff --git a/Prism/src/Prism/Renderer/Mesh.cpp b/Prism/src/Prism/Renderer/Mesh.cpp index 70af0c4..2db8e0d 100644 --- a/Prism/src/Prism/Renderer/Mesh.cpp +++ b/Prism/src/Prism/Renderer/Mesh.cpp @@ -11,23 +11,21 @@ #include "assimp/postprocess.h" #include "assimp/scene.h" #include "glad/glad.h" -#include "Prism/Core/Log.h" + +#define GLM_ENABLE_EXPERIMENTAL +#include "glm/gtx/quaternion.hpp" namespace Prism { + const unsigned int s_ImportFlags = + aiProcess_CalcTangentSpace | // Create binormals/tangents just in case + aiProcess_Triangulate | // Make sure we're triangles + aiProcess_SortByPType | // Split meshes by primitive type + aiProcess_GenNormals | // Make sure we have legit normals + aiProcess_GenUVCoords | // Convert UVs if required + aiProcess_OptimizeMeshes | // Batch draws where possible + aiProcess_ValidateDataStructure; // Validation - namespace { - const unsigned int ImportFlags = - aiProcess_CalcTangentSpace | - aiProcess_Triangulate | - aiProcess_SortByPType | - aiProcess_PreTransformVertices | - aiProcess_GenNormals | - aiProcess_GenUVCoords | - aiProcess_OptimizeMeshes | - aiProcess_Debone | - aiProcess_ValidateDataStructure; - } struct LogStream : public Assimp::LogStream { @@ -40,12 +38,56 @@ namespace Prism } } - void write(const char* message) override + virtual void write(const char* message) override { PM_CORE_ERROR("Assimp error: {0}", message); } }; + static glm::mat4 aiMatrix4x4ToGlm(const aiMatrix4x4 &from) + { + glm::mat4 to; + //the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column + to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4; + to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4; + to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4; + to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4; + return to; + } + + + void Vertex::AddBoneData(uint32_t BoneID, float Weight) + { + for (size_t i = 0; i < 4; i++) + { + if (Weights[i] == 0.0) + { + IDs[i] = BoneID; + Weights[i] = Weight; + return; + } + } + + // TODO: Keep top weights + PM_CORE_WARN("Vertex has more than four bones/weights affecting it, extra data will be discarded (BoneID={0}, Weight={1})", BoneID, Weight); + } + + void VertexBoneData::AddBoneData(uint32_t BoneID, float Weight) + { + for (size_t i = 0; i < 4; i++) + { + if (Weights[i] == 0.0) + { + IDs[i] = BoneID; + Weights[i] = Weight; + return; + } + } + + // should never get here - more bones than we have space for + PM_CORE_ASSERT(false, "Too many bones!"); + } + Mesh::Mesh(const std::string& filename) : m_FilePath(filename) { @@ -53,77 +95,365 @@ namespace Prism PM_CORE_INFO("Loading mesh: {0}", filename.c_str()); - Assimp::Importer importer; + m_Importer = std::make_unique(); - const aiScene* scene = importer.ReadFile(filename, ImportFlags); + const aiScene* scene = m_Importer->ReadFile(filename, s_ImportFlags); if (!scene || !scene->HasMeshes()) PM_CORE_ERROR("Failed to load mesh file: {0}", filename); - aiMesh* mesh = scene->mMeshes[0]; - PM_CORE_ASSERT(mesh->HasPositions(), "Meshes require positions."); - PM_CORE_ASSERT(mesh->HasNormals(), "Meshes require normals."); + m_InverseTransform = glm::inverse(aiMatrix4x4ToGlm(scene->mRootNode->mTransformation)); - m_Vertices.reserve(mesh->mNumVertices); + uint32_t vertexCount = 0; + uint32_t indexCount = 0; - // Extract vertices from model - for (size_t i = 0; i < m_Vertices.capacity(); i++) + m_Submeshes.reserve(scene->mNumMeshes); + for (size_t m = 0; m < scene->mNumMeshes; m++) { - Vertex vertex; - vertex.Position = { mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z }; - vertex.Normal = { mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z }; + aiMesh* mesh = scene->mMeshes[m]; - if (mesh->HasTangentsAndBitangents()) + Submesh submesh; + submesh.BaseVertex = vertexCount; + submesh.BaseIndex = indexCount; + submesh.MaterialIndex = mesh->mMaterialIndex; + submesh.IndexCount = mesh->mNumFaces * 3; + m_Submeshes.push_back(submesh); + + vertexCount += mesh->mNumVertices; + indexCount += submesh.IndexCount; + + PM_CORE_ASSERT(mesh->HasPositions(), "Meshes require positions."); + PM_CORE_ASSERT(mesh->HasNormals(), "Meshes require normals."); + + // Vertices + for (size_t i = 0; i < mesh->mNumVertices; i++) { - vertex.Tangent = { mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z }; - vertex.Binormal = { mesh->mBitangents[i].x, mesh->mBitangents[i].y, mesh->mBitangents[i].z }; + Vertex vertex; + vertex.Position = { mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z }; + vertex.Normal = { mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z }; + + if (mesh->HasTangentsAndBitangents()) + { + vertex.Tangent = { mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z }; + vertex.Binormal = { mesh->mBitangents[i].x, mesh->mBitangents[i].y, mesh->mBitangents[i].z }; + } + + if (mesh->HasTextureCoords(0)) + vertex.Texcoord = { mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y }; + m_Vertices.push_back(vertex); } - if (mesh->HasTextureCoords(0)) - vertex.Texcoord = { mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y }; - m_Vertices.push_back(vertex); + // Indices + for (size_t i = 0; i < mesh->mNumFaces; i++) + { + PM_CORE_ASSERT(mesh->mFaces[i].mNumIndices == 3, "Must have 3 indices."); + m_Indices.push_back({ mesh->mFaces[i].mIndices[0], mesh->mFaces[i].mIndices[1], mesh->mFaces[i].mIndices[2] }); + } + } + + // Bones + for (size_t m = 0; m < scene->mNumMeshes; m++) + { + aiMesh* mesh = scene->mMeshes[m]; + Submesh& submesh = m_Submeshes[m]; + + for (size_t i = 0; i < mesh->mNumBones; i++) + { + aiBone* bone = mesh->mBones[i]; + std::string boneName(bone->mName.data); + int boneIndex = 0; + + if (m_BoneMapping.find(boneName) == m_BoneMapping.end()) + { + // Allocate an index for a new bone + boneIndex = m_BoneCount; + m_BoneCount++; + BoneInfo bi; + m_BoneInfo.push_back(bi); + m_BoneInfo[boneIndex].BoneOffset = aiMatrix4x4ToGlm(bone->mOffsetMatrix); + m_BoneMapping[boneName] = boneIndex; + } + else + { + PM_CORE_TRACE("Found existing bone in map"); + boneIndex = m_BoneMapping[boneName]; + } + + for (size_t j = 0; j < bone->mNumWeights; j++) + { + int VertexID = submesh.BaseVertex + bone->mWeights[j].mVertexId; + float Weight = bone->mWeights[j].mWeight; + m_Vertices[VertexID].AddBoneData(boneIndex, Weight); + } + } } m_VertexBuffer.reset(VertexBuffer::Create()); - m_VertexBuffer->SetData(m_Vertices.data(), (uint32_t)(m_Vertices.size() * sizeof(Vertex))); - - // Extract indices from model - m_Indices.reserve(mesh->mNumFaces); - for (size_t i = 0; i < m_Indices.capacity(); i++) - { - PM_CORE_ASSERT(mesh->mFaces[i].mNumIndices == 3, "Must have 3 indices."); - m_Indices.push_back({ mesh->mFaces[i].mIndices[0], mesh->mFaces[i].mIndices[1], mesh->mFaces[i].mIndices[2] }); - } + m_VertexBuffer->SetData(m_Vertices.data(), m_Vertices.size() * sizeof(Vertex)); m_IndexBuffer.reset(IndexBuffer::Create()); - m_IndexBuffer->SetData(m_Indices.data(), (uint32_t)(m_Indices.size() * sizeof(Index))); + m_IndexBuffer->SetData(m_Indices.data(), m_Indices.size() * sizeof(Index)); + + m_Scene = scene; } Mesh::~Mesh() { } - void Mesh::Render() + void Mesh::Render(TimeStep deltaTime, Shader* shader) { - // TODO: Sort this out - m_VertexBuffer->Bind(); - m_IndexBuffer->Bind(); - PM_RENDER_S({ - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Position)); + if (m_AnimationPlaying && m_Scene->mAnimations) + { + m_WorldTime += deltaTime; - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Normal)); + float ticksPerSecond = (float)(m_Scene->mAnimations[0]->mTicksPerSecond != 0 ? m_Scene->mAnimations[0]->mTicksPerSecond : 25.0f) * m_TimeMultiplier; + m_AnimationTime += deltaTime * ticksPerSecond; + m_AnimationTime = fmod(m_AnimationTime, (float)m_Scene->mAnimations[0]->mDuration); + } - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Tangent)); + if (m_Scene->mAnimations) + BoneTransform(m_AnimationTime); - glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Binormal)); + // TODO: Sort this out + m_VertexBuffer->Bind(); + m_IndexBuffer->Bind(); - glEnableVertexAttribArray(4); - glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Texcoord)); - }); - Renderer::DrawIndexed(m_IndexBuffer->GetCount()); + // TODO: replace with render API calls + PM_RENDER_S1(shader, { + for (Submesh& submesh : self->m_Submeshes) + { + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Position)); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Normal)); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Tangent)); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Binormal)); + + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Texcoord)); + + glEnableVertexAttribArray(5); + glVertexAttribIPointer(5, 4, GL_INT, sizeof(Vertex), (const void*)offsetof(Vertex, IDs)); + + glEnableVertexAttribArray(6); + glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, Weights)); + + if (self->m_Scene->mAnimations) + { + for (size_t i = 0; i < self->m_BoneTransforms.size(); i++) + { + std::string uniformName = std::string("u_BoneTransforms[") + std::to_string(i) + std::string("]"); + shader->SetMat4FromRenderThread(uniformName, self->m_BoneTransforms[i]); + } + } + glDrawElementsBaseVertex(GL_TRIANGLES, submesh.IndexCount, GL_UNSIGNED_INT, (void*)(sizeof(uint32_t) * submesh.BaseIndex), submesh.BaseVertex); + } + }); + } + + void Mesh::OnImGuiRender() + { + ImGui::Begin("Mesh Debug"); + if (ImGui::CollapsingHeader(m_FilePath.c_str())) + { + if (ImGui::CollapsingHeader("Animation")) + { + if (ImGui::Button(m_AnimationPlaying ? "Pause" : "Play")) + m_AnimationPlaying = !m_AnimationPlaying; + + ImGui::SliderFloat("##AnimationTime", &m_AnimationTime, 0.0f, (float)m_Scene->mAnimations[0]->mDuration); + ImGui::DragFloat("Time Scale", &m_TimeMultiplier, 0.05f, 0.0f, 10.0f); + } + } + + ImGui::End(); + } + + void Mesh::DumpVertexBuffer() + { + // TODO: Convert to ImGui + PM_CORE_TRACE("------------------------------------------------------"); + PM_CORE_TRACE("Vertex Buffer Dump"); + PM_CORE_TRACE("Mesh: {0}", m_FilePath); + for (size_t i = 0; i < m_Vertices.size(); i++) + { + auto& vertex = m_Vertices[i]; + PM_CORE_TRACE("Vertex: {0}", i); + PM_CORE_TRACE("Position: {0}, {1}, {2}", vertex.Position.x, vertex.Position.y, vertex.Position.z); + PM_CORE_TRACE("Normal: {0}, {1}, {2}", vertex.Normal.x, vertex.Normal.y, vertex.Normal.z); + PM_CORE_TRACE("Binormal: {0}, {1}, {2}", vertex.Binormal.x, vertex.Binormal.y, vertex.Binormal.z); + PM_CORE_TRACE("Tangent: {0}, {1}, {2}", vertex.Tangent.x, vertex.Tangent.y, vertex.Tangent.z); + PM_CORE_TRACE("TexCoord: {0}, {1}", vertex.Texcoord.x, vertex.Texcoord.y); + PM_CORE_TRACE("--"); + } + PM_CORE_TRACE("------------------------------------------------------"); + } + + void Mesh::BoneTransform(float time) + { + ReadNodeHierarchy(time, m_Scene->mRootNode, glm::mat4(1.0f)); + m_BoneTransforms.resize(m_BoneCount); + for (size_t i = 0; i < m_BoneCount; i++) + m_BoneTransforms[i] = m_BoneInfo[i].FinalTransformation; + } + + void Mesh::ReadNodeHierarchy(float AnimationTime, const aiNode* pNode, const glm::mat4& ParentTransform) + { + std::string name(pNode->mName.data); + const aiAnimation* animation = m_Scene->mAnimations[0]; + glm::mat4 nodeTransform(aiMatrix4x4ToGlm(pNode->mTransformation)); + const aiNodeAnim* nodeAnim = FindNodeAnim(animation, name); + + if (nodeAnim) + { + glm::vec3 translation = InterpolateTranslation(AnimationTime, nodeAnim); + glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(translation.x, translation.y, translation.z)); + + glm::quat rotation = InterpolateRotation(AnimationTime, nodeAnim); + glm::mat4 rotationMatrix = glm::toMat4(rotation); + + glm::vec3 scale = InterpolateScale(AnimationTime, nodeAnim); + glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(scale.x, scale.y, scale.z)); + + nodeTransform = translationMatrix * rotationMatrix * scaleMatrix; + } + + glm::mat4 transform = ParentTransform * nodeTransform; + + if (m_BoneMapping.find(name) != m_BoneMapping.end()) + { + uint32_t BoneIndex = m_BoneMapping[name]; + m_BoneInfo[BoneIndex].FinalTransformation = m_InverseTransform * transform * m_BoneInfo[BoneIndex].BoneOffset; + } + + for (uint32_t i = 0; i < pNode->mNumChildren; i++) + ReadNodeHierarchy(AnimationTime, pNode->mChildren[i], transform); + } + + const aiNodeAnim* Mesh::FindNodeAnim(const aiAnimation* animation, const std::string& nodeName) + { + for (uint32_t i = 0; i < animation->mNumChannels; i++) + { + const aiNodeAnim* nodeAnim = animation->mChannels[i]; + if (std::string(nodeAnim->mNodeName.data) == nodeName) + return nodeAnim; + } + return nullptr; + } + + uint32_t Mesh::FindPosition(float AnimationTime, const aiNodeAnim* pNodeAnim) + { + for (uint32_t i = 0; i < pNodeAnim->mNumPositionKeys - 1; i++) + { + if (AnimationTime < (float)pNodeAnim->mPositionKeys[i + 1].mTime) + return i; + } + + return 0; + } + + uint32_t Mesh::FindRotation(float AnimationTime, const aiNodeAnim* pNodeAnim) + { + PM_CORE_ASSERT(pNodeAnim->mNumRotationKeys > 0); + + for (uint32_t i = 0; i < pNodeAnim->mNumRotationKeys - 1; i++) + { + if (AnimationTime < (float)pNodeAnim->mRotationKeys[i + 1].mTime) + return i; + } + + return 0; + } + + uint32_t Mesh::FindScaling(float AnimationTime, const aiNodeAnim* pNodeAnim) + { + PM_CORE_ASSERT(pNodeAnim->mNumScalingKeys > 0); + + for (uint32_t i = 0; i < pNodeAnim->mNumScalingKeys - 1; i++) + { + if (AnimationTime < (float)pNodeAnim->mScalingKeys[i + 1].mTime) + return i; + } + + return 0; + } + + glm::vec3 Mesh::InterpolateTranslation(float animationTime, const aiNodeAnim* nodeAnim) + { + if (nodeAnim->mNumPositionKeys == 1) + { + // No interpolation necessary for single value + auto v = nodeAnim->mPositionKeys[0].mValue; + return { v.x, v.y, v.z }; + } + + uint32_t PositionIndex = FindPosition(animationTime, nodeAnim); + uint32_t NextPositionIndex = (PositionIndex + 1); + PM_CORE_ASSERT(NextPositionIndex < nodeAnim->mNumPositionKeys); + float DeltaTime = (float)(nodeAnim->mPositionKeys[NextPositionIndex].mTime - nodeAnim->mPositionKeys[PositionIndex].mTime); + float Factor = (animationTime - (float)nodeAnim->mPositionKeys[PositionIndex].mTime) / DeltaTime; + if (Factor < 0.0f) + Factor = 0.0f; + PM_CORE_ASSERT(Factor <= 1.0f, "Factor must be below 1.0f"); + const aiVector3D& Start = nodeAnim->mPositionKeys[PositionIndex].mValue; + const aiVector3D& End = nodeAnim->mPositionKeys[NextPositionIndex].mValue; + aiVector3D Delta = End - Start; + auto aiVec = Start + Factor * Delta; + return { aiVec.x, aiVec.y, aiVec.z }; + } + + glm::quat Mesh::InterpolateRotation(float animationTime, const aiNodeAnim* nodeAnim) + { + if (nodeAnim->mNumRotationKeys == 1) + { + // No interpolation necessary for single value + auto v = nodeAnim->mRotationKeys[0].mValue; + return glm::quat(v.w, v.x, v.y, v.z); + } + + uint32_t RotationIndex = FindRotation(animationTime, nodeAnim); + uint32_t NextRotationIndex = (RotationIndex + 1); + PM_CORE_ASSERT(NextRotationIndex < nodeAnim->mNumRotationKeys); + float DeltaTime = (float)(nodeAnim->mRotationKeys[NextRotationIndex].mTime - nodeAnim->mRotationKeys[RotationIndex].mTime); + float Factor = (animationTime - (float)nodeAnim->mRotationKeys[RotationIndex].mTime) / DeltaTime; + if (Factor < 0.0f) + Factor = 0.0f; + PM_CORE_ASSERT(Factor <= 1.0f, "Factor must be below 1.0f"); + const aiQuaternion& StartRotationQ = nodeAnim->mRotationKeys[RotationIndex].mValue; + const aiQuaternion& EndRotationQ = nodeAnim->mRotationKeys[NextRotationIndex].mValue; + auto q = aiQuaternion(); + aiQuaternion::Interpolate(q, StartRotationQ, EndRotationQ, Factor); + q = q.Normalize(); + return glm::quat(q.w, q.x, q.y, q.z); + } + + glm::vec3 Mesh::InterpolateScale(float animationTime, const aiNodeAnim* nodeAnim) + { + if (nodeAnim->mNumScalingKeys == 1) + { + // No interpolation necessary for single value + auto v = nodeAnim->mScalingKeys[0].mValue; + return { v.x, v.y, v.z }; + } + + uint32_t index = FindScaling(animationTime, nodeAnim); + uint32_t nextIndex = (index + 1); + PM_CORE_ASSERT(nextIndex < nodeAnim->mNumScalingKeys); + float deltaTime = (float)(nodeAnim->mScalingKeys[nextIndex].mTime - nodeAnim->mScalingKeys[index].mTime); + float factor = (animationTime - (float)nodeAnim->mScalingKeys[index].mTime) / deltaTime; + if (factor < 0.0f) + factor = 0.0f; + PM_CORE_ASSERT(factor <= 1.0f, "Factor must be below 1.0f"); + const auto& start = nodeAnim->mScalingKeys[index].mValue; + const auto& end = nodeAnim->mScalingKeys[nextIndex].mValue; + auto delta = end - start; + auto aiVec = start + factor * delta; + return { aiVec.x, aiVec.y, aiVec.z }; } } diff --git a/Prism/src/Prism/Renderer/Mesh.h b/Prism/src/Prism/Renderer/Mesh.h index 6659d12..a9a7411 100644 --- a/Prism/src/Prism/Renderer/Mesh.h +++ b/Prism/src/Prism/Renderer/Mesh.h @@ -8,42 +8,122 @@ #include #include "Buffer.h" +#include "Shader.h" +#include "Prism/Core/TimeStep.h" + +struct aiNode; +struct aiAnimation; +struct aiNodeAnim; +struct aiScene; + +namespace Assimp +{ + class Importer; +} namespace Prism { + struct Vertex + { + glm::vec3 Position; + glm::vec3 Normal; + glm::vec3 Tangent; + glm::vec3 Binormal; + glm::vec2 Texcoord; + + uint32_t IDs[4] = { 0, 0,0, 0 }; + float Weights[4]{ 0.0f, 0.0f, 0.0f, 0.0f }; + + void AddBoneData(uint32_t BoneID, float Weight); + }; + + static const int NumAttributes = 5; + + struct Index + { + uint32_t V1, V2, V3; + }; + static_assert(sizeof(Index) == 3 * sizeof(uint32_t)); + + struct BoneInfo + { + glm::mat4 BoneOffset; + glm::mat4 FinalTransformation; + }; + + struct VertexBoneData + { + uint32_t IDs[4]; + float Weights[4]; + + VertexBoneData() + { + memset(IDs, 0, sizeof(IDs)); + memset(Weights, 0, sizeof(Weights)); + }; + + void AddBoneData(uint32_t BoneID, float Weight); + }; + + class Submesh + { + public: + uint32_t BaseVertex; + uint32_t BaseIndex; + uint32_t MaterialIndex; + uint32_t IndexCount; + }; + class PRISM_API Mesh { public: - struct Vertex - { - glm::vec3 Position; - glm::vec3 Normal; - glm::vec3 Tangent; - glm::vec3 Binormal; - glm::vec2 Texcoord; - }; - static_assert(sizeof(Vertex) == 14 * sizeof(float)); - static const int NumAttributes = 5; - struct Index - { - uint32_t V1, V2, V3; - }; - static_assert(sizeof(Index) == 3 * sizeof(uint32_t)); Mesh(const std::string& filename); ~Mesh(); - void Render(); + void Render(TimeStep deltaTime, Shader* shader); + void OnImGuiRender(); + void DumpVertexBuffer(); inline const std::string& GetFilePath() const { return m_FilePath; } private: - std::vector m_Vertices; - std::vector m_Indices; + void BoneTransform(float time); + void ReadNodeHierarchy(float AnimationTime, const aiNode* pNode, const glm::mat4& ParentTransform); + + const aiNodeAnim* FindNodeAnim(const aiAnimation* animation, const std::string& nodeName); + uint32_t FindPosition(float AnimationTime, const aiNodeAnim* pNodeAnim); + uint32_t FindRotation(float AnimationTime, const aiNodeAnim* pNodeAnim); + uint32_t FindScaling(float AnimationTime, const aiNodeAnim* pNodeAnim); + glm::vec3 InterpolateTranslation(float animationTime, const aiNodeAnim* nodeAnim); + glm::quat InterpolateRotation(float animationTime, const aiNodeAnim* nodeAnim); + glm::vec3 InterpolateScale(float animationTime, const aiNodeAnim* nodeAnim); + + private: + std::vector m_Submeshes; + + std::unique_ptr m_Importer; + + glm::mat4 m_InverseTransform; + + uint32_t m_BoneCount = 0; + std::vector m_BoneInfo; std::unique_ptr m_VertexBuffer; std::unique_ptr m_IndexBuffer; + std::vector m_Vertices; + std::vector m_Indices; + std::unordered_map m_BoneMapping; + std::vector m_BoneTransforms; + const aiScene* m_Scene; + + // Animation + float m_AnimationTime = 0.0f; + float m_WorldTime = 0.0f; + float m_TimeMultiplier = 1.0f; + bool m_AnimationPlaying = true; + std::string m_FilePath; }; } diff --git a/Prism/src/Prism/Renderer/Shader.cpp b/Prism/src/Prism/Renderer/Shader.cpp index 5336efb..66f6140 100644 --- a/Prism/src/Prism/Renderer/Shader.cpp +++ b/Prism/src/Prism/Renderer/Shader.cpp @@ -9,7 +9,7 @@ namespace Prism { - // std::vector Shader::s_AllShaders; + std::vector Shader::s_AllShaders; Shader* Shader::Create(const std::string& filepath) { @@ -25,4 +25,9 @@ namespace Prism return result; } + + std::vector& Shader::GetAllShaders() + { + return s_AllShaders; + } } diff --git a/Prism/src/Prism/Renderer/Shader.h b/Prism/src/Prism/Renderer/Shader.h index ce19f4b..4181d51 100644 --- a/Prism/src/Prism/Renderer/Shader.h +++ b/Prism/src/Prism/Renderer/Shader.h @@ -8,6 +8,8 @@ #include #include "glm/gtc/type_ptr.hpp" +#include "Prism/Core/Buffer.h" +#include "ShaderUniform.h" namespace Prism { @@ -95,17 +97,36 @@ namespace Prism class PRISM_API Shader { public: + using ShaderReloadedCallback = std::function; + + virtual void Reload() = 0; virtual void Bind() = 0; virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0; virtual void SetFloat(const std::string& name, float value) = 0; virtual void SetMat4(const std::string& name, const glm::mat4& value) = 0; + virtual void SetMat4FromRenderThread(const std::string& name, const glm::mat4& value) = 0; + virtual const std::string& GetName() const = 0; - static Shader* Create(const std::string& filepath); + virtual void SetVSMaterialUniformBuffer(Buffer buffer) = 0; + virtual void SetPSMaterialUniformBuffer(Buffer buffer) = 0; - static inline std::vector s_AllShaders; + virtual const ShaderUniformBufferList& GetVSRendererUniforms() const = 0; + virtual const ShaderUniformBufferList& GetPSRendererUniforms() const = 0; + virtual const ShaderUniformBufferDeclaration& GetVSMaterialUniformBuffer() const = 0; + virtual const ShaderUniformBufferDeclaration& GetPSMaterialUniformBuffer() const = 0; + + virtual const ShaderResourceList& GetResources() const = 0; + + virtual void AddShaderReloadedCallback(const ShaderReloadedCallback& callback) = 0; + + static Shader* Create(const std::string& filepath); + static std::vector& GetAllShaders(); + + private: + static std::vector s_AllShaders; }; } diff --git a/Prism/src/Prism/Renderer/ShaderUniform.h b/Prism/src/Prism/Renderer/ShaderUniform.h new file mode 100644 index 0000000..dd4f182 --- /dev/null +++ b/Prism/src/Prism/Renderer/ShaderUniform.h @@ -0,0 +1,95 @@ +// +// Created by sfd on 25-11-27. +// + +#ifndef SHADERUNIFORM_H +#define SHADERUNIFORM_H + +namespace Prism +{ + enum class ShaderDomain + { + None = 0, Vertex = 0, Pixel = 1 + }; + + class PRISM_API ShaderUniformDeclaration + { + private: + friend class Shader; + friend class OpenGLShader; + friend class ShaderStruct; + public: + virtual const std::string& GetName() const = 0; + virtual uint32_t GetSize() const = 0; + virtual uint32_t GetCount() const = 0; + virtual uint32_t GetOffset() const = 0; + virtual ShaderDomain GetDomain() const = 0; + protected: + virtual void SetOffset(uint32_t offset) = 0; + }; + + typedef std::vector ShaderUniformList; + + class PRISM_API ShaderUniformBufferDeclaration + { + public: + virtual const std::string& GetName() const = 0; + virtual uint32_t GetRegister() const = 0; + virtual uint32_t GetSize() const = 0; + virtual const ShaderUniformList& GetUniformDeclarations() const = 0; + + virtual ShaderUniformDeclaration* FindUniform(const std::string& name) = 0; + }; + + typedef std::vector ShaderUniformBufferList; + + class ShaderStruct + { + private: + friend class Shader; + private: + std::string m_Name; + std::vector m_Fields; + uint32_t m_Size; + uint32_t m_Offset; + public: + ShaderStruct(const std::string& name) + : m_Name(name), m_Size(0), m_Offset(0) + { + } + + void AddField(ShaderUniformDeclaration* field) + { + m_Size += field->GetSize(); + uint32_t offset = 0; + if (m_Fields.size()) + { + ShaderUniformDeclaration* previous = m_Fields.back(); + offset = previous->GetOffset() + previous->GetSize(); + } + field->SetOffset(offset); + m_Fields.push_back(field); + } + + inline void SetOffset(uint32_t offset) { m_Offset = offset; } + + inline const std::string& GetName() const { return m_Name; } + inline uint32_t GetSize() const { return m_Size; } + inline uint32_t GetOffset() const { return m_Offset; } + inline const std::vector& GetFields() const { return m_Fields; } + }; + + typedef std::vector ShaderStructList; + + class ShaderResourceDeclaration + { + public: + virtual const std::string& GetName() const = 0; + virtual uint32_t GetRegister() const = 0; + virtual uint32_t GetCount() const = 0; + }; + + typedef std::vector ShaderResourceList; +} + +#endif //SHADERUNIFORM_H diff --git a/Prism/src/Prism/Renderer/Texture.cpp b/Prism/src/Prism/Renderer/Texture.cpp index 7048e2b..90d3cf4 100644 --- a/Prism/src/Prism/Renderer/Texture.cpp +++ b/Prism/src/Prism/Renderer/Texture.cpp @@ -9,16 +9,27 @@ namespace Prism { - Texture2D* Texture2D::Create(TextureFormat format, unsigned int width, unsigned int height) + uint32_t Texture::GetBPP(const TextureFormat format) + { + switch (format) + { + case TextureFormat::RGB: return 3; + case TextureFormat::RGBA: return 4; + } + return 0; + } + + Texture2D* Texture2D::Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap) { switch (RendererAPI::Current()) { case RendererAPIType::None: return nullptr; - case RendererAPIType::OpenGL: return new OpenGLTexture2D(format, width, height); + case RendererAPIType::OpenGL: return new OpenGLTexture2D(format, width, height, wrap); } return nullptr; } + Texture2D* Texture2D::Create(const std::string& path, bool srgb) { switch (RendererAPI::Current()) diff --git a/Prism/src/Prism/Renderer/Texture.h b/Prism/src/Prism/Renderer/Texture.h index be807cf..03e69e2 100644 --- a/Prism/src/Prism/Renderer/Texture.h +++ b/Prism/src/Prism/Renderer/Texture.h @@ -5,6 +5,7 @@ #ifndef TEXTURE_H #define TEXTURE_H #include "RendererAPI.h" +#include "Prism/Core/Buffer.h" namespace Prism @@ -17,24 +18,40 @@ namespace Prism RGBA = 2, }; + enum class TextureWrap + { + None = 0, + Clamp = 1, + Repeat = 2 + }; + class PRISM_API Texture { public: virtual ~Texture() {} + + virtual void Bind(uint32_t slot = 0) const = 0; + virtual RendererID GetRendererID() const = 0; + + static uint32_t GetBPP(TextureFormat format); }; class PRISM_API Texture2D : public Texture { public: - static Texture2D* Create(TextureFormat format, unsigned int width, unsigned int height); + static Texture2D* Create(TextureFormat format, unsigned int width, unsigned int height, TextureWrap wrap = TextureWrap::Clamp); static Texture2D* Create(const std::string& path, bool srgb = false); - virtual void Bind(unsigned int slot = 0) const = 0; virtual TextureFormat GetFormat() const = 0; - virtual unsigned int GetWidth() const = 0; - virtual unsigned int GetHeight() const = 0; + virtual uint32_t GetWidth() const = 0; + virtual uint32_t GetHeight() const = 0; + + virtual void Lock() = 0; + virtual void Unlock() = 0; + virtual void Resize(uint32_t width, uint32_t height) = 0; + virtual Buffer GetWriteableBuffer() = 0; virtual const std::string& GetPath() const = 0; }; @@ -44,11 +61,9 @@ namespace Prism public: static TextureCube* Create(const std::string& path); - virtual void Bind(unsigned int slot = 0) const = 0; - virtual TextureFormat GetFormat() const = 0; - virtual unsigned int GetWidth() const = 0; - virtual unsigned int GetHeight() const = 0; + virtual uint32_t GetWidth() const = 0; + virtual uint32_t GetHeight() const = 0; virtual const std::string& GetPath() const = 0; }; diff --git a/Sandbox/Sandbox/Layer/DemoLayer.cpp b/Sandbox/Sandbox/Layer/DemoLayer.cpp index a24abd4..1363034 100644 --- a/Sandbox/Sandbox/Layer/DemoLayer.cpp +++ b/Sandbox/Sandbox/Layer/DemoLayer.cpp @@ -7,21 +7,100 @@ #include "Prism/Renderer/Renderer.h" #include "Prism/Renderer/Shader.h" -static void ImGuiShowHelpMarker(const char* desc) -{ - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) + +namespace { + enum class PropertyFlag { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(desc); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + None = 0, ColorProperty = 1 + }; + + void Property(const std::string& name, bool& value) + { + ImGui::Text(name.c_str()); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + std::string id = "##" + name; + ImGui::Checkbox(id.c_str(), &value); + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + } + + void Property(const std::string& name, float& value, float min = -1.0f, float max = 1.0f, PropertyFlag flags = PropertyFlag::None) + { + ImGui::Text(name.c_str()); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + std::string id = "##" + name; + ImGui::SliderFloat(id.c_str(), &value, min, max); + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + } + + + void Property(const std::string& name, glm::vec3& value, float min = -1.0f, float max = 1.0f, PropertyFlag flags = PropertyFlag::None) + { + ImGui::Text(name.c_str()); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + std::string id = "##" + name; + if ((int)flags & (int)PropertyFlag::ColorProperty) + ImGui::ColorEdit3(id.c_str(), glm::value_ptr(value), ImGuiColorEditFlags_NoInputs); + else + ImGui::SliderFloat3(id.c_str(), glm::value_ptr(value), min, max); + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + } + + void Property(const std::string& name, glm::vec3& value, PropertyFlag flags) + { + Property(name, value, -1.0f, 1.0f, flags); + } + + + void Property(const std::string& name, glm::vec4& value, float min = -1.0f, float max = 1.0f, PropertyFlag flags = PropertyFlag::None) + { + ImGui::Text(name.c_str()); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + + std::string id = "##" + name; + if ((int)flags & (int)PropertyFlag::ColorProperty) + ImGui::ColorEdit4(id.c_str(), glm::value_ptr(value), ImGuiColorEditFlags_NoInputs); + else + ImGui::SliderFloat4(id.c_str(), glm::value_ptr(value), min, max); + + ImGui::PopItemWidth(); + ImGui::NextColumn(); + } + + void Property(const std::string& name, glm::vec4& value, PropertyFlag flags) + { + Property(name, value, -1.0f, 1.0f, flags); + } + + + static void ImGuiShowHelpMarker(const char* desc) + { + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } } } DemoLayer::DemoLayer() - : m_ClearColor{ 0.1f, 0.1f, 0.1f, 1.0f }, m_Scene(Scene::Spheres), + : m_ClearColor{ 0.1f, 0.1f, 0.1f, 1.0f }, m_Scene(Scene::Model), m_Camera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 10000.0f)) { } @@ -35,8 +114,10 @@ void DemoLayer::OnAttach() m_SimplePBRShader.reset(Prism::Shader::Create("assets/shaders/simplepbr.glsl")); m_QuadShader.reset(Prism::Shader::Create("assets/shaders/quad.glsl")); m_HDRShader.reset(Prism::Shader::Create("assets/shaders/hdr.glsl")); - m_Mesh.reset(new Prism::Mesh("assets/meshes/cerberus.fbx")); - m_SphereMesh.reset(new Prism::Mesh("assets/models/Sphere.fbx")); + m_GridShader.reset(Prism::Shader::Create("assets/shaders/Grid.glsl")); + m_Mesh.reset(new Prism::Mesh("assets/models/m1911/m1911.fbx")); + m_SphereMesh.reset(new Prism::Mesh("assets/models/Sphere1m.fbx")); + m_PlaneMesh.reset(new Prism::Mesh("assets/models/Plane1m.fbx")); // Editor m_CheckerboardTex.reset(Prism::Texture2D::Create("assets/editor/Checkerboard.tga")); @@ -49,8 +130,36 @@ void DemoLayer::OnAttach() m_Framebuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA16F)); m_FinalPresentBuffer.reset(Prism::FrameBuffer::Create(1280, 720, Prism::FramebufferFormat::RGBA8)); + m_PBRMaterial.reset(new Prism::Material(m_SimplePBRShader)); + + float x = -4.0f; + float roughness = 0.0f; + for (int i = 0; i < 8; i++) + { + Prism::Ref mi(new Prism::MaterialInstance(m_PBRMaterial)); + mi->Set("u_Metalness", 1.0f); + mi->Set("u_Roughness", roughness); + mi->Set("u_ModelMatrix", glm::translate(glm::mat4(1.0f), glm::vec3(x, 0.0f, 0.0f))); + x += 1.1f; + roughness += 0.15f; + m_MetalSphereMaterialInstances.push_back(mi); + } + + x = -4.0f; + roughness = 0.0f; + for (int i = 0; i < 8; i++) + { + Prism::Ref mi(new Prism::MaterialInstance(m_PBRMaterial)); + mi->Set("u_Metalness", 0.0f); + mi->Set("u_Roughness", roughness); + mi->Set("u_ModelMatrix", translate(glm::mat4(1.0f), glm::vec3(x, 1.2f, 0.0f))); + x += 1.1f; + roughness += 0.15f; + m_DielectricSphereMaterialInstances.push_back(mi); + } // Create Quad - float x = -1, y = -1; + x = -1; + float y = -1; float width = 2, height = 2; struct QuadVertex { @@ -77,7 +186,7 @@ void DemoLayer::OnAttach() uint32_t* indices = new uint32_t[6] { 0, 1, 2, 2, 3, 0, }; m_IndexBuffer.reset(Prism::IndexBuffer::Create()); - m_IndexBuffer->SetData(indices, 6 * sizeof(unsigned int)); + m_IndexBuffer->SetData(indices, 6 * sizeof(uint32_t)); m_Light.Direction = { -0.5f, -0.5f, 1.0f }; m_Light.Radiance = { 1.0f, 1.0f, 1.0f }; @@ -87,7 +196,7 @@ void DemoLayer::OnDetach() { } -void DemoLayer::OnUpdate(Prism::TimeStep deltaTime) +void DemoLayer::OnUpdate(const Prism::TimeStep deltaTime) { { // THINGS TO LOOK AT: @@ -108,12 +217,31 @@ void DemoLayer::OnUpdate(Prism::TimeStep deltaTime) m_QuadShader->UploadUniformBuffer(quadShaderUB); m_QuadShader->Bind(); - // m_EnvironmentIrradiance->Bind(0); + m_QuadShader->SetMat4("u_InverseVP", inverse(viewProjection)); m_EnvironmentCubeMap->Bind(0); m_VertexBuffer->Bind(); m_IndexBuffer->Bind(); Renderer::DrawIndexed(m_IndexBuffer->GetCount(), false); + + m_PBRMaterial->Set("u_AlbedoColor", m_AlbedoInput.Color); + m_PBRMaterial->Set("u_Metalness", m_MetalnessInput.Value); + m_PBRMaterial->Set("u_Roughness", m_RoughnessInput.Value); + m_PBRMaterial->Set("u_ViewProjectionMatrix", viewProjection); + m_PBRMaterial->Set("u_ModelMatrix", scale(mat4(1.0f), vec3(m_MeshScale))); + m_PBRMaterial->Set("lights", m_Light); + m_PBRMaterial->Set("u_CameraPosition", m_Camera.GetPosition()); + m_PBRMaterial->Set("u_RadiancePrefilter", m_RadiancePrefilter ? 1.0f : 0.0f); + m_PBRMaterial->Set("u_AlbedoTexToggle", m_AlbedoInput.UseTexture ? 1.0f : 0.0f); + m_PBRMaterial->Set("u_NormalTexToggle", m_NormalInput.UseTexture ? 1.0f : 0.0f); + m_PBRMaterial->Set("u_MetalnessTexToggle", m_MetalnessInput.UseTexture ? 1.0f : 0.0f); + m_PBRMaterial->Set("u_RoughnessTexToggle", m_RoughnessInput.UseTexture ? 1.0f : 0.0f); + m_PBRMaterial->Set("u_EnvMapRotation", m_EnvMapRotation); + + m_PBRMaterial->Set("u_EnvRadianceTex", m_EnvironmentCubeMap); + m_PBRMaterial->Set("u_EnvIrradianceTex", m_EnvironmentIrradiance); + m_PBRMaterial->Set("u_BRDFLUTTexture", m_BRDFLUT); + /* Prism::UniformBufferDeclaration simplePbrShaderUB; simplePbrShaderUB.Push("u_ViewProjectionMatrix", viewProjection); simplePbrShaderUB.Push("u_ModelMatrix", mat4(1.0f)); @@ -134,53 +262,52 @@ void DemoLayer::OnUpdate(Prism::TimeStep deltaTime) m_EnvironmentCubeMap->Bind(10); m_EnvironmentIrradiance->Bind(11); m_BRDFLUT->Bind(15); - m_SimplePBRShader->Bind(); + */ + + if (m_AlbedoInput.TextureMap) - m_AlbedoInput.TextureMap->Bind(1); + m_PBRMaterial->Set("u_AlbedoTexture", m_AlbedoInput.TextureMap); if (m_NormalInput.TextureMap) - m_NormalInput.TextureMap->Bind(2); + m_PBRMaterial->Set("u_NormalTexture", m_NormalInput.TextureMap); if (m_MetalnessInput.TextureMap) - m_MetalnessInput.TextureMap->Bind(3); + m_PBRMaterial->Set("u_MetalnessTexture", m_MetalnessInput.TextureMap); if (m_RoughnessInput.TextureMap) - m_RoughnessInput.TextureMap->Bind(4); + m_PBRMaterial->Set("u_RoughnessTexture", m_RoughnessInput.TextureMap); if (m_Scene == Scene::Spheres) { // Metals - float roughness = 0.0f; - float x = -88.0f; for (int i = 0; i < 8; i++) { - m_SimplePBRShader->SetMat4("u_ModelMatrix", translate(mat4(1.0f), vec3(x, 0.0f, 0.0f))); - m_SimplePBRShader->SetFloat("u_Roughness", roughness); - m_SimplePBRShader->SetFloat("u_Metalness", 1.0f); - m_SphereMesh->Render(); - roughness += 0.15f; - x += 22.0f; + m_MetalSphereMaterialInstances[i]->Bind(); + m_SphereMesh->Render(deltaTime, m_SimplePBRShader.get()); } // Dielectrics - roughness = 0.0f; - x = -88.0f; for (int i = 0; i < 8; i++) { - m_SimplePBRShader->SetMat4("u_ModelMatrix", translate(mat4(1.0f), vec3(x, 22.0f, 0.0f))); - m_SimplePBRShader->SetFloat("u_Roughness", roughness); - m_SimplePBRShader->SetFloat("u_Metalness", 0.0f); - m_SphereMesh->Render(); - - roughness += 0.15f; - x += 22.0f; + m_DielectricSphereMaterialInstances[i]->Bind(); + m_SphereMesh->Render(deltaTime, m_SimplePBRShader.get()); } } else if (m_Scene == Scene::Model) { - m_Mesh->Render(); + if (m_Mesh) + { + m_PBRMaterial->Bind(); + m_Mesh->Render(deltaTime, m_SimplePBRShader.get()); + } } + m_GridShader->Bind(); + m_GridShader->SetMat4("u_MVP", viewProjection * glm::scale(glm::mat4(1.0f), glm::vec3(16.0f))); + m_GridShader->SetFloat("u_Scale", m_GridScale); + m_GridShader->SetFloat("u_Res", m_GridSize); + m_PlaneMesh->Render(deltaTime, m_GridShader.get()); + m_Framebuffer->Unbind(); m_FinalPresentBuffer->Bind(); @@ -311,35 +438,27 @@ void DemoLayer::OnImGuiRender() #endif // Editor Panel ------------------------------------------------------------------------------ - ImGui::Begin("Settings"); - if (ImGui::TreeNode("Shaders")) - { - auto& shaders = Prism::Shader::s_AllShaders; - for (auto& shader : shaders) - { - if (ImGui::TreeNode(shader->GetName().c_str())) - { - std::string buttonName = "Reload##" + shader->GetName(); - if (ImGui::Button(buttonName.c_str())) - shader->Reload(); - ImGui::TreePop(); - } - } - ImGui::TreePop(); - } - + ImGui::Begin("Model"); ImGui::RadioButton("Spheres", (int*)&m_Scene, (int)Scene::Spheres); ImGui::SameLine(); ImGui::RadioButton("Model", (int*)&m_Scene, (int)Scene::Model); - ImGui::ColorEdit4("Clear Color", m_ClearColor); + ImGui::Begin("Environment"); - ImGui::SliderFloat3("Light Dir", glm::value_ptr(m_Light.Direction), -1, 1); - ImGui::ColorEdit3("Light Radiance", glm::value_ptr(m_Light.Radiance)); - ImGui::SliderFloat("Light Multiplier", &m_LightMultiplier, 0.0f, 5.0f); - ImGui::SliderFloat("Exposure", &m_Exposure, 0.0f, 10.0f); - auto cameraForward = m_Camera.GetForwardDirection(); - ImGui::Text("Camera Forward: %.2f, %.2f, %.2f", cameraForward.x, cameraForward.y, cameraForward.z); + ImGui::Columns(2); + ImGui::AlignTextToFramePadding(); + + Property("Light Direction", m_Light.Direction); + Property("Light Radiance", m_Light.Radiance, PropertyFlag::ColorProperty); + Property("Light Multiplier", m_LightMultiplier, 0.0f, 5.0f); + Property("Exposure", m_Exposure, 0.0f, 5.0f); + + Property("Radiance Prefiltering", m_RadiancePrefilter); + Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f); + + ImGui::Columns(1); + + ImGui::End(); ImGui::Separator(); { @@ -357,12 +476,6 @@ void DemoLayer::OnImGuiRender() } ImGui::Separator(); - ImGui::Text("Shader Parameters"); - ImGui::Checkbox("Radiance Prefiltering", &m_RadiancePrefilter); - ImGui::SliderFloat("Env Map Rotation", &m_EnvMapRotation, -360.0f, 360.0f); - - ImGui::Separator(); - // Textures ------------------------------------------------------------------------------ { // Albedo @@ -494,6 +607,22 @@ void DemoLayer::OnImGuiRender() } } + if (ImGui::TreeNode("Shaders")) + { + auto& shaders = Prism::Shader::GetAllShaders(); + for (auto& shader : shaders) + { + if (ImGui::TreeNode(shader->GetName().c_str())) + { + std::string buttonName = "Reload##" + shader->GetName(); + if (ImGui::Button(buttonName.c_str())) + shader->Reload(); + ImGui::TreePop(); + } + } + ImGui::TreePop(); + } + ImGui::Separator(); ImGui::End(); @@ -509,7 +638,8 @@ void DemoLayer::OnImGuiRender() ImGui::End(); ImGui::PopStyleVar(); - + if (m_Mesh) + m_Mesh->OnImGuiRender(); } diff --git a/Sandbox/Sandbox/Layer/DemoLayer.h b/Sandbox/Sandbox/Layer/DemoLayer.h index 053b7fb..44f586e 100644 --- a/Sandbox/Sandbox/Layer/DemoLayer.h +++ b/Sandbox/Sandbox/Layer/DemoLayer.h @@ -8,6 +8,7 @@ #include "Prism.h" #include "Prism/Renderer/Camera.h" #include "Prism/Renderer/FrameBuffer.h" +#include "Prism/Renderer/Material.h" #include "Prism/Renderer/Mesh.h" class DemoLayer : public Prism::Layer @@ -25,19 +26,28 @@ public: private: float m_ClearColor[4]; - std::unique_ptr m_Shader; - std::unique_ptr m_PBRShader; - std::unique_ptr m_SimplePBRShader; - std::unique_ptr m_QuadShader; - std::unique_ptr m_HDRShader; - std::unique_ptr m_Mesh; - std::unique_ptr m_SphereMesh; - std::unique_ptr m_BRDFLUT; + Prism::Ref m_SimplePBRShader; + Prism::Scope m_QuadShader; + Prism::Scope m_HDRShader; + Prism::Scope m_GridShader; + Prism::Scope m_Mesh; + Prism::Scope m_SphereMesh, m_PlaneMesh; + Prism::Ref m_BRDFLUT; + + Prism::Ref m_PBRMaterial; + std::vector> m_MetalSphereMaterialInstances; + std::vector> m_DielectricSphereMaterialInstances; + + float m_GridScale = 16.025f, m_GridSize = 0.025f; + float m_MeshScale = 1.0f; + + Prism::Ref m_Shader; + Prism::Ref m_PBRShader; struct AlbedoInput { glm::vec3 Color = { 0.972f, 0.96f, 0.915f }; // Silver, from https://docs.unrealengine.com/en-us/Engine/Rendering/Materials/PhysicallyBased - std::unique_ptr TextureMap; + Prism::Ref TextureMap; bool SRGB = true; bool UseTexture = false; }; @@ -45,7 +55,7 @@ private: struct NormalInput { - std::unique_ptr TextureMap; + Prism::Ref TextureMap; bool UseTexture = false; }; NormalInput m_NormalInput; @@ -53,7 +63,7 @@ private: struct MetalnessInput { float Value = 1.0f; - std::unique_ptr TextureMap; + Prism::Ref TextureMap; bool UseTexture = false; }; MetalnessInput m_MetalnessInput; @@ -61,16 +71,16 @@ private: struct RoughnessInput { float Value = 0.5f; - std::unique_ptr TextureMap; + Prism::Ref TextureMap; bool UseTexture = false; }; RoughnessInput m_RoughnessInput; - std::unique_ptr m_Framebuffer, m_FinalPresentBuffer; + Prism::Ref m_Framebuffer, m_FinalPresentBuffer; - std::unique_ptr m_VertexBuffer; - std::unique_ptr m_IndexBuffer; - std::unique_ptr m_EnvironmentCubeMap, m_EnvironmentIrradiance; + Prism::Ref m_VertexBuffer; + Prism::Ref m_IndexBuffer; + Prism::Ref m_EnvironmentCubeMap, m_EnvironmentIrradiance; Prism::Camera m_Camera; @@ -96,7 +106,7 @@ private: Scene m_Scene; // Editor resources - std::unique_ptr m_CheckerboardTex; + Prism::Ref m_CheckerboardTex; }; diff --git a/Sandbox/Sandbox/Layer/TestLayer.cpp b/Sandbox/Sandbox/Layer/TestLayer.cpp index cf82820..6f1ce03 100644 --- a/Sandbox/Sandbox/Layer/TestLayer.cpp +++ b/Sandbox/Sandbox/Layer/TestLayer.cpp @@ -208,7 +208,7 @@ void TestLayer::OnUpdate(Prism::TimeStep deltaTime) m_Shader->Bind(); - m_Mesh->Render(); + m_Mesh->Render(deltaTime, m_Shader.get()); // m_VertexBuffer->Bind(); // m_IndexBuffer->Bind(); diff --git a/Sandbox/assets/models/Plane1m.fbx b/Sandbox/assets/models/Plane1m.fbx new file mode 100644 index 0000000..d64d6d5 Binary files /dev/null and b/Sandbox/assets/models/Plane1m.fbx differ diff --git a/Sandbox/assets/models/Plane1m.obj b/Sandbox/assets/models/Plane1m.obj new file mode 100644 index 0000000..1d8b8dc --- /dev/null +++ b/Sandbox/assets/models/Plane1m.obj @@ -0,0 +1,17 @@ +# Blender v2.80 (sub 75) OBJ File: 'Plane1m.blend' +# www.blender.org +mtllib Plane1m.mtl +o Plane +v -0.500000 0.000000 0.500000 +v 0.500000 0.000000 0.500000 +v -0.500000 0.000000 -0.500000 +v 0.500000 0.000000 -0.500000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 1.000000 +vn 0.0000 1.0000 0.0000 +usemtl None +s off +f 2/1/1 3/2/1 1/3/1 +f 2/1/1 4/4/1 3/2/1 diff --git a/Sandbox/assets/models/Sphere1m.fbx b/Sandbox/assets/models/Sphere1m.fbx new file mode 100644 index 0000000..478666f Binary files /dev/null and b/Sandbox/assets/models/Sphere1m.fbx differ diff --git a/Sandbox/assets/models/SphereOld.fbx b/Sandbox/assets/models/SphereOld.fbx deleted file mode 100644 index 5e1d3f6..0000000 Binary files a/Sandbox/assets/models/SphereOld.fbx and /dev/null differ diff --git a/Sandbox/assets/models/m1911/m1911.fbx b/Sandbox/assets/models/m1911/m1911.fbx new file mode 100644 index 0000000..8a9b13a Binary files /dev/null and b/Sandbox/assets/models/m1911/m1911.fbx differ diff --git a/Sandbox/assets/models/m1911/m1911_color.png b/Sandbox/assets/models/m1911/m1911_color.png new file mode 100644 index 0000000..4ee584e Binary files /dev/null and b/Sandbox/assets/models/m1911/m1911_color.png differ diff --git a/Sandbox/assets/models/m1911/m1911_metalness.png b/Sandbox/assets/models/m1911/m1911_metalness.png new file mode 100644 index 0000000..a738215 Binary files /dev/null and b/Sandbox/assets/models/m1911/m1911_metalness.png differ diff --git a/Sandbox/assets/models/m1911/m1911_normal.png b/Sandbox/assets/models/m1911/m1911_normal.png new file mode 100644 index 0000000..1e79b34 Binary files /dev/null and b/Sandbox/assets/models/m1911/m1911_normal.png differ diff --git a/Sandbox/assets/models/m1911/m1911_roughness.png b/Sandbox/assets/models/m1911/m1911_roughness.png new file mode 100644 index 0000000..5faef98 Binary files /dev/null and b/Sandbox/assets/models/m1911/m1911_roughness.png differ diff --git a/Sandbox/assets/shaders/Grid.glsl b/Sandbox/assets/shaders/Grid.glsl new file mode 100644 index 0000000..fa2ced0 --- /dev/null +++ b/Sandbox/assets/shaders/Grid.glsl @@ -0,0 +1,50 @@ +// Simple Texture Shader + +#type vertex +#version 430 + +layout(location = 0) in vec3 a_Position; +layout(location = 4) in vec2 a_TexCoord; + +uniform mat4 u_MVP; + +out vec2 v_TexCoord; + +void main() +{ + vec4 position = u_MVP * vec4(a_Position, 1.0); + gl_Position = position; + + v_TexCoord = a_TexCoord; +} + +#type fragment +#version 430 + +layout(location = 0) out vec4 color; + +uniform sampler2D u_Texture; +uniform float u_Scale; +uniform float u_Res; + +in vec2 v_TexCoord; + +/*void main() +{ + color = texture(u_Texture, v_TexCoord * 8.0); +}*/ + +float grid(vec2 st, float res) +{ + vec2 grid = fract(st); + return step(res, grid.x) * step(res, grid.y); +} + +void main() +{ + float scale = u_Scale; + float resolution = u_Res; + + float x = grid(v_TexCoord * scale, resolution); + color = vec4(vec3(0.2), 0.5) * (1.0 - x); +} diff --git a/Sandbox/assets/shaders/simplepbr.glsl b/Sandbox/assets/shaders/simplepbr.glsl index 3713d52..b4add84 100644 --- a/Sandbox/assets/shaders/simplepbr.glsl +++ b/Sandbox/assets/shaders/simplepbr.glsl @@ -18,25 +18,41 @@ layout(location = 2) in vec3 a_Tangent; layout(location = 3) in vec3 a_Binormal; layout(location = 4) in vec2 a_TexCoord; +layout(location = 5) in ivec4 a_BoneIndices; +layout(location = 6) in vec4 a_BoneWeights; + uniform mat4 u_ViewProjectionMatrix; uniform mat4 u_ModelMatrix; +const int MAX_BONES = 100; +uniform mat4 u_BoneTransforms[100]; + out VertexOutput { vec3 WorldPosition; vec3 Normal; vec2 TexCoord; mat3 WorldNormals; + vec3 Binormal; } vs_Output; void main() { - vs_Output.WorldPosition = vec3(mat4(u_ModelMatrix) * vec4(a_Position, 1.0)); - vs_Output.Normal = a_Normal; + mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0]; + boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1]; + boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2]; + boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3]; + + vec4 localPosition = boneTransform * vec4(a_Position, 1.0); + + vs_Output.WorldPosition = vec3(u_ModelMatrix * boneTransform * vec4(a_Position, 1.0)); + vs_Output.Normal = mat3(boneTransform) * a_Normal; vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y); vs_Output.WorldNormals = mat3(u_ModelMatrix) * mat3(a_Tangent, a_Binormal, a_Normal); + vs_Output.Binormal = mat3(boneTransform) * a_Binormal; - gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0); + //gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0); + gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * localPosition; } #type fragment @@ -62,6 +78,7 @@ in VertexOutput vec3 Normal; vec2 TexCoord; mat3 WorldNormals; + vec3 Binormal; } vs_Input; layout(location=0) out vec4 color;