diff --git a/2023-4-1_15-19-50.log b/2023-4-1_15-19-50.log new file mode 100644 index 0000000..e69de29 diff --git a/CMakeLists.txt b/CMakeLists.txt index 27e8cd9..285e068 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,15 @@ set(CMAKE_CXX_STANDARD 17) # I like absolute directories since I think relative file paths are ugly and hard to read include_directories(include) include_directories(${CMAKE_BINARY_DIR}/include/) -file(GLOB_RECURSE SRC_FILES "src/*.cpp") +#file(GLOB_RECURSE SRC_FILES "src/*.cpp") + +# don't try to build other mode's files as we will use the same names of functions to make it easier +# we have to do this because GL +if (${EXTRAS}) + file(GLOB SRC_FILES "src/*.cpp" "src/high_perf/*.cpp") +else () + file(GLOB SRC_FILES "src/*.cpp" "src/basic/*.cpp") +endif () # Include my utility library add_subdirectory(libs/BLT) diff --git a/include/camera.h b/include/camera.h index 43ceeda..19b721e 100644 --- a/include/camera.h +++ b/include/camera.h @@ -6,10 +6,10 @@ #ifndef ASSIGN3_CAMERA_H #define ASSIGN3_CAMERA_H -#include -#include #include #include +#include +#include #include #include "blt/std/logging.h" @@ -29,6 +29,7 @@ class camera { const float MAX_SPEED = 100; const float DEFAULT_SPEED = 50; + const float MIN_SPEED = 1; const float ROTATION_SPEED = 3; float cur_speed = DEFAULT_SPEED; @@ -106,7 +107,14 @@ class camera { rotation[2] += (-(float) horzSpeed * delta * ROTATION_SPEED); else if (specialState[GLUT_KEY_RIGHT]) rotation[2] += ((float) horzSpeed * delta * ROTATION_SPEED); - + + if (specialState[GLUT_KEY_F1]) + cur_speed = DEFAULT_SPEED; + if (specialState[GLUT_KEY_F2]) + cur_speed = MIN_SPEED; + if (specialState[GLUT_KEY_F3]) + cur_speed = MAX_SPEED; + if (rotation[2] > 360) rotation[2] = 0; if (rotation[2] < 0) diff --git a/include/glad/gl.h b/include/glad/gl.h index 83c7fa4..4212904 100644 --- a/include/glad/gl.h +++ b/include/glad/gl.h @@ -3033,7 +3033,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro #endif /*------------------------------------------------------------------------- - * modes type definitions + * basic type definitions *-----------------------------------------------------------------------*/ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) diff --git a/include/high_perf/gl_util.h b/include/high_perf/gl_util.h new file mode 100644 index 0000000..f099bb3 --- /dev/null +++ b/include/high_perf/gl_util.h @@ -0,0 +1,132 @@ +/* + * Created by Brett on 31/03/23. + * Licensed under GNU General Public License V3.0 + * See LICENSE file for license detail + */ + +#ifndef ASSIGN3_GL_UTIL_H +#define ASSIGN3_GL_UTIL_H + +#include +#include +#include +#include +#include + +/** + * Note: This is taken from my final project, + * https://github.com/Tri11Paragon/COSC-3P98-Final-Project/blob/main/include/render/gl.h + */ + +class shader { + private: + struct IntDefaultedToMinusOne { + GLint i = -1; + }; + // we can have shaders of many types in OpenGL + unsigned int programID = 0; + // but we will only make use of these two for now + unsigned int vertexShaderID = 0; + unsigned int fragmentShaderID = 0; + // while these will remain unused. (Webgl2 apparently doesn't support them despite being based on GL4.3? that's a TODO!) + unsigned int geometryShaderID = 0; + // this would be very useful however it is highly unlikely webgl will support it + // im leaving some of this stuff in here because I might expand the native application to use some of it. + // im trying to keep the web and native versions the same though + unsigned int tessellationShaderID = 0; + std::unordered_map uniformVars; + + static unsigned int createShader(const std::string& source, int type); + + inline GLint getUniformLocation(const std::string &name) { + if (uniformVars[name].i != -1) + return uniformVars[name].i; + // caching the result is a lot faster since it won't change after the shader is created. + // TODO: look into this: https://webglfundamentals.org/webgl/lessons/webgl-qna-how-can-i-get-all-the-uniforms-and-uniformblocks.html + int loc = glGetUniformLocation(programID, name.c_str()); + uniformVars[name].i = loc; + return loc; + } + + static inline std::string removeEmptyFirstLines(const std::string& string){ + auto lines = blt::string::split(string, "\n"); + std::string new_source_string; + for (const auto& line : lines) { + if (!line.empty() && !blt::string::contains(line, "\"")) { + new_source_string += line; + new_source_string += "\n"; + } + } + return new_source_string; + } + + public: + /** + * Creates a shader + * @param vertex vertex shader source or file + * @param fragment fragment shader source or file + * @param geometry geometry shader source or file (optional) + * @param load_as_string load the shader as a string (true) or use the string to load the shader as a file (false) + */ + shader(const std::string &vertex, const std::string &fragment, const std::string &geometry = "", bool load_as_string = true); + + shader(shader&& move) noexcept; + + // used to set the location of VAOs to the in variables in opengl shaders. + void bindAttribute(int attribute, const std::string &name) const; + + // used to set location of shared UBOs like the perspective and view matrix + void setUniformBlockLocation(const std::string &name, int location) const; + + // set various data-types. + inline void setBool(const std::string &name, bool value) { + glUniform1i(getUniformLocation(name), (int) value); + } + + inline void setInt(const std::string &name, int value) { + glUniform1i(getUniformLocation(name), value); + } + + inline void setFloat(const std::string &name, float value) { + glUniform1f(getUniformLocation(name), value); + } + + inline void setMatrix(const std::string &name, blt::mat4x4 &matrix) { + glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, matrix.ptr()); + } + + inline void setVec3(const std::string &name, const blt::vec3 &vec) { + glUniform3f(getUniformLocation(name), vec.x(), vec.y(), vec.z()); + } + + inline void setVec4(const std::string &name, const blt::vec4 &vec) { + // TODO: edit BLT to include a w component + glUniform4f(getUniformLocation(name), vec.x(), vec.y(), vec.z(), vec[3]); + } + + inline void setVec2(const std::string &name, float x, float y) { + glUniform2f(getUniformLocation(name), x, y); + } + + inline void setVec3(const std::string &name, float x, float y, float z) { + glUniform3f(getUniformLocation(name), x, y, z); + } + + inline void setVec4(const std::string &name, float x, float y, float z, float w) { + glUniform4f(getUniformLocation(name), x, y, z, w); + } + + inline void bind() const { + glUseProgram(programID); + } + + static void updateProjectionMatrix(const blt::mat4x4& projectionMatrix); + static void updateOrthographicMatrix(const blt::mat4x4& orthoMatrix); + static void updateViewMatrix(const blt::mat4x4& viewMatrix); + // returns the perspective view matrix which is calculated per frame. (This is for optimization) + static const blt::mat4x4& getPVM(); + + ~shader(); +}; + +#endif //ASSIGN3_GL_UTIL_H diff --git a/include/modes/advanced.h b/include/modes/high_perf.h similarity index 73% rename from include/modes/advanced.h rename to include/modes/high_perf.h index 0b80613..362457d 100644 --- a/include/modes/advanced.h +++ b/include/modes/high_perf.h @@ -4,17 +4,20 @@ * See LICENSE file for license detail */ -#ifndef ASSIGN3_ADVANCED_H -#define ASSIGN3_ADVANCED_H +#ifndef ASSIGN3_HIGH_PERF_H +#define ASSIGN3_HIGH_PERF_H #include #include #include -extern particle_system* fountain; +#ifdef EXTRAS + extern int WINDOW_WIDTH; extern int WINDOW_HEIGHT; +extern particle_system* fountain; extern camera cam; +extern const unsigned int particle_count; void window_resize(int width, int height); @@ -26,4 +29,6 @@ void init(); void cleanup(); -#endif //ASSIGN3_ADVANCED_H +#endif + +#endif //ASSIGN3_HIGH_PERF_H diff --git a/include/particle_system.h b/include/particle_system.h index 369a4ed..dddb8ee 100644 --- a/include/particle_system.h +++ b/include/particle_system.h @@ -92,6 +92,7 @@ class particle_system { particle_system( const blt::vec3& position, const blt::vec3& direction, float spread, int pps ): position(position), direction(direction), spread(spread), pps(pps) { +#ifndef EXTRAS quad = glGenLists(1); glNewList(quad, GL_COMPILE); glBegin(GL_QUADS); @@ -105,6 +106,7 @@ class particle_system { glVertex3f(s, s, 0); glEnd(); glEndList(); +#endif } void update(camera& cam, float bnx, float bnz, float bpx, float bpz) { @@ -173,6 +175,7 @@ class particle_system { } inline static void applyBillboard() { +#ifndef EXTRAS GLfloat m[16]; glMatrixMode(GL_MODELVIEW); glGetFloatv(GL_MODELVIEW_MATRIX, m); @@ -187,9 +190,11 @@ class particle_system { } glLoadMatrixf(m); +#endif } void render(camera& cam, texture** textures) { +#ifndef EXTRAS glMatrixMode(GL_MODELVIEW); // by batching particles by texture we save a little driver overhead for (auto& pair : particles) { @@ -243,6 +248,7 @@ class particle_system { str << (randomizeTexture ? "True" : "False"); glutSetWindowTitle(str.str().c_str()); } +#endif } void randomizeSpeed(float n) { diff --git a/include/shaders/fragment.frag b/include/shaders/fragment.frag new file mode 100644 index 0000000..d3f464b --- /dev/null +++ b/include/shaders/fragment.frag @@ -0,0 +1,15 @@ +#ifdef __cplusplus +#include +std::string shader_frag = R"(" +#version 460 + +in vec2 uv_; + +out vec4 out_color; + +void main() { + out_color = vec4(uv_, 0.0, 1.0); +} + +")"; +#endif \ No newline at end of file diff --git a/include/shaders/vertex.vert b/include/shaders/vertex.vert new file mode 100644 index 0000000..39b9791 --- /dev/null +++ b/include/shaders/vertex.vert @@ -0,0 +1,23 @@ +#ifdef __cplusplus +#include +std::string shader_vert = R"(" +#version 460 + +layout (location = 0) in vec3 vertex; +layout (location = 1) in vec2 uv; +layout (location = 2) in vec4 pos; +layout (location = 3) in vec4 dir; + +out vec2 uv_; + +uniform mat4 pvm; + +void main() { + // passthough the UV (OpenGL interpolates this per fragment) + uv_ = uv; + // offset the vertex by the particle's position + gl_Position = pvm * vec4(vertex + pos.xyz, 1.0); +} + +")"; +#endif \ No newline at end of file diff --git a/include/stb_image.h b/include/stb_image.h index e845615..91fcc41 100644 --- a/include/stb_image.h +++ b/include/stb_image.h @@ -798,8 +798,8 @@ static int stbi__sse2_available(void) // // stbi__context struct and start_xxx functions -// stbi__context structure is our modes context used by all images, so it -// contains all the IO context, plus some modes image information +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information typedef struct { stbi__uint32 img_x, img_y; @@ -2983,7 +2983,7 @@ static int stbi__parse_entropy_coded_data(stbi__jpeg *z) for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined - // by the modes H and V specified for the component + // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x)*8; @@ -3043,7 +3043,7 @@ static int stbi__parse_entropy_coded_data(stbi__jpeg *z) for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined - // by the modes H and V specified for the component + // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x); @@ -5471,7 +5471,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG basic if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres @@ -5912,7 +5912,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured modes consistency + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); // tga info @@ -6173,7 +6173,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req if (stbi__get16be(s) != 3) return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + // Skip the Mode Data. (It's the palette for indexed color; other info for other basic.) stbi__skip(s,stbi__get32be(s) ); // Skip the image resources. (resolution, pen tool paths, etc) diff --git a/include/util.h b/include/util.h index 2c4f804..bb2faa9 100644 --- a/include/util.h +++ b/include/util.h @@ -32,6 +32,10 @@ typedef struct { float x, y, z; } vec; +typedef struct { + float x, y, z, w; +} vec4; + inline vec operator+(const vec& l, const vec& r) { return {l.x + r.x, l.y + r.y, l.z + r.z}; } diff --git a/libs/BLT b/libs/BLT index 5d841af..289af13 160000 --- a/libs/BLT +++ b/libs/BLT @@ -1 +1 @@ -Subproject commit 5d841afe8ca0a8776820eb8593005ca5539205bc +Subproject commit 289af1317141c53e04998b32760efced0956db57 diff --git a/mangohud.sh b/mangohud.sh new file mode 100755 index 0000000..3197960 --- /dev/null +++ b/mangohud.sh @@ -0,0 +1,2 @@ +#!/bin/bash +mangohud $1 diff --git a/src/modes/basic.cpp b/src/basic/basic.cpp similarity index 98% rename from src/modes/basic.cpp rename to src/basic/basic.cpp index a55292f..1e498a5 100644 --- a/src/modes/basic.cpp +++ b/src/basic/basic.cpp @@ -5,7 +5,7 @@ */ #include -#ifndef EXTRAS +//#ifndef EXTRAS texture* world_floor; texture* particle_tex[10]; @@ -64,4 +64,4 @@ void cleanup() { delete world_floor; } -#endif \ No newline at end of file +//#endif \ No newline at end of file diff --git a/src/high_perf/gl_util.cpp b/src/high_perf/gl_util.cpp new file mode 100644 index 0000000..3527009 --- /dev/null +++ b/src/high_perf/gl_util.cpp @@ -0,0 +1,156 @@ +/* + * Created by Brett on 31/03/23. + * Licensed under GNU General Public License V3.0 + * See LICENSE file for license detail + */ +#include +#include "blt/std/memory.h" +#include + +/** + * Note: This is taken from my final project, + * https://github.com/Tri11Paragon/COSC-3P98-Final-Project/blob/main/include/render/gl.h + */ + +unsigned int shader::createShader(const std::string& source, int type) { + const char* shader_code = source.c_str(); + // creates a Shader + unsigned int shaderID = glCreateShader(type); + // loads the shader code for later complication and uploading into the graphics card + // TODO: defines can be added here by sending them as additional strings. No need to edit the source string + glShaderSource(shaderID, 1, &shader_code, nullptr); + // Compile it + glCompileShader(shaderID); + + // make sure there are no errors in the compilation. If there is then print out information pertaining to the error. + // the actual log is highly dependent on the platform this is being run from, so we cannot make any assumptions about the issue. + // the TODO: maybe find a way of lexing the output to give suggestions about fixing the error? default error messages can be unhelpful at times. + GLint success; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); + if (!success) { + int log_length = 0; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &log_length); + + // scoped buffers will delete their memory when they go out of scope. A benefit of using BLT + blt::scoped_buffer infoLog{static_cast(log_length + 1)}; + + glGetShaderInfoLog(shaderID, log_length + 1, nullptr, infoLog.buffer); + auto shader_type_str = (type == GL_VERTEX_SHADER ? "Vertex Shader" : type == GL_FRAGMENT_SHADER ? "Fragment Shader" : "Other Shader"); + BLT_ERROR("--- --- --- --- --- --- --- --- ---"); + BLT_ERROR("Unable to compile shader of type %s\nShader source:", shader_type_str); + BLT_ERROR(source); + BLT_ERROR("I have an log of %d length", log_length); + BLT_ERROR(infoLog.buffer); + BLT_ERROR("--- --- --- --- --- --- --- --- ---"); + } + return shaderID; +} + +shader::shader(const std::string& vertex, const std::string& fragment, const std::string& geometry, bool load_as_string) { + // load shader sources + bool load_geometry = !geometry.empty(); + std::string vertex_source = vertex; + std::string fragment_source = fragment; + std::string geometry_source = geometry; + if (!load_as_string){ + // BLT provides a recursive file loader for glsl shaders. It's pretty much just a recursive function looking for include statements. + vertex_source = blt::fs::loadShaderFile(vertex); + fragment_source = blt::fs::loadShaderFile(fragment); + if (load_geometry) + geometry_source = blt::fs::loadShaderFile(geometry); + } else { + vertex_source = removeEmptyFirstLines(vertex_source); + fragment_source = removeEmptyFirstLines(fragment_source); + geometry_source = removeEmptyFirstLines(geometry_source); + } + + // create the shaders + vertexShaderID = createShader(vertex_source, GL_VERTEX_SHADER); + fragmentShaderID = createShader(fragment_source, GL_FRAGMENT_SHADER); + if (load_geometry) + BLT_ERROR("Unable to load geometry shader because webgl doesn't support it!"); + + // bind them to a program + programID = glCreateProgram(); + // attach the loaded shaders to the Shader program + glAttachShader(programID, vertexShaderID); + glAttachShader(programID, fragmentShaderID); + if (load_geometry) + glAttachShader(programID, geometryShaderID); + // link and make sure that our program is valid. + glLinkProgram(programID); + + GLint success; + glGetProgramiv(programID, GL_LINK_STATUS, &success); + if (!success) { + int log_length = 0; + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &log_length); + + // scoped buffers will delete their memory when they go out of scope. + blt::scoped_buffer infoLog{static_cast(log_length + 1)}; + + glGetProgramInfoLog(programID, log_length + 1, nullptr, infoLog.buffer); + BLT_ERROR("--- --- --- --- --- --- --- --- ---"); + BLT_ERROR("Unable to link program of ID: %d", programID); + BLT_ERROR(vertex_source); + BLT_ERROR(fragment_source); + BLT_ERROR(geometry_source); + BLT_ERROR("I have an log of %d length", log_length); + BLT_ERROR(infoLog.buffer); + BLT_ERROR("--- --- --- --- --- --- --- --- ---"); + } + + glValidateProgram(programID); + bind(); + setUniformBlockLocation("StandardMatrices", 0); + glUseProgram(0); + +} + +void shader::bindAttribute(int attribute, const std::string &name) const { + bind(); + glBindAttribLocation(programID, attribute, name.c_str()); +} + +void shader::setUniformBlockLocation(const std::string &name, int location) const { + bind(); + glUniformBlockBinding(programID, glGetUniformBlockIndex(programID, name.c_str()), location); +} + +shader::~shader() { + glUseProgram(0); + // shader was moved + if (programID <= 0) + return; + // remove all the shaders from the program + glDetachShader(programID, vertexShaderID); + if (geometryShaderID) + glDetachShader(programID, geometryShaderID); + if (tessellationShaderID) + glDetachShader(programID, tessellationShaderID); + glDetachShader(programID, fragmentShaderID); + + // delete the shaders + glDeleteShader(vertexShaderID); + if (geometryShaderID) + glDeleteShader(geometryShaderID); + if (tessellationShaderID) + glDeleteShader(tessellationShaderID); + glDeleteShader(fragmentShaderID); + + // delete the Shader program + glDeleteProgram(programID); +} + +shader::shader(shader&& move) noexcept { + // the move constructor doesn't need to construct a new shader but it does need to ensure all old variables are moved over + programID = move.programID; + vertexShaderID = move.vertexShaderID; + fragmentShaderID = move.fragmentShaderID; + geometryShaderID = move.geometryShaderID; + tessellationShaderID = move.tessellationShaderID; + for (const auto& pair : move.uniformVars) + uniformVars.insert(pair); + // by setting the program ID to -1 we tell the shader it has been moved. + move.programID = -1; +} \ No newline at end of file diff --git a/src/high_perf/high_perf.cpp b/src/high_perf/high_perf.cpp new file mode 100644 index 0000000..d0373b5 --- /dev/null +++ b/src/high_perf/high_perf.cpp @@ -0,0 +1,154 @@ +/* + * Created by Brett on 30/03/23. + * Licensed under GNU General Public License V3.0 + * See LICENSE file for license detail + */ +#include +#include +#include +#include +#include "blt/std/memory.h" +#include +#include + +//static inline float degreesToRadian(float deg) { +// return deg * (float)M_PI / 180.0f; +//} + +blt::mat4x4 createViewMatrix(){ + auto position = cam.getPosition(); + auto rotation = cam.getRotation(); + + blt::mat4x4 viewMatrix; + + viewMatrix.rotateX(rotation.y() * TO_RAD); + viewMatrix.rotateY(rotation.z() * TO_RAD); + viewMatrix.translate(-position); + + return viewMatrix; +} + +void window_resize(int width, int height) { + +} + +GLuint particleTranslationsVBO; +GLuint verticesVBO; +GLuint uvsVBO; +GLuint indicesEBO; +GLuint particleVAO; + +const unsigned int particle_count = 25000000; + +// generally alignment to multiples of 4 floats helps performance, plus we can use that extra space for info we need. +typedef struct { + // x y z (texture index) + vec4 pos; + // dx dy dz (unused) + vec4 dir; +} particle_record; + +const float vertices[] = { + 0.5f, 0.5f, 0.0f, + 0.5f, -0.5f, 0.0f, + -0.5f, -0.5f, 0.0f, + -0.5f, 0.5f, 0.0f +}; +const float uvs[] = { + 0, 0, + 0, 1, + 1, 1, + 1, 0 +}; +const unsigned int indices[] = { + 0, 1, 3, + 1, 2, 3 +}; + +blt::mat4x4 perspectiveMatrix; +blt::mat4x4 viewMatrix; + +shader* instance_shader; + +void updateView() { + viewMatrix = createViewMatrix(); +} + +void render() { + updateView(); + perspectiveMatrix = blt::perspective(FOV, (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 1000.0f); + auto pvm = perspectiveMatrix * viewMatrix; + + instance_shader->bind(); + instance_shader->setMatrix("pvm", pvm); + + glBindVertexArray(particleVAO); + glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, particle_count); + glBindVertexArray(0); + +} + +void init() { + blt::scoped_buffer translations{particle_count}; + blt::random pos{-50.0, 50.0}; + + for (int i = 0; i < particle_count; i++) + translations[i] = particle_record{vec4{pos.get(), pos.get() / 2, pos.get(), (float)(i % 10)}, vec4{0, 0, 0, 0}}; + + // ---------------------------------- + // Create OpenGL Objects + // ---------------------------------- + // create our VAO + glGenVertexArrays(1, &particleVAO); + // create our VBOs + glGenBuffers(1, &particleTranslationsVBO); + glGenBuffers(1, &verticesVBO); + glGenBuffers(1, &uvsVBO); + glGenBuffers(1, &indicesEBO); + + glBindVertexArray(particleVAO); + + // bind and upload vertices data to the GPU + glBindBuffer(GL_ARRAY_BUFFER, verticesVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, vertices, GL_STATIC_DRAW); + // tell OpenGL how to handle the vertex data when rendering the VAO, the vertices will be bound to slot 0. + // (we will tell OpenGL what variable uses slot 0 in the shader!) + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*) 0); + // tell OpenGL we will be using the first VAO slot, prevents us from having to call it before rendering + glEnableVertexAttribArray(0); + + + glBindBuffer(GL_ARRAY_BUFFER, uvsVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, uvs, GL_STATIC_DRAW); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void*) 0); + glEnableVertexAttribArray(1); + + int translations_size = sizeof(particle_record) * particle_count; + glBindBuffer(GL_ARRAY_BUFFER, particleTranslationsVBO); + glBufferData(GL_ARRAY_BUFFER, translations_size, translations.buffer, GL_DYNAMIC_DRAW); // allocate some memory on the GPU + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(particle_record), (void*) 0); + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(particle_record), (void*) offsetof(particle_record, dir)); + // tells opengl that we want to present this data per 1 instance instead of per vertex. + glVertexAttribDivisor(2, 1); + glVertexAttribDivisor(3, 1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesEBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(float) * 6, indices, GL_STATIC_DRAW); + + + instance_shader = new shader(shader_vert, shader_frag, "", true); +} + +void cleanup() { + // cleanup opengl resources + glDeleteVertexArrays(1, &particleVAO); + glDeleteBuffers(1, &particleTranslationsVBO); + glDeleteBuffers(1, &verticesVBO); + glDeleteBuffers(1, &uvsVBO); + glDeleteBuffers(1, &indicesEBO); + + delete(instance_shader); +} diff --git a/src/main.cpp b/src/main.cpp index 841df63..5db0d07 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,12 @@ #define GLAD_GL_IMPLEMENTATION #include #ifdef EXTRAS - #include + //#include + #include #else #include #endif + #include #include @@ -40,6 +42,16 @@ void render_i(){ updateView(); render(); + +#ifdef EXTRAS + std::stringstream str; + str << WINDOW_TITLE; + str << " | Particle Count: "; + str << particle_count; + str << " | FPS: "; + str << 1000000000.0 / (double)getDelta(); + glutSetWindowTitle(str.str().c_str()); +#endif glutSwapBuffers(); cam.inputUpdate(); @@ -55,7 +67,6 @@ int main(int argc, char** argv) { blt::logging::init(logging_properties); - // BLT logging functions are designed to operate one call per line of text. Thus use formatting for all uses // (\n is implicitly added, if the last character in the format string is \n, it will be ignored!) // (\n\n will for instance insert one extra line between the current line and the next, not two!) @@ -83,9 +94,14 @@ int main(int argc, char** argv) { // create the display glutInit(&argc, argv); +#ifdef EXTRAS + glutInitContextVersion(4, 6); + glutInitContextProfile(GLUT_CORE_PROFILE); + glutInitContextFlags(GLUT_FORWARD_COMPATIBLE); +#endif glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT); glutSetOption(GLUT_MULTISAMPLE, 8); - glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_MULTISAMPLE | GLUT_DEPTH); + glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_MULTISAMPLE | GLUT_DEPTH); glutCreateWindow(WINDOW_TITLE.c_str()); BLT_DEBUG("Window successfully created!"); @@ -161,7 +177,7 @@ int main(int argc, char** argv) { init(); - fountain = new particle_system({0, 1, 0}, {0, 1, 0}, 4.5, 100); + fountain = new particle_system({0, 1, 0}, {0, 1, 0}, 4.5, 5000); BLT_DEBUG("Resource initialization complete!"); diff --git a/src/modes/advanced.cpp b/src/modes/advanced.cpp deleted file mode 100644 index 2f96622..0000000 --- a/src/modes/advanced.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Created by Brett on 30/03/23. - * Licensed under GNU General Public License V3.0 - * See LICENSE file for license detail - */ -#include - -#ifdef EXTRAS - -void window_resize(int width, int height) { - -} - -void updateView() { - -} - -void render() { - -} - -void init() { - -} - -void cleanup() { - -} - -#endif \ No newline at end of file