├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── images └── screenie.png └── src ├── Makefile ├── app ├── Application.cpp ├── Application.h ├── Context.h ├── Window.cpp └── Window.h ├── data ├── particles.comp.glsl ├── particles.frag.glsl └── particles.vert.glsl ├── main.cpp ├── opengl ├── BufferObject.cpp ├── BufferObject.h ├── Camera.cpp ├── Camera.h ├── Enum.h ├── Interface.cpp ├── Interface.h ├── Program.cpp ├── Program.h ├── Shader.cpp ├── Shader.h ├── Texture.cpp ├── Texture.h ├── Use.h ├── VertexBuffer.cpp └── VertexBuffer.h ├── serialization ├── DataParser.cpp ├── DataParser.h ├── DataWriter.cpp ├── DataWriter.h ├── ISerializable.h ├── ObjectNode.cpp ├── ObjectNode.h ├── SymbolicConstants.h ├── Token.h ├── TokenStream.cpp └── TokenStream.h ├── settings.txt └── utils ├── Assert.h ├── File.h ├── Random.cpp └── Random.h /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ 2 | #Compiled object files 3 | *.o 4 | *.obj 5 | *.lo 6 | *.slo 7 | 8 | #Compiled dynamic libraries 9 | *.so 10 | *.dylib 11 | *.dll 12 | 13 | # Executables 14 | *.out 15 | *.app 16 | *.exe 17 | toothie 18 | test 19 | toothie_linux 20 | 21 | # Code::blocks project files 22 | *.cbp 23 | *.layout 24 | *.depend 25 | src/bin 26 | src/obj 27 | 28 | # CodeLite project files 29 | .codelite/ 30 | .clang/ 31 | *.workspace 32 | *.project 33 | particles/ 34 | particles.tags 35 | 36 | # 3d file format 37 | *.ply 38 | *.off 39 | *.obj 40 | 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Johann Muszynski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | 3 | COMPILER_INCLUDES = -I ./src -IC:/dev/glm -IC:/dev/glew-1.12.0/glew-1.12.0/include -IC:/dev/SDL2-devel-2.0.3-mingw/SDL2-2.0.3/x86_64-w64-mingw32/include 4 | 5 | LINKER_INCLUDES = -LC:/dev/SDL2-devel-2.0.3-mingw/SDL2-2.0.3/lib/x86 -LC:/dev/glew-1.12.0/glew-1.12.0/lib 6 | 7 | CFLAGS = -std=c++11 -Wall -O2 -DDEBUG -DGLM_FORCE_RADIANS 8 | 9 | LDFLAGS = 10 | 11 | EXECUTABLE = 12 | 13 | ifeq ($(OS),Windows_NT) 14 | CFLAGS += $(COMPILER_INCLUDES) 15 | LDFLAGS += -lopengl32 -lglew32 -lmingw32 -lSDL2main -lSDL2 $(LINKER_INCLUDES) 16 | EXECUTABLE += app.exe 17 | else 18 | UNAME_S := $(shell uname -s) 19 | ifeq ($(UNAME_S),Linux) 20 | CFLAGS += -I ./src 21 | LDFLAGS += -lGL -lGLEW -lSDL2 22 | EXECUTABLE += app 23 | endif 24 | endif 25 | 26 | OBJ = src/main.o \ 27 | src/app/Window.o \ 28 | src/app/Application.o \ 29 | src/serialization/TokenStream.o \ 30 | src/serialization/DataParser.o \ 31 | src/serialization/ObjectNode.o \ 32 | src/opengl/Shader.o \ 33 | src/opengl/Program.o \ 34 | src/opengl/Interface.o \ 35 | src/opengl/Camera.o \ 36 | src/opengl/BufferObject.o \ 37 | src/opengl/VertexBuffer.o \ 38 | src/opengl/Texture.o \ 39 | src/utils/Random.o \ 40 | 41 | all: $(EXECUTABLE) 42 | 43 | %.o: %.cpp 44 | $(CC) $(CFLAGS) -c -o $@ $< 45 | 46 | $(EXECUTABLE): $(OBJ) 47 | $(CC) -o $(EXECUTABLE) $(OBJ) $(LDFLAGS) 48 | 49 | resources: 50 | cp src/settings.txt . 51 | cp -r src/data . 52 | 53 | .PHONY: clean 54 | 55 | clean: 56 | rm $(OBJ) $(EXECUTABLE) 57 | rm settings.txt 58 | rm -r data/ 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GPU-particles 2 | ![particles in action](https://cdn.rawgit.com/Nelarius/GPU-particles/master/images/screenie.png) 3 | 4 | A small program I wrote for the purpose of learning to use OpenGL compute shaders, and figuring out a good way to organize OpenGL code. 5 | 6 | ## Release 7 | 8 | The release contains a build for Windows only, currently. 9 | 10 | Camera controls: left mouse to orbit, mouse wheel to zoom, right mouse to pan. 11 | 12 | ## Build 13 | 14 | ### Linux 15 | 16 | In the root folder, run 17 | 18 | `make` 19 | 20 | and then 21 | 22 | `make resources` 23 | 24 | to copy the relevant text files. A program called `app` will have appeared in the root. 25 | 26 | ### Windows 27 | 28 | Assumes that you have the MinGW toolchain installed. The `COMPILER_INCLUDES` and `LINKER_INCLUDES` need to be edited so that the compiler can include the required libraries. See below. 29 | 30 | After that is done, the build can be executed as on Linux. 31 | 32 | ## Dependencies 33 | 34 | SDL2 35 | 36 | GLEW 37 | 38 | 39 | -------------------------------------------------------------------------------- /images/screenie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nelarius/GPU-particles/a7e9bc32a9c63d26c9ae7fb8d2b8826bf39a2860/images/screenie.png -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC=i686-w64-mingw32-g++ 2 | 3 | COMPILER_INCLUDES = -I. -IC:/dev/glm -IC:/dev/glew-1.12.0/glew-1.12.0/include -IC:/dev/SDL2-devel-2.0.3-mingw/SDL2-2.0.3/x86_64-w64-mingw32/include 4 | 5 | LINKER_INCLUDES = -LC:/dev/SDL2-devel-2.0.3-mingw/SDL2-2.0.3/lib/x86 -LC:/dev/glew-1.12.0/glew-1.12.0/lib 6 | 7 | CFLAGS = -std=c++11 -Wall -O2 -DDEBUG -DGLM_FORCE_RADIANS $(COMPILER_INCLUDES) 8 | 9 | #LDFLAGS = -lGL -lGLEW -lSDL2 10 | LDFLAGS = -lopengl32 -lglew32 -lmingw32 -lSDL2main -lSDL2 $(LINKER_INCLUDES) 11 | 12 | OBJ = main.o \ 13 | app/Window.o \ 14 | app/Application.o \ 15 | serialization/TokenStream.o \ 16 | serialization/DataParser.o \ 17 | serialization/ObjectNode.o \ 18 | opengl/Shader.o \ 19 | opengl/Program.o \ 20 | opengl/Interface.o \ 21 | opengl/Camera.o \ 22 | opengl/BufferObject.o \ 23 | opengl/VertexBuffer.o \ 24 | opengl/Texture.o \ 25 | utils/Random.o \ 26 | 27 | EXECUTABLE = app.exe 28 | 29 | all: $(EXECUTABLE) 30 | 31 | %.o: %.cpp 32 | $(CC) $(CFLAGS) -c -o $@ $< 33 | 34 | $(EXECUTABLE): $(OBJ) 35 | $(CC) -o $(EXECUTABLE) $(OBJ) $(LDFLAGS) 36 | 37 | .PHONY: clean 38 | 39 | clean: 40 | rm $(OBJ) $(EXECUTABLE) 41 | -------------------------------------------------------------------------------- /src/app/Application.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | using ce::Application; 21 | 22 | namespace { 23 | float AttractorMovementSpeed; 24 | } 25 | 26 | Application::Application() 27 | : running_( false ), 28 | window_(), 29 | context_(), 30 | camera_(), //this will be moved somewhere else eventually 31 | compute_( nullptr ), 32 | display_( nullptr ) 33 | {} 34 | 35 | void Application::run() { 36 | initialize_(); 37 | 38 | /*TEMPORARY TEST STUFF BEGINS HERE*/ 39 | 40 | // compute particle simulator stuff 41 | const unsigned ParticleCount = ParticleGroupSize_ * ParticleGroupCount_; 42 | const unsigned MaxAttractors = 64; 43 | 44 | ce::VertexBuffer positionBuffer{}; 45 | ce::BufferObject velocityBuffer{ GL_ARRAY_BUFFER }; 46 | 47 | ce::Texture positionTbo( GL_TEXTURE_BUFFER ); 48 | ce::Texture velocityTbo( GL_TEXTURE_BUFFER ); 49 | 50 | GLuint renderVao; 51 | glGenVertexArrays( 1, &renderVao ); 52 | glBindVertexArray( renderVao ); 53 | 54 | positionBuffer.dataStore( 55 | ParticleCount * 4 * sizeof(float), 56 | NULL, GL_DYNAMIC_COPY 57 | ); 58 | // the following operation binds positionBuffer to the buffer target 59 | float* posPtr = (float*) positionBuffer.mapBufferRange( 60 | 0, ParticleCount * 4u * sizeof(float), 61 | GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT 62 | ); 63 | for ( std::size_t i = 0u; i < 4u*ParticleCount; i += 4u ) { 64 | posPtr[i] = ce::Randf( -10.0f, 10.0f ); 65 | posPtr[i+1u] = ce::Randf( -10.0f, 10.0f ); 66 | posPtr[i+2u] = ce::Randf( -10.0f, 10.0f ); 67 | posPtr[i+3u] = ce::Randf( 0.0f, 1.0f ); 68 | } 69 | positionBuffer.unmapBuffer(); // and here we implicitly unbind it 70 | 71 | { 72 | ce::UseProgram use( display_ ); 73 | // we render from this buffer, so set the vertex attribute object! 74 | positionBuffer.addAttribute( 75 | display_->attribute("vert"), 76 | 4, 77 | GL_FLOAT, 78 | GL_FALSE, 79 | 0, 80 | 0u 81 | ); 82 | //vbo and vao are bound in here 83 | positionBuffer.enableAttributes(); 84 | } 85 | 86 | velocityBuffer.dataStore( 87 | ParticleCount * 4 * sizeof(float), 88 | NULL, 89 | GL_DYNAMIC_COPY 90 | ); 91 | float* velPtr = (float*) velocityBuffer.mapBufferRange( 92 | 0, ParticleCount * 4 * sizeof(float), 93 | GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT 94 | ); 95 | for ( std::size_t i = 0u; i < 4u*ParticleCount; i += 4u ) { 96 | velPtr[i] = ce::Randf( -0.1f, 0.1f ); 97 | velPtr[i+1u] = ce::Randf( -0.1f, 0.1f ); 98 | velPtr[i+2u] = ce::Randf( -0.1f, 0.1f ); 99 | velPtr[i+3u] = 0.0f; 100 | } 101 | velocityBuffer.unmapBuffer(); 102 | 103 | // why do we now store in the uniform buffer? 104 | ce::BufferObject attractorBuffer( GL_UNIFORM_BUFFER ); 105 | attractorBuffer.dataStore( 106 | MaxAttractors*4*sizeof(float), 107 | NULL, 108 | GL_STATIC_DRAW 109 | ); 110 | float attractorMass[MaxAttractors]; 111 | for ( std::size_t i = 0; i < MaxAttractors; i++ ) { 112 | attractorMass[i] = 0.5f + ce::Randf(0.0f, 1.0f) * 0.5; 113 | } 114 | glBindBufferBase( GL_UNIFORM_BUFFER, 0, attractorBuffer.object() ); 115 | 116 | positionTbo.setStore( GL_RGBA32F, positionBuffer ); 117 | velocityTbo.setStore( GL_RGBA32F, velocityBuffer ); 118 | /*TEMPORARY TEST STUFF ENDS HERE*/ 119 | 120 | running_ = true; 121 | std::chrono::duration> time( 0.0f ); 122 | std::chrono::duration> tdiff( 0.01f ); 123 | while( running_ ) { 124 | auto tstart = std::chrono::steady_clock::now(); 125 | updateContext_(); 126 | 127 | if ( SDL_GetMouseState( NULL, NULL ) & SDL_BUTTON(SDL_BUTTON_LEFT) ) { 128 | camera_.orbit( context_.mouse().dx, context_.mouse().dy ); 129 | } else if ( SDL_GetMouseState( NULL, NULL ) & SDL_BUTTON(SDL_BUTTON_RIGHT) ) { 130 | camera_.translate( context_.mouse().dx, context_.mouse().dy ); 131 | } 132 | 133 | camera_.update( tdiff.count() ); 134 | 135 | /*HANDLE EVENTS HERE*/ 136 | for (auto& event: context_.events_ ) { 137 | if ( event.type == SDL_MOUSEWHEEL ) { 138 | camera_.dolly( 0.5f*event.wheel.y ); 139 | } 140 | } 141 | /*EVENT HANDLING ENDS HERE*/ 142 | 143 | /*DISPLAY CODE BEGINS HERE*/ 144 | // I never unbound the attractor buffer from the GL_UNIFORM_BUFFER target 145 | float* attractors = (float*) attractorBuffer.mapBufferRange( 146 | 0, MaxAttractors * 4 * sizeof(float), 147 | GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT 148 | ); 149 | for ( std::size_t i = 0u; i < 4*MaxAttractors; i += 4u ) { 150 | attractors[i] = sinf(AttractorMovementSpeed*time.count() * (float)(i/4u + 4) * 7.5f * 20.0f) * 30.0f; 151 | attractors[i+1u] = cosf(AttractorMovementSpeed*time.count() * (float)(i/4u + 7) * 3.9f * 20.0f) * 30.0f; 152 | attractors[i+2u] = sinf(AttractorMovementSpeed*time.count() * (float)(i/4u + 3) * 5.3f * 20.0f) * cosf(AttractorMovementSpeed*time.count() * (float)(i + 5) * 9.1f) * 60.0f; 153 | attractors[i+3u] = attractorMass[i / 4u]; 154 | } 155 | attractorBuffer.unmapBuffer(); 156 | 157 | //activate the compute program 158 | { 159 | ce::UseProgram use( compute_ ); 160 | glBindImageTexture( 0, velocityTbo.object(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F ); 161 | glBindImageTexture( 1, positionTbo.object(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F ); 162 | glUniform1f( compute_->uniform("dt"), tdiff.count() ); 163 | glDispatchCompute( ParticleGroupCount_, 1, 1 ); 164 | glMemoryBarrier( GL_SHADER_IMAGE_ACCESS_BARRIER_BIT ); 165 | } 166 | glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 167 | glDisable( GL_DEPTH_TEST ); 168 | 169 | { 170 | ce::UseProgram use( display_ ); 171 | display_->setUniform( "modelViewMatrix", camera_.matrix() ); 172 | positionBuffer.bindVao(); 173 | glEnable( GL_BLEND ); 174 | glBlendFunc( GL_ONE, GL_ONE ); 175 | glDrawArrays( GL_POINTS, 0, ParticleCount ); 176 | positionBuffer.unbindVao(); 177 | } 178 | 179 | auto tend = std::chrono::steady_clock::now(); 180 | tdiff = tend - tstart; 181 | time += tdiff; 182 | /* DISPLAY CODE ENDS HERE*/ 183 | 184 | window_.display(); 185 | } 186 | 187 | glDeleteVertexArrays( 1, &renderVao ); 188 | } 189 | 190 | void Application::initialize_() { 191 | if ( !ce::FileExists( "settings.txt" ) ) { 192 | std::cout << "No settings.txt file found!" << std::endl; 193 | std::exit( 0 ); 194 | } 195 | 196 | std::ifstream fin( "settings.txt", std::ifstream::in ); 197 | ce::DataParser parser( fin ); 198 | parser.parse(); 199 | 200 | ParticleGroupSize_ = unsigned( parser.node( "app" )->number( "particles_per_workgroup" ) ); 201 | ParticleGroupCount_ = unsigned( parser.node( "app" )->number( "workgroups" ) ); 202 | 203 | // a valid openGL context exists after this call 204 | window_.deserialize( parser.node( "app" )->node( "window" ) ); 205 | camera_.deserialize( parser.node( "app" )->node( "camera" ) ); 206 | camera_.setViewportAspectRatio( float(window_.width() ) / window_.height() ); 207 | 208 | AttractorMovementSpeed = float( parser.node("app")->number("attractor_movement_speed") ); 209 | 210 | std::vector shaders; 211 | shaders.emplace_back( 212 | ce::ShaderFromFile( "data/particles.comp.glsl", GL_COMPUTE_SHADER ) 213 | ); 214 | compute_ = ce::Program::ptr( new ce::Program( shaders ) ); 215 | 216 | shaders.clear(); 217 | shaders.emplace_back( 218 | ce::ShaderFromFile( "data/particles.vert.glsl", GL_VERTEX_SHADER ) 219 | ); 220 | shaders.emplace_back( 221 | ce::ShaderFromFile( "data/particles.frag.glsl", GL_FRAGMENT_SHADER ) 222 | ); 223 | display_ = ce::Program::ptr( new ce::Program( shaders ) ); 224 | 225 | } 226 | 227 | void Application::updateContext_() { 228 | context_.events_.clear(); 229 | 230 | // update real-time input 231 | int oldx = context_.mouse_.x; 232 | int oldy = context_.mouse_.y; 233 | int newx, newy; 234 | SDL_GetMouseState( &newx, &newy ); 235 | context_.mouse_.dx = newx - oldx; 236 | context_.mouse_.dy = newy - oldy; 237 | context_.mouse_.x = newx; 238 | context_.mouse_.y = newy; 239 | 240 | // update events 241 | SDL_Event event; 242 | while( SDL_PollEvent( &event ) ) { 243 | // close is the only event that the application shell handles 244 | if ( event.type == SDL_QUIT ) { 245 | running_ = false; 246 | } else { 247 | context_.events_.push_back( event ); 248 | } 249 | } 250 | } 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /src/app/Application.h: -------------------------------------------------------------------------------- 1 | #ifndef APPLICATION_H 2 | #define APPLICATION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ce { 10 | 11 | /// \brief The main application class. 12 | /// All functionality of the application will be dependent on this class. 13 | /// It is the responsibilty of this class to provide the following services: 14 | /// -maintain the list of current events 15 | /// -provide the application window 16 | /// -provide real-time input 17 | /// All of these services are available to application states via the Context-structure. 18 | class Application { 19 | public: 20 | Application(); 21 | ~Application() = default; 22 | 23 | void run(); 24 | 25 | private: 26 | void initialize_(); 27 | void updateContext_(); 28 | 29 | bool running_; 30 | Window window_; 31 | Context context_; 32 | Camera camera_; 33 | Program::ptr compute_; 34 | Program::ptr display_; 35 | 36 | //TEMPORARY STUFF 37 | unsigned ParticleGroupSize_; 38 | unsigned ParticleGroupCount_; 39 | }; 40 | 41 | } 42 | 43 | #endif // APPLICATION_H 44 | -------------------------------------------------------------------------------- /src/app/Context.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTEXT_H_INCLUDED 2 | #define CONTEXT_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | namespace ce { 8 | 9 | struct Mouse { 10 | Mouse() 11 | : x(0), 12 | y(0), 13 | dx(0), 14 | dy(0) 15 | {} 16 | 17 | int x; 18 | int y; 19 | int dx; 20 | int dy; 21 | }; 22 | 23 | /// \brief This holds variables that will be needed by the application components at run time. 24 | struct Context { 25 | 26 | friend class Application; 27 | 28 | Context() 29 | : mouse_() 30 | {} 31 | 32 | /// \brief Get real-time input for the mouse. 33 | Mouse mouse() const { return mouse_; } 34 | /// \brief Get the current events. 35 | const std::vector& getEvents() const { return events_; } 36 | 37 | private: 38 | Mouse mouse_; 39 | std::vector events_; 40 | 41 | }; 42 | 43 | } 44 | 45 | #endif // CONTEXT_H_INCLUDED 46 | -------------------------------------------------------------------------------- /src/app/Window.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using ce::Window; 10 | using ce::ObjectNode; 11 | 12 | Window::Window() 13 | : window_( nullptr ), 14 | glContext_(), 15 | width_( 800 ), 16 | height_( 600 ), 17 | glMajor_( 4 ), 18 | glMinor_( 3 ), 19 | stencilBits_( 8 ), 20 | depthBits_( 24 ), 21 | msBuffer_( 1 ), 22 | msSamples_( 4 ) 23 | {} 24 | 25 | Window::~Window() { 26 | SDL_DestroyWindow( window_ ); 27 | SDL_Quit(); 28 | } 29 | 30 | void Window::deserialize( ObjectNode::node_ptr node ) { 31 | width_ = int( node->number( "screen_width" ) ); 32 | height_ = int( node->number( "screen_height" ) ); 33 | glMajor_ = int( node->number( "opengl_major_version" ) ); 34 | glMinor_ = int( node->number( "opengl_minor_version" ) ); 35 | depthBits_ = int( node->number( "depth_buffer_bits" ) ); 36 | stencilBits_ = int( node->number( "stencil_buffer_bits" ) ); 37 | 38 | initialize_(); 39 | } 40 | 41 | ObjectNode::node_ptr Window::serialize() { 42 | auto node = std::shared_ptr( new ObjectNode( "window" ) ); 43 | node->addNumber( "screen_width", width_ ); 44 | node->addNumber( "screen_height", height_ ); 45 | return node; 46 | } 47 | 48 | void Window::initialize_() { 49 | initializeSDL_(); 50 | initializeOpenGL_(); 51 | } 52 | 53 | void Window::initializeSDL_() { 54 | SDL_Init( SDL_INIT_EVERYTHING ); 55 | 56 | SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, glMajor_ ); 57 | SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, glMinor_ ); 58 | /*SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, 59 | SDL_GL_CONTEXT_PROFILE_CORE );*/ 60 | SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); 61 | SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, depthBits_ ); 62 | SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, stencilBits_ ); 63 | //anti-aliasing 64 | SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 ); 65 | SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 4 ); 66 | 67 | window_ = SDL_CreateWindow( 68 | "crowd engine", 69 | SDL_WINDOWPOS_UNDEFINED, 70 | SDL_WINDOWPOS_UNDEFINED, 71 | width_, height_, 72 | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL 73 | ); 74 | glContext_ = SDL_GL_CreateContext( window_ ); 75 | } 76 | 77 | void Window::initializeOpenGL_() { 78 | //initialize GLEW 79 | glewExperimental = GL_TRUE; 80 | 81 | if ( glewInit() ) { 82 | std::cerr << "glewInit failed." << std::endl; 83 | std::exit( EXIT_FAILURE ); 84 | } 85 | 86 | glEnable( GL_DEPTH_TEST ); 87 | //enable culling 88 | glEnable( GL_CULL_FACE ); 89 | glCullFace( GL_BACK ); 90 | 91 | glDepthFunc( GL_LEQUAL ); 92 | glEnable( GL_TEXTURE_CUBE_MAP_SEAMLESS ); 93 | 94 | std::cout << "OpenGL version: " << glGetString( GL_VERSION ) << std::endl; 95 | std::cout << "GLSL version: " << glGetString( GL_SHADING_LANGUAGE_VERSION ) << std::endl; 96 | std::cout << "Vendor: " << glGetString( GL_VENDOR ) << std::endl; 97 | std::cout << "Renderer: " << glGetString( GL_RENDERER ) << std::endl << std::endl; 98 | } 99 | 100 | void Window::display() { 101 | SDL_GL_SwapWindow( window_ ); 102 | } 103 | 104 | unsigned Window::width() const { 105 | return width_; 106 | } 107 | 108 | unsigned Window::height() const { 109 | return height_; 110 | } 111 | -------------------------------------------------------------------------------- /src/app/Window.h: -------------------------------------------------------------------------------- 1 | #ifndef WINDOW_H 2 | #define WINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | namespace ce { 8 | 9 | /// \brief A platform-independent window. 10 | /// This window contains a valid OpenGL 4.3 context. 11 | class Window: public ISerializable { 12 | public: 13 | /// \brief Construct the window. 14 | /// The OpenGL context and window are ready for use after construction. 15 | Window(); 16 | ~Window(); 17 | 18 | Window( const Window& ) = delete; 19 | Window& operator=( const Window& ) = delete; 20 | Window( Window&& ) = delete; 21 | Window& operator=( Window&& ) = delete; 22 | 23 | void deserialize( ObjectNode::node_ptr ) override; 24 | ObjectNode::node_ptr serialize() override; 25 | 26 | /// \brief Display the window and any changes made to the back buffer. 27 | void display(); 28 | 29 | unsigned width() const; 30 | unsigned height() const; 31 | 32 | private: 33 | void initialize_(); 34 | void initializeSDL_(); 35 | void initializeOpenGL_(); 36 | 37 | SDL_Window* window_; 38 | SDL_GLContext glContext_; 39 | int width_; 40 | int height_; 41 | int glMajor_; 42 | int glMinor_; 43 | int stencilBits_; 44 | int depthBits_; 45 | int msBuffer_; // multisample buffer 46 | int msSamples_; // number of multisamples 47 | 48 | }; 49 | 50 | } //namespace ce 51 | 52 | #endif // WINDOW_H 53 | 54 | -------------------------------------------------------------------------------- /src/data/particles.comp.glsl: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | 3 | layout (std140, binding = 0) uniform attractorBlock 4 | { 5 | vec4 attractor[64]; // xyz = coordinates, w = mass 6 | }; 7 | 8 | layout (local_size_x = 128) in; 9 | 10 | layout (rgba32f, binding = 0) uniform imageBuffer velocityBuffer; 11 | layout (rgba32f, binding = 1) uniform imageBuffer positionBuffer; 12 | 13 | uniform float dt; 14 | 15 | void main() { 16 | // read the current position and velocity from the buffers 17 | vec4 vel = imageLoad(velocityBuffer, int(gl_GlobalInvocationID.x)); 18 | vec4 pos = imageLoad(positionBuffer, int(gl_GlobalInvocationID.x)); 19 | 20 | int i; 21 | 22 | //update position using current velocity 23 | pos.xyz += 5.0*vel.xyz * dt; 24 | pos.w -= 0.004 * dt; //decrease lifetime 25 | 26 | // for each attractor 27 | for ( i = 0; i < 64; i++ ) { 28 | vec3 dist = attractor[i].xyz - pos.xyz; 29 | vel.xyz += dt*attractor[i].w * normalize(dist) / (dot(dist,dist) + 5.0 ); 30 | //pos.xyz += 1000.0*attractor[i]*dt*dt*normalize(dist) / dot(dist, dist); 31 | } 32 | 33 | // if the particle expires, then reset it 34 | if ( pos.w < 0.0001 ) { 35 | pos.xyz = -pos.xyz * 0.1; 36 | vel.xyz *= 0.8; 37 | pos.w += 1.0; 38 | } 39 | 40 | //store the new positions and velocities 41 | imageStore(positionBuffer, int(gl_GlobalInvocationID.x), pos); 42 | imageStore(velocityBuffer, int(gl_GlobalInvocationID.x), vel); 43 | } 44 | -------------------------------------------------------------------------------- /src/data/particles.frag.glsl: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | layout (location = 0) out vec4 color; 3 | in float intensity; 4 | 5 | void main() { 6 | color = mix( vec4(0.0, 0.2, 1.0, 1.0), vec4(0.2, 0.1, 0.0, 1.0), intensity ); 7 | } 8 | -------------------------------------------------------------------------------- /src/data/particles.vert.glsl: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | in vec4 vert; 3 | uniform mat4 modelViewMatrix; 4 | out float intensity; 5 | 6 | void main() { 7 | intensity = vert.w; 8 | gl_Position = modelViewMatrix * vec4(vert.xyz, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main( int argc, char** argv ) { 4 | 5 | ce::Application app; 6 | app.run(); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /src/opengl/BufferObject.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using ce::BufferObject; 5 | 6 | BufferObject::BufferObject( GLenum type ) 7 | : type_( type ) { 8 | glGenBuffers( 1, &object_ ); 9 | } 10 | 11 | BufferObject::~BufferObject() { 12 | glDeleteBuffers( 1, &object_ ); 13 | } 14 | 15 | void BufferObject::dataStore( GLsizeiptr size, const GLvoid* data, int usage ) { 16 | size_ = size; 17 | this->bind(); 18 | glBufferData( type_, size_, data, usage ); 19 | this->unbind(); 20 | } 21 | 22 | void* BufferObject::mapBufferRange(GLintptr offset, GLsizeiptr length, int accessFlag ) { 23 | this->bind(); 24 | return glMapBufferRange( type_, offset, length, accessFlag ); 25 | } 26 | 27 | void BufferObject::unmapBuffer() { 28 | glUnmapBuffer( type_ ); 29 | this->unbind(); 30 | } 31 | 32 | void BufferObject::bind() { 33 | glGetIntegerv( ce::GetBindingTarget( type_ ), &old_ ); 34 | glBindBuffer( type_, object_ ); 35 | } 36 | 37 | void BufferObject::unbind() { 38 | glBindBuffer( type_, old_ ); 39 | } 40 | 41 | GLuint BufferObject::object() const { 42 | return object_; 43 | } 44 | 45 | GLenum BufferObject::type() const { 46 | return type_; 47 | } 48 | 49 | GLsizei BufferObject::size() const { 50 | return size_; 51 | } 52 | -------------------------------------------------------------------------------- /src/opengl/BufferObject.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace ce { 7 | 8 | /// @brief A wrapper for an OpenGL buffer object, living on GPU memory. 9 | class BufferObject { 10 | public: 11 | 12 | /** 13 | * @brief Construct the BufferObject with the type of target it should be bound to. 14 | * @param type The type of target this buffer object will be bound to. 15 | * The following are valid buffer targets: GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, 16 | * GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, GL_PIXEL_UNPACK_BUFFER, GL_PIXEL_PACK_BUFFER, 17 | * GL_QUERY_BUFFER, GL_TEXTURE_BUFFER, GL_TRANSFORM_FEEDBACK_BUFFER, GL_UNIFORM_BUFFER, 18 | * GL_DRAW_INDIRECT_BUFFER, GL_ATOMIC_COUNTER_BUFFER, GL_DISPATCH_INDIRECT_BUFFER, 19 | * GL_SHADER_STORAGE_BUFFER. 20 | * 21 | * See https://www.opengl.org/wiki/Buffer_Object for more information. 22 | */ 23 | explicit BufferObject( GLenum type ); 24 | virtual ~BufferObject(); 25 | 26 | BufferObject() = delete; 27 | BufferObject( const BufferObject& ) = delete; 28 | BufferObject& operator=( const BufferObject&) = delete; 29 | BufferObject( BufferObject&& ) = delete; 30 | BufferObject& operator=( BufferObject&& ) = delete; 31 | 32 | /// @brief Define the data store and optionally copy data to the store. 33 | /// @param size The size in bytes of the new data store 34 | /// @param data A pointer to the data which will be copied into the store. Nothing is copied if it is NULL. 35 | /// @param usage Specifies the expected data store usage pattern. 36 | /// This method should be called with a null pointer before the buffer object is mapped to our memory space. 37 | /// The usage flag can be one of the following: GL_STREAM_DRAW, GL_STREAM_READ, GL_STREAM_COPY, 38 | /// GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_WRITE, GL_DYNAMIC_READ, GL_DYNAMIC_COPY. 39 | /// For more information, see glBufferData doc entry. 40 | void dataStore( GLsizeiptr size, const GLvoid* data, int usage ); 41 | 42 | /// @brief Map the data store memory range to local memory space. 43 | /// @param offset The starting offset within the buffer to be mapped. 44 | /// @param length The length of the range to be mapped. 45 | /// @param accessFlag Specify the access pattern of the mapped range using a combination of flags. 46 | /// accessFlag can be a combination of the following: GL_MAP_READ_BIT, GL_MAP_WRITE_BIT, GL_MAP_PERISTENT_BIT, 47 | /// GL_MAP_COHERENT_BIT, GL_MAP_INVALIDATE_RANGE_BIT, GL_MAP_INVALIDATE_BUFFER_BIT, GL_MAP_FLUSH_EXPLICIT_BIT, 48 | /// GL_MAP_UNSYNCHRONIZED_BIT. See glMapBufferRange doc entry for more info. 49 | void* mapBufferRange( GLintptr offset, GLsizeiptr length, int accessFlag ); 50 | void unmapBuffer(); 51 | 52 | void bind(); 53 | void unbind(); 54 | 55 | GLuint object() const; 56 | GLenum type() const; 57 | GLsizei size() const; 58 | 59 | protected: 60 | GLenum type_{ GL_ARRAY_BUFFER }; // the target that this object will be bound to 61 | GLuint object_{ 0u }; // the OpenGL name of this object 62 | GLsizei size_{ 0u }; // the size of the data store in bytes 63 | GLint old_{ 0u }; 64 | }; 65 | 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/opengl/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | using ce::Camera; 10 | using ce::ObjectNode; 11 | 12 | // hide local implementation 13 | namespace { 14 | const float DegreesToRads = 3.141592653f / 180.0f; 15 | const float Pi = 3.141592653f; 16 | 17 | float Clamp( float val, float min, float max ) { 18 | if ( val < min ) { 19 | return min; 20 | } 21 | if ( val > max ) { 22 | return max; 23 | } 24 | return val; 25 | } 26 | 27 | // t should be limited to [0,1] 28 | float Lerp( float a, float b, float t ) { 29 | t = Clamp( t, 0.0f, 1.0f ); 30 | return a + t*( b - a ); 31 | } 32 | 33 | glm::vec3 Lerp( const glm::vec3& a, const glm::vec3& b, float t ) { 34 | t = Clamp( t, 0.0f, 1.0f ); 35 | return a + t*( b - a ); 36 | } 37 | } 38 | 39 | Camera::Camera() 40 | : verticalFov_( 60.0f * DegreesToRads ), 41 | nearPlane_( 0.1f ), 42 | farPlane_( 1000.0f ), 43 | aspectRatio_( 1.3333333f ), 44 | position_( 0.0f, 0.0f, -10.0f ), 45 | targetPosition_( 0.0f, 0.0f, -10.0f ), 46 | focal_( 0.0f, 0.0f, 0.0f ), 47 | radius_( 40.0f ), 48 | targetRadius_( 60.0f ), 49 | minRadius_( 10.0f ), 50 | maxRadius_( 100.0f ), 51 | zoomFactor_( 0.07f * radius_ ), 52 | targetZoomFactor_( 0.07f * radius_ ), 53 | dollySensitivity_( 1.0f ), 54 | orbitSensitivity_( 1.0f ), 55 | panSensitivity_( 1.0f ), 56 | polar_( Pi / 2.0f ), 57 | azimuth_( 3.0f*Pi / 2.0f ), 58 | viewPerspective_( true ) 59 | {} 60 | 61 | float Camera::fov() const { 62 | return verticalFov_; 63 | } 64 | 65 | void Camera::setFov( float f ) { 66 | ASSERT( f > 0.0f, "error: field of view is negative" ); 67 | verticalFov_ = f; 68 | } 69 | 70 | float Camera::nearPlane() const { 71 | return nearPlane_; 72 | } 73 | 74 | void Camera::setNearPlane( float near ) { 75 | ASSERT( near < farPlane_ && near > 0.0f, "error: near plane is negative or larger than the far plane" ); 76 | nearPlane_ = near; 77 | } 78 | 79 | float Camera::farPlane() const { 80 | return farPlane_; 81 | } 82 | 83 | void Camera::setFarPlane( float far ) { 84 | ASSERT( far > nearPlane_, "error: far plane is less than the near plane" ); 85 | farPlane_ = far; 86 | } 87 | 88 | float Camera::viewportAspectRatio() const { 89 | return aspectRatio_; 90 | } 91 | 92 | void Camera::setViewportAspectRatio( float aspect ) { 93 | ASSERT( aspect > 0.0f, "error: aspect ratio is negative" ); 94 | aspectRatio_ = aspect; 95 | } 96 | 97 | void Camera::viewPerspective( bool p ) { 98 | viewPerspective_ = p; 99 | } 100 | 101 | void Camera::dolly( float movez ) { 102 | targetRadius_ += dollySensitivity_ * radius_ * movez; 103 | if ( targetRadius_ < minRadius_ ) { 104 | targetRadius_ = minRadius_; 105 | } else if ( targetRadius_ > maxRadius_ ) { 106 | targetRadius_ = maxRadius_; 107 | } 108 | } 109 | 110 | void Camera::orbit( float movex, float movey ) { 111 | polar_ -= orbitSensitivity_ * movey; 112 | polar_ = Clamp( polar_, 0.001f, Pi-0.001f ); 113 | azimuth_ -= orbitSensitivity_ * movex; 114 | // limit the azimuth angle to the [0, 2PI] range. 115 | if ( azimuth_< 0.0f ) { 116 | azimuth_ += 2*Pi; 117 | } 118 | if ( azimuth_ > 2*Pi ) { 119 | azimuth_ -= 2*Pi; 120 | } 121 | } 122 | 123 | void Camera::translate( float movex, float movey ) { 124 | focal_ += - panSensitivity_ * radius_ * movex * right(); 125 | focal_ += panSensitivity_ * radius_ * movey * up(); 126 | } 127 | 128 | void Camera::update( float dt ) { 129 | /* 130 | * target targetZoomFactor_ defines how wide the orthographic proejection is 131 | * our projection width is scaled by our current radius, in order to simulate 132 | * "zooming" in and out in the ortho mode 133 | * 134 | * this value was arrived at by trial and error 135 | * using this value, the objects in view do not change size when switching to ortho 136 | */ 137 | targetZoomFactor_ = 0.07f * targetRadius_; 138 | 139 | // update values 140 | zoomFactor_ = Lerp( zoomFactor_, targetZoomFactor_, Clamp( 20.0f*dt, 0.0f, 1.0f ) ); 141 | radius_ = Lerp( radius_, targetRadius_, Clamp( 20.0f*dt, 0.0f, 1.0f ) ); 142 | 143 | // calculate unit offset vector 144 | glm::vec3 offset = glm::vec3( 145 | sinf( polar_ ) * sinf( azimuth_ ), 146 | cosf( polar_ ), 147 | sinf( polar_ ) * cosf( azimuth_ ) 148 | ); 149 | // scale it by the radius 150 | targetPosition_ = radius_ * offset + focal_; 151 | position_ = Lerp( position_, targetPosition_, Clamp( 20.0f*dt, 0.0f, 1.0f ) ); 152 | } 153 | 154 | glm::vec3 Camera::forward() const { 155 | glm::vec3 n = glm::normalize( position_ ); 156 | return glm::vec3( -n[0], -n[1], -n[2] ); 157 | } 158 | 159 | glm::vec3 Camera::right() const { 160 | float x = radius_ * sinf( polar_ ) * cosf( azimuth_ ); 161 | float z = -radius_ * sinf( polar_ ) * sinf( azimuth_ ); 162 | float n = sqrt( x*x + z*z ); 163 | return glm::vec3( x/n, 0.0f, z/n ); 164 | } 165 | 166 | glm::vec3 Camera::up() const { 167 | return glm::cross( right(), forward() ); 168 | } 169 | 170 | glm::mat4 Camera::view() const { 171 | return glm::lookAt( position_, focal_, up() ); 172 | } 173 | 174 | glm::mat4 Camera::projection() const { 175 | if ( viewPerspective_ ) { 176 | return glm::perspective( verticalFov_, aspectRatio_, nearPlane_, farPlane_ ); 177 | } else { 178 | // return orthogonal projection 179 | } 180 | } 181 | 182 | glm::mat4 Camera::matrix() const { 183 | return projection() * view(); 184 | } 185 | 186 | void Camera::deserialize( ObjectNode::node_ptr node ) { 187 | verticalFov_ = float( node->number("vertical_fov") ); 188 | nearPlane_ = float( node->number("near_plane") ); 189 | farPlane_ = float( node->number("far_plane") ); 190 | minRadius_ = float( node->number("min_radius") ); 191 | maxRadius_ = float( node->number("max_radius") ); 192 | dollySensitivity_ = float( node->number("dolly_sensitivity") ); 193 | orbitSensitivity_ = float( node->number("orbit_sensitivity") ); 194 | panSensitivity_ = float( node->number("pan_sensitivity") ); 195 | std::string pers = node->string( "view_as_perspective" ); 196 | if ( pers == "true" ) { 197 | viewPerspective_ = true; 198 | } else if ( pers == "false" ) { 199 | viewPerspective_ = false; 200 | } 201 | } 202 | 203 | ObjectNode::node_ptr Camera::serialize() { 204 | auto node = std::shared_ptr( new ObjectNode( "camera" ) ); 205 | node->addNumber( "vertical_fov", verticalFov_ ); 206 | node->addNumber( "near_plane", nearPlane_ ); 207 | node->addNumber( "far_plane", farPlane_ ); 208 | node->addNumber( "min_radius", minRadius_ ); 209 | node->addNumber( "max_radius", maxRadius_ ); 210 | node->addNumber( "dolly_sensitivity", dollySensitivity_ ); 211 | node->addNumber( "orbit_sensitivity", orbitSensitivity_ ); 212 | node->addNumber( "pan_sensitivity", panSensitivity_ ); 213 | if ( viewPerspective_ ) { 214 | node->addString( "view_as_perspective", "true" ); 215 | } else { 216 | node->addString( "view_as_perspective", "false" ); 217 | } 218 | return node; 219 | } 220 | 221 | 222 | -------------------------------------------------------------------------------- /src/opengl/Camera.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_H 2 | #define CAMERA_H 3 | 4 | #include 5 | #include 6 | 7 | namespace ce { 8 | 9 | class Camera: public ISerializable { 10 | public: 11 | Camera(); 12 | ~Camera() = default; 13 | 14 | float fov() const; 15 | /// \brief Set the vertical field of view. 16 | /// Value must be greater than zero! 17 | /// \return f The vertical field-of-view, specified in degrees. 18 | void setFov( float f ); 19 | 20 | float nearPlane() const; 21 | /// \brief Set the closest visible distance from the camera. 22 | /// Must be less than the current far plane. 23 | void setNearPlane( float near ); 24 | 25 | float farPlane() const; 26 | /// \brief Set the farthest visible distance from the camera. 27 | /// Must be greater than the current near plane. 28 | void setFarPlane( float far ); 29 | 30 | float viewportAspectRatio() const; 31 | void setViewportAspectRatio( float aspect ); 32 | 33 | /// \brief Toggle the projection mode between perspective and ortho. 34 | /// \param isPerspective perspective projection is used when true, ortho projection otherwise. 35 | void viewPerspective( bool isPerspective ); 36 | 37 | /// \brief Dolly along the the local z-axis. 38 | void dolly( float movez ); 39 | 40 | /// \brief Orbit around the focal point. 41 | void orbit( float movex, float movey ); 42 | 43 | /// \brief Translate the camera in the local x and y axis. 44 | void translate( float movex, float movey ); 45 | 46 | /// \brief Update the camera 47 | /// In a true ECS, this logic would be owned by the system. The system would handle inputs as well. 48 | void update( float dt ); 49 | 50 | glm::vec3 forward() const; 51 | glm::vec3 right() const; 52 | glm::vec3 up() const; 53 | 54 | glm::mat4 view() const; 55 | glm::mat4 projection() const; 56 | glm::mat4 matrix() const; //combined view and projection matrix 57 | 58 | void deserialize( ObjectNode::node_ptr ) override; 59 | ObjectNode::node_ptr serialize() override; 60 | 61 | private: 62 | float verticalFov_; 63 | float nearPlane_; 64 | float farPlane_; 65 | float aspectRatio_; 66 | glm::vec3 position_; 67 | glm::vec3 targetPosition_; 68 | glm::vec3 focal_; 69 | float radius_; //the distance from the camera's focal point 70 | float targetRadius_; 71 | float minRadius_; 72 | float maxRadius_; 73 | float zoomFactor_; 74 | float targetZoomFactor_; 75 | float dollySensitivity_; 76 | float orbitSensitivity_; 77 | float panSensitivity_; 78 | float polar_; 79 | float azimuth_; 80 | bool viewPerspective_; 81 | }; 82 | 83 | } // namespace ce 84 | 85 | #endif // CAMERA_H 86 | -------------------------------------------------------------------------------- /src/opengl/Enum.h: -------------------------------------------------------------------------------- 1 | #ifndef ENUM_OPENGL_H 2 | #define ENUM_OPENGL_H 3 | 4 | namespace ce { 5 | 6 | inline GLenum GetBindingTarget( GLenum type ) { 7 | switch( type ) { 8 | case GL_ARRAY_BUFFER: return GL_ARRAY_BUFFER_BINDING; 9 | case GL_ELEMENT_ARRAY_BUFFER: return GL_ELEMENT_ARRAY_BUFFER_BINDING; 10 | // these don't exist on linux for some reason 11 | //case GL_COPY_READ_BUFFER: return GL_COPY_READ_BUFFER_BINDING; 12 | //case GL_COPY_WRITE_BUFFER: return GL_COPY_WRITE_BUFFER_BINDING; 13 | case GL_PIXEL_UNPACK_BUFFER: return GL_PIXEL_UNPACK_BUFFER_BINDING; 14 | case GL_PIXEL_PACK_BUFFER: return GL_PIXEL_PACK_BUFFER_BINDING; 15 | case GL_QUERY_BUFFER: return GL_QUERY_BUFFER_BINDING; 16 | case GL_TRANSFORM_FEEDBACK_BUFFER: return GL_TRANSFORM_FEEDBACK_BINDING; 17 | case GL_UNIFORM_BUFFER: return GL_UNIFORM_BUFFER_BINDING; 18 | case GL_DRAW_INDIRECT_BUFFER: return GL_DRAW_INDIRECT_BUFFER_BINDING; 19 | case GL_ATOMIC_COUNTER_BUFFER: return GL_ATOMIC_COUNTER_BUFFER_BINDING; 20 | case GL_DISPATCH_INDIRECT_BUFFER: return GL_DISPATCH_INDIRECT_BUFFER_BINDING; 21 | case GL_SHADER_STORAGE_BUFFER: return GL_SHADER_STORAGE_BUFFER_BINDING; 22 | case GL_TEXTURE_BUFFER: return GL_TEXTURE_BINDING_BUFFER; 23 | case GL_TEXTURE_1D: return GL_TEXTURE_BINDING_1D; 24 | case GL_TEXTURE_2D: return GL_TEXTURE_BINDING_2D; 25 | case GL_TEXTURE_3D: return GL_TEXTURE_BINDING_3D; 26 | case GL_TEXTURE_1D_ARRAY: return GL_TEXTURE_BINDING_1D_ARRAY; 27 | case GL_TEXTURE_2D_ARRAY: return GL_TEXTURE_BINDING_2D_ARRAY; 28 | case GL_TEXTURE_RECTANGLE: return GL_TEXTURE_BINDING_RECTANGLE; 29 | case GL_TEXTURE_CUBE_MAP: return GL_TEXTURE_BINDING_CUBE_MAP; 30 | case GL_TEXTURE_CUBE_MAP_ARRAY: return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY; 31 | case GL_TEXTURE_2D_MULTISAMPLE: return GL_TEXTURE_BINDING_2D_MULTISAMPLE; 32 | case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: return GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY; 33 | } 34 | 35 | } 36 | 37 | } 38 | 39 | #endif //ENUM_OPENGL_H 40 | -------------------------------------------------------------------------------- /src/opengl/Interface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using ce::Shader; 5 | 6 | Shader::ptr ce::ShaderFromFile( const std::string& file, GLenum shaderType ) { 7 | return Shader::ptr( new Shader( ce::FileToString(file), shaderType ) ); 8 | } 9 | -------------------------------------------------------------------------------- /src/opengl/Interface.h: -------------------------------------------------------------------------------- 1 | #ifndef INTERFACE_H_INCLUDED 2 | #define INTERFACE_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | //an interface with the file system 8 | //load resources, etc. 9 | namespace ce { 10 | 11 | /// \brief Load a shader from a file. 12 | /// \return Pointer to the shader. 13 | Shader::ptr ShaderFromFile( const std::string& file, GLenum shaderType ); 14 | 15 | } 16 | 17 | #endif // INTERFACE_H_INCLUDED 18 | -------------------------------------------------------------------------------- /src/opengl/Program.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include //for std::exit 6 | 7 | using ce::Program; 8 | using ce::Shader; 9 | 10 | Program::Program( const std::vector& shaders ) { 11 | if ( shaders.size() == 0u ) { 12 | std::cerr << "No shaders were provided to create the program.\n"; 13 | std::exit( EXIT_SUCCESS ); 14 | } 15 | 16 | object_ = glCreateProgram(); 17 | ASSERT( object_ != 0, "error: glCreateProgram() failed." ); 18 | 19 | for ( auto& shader: shaders ) { 20 | glAttachShader( object_, shader->object() ); 21 | } 22 | 23 | glLinkProgram( object_ ); 24 | 25 | //check the linking status 26 | GLint status; 27 | glGetProgramiv( object_, GL_LINK_STATUS, &status ); 28 | if ( status == GL_FALSE ) { 29 | std::string msg("Program linking failure: "); 30 | 31 | GLint infoLogLength; 32 | glGetProgramiv( object_, GL_INFO_LOG_LENGTH, &infoLogLength ); 33 | char* infoLogStr = new char[ infoLogLength + 1 ]; 34 | glGetProgramInfoLog( object_, infoLogLength, NULL, infoLogStr ); 35 | msg += infoLogStr; 36 | delete [] infoLogStr; 37 | 38 | glDeleteProgram( object_ ); 39 | object_ = 0; 40 | ASSERT( false, msg.c_str() ); 41 | } 42 | } 43 | 44 | Program::~Program() { 45 | glDeleteProgram( object_ ); 46 | object_ = 0; 47 | } 48 | 49 | GLuint Program::object() const { 50 | return object_; 51 | } 52 | 53 | GLint Program::attribute( const GLchar* attribName ) const { 54 | ASSERT( attribName, "error: attribName was NULL" ); 55 | 56 | GLint attrib = glGetAttribLocation( object_, attribName ); 57 | ASSERT( attrib != -1, "error: program attribute not found" ); 58 | 59 | return attrib; 60 | } 61 | 62 | GLint Program::uniform( const GLchar* uniformName ) const { 63 | ASSERT( uniformName, "error: uniformName was NULL" ); 64 | 65 | GLint uniform = glGetUniformLocation( object_, uniformName ); 66 | #ifdef DEBUG 67 | std::string msg( "error: program uniform " ); 68 | msg += uniformName; 69 | msg += " not found"; 70 | ASSERT( uniform != -1, msg.c_str() ); 71 | #endif // DEBUG 72 | return uniform; 73 | } 74 | 75 | GLint Program::subroutineUniform( const GLchar* uniformName, GLenum shaderType ) const { 76 | ASSERT( uniformName, "error: subroutine uniform name was null" ); 77 | GLint uniform = glGetSubroutineUniformLocation( object_, shaderType, uniformName ); 78 | #ifdef DEBUG 79 | std::string msg( "error: subroutine uniform " ); 80 | msg += uniformName; 81 | msg += " not found"; 82 | ASSERT( uniform != -1, msg.c_str() ); 83 | #endif // DEBUG 84 | return uniform; 85 | } 86 | 87 | GLuint Program::subroutineIndex( const GLchar* functionName, GLenum shaderType ) const { 88 | ASSERT( functionName, "error: subroutine function name was null" ); 89 | GLuint index = glGetSubroutineIndex( object_, shaderType, functionName ); 90 | #ifdef DEBUG 91 | std::string msg("error: active subroutine " ); 92 | msg += functionName; 93 | msg += " not found"; 94 | ASSERT( index != GL_INVALID_INDEX, msg.c_str() ); 95 | #endif // DEBUG 96 | return index; 97 | } 98 | 99 | void Program::use() const { 100 | glUseProgram( object_ ); 101 | } 102 | 103 | bool Program::isInUse() const { 104 | GLint currentProgram = 0; 105 | glGetIntegerv( GL_CURRENT_PROGRAM, ¤tProgram ); 106 | return ( currentProgram == (GLint) object_ ); 107 | } 108 | 109 | void Program::stopUsing() const { 110 | ASSERT( isInUse(), "error: cannot stop using because program is not in use" ); 111 | glUseProgram( 0 ); 112 | } 113 | 114 | void Program::setUniform( const GLchar* name, GLint v0 ) const { 115 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 116 | glUniform1i( uniform(name), v0 ); 117 | } 118 | 119 | void Program::setUniform( const GLchar* name, GLint v0, GLint v1 ) const { 120 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 121 | glUniform2i( uniform(name), v0, v1); 122 | } 123 | 124 | void Program::setUniform( const GLchar* name, GLint v0, GLint v1, GLint v2 ) const { 125 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 126 | glUniform3i( uniform(name), v0, v1, v2 ); 127 | } 128 | 129 | void Program::setUniform( const GLchar* name, GLint v0, GLint v1, GLint v2, GLint v3 ) const { 130 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 131 | glUniform4i( uniform(name), v0, v1, v2, v3 ); 132 | } 133 | 134 | void Program::setUniform( const GLchar* name, GLfloat v0 ) const { 135 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 136 | glUniform1f( uniform(name), v0); 137 | } 138 | 139 | void Program::setUniform( const GLchar* name, GLfloat v0, GLfloat v1 ) const { 140 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 141 | glUniform2f( uniform(name), v0, v1 ); 142 | } 143 | 144 | void Program::setUniform( const GLchar* name, GLfloat v0, GLfloat v1, GLfloat v2 ) const { 145 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 146 | glUniform3f( uniform(name), v0, v1, v2 ); 147 | } 148 | 149 | void Program::setUniform( const GLchar* name, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 ) const 150 | { 151 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 152 | glUniform4f( uniform(name), v0, v1, v2, v3 ); 153 | } 154 | 155 | void Program::setUniform( const GLchar* name, const glm::vec2& v ) const 156 | { 157 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 158 | glUniform2fv( uniform(name), 1, glm::value_ptr( v ) ); 159 | } 160 | 161 | void Program::setUniform( const GLchar* name, const glm::vec3& v ) const 162 | { 163 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 164 | glUniform3fv( uniform(name), 1, glm::value_ptr( v ) ); 165 | } 166 | 167 | void Program::setUniform( const GLchar* name, const glm::vec4& v ) const 168 | { 169 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 170 | glUniform4fv( uniform(name), 1, glm::value_ptr( v ) ); 171 | } 172 | 173 | void Program::setUniform( const GLchar* name, const glm::mat3& m, GLboolean transpose ) const { 174 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 175 | glUniformMatrix3fv( uniform(name), 1, transpose, glm::value_ptr( m ) ); 176 | } 177 | 178 | void Program::setUniform( const GLchar* name, const glm::mat4& m, GLboolean transpose ) const { 179 | ASSERT( isInUse(), "Program::setUniform> program not in use" ); 180 | glUniformMatrix4fv( uniform(name), 1, transpose, glm::value_ptr( m ) ); 181 | } 182 | 183 | -------------------------------------------------------------------------------- /src/opengl/Program.h: -------------------------------------------------------------------------------- 1 | #ifndef PROGRAM_H 2 | #define PROGRAM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ce { 10 | 11 | /// \brief Contains a compiled and linked OpenGL shader program. 12 | class Program { 13 | public: 14 | typedef std::unique_ptr ptr; 15 | 16 | Program() = delete; 17 | /// \brief Construct the program from a vector of shader objects 18 | Program( const std::vector& shaders ); 19 | ~Program(); 20 | 21 | //make uncopyable for now 22 | Program( const Program& ) = delete; 23 | Program( Program&& ) = delete; 24 | Program& operator=( const Program& ) = delete; 25 | Program& operator=( Program&& ) = delete; 26 | 27 | /// \brief Get the OpenGL handle. 28 | GLuint object() const; 29 | 30 | GLint attribute( const GLchar* attribName ) const; 31 | 32 | GLint uniform( const GLchar* uniformName ) const; 33 | 34 | GLint subroutineUniform( const GLchar* uniformName, GLenum shaderType ) const; 35 | 36 | GLuint subroutineIndex( const GLchar* functionName, GLenum shaderType ) const; 37 | 38 | /// \brief Use this shader. 39 | void use() const; 40 | bool isInUse() const; 41 | void stopUsing() const; 42 | 43 | /// A wrapper for glUniform*i 44 | void setUniform( const GLchar*, GLint ) const; 45 | void setUniform( const GLchar*, GLint, GLint ) const; 46 | void setUniform( const GLchar*, GLint, GLint, GLint ) const; 47 | void setUniform( const GLchar*, GLint, GLint, GLint, GLint ) const; 48 | /// A wrapper for glUniform*f 49 | void setUniform( const GLchar*, GLfloat ) const; 50 | void setUniform( const GLchar*, GLfloat, GLfloat ) const; 51 | void setUniform( const GLchar*, GLfloat, GLfloat, GLfloat ) const; 52 | void setUniform( const GLchar*, GLfloat, GLfloat, GLfloat, GLfloat ) const; 53 | /// A wrapper for glUniform*fv 54 | void setUniform( const GLchar*, const glm::vec2& ) const; 55 | void setUniform( const GLchar*, const glm::vec3& ) const; 56 | void setUniform( const GLchar*, const glm::vec4& ) const; 57 | 58 | /// A wrapper for glUniformMatrix*fv 59 | void setUniform( const GLchar*, const glm::mat3&, GLboolean transpose = GL_FALSE ) const; 60 | void setUniform( const GLchar*, const glm::mat4&, GLboolean transpose = GL_FALSE ) const; 61 | 62 | private: 63 | GLuint object_{ 0u }; 64 | }; 65 | 66 | 67 | } //namespace ce 68 | 69 | #endif // PROGRAM_H 70 | -------------------------------------------------------------------------------- /src/opengl/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using ce::Shader; 5 | 6 | Shader::Shader( const std::string& shaderCode, GLenum shaderType ) { 7 | //create the shader object, returns 0 on error 8 | object_ = glCreateShader( shaderType ); 9 | ASSERT( object_ != 0, "error: glCreateShader() failed." ); 10 | 11 | const char* code = shaderCode.c_str(); 12 | glShaderSource( object_, 1, (const GLchar**)& code, NULL ); 13 | 14 | glCompileShader( object_ ); 15 | 16 | //check for compilation error 17 | GLint status; 18 | glGetShaderiv( object_, GL_COMPILE_STATUS, &status ); 19 | if (status == GL_FALSE ) { 20 | std::string msg("Compile failure\n"); 21 | GLint infoLogLength; 22 | glGetShaderiv( object_, GL_INFO_LOG_LENGTH, &infoLogLength ); 23 | char* infoLogStr = new char[ infoLogLength + 1 ]; 24 | glGetShaderInfoLog( object_, infoLogLength, NULL, infoLogStr ); 25 | msg += infoLogStr; 26 | delete [] infoLogStr; 27 | 28 | glDeleteShader( object_ ); 29 | object_ = 0; 30 | ASSERT( false, msg.c_str() ); 31 | } 32 | } 33 | 34 | Shader::~Shader() { 35 | glDeleteShader( object_ ); 36 | object_ = 0; 37 | } 38 | 39 | GLuint Shader::object() const { 40 | return object_; 41 | } 42 | -------------------------------------------------------------------------------- /src/opengl/Shader.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_H 2 | #define SHADER_H 3 | 4 | #include 5 | #include 6 | 7 | namespace ce { 8 | 9 | /// \brief Contains an OpenGL shader object. 10 | class Shader { 11 | public: 12 | typedef std::unique_ptr ptr; 13 | 14 | Shader() = delete; 15 | 16 | /// \brief Construct a shader from a string source. 17 | Shader( const std::string& shaderCode, GLenum shaderType ); 18 | ~Shader(); 19 | 20 | //make uncopyable for now 21 | Shader( const Shader& ) = delete; 22 | Shader( Shader&& ) = delete; 23 | Shader& operator=( const Shader& ) = delete; 24 | Shader& operator=( Shader&& ) = delete; 25 | 26 | /// \brief Get the OpenGL handle. 27 | GLuint object() const; 28 | 29 | private: 30 | GLuint object_{ 0u }; 31 | }; 32 | 33 | } 34 | 35 | #endif // SHADER_H 36 | -------------------------------------------------------------------------------- /src/opengl/Texture.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using ce::BufferObject; 6 | using ce::Texture; 7 | 8 | namespace { 9 | 10 | /* 11 | * This function exists, because when querying what is in the current target, 12 | * we cant't query with the target's GLenum type - rather, we have to use a variation 13 | * of it, with the word BINDING in it :( 14 | * */ 15 | /*GLenum GetBindingTarget( GLenum type ) { 16 | switch( type ) { 17 | case GL_TEXTURE_BUFFER: return GL_TEXTURE_BINDING_BUFFER; 18 | } 19 | }*/ 20 | 21 | } 22 | 23 | Texture::Texture( GLenum type ) 24 | : object_( 0 ), 25 | old_( 0 ), 26 | type_( type ) { 27 | glGenTextures( 1, &object_ ); 28 | } 29 | 30 | Texture::~Texture() { 31 | glDeleteTextures( 1, &object_ ); 32 | } 33 | 34 | void Texture::setStore( GLenum internalFormat, const BufferObject& object ) { 35 | this->bind(); 36 | glTexBuffer( type_, internalFormat, object.object() ); 37 | this->unbind(); 38 | } 39 | 40 | void Texture::bind() { 41 | glGetIntegerv( ce::GetBindingTarget( type_ ), &old_ ); 42 | glBindTexture( type_, object_ ); 43 | } 44 | 45 | void Texture::unbind() { 46 | glBindTexture( type_, old_ ); 47 | } 48 | 49 | GLuint Texture::object() const { 50 | return object_; 51 | } 52 | -------------------------------------------------------------------------------- /src/opengl/Texture.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | namespace ce { 7 | 8 | class BufferObject; 9 | 10 | class Texture { 11 | public: 12 | /// @brief Construct the texture. 13 | /// @param type The target that you want to bind this object to. 14 | /// Valid targets are GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_1D_ARRAY, 15 | /// GL_TEXTURE_2D_ARRAY, GL_TEXTURE_RECTANGLE, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_ARRAY, 16 | /// GL_TEXTURE_BUFFER, GL_TEXTURE_2D_MULTISAMPLE or GL_TEXTURE_2D_MULTISAMPLE_ARRAY. 17 | /// For more information, see the glBindTexture doc entry 18 | explicit Texture( GLenum type ); 19 | ~Texture(); 20 | 21 | Texture() = delete; 22 | Texture( const Texture& ) = delete; 23 | Texture( Texture&& ) = delete; 24 | Texture& operator=( const Texture& ) = delete; 25 | Texture& operator=( Texture&& ) = delete; 26 | 27 | void setStore( GLenum internalFormat, const BufferObject& object ); 28 | 29 | void bind(); 30 | void unbind(); 31 | GLuint object() const; 32 | 33 | private: 34 | GLuint object_; 35 | GLint old_; 36 | GLenum type_; 37 | }; 38 | 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/opengl/Use.h: -------------------------------------------------------------------------------- 1 | #ifndef USE_H_INCLUDED 2 | #define USE_H_INCLUDED 3 | 4 | namespace ce { 5 | 6 | /// \brief A RAII for OpenGL resources which have to bound to the current context. 7 | /// The type T is a pointer to the OpenGL resource. 8 | /// The underlying type T has to have the use() and stopUsing() methods defined and implemented. 9 | template 10 | class Use { 11 | public: 12 | Use( T& t ) 13 | : resource_( t ) 14 | { resource_->use(); } 15 | 16 | ~Use() { resource_->stopUsing(); } 17 | 18 | private: 19 | T& resource_; 20 | 21 | }; 22 | 23 | #include 24 | typedef Use UseProgram; 25 | 26 | } 27 | 28 | 29 | #endif // USE_H_INCLUDED 30 | -------------------------------------------------------------------------------- /src/opengl/VertexBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using ce::VertexBuffer; 5 | using ce::VBOAttribute; 6 | 7 | VertexBuffer::VertexBuffer() 8 | : BufferObject( GL_ARRAY_BUFFER ) { 9 | glGenVertexArrays( 1, &arrayObject_ ); 10 | } 11 | 12 | VertexBuffer::~VertexBuffer() { 13 | glDeleteVertexArrays( 1, &arrayObject_ ); 14 | } 15 | 16 | void VertexBuffer::addStandardAttribute( int flag ) { 17 | GLint program; 18 | glGetIntegerv( GL_CURRENT_PROGRAM, &program ); 19 | 20 | for ( int i = 0; i < VBOAttribute::Count; i++ ) { 21 | if ( flag & VBOAttribute::Position ) { 22 | GLint attrib = glGetAttribLocation( program, "vert" ); 23 | this->addAttribute( 24 | attrib, 3, GL_FLOAT, GL_FALSE, 0, 0u 25 | ); 26 | } else if ( flag & VBOAttribute::Normal ) { 27 | GLint attrib = glGetAttribLocation( program, "norm" ); 28 | this->addAttribute( 29 | attrib, 3, GL_FLOAT, GL_FALSE, 0, 0u // THERE MUST BE AN OFFSET 30 | ); 31 | } else if ( flag & VBOAttribute::Color ) { 32 | GLint attrib = glGetAttribLocation( program, "color" ); 33 | this->addAttribute( 34 | attrib, 3, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0u 35 | ); 36 | } 37 | } 38 | } 39 | 40 | void VertexBuffer::addAttribute( 41 | GLuint index, 42 | GLint size, 43 | GLenum type, 44 | GLboolean normalized, 45 | GLsizei stride, 46 | std::size_t offset 47 | ) { 48 | attributes_[ attribPos_++ ] = VertexAttribute( 49 | index, 50 | size, 51 | type, 52 | normalized, 53 | stride, 54 | offset 55 | ); 56 | } 57 | 58 | void VertexBuffer::bindVao() { 59 | glGetIntegerv( GL_VERTEX_ARRAY_BINDING, &old_ ); 60 | glBindVertexArray( arrayObject_ ); 61 | } 62 | 63 | void VertexBuffer::unbindVao() { 64 | glBindVertexArray( old_ ); 65 | } 66 | 67 | /* 68 | * The GL_ARRAY_BUFFER_BINDING is NOT part of the VAO state. 69 | * The attribute index will get its data from whatever object is currently bound 70 | * to the target. This association is made when glVertexAttribPointer is called. Only 71 | * a call to this function can change the object association. 72 | * */ 73 | void VertexBuffer::enableAttributes() { 74 | this->bind(); 75 | this->bindVao(); 76 | for ( std::size_t i = 0u; i < attribPos_; i++ ) { 77 | this->enableAttribute_( i ); 78 | } 79 | this->unbindVao(); 80 | this->unbind(); 81 | } 82 | 83 | void VertexBuffer::enableAttribute_( std::size_t i ) { 84 | const VertexAttribute& attrib = attributes_[i]; 85 | glVertexAttribPointer( 86 | attrib.index, 87 | attrib.size, 88 | attrib.type, 89 | attrib.normalized, 90 | attrib.stride, 91 | ( const GLvoid* ) attrib.offset 92 | ); 93 | glEnableVertexAttribArray( attrib.index ); 94 | } 95 | 96 | void VertexBuffer::disableAttributes() { 97 | for ( std::size_t i = 0u; i < attribPos_; i++ ) { 98 | this->disableAttribute_( i ); 99 | } 100 | } 101 | 102 | void VertexBuffer::disableAttribute_( std::size_t i ) { 103 | const VertexAttribute& attrib = attributes_[i]; 104 | glDisableVertexAttribArray( attrib.index ); 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/opengl/VertexBuffer.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include //for std::size_t 6 | 7 | #define MAX_VBO_ATTRIBUTES 16 8 | 9 | namespace ce { 10 | 11 | enum VBOAttribute { 12 | Position = (1 << 0), 13 | Normal = (1 << 1), 14 | Color = (1 << 2), 15 | Count = 3 16 | }; 17 | 18 | /** 19 | * @brief A buffer object suitable to use as a source of vertex data. 20 | * An example of the use of this class might be the following: 21 | * @code 22 | * VertexBuffer buffer{}; 23 | * buffer.dataStore( 10, data, GL_STATIC_DRAW ); 24 | * buffer.addAttribute( shader->attribute("vertex"), 4, GL_FLOAT, GL_FALSE, 0, 0 ); 25 | * buffer.enableAttributes(); 26 | * //later, in drawing code 27 | * shader->use(); 28 | * buffer.bind(); 29 | * glDrawArrays(...); 30 | * buffer.unbind(); 31 | * shader->stopUsing(); 32 | * @endcode 33 | * */ 34 | class VertexBuffer : public BufferObject { 35 | public: 36 | VertexBuffer(); 37 | // TODO: create initializer-list constructor 38 | ~VertexBuffer(); 39 | 40 | void bindVao(); 41 | void unbindVao(); 42 | 43 | void addAttribute( 44 | GLuint index, GLint size, GLenum type, 45 | GLboolean normalized, GLsizei stride, std::size_t offset = 0u 46 | ); 47 | 48 | /** 49 | * @brief Add A standard attribute such as vertex, normal, or color. 50 | * TODO: this method is still under construction. 51 | * The number of elements contained in the buffer object must be specified. 52 | * @param flag The logical combination of VBOAttribute enums. 53 | */ 54 | void addStandardAttribute( int flag ); 55 | 56 | void enableAttributes(); 57 | void disableAttributes(); 58 | 59 | private: 60 | void enableAttribute_( std::size_t index ); 61 | void disableAttribute_( std::size_t index ); 62 | 63 | struct VertexAttribute { 64 | GLuint index; 65 | GLint size; 66 | GLenum type; 67 | GLboolean normalized; 68 | GLsizei stride; 69 | std::size_t offset; 70 | 71 | VertexAttribute() = default; 72 | VertexAttribute( 73 | GLuint _index, 74 | int _size, 75 | GLenum _type, 76 | bool norm, 77 | GLsizei _stride, 78 | std::size_t _offset 79 | ) 80 | : index( _index ), 81 | size( _size ), 82 | type( _type ), 83 | normalized( norm ), 84 | stride( _stride ), 85 | offset( _offset ) 86 | {} 87 | }; 88 | 89 | VertexAttribute attributes_[MAX_VBO_ATTRIBUTES]; 90 | GLuint arrayObject_{ 0u }; 91 | GLint old_{ 0 }; 92 | std::size_t attribPos_{ 0u }; 93 | }; 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /src/serialization/DataParser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using ce::DataParser; 6 | using ce::Token; 7 | using ce::ObjectNode; 8 | 9 | DataParser::DataParser( std::istream& in ) 10 | : tstream_( in ), 11 | in_( in ) 12 | {} 13 | 14 | void DataParser::parse() { 15 | ASSERT( in_.good(), "error: input stream is no good." ); 16 | 17 | while ( in_.good() ) { 18 | Token t = tstream_.get(); 19 | // discard all newlines 20 | while ( t.kind == '\n' ) { 21 | t = tstream_.get(); 22 | } 23 | 24 | // handle end of file 25 | if ( t.kind == ce::symbol::Eof ) { 26 | break; 27 | } 28 | 29 | // terrible hack 30 | // for some reason there is no end of file, instead the parser thinks the name 'M' 31 | // is being defined at the end 32 | if ( t.kind == ce::symbol::Name && t.string == "M" ) { 33 | break; 34 | } 35 | 36 | // EOF doesn't seem to be detected properly in release mode 37 | // I need to work around it 38 | 39 | tstream_.putback( t ); 40 | auto node = assignment_(); 41 | nodeMap_.insert( std::make_pair( node->name(), node ) ); 42 | } 43 | } 44 | 45 | ObjectNode::node_ptr DataParser::assignment_() { 46 | // the first token must be a name 47 | Token t = tstream_.get(); 48 | if ( t.kind == ce::symbol::Name ) { 49 | const std::string field( t.string ); 50 | // here should be the assignment operator 51 | t = tstream_.get(); 52 | if ( t.kind == ce::symbol::Assignment ) { 53 | // get rid of the opening delimiter 54 | tstream_.get(); 55 | return objectTraversal_( field ); 56 | } else { 57 | std::string msg = "error: expected assignment operator after "; 58 | msg += field; 59 | ASSERT( false, msg.c_str() ); 60 | } 61 | } else { 62 | ASSERT( false, "error: expected field name" ); 63 | } 64 | } 65 | 66 | ObjectNode::node_ptr DataParser::objectTraversal_( const std::string& name ) { 67 | auto node = ObjectNode::node_ptr( new ObjectNode( name ) ); 68 | 69 | while( true ) { 70 | Token t = tstream_.get(); 71 | 72 | //handle possible newlines 73 | while ( t.kind == '\n' ) { 74 | t = tstream_.get(); 75 | } 76 | 77 | // handle the end of object declaration 78 | if ( t.kind == ce::symbol::ObjEnd ) { 79 | break; 80 | } 81 | 82 | // here must be a name, or else syntax error 83 | if ( t.kind == ce::symbol::Name ) { 84 | const std::string field( t.string ); 85 | Token t2 = tstream_.get(); 86 | // here must be the assignment operator, or else syntax error 87 | if ( t2.kind == ce::symbol::Assignment ) { 88 | // here can be three different tokens: 89 | // a number, a string, or ObjStart delimiter 90 | Token t3 = tstream_.get(); 91 | if ( t3.kind == ce::symbol::ObjStart ) { 92 | node->addNode( field, objectTraversal_( field ) ); 93 | } else if ( t3.kind == ce::symbol::Number ) { 94 | node->addNumber( field, t3.value ); 95 | } else if ( t3.kind == ce::symbol::String ) { 96 | node->addString( field, t3.string ); 97 | } 98 | } else { 99 | ASSERT( false, "error: expected assignment operator after %s in object %s" ); 100 | } 101 | } else { 102 | ASSERT( false, "error: expected name in object %s" ); 103 | } //if 104 | } // while 105 | return node; 106 | } 107 | 108 | const ObjectNode::node_map& DataParser::getNodes() const { 109 | return nodeMap_; 110 | } 111 | 112 | ObjectNode::node_ptr DataParser::node( const std::string& field ) const { 113 | return nodeMap_.at( field ); 114 | } 115 | 116 | bool DataParser::contains( const std::string& field ) const { 117 | auto it = nodeMap_.find( field ); 118 | if ( it != nodeMap_.end() ) { 119 | return true; 120 | } 121 | return false; 122 | } 123 | -------------------------------------------------------------------------------- /src/serialization/DataParser.h: -------------------------------------------------------------------------------- 1 | #ifndef DATAPARSER_H 2 | #define DATAPARSER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ce { 10 | 11 | class DataParser { 12 | public: 13 | DataParser( std::istream& in ); 14 | ~DataParser() = default; 15 | 16 | DataParser( const DataParser& ) = delete; 17 | DataParser& operator=( const DataParser& ) = delete; 18 | DataParser( DataParser&& ) = delete; 19 | DataParser& operator=( DataParser&& ) = delete; 20 | 21 | void parse(); 22 | 23 | const ObjectNode::node_map& getNodes() const; 24 | /// \brief Get the specific node. 25 | ObjectNode::node_ptr node( const std::string& field ) const; 26 | /// \brief Check if the parser encountered the specific object. 27 | bool contains( const std::string& field ) const; 28 | 29 | private: 30 | // METHODS 31 | // handles node assignment on file level 32 | ObjectNode::node_ptr assignment_(); 33 | // traverses into a node's description 34 | // adds all described fields to the node 35 | // including nested nodes ( this function is recursive ) 36 | // returns a node, containing all fields and nested nodes 37 | ObjectNode::node_ptr objectTraversal_( const std::string& parent ); 38 | 39 | // FIELDS 40 | ObjectNode::node_map nodeMap_; 41 | TokenStream tstream_; 42 | std::istream& in_; 43 | 44 | }; 45 | 46 | } 47 | 48 | #endif // DATAPARSER_H 49 | -------------------------------------------------------------------------------- /src/serialization/DataWriter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using ce::DataWriter; 4 | -------------------------------------------------------------------------------- /src/serialization/DataWriter.h: -------------------------------------------------------------------------------- 1 | #ifndef DATAWRITER_H 2 | #define DATAWRITER_H 3 | 4 | namespace ce { 5 | 6 | class DataWriter { 7 | public: 8 | DataWriter() = default; 9 | ~DataWriter() = default; 10 | 11 | private: 12 | }; 13 | 14 | } 15 | 16 | #endif // DATAWRITER_H 17 | -------------------------------------------------------------------------------- /src/serialization/ISerializable.h: -------------------------------------------------------------------------------- 1 | #ifndef ISERIALIZABLE_H_INCLUDED 2 | #define ISERIALIZABLE_H_INCLUDED 3 | 4 | #include 5 | 6 | namespace ce { 7 | 8 | class ISerializable { 9 | public: 10 | virtual void deserialize( ObjectNode::node_ptr ) = 0; 11 | virtual ObjectNode::node_ptr serialize() = 0; 12 | }; 13 | 14 | } 15 | 16 | #endif // ISERIALIZABLE_H_INCLUDED 17 | -------------------------------------------------------------------------------- /src/serialization/ObjectNode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // for std::exit() 5 | #include 6 | 7 | using ce::ObjectNode; 8 | 9 | ObjectNode::ObjectNode( const std::string& name ) 10 | : name_( name ) 11 | {} 12 | 13 | void ObjectNode::addNode( const std::string& name, node_ptr node ) { 14 | nodes_.insert( std::make_pair( name, node ) ); 15 | } 16 | 17 | void ObjectNode::addNumber( const std::string& name, double value ) { 18 | numbers_.insert( std::make_pair( name, value) ); 19 | } 20 | 21 | void ObjectNode::addString( const std::string& name, const std::string& str ) { 22 | strings_.insert( std::make_pair( name, str ) ); 23 | } 24 | 25 | bool ObjectNode::isNumber( const std::string& name ) const { 26 | auto it = numbers_.find( name ); 27 | if ( it != numbers_.end() ) { 28 | return true; 29 | } 30 | return false; 31 | } 32 | 33 | bool ObjectNode::isString( const std::string& name ) const { 34 | auto it = strings_.find( name ); 35 | if ( it != strings_. end() ) { 36 | return true; 37 | } 38 | return false; 39 | } 40 | 41 | bool ObjectNode::isObjectNode( const std::string& name ) const { 42 | auto it = nodes_.find( name ); 43 | if ( it != nodes_.end() ) { 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | bool ObjectNode::contains( const std::string& name ) const { 50 | if ( isNumber( name ) || isObjectNode( name ) || isString( name ) ) { 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | float ObjectNode::number( const std::string& field ) const { 57 | #ifdef DEBUG 58 | //ASSERT( isNumber(field), std::sprintf("error: no such number %s", field.c_str()) ); 59 | #endif // DEBUG 60 | return numbers_.at( field ); 61 | } 62 | 63 | const std::string& ObjectNode::string( const std::string& field ) const { 64 | #ifdef DEBUG 65 | //ASSERT( isString(field), std::sprintf("error: no such string %s", field.c_str()) ); 66 | #endif // DEBUG 67 | return strings_.at( field ); 68 | } 69 | 70 | ObjectNode::node_ptr ObjectNode::node( const std::string& field ) const { 71 | #ifdef DEBUG 72 | //ASSERT( isObjectNode( field ), std::sprintf("error: no such node %s", field.c_str()) ); 73 | #endif 74 | return nodes_.at( field ); 75 | } 76 | 77 | const std::string& ObjectNode::name() const { 78 | return name_; 79 | } 80 | 81 | const ObjectNode::number_map& ObjectNode::getNumbers() const { 82 | return numbers_; 83 | } 84 | 85 | const ObjectNode::string_map& ObjectNode::getStrings() const { 86 | return strings_; 87 | } 88 | 89 | const ObjectNode::node_map& ObjectNode::getNodes() const { 90 | return nodes_; 91 | } 92 | -------------------------------------------------------------------------------- /src/serialization/ObjectNode.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECTNODE_H_INCLUDED 2 | #define OBJECTNODE_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ce { 9 | 10 | /// \brief A node in an object hierarchy. 11 | /// The ObjectNode can store numbers, strings, or other ObjectNodes. 12 | class ObjectNode { 13 | public: 14 | typedef std::map number_map; 15 | typedef std::map string_map; 16 | typedef std::shared_ptr node_ptr; 17 | typedef std::map node_map; 18 | typedef std::map::iterator node_iterator; 19 | 20 | explicit ObjectNode( const std::string& name ); 21 | ~ObjectNode() = default; 22 | 23 | void addNode( const std::string&, node_ptr node ); 24 | void addNumber( const std::string&, double value ); 25 | void addString( const std::string&, const std::string& ); 26 | 27 | /// \brief Get the number value of the field. 28 | float number( const std::string& field ) const; 29 | /// \brief Get the string value of the field. 30 | const std::string& string( const std::string& field ) const; 31 | /// \brief Get the node pointer of the field. 32 | node_ptr node( const std::string& field ) const; 33 | 34 | /// \brief Check to see if the field is a number 35 | bool isNumber( const std::string& field ) const; 36 | 37 | /// \brief Check to see if the field is a string 38 | bool isString( const std::string& field ) const; 39 | 40 | /// \brief Check to see if the field is an ObjectNode 41 | bool isObjectNode( const std::string& field ) const; 42 | 43 | /// \brief Check whether the node contains the name. 44 | bool contains( const std::string& name ) const; 45 | 46 | /// \brief Get the name of this node. 47 | const std::string& name() const; 48 | 49 | const number_map& getNumbers() const; 50 | const string_map& getStrings() const; 51 | const node_map& getNodes() const; 52 | 53 | private: 54 | const std::string name_; 55 | number_map numbers_; 56 | string_map strings_; 57 | node_map nodes_; 58 | 59 | }; 60 | 61 | } //namespace ce 62 | 63 | 64 | #endif // OBJECTNODE_H_INCLUDED 65 | -------------------------------------------------------------------------------- /src/serialization/SymbolicConstants.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMBOLICCONSTANTS_H_INCLUDED 2 | #define SYMBOLICCONSTANTS_H_INCLUDED 3 | 4 | namespace ce { 5 | 6 | namespace symbol { 7 | 8 | const char Number = 'v'; 9 | const char String = 's'; 10 | const char Comment = '#'; 11 | const char Name = 'n'; // the name of a field 12 | const char Assignment = ':'; 13 | const char Eof = 'e'; 14 | const char StrDelimiter = '\"'; 15 | const char ObjStart = '{'; // the start delimiter of object declaration 16 | const char ObjEnd = '}'; // the end delimiter of object declaration 17 | } 18 | 19 | } 20 | 21 | #endif // SYMBOLICCONSTANTS_H_INCLUDED 22 | -------------------------------------------------------------------------------- /src/serialization/Token.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKEN_H_INCLUDED 2 | #define TOKEN_H_INCLUDED 3 | 4 | #include 5 | 6 | namespace ce { 7 | 8 | /// \brief A token can represent a symbol, or a value, or a string 9 | struct Token { 10 | char kind; 11 | float value; 12 | std::string string; 13 | 14 | Token( char ch ) 15 | : kind( ch ), 16 | value( 0.0 ), 17 | string( "" ) 18 | {} 19 | 20 | Token( char ch, double val ) 21 | : kind( ch ), 22 | value( val ), 23 | string( "" ) 24 | {} 25 | 26 | Token( char ch, const std::string& str ) 27 | : kind( ch ), 28 | value( 0.0 ), 29 | string( str ) 30 | {} 31 | }; 32 | 33 | } 34 | 35 | 36 | 37 | #endif // TOKEN_H_INCLUDED 38 | -------------------------------------------------------------------------------- /src/serialization/TokenStream.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include //for sprintf 6 | 7 | #include 8 | 9 | using ce::TokenStream; 10 | using ce::Token; 11 | 12 | TokenStream::TokenStream( std::istream& in ) 13 | : buffer_(), 14 | in_( in ) 15 | {} 16 | 17 | Token TokenStream::get() { 18 | if ( !buffer_.empty() ) { 19 | Token t = buffer_.front(); 20 | buffer_.pop(); 21 | return t; 22 | } 23 | 24 | char ch; 25 | 26 | //check for end of file 27 | if ( in_.eof() ) { 28 | return Token( ce::symbol::Eof ); 29 | } 30 | 31 | in_ >> ch; 32 | 33 | checkForComments_( &ch ); 34 | 35 | switch( ch ) { 36 | case '\n': 37 | case ce::symbol::Assignment: 38 | case ce::symbol::ObjStart: 39 | case ce::symbol::ObjEnd: 40 | return Token( ch ); //let each operator represent itself 41 | case EOF: 42 | return Token( ce::symbol::Eof ); 43 | //numeric literal can start with a point 44 | case '.': 45 | case '0': case '1': case '2': case '3': case '4': 46 | case '5': case '6': case '7': case '8': case '9': { 47 | //stick the leading digit or point of number back, and read it 48 | //from stream as a floating point literal 49 | in_.putback( ch ); 50 | double val; 51 | in_ >> val; 52 | return Token( ce::symbol::Number, val ); 53 | } 54 | 55 | // handle string 56 | case ce::symbol::StrDelimiter: { 57 | std::string str; 58 | while ( in_.get( ch ) && ch != ce::symbol::StrDelimiter ) { 59 | str += ch; 60 | } 61 | return Token( ce::symbol::String, str ); 62 | } 63 | 64 | default: { 65 | if ( isalpha( ch ) ) { 66 | std::string str; 67 | str += ch; 68 | // this defines a string: 69 | // it must begin with an alphanumeric character 70 | // after that, it can contain alphabetical characters, 71 | // numbers, or underscores 72 | while ( in_.get( ch ) && 73 | ( (isalpha( ch ) || isdigit( ch ) ) || ch == '_' ) ) { 74 | str += ch; 75 | } 76 | in_.putback( ch ); 77 | return Token( ce::symbol::Name, str ); 78 | } else { 79 | // this is a horrific hack 80 | // on windows, EOF doesn't seem to be detected. 81 | // instead, return an EOF when we start to encounter garbage 82 | return Token( ce::symbol::Eof ); 83 | } 84 | } 85 | } 86 | } 87 | 88 | void TokenStream::putback( const Token& token ) { 89 | buffer_.push( token ); 90 | } 91 | 92 | void TokenStream::checkForComments_( char* ch ) { 93 | while( true ) { 94 | if ( *ch == ce::symbol::Comment ) { 95 | eatComment_(); 96 | } else { 97 | break; 98 | } 99 | // get the next char 100 | // prevent operator>>() from eating the newline 101 | /*if ( in_.peek() == '\n' ) { 102 | *ch = in_.get(); 103 | } else { 104 | in_ >> *ch; 105 | }*/ 106 | in_ >> *ch; 107 | } 108 | } 109 | 110 | void TokenStream::eatComment_() { 111 | char ch; 112 | while ( in_.get( ch ) && ch != '\n' ); 113 | } 114 | -------------------------------------------------------------------------------- /src/serialization/TokenStream.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKENSTREAM_H_INCLUDED 2 | #define TOKENSTREAM_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ce { 9 | 10 | class TokenStream { 11 | public: 12 | /// \brief Construct the Tokenstream from a reference to the source input stream 13 | TokenStream( std::istream& in ); 14 | ~TokenStream() = default; 15 | 16 | TokenStream( const TokenStream& ) = delete; 17 | TokenStream& operator=( const TokenStream& ) = delete; 18 | TokenStream( TokenStream&& ) = delete; 19 | TokenStream& operator=( TokenStream&& ) = delete; 20 | 21 | Token get(); 22 | 23 | void putback( const Token& t ); 24 | 25 | private: 26 | 27 | // METHODS 28 | void checkForComments_( char* ); 29 | void eatComment_(); 30 | 31 | // FIELDS 32 | std::queue buffer_; 33 | std::istream& in_; 34 | }; 35 | 36 | } //namespace ce 37 | 38 | 39 | #endif // TOKENSTREAM_H_INCLUDED 40 | -------------------------------------------------------------------------------- /src/settings.txt: -------------------------------------------------------------------------------- 1 | 2 | app: { 3 | 4 | # I was able to raise this up to 65125! 5 | particles_per_workgroup : 128 6 | workgroups: 20000 7 | attractor_movement_speed: 0.00005 8 | 9 | window: { 10 | screen_width: 1800 11 | screen_height: 950 12 | 13 | opengl_major_version: 4 14 | opengl_minor_version: 3 15 | 16 | stencil_buffer_bits: 8 17 | depth_buffer_bits: 24 18 | } 19 | 20 | camera : { 21 | vertical_fov: 45.0 # degrees! 22 | near_plane: 0.1 23 | far_plane: 5000.0 24 | min_radius: 5.0 25 | max_radius: 500.0 26 | dolly_sensitivity: 0.1 27 | orbit_sensitivity: 0.008 28 | pan_sensitivity: 0.001 29 | view_as_perspective: "true" 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/utils/Assert.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSERT_H_INCLUDED 2 | #define ASSERT_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | //defined as a do-while so that a semicolon can be used 8 | #ifdef DEBUG 9 | #define ASSERT(condition, message) \ 10 | do { \ 11 | if (! (condition)) { \ 12 | std::cerr << "Assertion '" #condition "' failed in " << __FILE__ \ 13 | << " line " << __LINE__ << ": " << message << std::endl; \ 14 | std::exit(EXIT_FAILURE); \ 15 | } \ 16 | } while (false) \ 17 | 18 | #else 19 | #define ASSERT(condition, message) do {} while (false) 20 | #endif 21 | 22 | 23 | #endif // ASSERT_H_INCLUDED 24 | -------------------------------------------------------------------------------- /src/utils/File.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_H_INCLUDED 2 | #define FILE_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace ce { 11 | 12 | /// \brief Check whether a file of the given name exists. 13 | inline bool FileExists( const std::string& file ) { 14 | struct stat buffer; 15 | return ( stat( file.c_str(), &buffer) == 0 ); 16 | } 17 | 18 | /// \brief Get the contents of a file as a string. 19 | inline std::string FileToString( const std::string& file ) { 20 | std::ifstream fin; 21 | fin.open( file, std::ios::in ); 22 | std::string msg = "error: no such file "; 23 | msg += file; 24 | ASSERT( FileExists( file ), msg.c_str() ); 25 | 26 | std::stringstream buffer; 27 | buffer << fin.rdbuf(); 28 | return buffer.str(); 29 | } 30 | 31 | } 32 | 33 | #endif // FILE_H_INCLUDED 34 | -------------------------------------------------------------------------------- /src/utils/Random.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // hide the implementation details 9 | namespace { 10 | 11 | std::minstd_rand& global_urng() { 12 | static std::minstd_rand u; 13 | return u; 14 | } 15 | 16 | } 17 | 18 | void ce::Randomize() { 19 | std::random_device rd{}; 20 | global_urng().seed( rd() ); 21 | } 22 | 23 | std::int32_t ce::Randi( std::int32_t a, std::int32_t b ) { 24 | static std::uniform_int_distribution<> d{}; 25 | using parm_t = decltype(d)::param_type; 26 | return d( global_urng(), parm_t{ a, b } ); 27 | } 28 | 29 | float ce::Randf( float a, float b ) { 30 | static std::uniform_real_distribution<> d{}; 31 | using parm_t = decltype(d)::param_type; 32 | return d( global_urng(), parm_t{ a, b } ); 33 | } 34 | 35 | float ce::Randf() { 36 | return ce::Randf( 0.0f, 1.0f ); 37 | } 38 | 39 | double ce::Randd( double a, double b ) { 40 | static std::uniform_real_distribution<> d{}; 41 | using parm_t = decltype(d)::param_type; 42 | return d( global_urng(), parm_t{ a, b } ); 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/Random.h: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_H_INCLUDED 2 | #define RANDOM_H_INCLUDED 3 | 4 | #include 5 | 6 | namespace ce { 7 | 8 | /// \brief Seed the generator with a random sequence drawn from the system's random device. 9 | /// The underlying generator is the new Park-Miller minimum standard linear congruential generator. 10 | void Randomize(); 11 | 12 | /// \brief Get a random uint32 in [ a, b ]. 13 | std::int32_t Randi( std::int32_t a, std::int32_t b ); 14 | 15 | /// \brief Get a random float in [ a, b ). 16 | float Randf( float a, float b ); 17 | 18 | /// \brief Get a random float in [ 0, 1 ). 19 | float Randf(); 20 | 21 | /// \brief Get a random double in [ a, b ). 22 | double Randd( double a, double b ); 23 | } 24 | 25 | #endif // RANDOM_H_INCLUDED 26 | --------------------------------------------------------------------------------