instancing of particles, no movement
parent
d66eed3a42
commit
686c966a77
|
@ -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)
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
#ifndef ASSIGN3_CAMERA_H
|
||||
#define ASSIGN3_CAMERA_H
|
||||
|
||||
#include <cmath>
|
||||
#include <blt/math/vectors.h>
|
||||
#include <GL/glut.h>
|
||||
#include <GL/freeglut.h>
|
||||
#include <cmath>
|
||||
#include <blt/math/vectors.h>
|
||||
#include <util.h>
|
||||
#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)
|
||||
|
|
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 <glad/gl.h>
|
||||
#include <camera.h>
|
||||
#include <particle_system.h>
|
||||
|
||||
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
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
|
2
libs/BLT
2
libs/BLT
|
@ -1 +1 @@
|
|||
Subproject commit 5d841afe8ca0a8776820eb8593005ca5539205bc
|
||||
Subproject commit 289af1317141c53e04998b32760efced0956db57
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
mangohud $1
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
#include <modes/basic.h>
|
||||
|
||||
#ifndef EXTRAS
|
||||
//#ifndef EXTRAS
|
||||
texture* world_floor;
|
||||
texture* particle_tex[10];
|
||||
|
||||
|
@ -64,4 +64,4 @@
|
|||
void cleanup() {
|
||||
delete world_floor;
|
||||
}
|
||||
#endif
|
||||
//#endif
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
24
src/main.cpp
24
src/main.cpp
|
@ -1,10 +1,12 @@
|
|||
#define GLAD_GL_IMPLEMENTATION
|
||||
#include <config.h>
|
||||
#ifdef EXTRAS
|
||||
#include <modes/advanced.h>
|
||||
//#include <modes/basic.h>
|
||||
#include <modes/high_perf.h>
|
||||
#else
|
||||
#include <modes/basic.h>
|
||||
#endif
|
||||
|
||||
#include <camera.h>
|
||||
#include <blt/std/logging.h>
|
||||
|
||||
|
@ -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!");
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue