PDA

View Full Version : Texture not showing



BBeck1
06-04-2016, 05:57 PM
By the title you might think this is the same issue as the previous post. I read that one. Looks like we're doing things significantly different.

So, I'm slowly trying to add features to my basic OGL program. And I went to add a texture to the quad rather than coloring it with vertex colors. I left in place the vertex color uniform, even though it is no longer using it. If I use the VertexColor uniform to color the fragment, it works as expected. But when I try and use the sampler, it always comes out black.

The quad moves around the screen. It's clearly drawing the quad. It's just black instead of using the texture. In fact, if you change the fragment shader to use the vertex color uniform, the program draws a blue quad as expected.

I was trying to work off a tutorial that used STBI to load images. But for whatever reason, STBI did not work on my machine (probably because of the class being involved in inheritance - the tutorial I was working from was not really object oriented and certainly was not using inheritance). So, I started using FreeImage. As far as I can tell, the jpeg is loading. FreeImage correctly determines its height and width anyway. And it doesn't crash.

I've spent about 2 whole days going over this code with a fine tooth comb and googling this problem and have come up with nothing that gives me any hint as to why textures are not working for me.

It's difficult to figure out because there are no errors or crashing, just no texture. I may have made a really elementary mistake here. This is my first time trying to texture something in OGL.

I might also mention that this project is to rewrite an existing/working DX11 project in OGL 4.5, so a lot of comments were copied and pasted directly from the DX code. Don't let that throw you off.

Sorry to post all the code, but at this point, the problem could be anywhere. I could be declaring my vertex array wrong, have a bad shader, not actually loading the image, not binding the texture correctly, etc.

Can anyone tell me why I'm getting a black quad instead of a textured quad?

Let me start with my shader:

Simple.vrt


#version 450 core
layout (location = 0) in vec3 Pos;
layout (location = 1) in vec2 UV;

uniform vec2 PositionOffset;

smooth out vec2 TextureCoordinates;

void main()
{
gl_Position = vec4(Pos.x + PositionOffset.x, Pos.y + PositionOffset.y, Pos.z, 1.0f);
TextureCoordinates = UV;
};


Simple.frg


#version 450 core

smooth in vec2 TextureCoordinates;
layout (location =0) out vec4 OutputColor;


uniform vec4 VertexColor;
uniform sampler2D Image;

void main()
{
OutputColor = texture(Image, TextureCoordinates);
};



Main.cpp


#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <WinBase.h>
#include "..\System\OSClass.h"
#include "..\Game.h"


using namespace OGLGameNameSpace;
//================================================== ================================================== =================
// wWinMain()
//
// Purpose:
// This is where the code begins.
//
// Notes:
// All Windows programs must start with WinMain as the application entry point. Technically this is our application
// here, but we want to follow object oriented design principles and so we encapsulate and obfuscate everything in various
// classes that do all the work. That makes this function the conductor of this symphony. It calls each piece and maintains
// the event loop that keeps everything running.
//
// It may not be readily apparent, but the Game1.InitializeOGL() is actually calling the Initialize method of the OGLGame
// class to initialize OGL and then run Game1's initialize method.
//
// In practice, this code should not be altered at all unless you just want to tweak something. All code for the application
// should be in or called from the Game object Game1.
//
// There is quite a bit of setup for OGL. The following paths are the Additional Include paths for 64 bit. This presumes
// that you have GLFW and GLEW installed in a "Libraries" folder shared by all OGL programs.
// C:\VirtuallyProgramming\OGL\Libraries\GLFW\glfw-3.1.2.bin.WIN64\include\GLFW
// C:\VirtuallyProgramming\OGL\Libraries\GLEW\glew - 1.13.0\include\GL
//
// Additionally, these Additional Library paths have to be set as well.
// C:\VirtuallyProgramming\OGL\Libraries\GLFW\glfw-3.1.2.bin.WIN64\lib-vc2015
// C:\VirtuallyProgramming\OGL\Libraries\GLEW\glew - 1.13.0\lib\Release\x64
//
// The following Additional Dependencies must be defined:
// opengl32.lib
// glew32s.lib
// glew32.lib
// glfw3.lib
//
// And if you intend to use the lib files for static linking rather than a .DLL you have to set "GLEW_STATIC" as a
// pre-processor definition in Properties==>C++==>Preprocessor. Not doing this will result in a "glew32.dll is missing" error.
//
//
//================================================== ================================================== =================
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

OSClass OS;
Game Game1;
int ReturnCode = -1;


if (OS.Initialize())
{
if (OS.MainWindowPointer())
{
if (Game1.InitializeOGL())
{
while (!OS.ShouldClose()) //Loop until GLFW told to close.
{
OS.PollEvents();
Game1.Update();
Game1.Draw();
glfwSwapBuffers(OS.MainWindowPointer());
}
ReturnCode = 0;
}
else
{
//std::cerr << "GLEW failed to initialize.";
}
}
else
{
//std::cerr << "GLFW failed to create a window for the program.";
}

}
else
{
//std::cerr << "GLFW failed to initialize.";
}

return ReturnCode;
}



OSClass.h


#pragma once
#include <glew.h>
#include <glfw3.h>

namespace OGLGameNameSpace
{
class OSClass
{
public:
OSClass();
~OSClass();
bool Initialize(void);
bool ShouldClose(void);
GLFWwindow* MainWindowPointer(void);
void PollEvents(void);

private:
const char* APP_TITLE = "Most Basic OGL Program";
GLFWwindow* MainWindow = nullptr;
};

void glfw_OnKey(GLFWwindow* Window, int Key, int ScanCode, int Action, int Mode);

}


OSClass.cpp


#include "OSClass.h"

using namespace OGLGameNameSpace;


OSClass::OSClass()
{
}


OSClass::~OSClass()
{
glfwTerminate();
}

bool OSClass::Initialize(void)
{
bool InitializedOK = false;
if (glfwInit())
{
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

MainWindow = glfwCreateWindow(1360, 768, APP_TITLE, NULL, NULL);
if (MainWindow)
{
glfwMakeContextCurrent(MainWindow);
glfwSetKeyCallback(MainWindow, glfw_OnKey);
InitializedOK = true;
}
}

return InitializedOK;
}

bool OSClass::ShouldClose(void)
{
return (bool)glfwWindowShouldClose(MainWindow);
}

GLFWwindow* OSClass::MainWindowPointer(void)
{
return MainWindow;
}

void OSClass::PollEvents()
{
glfwPollEvents(); //Needed for callback functions.
}




void OGLGameNameSpace::glfw_OnKey(GLFWwindow* Window, int Key, int ScanCode, int Action, int Mode)
{
if (Key == GLFW_KEY_ESCAPE && Action == GLFW_PRESS) glfwSetWindowShouldClose(Window, GL_TRUE);

}



OGLGameClass.h


#pragma once
//#include "GameTimer.h"
//#include "KeyboardClass.h"
#include <glew.h>
#include <glfw3.h>
#include <glm.hpp>
#include "Texture2DClass.h"


namespace OGLGameNameSpace
{

//Predefined colors made to match XNA that you can use or not use.
//const float RGBABlue[4] = { 0.0f,0.0f,1.0f,1.0f };
//const float RGBABlack[4] = { 0.0f,0.0f,0.0f,1.0f };
//const float RGBAWhite[4] = { 1.0f,1.0f,1.0f,1.0f };
//const float RGBAGreen[4] = { 0.0f,0.5019607843137255f,0.0f,1.0f };
//const float RGBARed[4] = { 1.0f,0.0f,0.0f,1.0f };
//const float RGBAYellow[4] = { 1.0f,1.0f,0.0f,1.0f };
//const float RGBACornFlowerBlue[4] = { 0.392156862745098f,0.5843137254901961f,0.929411764 7058824f,1.0f };

//================================================== ================================================== =================
// OGLGame
//
// Purpose:
// To encapsulate GLEW and any OGL initialization.
//
// Notes:
// In order to write a "game" the programmer is expected to create a game object that inherits from this class which will
// hook it in to this whole system. The main code will call this object to initialize OGL and this code will call the game
// object that contains all the code for the game.
//
// This is very basic bare bones code not designed to handle every feature of every graphics card. You may want to expand
// on this code a little bit to do things like checking whether the graphics card supports anti-aliasing or not before turning
// anti-aliasing on. However, by making a few minor adjustments such as matching the screen resolution to your own, this should
// give you the code needed to start every DX project you might want to do and run it on your computer.
//
//================================================== ================================================== =================
class OGLGameClass
{
public:
OGLGameClass();
~OGLGameClass();
//bool ShouldClose(void);
bool InitializeOGL(void);
bool InitializeGameClass(void);
virtual bool Initialize();
virtual bool LoadContent();
virtual void UnloadContent();
virtual void Update();
virtual void Draw();
virtual void Shutdown();
GLdouble GetTime(void);
// GameTimer Timer;
//protected:
//
// KeyboardClass Keyboard;
//
};
//================================================== ================================================== =================
}



OGLGameClass.cpp


#include "OGLGameClass.h"

using namespace OGLGameNameSpace;


//================================================== ================================================== =================
// Constructor
//
// Purpose:
// Initialize basic values for the OGLGame object.
//
// Notes:
//
//
//================================================== ================================================== =================
OGLGameClass::OGLGameClass()
{
glewExperimental = GL_TRUE;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Destructor
//
// Purpose:
// Cleanup of any class specific code.
//
// Notes:
// Not used, but you are welcome to use it if you like.
//
//================================================== ================================================== =================
OGLGameClass::~OGLGameClass()
{
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// OGLGame::InitializeOGL()
//
// Purpose:
//
//
// Input:
// None.
//
// Output:
// bool - never fail.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an initialization method.
//
//================================================== ================================================== =================
bool OGLGameClass::InitializeOGL(void)
{
bool Initialized = false; //Return value - must be true if properly initialized.

if (glewInit() == GLEW_OK)
{
Initialized = true;
Initialized = InitializeGameClass();
}


return Initialized;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// OGLGame::InitializeGameClass()
//
// Purpose:
// To run the programmer's custom initialization code at startup.
//
// Input:
// None.
//
// Output:
// bool - true if no errors occured.
//
// Notes:
// This method calls into the Game class that inheriets from this class which allows the programmer to include their own
// code to initialize and load art assets. The timer object is also initialized to start the clock so that we can know
// how much time has passed between each frame.
//
// All of the method that this method calls out to return false if there was a failure or error. So, if everything returns
// true, this method will return true also to indicate that everything initialized properly.
//
//================================================== ================================================== =================
bool OGLGameClass::InitializeGameClass()
{
bool Initialized = false; //Return value - must be true if properly initialized.

if (Initialize()) //Call Game class's Initialize method to do programmer defined initialization.
{
if (LoadContent()) //Call Game class's LoadContent method to load programmer defined game assets.
{
Initialized = true; //Initialize the Game Timer and we're good.
}
}
return Initialized;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// OGLGame::Initialize()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have an Initialize() method of its own.
//
// Input:
// None.
//
// Output:
// bool - never fail.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an initialization method.
//
////================================================== ================================================== =================
bool OGLGameClass::Initialize(void)
{
bool Initialized = false; //Return value - must be true if properly initialized.

Initialized = InitializeGameClass();

return Initialized;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// OGLGame::LoadContent()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have a LoadContent() method of its own.
//
// Input:
// None.
//
// Output:
// bool - never fail.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement a load content method.
//
//================================================== ================================================== =================
bool OGLGameClass::LoadContent()
{
return true;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// OGLGame::UnloadContent()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have an UnloadContent() method of its own.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an UnloadContent method.
//
//================================================== ================================================== =================
void OGLGameClass::UnloadContent()
{

}
//================================================== ================================================== =================


//================================================== ================================================== =================
// OGLGame::Update()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have an Update() method of its own.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an UnloadContent method.
//
//================================================== ================================================== =================
void OGLGameClass::Update()
{


}
//================================================== ================================================== =================


//================================================== ================================================== =================
// OGLGame::Draw()
//
// Purpose:
// Mostly just a place holder to make the Game object that inherits from this class have an Draw() method of its own.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// This is a virtual method to make the child object that inherits from this object implement an UnloadContent method.
//
//================================================== ================================================== =================
void OGLGameClass::Draw()
{


}
//================================================== ================================================== =================


//================================================== ================================================== =================
// OGLGame::Shutdown()
//
// Purpose:
// To cleanly shut down DirectX and our application.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// The first thing this method does is call the Game object that inherits from this class's UnloadContent() method to
// free up any memory used for art assets by that object. Then it basically just shuts down DX and releases any memory
// associated with DX.
//
//================================================== ================================================== =================
void OGLGameClass::Shutdown()
{
UnloadContent();

//Keyboard.Shutdown(); //Release DirectInput and the keyboard device.

}
//================================================== ================================================== =================


GLdouble OGLGameClass::GetTime(void)
{
return glfwGetTime();
}



Game.h


#pragma once
#include "System\OGLGameClass.h"
//#include "System\Texture2DClass.h"
#include "System\ShaderClass.h"
//#include "SceneObjectClass.h" //New class to allow us to put objects in the scene.
//#include "TextureClass.h"
//#include "SkyBoxClass.h"
//#include "ModelCompiler.h"
namespace OGLGameNameSpace
{

//================================================== ================================================== =================
// Game : public
//
// Purpose:
// This is where this frame work is modified to create a game.
//
// Notes:
// I am changing the comments on this class from the previous example. You modify this class in order to create your
// game. So, you will quickly notice the code in this class has been modified to create a scene with objects in it that
// you can move around in using either the keyboard or an XBox 360 controller if you have one hooked up to your PC (which
// I HIGHLY recommend although for programming it can be more convienient not to have to leave the keyboard). For actual
// games I would prefer to see the XBox 360 controller be the primary, if not sole input device.
//
// Notice that this class inherits from OGLGame. Because of that, the methods of this class are automatically
// called at the appropriate time. You just have to fill these methods with instructions to create a game.
//
//================================================== ================================================== =================
class Game : public OGLGameNameSpace::OGLGameClass
{
public:
Game(void);
virtual ~Game(void);
bool Initialize(void);
bool LoadContent(void);
void UnloadContent(void);
void Update();
void Draw();

private:
GLuint vbo, vao, ibo;
ShaderClass Shader;
Texture2DClass Texture;
//SceneObjectClass Triangle;
//SceneObjectClass Ground;
//SceneObjectClass YellowCube;
//TextureClass GrassTexture;
//SkyBoxClass SkyBox;
//XMMATRIX View;
//XMMATRIX Projection;
//XMVECTOR CameraFacingNormal;
//XINPUT_STATE Controller1State;
//XINPUT_STATE PreviousController1State;
//float CameraHeight;
//XMVECTOR CameraPosition;
//float CameraTilt;
//bool InFullScreenMode;
//XMFLOAT3 DiffuseLightDirection;
//XMFLOAT4 AmbientLightColor;
//XMFLOAT4 DiffuseLightColor;
//ModelCompiler ModelBuilder;
//RigidModel Model;

#define VERTICESINCUBE 36 //Constant to make it more clear what this number is.
};
//================================================== ================================================== =================
}


Game.cpp


#include "Game.h"

using namespace OGLGameNameSpace;


Game::Game(void)
{
}


Game::~Game(void)
{
}


//================================================== ================================================== =================
// Game::Initialize()
//
// Purpose:
// To allow any code you want to run once at startup not associated with art assets.
//
// Input:
// None.
//
// Output:
// bool - The program will close if it returns false assuming a catastrophic error has occured.
//
// Notes:
//
//
//================================================== ================================================== =================
bool Game::Initialize()
{
bool GameObjectInitializedProperly = false; //Must be set to true to keep the program from closing.


//Projection = XMMatrixPerspectiveFovRH(XM_PIDIV4, DXViewPort.Width / DXViewPort.Height, 0.1f, 5000.0f); //Create a RH Perspective Projection matrix.

GameObjectInitializedProperly = true; // Shader.Initialize(GraphicsDevice, GraphicsDeviceContext); //Do any initialization required for the Shader class and fail if it fails.

return GameObjectInitializedProperly;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::LoadContent()
//
// Purpose:
// To allow any code you want to run once at startup associated with art assets.
//
// Input:
// None.
//
// Output:
// bool - The program will close if it returns false assuming a catastrophic error has occured.
//
// Notes:
// Here we are creating all the objects in the scene. The switch statement makes it easy to manage this and add more
// objects of your own. You merely define all the objects vertices it will need to be drawn and then list the index order
// of every vertex to specify what order it will be drawn in. These examples merely count the number of vertices for the
// index which is largely pointless. However, the design means you "could" reuse vertices or specify them out of order and
// use the index buffer to specify what order to draw them in.
//
// You'll quickly notice that the number of vertices you have to specify for even the most simple of objects is nearly
// unacceptable. There are better ways, but we're learning the most straight forward way here which we will build on later.
//
// Textures were created with Paint.Net by saving as .DDS files with mip maps.
//
//================================================== ================================================== =================
bool Game::LoadContent()
{
bool NoCatastrophicFailuresOccured = false; //Close the program if even one mesh fails to initialize correctly.
vbo = 0;
vao = 0;
ibo = 0;

glClearColor(0.392156862745098f, 0.5843137254901961f, 0.9294117647058824f, 1.0f);

GLfloat VertexBuffer[] = {
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f
};

GLuint Indices[] = {
0,1,2,
0,2,3
};

glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexBuffer), VertexBuffer, GL_STATIC_DRAW);

glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, nullptr);
glEnableVertexAttribArray(0);

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLvoid*)(sizeof(GLfloat) * 3));
//glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex, (GLvoid*)(sizeof(GLfloat) * 3));
glEnableVertexAttribArray(1);

glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);

Texture.Load("Textures/Dog.jpg", true);

NoCatastrophicFailuresOccured = Shader.LoadShader("Shaders/Simple.vrt", "Shaders/Simple.frg");


return NoCatastrophicFailuresOccured;
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::UnloadContent()
//
// Purpose:
// To allow any code you want to run as the program closes in order to cleanup.
//
// Input:
// None.
//
// Output:
// None.
//
// Notes:
// All the game objects you create need their Shutdown() methods called to release the associate vertex and index buffers.
// Since Shaders are owned by this class, it is also responsible for calling the Shutdown() method of any shaders created.
//
//================================================== ================================================== =================
void Game::UnloadContent()
{
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ibo);
}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::Update()
//
// Purpose:
// To do everything that needs to be done in one frame except draw stuff to the screen.
//
// Input:
// float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
// Output:
// None.
//
// Notes:
// This is where most of your game code will be. It gets called every frame to change anything that needs to be changed
// or done during that frame. It runs in a loop that needs to be called at least 30 times per second but there's nothing
// to control how often it gets called. Things would move at unpredictable rates if we did not use TimeDelta to take in to
// account the amount of time that has passed since the last frame.
//
// We start out by processing the keyboard and game controller input to change the camera's position and direction. You
// can also toggle full screen on and off.
//
// The camera movement this frame is stored as a 3D vector that we treat more like a 2D vector. The facing normal should
// point in the direction we want the camera to face. And as a normal should have a length of 1. Any movement during the
// frame is cumulative from the various controls. When you move it uses either the CameraFacingNormal or a normal rotated 90
// degrees away from the camera facing. It's basic vector addition to add the movement to the camera position.
//
// XMMatrixLookAtRH is used to create a view matrix to simulate the camera every frame. Generally, I would say it is a
// good idea to not continuously recreate the view matrix but it's easier then maintaining a view matrix between frames and
// this is really early in this tutorial series.
//
// Finally some very simple rigid animation is thrown in to show you not only how to do it, but that it can be done and how
// easy it is to do. Experiment by turning the rotations off and on and changing their directions and speed.
//
// The scene is lit with a simple Blinn-Phong shader that has "directional" lighting as opposed to point lights or
// spot lights. Directional lighting is nothing more than a direction that the light shines in. It is a normalized vector
// describing a direction and it has a color. That's all it is. Look at the shader for more detail. By rotating that direction
// the light source seems to orbit the scene similar to a day and night cycle except the light shines through solid objects.
//
//================================================== ================================================== =================
void Game::Update()
{

//if (!Keyboard.Update()) PostQuitMessage(0); //If DirectInput has crashed, go ahead and shutdown the app. Otherwise, get the current keyboard state.



//if (Keyboard.KeyPressed(DIK_LSHIFT)) //If these keys are pressed while the left shift key is pressed...
//{
// if (Keyboard.KeyPressed(DIK_D)) CameraMovement += XMVector4Transform(CameraFacingNormal, XMMatrixRotationZ(XM_PIDIV2)); //Move right. Pi over 2 is 90 degrees.
// if (Keyboard.KeyPressed(DIK_A)) CameraMovement += XMVector4Transform(CameraFacingNormal, XMMatrixRotationZ(-XM_PIDIV2)); //Move left.
//}


}
//================================================== ================================================== =================


//================================================== ================================================== =================
// Game::Draw()
//
// Purpose:
// To do draw a single frame on the computer screen.
//
// Input:
// float TimeDelta - Amount of time that has passed since the last frame occured in milliseconds.
//
// Output:
// None.
//
// Notes:
// Since all the drawing code is tucked away neatly elsewhere, this method ends up being very short and sweet. There's
// basically nothing to it. The first two lines clear the backbuffer in corn flower blue which is a constant. And then
// it clears the depth stencil. You can clear the screen in any color you like.
//
// Then each game object's Draw() method needs to be called to tell it to draw it self to the back buffer. The parameters
// for the shader have to be sent here.
//
// Finaly, the swapchain is told to make the backbuffer the frontbuffer and draw it to the screen by presenting it and
// the image appears on the computer monitor.
//
//================================================== ================================================== =================
void Game::Draw()
{
glClear(GL_COLOR_BUFFER_BIT);


GLfloat Time = GetTime();
GLfloat BlueColor = (sin(Time) / 2) + 0.5f;
glm::vec2 Position;
Position.x = (sin(Time) / 2);
Position.y = (cos(Time) / 2);

Shader.Use();
Shader.SetUniform("VertexColor", glm::vec4(0.0f, 0.0f, BlueColor, 1.0f));
Shader.SetUniform("PositionOffset", glm::vec2(Position.x, Position.y));
//Shader.SetUniform("Texture", Texture.GetTextureID());
//Shader.SetUniform("Texture", Texture.GetHandle());

//Shader.SetUniform("Texture", 0);

Texture.Bind(0);


glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0); //Release it.


}
//================================================== ================================================== =================


ShaderClass.h


#pragma once
#include <glew.h>
#include <fstream>
#include <sstream>
#include <glm.hpp>
#include <type_ptr.hpp>
#include <map>

namespace OGLGameNameSpace
{
class ShaderClass
{
public:
ShaderClass();
~ShaderClass();
bool LoadShader(std::string VertexShaderFileName, std::string FragmentShaderFileName);
void Use();
void SetUniform(std::string Name, GLint IntegerValue);
void SetUniform(std::string Name, glm::vec2& Tuple);
void SetUniform(std::string Name, glm::vec3& Tuple);
void SetUniform(std::string Name, glm::vec4& Tuple);
void SetUniform(std::string Name, glm::mat4& Matrix);
//void SetUniform(std::string Name, )

private:
GLuint Shader;
enum ShaderType { Vertex, Fragment, Program };
std::string LoadSourceCode(std::string SourceCodeFile);
GLuint CompileShader(std::string SourceCodeFile, ShaderType Type);
bool NoCompileErrors(GLuint Shader, ShaderType Type);
GLint GetUniformLocation(std::string Name);
std::map<std::string, GLint> UniformLocations;

};
}



ShaderClass.cpp


#include "ShaderClass.h"

using namespace OGLGameNameSpace;


ShaderClass::ShaderClass()
{
Shader = 0;
}

ShaderClass::~ShaderClass()
{
glDeleteProgram(Shader);
}


bool ShaderClass::LoadShader(std::string VertexShaderFileName, std::string FragmentShaderFileName)
{
bool NoCatastrophicFailuresOccured = false;
GLuint VertexShader;
GLuint FragmentShader;
GLuint ShaderProgram;
GLint Result;


VertexShader = CompileShader(VertexShaderFileName, ShaderType::Vertex);
if (NoCompileErrors(VertexShader, ShaderType::Vertex))
{
FragmentShader = CompileShader(FragmentShaderFileName, ShaderType::Fragment);
if (NoCompileErrors(FragmentShader, ShaderType::Fragment))
{
ShaderProgram = glCreateProgram();
glAttachShader(ShaderProgram, VertexShader);
glAttachShader(ShaderProgram, FragmentShader);
glLinkProgram(ShaderProgram);
if (NoCompileErrors(ShaderProgram, ShaderType::Program))
{
Shader = ShaderProgram;
NoCatastrophicFailuresOccured = true;
}
}
glDeleteShader(FragmentShader);
}

glDeleteShader(VertexShader);

UniformLocations.clear();

return NoCatastrophicFailuresOccured;
}


void ShaderClass::Use()
{
if (Shader > 0)
{
glUseProgram(Shader);
}
}


std::string ShaderClass::LoadSourceCode(std::string SourceCodeFile)
{
std::ifstream ShaderFile;
std::stringstream CodeText;

try
{
ShaderFile.open(SourceCodeFile, std::ios::in);
if (!ShaderFile.fail())
{
CodeText << ShaderFile.rdbuf(); //Takes all the source code and puts it in a single stringstream.
}
ShaderFile.close();
}
catch (std::exception Exception)
{

}
return CodeText.str(); //Convert the stringstream of the source code into a std::string.
}


GLuint ShaderClass::CompileShader(std::string SourceCodeFile, ShaderType Type)
{
GLuint ShaderHandle = 0;
std::string ShaderSourceCode;
const GLchar* SourceCodePointer;


ShaderSourceCode = LoadSourceCode(SourceCodeFile);
SourceCodePointer = ShaderSourceCode.c_str();

switch (Type)
{
case ShaderClass::Vertex:
ShaderHandle = glCreateShader(GL_VERTEX_SHADER);
break;
case ShaderClass::Fragment:
ShaderHandle = glCreateShader(GL_FRAGMENT_SHADER);
break;
default:
break;
}

glShaderSource(ShaderHandle, 1, &SourceCodePointer, nullptr);
glCompileShader(ShaderHandle);


return ShaderHandle;
}


bool ShaderClass::NoCompileErrors(GLuint Shader, ShaderType Type)
{
GLint Status = 0;
GLint Length = 0;
GLchar InfoLog[512];
bool NoErrorsFound = false;

if (Type == ShaderType::Program)
{
glGetProgramiv(Shader, GL_LINK_STATUS, &Status);
if (Status == GL_FALSE)
{
glGetProgramiv(Shader, GL_INFO_LOG_LENGTH, &Status);
glGetProgramInfoLog(Shader, sizeof(InfoLog), nullptr, InfoLog);
}
else
{
NoErrorsFound = true;
}
}
else
{
glGetShaderiv(Shader, GL_COMPILE_STATUS, &Status);
if (Status == GL_FALSE)
{
glGetShaderiv(Shader, GL_INFO_LOG_LENGTH, &Status);
std::string ErrorLog(Status, ' ');
glGetProgramInfoLog(Shader, Status, &Status, &ErrorLog[0]);
}
else
{
NoErrorsFound = true;
}
}

return NoErrorsFound;
}


GLint ShaderClass::GetUniformLocation(std::string Name)
{
std::map<std::string, GLint>::iterator UniformsIterator = UniformLocations.find(Name);
if (UniformsIterator == UniformLocations.end())
{
UniformLocations[Name] = glGetUniformLocation(Shader, Name.c_str());
}
return UniformLocations[Name];
}


void ShaderClass::SetUniform(std::string Name, GLint IntegerValue)
{
GLint Location;

Location = GetUniformLocation(Name);
glUniform1i(Location, IntegerValue);
}


void ShaderClass::SetUniform(std::string Name, glm::vec2& Tuple)
{
GLint Location;

Location = GetUniformLocation(Name);
glUniform2f(Location, Tuple.x, Tuple.y);
}


void ShaderClass::SetUniform(std::string Name, glm::vec3& Tuple)
{
GLint Location;

Location = GetUniformLocation(Name);
glUniform3f(Location, Tuple.x, Tuple.y, Tuple.z);
}


void ShaderClass::SetUniform(std::string Name, glm::vec4& Tuple)
{
GLint Location;

Location = GetUniformLocation(Name);
glUniform4f(Location, Tuple.x, Tuple.y, Tuple.z, Tuple.w);
}


void ShaderClass::SetUniform(std::string Name, glm::mat4& Matrix)
{
GLint Location = GetUniformLocation(Name);
glUniformMatrix4fv(Location, 1, GL_FALSE, glm::value_ptr(Matrix));
}



Texture2DClass.h


#pragma once
#include <string>
#include <glew.h>

#include <FreeImage.h>


namespace OGLGameNameSpace
{

class Texture2DClass
{
public:
Texture2DClass();
~Texture2DClass();
bool Load(std::string File, bool GenerateMipMaps = true);
void Bind(GLuint TexUnit = 0);
GLuint GetHandle();
GLuint GetTextureID();

private:
GLuint TextureHandle;
GLuint TextureID;
};

}


Texture2DClass.cpp


#include "Texture2DClass.h"

//#ifndef STBI_INCLUDE_STB_IMAGE_H
//#define STBI_INCLUDE_STB_IMAGE_H
//#define STB_IMAGE_IMPLEMENTATION //Required by STB.
//#define STBI_FAILURE_USERMSG
//#include <stb_image.h>
//#endif STBI_INCLUDE_STB_IMAGE_H

using namespace OGLGameNameSpace;


Texture2DClass::Texture2DClass() : TextureHandle(0)
{
}


Texture2DClass::~Texture2DClass()
{
}


bool Texture2DClass::Load(std::string File, bool GenerateMipMaps)
{
int Width;
int Height;
int Components;
bool TextureLoaded = false;

FREE_IMAGE_FORMAT ImageFormat = FreeImage_GetFileType(File.c_str(), 0);
FIBITMAP* ImageBitMap = FreeImage_Load(ImageFormat, File.c_str());

FIBITMAP* TempPointer = ImageBitMap;
ImageBitMap = FreeImage_ConvertTo32Bits(ImageBitMap);
FreeImage_Unload(TempPointer);

Width = FreeImage_GetWidth(ImageBitMap);
Height = FreeImage_GetHeight(ImageBitMap);

GLubyte* Texture = new GLubyte[4 * Width*Height];
char* Pixels = (char*)FreeImage_GetBits(ImageBitMap);

for (int x = 0; x < (Width*Height); x++)
{
Texture[x * 4 + 0] = Pixels[x * 4 + 2];
Texture[x * 4 + 1] = Pixels[x * 4 + 1];
Texture[x * 4 + 2] = Pixels[x * 4 + 0];
Texture[x * 4 + 3] = Pixels[x * 4 + 3];
}

TextureID = 0;
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_2D, TextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
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, (GLvoid*)Texture);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, Width, Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, Pixels);
glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//glBindTexture(GL_TEXTURE_2D, 0);
if (GenerateMipMaps) glGenerateMipmap(GL_TEXTURE_2D);
TextureLoaded = true;


//unsigned char* ImageData = stbi_load(File.c_str(), &Width, &Height, &Components, STBI_rgb_alpha);
//if (ImageData != NULL)
//{
// glGenTextures(1, &TextureHandle);
// glBindTexture(GL_TEXTURE_2D, TextureHandle);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 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, ImageData);
// if (GenerateMipMaps) glGenerateMipmap(GL_TEXTURE_2D);
// stbi_image_free(ImageData);
// glBindTexture(GL_TEXTURE_2D, 0);
// TextureLoaded = true;
//}

return TextureLoaded;
}


void Texture2DClass::Bind(GLuint TexUnit)
{
glActiveTexture(GL_TEXTURE0 + TexUnit);
glBindTexture(GL_TEXTURE_2D, TextureHandle);
}


GLuint Texture2DClass::GetHandle()
{
return TextureHandle;
}


GLuint Texture2DClass::GetTextureID()
{
return TextureID;
}

Hermannicus
06-04-2016, 09:27 PM
uniform sampler2D Image;

//Shader.SetUniform("Texture", 0);

If you name your uniform var "Image", then you should also use "Image" when setting it.

GClements
06-04-2016, 09:29 PM
So, I'm slowly trying to add features to my basic OGL program.

Sorry, but it ceases to qualify as "basic" once the number of source files exceeds one. With thirteen source files, it's either moderately complex, or over-engineered, or both. Either way, you should put some amount of effort into simplifying it before asking other people to read it.



So, I started using FreeImage. As far as I can tell, the jpeg is loading. FreeImage correctly determines its height and width anyway. And it doesn't crash.

If you unsure as to whether image loading is working correctly (i.e. you can't absolutely rule out the possibility that there may be an issue with it), get rid of image loading entirely. Simply declare an array of unsigned char and fill it with a chequerboard, gradient, random values or whatever.



Sorry to post all the code, but at this point, the problem could be anywhere. I could be declaring my vertex array wrong, have a bad shader, not actually loading the image, not binding the texture correctly, etc.

That's why you pare the program down to the absolute minimum. Either you'll discover the point where the problem lies, or you'll at least have reduced the effort required of someone examining your program to a minimum.



Can anyone tell me why I'm getting a black quad instead of a textured quad?

Well, there's nothing wrong with the shaders. I'm not going to bother looking at the rest because of the extreme degree of obfuscation.

BBeck1
06-05-2016, 07:29 AM
uniform sampler2D Image;

//Shader.SetUniform("Texture", 0);

If you name your uniform var "Image", then you should also use "Image" when setting it.

That's from hacking on this for two weeks (two weekends actually). I named it Texture and then thought perhaps that was a reserved word and so I changed it to image to see if that makes a difference. It didn't, but that's why it has two different names in the code. Texture is currently commented out. That's also because it has been unclear whether you really need to set the uniform for a texture, especially in the 0 slot. I've seen indication that you don't have to since it's in the 0 slot and that you only need that when you start using the other texture slots. So, I've tried it with setting the uniform and not setting it.

I've tried just about everything. But I think it's a good idea to fill the texture with data rather than working off an image file. That should be an interesting exercise anyway.

BBeck1
06-05-2016, 07:56 AM
Hmm... I tried filling the texture with byte values (a solid grey color) to see if it's a problem loading the image with the following code and I still got a black quad:



Height = 256;
Width = 256;
GLubyte* Texture = new GLubyte[4 * Width*Height];
int PicSize = Width * Height * 4;
for (int x = 0; x < Height; x++)
{
for (int y = 0; y < Width * 4; y += 4)
{
Texture[x * y + 0] = 200;
Texture[x * y + 1] = 200;
Texture[x * y + 2] = 200;
Texture[x * y + 3] = 255;
}
}

TextureID = 0;
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_2D, TextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
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, (GLvoid*)Texture);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, Width, Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, Pixels);
glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
if (GenerateMipMaps) glGenerateMipmap(GL_TEXTURE_2D);


I also uncommented that set Uniform and changed it to "Image" and that didn't help either.

Most of what I found on the Internet googling suggested that you have to clamp to edges if the picture size isn't a perfect factor of 2, which my original image was not. So, the other day I added:


glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);


That was really the only common suggestion on this problem. So whatever it is, it must be pretty simple.

Ruling out the texture being the problem seems to solidly point to the texture not being bound to the graphics card correctly. I mean the program works fine other than it's displaying a black quad instead of a textured quad. It's just not feeding the sampler.

BBeck1
06-05-2016, 10:09 AM
I've noticed the OGL doesn't really return any indication that it failed. I was noticing I saw no error checking in pretty much any OGL example I've seen ever. And sleeping on it, I'm thinking "That can't be right." Sure enough, it returns void on just about everything. However, looking into it a bit deeper, I found out that since 4.3 they've addressed this by putting in the ability to define an error routine as a callback function. Took me a little bit to get that implemented, but I got that implemented and got a slew of errors back:



---------------------opengl-callback-start------------
message: GL_INVALID_ENUM error generated. Operation is not valid from the core profile.
type: ERROR
id: 1280
severity: HIGH
---------------------opengl-callback-end--------------
---------------------opengl-callback-start------------
message: Buffer detailed info: Buffer object 1 (bound to GL_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.
type: OTHER
id: 131185
severity:
---------------------opengl-callback-end--------------
---------------------opengl-callback-start------------
message: Buffer detailed info: Buffer object 2 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.
type: OTHER
id: 131185
severity:
---------------------opengl-callback-end--------------
---------------------opengl-callback-start------------
message: GL_INVALID_OPERATION error generated. <texture> does not refer to an existing texture object
type: ERROR
id: 1282
severity: HIGH
---------------------opengl-callback-end--------------



Now if I can just figure out which lines of code triggered these...

BBeck1
06-05-2016, 11:09 AM
Ok. So, the first error seems to come from this line:


if (glewInit() == GLEW_OK)



---------------------opengl-callback-start------------
message: GL_INVALID_ENUM error generated. Operation is not valid from the core profile.
type: ERROR
id: 1280
severity: HIGH
---------------------opengl-callback-end--------------


Interesting. But that looks like a false lead. I'll put that one off until later. (And if anyone knows why that might throw an error, let me know.)

Ok. Now we're on to something. The next error was generated by this line of code. Although, now that I think about it, that looks like more of a warning than an error. Hmm... Maybe look a bit further.


glGenVertexArrays(1, &vao);



---------------------opengl-callback-start------------
message: Buffer detailed info: Buffer object 1 (bound to GL_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.
type: OTHER
id: 131185
severity:
---------------------opengl-callback-end--------------


Here's another one, but again it appears to be a warning and not an error. (Yes, I declared it static on purpose. I have no plans to edit it later.)


glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);



---------------------opengl-callback-start------------
message: Buffer detailed info: Buffer object 2 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.
type: OTHER
id: 131185
severity:
---------------------opengl-callback-end--------------


Ok. I think here is the culprit;


glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);



---------------------opengl-callback-start------------
message: GL_INVALID_OPERATION error generated. <texture> does not refer to an existing texture object
type: ERROR
id: 1282
severity: HIGH
---------------------opengl-callback-end--------------


That line does not have a variable in it. So, I think it must be referring to the previous line of code at least somewhat. (Edit: Turns out I was wrong about that.)


glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)Texture);


Not sure what <texture> is. Is that my name or just a standard error? My Texture variable was defined as


GLubyte* Texture = new GLubyte[4 * Width*Height];


So Google is our friend; when I google glTexImage it says it wants a "const GLvoid*" of Image data. Hmmm...
https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml

Well, technically a GLubyte* is not a GLvoid pointer. But presumably it's just saying it doesn't care as long as that GL_RGBA parameter describes the correct layout in the buffer and it has the correct size. And there's not some scope issue, because I declare the buffer just a few lines above that code. Back to Google with "GL_INVALID_OPERATION error generated. <texture> does not refer to an existing texture object".

BINGO! Got it! It's exactly right about what line it's on. Amazing what having some sort of error checking does, huh.



glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //WRONG!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //Right!


Interesting that it compiled and ran that way. Autocomplete probably hoodwinked me on this one. (Edit:I later figured out that not only is this a bad line of code, it's entirely redundant with the MIN_FILTER definition just a couple lines above it which was written correctly. There's also a question of when to do the Bind because apparently it has to be done after the parameters.)

Ah! Looks like there was one other change that was a problem. While googling I saw someone suggest that problem was due to binding the texture before this. I move "glBindTexture(GL_TEXTURE_2D, TextureID);" to after the parameters without mentioning that. When I moved it back, it broke again. So, looks like the problem was two fold: glTexParameteri and needing to bind after the parameters.

So, that gave me my grey box as expected. Let's see if I can get it to use the actual image with FreeImage.

Hurray! It works!

Well, the good news is that I figured out how to get error messages out of OGL finally. The way I have it setup, it doesn't give me the place in the code that is failing although I can set a break point and look at the call stack. But if you step through one line at a time and watch the Output window, you can see which line of code generates the output text. Here's the code if you're interested.
OSClass.h


#pragma once
#include <glew.h>
#include <glfw3.h>
#include <Windows.h>
#include <string>

namespace OGLGameNameSpace
{
class OSClass
{
public:
OSClass();
~OSClass();
bool Initialize(void);
bool ShouldClose(void);
GLFWwindow* MainWindowPointer(void);
void PollEvents(void);

private:
const char* APP_TITLE = "Most Basic OGL Program";
GLFWwindow* MainWindow = nullptr;
};

void glfw_OnKey(GLFWwindow* Window, int Key, int ScanCode, int Action, int Mode);
void APIENTRY openglCallbackFunction(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const void* userParam);
}


OSClass.cpp


#include "OSClass.h"

using namespace OGLGameNameSpace;


OSClass::OSClass()
{
}


OSClass::~OSClass()
{
glfwTerminate();
}

bool OSClass::Initialize(void)
{
bool InitializedOK = false;
if (glfwInit())
{
#if _DEBUG
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
#endif
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
MainWindow = glfwCreateWindow(1360, 768, APP_TITLE, NULL, NULL);
if (MainWindow)
{
glfwMakeContextCurrent(MainWindow);
glfwSetKeyCallback(MainWindow, glfw_OnKey);
InitializedOK = true;
#if _DEBUG
PFNGLDEBUGMESSAGECALLBACKPROC _glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)wglGetProcAddress("glDebugMessageCallback");
if (_glDebugMessageCallback)
{
OutputDebugString(L"Register OpenGL debug callback \n");
//std::cout << "Register OpenGL debug callback " << std::endl;
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
_glDebugMessageCallback(OGLGameNameSpace::openglCa llbackFunction, nullptr);
GLuint unusedIds = 0;
/*glDebugMessageControl(GL_DONT_CARE,
GL_DONT_CARE,
GL_DONT_CARE,
0,
&unusedIds,
true);*/
}
else
OutputDebugString(L"glDebugMessageCallback not available\n");
//std::cout << "glDebugMessageCallback not available" << std::endl;
#endif

}
}

return InitializedOK;
}

bool OSClass::ShouldClose(void)
{
return (bool)glfwWindowShouldClose(MainWindow);
}

GLFWwindow* OSClass::MainWindowPointer(void)
{
return MainWindow;
}

void OSClass::PollEvents()
{
glfwPollEvents(); //Needed for callback functions.
}




void OGLGameNameSpace::glfw_OnKey(GLFWwindow* Window, int Key, int ScanCode, int Action, int Mode)
{
if (Key == GLFW_KEY_ESCAPE && Action == GLFW_PRESS) glfwSetWindowShouldClose(Window, GL_TRUE);

}


static void APIENTRY OGLGameNameSpace::openglCallbackFunction(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const void* userParam) {

//std::cout << "---------------------opengl-callback-start------------" << std::endl;
//std::cout << "message: " << message << std::endl;
//std::cout << "type: ";
OutputDebugString(L"---------------------opengl-callback-start------------\n");
OutputDebugString(L"message: ");
std::string GLMessage(message);
std::wstring Message;
Message.assign(GLMessage.begin(), GLMessage.end());
OutputDebugString(Message.c_str());
OutputDebugString(L"\n");
OutputDebugString(L"type: ");
switch (type)
{
case GL_DEBUG_TYPE_ERROR:
//std::cout << "ERROR";
OutputDebugString(L"ERROR");
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
//std::cout << "DEPRECATED_BEHAVIOR";
OutputDebugString(L"DEPRECATED BEHAVIOR");
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
//std::cout << "UNDEFINED_BEHAVIOR";
OutputDebugString(L"UNDEFINED BEHAVIOR");
break;
case GL_DEBUG_TYPE_PORTABILITY:
//std::cout << "PORTABILITY";
OutputDebugString(L"PORTABILITY");
break;
case GL_DEBUG_TYPE_PERFORMANCE:
//std::cout << "PERFORMANCE";
OutputDebugString(L"PERFORMANCE");
break;
case GL_DEBUG_TYPE_OTHER:
//std::cout << "OTHER";
OutputDebugString(L"OTHER");
break;
}
//std::cout << std::endl;
OutputDebugString(L"\n");

//std::cout << "id: " << id << std::endl;
OutputDebugString(L"id: ");
std::wstring ID = std::to_wstring(id);
OutputDebugString(ID.c_str());
OutputDebugString(L"\n");

//std::cout << "severity: ";
OutputDebugString(L"severity: ");
switch (severity)
{
case GL_DEBUG_SEVERITY_LOW:
//std::cout << "LOW";
OutputDebugString(L"LOW");
break;
case GL_DEBUG_SEVERITY_MEDIUM:
//std::cout << "MEDIUM";
OutputDebugString(L"MEDIUM");
break;
case GL_DEBUG_SEVERITY_HIGH:
//std::cout << "HIGH";
OutputDebugString(L"HIGH");
break;
}
//std::cout << std::endl;
OutputDebugString(L"\n");
//std::cout << "---------------------opengl-callback-end--------------" << std::endl;
OutputDebugString(L"---------------------opengl-callback-end--------------\n");

}


I changed all the std::cout's and what-not to OutputDebugString(). If you are not using Visual Studio in Windows, you may want to change those back. I may want to change that back when I recompile this on Linux here eventually, which is why I just commented it out. You probably won't see too many examples of OGL callback functions in C++ instead of C, but they seem to work pretty nicely this way.

And just for thoroughness, here's the corrected and working code:
Texture2DClass.h (I think this one is unchanged.)


#pragma once
#include <string>
#include <glew.h>

#include <FreeImage.h>


namespace OGLGameNameSpace
{

class Texture2DClass
{
public:
Texture2DClass();
~Texture2DClass();
bool Load(std::string File, bool GenerateMipMaps = true);
void Bind(GLuint TexUnit = 0);
GLuint GetHandle();
GLuint GetTextureID();

private:
GLuint TextureHandle;
GLuint TextureID;
};

}


Texture2DClass.cpp (With the bug fixed and using FreeImage).


#include "Texture2DClass.h"

//#ifndef STBI_INCLUDE_STB_IMAGE_H
//#define STBI_INCLUDE_STB_IMAGE_H
//#define STB_IMAGE_IMPLEMENTATION //Required by STB.
//#define STBI_FAILURE_USERMSG
//#include <stb_image.h>
//#endif STBI_INCLUDE_STB_IMAGE_H

using namespace OGLGameNameSpace;


Texture2DClass::Texture2DClass() : TextureHandle(0)
{
}


Texture2DClass::~Texture2DClass()
{
}


bool Texture2DClass::Load(std::string File, bool GenerateMipMaps)
{
int Width;
int Height;
int Components;
bool TextureLoaded = false;

FREE_IMAGE_FORMAT ImageFormat = FreeImage_GetFileType(File.c_str(), 0);
FIBITMAP* ImageBitMap = FreeImage_Load(ImageFormat, File.c_str());

FIBITMAP* TempPointer = ImageBitMap;
ImageBitMap = FreeImage_ConvertTo32Bits(ImageBitMap);
FreeImage_Unload(TempPointer);

Width = FreeImage_GetWidth(ImageBitMap);
Height = FreeImage_GetHeight(ImageBitMap);

GLubyte* Texture = new GLubyte[4 * Width*Height];
char* Pixels = (char*)FreeImage_GetBits(ImageBitMap);

for (int x = 0; x < (Width*Height); x++)
{
Texture[x * 4 + 0] = Pixels[x * 4 + 2];
Texture[x * 4 + 1] = Pixels[x * 4 + 1];
Texture[x * 4 + 2] = Pixels[x * 4 + 0];
Texture[x * 4 + 3] = Pixels[x * 4 + 3];
}

//Height = 256;
//Width = 256;
//GLubyte* Texture = new GLubyte[4 * Width*Height];
//int PicSize = Width * Height * 4;
//for (int x = 0; x < Height; x++)
//{
// for (int y = 0; y < Width * 4; y += 4)
// {
// Texture[x * y + 0] = 200;
// Texture[x * y + 1] = 200;
// Texture[x * y + 2] = 200;
// Texture[x * y + 3] = 256;
// }
//}

TextureID = 0;
glGenTextures(1, &TextureID);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)Texture);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, Width, Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, Pixels);
//glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(GL_TEXTURE_2D, TextureID);
if (GenerateMipMaps) glGenerateMipmap(GL_TEXTURE_2D);
TextureLoaded = true;

return TextureLoaded;
}


void Texture2DClass::Bind(GLuint TexUnit)
{
glActiveTexture(GL_TEXTURE0 + TexUnit);
glBindTexture(GL_TEXTURE_2D, TextureHandle);
}


GLuint Texture2DClass::GetHandle()
{
return TextureHandle;
}


GLuint Texture2DClass::GetTextureID()
{
return TextureID;
}

GClements
06-05-2016, 11:30 AM
I've noticed the OGL doesn't really return any indication that it failed. I was noticing I saw no error checking in pretty much any OGL example I've seen ever. And sleeping on it, I'm thinking "That can't be right." Sure enough, it returns void on just about everything.
That's because most OpenGL functions simply append a command to the queue, they don't wait until it has been executed.

If you want to check for errors, you use glGetError(). Like most glGet* functions, that has to wait until all prior commands have been executed, making it potentially very slow. In production code, you might call it at the end of the rendering function (just before or after swapping buffers) to confirm that there are no errors. For debugging, if errors are occurring, you'd add calls as needed to narrow down the source of the error.

BBeck1
06-05-2016, 11:37 AM
That's because most OpenGL functions simply append a command to the queue, they don't wait until it has been executed.

Ah! Good to know! Thanks!

And thanks for the suggestion of building the texture myself to rule out FreeImage not building the texture correctly, too.

BBeck1
06-05-2016, 11:42 AM
LOL

What's worse is that I just realized that the offending line of code is a repeat of another line of code:


glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);


I already defined MIN_FILTER correctly before that. Not only was glTextureParametri wrong, but that entire line of code was redundant with a line of code that was written correctly. Not sure how that redundant and incorrect line of code popped in there, but I'm working off a couple different tutorials and then when I run into a problem whatever random thing I pull off Google.

But even in my "corrected" version I didn't notice I defined MIN_FILTER twice. Doh!

I probably would have noticed it if it were not for the fact that the bad line of code worked rather than blowing up. It must be defined in GLFW as a redefinition of glTexParameteri or something.