instancing of particles, no movement

main
Brett 2023-04-01 15:48:31 -04:00
parent d66eed3a42
commit 686c966a77
18 changed files with 552 additions and 53 deletions

0
2023-4-1_15-19-50.log Normal file
View File

View File

@ -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 # I like absolute directories since I think relative file paths are ugly and hard to read
include_directories(include) include_directories(include)
include_directories(${CMAKE_BINARY_DIR}/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 # Include my utility library
add_subdirectory(libs/BLT) add_subdirectory(libs/BLT)

View File

@ -6,10 +6,10 @@
#ifndef ASSIGN3_CAMERA_H #ifndef ASSIGN3_CAMERA_H
#define ASSIGN3_CAMERA_H #define ASSIGN3_CAMERA_H
#include <cmath>
#include <blt/math/vectors.h>
#include <GL/glut.h> #include <GL/glut.h>
#include <GL/freeglut.h> #include <GL/freeglut.h>
#include <cmath>
#include <blt/math/vectors.h>
#include <util.h> #include <util.h>
#include "blt/std/logging.h" #include "blt/std/logging.h"
@ -29,6 +29,7 @@ class camera {
const float MAX_SPEED = 100; const float MAX_SPEED = 100;
const float DEFAULT_SPEED = 50; const float DEFAULT_SPEED = 50;
const float MIN_SPEED = 1;
const float ROTATION_SPEED = 3; const float ROTATION_SPEED = 3;
float cur_speed = DEFAULT_SPEED; float cur_speed = DEFAULT_SPEED;
@ -107,6 +108,13 @@ class camera {
else if (specialState[GLUT_KEY_RIGHT]) else if (specialState[GLUT_KEY_RIGHT])
rotation[2] += ((float) horzSpeed * delta * ROTATION_SPEED); 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) if (rotation[2] > 360)
rotation[2] = 0; rotation[2] = 0;
if (rotation[2] < 0) if (rotation[2] < 0)

View File

@ -3033,7 +3033,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
#endif #endif
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* modes type definitions * basic type definitions
*-----------------------------------------------------------------------*/ *-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)

132
include/high_perf/gl_util.h Normal file
View File

@ -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 <glad/gl.h>
#include <blt/math/math.h>
#include <blt/std/string.h>
#include <string>
#include <unordered_map>
/**
* 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<std::string, IntDefaultedToMinusOne> 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

View File

@ -4,17 +4,20 @@
* See LICENSE file for license detail * See LICENSE file for license detail
*/ */
#ifndef ASSIGN3_ADVANCED_H #ifndef ASSIGN3_HIGH_PERF_H
#define ASSIGN3_ADVANCED_H #define ASSIGN3_HIGH_PERF_H
#include <glad/gl.h> #include <glad/gl.h>
#include <camera.h> #include <camera.h>
#include <particle_system.h> #include <particle_system.h>
extern particle_system* fountain; #ifdef EXTRAS
extern int WINDOW_WIDTH; extern int WINDOW_WIDTH;
extern int WINDOW_HEIGHT; extern int WINDOW_HEIGHT;
extern particle_system* fountain;
extern camera cam; extern camera cam;
extern const unsigned int particle_count;
void window_resize(int width, int height); void window_resize(int width, int height);
@ -26,4 +29,6 @@ void init();
void cleanup(); void cleanup();
#endif //ASSIGN3_ADVANCED_H #endif
#endif //ASSIGN3_HIGH_PERF_H

View File

@ -92,6 +92,7 @@ class particle_system {
particle_system( particle_system(
const blt::vec3& position, const blt::vec3& direction, float spread, int pps const blt::vec3& position, const blt::vec3& direction, float spread, int pps
): position(position), direction(direction), spread(spread), pps(pps) { ): position(position), direction(direction), spread(spread), pps(pps) {
#ifndef EXTRAS
quad = glGenLists(1); quad = glGenLists(1);
glNewList(quad, GL_COMPILE); glNewList(quad, GL_COMPILE);
glBegin(GL_QUADS); glBegin(GL_QUADS);
@ -105,6 +106,7 @@ class particle_system {
glVertex3f(s, s, 0); glVertex3f(s, s, 0);
glEnd(); glEnd();
glEndList(); glEndList();
#endif
} }
void update(camera& cam, float bnx, float bnz, float bpx, float bpz) { void update(camera& cam, float bnx, float bnz, float bpx, float bpz) {
@ -173,6 +175,7 @@ class particle_system {
} }
inline static void applyBillboard() { inline static void applyBillboard() {
#ifndef EXTRAS
GLfloat m[16]; GLfloat m[16];
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glGetFloatv(GL_MODELVIEW_MATRIX, m); glGetFloatv(GL_MODELVIEW_MATRIX, m);
@ -187,9 +190,11 @@ class particle_system {
} }
glLoadMatrixf(m); glLoadMatrixf(m);
#endif
} }
void render(camera& cam, texture** textures) { void render(camera& cam, texture** textures) {
#ifndef EXTRAS
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
// by batching particles by texture we save a little driver overhead // by batching particles by texture we save a little driver overhead
for (auto& pair : particles) { for (auto& pair : particles) {
@ -243,6 +248,7 @@ class particle_system {
str << (randomizeTexture ? "True" : "False"); str << (randomizeTexture ? "True" : "False");
glutSetWindowTitle(str.str().c_str()); glutSetWindowTitle(str.str().c_str());
} }
#endif
} }
void randomizeSpeed(float n) { void randomizeSpeed(float n) {

View File

@ -0,0 +1,15 @@
#ifdef __cplusplus
#include <string>
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

View File

@ -0,0 +1,23 @@
#ifdef __cplusplus
#include <string>
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

View File

@ -798,8 +798,8 @@ static int stbi__sse2_available(void)
// //
// stbi__context struct and start_xxx functions // stbi__context struct and start_xxx functions
// stbi__context structure is our modes context used by all images, so it // stbi__context structure is our basic context used by all images, so it
// contains all the IO context, plus some modes image information // contains all the IO context, plus some basic image information
typedef struct typedef struct
{ {
stbi__uint32 img_x, img_y; 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) { for (k=0; k < z->scan_n; ++k) {
int n = z->order[k]; int n = z->order[k];
// scan out an mcu's worth of this component; that's just determined // 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 (y=0; y < z->img_comp[n].v; ++y) {
for (x=0; x < z->img_comp[n].h; ++x) { for (x=0; x < z->img_comp[n].h; ++x) {
int x2 = (i*z->img_comp[n].h + x)*8; 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) { for (k=0; k < z->scan_n; ++k) {
int n = z->order[k]; int n = z->order[k];
// scan out an mcu's worth of this component; that's just determined // 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 (y=0; y < z->img_comp[n].v; ++y) {
for (x=0; x < z->img_comp[n].h; ++x) { for (x=0; x < z->img_comp[n].h; ++x) {
int x2 = (i*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) { if (hsz != 12) {
int compress = stbi__get32le(s); int compress = stbi__get32le(s);
if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); 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 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 sizeof
stbi__get32le(s); // discard hres 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); 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); 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"); return stbi__errpuc("bad format", "Can't find out TGA pixelformat");
// tga info // 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) if (stbi__get16be(s) != 3)
return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); 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) ); stbi__skip(s,stbi__get32be(s) );
// Skip the image resources. (resolution, pen tool paths, etc) // Skip the image resources. (resolution, pen tool paths, etc)

View File

@ -32,6 +32,10 @@ typedef struct {
float x, y, z; float x, y, z;
} vec; } vec;
typedef struct {
float x, y, z, w;
} vec4;
inline vec operator+(const vec& l, const vec& r) { inline vec operator+(const vec& l, const vec& r) {
return {l.x + r.x, l.y + r.y, l.z + r.z}; return {l.x + r.x, l.y + r.y, l.z + r.z};
} }

@ -1 +1 @@
Subproject commit 5d841afe8ca0a8776820eb8593005ca5539205bc Subproject commit 289af1317141c53e04998b32760efced0956db57

2
mangohud.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/bash
mangohud $1

View File

@ -5,7 +5,7 @@
*/ */
#include <modes/basic.h> #include <modes/basic.h>
#ifndef EXTRAS //#ifndef EXTRAS
texture* world_floor; texture* world_floor;
texture* particle_tex[10]; texture* particle_tex[10];
@ -64,4 +64,4 @@
void cleanup() { void cleanup() {
delete world_floor; delete world_floor;
} }
#endif //#endif

156
src/high_perf/gl_util.cpp Normal file
View File

@ -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 <high_perf/gl_util.h>
#include "blt/std/memory.h"
#include <blt/std/loader.h>
/**
* 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<GLchar> infoLog{static_cast<unsigned long>(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<GLchar> infoLog{static_cast<unsigned long>(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;
}

154
src/high_perf/high_perf.cpp Normal file
View File

@ -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 <high_perf/gl_util.h>
#include <modes/high_perf.h>
#include <util.h>
#include <camera.h>
#include "blt/std/memory.h"
#include <shaders/vertex.vert>
#include <shaders/fragment.frag>
//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<particle_record> translations{particle_count};
blt::random<float> 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);
}

View File

@ -1,10 +1,12 @@
#define GLAD_GL_IMPLEMENTATION #define GLAD_GL_IMPLEMENTATION
#include <config.h> #include <config.h>
#ifdef EXTRAS #ifdef EXTRAS
#include <modes/advanced.h> //#include <modes/basic.h>
#include <modes/high_perf.h>
#else #else
#include <modes/basic.h> #include <modes/basic.h>
#endif #endif
#include <camera.h> #include <camera.h>
#include <blt/std/logging.h> #include <blt/std/logging.h>
@ -41,6 +43,16 @@ void render_i(){
render(); 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(); glutSwapBuffers();
cam.inputUpdate(); cam.inputUpdate();
auto curTime = getCurrentTimeNanoseconds(); auto curTime = getCurrentTimeNanoseconds();
@ -55,7 +67,6 @@ int main(int argc, char** argv) {
blt::logging::init(logging_properties); blt::logging::init(logging_properties);
// BLT logging functions are designed to operate one call per line of text. Thus use formatting for all uses // 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 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!) // (\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 // create the display
glutInit(&argc, argv); glutInit(&argc, argv);
#ifdef EXTRAS
glutInitContextVersion(4, 6);
glutInitContextProfile(GLUT_CORE_PROFILE);
glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
#endif
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT); glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutSetOption(GLUT_MULTISAMPLE, 8); 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()); glutCreateWindow(WINDOW_TITLE.c_str());
BLT_DEBUG("Window successfully created!"); BLT_DEBUG("Window successfully created!");
@ -161,7 +177,7 @@ int main(int argc, char** argv) {
init(); 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!"); BLT_DEBUG("Resource initialization complete!");

View File

@ -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 <modes/advanced.h>
#ifdef EXTRAS
void window_resize(int width, int height) {
}
void updateView() {
}
void render() {
}
void init() {
}
void cleanup() {
}
#endif