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;
const char* typeToken = "#type";
size_t typeTokenLength = strlen(typeToken);
size_t pos = m_ShaderSource.find(typeToken, 0);
size_t pos = source.find(typeToken, 0);
while (pos != std::string::npos)
{
size_t eol = m_ShaderSource.find("\r\n", pos);
size_t eol = source.find("\r\n", pos);
if (eol == std::string::npos) {
eol = m_ShaderSource.find('\n', pos);
eol = source.find('\n', pos);
if (eol == std::string::npos) {
eol = m_ShaderSource.find('\r', pos);
eol = source.find('\r', pos);
}
}
PM_CORE_ASSERT(eol != std::string::npos, "Syntax error");
size_t begin = pos + typeTokenLength + 1;
std::string type = m_ShaderSource.substr(begin, eol - begin);
std::string type = source.substr(begin, eol - begin);
PM_CORE_ASSERT(type == "vertex" || type == "fragment" || type == "pixel", "Invalid shader type");
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));
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, "["))
{
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);
}
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));
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,21 +95,38 @@ 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];
m_InverseTransform = glm::inverse(aiMatrix4x4ToGlm(scene->mRootNode->mTransformation));
uint32_t vertexCount = 0;
uint32_t indexCount = 0;
m_Submeshes.reserve(scene->mNumMeshes);
for (size_t m = 0; m < scene->mNumMeshes; m++)
{
aiMesh* mesh = scene->mMeshes[m];
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.");
m_Vertices.reserve(mesh->mNumVertices);
// Extract vertices from model
for (size_t i = 0; i < m_Vertices.capacity(); i++)
// Vertices
for (size_t i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
vertex.Position = { mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z };
@ -84,31 +143,86 @@ namespace Prism
m_Vertices.push_back(vertex);
}
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++)
// 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(), 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)
{
if (m_AnimationPlaying && m_Scene->mAnimations)
{
m_WorldTime += deltaTime;
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);
}
if (m_Scene->mAnimations)
BoneTransform(m_AnimationTime);
// TODO: Sort this out
m_VertexBuffer->Bind();
m_IndexBuffer->Bind();
PM_RENDER_S({
// 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));
@ -123,7 +237,223 @@ namespace Prism
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);
}
});
Renderer::DrawIndexed(m_IndexBuffer->GetCount());
}
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,12 +8,21 @@
#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
{
class PRISM_API Mesh
{
public:
struct Vertex
{
glm::vec3 Position;
@ -21,8 +30,13 @@ namespace Prism
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_assert(sizeof(Vertex) == 14 * sizeof(float));
static const int NumAttributes = 5;
struct Index
@ -31,19 +45,85 @@ namespace Prism
};
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:
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;
};

View File

@ -7,8 +7,86 @@
#include "Prism/Renderer/Renderer.h"
#include "Prism/Renderer/Shader.h"
static void ImGuiShowHelpMarker(const char* desc)
{
namespace {
enum class PropertyFlag
{
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())
{
@ -18,10 +96,11 @@ static void ImGuiShowHelpMarker(const char* 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<Prism::MaterialInstance> 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<Prism::MaterialInstance> 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<sizeof(mat4) * 2 + sizeof(vec3) * 4 + sizeof(float) * 8, 14> simplePbrShaderUB;
simplePbrShaderUB.Push("u_ViewProjectionMatrix", viewProjection);
simplePbrShaderUB.Push("u_ModelMatrix", mat4(1.0f));
@ -134,52 +262,51 @@ 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();
@ -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();
}

View File

@ -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<Prism::Shader> m_Shader;
std::unique_ptr<Prism::Shader> m_PBRShader;
std::unique_ptr<Prism::Shader> m_SimplePBRShader;
std::unique_ptr<Prism::Shader> m_QuadShader;
std::unique_ptr<Prism::Shader> m_HDRShader;
std::unique_ptr<Prism::Mesh> m_Mesh;
std::unique_ptr<Prism::Mesh> m_SphereMesh;
std::unique_ptr<Prism::Texture2D> m_BRDFLUT;
Prism::Ref<Prism::Shader> m_SimplePBRShader;
Prism::Scope<Prism::Shader> m_QuadShader;
Prism::Scope<Prism::Shader> m_HDRShader;
Prism::Scope<Prism::Shader> m_GridShader;
Prism::Scope<Prism::Mesh> m_Mesh;
Prism::Scope<Prism::Mesh> m_SphereMesh, m_PlaneMesh;
Prism::Ref<Prism::Texture2D> m_BRDFLUT;
Prism::Ref<Prism::Material> m_PBRMaterial;
std::vector<Prism::Ref<Prism::MaterialInstance>> m_MetalSphereMaterialInstances;
std::vector<Prism::Ref<Prism::MaterialInstance>> m_DielectricSphereMaterialInstances;
float m_GridScale = 16.025f, m_GridSize = 0.025f;
float m_MeshScale = 1.0f;
Prism::Ref<Prism::Shader> m_Shader;
Prism::Ref<Prism::Shader> 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<Prism::Texture2D> TextureMap;
Prism::Ref<Prism::Texture2D> TextureMap;
bool SRGB = true;
bool UseTexture = false;
};
@ -45,7 +55,7 @@ private:
struct NormalInput
{
std::unique_ptr<Prism::Texture2D> TextureMap;
Prism::Ref<Prism::Texture2D> TextureMap;
bool UseTexture = false;
};
NormalInput m_NormalInput;
@ -53,7 +63,7 @@ private:
struct MetalnessInput
{
float Value = 1.0f;
std::unique_ptr<Prism::Texture2D> TextureMap;
Prism::Ref<Prism::Texture2D> TextureMap;
bool UseTexture = false;
};
MetalnessInput m_MetalnessInput;
@ -61,16 +71,16 @@ private:
struct RoughnessInput
{
float Value = 0.5f;
std::unique_ptr<Prism::Texture2D> TextureMap;
Prism::Ref<Prism::Texture2D> TextureMap;
bool UseTexture = false;
};
RoughnessInput m_RoughnessInput;
std::unique_ptr<Prism::FrameBuffer> m_Framebuffer, m_FinalPresentBuffer;
Prism::Ref<Prism::FrameBuffer> m_Framebuffer, m_FinalPresentBuffer;
std::unique_ptr<Prism::VertexBuffer> m_VertexBuffer;
std::unique_ptr<Prism::IndexBuffer> m_IndexBuffer;
std::unique_ptr<Prism::TextureCube> m_EnvironmentCubeMap, m_EnvironmentIrradiance;
Prism::Ref<Prism::VertexBuffer> m_VertexBuffer;
Prism::Ref<Prism::IndexBuffer> m_IndexBuffer;
Prism::Ref<Prism::TextureCube> m_EnvironmentCubeMap, m_EnvironmentIrradiance;
Prism::Camera m_Camera;
@ -96,7 +106,7 @@ private:
Scene m_Scene;
// Editor resources
std::unique_ptr<Prism::Texture2D> m_CheckerboardTex;
Prism::Ref<Prism::Texture2D> m_CheckerboardTex;
};

View File

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

Binary file not shown.

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

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

View File

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