GAME PROG

This commit is contained in:
2024-06-19 11:45:12 -05:00
parent fec8771c75
commit bba4a83240
85 changed files with 3687 additions and 27 deletions

View File

@ -0,0 +1,68 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "dawnopengl.hpp"
#include "error/assert.hpp"
#include "error/assertgl.hpp"
#include "BackBuffer.hpp"
using namespace Dawn;
BackBuffer::BackBuffer() {
}
float_t BackBuffer::getScale() {
return this->scale;
}
float_t BackBuffer::getWidth() {
return this->width;
}
float_t BackBuffer::getHeight() {
return this->height;
}
void BackBuffer::setSize(
const float_t width,
const float_t height
) {
if(this->width == width && this->height == height) return;
// Fixes a new bug that it seems GLFW has introduced.
this->width = width == 0 ? 1 : width;
this->height = height == 0 ? 1 : height;
onResize.emit(this->width, this->height);
}
void BackBuffer::setClearColor(const struct Color color) {
this->clearColor = color;
}
void BackBuffer::clear(const int32_t clearFlags) {
glClearColor(clearColor.r, clearColor.g, clearColor.b, 1.0f);
assertNoGLError();
GLbitfield mask = 0;
if((clearFlags & RENDER_TARGET_CLEAR_COLOR) == RENDER_TARGET_CLEAR_COLOR) {
mask |= GL_COLOR_BUFFER_BIT;
}
if((clearFlags & RENDER_TARGET_CLEAR_DEPTH) == RENDER_TARGET_CLEAR_DEPTH) {
mask |= GL_DEPTH_BUFFER_BIT;
}
glClear(mask);
assertNoGLError();
}
void BackBuffer::bind() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
assertNoGLError();
glViewport(0, 0, (GLsizei)this->width, (GLsizei)this->height);
assertNoGLError();
}

View File

@ -0,0 +1,44 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/Color.hpp"
#include "display/RenderTarget.hpp"
namespace Dawn {
class BackBuffer final : public RenderTarget {
private:
float_t width = 1;
float_t height = 1;
struct Color clearColor = COLOR_CORNFLOWER_BLUE;
public:
float_t scale = 1.0f;
/**
* Construct the back buffer render target.
*/
BackBuffer();
float_t getScale() override;
float_t getWidth() override;
float_t getHeight() override;
void setClearColor(const struct Color color) override;
void clear(const int32_t) override;
void bind() override;
/**
* Requests to modify the viewport directly. This is mostly to be called
* by whatever is setting the window/display resolution, so that the
* backbuffer can keep track of what the viewport size is. This should
* also be DPI aware, e.g. "4k @ 2xDPI, resulting in a 1080p equiv" should
* still call this method with 3840, 2160.
*
* @param width New width of the back buffer.
* @param height New height of the back buffer.
*/
void setSize(const float_t width, const float_t height);
};
}

View File

@ -0,0 +1,15 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
BackBuffer.cpp
Texture.cpp
)
# Subdirs
add_subdirectory(mesh)
add_subdirectory(shader)

View File

@ -0,0 +1,213 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "error/assert.hpp"
#include "error/assertgl.hpp"
#include "Texture.hpp"
using namespace Dawn;
void Texture::bind(const uint8_t slot) {
assertTrue(this->id != -1, "Texture is not ready!");
glActiveTexture(GL_TEXTURE0 + slot);
assertNoGLError();
glBindTexture(GL_TEXTURE_2D, this->id);
assertNoGLError();
this->updateTextureProperties();
}
int32_t Texture::getWidth() {
return this->width;
}
int32_t Texture::getHeight() {
return this->height;
}
bool_t Texture::isReady() {
return this->id != -1;
}
void Texture::setSize(
const int32_t width,
const int32_t height,
const enum TextureFormat format,
const enum TextureDataFormat dataFormat
) {
if(this->id != -1) {
glDeleteTextures(1, &this->id);
assertNoGLError();
this->id = -1;
}
int32_t maxSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
assertTrue(width > 0 && width <= maxSize, "Width is out of bounds!");
assertTrue(height > 0 && height <= maxSize, "Height is out of bounds!");
this->width = width;
this->height = height;
this->format = format;
this->dataFormat = dataFormat;
glGenTextures(1, &this->id);
assertNoGLError();
if(this->id <= 0) assertUnreachable("Texture generation failed!");
// Initialize the texture to blank
glActiveTexture(GL_TEXTURE0);
assertNoGLError();
this->bufferRaw(NULL);
}
// bool_t Texture::isReady() {
// return this->id != -1;
// }
void Texture::updateTextureProperties() {
auto setWrapMode = [](GLenum axis, enum TextureWrapMode wm) {
switch(wm) {
case TextureWrapMode::REPEAT:
glTexParameteri(GL_TEXTURE_2D, axis, GL_REPEAT);
break;
case TextureWrapMode::MIRRORED_REPEAT:
glTexParameteri(GL_TEXTURE_2D, axis, GL_MIRRORED_REPEAT);
break;
case TextureWrapMode::CLAMP_TO_EDGE:
glTexParameteri(GL_TEXTURE_2D, axis, GL_CLAMP_TO_EDGE);
break;
case TextureWrapMode::CLAMP_TO_BORDER:
glTexParameteri(GL_TEXTURE_2D, axis, GL_CLAMP_TO_BORDER);
break;
default:
assertUnreachable("Unknown wrap mode!");
}
assertNoGLError();
};
setWrapMode(GL_TEXTURE_WRAP_S, this->wrapModeX);
setWrapMode(GL_TEXTURE_WRAP_T, this->wrapModeY);
auto setFilterMode = [](
GLenum minMag,
enum TextureFilterMode filter,
enum TextureFilterMode mapFilterMode
) {
switch(filter) {
case TextureFilterMode::NEAREST: {
glTexParameteri(GL_TEXTURE_2D, minMag, GL_NEAREST);
break;
}
case TextureFilterMode::LINEAR: {
glTexParameteri(GL_TEXTURE_2D, minMag, GL_LINEAR);
break;
}
default: {
assertUnreachable("Unknown filter mode!");
}
}
assertNoGLError();
};
setFilterMode(
GL_TEXTURE_MIN_FILTER, this->filterModeMin, this->mipMapFilterModeMin
);
setFilterMode(
GL_TEXTURE_MAG_FILTER, this->filterModeMag, this->mipMapFilterModeMag
);
}
void Texture::bufferRaw(const void *data) {
assertTrue(this->id != -1, "Texture is not ready!");
GLenum format;
switch(this->format) {
case TextureFormat::R:
format = GL_RED;
break;
case TextureFormat::RG:
format = GL_RG;
break;
case TextureFormat::RGB:
format = GL_RGB;
break;
case TextureFormat::RGBA:
format = GL_RGBA;
break;
default:
assertUnreachable("Unknown texture format!");
}
GLenum dataFormat;
switch(this->dataFormat) {
case TextureDataFormat::UNSIGNED_BYTE:
dataFormat = GL_UNSIGNED_BYTE;
break;
case TextureDataFormat::FLOAT:
dataFormat = GL_FLOAT;
break;
default:
assertUnreachable("Unknown texture data format!");
}
glBindTexture(GL_TEXTURE_2D, this->id);
assertNoGLError();
glTexImage2D(
GL_TEXTURE_2D, 0, format,
this->width, this->height,
0, format, dataFormat, data
);
assertNoGLError();
glGenerateMipmap(GL_TEXTURE_2D);
assertNoGLError();
}
void Texture::buffer(const struct ColorU8 pixels[]) {
assertTrue(
this->dataFormat == TextureDataFormat::UNSIGNED_BYTE,
"Texture data format must be unsigned byte!"
);
this->bufferRaw((void*)pixels);
}
void Texture::buffer(const struct Color pixels[]) {
std::cout << "Correct buffer" << std::endl;
assertTrue(
this->dataFormat == TextureDataFormat::FLOAT,
"Texture data format must be float!"
);
assertTrue(
this->format == TextureFormat::RGBA,
"Texture format must be RGBA!"
);
this->bufferRaw((void*)pixels);
}
void Texture::buffer(const uint8_t pixels[]) {
assertTrue(
this->dataFormat == TextureDataFormat::UNSIGNED_BYTE,
"Texture data format must be unsigned byte!"
);
this->bufferRaw((void*)pixels);
}
Texture::~Texture() {
if(this->id != -1) {
glDeleteTextures(1, &this->id);
assertNoGLError();
}
}

View File

@ -0,0 +1,43 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnopengl.hpp"
#include "display/ITexture.hpp"
namespace Dawn {
class TextureRenderTarget;
typedef GLuint textureslot_t;
class Texture : public ITexture {
private:
int32_t width = -1;
int32_t height = -1;
GLuint id = -1;
enum TextureFormat format;
enum TextureDataFormat dataFormat;
void updateTextureProperties();
void bufferRaw(const void *data);
public:
int32_t getWidth() override;
int32_t getHeight() override;
void setSize(
const int32_t width,
const int32_t height,
const enum TextureFormat format,
const enum TextureDataFormat dataForat
) override;
bool_t isReady() override;
void buffer(const struct ColorU8 pixels[]) override;
void buffer(const struct Color pixels[]);
void buffer(const uint8_t pixels[]) override;
void bind(const uint8_t slot) override;
~Texture();
};
}

View File

@ -0,0 +1,10 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Mesh.cpp
)

View File

@ -0,0 +1,257 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "error/assert.hpp"
#include "error/assertgl.hpp"
#include "display/mesh/Mesh.hpp"
using namespace Dawn;
void Mesh::createBuffers(
const int32_t verticeCount,
const int32_t indiceCount
) {
assertTrue(verticeCount > 0, "Vertice count must be greater than zero.");
assertTrue(indiceCount > 0, "Indice count must be greater than zero.");
// Can we re-use the buffers?
if(
verticeCount <= this->verticeCount &&
indiceCount <= this->indiceCount
) return;
this->disposeBuffers();
this->verticeCount = verticeCount;
this->indiceCount = indiceCount;
auto sizePos = sizeof(glm::vec3) * verticeCount;
auto sizeInds = sizeof(int32_t) * indiceCount;
auto sizeCoords = sizeof(glm::vec2) * verticeCount;
// Generate vertex array, I don't think I need to do this tbh.
glGenVertexArrays(1, &this->vertexArray);
assertNoGLError();
glBindVertexArray(this->vertexArray);
assertNoGLError();
// Create some buffers, one for the vertex data, one for the indices
GLuint buffer[2];
glGenBuffers(2, buffer);
assertNoGLError();
this->vertexBuffer = buffer[0];
if(this->vertexBuffer < 0) assertUnreachable("Can't make vertex buffer");
this->indexBuffer = buffer[1];
if(this->indexBuffer < 0) assertUnreachable("Can't make index buffer");
// Buffer an empty set of data then buffer each component
glBindBuffer(GL_ARRAY_BUFFER, this->vertexBuffer);
assertNoGLError();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->indexBuffer);
assertNoGLError();
glBufferData(GL_ARRAY_BUFFER, sizePos+sizeCoords, 0, GL_DYNAMIC_DRAW);
assertNoGLError();
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeInds, 0, GL_DYNAMIC_DRAW);
assertNoGLError();
// Setup the attrib pointers
size_t offset = 0;
glVertexAttribPointer(
0, sizeof(glm::vec3) / sizeof(float_t),
GL_FLOAT, GL_FALSE,
0, (void *)offset
);
assertNoGLError();
glEnableVertexAttribArray(0);
assertNoGLError();
offset += sizePos;
glVertexAttribPointer(
1, sizeof(glm::vec2) / sizeof(float_t),
GL_FLOAT, GL_FALSE,
0, (void *)offset
);
assertNoGLError();
glEnableVertexAttribArray(1);
assertNoGLError();
}
void Mesh::disposeBuffers() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
assertNoGLError();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
assertNoGLError();
if(this->vertexBuffer != -1) {
glDeleteBuffers(1, &this->vertexBuffer);
assertNoGLError();
this->vertexBuffer = -1;
this->verticeCount = -1;
}
if(this->indexBuffer != -1) {
glDeleteBuffers(1, &this->indexBuffer);
assertNoGLError();
this->indexBuffer = -1;
this->indiceCount = -1;
}
if(this->vertexArray) {
glDeleteVertexArrays(1, &this->vertexArray);
assertNoGLError();
this->vertexArray = -1;
}
}
void Mesh::bufferPositions(
const int32_t pos,
const glm::vec3 positions[],
const int32_t len
) {
assertNotNull(positions, "Positions cannot be null");
assertTrue(pos >= 0 && pos < verticeCount, "Position must be within range");
assertTrue(pos+len <= verticeCount, "Position + Length must be within range");
assertTrue(len > 0, "Length must be greater than zero");
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
assertNoGLError();
glBufferSubData(
GL_ARRAY_BUFFER,
sizeof(glm::vec3) * pos,
sizeof(glm::vec3) * len,
(void*)positions
);
assertNoGLError();
}
void Mesh::bufferCoordinates(
const int32_t pos,
const glm::vec2 coordinates[],
const int32_t len
) {
assertNotNull(coordinates, "Coordinates cannot be null");
assertTrue(pos >= 0 && pos < verticeCount, "Position must be within range");
assertTrue(pos+len <= verticeCount, "Position + Length must be within range");
assertTrue(len > 0, "Length must be greater than zero");
auto offsetCoordinates = (
(sizeof(glm::vec3) * this->verticeCount) +
(sizeof(glm::vec2) * pos)
);
glBindBuffer(GL_ARRAY_BUFFER, this->vertexBuffer);
assertNoGLError();
glBufferSubData(
GL_ARRAY_BUFFER,
offsetCoordinates,
sizeof(glm::vec2) * len,
(void*)coordinates
);
assertNoGLError();
}
void Mesh::bufferIndices(
const int32_t pos,
const int32_t indices[],
const int32_t len
) {
assertNotNull(indices, "Indices cannot be null");
assertTrue(pos >= 0 && pos < indiceCount, "Position must be within range");
assertTrue(pos+len <= indiceCount, "Position + Length must be within range");
assertTrue(len > 0, "Length must be greater than zero");
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
assertNoGLError();
glBufferSubData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(int32_t) * pos,
sizeof(int32_t) * len,
(void*)indices
);
assertNoGLError();
}
void Mesh::draw(
const enum MeshDrawMode drawMode,
const int32_t start,
const int32_t count
) {
if(
count == 0 ||
this->vertexBuffer == -1 ||
this->indexBuffer == -1
) return;
int32_t drawCount = count;
if(count == -1) drawCount = this->indiceCount;
// Re-Bind the buffers
glBindVertexArray(this->vertexArray);
assertNoGLError();
glBindBuffer(GL_ARRAY_BUFFER, this->vertexBuffer);
assertNoGLError();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->indexBuffer);
assertNoGLError();
// Re-Calculate the attrib pointers.
size_t offset = 0;
glVertexAttribPointer(
0, sizeof(glm::vec3) / sizeof(float_t),
GL_FLOAT, GL_FALSE,
0, (void *)offset
);
assertNoGLError();
glEnableVertexAttribArray(0);
assertNoGLError();
offset += sizeof(glm::vec3) * this->verticeCount;
glVertexAttribPointer(
1, sizeof(glm::vec2) / sizeof(float_t),
GL_FLOAT, GL_FALSE,
0, (void *)offset
);
assertNoGLError();
glEnableVertexAttribArray(1);
assertNoGLError();
GLuint glDrawMode;
switch(drawMode) {
case MeshDrawMode::TRIANGLES:
glDrawMode = GL_TRIANGLES;
break;
case MeshDrawMode::TRIANGLE_STRIP:
glDrawMode = GL_TRIANGLE_STRIP;
break;
case MeshDrawMode::TRIANGLE_FAN:
glDrawMode = GL_TRIANGLE_FAN;
break;
case MeshDrawMode::LINES:
glDrawMode = GL_LINES;
break;
case MeshDrawMode::POINTS:
glDrawMode = GL_POINTS;
break;
default:
assertUnreachable("Unsupported draw mode");
}
// Render the elements.
glDrawElements(
glDrawMode,
drawCount,
GL_UNSIGNED_INT,
(void *)(sizeof(int32_t) * start)
);
assertNoGLError();
}
Mesh::~Mesh() {
this->disposeBuffers();
}

View File

@ -0,0 +1,51 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnopengl.hpp"
#include "display/mesh/IMesh.hpp"
namespace Dawn {
class Mesh : public IMesh {
protected:
GLuint vertexBuffer = -1;
GLuint indexBuffer = -1;
GLuint vertexArray = -1;
public:
void createBuffers(
const int32_t verticeCount,
const int32_t indiceCount
) override;
void disposeBuffers() override;
void bufferPositions(
const int32_t pos,
const glm::vec3 positions[],
const int32_t len
) override;
void bufferCoordinates(
const int32_t pos,
const glm::vec2 coordinates[],
const int32_t len
) override;
void bufferIndices(
const int32_t pos,
const int32_t indices[],
const int32_t len
) override;
void draw(
const enum MeshDrawMode drawMode,
const int32_t start,
const int32_t count
) override;
~Mesh();
};
}

View File

@ -0,0 +1,13 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Shader.cpp
ShaderStage.cpp
SimpleTexturedShader.cpp
ShaderParameter.cpp
)

View File

@ -0,0 +1,8 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Shader.hpp"
using namespace Dawn;

View File

@ -0,0 +1,238 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/shader/ShaderStage.hpp"
#include "display/shader/IShader.hpp"
#include "error/assert.hpp"
#include "error/assertgl.hpp"
#include "display/Color.hpp"
#include "display/Texture.hpp"
#include "ShaderParameter.hpp"
#include "ShaderStructure.hpp"
namespace Dawn {
typedef GLuint shadertexturebinding_t;
enum class ShaderOpenGLVariant {
GLSL_330_CORE
};
template<typename T>
class Shader : public IShader<T> {
private:
std::vector<std::shared_ptr<ShaderStage>> stages;
std::vector<struct ShaderParameter> parameters;
std::vector<struct IShaderStructure> structures;
enum ShaderOpenGLVariant variant;
GLuint shaderProgram = -1;
protected:
/**
* Overridable function to get the stages for the shader.
*
* @param variant The variant of the shader to use.
* @param rel The relative data to use.
* @param stages The stages to add to.
* @param parameters The parameters to add to.
* @param structures The structures to add to.
*/
virtual void getStages(
const enum ShaderOpenGLVariant variant,
const T *rel,
std::vector<std::shared_ptr<ShaderStage>> &stages,
std::vector<struct ShaderParameter> &parameters,
std::vector<struct IShaderStructure> &structures
) = 0;
public:
/**
* Initializes the shader, this needs to be called before the shader can
* be used.
*/
void init() override {
// Determine which kind of OpenGL shader to use.
variant = ShaderOpenGLVariant::GLSL_330_CORE;
// Now get the stages
T dummy;
this->getStages(
variant,
&dummy,
stages,
parameters,
structures
);
// Create the shader program
shaderProgram = glCreateProgram();
assertNoGLError();
// Attach all the stages
for(auto stage : stages) {
glAttachShader(shaderProgram, stage->id);
assertNoGLError();
}
// Link and verify the program
glLinkProgram(shaderProgram);
assertNoGLError();
GLint status;
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &status);
assertNoGLError();
assertTrue(status == GL_TRUE, "Failed to link shader program.");
// Map parameters correctly.
std::for_each(
parameters.begin(),
parameters.end(),
[&](struct ShaderParameter &param) {
// Correct offset
param.offset = param.offset - (size_t)(&dummy);
param.location = glGetUniformLocation(
shaderProgram,
param.name.c_str()
);
assertNoGLError();
assertTrue(
param.location != -1,
"Failed to get location for parameter %s.",
param.name.c_str()
);
}
);
// Map structures
std::for_each(
structures.begin(),
structures.end(),
[&](struct IShaderStructure &structure) {
structure.offset = structure.offset - (size_t)(&dummy);
structure.location = glGetUniformBlockIndex(
shaderProgram,
structure.structureName.c_str()
);
assertNoGLError();
assertTrue(
structure.location != -1,
"Failed to get location for structure %s.",
structure.structureName.c_str()
);
// Create the buffer
glGenBuffers(1, &structure.buffer);
}
);
this->bind();
}
/**
* Binds the shader as the current one, does not upload any data, somewhat
* relies on something else uploading the data.
*/
void bind() override {
glUseProgram(shaderProgram);
assertNoGLError();
}
/**
* Uploads the data to the GPU.
*/
void upload() override {
switch(this->variant) {
case ShaderOpenGLVariant::GLSL_330_CORE:
for(auto param : parameters) {
void *value = (void*)(
((size_t)&this->data) + param.offset
);
switch(param.type) {
case ShaderParameterType::MAT4: {
glm::mat4 *matrix = (glm::mat4 *)value;
if(param.count != 1) {
assertUnreachable("I haven't implemented multiple mat4s");
}
glUniformMatrix4fv(
param.location, 1, GL_FALSE, glm::value_ptr(*matrix)
);
break;
}
case ShaderParameterType::COLOR: {
auto color = (Color *)value;
glUniform4fv(
param.location,
param.count,
(GLfloat*)value
);
break;
}
case ShaderParameterType::BOOLEAN: {
glUniform1iv(param.location, param.count, (GLint*)value);
break;
}
case ShaderParameterType::TEXTURE: {
glUniform1iv(param.location, param.count, (GLint*)value);
break;
}
default: {
assertUnreachable("Unsupported ShaderParameterType");
}
}
assertNoGLError();
}
break;
default:
assertUnreachable("Unsupported ShaderOpenGLVariant");
}
// Upload structures
for(auto structure : structures) {
switch(structure.structureType) {
case ShaderOpenGLStructureType::STD140: {
// Upload the data
glBindBuffer(GL_UNIFORM_BUFFER, structure.buffer);
assertNoGLError();
glBindBufferBase(GL_UNIFORM_BUFFER, structure.location, structure.buffer);
assertNoGLError();
glBufferData(
GL_UNIFORM_BUFFER,
structure.size * structure.count,
(void*)((size_t)&this->data + (size_t)structure.offset),
GL_STATIC_DRAW
);
assertNoGLError();
break;
}
default:
assertUnreachable("Unsupported ShaderOpenGLStructureType");
}
}
}
~Shader() {
// Delete the structures
for(auto structure : structures) {
assertTrue(structure.buffer != -1, "Invalid buffer.");
glDeleteBuffers(1, &structure.buffer);
assertNoGLError();
}
// Delete the shader program
glDeleteProgram(shaderProgram);
assertNoGLError();
}
};
}

View File

@ -0,0 +1,20 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "ShaderParameter.hpp"
using namespace Dawn;
ShaderParameter::ShaderParameter(
const std::string &name,
const void *offset,
const enum ShaderParameterType type,
const size_t count
) {
this->name = name;
this->offset = (size_t)offset;
this->type = type;
this->count = count;
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/shader/IShader.hpp"
#include "dawnopengl.hpp"
namespace Dawn {
struct ShaderParameter {
std::string name;
size_t offset;
enum ShaderParameterType type;
size_t count;
GLint location = -1;
/**
* Construct a new shader parameter.
*
* @param name Name of the parameter within the shader.
* @param offset Offset, relative to the structure of the data.
* @param type Type of the parameter.
* @param count How many elements in the array (if multiple).
*/
ShaderParameter(
const std::string &name,
const void *offset,
const enum ShaderParameterType type,
const size_t count = 1
);
};
}

View File

@ -0,0 +1,69 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "error/assertgl.hpp"
#include "error/assert.hpp"
#include "ShaderStage.hpp"
using namespace Dawn;
ShaderStage::ShaderStage(
const enum ShaderStageType type,
const std::string source
) : IShaderStage(type) {
// Get OpenGL Shader Type
GLenum shaderType;
switch(this->type) {
case ShaderStageType::VERTEX:
shaderType = GL_VERTEX_SHADER;
break;
case ShaderStageType::FRAGMENT:
shaderType = GL_FRAGMENT_SHADER;
break;
// case ShaderStageType::COMPUTE:
// shaderType = GL_COMPUTE;
// break;
default:
assertUnreachable("Unknown ShaderStageType");
}
// Initialize the shader
this->id = glCreateShader(shaderType);
assertNoGLError();
// Compile the shader
auto cSource = source.c_str();
glShaderSource(this->id, 1, &cSource, NULL);
assertNoGLError();
glCompileShader(this->id);
assertNoGLError();
// Validate
GLint status;
glGetShaderiv(this->id, GL_COMPILE_STATUS, &status);
assertNoGLError();
if(!status) {
// Failed to compile
GLint logLength;
glGetShaderiv(this->id, GL_INFO_LOG_LENGTH, &logLength);
assertNoGLError();
GLchar *log = new GLchar[logLength];
glGetShaderInfoLog(this->id, logLength, NULL, log);
assertNoGLError();
assertUnreachable("Failed to compile shader stage %i:\n%s", type, log);
}
}
ShaderStage::~ShaderStage() {
if(this->id != -1) {
glDeleteShader(this->id);
assertNoGLError();
}
}

View File

@ -0,0 +1,28 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnopengl.hpp"
#include "display/shader/IShaderStage.hpp"
namespace Dawn {
class ShaderStage : public IShaderStage {
public:
GLuint id = -1;
/**
* Constructs a new ShaderStage.
*
* @param type The type of shader this is.
* @param source The source code to compile.
*/
ShaderStage(const enum ShaderStageType type, const std::string source);
/**
* Disposes of the shader stage.
*/
~ShaderStage();
};
}

View File

@ -0,0 +1,95 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma ocne
#include "display/shader/ShaderParameter.hpp"
namespace Dawn {
enum class ShaderOpenGLStructureType {
STD140
};
struct IShaderStructure {
std::string structureName;
size_t offset;
enum ShaderOpenGLStructureType structureType;
size_t size;
size_t count;
std::vector<struct ShaderParameter> parameters;
GLint location = -1;
GLuint buffer = -1;
};
template<typename T>
struct ShaderStructure final : public IShaderStructure {
public:
/**
* Constructs a new shader structure. Shader structures allow for larger
* amounts/volumes of data to be passed to the shader in a single call.
* Ideally I wouldn't really need this as I wanted the entire shader to
* basically be a single large structure, but OpenGL doesn't support that
* so I have to do this instead.
*
* @param structureName Structure name within the shader.
* @param offset Offset, within the data structure, that this structure
* starts at.
* @param structureType The type of structure data format to use.
* @param getParameters A callback that, when invoked, will populate the
* parameters vector with the parameters for this
* structure.
*/
ShaderStructure(
const std::string &structureName,
const void *offset,
const enum ShaderOpenGLStructureType structureType,
std::function<
void(const T&, std::vector<struct ShaderParameter>&)
> getParameters,
size_t count = 1
) {
this->structureName = structureName;
this->offset = (size_t)offset;
this->structureType = structureType;
this->size = sizeof(T);
this->count = count;
this->parameters = std::vector<struct ShaderParameter>();
T dummy;
getParameters(dummy, this->parameters);
// Update offsets.
auto itParams = this->parameters.begin();
while(itParams != this->parameters.end()) {
struct ShaderParameter &param = *itParams;
param.offset -= (size_t)(&dummy);
// Check for non-aligned OpenGL structures.
if(param.offset % sizeof(glm::vec4) != 0) {
assertUnreachable(
"%s%s%s",
"Non-aligned OpenGL structure detected on param ",
param.name.c_str(),
"!\nEnsure you have padded correctly."
);
}
if(
itParams == (this->parameters.end() - 1) &&
count > 1 &&
(sizeof(T) % sizeof(glm::vec4)) != 0
) {
assertUnreachable(
"%s%s%s",
"Non-aligned OpenGL structure detected on last element in array structure on param ",
param.name.c_str(),
"!\nEnsure you have padded correctly."
);
}
++itParams;
}
}
};
}

View File

@ -0,0 +1,100 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "display/shader/SimpleTexturedShader.hpp"
using namespace Dawn;
void SimpleTexturedShader::getStages(
const enum ShaderOpenGLVariant variant,
const struct SimpleTexturedShaderData *rel,
std::vector<std::shared_ptr<ShaderStage>> &stages,
std::vector<struct ShaderParameter> &parameters,
std::vector<struct IShaderStructure> &structures
) {
// Stages
std::shared_ptr<ShaderStage> vertex;
std::shared_ptr<ShaderStage> fragment;
switch(variant) {
case ShaderOpenGLVariant::GLSL_330_CORE:
vertex = std::make_shared<ShaderStage>(
ShaderStageType::VERTEX,
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"uniform mat4 u_Projection;\n"
"uniform mat4 u_View;\n"
"uniform mat4 u_Model;\n"
"out vec2 o_TextCoord;\n"
"void main() {\n"
"gl_Position = u_Projection * u_View * u_Model * vec4(aPos, 1.0);\n"
"o_TextCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}"
);
fragment = std::make_shared<ShaderStage>(
ShaderStageType::FRAGMENT,
"#version 330 core\n"
"in vec2 o_TextCoord;\n"
"out vec4 o_Color;\n"
"uniform vec4 u_Color;\n"
"uniform bool u_HasTexture;\n"
"uniform sampler2D u_Texture;\n"
"void main() {\n"
"if(u_HasTexture) {\n"
"o_Color = texture(u_Texture, o_TextCoord) * u_Color;\n"
"} else {\n"
"o_Color = u_Color;"
"}\n"
"}\n"
);
break;
default:
assertUnreachable("Unsupported ShaderOpenGLVariant");
}
// Add stages
stages.push_back(vertex);
stages.push_back(fragment);
// Parameters
parameters.push_back(ShaderParameter(
"u_Projection",
&rel->projection,
ShaderParameterType::MAT4
));
parameters.push_back(ShaderParameter(
"u_View",
&rel->view,
ShaderParameterType::MAT4
));
parameters.push_back(ShaderParameter(
"u_Model",
&rel->model,
ShaderParameterType::MAT4
));
parameters.push_back(ShaderParameter(
"u_Color",
&rel->color,
ShaderParameterType::COLOR
));
parameters.push_back(ShaderParameter(
"u_HasTexture",
&rel->hasTexture,
ShaderParameterType::BOOLEAN
));
parameters.push_back(ShaderParameter(
"u_Texture",
&rel->texture,
ShaderParameterType::TEXTURE
));
}

View File

@ -0,0 +1,29 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/shader/Shader.hpp"
namespace Dawn {
struct SimpleTexturedShaderData {
glm::mat4 projection;
glm::mat4 view;
glm::mat4 model;
struct Color color = COLOR_WHITE;
bool hasTexture = false;
shadertexturebinding_t texture = 0;
};
class SimpleTexturedShader : public Shader<SimpleTexturedShaderData> {
protected:
void getStages(
const enum ShaderOpenGLVariant variant,
const struct SimpleTexturedShaderData *rel,
std::vector<std::shared_ptr<ShaderStage>> &stages,
std::vector<struct ShaderParameter> &parameters,
std::vector<struct IShaderStructure> &structures
) override;
};
}