Advertisement Jump to content
  • Advertisement

OpenGL Tower Defense Indie Game Dev Blog #1: 3D Models and Pathfinding

EddieK

1552 views

About the Game

As you probably have guessed from the title, the game I'm working on is a Tower Defense type of game. At this point I'm still not sure what theme it's going to be in, but I think I will go with military based theme. The game itself is inspired by Red Alert, Robo Defense and Kingdom Rush. For development side of things I'm using Java/Kotlin (mostly Kotlin) + OpenGL and LWJGL with the IntellJ IDEA editor.

3D Models

In the last couple of weeks I've been learning how to make 3D models using Blender. After few days of modelling I got hang of the basics and could model few simple trees, turrets and a car which I then imported into the game "engine".  Here are few screenshots of the models that I have created during those days:

turret

turretScreenshot from 2018-05-14 19-58-12

car.png

 

Pathfinding

I have programmed a pathfinding management system, which uses flood-fill pathfinding algorithm to calculate where the enemies have to go. The way it works is pretty simple. You start by splitting your game map into square nodes and then generating a gradient map which will tell how far away the current node is from the target node. To do this, you firstly start at the target node, assign its value to 0, then for all the neighboring nodes increment their value by 1, or whatever number you want, as long as its a positive number. If the neighboring node is non-collidable, assign its value to something very large, like 999999.

To get the path from the start node to the current node you start at the start node, and select its neighboring node with the lowest value. Then for that selected node, do the same process until you reach node with the value of 0, which will mean that you have reached your target node.

This is how the gradient map looks in my game:

pathfinding1.png

 

Here you can see the numbers at the center of each node which represent its value. The cars are moving towards the surrounding nodes which have the lowest values until they reach 0, that's when they stop.

Okey, so why did I use this method instead of the famous A* algorithm? First of all. This is heck of a lot faster. Instead of always calculating a path each frame for each entity. You just generate the gradient map once, and update it every time an object is placed on the map. The drawback of this method is that all the entities can only go to a single target destination. If you want multiple target destinations, you will have to recalculate the gradient map with different target nodes.

Gameplay

There's not much of a gameplay at this stage of development. As of now all you can do is place turrets, watch them shoot the enemies and that's pretty much it. Nonetheless this is the gameplay footage:

 

 

Twitter: https://twitter.com/extrabitgames
Facebook: https://www.facebook.com/extrabitgames
Website: http://extrabitgames.com

My website: http://extrabitgames.com



12 Comments


Recommended Comments

This is looking very good already! You can do it as part of the gamedev challenge too, there are several of us making tower defence games at the moment. :)

Share this comment


Link to comment
2 hours ago, lawnjelly said:

This is looking very good already! You can do it as part of the gamedev challenge too, there are several of us making tower defence games at the moment. :)

Thank you!

Oh I didn't know there was a challenge like this. Is there a link in which I could read more about this? 

Share this comment


Link to comment

Is just a bit of fun, but I don't see why you can't put yours as an entry even if you are planning on selling your game after. Rutin, Awoken, Dexter and I are doing games I know of so far. :)

Share this comment


Link to comment
43 minutes ago, lawnjelly said:

Is just a bit of fun, but I don't see why you can't put yours as an entry even if you are planning on selling your game after. Rutin, Awoken, Dexter and I are doing games I know of so far. :)

Oh sounds very cool :) I just don't know if I will be able to finish this game by the time the submission date ends. Though I could try making a minimal viable product and submitting that. And then continue working on the game after submission. Anyways, thanks for letting me know. This community is so friendly :D

Share this comment


Link to comment
1 hour ago, EddieK said:

Oh sounds very cool :) I just don't know if I will be able to finish this game by the time the submission date ends. Though I could try making a minimal viable product and submitting that. And then continue working on the game after submission. Anyways, thanks for letting me know. This community is so friendly :D

If you do meet the minimum requirements for the challenge you can earn yourself the 'Tower Defence Badge'!  

Great job by the way.  Your turning mechanism is very fluid.  I also like the military theme you're going with.

Share this comment


Link to comment
1 hour ago, Awoken said:

If you do meet the minimum requirements for the challenge you can earn yourself the 'Tower Defence Badge'!  

Great job by the way.  Your turning mechanism is very fluid.  I also like the military theme you're going with.

Thanks! I will certainly try to get my game submitted there :)

Share this comment


Link to comment

Awesome TD development bro ^_^y It's nice to know your using KOTLIN... I'm learning the language but on early stage.

Share this comment


Link to comment
37 minutes ago, DexterZ101 said:

Awesome TD development bro ^_^y It's nice to know your using KOTLIN... I'm learning the language but on early stage.

Thanks! I've have been using Java for few years now and then decided to switch to Kotlin like a month ago. And all I can is that it is AWESOME :D Less boilerplate code, operator overloading (especially useful in game dev) and many more things which make programming faster. The only thing that I don't really get is the whole thing about non-nullable objects. I mean I understand how it works, but there are lots of parts in my code where I need to have a null object referance. Anyways, good luck with learning! It is really a great and modern language :)

Share this comment


Link to comment

I really like the art work! :) You should be able to meet the min. requirements easily before the due date. I hope you submit an entry! :) Great work!

Share this comment


Link to comment
16 minutes ago, Rutin said:

I really like the art work!  You should be able to meet the min. requirements easily before the due date. I hope you submit an entry! Great work!

Wow, thanks! Didn't expect to get such a positive feedback :) I will try to submit an entry, so I better go back to work now :D 

Share this comment


Link to comment

I found the pathfinding part very interesting!

By the way, which Red Alert did you take your inspiration from? I'm certainly not a huge fan of Command & Conquer and Red Alert :P

Share this comment


Link to comment

Just wanted to thank you guys for all the kind feedback, you guys are the best!

40 minutes ago, thecheeselover said:

I found the pathfinding part very interesting!

By the way, which Red Alert did you take your inspiration from? I'm certainly not a huge fan of Command & Conquer and Red Alert :P

Thank you! Although I have played all of the Red Alert installments, I think my inspiration comes from the original Red Alert which I played on my PS1 :D I'm actually thinking of making tesla towers inside my game, just because I loved them in Red Alert so much :D

Edited by EddieK

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • What is your GameDev Story?

    In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

    (You must login to your GameDev.net account.)

  • Blog Entries

  • Similar Content

    • By I3DI
      I have spent many hours dedicated to advanced blending, shading, modeling and texture art for a game concept that will requires stages.  Think of this in the context of the sims, but far more advanced.  I need someone with a server that can host the Unreal server.  The plant life, very advanced.  The terrain textures very advanced and using blending, GIS, varying liquid textures I am building the foundation for a economic sim first.  The idea initially will be to build businesses in cities and have a character you can customize with basic animations.  Later down the road once world economies are in motion, we will add simulations, politics, governments, and finally combat simulations.  The idea is you can assume any role you would in life, build a life in modern civilization, all connected world wide.  Initially it's very basic, businesses and economies with a customizable male or female character, house or business building.
      My company has secured a contract that generates about 120,000.00 USD a year, and those willing to vest in this project will ultimately be paid.  Where is the money?  Well, the idea is to take subscriptions initially but then, money is deposited by players to build their virtual life, career, and each nation will have a government that builds on taxation.  I will add stock markets, housing markets, medical markets, technology markets and resource markets.  The beauty is, since players invest real cash, they can make real cash thru inventions, politics, banking, travel, whatever they set up in the virtual world.  Simply working a job for another players business that pays.  As the economy grows and more players are added, then they supply cash to begin their lives in our virtual world and the ones that make money can simply withdraw into their real world bank accounts.  We simply make our money off fees in transactions that occur in our virtual world.
      So to be clear, this is a beginning try out with stages of development.  I will be doing a early release and as players are added, money is invested, this will fund further development.  
      Stage 1:
      Government, Business and World Setup.  I have completed all texture art necessary to generate a complete earth in very vivid detail.  I am making preset buildings.  But I require assistance in the development of a in game GIS terrain generator.  I will also need help finding a database of cities and their building and maps so we can procedurally generate the cities.  I have iClone and could use another person to help me develop the basic animations for people.  I would like to make a building designer in game, setup zoned properties, making clothing, furniture, and get the economic part of the game setup.
      I am seeking someone with a server that can run 24/7, a Unreal game server.  I am seeking one or two artists that can help speed up the basic modeling process for many models.  I personally will deal with the terrain, city and various programming aspects.  I already have done all the architectural textures, detail and base, PBR.  I am doing models, like furniture, fountains, but being able to shift this work to several artists that are consistent would be of real help so I can focus on the programming aspect of this first stage.  I also have a merchant processor so I can run the credit cards and accounts thru them.  If you are willing to vest in this one project, one piece at a time, then I can guarantee once you are proven and consistent, there will be pay.  There are several other stages to building this advanced simulation but this first stage is the simplest and will at least begin a very lucrative money income for the company.
      A additional programmer would be of real help that is familiar with C++, Unreal, Blueprints, Materials, GitHub is hard working consistent, and wants to get their foot in the door in game development.
      Seeking two artists, 2D knowledgeable in Adobe 
      Seeking two artists 3D knowledgeable in Substance, Adobe, Blender, iClone is a bonus
      Seeking one individual with a 24/7 server for Beta Testing at no cost.
      Tools and accounts are setup and paid for by me.
      Thanks.
    • By Alladin
      Got an amount of inspiration and made this game. It's humoristic and satiric, so don't take it too seriously)
      Here you play as a priest and your main task is catching the kids. Steam store page: 
      https://store.steampowered.com/app/915730/Catch_The_Kids_Priest_Simulator_Game/?beta=0
      Gameplay trailer:
      https://www.youtube.com/watch?v=7cRWIyXU1dc&t=0s
      If you have any suggestions, advice or something else, write here)
       
       
    • By ProjectTaival
      Something about me and my vision for the project:

      I have been interested at making games since I was a teenager, but never had the courage to even try, due to my personal issues I have encountered in life. But that has changed recently, as during the years of recovery I have gained more confidence in my ability to learn new things and have had extensive experience in Google Sketchup, as the picture above shows. After reading how it can be used in conjunction with Unreal Engine and Unity 3D, I decided to give it a try, no matter how hard it could turn out to be. This project is an attempt to have a new beginning and thus a very important step forward in my life, which makes it all the more important for it to be a success. I'm not seeking triple A quality and try to keep my goals achievable, but at the same time want to try my utmost to bring my vision to life without sacrificing too much visual fidelity.

      Hideo Kojima and the Metal Gear -series has also been a huge inspiration to me and I think that at least one aspect of Hideo's advice can be used in the Project Taival as well - "Betray your audience". I want to make this game in both story and mechanic wise, to be as unpredictable as possible.

      I started up as an Indie Game Developer in December 14th, 2018. I have always loved to be able to think freely - no matter how silly some idea might sound at first, I just have to entertain the idea to see if I can find some non-obvious creative value in it. Innovative ideas need analytic thinking, which have helped me tremendously on anything that requires creativity, like creating 3D models to plan out carpentry solutions as a carpenter.

      To summarize some features of the game, here is a short list of things;

      - The game will be in First Person perspective.
      - It is going to be divided in multiple games, so be sure to expect a sequel.
      - All the final assets are going to be original content or modified from free assets.
      - Paid assets remains a possibility, depending on the amount of donations and pre-orders.
      - The game is based on modern days, but the roots of the story are much deeper in time.
      - 2 layer game world, similar to Silent Hill, but different.

      My personal goals for this project are;
      • Get better at 3D modeling on more various selection of modeling software.
      • Learn to make presentable music.
      • Study on how to make animated videos from scratch.
      • Get to know some basics about several game engines, before choosing the one that I will use.
      • To improve my storytelling and compilation skills.
      • Bringing to fruition a marketable end result and registering a Game Development company.

      For all the latest information, you can subscribe to these social media sites;

      https://www.youtube.com/channel/UCdrMyKZohHIQq0qTTNmjHYQ/about
      https://www.reddit.com/user/ProjectTaival
      https://www.facebook.com/projecttaival
      https://www.instagram.com/projecttaival/
      https://plus.google.com/u/0/109642573532621061672

      The best kind of free help you can give me, is advice and constructive criticism. I'm always ready to learn more on how to market my creations and first and foremost develop them in the first place.
      Second best and free kind of help you can give me, is to spread the word anywhere.

      Thank you for time.

    • By Ozden79
      Hello Everyone, 
      http://www.youtube.com/watch?v=V4BEluerj0c

      Clan N is a beatemup game which combines the classic arcades gameplay with today's modern brawlers. With an ancient far east theme, you'll get challenged across 7 different levels with many different enemies, mid/end level bosses and casual mini games integrated into main gameplay.

      Features
      A fast paced beatemup which combines the classic arcades gameplay with today's modern brawlers. A main story with 7 levels divided into more than 50 sections. Levels contain casual minigames which are tightly integrated into the main story and can be played separately as well. Can be played as local or online coop with up to 4 players. A slick and clean pixel based graphics with accompanying far east inspired music and sfx. ScreenShots

       

       

       

      More information
      WebSite : http://www.clanngame.com/
      Reveal Trailer : http://www.youtube.com/V4BEluerj0c
      Steam : https://store.steampowered.com/app/982670/Clan_N/
      Let me know any questions you may have and happy gaming!
    • By babaliaris
      So, I'm trying to solve this problem for months. I have already opened relative threads for this but now that I learned a lot of stuff  I still seek your help for this one. What I learned so far when dealing with texture Uploading and Pixel Reads:
      1) Make sure how many channels the source image file has in order to configure the glTexImage2D() to read the data correctly.
      2) Make sure that the width * num_OfChannels of the image is multiple of 4, so you won't have problems with the alignment. (OpenGL Common Mistakes, Texture Upload And Pixel Reads)
      3) Forcing any kind of texture (R, RG, RGB) to have exactly 4 channels ALWAYS works (But you waste a lot of memory)!!!
       
       
      Below, I'm going to show you step by step what I tried and what glitches are occurring, NOTICE that even if I'm creating more than one textures I ONLY render the first one a.jpg:
      First check out my texture code. As you can see I'm configuring glTexImage2D() to read pixel data based on how many channels they have (I'm only using textures with 3 and 4 channels) and I already made sure that the width * channels for each image is multiple of 4.
      #include "texture.h" #include "stb_image/stb_image.h" #include "glcall.h" #include "engine_error.h" #include <math.h> Texture::Texture(std::string path, bool trans, int unit) { //Reverse the pixels. stbi_set_flip_vertically_on_load(1); //Try to load the image. unsigned char *data = stbi_load(path.c_str(), &m_width, &m_height, &m_channels, 0); //Debug. float check = (m_width * m_channels) / 4.0f; printf("file: %20s \tchannels: %d, Divisible by 4: %s, width: %d, height: %d, widthXheight: %d\n", path.c_str(), m_channels, check == ceilf(check) ? "yes" : "no", m_width, m_height, m_width * m_height); /* //The length of the pixes row is multiple of 4. if ( check == ceilf(check) ) { GLCall(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); } //It's NOT!!!! else { GLCall(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); } */ //Image loaded successfully. if (data) { //Generate the texture and bind it. GLCall(glGenTextures(1, &m_id)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); //Not Transparent texture. if (m_channels == 3) { GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data)); } //Transparent texture. else if (m_channels == 4) { GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)); } else { throw EngineError("Unsupported Channels!!!"); } //Texture Filters. GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); //Generate mipmaps. GLCall(glGenerateMipmap(GL_TEXTURE_2D)); } //Loading Failed. else throw EngineError("The was an error loading image: " + path); //Unbind the texture. GLCall(glBindTexture(GL_TEXTURE_2D, 0)); //Free the image data. stbi_image_free(data); } Texture::~Texture() { GLCall(glDeleteTextures(1, &m_id)); } void Texture::Bind(int unit) { GLCall(glActiveTexture(GL_TEXTURE0 + unit)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); }  
      Now Check out the Main.cpp File
      #include "Renderer.h" #include "camera.h" Camera *camera; //Handle Key Input. void HandleInput(GLFWwindow *window) { //Exit the application with ESCAPE KEY. if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, 1); //Move Forward. if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) camera->Move(true); //Move Backward. if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) camera->Move(false, true); //Move left. if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) camera->Move(false, false, true); //Move right. if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) camera->Move(false, false, false, true); } //Mouse Input. void MouseInput(GLFWwindow *window, double x, double y) { camera->UpdateRotation(x, y); } //Mouse Zoom input. void MouseZoom(GLFWwindow *window, double x, double y) { camera->UpdateZoom(x, y); } int main(void) { GLFWwindow* window; /* Initialize the library */ if (!glfwInit()) return -1; //Use openGL version 3.3 Core Profile. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); /* Create a windowed mode window and its OpenGL context */ window = glfwCreateWindow(800, 600, "Hello World", NULL, NULL); if (!window) { glfwTerminate(); return -1; } /* Make the window's context current */ glfwMakeContextCurrent(window); //Initialize GLEW. if (glewInit() != GLEW_OK) { glfwTerminate(); return -1; } //Set Callback functions. glfwSetCursorPosCallback(window, MouseInput); glfwSetScrollCallback(window, MouseZoom); //Disable the cursor. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); //Enable Depth Test. GLCall(glEnable(GL_DEPTH_TEST)); //Get the max texture size. GLint size; GLCall(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size)); std::cout << "Texture Max Size: "<< size << std::endl; camera = new Camera(glm::vec3(0.0f, 0.0f, 3.0f)); Renderer *renderer = new Renderer(); Shader *shader = new Shader("Shaders/basic_vertex.glsl", "Shaders/basic_fragment.glsl"); Texture *texture1 = new Texture("Resources/a.jpg", false); Texture *texture2 = new Texture("Resources/container.jpg", false); Texture *texture3 = new Texture("Resources/brick2.jpg", false); Texture *texture4 = new Texture("Resources/brick3.jpg", false); //Forget this texture. //Texture *texture5 = new Texture("Resources/brick4.jpg", false); Texture *texture6 = new Texture("Resources/container2.png", true); /* Loop until the user closes the window */ while (!glfwWindowShouldClose(window)) { //Handle input. HandleInput(window); //Clear the screen. renderer->ClearScreen(0.0f, 0.0f, 0.0f); //Render the cube. renderer->Render(texture1, shader, camera); //Update. renderer->Update(window); } //-------------Clean Up-------------// delete camera; delete renderer; delete shader; //forget about textures for now. //-------------Clean Up-------------// glfwTerminate(); return 0; }  
      I will put the code of the rest classes and the glsl shaders at the end if you want to check them out, but i assure you that they work just fine.
      Now if i run the code below I'm getting this:

       
      Now lets see what happens if I'm loading the textures one by one starting from the first one which is the only one i render.
       
      Attempt 1:
      Texture *texture1 = new Texture("Resources/a.jpg", false); //Texture *texture2 = new Texture("Resources/container.jpg", false); //Texture *texture3 = new Texture("Resources/brick2.jpg", false); //Texture *texture4 = new Texture("Resources/brick3.jpg", false); //Forget this texture. //Texture *texture5 = new Texture("Resources/brick4.jpg", false); //Texture *texture6 = new Texture("Resources/container2.png", true);
       
      Attempt 2:
      Texture *texture1 = new Texture("Resources/a.jpg", false); Texture *texture2 = new Texture("Resources/container.jpg", false); //Texture *texture3 = new Texture("Resources/brick2.jpg", false); //Texture *texture4 = new Texture("Resources/brick3.jpg", false); //Forget this texture. //Texture *texture5 = new Texture("Resources/brick4.jpg", false); //Texture *texture6 = new Texture("Resources/container2.png", true);
       
      Attempt 3:
      Texture *texture1 = new Texture("Resources/a.jpg", false); Texture *texture2 = new Texture("Resources/container.jpg", false); Texture *texture3 = new Texture("Resources/brick2.jpg", false); //Texture *texture4 = new Texture("Resources/brick3.jpg", false); //Forget this texture. //Texture *texture5 = new Texture("Resources/brick4.jpg", false); //Texture *texture6 = new Texture("Resources/container2.png", true);
       
      Attempt 4 (Orange Glitch Appears)
      Texture *texture1 = new Texture("Resources/a.jpg", false); Texture *texture2 = new Texture("Resources/container.jpg", false); Texture *texture3 = new Texture("Resources/brick2.jpg", false); Texture *texture4 = new Texture("Resources/brick3.jpg", false); //Forget this texture. //Texture *texture5 = new Texture("Resources/brick4.jpg", false); //Texture *texture6 = new Texture("Resources/container2.png", true);
       
      Attempt 5 (Grey Glitch Appears)
      Texture *texture1 = new Texture("Resources/a.jpg", false); Texture *texture2 = new Texture("Resources/container.jpg", false); Texture *texture3 = new Texture("Resources/brick2.jpg", false); Texture *texture4 = new Texture("Resources/brick3.jpg", false); //Forget this texture. //Texture *texture5 = new Texture("Resources/brick4.jpg", false); Texture *texture6 = new Texture("Resources/container2.png", true);
       
      If you see it, they only texture which I'm rendering is the first one, so how can the loading of the rest textures affect the rendering, since I'm not using them? (I'm binding the first texture before every draw call, you can check it out in the renderer class). This is so weird I literally can't think anything that causes the problem.
       
      Now check this out. I'm going to run Attempt 5 again but with these changes in the Texture class (I'm going to Force 4 channels no matter what the source file's channels😞
      #include "texture.h" #include "stb_image/stb_image.h" #include "glcall.h" #include "engine_error.h" #include <math.h> Texture::Texture(std::string path, bool trans, int unit) { //Reverse the pixels. stbi_set_flip_vertically_on_load(1); //Try to load the image. unsigned char *data = stbi_load(path.c_str(), &m_width, &m_height, &m_channels, 4); //FORCE 4 CHANNELS. //Debug. float check = (m_width * m_channels) / 4.0f; printf("file: %20s \tchannels: %d, Divisible by 4: %s, width: %d, height: %d, widthXheight: %d\n", path.c_str(), m_channels, check == ceilf(check) ? "yes" : "no", m_width, m_height, m_width * m_height); /* //The length of the pixes row is multiple of 4. if ( check == ceilf(check) ) { GLCall(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); } //It's NOT!!!! else { GLCall(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); } */ //Image loaded successfully. if (data) { //Generate the texture and bind it. GLCall(glGenTextures(1, &m_id)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); /* //Not Transparent texture. if (m_channels == 3) { GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data)); } //Transparent texture. else if (m_channels == 4) { GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)); } else { throw EngineError("Unsupported Channels!!!"); } */ GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)); //Texture Filters. GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); //Generate mipmaps. GLCall(glGenerateMipmap(GL_TEXTURE_2D)); } //Loading Failed. else throw EngineError("The was an error loading image: " + path); //Unbind the texture. GLCall(glBindTexture(GL_TEXTURE_2D, 0)); //Free the image data. stbi_image_free(data); } Texture::~Texture() { GLCall(glDeleteTextures(1, &m_id)); } void Texture::Bind(int unit) { GLCall(glActiveTexture(GL_TEXTURE0 + unit)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); }
      Rendering is what I expected!
      But I still can't understand why this fixes it. In the first version of my texture class, which I don't force 4 channels but instead I'm using the default channels, I'm configuring glTexImage2D the right way based on the source files channels and also I'm SURE that the width * channels of each image file is multiple of 4.But again in the second version of my texture class, which solve's the problem my mind is thinking again that this might be an alignment problem but it's not, I made sure of that.
      So what else can cause such a problem? Does anybody knows the answer?
       
      Below you will find the rest of the code:
      Vertex Shader:
      #version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in vec3 aNormal; layout(location = 2) in vec2 aTexCoord; uniform mat4 model; uniform mat4 view; uniform mat4 proj; out vec2 TexCoord; void main() { gl_Position = proj * view * model * vec4(aPos, 1.0); TexCoord = aTexCoord; }  
      Fragment Shader:
      #version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in vec3 aNormal; layout(location = 2) in vec2 aTexCoord; uniform mat4 model; uniform mat4 view; uniform mat4 proj; out vec2 TexCoord; void main() { gl_Position = proj * view * model * vec4(aPos, 1.0); TexCoord = aTexCoord; }  
      Renderer Class:
      #include "Renderer.h" Renderer::Renderer() { //Vertex Data. float vertices[] = { // positions // normals // texture coords -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f }; //Generate a VAO and a VBO. GLCall(glGenVertexArrays(1, &m_VAO)); GLCall(glGenBuffers(1, &m_VBO)); //Bind VAO and VBO. GLCall(glBindVertexArray(m_VAO)); GLCall(glBindBuffer(GL_ARRAY_BUFFER, m_VBO)); //Transfer The Data. GLCall(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW)); //Positions. GLCall(glEnableVertexAttribArray(0)); GLCall(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void *)0)); //Normals. GLCall(glEnableVertexAttribArray(1)); GLCall(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void *) 12)); //Texture Coordinates. GLCall(glEnableVertexAttribArray(2)); GLCall(glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (void *) 24)); //Unbind The Buffers. GLCall(glBindVertexArray(0)); GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); } Renderer::~Renderer() { } void Renderer::ClearScreen(float r, float g, float b) { GLCall(glClearColor(r, g, b, 1.0f)); GLCall(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } void Renderer::Update(GLFWwindow * window) { glfwSwapBuffers(window); glfwPollEvents(); } void Renderer::Render(Texture *texture, Shader *program, Camera *camera) { //Bind VAO. GLCall(glBindVertexArray(m_VAO)); //Bind The Program. program->Bind(); //Set the unit to be used on the shader. program->SetUniform1i("diffuse", 0); //Bind the texture at unit zero. texture->Bind(0); glm::mat4 model = glm::mat4(1.0f); glm::mat4 view = glm::mat4(1.0f); glm::mat4 proj = glm::mat4(1.0f); //Get The View Matrix. view = camera->GetView(); //Create The Perspective Projection. proj = glm::perspective(glm::radians(camera->m_fov), 800.0f / 600, 0.1f, 100.0f); //Set the transformation uniforms. program->SetUniformMat4f("model", model); program->SetUniformMat4f("view", view); program->SetUniformMat4f("proj", proj); //Draw Call. GLCall(glDrawArrays(GL_TRIANGLES, 0, 36)); }  
       
       
      Shader Class:
      #include "shader.h" #include "glcall.h" #include "engine_error.h" #include <fstream> #include <string> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> struct ShaderSource { std::string vertex_src; std::string fragment_src; }; static void ReadSources(std::string filename, bool is_vertex, struct ShaderSource *src) { //Create a file object. std::ifstream file; //Open the file. file.open(filename, std::ios::in); //If the file opened successfully read it. if (file.is_open()) { //Size of the file. file.seekg(0, std::ios::end); std::streampos size = file.tellg(); file.seekg(0, std::ios::beg); //Allocate memory to store the data. char *data = (char *)malloc(sizeof(char) * (size + (std::streampos)1) ); //Read the data from the file. file.read(data, size); //Close the string. data[file.gcount()] = '\0'; //Close the file. file.close(); //This was the vertex file. if (is_vertex) src->vertex_src = (const char *)data; //This was the fragment file. else src->fragment_src = (const char *)data; //Release the memory for the data since I coppied them into the ShaderSource structure. free(data); } //Problem opening the file. else throw EngineError("There was a problem opening the file: " + filename); } static unsigned int CompileShader(std::string source, GLenum type) { //__________Local Variables__________// int length, success; //__________Local Variables__________// //Create the shader object. GLCall(unsigned int shader = glCreateShader(type)); //std::string to const c string. const char *src = source.c_str(); //Copy the source code into the shader object. GLCall(glShaderSource(shader, 1, &src, NULL)); //Compile the shader. GLCall(glCompileShader(shader)); //Get the shader info length. GLCall(glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length)); //Get the shader compilations status. GLCall(glGetShaderiv(shader, GL_COMPILE_STATUS, &success)); //Compilation Failed. if (!success) { //Error string. std::string error; //Allocate memory for the info log. char *info = (char *)malloc(sizeof(char) * (length+1) ); //Get the info. GLCall(glGetShaderInfoLog(shader, length, NULL, info)); //Terminate the string. info[length] = '\0'; //Initialize the error message as vertex compilation error. if (type == GL_VERTEX_SHADER) error = "Vertex Shader compilations error: "; //Initialize the error message as fragment compilation error. else error = "Fragment Shader compilation error: "; //Add the info log to the message. error += info; //Free info. free(info); //Throw a message error. throw EngineError(error); } return shader; } static unsigned int CreateProgram(ShaderSource &src) { //__________Local Variables__________// int length, success; //__________Local Variables__________// unsigned int program = glCreateProgram(); unsigned int vertex_shader = CompileShader(src.vertex_src, GL_VERTEX_SHADER); unsigned int fragment_shader = CompileShader(src.fragment_src, GL_FRAGMENT_SHADER); GLCall(glAttachShader(program, vertex_shader)); GLCall(glAttachShader(program, fragment_shader)); GLCall(glLinkProgram(program)); GLCall(glValidateProgram(program)); //Get the shader info length. GLCall(glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length)); //Get the shader compilations status. GLCall(glGetProgramiv(program, GL_LINK_STATUS, &success)); //Linking Failed. if (!success) { //Error string. std::string error = "Linking Error: "; //Allocate memory for the info log. char *info = (char *)malloc(sizeof(char) * (length + 1)); //Get the info. GLCall(glGetProgramInfoLog(program, length, NULL, info)); //Terminate the string. info[length] = '\0'; //Add the info log to the message. error += info; //Free info. free(info); //Throw a message error. throw EngineError(error); } return program; } Shader::Shader(std::string vertex_filename, std::string fragment_filename) { //Create a ShaderSource object. ShaderSource source; //Read the sources. ReadSources(vertex_filename, true, &source); ReadSources(fragment_filename, false, &source); //Create the program. m_id = CreateProgram(source); //And start using it. this->Bind(); } Shader::~Shader() { } void Shader::Bind() { GLCall(glUseProgram(m_id)); } void Shader::SetUniform1i(std::string name, int value) { //Bind the shader. this->Bind(); //Get uniform location. GLCall(int location = glGetUniformLocation(m_id, name.c_str())); //Set the value. GLCall(glUniform1i(location, value)); } void Shader::SetUniformMat4f(std::string name, glm::mat4 mat) { //Bind the shader. this->Bind(); //Get uniform location. GLCall(int location = glGetUniformLocation(m_id, name.c_str())); //Set the mat4. GLCall(glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(mat))); } void Shader::SetUniformVec3(std::string name, glm::vec3 vec3) { //Bind the shader. this->Bind(); //Get uniform location. GLCall(int location = glGetUniformLocation(m_id, name.c_str())); //Set the Uniform. GLCall(glUniform3f(location, vec3.x, vec3.y, vec3.z)); } void Shader::SetUniform1f(std::string name, float value) { //Bind the shader. this->Bind(); //Get uniform location. GLCall(int location = glGetUniformLocation(m_id, name.c_str())); GLCall(glUniform1f(location, value)); }  
      Camera Class:
      #include "camera.h" #include <glm/gtc/matrix_transform.hpp> #include <iostream> Camera::Camera(glm::vec3 cam_pos) : m_cameraPos(cam_pos), m_pitch(0), m_yaw(-90), m_FirstTime(true), m_sensitivity(0.1), m_fov(45.0) { //Calculate last x and y. m_lastx = 800 / 2.0f; m_lasty = 600 / 2.0f; } Camera::~Camera() { } void Camera::Move(bool forward, bool backward, bool left, bool right) { //Move Forward. if (forward) m_cameraPos += m_cameraFront * m_speed; //Move Backwards. else if (backward) m_cameraPos -= m_cameraFront * m_speed; //Move Left. if (left) m_cameraPos += glm::normalize(glm::cross(m_cameraUp, m_cameraFront)) * m_speed; //Move Right. else if (right) m_cameraPos -= glm::normalize(glm::cross(m_cameraUp, m_cameraFront)) * m_speed; } glm::mat4 Camera::GetView() { return glm::lookAt(m_cameraPos, m_cameraPos + m_cameraFront, m_cameraUp); } void Camera::UpdateRotation(double xpos, double ypos) { //First time, don't do anything. if (m_FirstTime) { m_lastx = xpos; m_lasty = ypos; m_FirstTime = false; } //Get the offset for pitch and yaw. float xoffset = (xpos - m_lastx) * m_sensitivity; float yoffset = (m_lasty - ypos) * m_sensitivity; //Update lastX and lastY. m_lastx = xpos; m_lasty = ypos; //Add them to pitch and yaw. m_pitch += yoffset; m_yaw += xoffset; //Limits for pitch. if (m_pitch > 89.0f) m_pitch = 89.0f; if (m_pitch < -89.0f) m_pitch = -89.0f; //Calculate the new vector. glm::vec3 front = glm::vec3(1.0f); front.x = cos(glm::radians(m_pitch)) * cos(glm::radians(m_yaw)); front.y = sin(glm::radians(m_pitch)); front.z = cos(glm::radians(m_pitch)) * sin(glm::radians(m_yaw)); //Create the direction camera front vector. m_cameraFront = front; } void Camera::UpdateZoom(double x, double y) { m_fov -= y; if (m_fov <= 25) m_fov = 25; else if (m_fov > 45) m_fov = 45; std::cout << m_fov << std::endl; }  
×

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!