Jump to content
  • Advertisement

Ookamix

Member
  • Content Count

    8
  • Joined

  • Last visited

Community Reputation

1 Neutral

About Ookamix

  • Rank
    Newbie

Personal Information

  • Role
    Programmer
  • Interests
    Art
    Audio
    Programming

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. It stores the amount of quads which are in one quad struct. I just realized that the amount of vertices %20 would be enough to find it t so I removed it. I guess the other vector of quads is kind of unnecessary. I will best make a struct for multiple quads. I have worked on bigger 2d games in java and am already experienced it in, but I am fairly new to C++, that's true. Since i wanted to improve my c++ skills I have been looking for a 2d framework close to opengl, so where you draw yourself and have a render loop... But no framework really statisfied me, and when you look at this: http://nivrigdev.tumblr.com/post/137158222896/game-engine-benchmarking you see how badly optimized c++ frameworks are (I also want to target mobile platforms in the future) In godot I had a tilemap with like 100 sprites created in the editor and I just coded a camera which moves, and it already started lagging. Quickly did the same thing very dirty in libgdx and it ran much smoother without any optimization. But well godot isn't a framework anyways. Maybe OpenGL is a bit too hard for me right now.
  2. Thanks for the tips. Yeah my array was too small, I increased it. Yeah it was intended to do that but I have to store the combined quads somewhere too If I store it somewhere else I will end up doing vectors glfloat arrays again which doesn't work. Umm what's the purpose of a spritebatch if every sprite is the same and at the same position? I mean I have to change the change the position somehow or else the sprite will just overlap themselves. I just wanted to have the batching done now as I'm very used to using a batch in libgdx. I just thought it would be good to make provisions against bad performance.
  3. Alright, I have followed your approach and created a struct for each Quad. If it's not performant with cache and stuff I can change it in the future, but I will want to make it work with this first because this code is as clear as it can get. Right now, everything but rendering same textures. This is the Quad struct: #ifndef QUAD_H #define QUAD_H #include "Texture.h" struct Quad { GLfloat vertices[100] = { 0.0f }; GLuint indices[600] = { 0, 1, 3, 1, 2, 3 }; Texture *texture; glm::mat4 transform; }; #endif Don't know how hight to set the array size. And this is my SpriteBatch Header: #ifndef SPRITEBATCH_H #define SPRITEBATCH_H #include <glm/glm.hpp> #include "Texture.h" #include <GL/glew.h> #include "Camera.h" #include "Shader.h" #include <vector> #include "Quad.h" class SpriteBatch { public: SpriteBatch(Shader& shader, Camera &camera); ~SpriteBatch(); void draw(Texture *texture, GLfloat x, GLfloat y, GLfloat width, GLfloat height); void flush(); private: GLuint VBO = 0, VAO = 0, EBO = 0; GLint transformShaderLocation, viewShaderLocation, projectionShaderLocation; Shader *shader; Camera *camera; std::vector<std::vector<Quad>> batchedQuads; std::vector<Quad> combinedQuads; std::vector<int> batchedQuadsContentQuadAmount; glm::mat4 projection, view; int currentIndex{ 0 }; }; #endif And class: #include "SpriteBatch.h" SpriteBatch::SpriteBatch(Shader& shader, Camera &camera) { this->shader = &shader; this->camera = &camera; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); } SpriteBatch::~SpriteBatch() { glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glDeleteBuffers(1, &VAO); } void SpriteBatch::draw(Texture *texture, GLfloat x, GLfloat y, GLfloat width, GLfloat height) { Quad quad; quad.texture = texture; quad.transform = glm::translate(quad.transform, glm::vec3(x, y, 0)); quad.vertices[0] = width/2 ; quad.vertices[1] = height/2; quad.vertices[2] = 0.0f; quad.vertices[3] = 1.0f; quad.vertices[4] = 1.0f; quad.vertices[5] = width / 2; quad.vertices[6] = -height / 2; quad.vertices[7] = 0.0f; quad.vertices[8] = 1.0f; quad.vertices[9] = 0.0f; quad.vertices[10] = -width / 2; quad.vertices[11] = -height / 2; quad.vertices[12] = 0.0f; quad.vertices[13] = 0.0f; quad.vertices[14] = 0.0f; quad.vertices[15] = -width / 2; quad.vertices[16] = height / 2; quad.vertices[17] = 0.0f; quad.vertices[18] = 0.0f; quad.vertices[19] = 1.0f; //Sort this quad into either a new list of quads which should be rendered or adds it to the last list if the texture is the same, so that they can be rendered together if (currentIndex != 0) { if (batchedQuads.at(currentIndex - 1).at(0).texture == texture) { batchedQuads.at(currentIndex - 1).push_back(quad); } else { batchedQuadsContentQuadAmount.push_back(batchedQuads.at(currentIndex - 1).size()); std::vector<Quad> anotherQuadVector; anotherQuadVector.push_back(quad); batchedQuads.push_back(anotherQuadVector); currentIndex += 1; } } else { std::vector<Quad> firstQuadVector; firstQuadVector.push_back(quad); batchedQuads.push_back(firstQuadVector); currentIndex += 1; } } void SpriteBatch::flush() { if (currentIndex == 0) return; //Ensures that there are sprites added for (std::vector<Quad> quadBatch : batchedQuads) { Quad combinedQuad; int processedQuads{ 0 }; for (Quad quad : quadBatch) { combinedQuad.vertices[processedQuads * 20] = quad.vertices[0]; combinedQuad.vertices[processedQuads * 20 + 1] = quad.vertices[1]; combinedQuad.vertices[processedQuads * 20 + 2] = quad.vertices[2]; combinedQuad.vertices[processedQuads * 20 + 3] = quad.vertices[3]; combinedQuad.vertices[processedQuads * 20 + 4] = quad.vertices[4]; combinedQuad.vertices[processedQuads * 20 + 5] = quad.vertices[5]; combinedQuad.vertices[processedQuads * 20 + 6] = quad.vertices[6]; combinedQuad.vertices[processedQuads * 20 + 7] = quad.vertices[7]; combinedQuad.vertices[processedQuads * 20 + 8] = quad.vertices[8]; combinedQuad.vertices[processedQuads * 20 + 9] = quad.vertices[9]; combinedQuad.vertices[processedQuads * 20 + 10] = quad.vertices[10]; combinedQuad.vertices[processedQuads * 20 + 11] = quad.vertices[11]; combinedQuad.vertices[processedQuads * 20 + 12] = quad.vertices[12]; combinedQuad.vertices[processedQuads * 20 + 13] = quad.vertices[13]; combinedQuad.vertices[processedQuads * 20 + 14] = quad.vertices[14]; combinedQuad.vertices[processedQuads * 20 + 15] = quad.vertices[15]; combinedQuad.vertices[processedQuads * 20 + 16] = quad.vertices[16]; combinedQuad.vertices[processedQuads * 20 + 17] = quad.vertices[17]; combinedQuad.vertices[processedQuads * 20 + 18] = quad.vertices[18]; combinedQuad.vertices[processedQuads * 20 + 19] = quad.vertices[19]; combinedQuad.indices[processedQuads * 6] = 0 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 1] = 1 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 2] = 3 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 3] = 1 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 4] = 2 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 5] = 3 + processedQuads * 4; processedQuads += 1; } combinedQuad.texture = quadBatch.at(0).texture; combinedQuad.transform = quadBatch.at(0).transform; combinedQuads.push_back(combinedQuad); } batchedQuadsContentQuadAmount.push_back(batchedQuads.at(batchedQuads.size()-1).size()); batchedQuads.clear(); int drawCalls{ 0 }; for (Quad combinedQuad : combinedQuads) { shader->Use(); glBindTexture(GL_TEXTURE_2D, combinedQuad.texture->texture); view = camera->getView(); projection = camera->getProjection(); glBindVertexArray(VAO); //Bind vertices glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(combinedQuad.vertices), combinedQuad.vertices, GL_STATIC_DRAW); //Bind indices glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(combinedQuad.indices), combinedQuad.indices, GL_STATIC_DRAW); //Position glVertexAttribPointer(0, 3 , GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); // TexCoord glVertexAttribPointer(1, 2 , GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); glEnableVertexAttribArray(1); //VAO glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); //Shader locations transformShaderLocation = glGetUniformLocation(shader->program, "transform"); viewShaderLocation = glGetUniformLocation(shader->program, "view"); projectionShaderLocation = glGetUniformLocation(shader->program, "projection"); // Pass them to the shaders glUniformMatrix4fv(transformShaderLocation, 1, GL_FALSE, glm::value_ptr(combinedQuad.transform)); glUniformMatrix4fv(viewShaderLocation, 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(projectionShaderLocation, 1, GL_FALSE, glm::value_ptr(projection)); //Draw VAO glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6 * batchedQuadsContentQuadAmount.at(drawCalls), GL_UNSIGNED_INT, 0); glBindVertexArray(0); drawCalls += 1; } //Sets index to 0 and clears combinedQuads to welcome new sprites. Batchedquads has already been cleared. currentIndex = 0; combinedQuads.clear(); } For me, all is logical and does its job as intended, but same textures still don't work. Here is the main class: #include "main.h" #define STB_IMAGE_IMPLEMENTATION #include <stb_image.h> int main(int argc, char* argv[]) { Main main; return main.init(); } int Main::init() { display = new Display(800, 600, "OpenGL" ); shader = new Shader("Shaders/default.vert", "Shaders/default.frag"); texture1 = new Texture("Textures/libGDX.png", STBI_rgb_alpha); texture2 = new Texture("Textures/textuuur.png", STBI_rgb); _sprite1 = new Sprite(150.f, 150.f, 100.0f, 100.0f, *shader, texture1); _sprite2 = new Sprite(150.f, 150.f, 100.0f, 100.0f, *shader, texture2); if (_test3D) { camera = new Camera(PERSPECTIVE, display); } else { camera = new Camera(ORTHOGRAPHIC, display); } if (_test3D) { glEnable(GL_DEPTH_TEST); } this->spriteBatch = new SpriteBatch(*shader, *camera); while (!display->isClosed()) { update(); } delete _sprite1, _sprite2; delete texture1, texture2; delete shader; delete camera; delete display; delete spriteBatch; return 0; } void Main::update() { draw(); display->Update(); } void Main::draw() { glClearColor(0.0f, 0.3f, 0.5f, 1.0f); if (_test3D) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { glClear(GL_COLOR_BUFFER_BIT); } //_sprite1->draw(*camera); //_sprite2->draw(*camera); spriteBatch->draw(texture1, _sprite1->x, _sprite1->y, _sprite1->width, _sprite1->height); spriteBatch->draw(texture2, _sprite1->x + 600, _sprite1->y + 10, _sprite1->width, _sprite1->height); spriteBatch->draw(texture2, _sprite1->x + 400, _sprite1->y + 10, _sprite1->width, _sprite1->height); spriteBatch->flush(); } The first texture draws, but the second or third doesn't. Only one of the two same textures actually is drawn on the screen, the other is not. Now, this sounds pretty bad, how do I improve that. Do I have to write that without the Quad struct again? Also, strangely with this: Quad combinedQuad; int processedQuads{ 0 }; combinedQuad.texture = quadBatch.at(0).texture; combinedQuad.transform = quadBatch.at(0).transform; for (Quad quad : quadBatch) { combinedQuad.vertices[processedQuads * 20] = quad.vertices[0]; combinedQuad.vertices[processedQuads * 20 + 1] = quad.vertices[1]; combinedQuad.vertices[processedQuads * 20 + 2] = quad.vertices[2]; combinedQuad.vertices[processedQuads * 20 + 3] = quad.vertices[3]; combinedQuad.vertices[processedQuads * 20 + 4] = quad.vertices[4]; combinedQuad.vertices[processedQuads * 20 + 5] = quad.vertices[5]; combinedQuad.vertices[processedQuads * 20 + 6] = quad.vertices[6]; combinedQuad.vertices[processedQuads * 20 + 7] = quad.vertices[7]; combinedQuad.vertices[processedQuads * 20 + 8] = quad.vertices[8]; combinedQuad.vertices[processedQuads * 20 + 9] = quad.vertices[9]; combinedQuad.vertices[processedQuads * 20 + 10] = quad.vertices[10]; combinedQuad.vertices[processedQuads * 20 + 11] = quad.vertices[11]; combinedQuad.vertices[processedQuads * 20 + 12] = quad.vertices[12]; combinedQuad.vertices[processedQuads * 20 + 13] = quad.vertices[13]; combinedQuad.vertices[processedQuads * 20 + 14] = quad.vertices[14]; combinedQuad.vertices[processedQuads * 20 + 15] = quad.vertices[15]; combinedQuad.vertices[processedQuads * 20 + 16] = quad.vertices[16]; combinedQuad.vertices[processedQuads * 20 + 17] = quad.vertices[17]; combinedQuad.vertices[processedQuads * 20 + 18] = quad.vertices[18]; combinedQuad.vertices[processedQuads * 20 + 19] = quad.vertices[19]; combinedQuad.indices[processedQuads * 6] = 0 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 1] = 1 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 2] = 3 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 3] = 1 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 4] = 2 + processedQuads * 4; combinedQuad.indices[processedQuads * 6 + 5] = 3 + processedQuads * 4; processedQuads += 1; } combinedQuads.push_back(combinedQuad); combinedQuad.texture will turn into a read access violation at combinedQuads.push_back(combinedQuad); If I put combinedQuad.texture = quadBatch.at(0).texture; combinedQuad.transform = quadBatch.at(0).transform; after the loop / before the push_back, it will be fine. Why? Sorry for the billion edits.
  4. Alright alright I know that my code is dirty, I will rewrite it. "item" was actually called "glfloat" too and it somehow compiled, but I'm not that dirty ^^ I'll do that, but that doesn't explain why I can't set C-array in the struct without getting errors I'd rather not set the value for every index of the array seperately. I plan to make a game for desktop only first. I have used the Godot engine first and it really lagged a lot with just 20 sprites, so I think it would really need optimizing. Also I can't stop thinking about my fellow gamers with integrated intel hd graphics, I don't want to give them a game which runs at 20fps. The main reason why I want to code an engine though is because I want to improve my C++ skills (and because I have to kill time until I get an artistic friend who can actually provide me with assets for games.) Well, I'll then continue to write a batch which can draw different textures. But I have noticed that the draw order will be incorrect. If I want to draw a quad with texture A, a quad with texture B and then another quad with texture A so texture B is rendered between them, it won't work because all texture A's are drawn first. How do I address this issue?
  5. Alright thanks, now I have quite a bit of work to do I have one other problem though: struct glfloat { GLfloat item[800]; }; GLfloat myVertices[800]; glfloat structMyVertices = { myVertices }; //ERRORHERE It says I can't use a value of type ""GLfloat *"" to initialize an entity of type ""GLfloat"". But there is no pointer anywhere. And if I do glfloat structMyVertices; structMyVertices/*ERRORHERE*/.item = myVertices; it says expression must be a modifiable lvalue. Why? That should be a workaround to store an [] array into a vector btw
  6. My goal with the batching is to do it like libgdx does it: You can draw every single texture in the batch between beginning and flushing. It doesn't have to be the same one. That's what I wanted to do because it's much simpler to work with in the end rather than starting a new batch for each texture. That's why I wanted to save the textures. The batch itself would start a new draw for every item in the quads vector automatically so I won't have to do it myself. That's why I sorted it, else wouldn't sorting be pretty unnecessary as everything has the same texture anyways? I think the batching will be pretty important for me because I want to do a 2d orthogonal tile game, like pokemon. There will be a lots of duplicate textures, especially for the ground, so it will probably give me a huge performance boost. The texture actually isn't created with new, I just create it in the main function and pass it as reference. Well if I used the VAO in the first batch it will be filled with data so I thought I have to clear it first if I want to use it again, but if it just gets overwritten then that's fine. Alright, that's just a java habit creating everything with new 😛 What exactly is incorrect with the index handling? Every quad has 2 triangles with exactly those 2 duplicate points, so shouldn't the index buffer be always the same? Sorry for asking so many questions and thanks for the answers. As for my Texture class, I think it looks okay. Apart from that that I have to set this reg_comp field (STBI_rgb_alpha or STBI_rgb) for each texture manually, I thinks everything is deleted and freed correctly. #include "Texture.h" Texture::Texture(char *texturePath, int reg_comp) { glGenTextures(1, &texture); int _width, _height, _ImageComponents; unsigned char *image = stbi_load(texturePath, &_width, &_height, &_ImageComponents, reg_comp); if (image == nullptr) { std::cout << "error loading image" << std::endl; } glBindTexture(GL_TEXTURE_2D, texture); // Set our texture parameters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); if (_ImageComponents == 3) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _width, _height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); else if (_ImageComponents == 4) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); //RGBA8? glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); stbi_image_free(image); } Texture::~Texture() { glDeleteTextures(1, &texture); } the texture (GLuint) will always be deleted at the destructor.
  7. Thanks a lot for the explanations. So if the quads share the same texture atlas, they can still be batched together even though they use different parts of the texture atlas, did I understand that right? As for textures->push_back(texture); I wanted to have all textures stored in a list, so I can bind them. I mean I must save them somewhere and it will be multiple textures until I flush. Also, they are just pointers to the objects, no real objects, so it doesn't matter if I have many of them here, right? I'll delete them elsewhere. For the vector, I have added a simple delete textures; in the destructor for now. I've moved glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); into the constructor. Is that enough or do I have to "empty" the objects every flush call? And where should I delete the texture? Visual studio throws an error when I close it at delete texture in the destructor, but I don't understand why. It's a pointer, so I have to delete it. Is it really necessary to delete the texture from where I created it? I'm thinking of looking into smart pointers but my main goal now is to get this to work, smart pointers look pretty time consuming. Well, the first thing I have to do is to sort the quads first, so that the batch only renders quads with the same textures at once, right? For this I have written this piece of code: std::vector<std::vector<textureandindex>> quads; for (int i = 0; i < spritesInBatch; i++) { std::vector<textureandindex> sameTextureObjects; Texture *tex = textures->at(i); textures->erase(textures->begin() + i); textureandindex tocompare = { i,tex }; sameTextureObjects.push_back(tocompare); for (int a = 0; a < spritesInBatch; a++) { if (textures->at(a) == tex) { textures->erase(textures->begin() + a); textureandindex compared = { a,textures->at(a) }; sameTextureObjects.push_back(compared); } } quads.push_back(sameTextureObjects); } It should sort all quads which have the same textures into the vector sameTextureObjects. And all those sameTextureObjects vectors are added to the quads vector. Now the batch should be able to render all quads in every vector in the quads vector at once, and the number of items in the quads vector are basically the amount of draw calls. The quads in there are also in a struct called textureandindex which is basically the index + the texture, so when drawing, I know what indexes and vertices each quad has. And you said this isn't that what my draw() function already does? How do I pre-apply the model transform? Just multiply every vertex in the draw function with it?
  8. Hello there, I have tried following various OpenGL tutorials and I'm now at a point where I can render multiple 2d sprites with textures. For that I have a sprite class: Header: #ifndef SPRITE_H #define SPRITE_H #include <GL/glew.h> #include "Shader.h" #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include "Texture.h" #include <stb_image.h> #include "Camera.h" class Sprite { public: Sprite(GLfloat x, GLfloat y, GLfloat width, GLfloat height, Shader& shader, Texture *texture); ~Sprite(); void draw(Camera &camera); void setPosition(float x, float y, float z); void move(float x, float y, float z); void setTexture(Texture *texture); Texture getTexture(); float x, y, width, height; private: void init(GLfloat x, GLfloat y, GLfloat width, GLfloat height, Shader& shader, Texture *texture); GLuint VBO = 0, VAO = 0, EBO = 0; GLint transformShaderLocation, viewShaderLocation, projectionShaderLocation; Shader* shader; glm::mat4 transform, projection, view; Texture *texture; }; #endif Code: #include "Sprite.h" Sprite::Sprite(GLfloat x, GLfloat y, GLfloat width, GLfloat height, Shader& shader, Texture *texture) { init(x, y, width, height, shader, texture); } void Sprite::init(GLfloat x, GLfloat y, GLfloat width, GLfloat height, Shader& shader, Texture *texture) { this->shader = &shader; this->x = x; this->y = y; this->width = width; this->height = height; GLfloat vertices[] = { width / 2 , height / 2, 0.0f, /* Top Right */ 1.0f, 1.0f, width / 2 , -height / 2 , 0.0f, /* Bottom Right*/ 1.0f, 0.0f, -width / 2 ,-height / 2 , 0.0f, /* Bottom Left */ 0.0f, 0.0f, -width / 2 , height / 2 , 0.0f, /* Top Left */ 0.0f, 1.0f }; GLuint indices[] = { 0, 1, 3, // 1 1, 2, 3 // 2 }; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //Position glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); // TexCoord glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); transformShaderLocation = glGetUniformLocation(shader.program, "transform"); viewShaderLocation = glGetUniformLocation(shader.program, "view"); projectionShaderLocation = glGetUniformLocation(shader.program, "projection"); transform = glm::translate(transform, glm::vec3(x , y , 0)); this->texture = texture; } Sprite::~Sprite() { //DELETE BUFFERS glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glDeleteBuffers(1, &VAO); delete texture; } void Sprite::draw(Camera &camera) { shader->Use(); glBindTexture(GL_TEXTURE_2D, texture->texture); view = camera.getView(); projection = camera.getProjection(); // Pass to shaders glUniformMatrix4fv(transformShaderLocation, 1, GL_FALSE, glm::value_ptr(transform)); glUniformMatrix4fv(viewShaderLocation, 1, GL_FALSE, glm::value_ptr(view)); // Note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once. glUniformMatrix4fv(projectionShaderLocation, 1, GL_FALSE, glm::value_ptr(projection)); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); } void Sprite::setPosition(float x, float y, float z) { //Z? transform = glm::translate(transform, glm::vec3(x - this->x , y - this->y , z)); this->x = x; this->y = y; } void Sprite::move(float x, float y, float z) { transform = glm::translate(transform, glm::vec3(x, y , z)); this->x += x; this->y += y; } void Sprite::setTexture(Texture *texture) { delete this->texture; this->texture = texture; } Texture Sprite::getTexture() { return *texture; } When I want to draw something, I create an instance of the sprite class with it's own Texture and use sprite->draw(); in the draw loop for each sprite to draw it. This works perfectly fine. To improve the performance, I now want to create a spritebatch. As far as I understood it puts all the sprites together so it can send them all at once to the gpu. I had no clue how to get started, so I just created a spritebatch class which put all the vertices and indices into one object every time draw() is called, and actually only draws when flush() is called. Here's the header file: #ifndef SPRITEBATCH_H #define SPRITEBATCH_H #include <glm/glm.hpp> #include "Texture.h" #include <GL/glew.h> #include "Camera.h" #include "Shader.h" #include <vector> class SpriteBatch { public: SpriteBatch(Shader& shader, Camera &camera); ~SpriteBatch(); void draw(Texture *texture, GLfloat x, GLfloat y, GLfloat width, GLfloat height); void flush(); private: GLfloat vertices[800]; GLuint indices[800]; int index{ 0 }; int indicesIndex{ 0 }; GLuint VBO = 0, VAO = 0, EBO = 0; GLint transformShaderLocation, viewShaderLocation, projectionShaderLocation; Shader *shader; Camera *camera; std::vector<Texture*>* textures; glm::mat4 transform, projection, view; }; #endif And the class. I added some comments here: #include "SpriteBatch.h" SpriteBatch::SpriteBatch(Shader& shader, Camera &camera) { this->shader = &shader; this->camera = &camera; textures = new std::vector<Texture*>(); } SpriteBatch::~SpriteBatch() { glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glDeleteBuffers(1, &VAO); //delete texture; } void SpriteBatch::draw(Texture *texture, GLfloat x, GLfloat y, GLfloat width, GLfloat height) { textures->push_back(texture); vertices[index] = width/2 ; vertices[index + 1] = height/2; vertices[index + 2] = 0.0f; vertices[index + 3] = 1.0f; vertices[index + 4] = 1.0f; vertices[index + 5] = width / 2; vertices[index + 6] = -height / 2; vertices[index + 7] = 0.0f; vertices[index + 8] = 1.0f; vertices[index + 9] = 0.0f; vertices[index + 10] = -width / 2; vertices[index + 11] = -height / 2; vertices[index + 12] = 0.0f; vertices[index + 13] = 0.0f; vertices[index + 14] = 0.0f; vertices[index + 15] = -width / 2; vertices[index + 16] = height / 2; vertices[index + 17] = 0.0f; vertices[index + 18] = 0.0f; vertices[index + 19] = 1.0f; index += 20; indices[indicesIndex] = 0; indices[indicesIndex + 1] = 1; indices[indicesIndex + 2] = 3; indices[indicesIndex + 3] = 1; indices[indicesIndex + 4] = 2; indices[indicesIndex + 5] = 3; indicesIndex += 6; } void SpriteBatch::flush() { if (index == 0) return; //Ensures that there are sprites added //Debug information. This works perfectly int spritesInBatch = index / 20; std::cout << spritesInBatch << " I : " << index << std::endl; int drawn = 0; //Create Buffers glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); //Bind vertices glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //Bind indices glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //Position glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); // TexCoord glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); glEnableVertexAttribArray(1); //VAO glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); //Shader locations transformShaderLocation = glGetUniformLocation(shader->program, "transform"); viewShaderLocation = glGetUniformLocation(shader->program, "view"); projectionShaderLocation = glGetUniformLocation(shader->program, "projection"); //Draw //So this sets the texture for each sprites and draws it afterwards with the right texture. At least that's how it should work. for (int i = 0; i < spritesInBatch; i++) { Texture *tex = textures->at(i); shader->Use(); glBindTexture(GL_TEXTURE_2D, tex->texture); //? view = camera->getView(); projection = camera->getProjection(); // Pass them to the shaders glUniformMatrix4fv(transformShaderLocation, 1, GL_FALSE, glm::value_ptr(transform)); glUniformMatrix4fv(viewShaderLocation, 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(projectionShaderLocation, 1, GL_FALSE, glm::value_ptr(projection)); //Draw VAO glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, indicesIndex, GL_UNSIGNED_INT, 0); glBindVertexArray(0); } //Sets index 0 to welcome new sprites index = 0; } It also puts the textures into a list. The code to draw two sprites is this: spriteBatch->draw(&_sprite1->getTexture(), _sprite1->x, _sprite1->y, _sprite1->width, _sprite1->height); spriteBatch->draw(&_sprite1->getTexture(), _sprite1->x+10, _sprite1->y+10, _sprite1->width*2, _sprite1->height); spriteBatch->flush(); but I only get one small black rectangle in the bottom left corner. It works perfectly when I draw the sprites without the spritebatch; _sprite1->draw(*camera); _sprite2->draw(*camera); I think I messed up in the flush() method, but I have no clue how to implement this. I'd be grateful if someone can help me with it. Thank you!
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!