Why does a 800x600 rectangle in OpenGL take so much longer time than many small rectangles? In my head I would have thought drawing 1 would be better than many.
public class LWJGLtest {
int screenwidth = 1024;
int screenheight = 768;
private Texture texture;
int[][] Star1 = new int[100][2];
public void start() {
try {
Display.setDisplayMode(new DisplayMode(screenwidth, screenheight));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
for (int r = 0; r < Star1.length; r++) {
Star1[r][0] = (int) (screenwidth * Math.random());
Star1[r][1] = (int) (screenheight * Math.random());
for (int c = 0; c < Star1[r].length; c++) {
System.out.print(" " + Star1[r][c]);
}
System.out.println("");
}
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, screenwidth, screenheight, 0, 1, -1);
try {
texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("fighter.png"));
} catch (IOException ex) {
Logger.getLogger(LWJGLtest.class.getName()).log(Level.SEVERE, null, ex);
}
//////////////////
boolean bsmall = false;
////////////////////
while (!Display.isCloseRequested()) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1f, 0f, 0f);
if (bsmall) {
for (int i = 0; i < 100; i++) {
int x = (int) (screenwidth * Math.random());
int y = (int) (screenheight * Math.random());
DrawImage(texture, x, y, 30, 30);
//DrawRect(x, y, screenwidth, screenheight);
}
} else {
for (int i = 0; i < 1; i++) {
int x = 0;//(int) (screenwidth * Math.random());
int y = 0;//(int) (screenheight * Math.random());
DrawImage(texture, x, y, screenwidth, screenheight);
//DrawRect(x, y, screenwidth, screenheight);
}
}
Display.update();
}
Display.destroy();
}
void DrawImage(Texture tex, int x, int y, int w, int h) {
if (tex == null) {
return;
}
tex.bind();
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(x, y);
glTexCoord2f(1, 0);
glVertex2f(x + w, y);
glTexCoord2f(1, 1);
glVertex2f(x + w, y + h);
glTexCoord2f(0, 1);
glVertex2f(x, y + h);
glEnd();
}
public static void main(String[] argv) {
LWJGLtest displayExample = new LWJGLtest();
displayExample.start();
}
}
When bsmall == true I get 1000 better fps than when false?
This is a very deep question and very hardware dependent. However, notice that in your case the texture coordinates are fixed. That means your smaller rectangles draw smaller versions of the texture. Likely your textures use mipmapping. Mipmapping has smaller versions of a texture for when you display the texture as smaller like you are here.
Therefore, the smaller your rectangles, the less data you'll actually end up accessing. This is called texture fetching, and often its overhead is far greater than vertex processing. So yes, you are processing more vertices, you're drawing about the same number of pixels, and you're doing the same amount of texture fetching -- but your texture fetching is most likely entirely in texture cache, so it's very much faster to access.
You need to compare apples and apples -- make the output look exactly the same and then see which technique is faster.
Another example -- On PS3 graphics hardware there's a certain pattern of tiling full screen drawing that causes the shader quad distributor to do a better job distributing work to the fragment shading units. Likewise it could be with your graphics card. It's hard to know and hard to understand, especially when manufacturers don't like giving away all of their secrets.
Related
I am having a small problem in essentially creating a path tracer.
In my project, I have an object which constantly moves around quite organically through an update function done in the while loop. I use immediate mode and represent the player as a square, I would like to make it so that every update the object is drawn in its current position, but also for it to draw it's previous position(s), so etching dots towards the path the object is going. I'm pretty sure we can do this by drawing the position as normal but not clearing everything up after this instance in the while loop, but I have no knowledge on how to do this.
Edit: For those who want the code, do understand that this code, in particular, is not adherent to the question and that I made a ton of generalizations (such as the particle(s) being referred to an object) so that the general gist of the question is understandable:
#include "PerlinNoise.hpp"
#include "Particle.hpp"
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cmath>
#include <vector>
using namespace siv;
float map(float oValue, float oMin, float oMax, float nMin, float nMax)
{
float oRange = (oMax - oMin);
float nRange = (nMax - nMin);
return(((oValue - oMin) * nRange)/oRange) + nMin;
}
void drawRectangle(float x, float y, float xr, float yr, float R, float G, float B)
{
glBegin(GL_QUADS);
glColor3f(R,G,B);
glVertex2f(x,y);
glVertex2f(x+xr,y);
glVertex2f(x+xr,y+yr);
glVertex2f(x,y+yr);
glEnd();
}
void drawLine(float x, float y, float xr, float yr, float rotation)
{
float radius = sqrt(xr*xr + yr*yr);
float a0 = asin(yr/radius);
float tangle = a0+rotation;
//std::cout<<tangle*180/M_PI<<std::endl;
glBegin(GL_LINES);
glColor3f(.1,.1,.1);
glVertex2f(x,y);
glVertex2f(x + sin(tangle)*radius,y + cos(tangle)*radius);
glEnd();
}
int main()
{
float inc = 0.1;
int scl = 20;
int cols,rows;
Particle particles[100000];
//V2D flowfield[cols*rows];
GLFWwindow* window;
if (!glfwInit())
return 1;
int width = 800;
int height = 800;
window = glfwCreateWindow(width, height, "Window", NULL, NULL);
cols = floor(width/scl);
rows = floor(height/scl);
V2D flowfield[cols*rows];
float zoff = 0;
if (!window) {
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(window);
if(glewInit()!=GLEW_OK)
std::cout<<"Error"<<std::endl;
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glfwGetFramebufferSize(window, &width, &height);
glOrtho(0, width*(width/height), height, 0, -2, 2);
PerlinNoise png = PerlinNoise(1);
while(!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glClearColor(0.11, 0.14, 0.17, 1);
float yoff = 0;
for(int y = 0; y < rows; y++)
{
float xoff = 0;
for(int x = 0; x < cols; x++)
{
double noise = map(png.noise((double)xoff, (double)yoff, (double)zoff),-1,1,0,1);
double angle = noise * 8 *M_PI;
//std::cout<<angle/(2*M_PI)<<std::endl;
int index = x + y * cols;
V2D v = V2D(cos(angle), sin(angle));
v.normalize();
v = V2D(v.x*5,v.y*5);
flowfield[index] = v;
//drawLine(x*scl, y*scl, scl, 0, atan2(v.x, v.y));
//drawRectangle(x*scl,y*scl,scl,scl,noise,noise,noise);
xoff += inc;
}
yoff += inc;
zoff += 0.0001;
}
for(int i = 0; i < 100000; i++)
{
particles[i].follow(flowfield);
particles[i].update();
particles[i].show();
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
}
When drawing directly to a window (be it double buffered or not doesn't make a difference) you must not make any assumptions about its contents being persistent between drawing. Heck, strictly speaking the contents may become damaged mid draw, before things even finished up; of course in practice this isn't very likely to happen and given modern compositing graphics systems it's practically eliminated.
Your application screams for drawing to an intermediary framebuffer object. FBOs are guaranteed to retain their contents no matter what happens; also you can add further drawing to the backing buffer of an FBO at any time.
The official OpenGL wiki describes FBOs at https://www.khronos.org/opengl/wiki/Framebuffer_Object
Also ages ago I wrote a simple codesample (using a lot of outdated, legacy OpenGL); drawing is legacy, but the FBO parts are done today as it was 10 years ago: https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/minimalfbo/minimalfbo.c (I implemented it using render to texture; a render to renderbuffer and buffer blit to main framebuffer would work for you, too).
Im trying to get the coordinates of the Box2D objects to fit with the coordinates of the objects on screen(these being displayed with textures). I'm already aware that box2D works in meters so i would have to find a conversion factor that works for my program(in this case i said 50 pixels is 2 meters).
So would first set the position and size of the box with this:
b2BodyDef def
def.postition.Set(0.02 * x, y* 0.02)
def.SetAsBox(0.02 * w/2, 0.02* h/2)
And since im using openGL i would just make a texture and render it like you normally would but this time I'm tring to match the textures x and y to the x and y of its collision box in Box2D.
And when i run the program whenever i try to move my player texture off of the floor texture, it falls off before it reaches the end. This let me know for sure that my box2D bodies and my OpenGL textures were not sharing the same positions. but whatever i do to try and fix that nothing ever changes(ex: i tried changing the conversion factor to 1m = 20p instead of 50p, i tried not converting the coordinates at all then i got some really wired result that also didnt work, and a bunch of other stuff). The worst part is box2D did the SAME THING when i was using SDL 2, and i switched to OpenGL thinking that this wouldn't happen again.
images from the demo:
https://drive.google.com/file/d/0B4anrJs1BRh1aVpOYXI3T3JlWWM/view?usp=drivesdk
https://drive.google.com/file/d/0B4anrJs1BRh1dVlTb0czMTE3RWc/view?usp=drivesdk
Code for the tile class(currently the only class in my projects that uses box2D)
void tile::load(bool square, std::string path)
{
if(square)
{
glGenTextures(1, &texID);
SDL_Surface *sur = IMG_Load(path.c_str());
w = sur->w;
h = sur->h;
glBindTexture(GL_TEXTURE_2D, texID);
glTexImage2D(GL_TEXTURE_2D, 0, 4, sur->w, sur->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, sur->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture( GL_TEXTURE_2D, NULL );
}
}
void tile::addRect(int x, int y, int wt, int ht, bool dyn)
{
b2BodyDef def;
//Convert the x and y into meters.
def.position.Set(vars::P2M * x, vars::P2M * y);
if(dyn)
{
def.type = b2_dynamicBody;
}
b = vars::world->CreateBody(&def);
b2PolygonShape shape;
//Convert the width and height entered into meters
shape.SetAsBox(vars::P2M * w/2,vars::P2M * h/2);
float th = w * 0.02;
float tw = h * 0.02;
b2FixtureDef fix;
fix.shape = &shape;
fix.density = 1.0;
b->CreateFixture(&fix);
h = h;
w = w;
}
void tile::Render()
{
glLoadIdentity();
b2Vec2 pos;
pos = b->GetPosition();
float x = pos.x;
float y = pos.y;
//Convert the x and y of the body to pixels.
glTranslatef(x * vars::M2P, y * vars::M2P, 0);
glBindTexture( GL_TEXTURE_2D, texID);
glBegin( GL_QUADS );
glTexCoord2f(0.f, 0.f); glVertex2f(0.f, 0.f);
glTexCoord2f(1.f, 0.f); glVertex2f(w, 0);
glTexCoord2f(1.f, 1.f); glVertex2f(w, h);
glTexCoord2f(0.f, 1.f); glVertex2f(0, h);
glEnd();
}
void tile::Update()
{
b2Vec2 vel;
vel = b->GetLinearVelocity();
if(key[SDL_SCANCODE_D])
{
d_key = true;
}
else
{
d_key = false;
}
if(key[SDL_SCANCODE_A])
{
a_key = true;
}
else
{
a_key = false;
}
if(a_key)
{
//Move the box left
vel.x = -5;
}
if(d_key)
{
//Move the box right
vel.x = 5;
}
b->SetLinearVelocity(vel);
}
If anyone has an solution for this, please post it because im out of options and i dont know what to do anymore. Thanks.
oh and btw,
vars::M2P = 50 and vars::P2M = 1/vars::M2P(which in-turn is 0.02).
how can I set the color for the lines I draw using openGL3 ?
the rendering function I'm using is the following
void renderVertex(std::vector<doubleVertex> &Poly, int32_t iniBound, int32_t endBound, int32_t Type){
for (int32_t i = iniBound; i < endBound; i++) {
GLfloat *ptr_polygonVertices;
ptr_polygonVertices = createPolygonVertices(Poly[i]);
// render OpenGL here
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, ptr_polygonVertices);
glDrawArrays(Type, 0, Poly[i].x.size() - 1);
glDisableClientState(GL_VERTEX_ARRAY);
delete[] ptr_polygonVertices;
}
}
Inside this loop
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
renderVertex(Poly, 0, nBound, GL_LINE_LOOP);
// Swap front and back buffers
glfwSwapBuffers(window);
// Poll for and process events
glfwPollEvents();
}
If necessary, the complete code is the listed here:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <math.h>
#include <stdlib.h>
#include "Globals.h"
GLfloat* createPolygonVertices(doubleVertex &Poly);
doubleVertex createCircle(double x, double y, double radius, int numberOfSides);
void windowInit(GLFWwindow *window);
void renderVertex(std::vector<doubleVertex> &Poly, int32_t iniBound, int32_t endBound, int32_t Type);
int main(void)
{
GLFWwindow *window;
// Initialize the library
if (!glfwInit())
exit(0);
// Create a windowed mode window and its OpenGL context
window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Polygon Linear Infill", NULL, NULL);
windowInit(window);
////////////////////////////////////// Polygon Example //////////////////////////////////////////
std::vector<doubleVertex> Poly{
{
{ 0, 23.37, 50.24, 31.26, 34.57, 1.46, 4.69, 0 },
{ 0, 11.91, 0, -21.39, -32.22, -26.31, -13.17, 0 }
},
{
{ 42.19, 35.69, 29.76, 34.46, 42.19 },
{ -4.26, 2.34, -5.2, -11.87, -4.26 }
},
{
{ 23.57, 26.29, 11.94, 23.57 },
{ -26.73, -17.39, -18.38, -26.73 }
}
};
double x = 14.97, y = -5.28, r = 7.33;
Poly.push_back(createCircle(x, y, r, 37));
uint32_t i, nBound = Poly.size();
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
renderVertex(Poly, 0, nBound, GL_LINE_LOOP);
// Swap front and back buffers
glfwSwapBuffers(window);
// Poll for and process events
glfwPollEvents();
}
glfwTerminate();
return 0;
}
GLfloat* createPolygonVertices(doubleVertex &Poly){
int32_t j, n_Vertex, cnt_polygonVertices=0;
n_Vertex = Poly.x.size();
GLfloat *polygonVertices;
polygonVertices = new GLfloat[(n_Vertex-1) * 2];
for (j = 0; j < n_Vertex-1; j++) {
polygonVertices[cnt_polygonVertices] = Poly.x[j];
cnt_polygonVertices++;
polygonVertices[cnt_polygonVertices] = Poly.y[j];
cnt_polygonVertices++;
}
return polygonVertices;
}
doubleVertex createCircle(double x, double y, double radius, int numberOfSides){
int numberOfVertices = numberOfSides + 2;
double twicePi = 2.0f * M_PI;
doubleVertex Poly;
for (int i = 0; i < numberOfVertices; i++)
{
Poly.x.push_back(x + (radius * cos(i * twicePi / numberOfSides)));
Poly.y.push_back(y + (radius * sin(i * twicePi / numberOfSides)));
}
return Poly;
}
void windowInit(GLFWwindow *window){
if (!window) {
glfwTerminate();
exit(0);
}
// Make the window's context current
glfwMakeContextCurrent(window);
glViewport(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT); // specifies the part of the window to which OpenGL will draw (in pixels), convert from normalised to pixels
glMatrixMode(GL_PROJECTION); // projection matrix defines the properties of the camera that views the objects in the world coordinate frame. Here you typically set the zoom factor, aspect ratio and the near and far clipping planes
glLoadIdentity(); // replace the current matrix with the identity matrix and starts us a fresh because matrix transforms such as glOrpho and glRotate cumulate, basically puts us at (0, 0, 0)
glOrtho(0, 51, -33, 12, 0, 1); // essentially set coordinate system
glMatrixMode(GL_MODELVIEW); // (default matrix mode) modelview matrix defines how your objects are transformed (meaning translation, rotation and scaling) in your world
glLoadIdentity(); // same as above comment
}
void renderVertex(std::vector<doubleVertex> &Poly, int32_t iniBound, int32_t endBound, int32_t Type){
for (int32_t i = iniBound; i < endBound; i++) {
GLfloat *ptr_polygonVertices;
ptr_polygonVertices = createPolygonVertices(Poly[i]);
// render OpenGL here
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, ptr_polygonVertices);
glDrawArrays(Type, 0, Poly[i].x.size() - 1);
glDisableClientState(GL_VERTEX_ARRAY);
delete[] ptr_polygonVertices;
}
}
Thanks in advance!
I'm currently rendering triangle strips within a display list and it shows weird little holes.
Beside that some triangles seem to disappear completely when rotating the environment.
Here is the Code:
public class Landscape {
int displayList;
public Landscape() throws IOException {
try {
Display.setDisplayMode(new DisplayMode(640, 480));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLU.gluPerspective(60f, 640f/480f, 0.0001f, 1000f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glPolygonMode(GL_FRONT_AND_BACK, GL_FLAT);
glEnable(GL_CULL_FACE);
BufferedImage heightMap = ImageIO.read(new File("heightmap.png"));
BufferedImage heightMapColor = ImageIO.read(new File("hoehenprofil.png"));
displayList = glGenLists(1);
glNewList(displayList, GL_COMPILE);
for (int z = 0; z < heightMap.getHeight(); z++) {
glBegin(GL_TRIANGLE_STRIP);
for (int x = 0; x < heightMap.getWidth(); x++) {
int y = 0xFF - heightMap.getRGB(x, z) & 0xFF;
int color = heightMapColor.getRGB(y-1, 0);
glColor3ub((byte)((color >> 16) & 0xFF), (byte)((color >> 8) & 0xFF), (byte)(color & 0xFF));
glVertex3i(x, y*5, z);
if (z < heightMap.getHeight() - 1) {
y = 0xFF - heightMap.getRGB(x, z+1) & 0xFF;
color = heightMapColor.getRGB(y-1, 0);
glColor3ub((byte)((color >> 16) & 0xFF), (byte)((color >> 8) & 0xFF), (byte)(color & 0xFF));
glVertex3i(x, y*5, z+1);
}
}
glEnd();
}
glEndList();
float camX = 500, camY = 500, camZ = -500;
float rotX = 45, rotY = 0, rotZ = 0;
long lastTick = System.currentTimeMillis();
int fps = 0;
while (!Display.isCloseRequested()) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
if (Keyboard.isKeyDown(Keyboard.KEY_W)) camZ++;
if (Keyboard.isKeyDown(Keyboard.KEY_S)) camZ--;
if (Keyboard.isKeyDown(Keyboard.KEY_A)) camX--;
if (Keyboard.isKeyDown(Keyboard.KEY_D)) camX++;
if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) camY++;
if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) camY--;
if (Keyboard.isKeyDown(Keyboard.KEY_Q)) rotY -= 0.5f;
if (Keyboard.isKeyDown(Keyboard.KEY_E)) rotY += 0.5f;
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glRotatef(rotZ, 0, 0, 1);
glTranslatef(-camX, -camY, camZ);
glCallList(displayList);
Display.update();
fps++;
if (lastTick + 1000 <= System.currentTimeMillis()) {
lastTick += 1000;
System.out.println("FPS: " + fps);
fps = 0;
}
}
}
public static void main(String[] args) throws IOException {
new Landscape();
}
}
It seem to also appear with depth test disabled. And even with cull face enabled this doesn't appear to work.
EDIT:
Other than that I get some black flickering just when translating a certain amount around the x or y axis. Here is a screenshot of that.
It causes a huge amount of lag and I can't find any reason for that.
EDIT2:
I scaled the x and z axis and removed the y axis scale for a screenshot, just as requested. It only seem to cause the bug when stretching far around the y axis.
Your difference between near and far plane (last two arguments to gluPerspective) is extremely large. The larger the relative size (far divided by near) between these two, the less depth buffer precision you get. In your case, the far/near is 1,000,000, which is much more than you normally want. I normally like to keep them at a ratio of around 10, even though something like 100 would probably still be fine.
Tweak the values while making sure that none of the geometry you want to see gets clipped away. Try something like 1.0 and 10.0. If that clips away geometry at the far end, increase the far value to something like 100.0, and maybe see if you can increase the near value as well.
I am trying to apply a texture to a quad, but I only get a black box instead of the texture. I am using DevIL to load images from files and OpenGL does the rest.
Here is what I am doing so far:
The following class abstracts the DevIL representation for an image.
#include "Image.h"
Image::Image()
{
ilGenImages(1, &this->imageId);
}
Image::~Image()
{
ilDeleteImages(1, &this->imageId);
}
ILint Image::getWidth()
{
return this->width;
}
ILint Image::getHeight()
{
return this->height;
}
ILint Image::getDepth()
{
return this->depth;
}
ILint Image::getBpp()
{
return this->bpp;
}
ILint Image::getFormat()
{
return this->format;
}
ILubyte* Image::getData()
{
return ilGetData();
}
bool Image::loadFromFile(wchar_t *filename)
{
// Load the image from file.
ILboolean retval = ilLoadImage(filename);
if (!retval) {
ILenum error;
while ((error = ilGetError()) != IL_NO_ERROR) {
wcout << error << L" " << iluErrorString(error);
}
return false;
}
this->width = ilGetInteger(IL_IMAGE_WIDTH);
this->height = ilGetInteger(IL_IMAGE_HEIGHT);
this->depth = ilGetInteger(IL_IMAGE_DEPTH);
this->bpp = ilGetInteger(IL_IMAGE_BPP);
this->format = ilGetInteger(IL_IMAGE_FORMAT);
return true;
}
bool Image::convert()
{
ILboolean retval = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
if (!retval) {
ILenum error;
while ((error = ilGetError()) != IL_NO_ERROR) {
wcout << error << L" " << iluErrorString(error);
}
return false;
}
return true;
}
bool Image::scale(ILint width, ILint height, ILint depth)
{
ILboolean retval = iluScale(width, height, depth);
if (!retval) {
ILenum error;
while ((error = ilGetError()) != IL_NO_ERROR) {
wcout << error << L" " << iluErrorString(error);
}
return false;
}
return true;
}
void Image::bind()
{
ilBindImage(this->imageId);
}
This class abstracts the texture representation for OpenGL.
#include "Texture.h"
Texture::Texture(int width, int height)
{
glGenTextures(1, &this->textureId);
this->width = width;
this->height = height;
}
int Texture::getWidth()
{
return this->width;
}
int Texture::getHeight()
{
return this->height;
}
void Texture::initFilter()
{
// We will use linear interpolation for magnification filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// We will use linear interpolation for minifying filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void Texture::unpack()
{
glPixelStoref(GL_UNPACK_ALIGNMENT, 1);
}
void Texture::bind()
{
glBindTexture(GL_TEXTURE_2D, this->textureId);
}
Texture::~Texture()
{
glDeleteTextures(1, &this->textureId);
}
The following class contains the texture loading process.
#include "TextureLoader.h"
void TextureLoader::initialize()
{
if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION) {
debug("Wrong DevIL version detected.");
return;
}
ilInit();
ilutRenderer(ILUT_OPENGL);
}
Texture* TextureLoader::createTexture(wchar_t *filename, Color *color)
{
// Generate some space for an image and bind it.
Image *image = new Image();
image->bind();
bool retval = image->loadFromFile(filename);
if (!retval) {
debug("Could not load image from file.");
return 0;
}
retval = image->convert();
if (!retval) {
debug("Could not convert image from RGBA to unsigned byte");
}
int pWidth = getNextPowerOfTwo(image->getWidth());
int pHeight = getNextPowerOfTwo(image->getHeight());
int size = pWidth * pHeight;
retval = image->scale(pWidth, pHeight, image->getDepth());
if (!retval) {
debug("Could not scale image from (w: %i, h: %i) to (w: %i, h: %i) with depth %i.", image->getWidth(), image->getHeight(), pWidth, pHeight, image->getDepth());
return 0;
}
// Generate some space for a texture and bind it.
Texture *texture = new Texture(image->getWidth(), image->getHeight());
texture->bind();
// Set the interpolation filters.
texture->initFilter();
// Unpack pixels.
texture->unpack();
ILubyte *imageData = image->getData();
TextureLoader::setColorKey(imageData, size, new Color(0, 0, 0));
TextureLoader::colorize(imageData, size, new Color(255, 0, 0));
debug("bpp: %i", image->getBpp());
debug("width: %i", image->getWidth());
debug("height: %i", image->getHeight());
debug("format: %i", image->getFormat());
// Map image data to texture data.
glTexImage2D(GL_TEXTURE_2D, 0, image->getBpp(), image->getWidth(), image->getHeight(), 0, image->getFormat(), GL_UNSIGNED_BYTE, imageData);
delete image;
return texture;
}
void TextureLoader::setColorKey(ILubyte *imageData, int size, Color *color)
{
for (int i = 0; i < size * 4; i += 4)
{
if (imageData[i] == color->r && imageData[i + 1] == color->g && imageData[i + 2] == color->b)
{
imageData[i + 3] = 0;
}
}
}
void TextureLoader::colorize(ILubyte *imageData, int size, Color *color)
{
for (int i = 0; i < size * 4; i += 4)
{
int rr = (int(imageData[i]) * int(color->r)) >> 8;
int rg = (int(imageData[i + 1]) * int(color->g)) >> 8;
int rb = (int(imageData[i + 2]) * int(color->b)) >> 8;
int fak = int(imageData[i]) * 5 - 4 * 256 - 138;
if (fak > 0)
{
rr += fak;
rg += fak;
rb += fak;
}
rr = rr < 255 ? rr : 255;
rg = rg < 255 ? rg : 255;
rb = rb < 255 ? rb : 255;
imageData[i] = rr > 0 ? (GLubyte) rr : 1;
imageData[i + 1] = rg > 0 ? (GLubyte) rg : 1;
imageData[i + 2] = rb > 0 ? (GLubyte) rb : 1;
}
}
The last class does the drawing.
#include "Texturizer.h"
void Texturizer::draw(Texture *texture, float x, float y, float angle)
{
// Enable texturing.
glEnable(GL_TEXTURE_2D);
// Bind the texture for drawing.
texture->bind();
// Enable alpha blending.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
int width = texture->getWidth();
int height = texture->getHeight();
// Create centered dimension vectors.
b2Vec2 vertices[4];
vertices[0] = 0.5f * b2Vec2(- width, - height);
vertices[1] = 0.5f * b2Vec2(+ width, - height);
vertices[2] = 0.5f * b2Vec2(+ width, + height);
vertices[3] = 0.5f * b2Vec2(- width, + height);
b2Mat22 matrix = b2Mat22();
matrix.Set(angle);
glBegin(GL_QUADS);
for (int i = 0; i < 4; i++) {
float texCoordX = i == 0 || i == 3 ? 0.0f : 1.0f;
float texCoordY = i < 2 ? 0.0f : 1.0f;
glTexCoord2f(texCoordX, texCoordY);
// Rotate and move vectors.
b2Vec2 vector = b2Mul(matrix, vertices[i]) + meter2pixel(b2Vec2(x, y));
glVertex2f(vector.x, vector.y);
}
glEnd();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
Last but not least, the following method initializes OpenGL (and triggers the initialization of DevIL):
void GraphicsEngine::initialize(int argc, char **argv)
{
// Initialize the window.
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(WIDTH, HEIGHT);
// Set shading model.
glShadeModel(GL_SMOOTH);
// Create the window.
this->mainWindow = glutCreateWindow(TITLE);
// Set keyboard methods.
glutKeyboardFunc(&onKeyDownCallback);
glutKeyboardUpFunc(&onKeyUpCallback);
glutSpecialFunc(&onSpecialKeyDownCallback);
glutSpecialUpFunc(&onSpecialKeyUpCallback);
// Set mouse callbacks.
glutMouseFunc(&onMouseButtonCallback);
#ifdef FREEGLUT
glutMouseWheelFunc(&onMouseWheelCallback);
#endif
glutMotionFunc(&onMouseMotionCallback);
glutPassiveMotionFunc(&onMousePassiveMotionCallback);
// Set display callbacks.
glutDisplayFunc(&onDrawCallback);
glutReshapeFunc(&onReshapeCallback);
// Set a timer to control the frame rate.
glutTimerFunc(FRAME_PERIOD, onTimerTickCallback, 0);
// Set clear color.
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
Camera::getInstance()->subscribe(this);
// Initialize texture loader.
TextureLoader::initialize();
}
The image I am using already worked for another OpenGL/DevIL project, so it cannot be the source of the problem.
The texture is created inside of every class which represents a world object (it's a game...). The character is called Blobby and here are the most important parts of its implementation:
#include "Blobby.h"
Blobby::Blobby()
{
this->isJumping = false;
this->isRotating = false;
this->isWalking = false;
this->isDucking = false;
this->isStandingUp = false;
this->isOnGround = false;
this->isTouchingWall = false;
this->angle = 0;
this->direction = DIRECTION_UNKNOWN;
this->wallDirection = DIRECTION_UNKNOWN;
// Create a red blobby texture.
this->texture = TextureLoader::createTexture(L"D:/01.bmp", new Color(255, 0, 0));
ContactListener::getInstance()->subscribe(this);
}
void Blobby::draw()
{
GraphicsEngine::drawString(35, 40, "isOnGround = %s", this->isOnGround ? "true" : "false");
GraphicsEngine::drawString(35, 55, "inJumping = %s", this->isJumping ? "true" : "false");
GraphicsEngine::drawString(35, 70, "isRotating = %s", this->isRotating ? "true" : "false");
GraphicsEngine::drawString(35, 85, "isTouchingWall = %s (%i)", this->isTouchingWall ? "true" : "false", this->wallDirection);
Texturizer::draw(this->texture, this->getBody(0)->GetPosition().x, this->getBody(0)->GetPosition().y, this->getBody(0)->GetAngle());
AbstractEntity::draw(); // draws debug information... not important
}
The OpenGL timer callback calls a step method which ends here:
void Simulator::step()
{
// Update physics.
this->gameWorld->step();
b2Vec2 p = Camera::convertWorldToScreen(meter2pixel(this->cameraBlobby->getBody(0)->GetPosition().x), 300.0f);
if (p.x < 300) {
Camera::getInstance()->setViewCenter(Camera::convertScreenToWorld(400 - (300 - int(p.x)), 300));
} else if (p.x > 500) {
Camera::getInstance()->setViewCenter(Camera::convertScreenToWorld(400 + (int(p.x) - 500), 300));
}
for (unsigned int i = 0; i < this->gameWorld->getEntityCount(); i++) {
IEntity *entity = this->gameWorld->getEntity(i);
entity->draw();
}
}
IEntity is a pure virtual class (i.e. interface), AbstractEntity implements this interface and adds global methods. Blobby inherits from AbstractEntity and adds routines which are special for this world object.
EDIT:
I have uploaded a more recent version of the code (the whole project incl. dependencies) here:
http://upload.visusnet.de/uploads/BlobbyWarriors-rev19.zip (~9.5 MB)
I'm not familiar with DevIL, but... are you providing the right diffuse color for your vertices? If lighting is enabled, are there some lights pointing on the quad? Does the camera look at the front side of the quad?
EDIT:
You got a bug in the code, but not the one you posted here, but in the version in the archive you linked.
You call glColor3i(255, 255, 255), and it sets the diffuse color to (very nearly) black as expected. glColor3i does not accept the color values in the target (calculation or framebuffer) range. The possible values are scaled to the entire range of the int type. This means the maximum value (1.0 in float) is represented by MAX_INT (2,147,483,647)
, 0 is 0, and -1.0 is MIN_INT (-2,147,483,648). The 255 value you provided represents about 0.000000118, which is very nearly zero.
I believe you intended one of the following (completely equivalent) forms:
glColor3f(1.0, 1.0, 1.0), glColor3ub(255, 255, 255),
glColor3i(2147483647, 2147483647, 2147483647).
What is in the b2Mat22 matrix? Could it be that multiplying by this matrix is causing your vertices to be drawn in a clockwise order, because I think in that case your square's back would be facing you, and the texture might be on the other (invisible) side.
I had an issue like this a long time ago, I think back then it was a problem with the texture dimensions not being an exponent of 2 (128x128, 512x512, etc.). I'm sure they've fixed that by now, but it might be something to try.