add support for skeletal animation

This commit is contained in:
2025-11-28 00:01:42 +08:00
parent 56d01b9c34
commit 018a4cb2c6
32 changed files with 2300 additions and 287 deletions

View File

@ -12,10 +12,6 @@
#include "Prism/Renderer/FrameBuffer.h"
#include "Prism/Renderer/RendererAPI.h"
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h>
#include <Windows.h>
#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<const char*> filterPatterns;
std::vector<std::string> 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)

View File

@ -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, ...)

View File

@ -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<GLenum, std::string> 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<GLenum, std::string> shaderSources;
std::unordered_map<GLenum, std::string> 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<std::string> SplitString(const std::string& string, const std::string& delimiters)
{
size_t start = 0;
size_t end = string.find_first_of(delimiters);
std::vector<std::string> 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<std::string> SplitString(const std::string& string, const char delimiter)
{
return SplitString(string, std::string(1, delimiter));
}
std::vector<std::string> Tokenize(const std::string& string)
{
return SplitString(string, " \t\n");
}
std::vector<std::string> 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<std::string> 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<std::string> 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<GLuint> 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<OpenGLShaderUniformBufferDeclaration>& 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);
}
}

View File

@ -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<GLenum, std::string> 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<OpenGLShaderUniformBufferDeclaration>& 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<GLenum, std::string> m_ShaderSource;
std::vector<ShaderReloadedCallback> m_ShaderReloadedCallbacks;
ShaderUniformBufferList m_VSRendererUniformBuffers;
ShaderUniformBufferList m_PSRendererUniformBuffers;
Scope<OpenGLShaderUniformBufferDeclaration> m_VSMaterialUniformBuffer;
Scope<OpenGLShaderUniformBufferDeclaration> m_PSMaterialUniformBuffer;
ShaderResourceList m_Resources;
ShaderStructList m_Structs;
};
}

View File

@ -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";
}
}

View File

@ -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

View File

@ -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);
});
}

View File

@ -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;
};

View File

@ -12,7 +12,6 @@
#include "glm/gtx/quaternion.hpp"
#include "Prism/Core/Input.h"
#define M_PI 3.14159f
namespace Prism
{

View File

@ -0,0 +1,178 @@
//
// Created by sfd on 25-11-27.
//
#include "Material.h"
namespace Prism
{
Material::Material(const Ref<Shader>& 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>& 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);
}
}
}

View File

@ -0,0 +1,141 @@
//
// Created by sfd on 25-11-27.
//
#ifndef MATERIAL_H
#define MATERIAL_H
#include <unordered_set>
#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>& shader);
virtual ~Material();
void Bind() const;
template <typename T>
void Set(const std::string& name, const T& value);
void Set(const std::string& name, const Ref<Texture>& 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<Texture2D>& texture)
{
Set(name, (const Ref<Texture>&)texture);
}
void Set(const std::string& name, const Ref<TextureCube>& texture)
{
Set(name, (const Ref<Texture>&)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<Shader> m_Shader;
std::unordered_set<MaterialInstance*> m_MaterialInstances;
Buffer m_VSUniformStorageBuffer;
Buffer m_PSUniformStorageBuffer;
std::vector<Ref<Texture>> m_Textures;
int32_t m_RenderFlags = 0;
};
class PRISM_API MaterialInstance
{
friend class Material;
public:
MaterialInstance(const Ref<Material>& material);
virtual ~MaterialInstance();
template <typename T>
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>& 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<Texture2D>& texture)
{
Set(name, (const Ref<Texture>&)texture);
}
void Set(const std::string& name, const Ref<TextureCube>& texture)
{
Set(name, (const Ref<Texture>&)texture);
}
void Bind() const;
private:
void AllocateStorage();
void OnShaderReloaded();
Buffer& GetUniformBufferTarget(ShaderUniformDeclaration* uniformDeclaration);
void OnMaterialValueUpdated(ShaderUniformDeclaration* decl);
private:
Ref<Material> m_Material;
Buffer m_VSUniformStorageBuffer;
Buffer m_PSUniformStorageBuffer;
std::vector<Ref<Texture>> m_Textures;
// TODO: This is temporary; come up with a proper system to track overrides
std::unordered_set<std::string> m_OverriddenValues;
};
template <typename T>
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

View File

@ -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<Assimp::Importer>();
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 };
}
}

View File

@ -8,42 +8,122 @@
#include <glm/glm.hpp>
#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<Vertex> m_Vertices;
std::vector<Index> 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<Submesh> m_Submeshes;
std::unique_ptr<Assimp::Importer> m_Importer;
glm::mat4 m_InverseTransform;
uint32_t m_BoneCount = 0;
std::vector<BoneInfo> m_BoneInfo;
std::unique_ptr<VertexBuffer> m_VertexBuffer;
std::unique_ptr<IndexBuffer> m_IndexBuffer;
std::vector<Vertex> m_Vertices;
std::vector<Index> m_Indices;
std::unordered_map<std::string, uint32_t> m_BoneMapping;
std::vector<glm::mat4> 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;
};
}

View File

@ -9,7 +9,7 @@
namespace Prism
{
// std::vector<Shader*> Shader::s_AllShaders;
std::vector<Shader*> Shader::s_AllShaders;
Shader* Shader::Create(const std::string& filepath)
{
@ -25,4 +25,9 @@ namespace Prism
return result;
}
std::vector<Shader*>& Shader::GetAllShaders()
{
return s_AllShaders;
}
}

View File

@ -8,6 +8,8 @@
#include <glm/glm.hpp>
#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<void()>;
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<Shader*> 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<Shader*>& GetAllShaders();
private:
static std::vector<Shader*> s_AllShaders;
};
}

View File

@ -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<ShaderUniformDeclaration*> 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<ShaderUniformBufferDeclaration*> ShaderUniformBufferList;
class ShaderStruct
{
private:
friend class Shader;
private:
std::string m_Name;
std::vector<ShaderUniformDeclaration*> 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<ShaderUniformDeclaration*>& GetFields() const { return m_Fields; }
};
typedef std::vector<ShaderStruct*> 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<ShaderResourceDeclaration*> ShaderResourceList;
}
#endif //SHADERUNIFORM_H

View File

@ -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())

View File

@ -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;
};