Texture rendered white

Started by Nanocryk, October 24, 2014, 13:34:47

Previous topic - Next topic

Nanocryk

Hi there !

I'm currently making my own little 2d engine based on LWJGL. But the problems arrive quick and I have problems with the textures.

I load my file and create an OpenGL texture from it (the file exist) but when I render it it's a white box !

There is my code :
Texture.java
package net.parallelworlds.core;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL12;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

import static org.lwjgl.opengl.GL11.*;

/**
 * Created by nanoc_000 on 22/10/2014.
 */
public class Texture {
    private static final int BYTES_PER_PIXEL = 4;

    private final int textureId;

    // Create a texture from a BufferedImage
    public Texture(BufferedImage image) {
        int[] pixels = new int[image.getWidth() * image.getHeight()];
        image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());

        ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * BYTES_PER_PIXEL); //4 for RGBA, 3 for RGB

        for(int y = 0; y < image.getHeight(); y++){
            for(int x = 0; x < image.getWidth(); x++){
                int pixel = pixels[y * image.getWidth() + x];
                buffer.put((byte) ((pixel >> 16) & 0xFF));     // Red component
                buffer.put((byte) ((pixel >> 8) & 0xFF));      // Green component
                buffer.put((byte) (pixel & 0xFF));               // Blue component
                buffer.put((byte) ((pixel >> 24) & 0xFF));    // Alpha component. Only for RGBA
            }
        }

        buffer.flip(); //FOR THE LOVE OF GOD DO NOT FORGET THIS

        // You now have a ByteBuffer filled with the color data of each pixel.
        // Now just create a texture ID and bind it. Then you can load it using
        // whatever OpenGL method you want, for example:

        textureId = glGenTextures(); //Generate texture ID
        glBindTexture(GL_TEXTURE_2D, textureId); //Bind texture ID

        //Setup wrap mode
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

        //Setup texture scaling filtering
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        //Send texel data to OpenGL
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

        glBindTexture(GL_TEXTURE_2D, 0);
    }

    // Load a BufferedImage from a file outside the jar
    public static BufferedImage loadImageOutJar(String loc) throws IOException {
        return ImageIO.read(new File(loc));
    }

    // Load a BufferedImage from a file inside the jar
    public static BufferedImage loadImageInJar(String loc) throws IOException {
        return ImageIO.read(Texture.class.getResource(loc));
    }

    public static BufferedImage loadImage(String loc) throws IOException {
        try {
            return loadImageInJar(loc);
        } catch (IOException e) {
            return loadImageOutJar(loc);
        }
    }

    // Bind the texture to use it with OpenGL
    public void bind() {
        glBindTexture(GL_TEXTURE_2D, textureId);
    }

    public void destroy() {
        glDeleteTextures(textureId);
    }

    // Unbind any texture
    public static void unbindTexture() {
        glBindTexture(GL_TEXTURE_2D, 0);
    }
}


Game.java
package net.parallelworlds.core;

import net.parallelworlds.tests.TestGameState;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Color;
import org.lwjgl.util.Rectangle;

import static org.lwjgl.opengl.GL11.*;

/**
 * Created by nanoc_000 on 20/10/2014.
 */

// Main class of the game
public class Game {
    public static final void main(String[] args) {
        Game.GetInstance().setGameState(new TestGameState());
        Game.GetInstance().run();
    }

    // STATIC VARS //
    private static Game GAME_INSTANCE = null;

    public static final String GAME_NAME = "Parallel Worlds";
    public static final String GAME_VERSION = "0.0.1";

    public static final int DEFAULT_WIDTH = 600;
    public static final int DEFAULT_HEIGHT = 800;

    public static final int TILES_PER_LINE = 16;

    // GAME SYSTEM IMPORTANT VARS //

    private float lastFrameTime; // Time at the last frame

    private int fpsCounter = 0; // FPS counter
    private int fps = 0; // Number of frames per second
    private float lastFPSTime; // Timer for FPS calculation

    private boolean running = false; // Is the game running ?

    // GAME GRAPHICS VARS //
    private Color clearColor = (Color) Color.BLACK;
    private GameState gameState = null;
    private GameState nextGameState = null;

    // USEFUL STATIC FUNCTIONS //
    public static Game GetInstance() {
        if(GAME_INSTANCE == null)   GAME_INSTANCE = new Game();

        return GAME_INSTANCE;
    }

    public static float[] getFloatColorValues(Color color) {
        float[] colors = new float[4];

        colors[0] = ((float) color.getRed()) / 255.f;
        colors[1] = ((float) color.getGreen()) / 255.f;
        colors[2] = ((float) color.getBlue()) / 255.f;
        colors[3] = ((float) color.getAlpha()) / 255.f;

        return colors;
    }

    // -------------------------- //
    // HERE THE HARD WORD START :D //

    // Private constructor for singleton
    private Game() { }

    // Start the game
    public void run() {
        // Prevent for running the game init / loop twice
        if(!running) {
            try {
                Display.setDisplayMode(new DisplayMode(800, 600));
                Display.setResizable(true);
                Display.create();
            } catch (LWJGLException e) {
                e.printStackTrace();
                System.exit(0);
            }

            // Initialisation
            computeDelta();
            lastFPSTime = getTime();
            running = true;

            glEnable(GL_TEXTURE_2D);
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

            // Game loop
            while (!Display.isCloseRequested() && running) {
                gameLoop();
            }

            gameState.destroy();

            // Destroy OpenGL
            Display.destroy();
        }
    }

    // Main loop of the game
    private void gameLoop() {
        // Load the next GameState in OpenGL context
        if(nextGameState != null) {
            if(gameState != null) gameState.destroy();

            gameState = nextGameState;
            gameState.create();
            nextGameState.destroy();
        }

        float delta = computeDelta();

        updateTitle();
        updateFPS();

        update(delta);
        render();

        Display.update();
        Display.sync(60);
    }

    // Update the game
    private void update(float delta) {
        // Update the game state
        if(gameState != null)
            gameState.update(delta);
    }

    // Render the game
    private void render() {
        // Clearing the screen
        float[] color = getFloatColorValues(clearColor);

        glClearColor(color[0], color[1], color[2], color[3]);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Render the game state
        if(gameState != null)
            gameState.render();
    }

    // Calculate the new delta time between the last frame and now
    private float computeDelta() {
        float time = getTime();
        float delta = time - lastFrameTime;
        lastFrameTime = time;

        return delta;
    }

    // Return the time in second
    public float getTime() {
        long ms =  (Sys.getTime() * 1000) / Sys.getTimerResolution();
        return ((float) ms) / 1000.f;
    }

    // Update the FPS counter
    private void updateFPS() {
        if(getTime() - lastFPSTime > 1.f) {
            fps = fpsCounter;
            fpsCounter = 0;
            lastFPSTime += 1.f;
        }
        fpsCounter++;
    }

    // Tell is the game is running or not
    public boolean isRunning() { return running; }

    // Stop the game at the next frame
    public void stop() { running = false; }

    // Update the window title
    private void updateTitle() {
        String state = "NOSTATE";
        if (gameState != null) state = gameState.getStateID();
        Display.setTitle(GAME_NAME + " " + GAME_VERSION + " - " + state + " ("+ fps + " FPS)");
    }

    // Set the color used to clear the screen (maybe background ?)
    public void setClearColor(Color color) { clearColor = color; }

    // Return the color used to clear the screen
    public Color getClearColor() { return clearColor; }

    // Set the next game state
    public void setGameState(GameState state) { nextGameState = state; }

    // Return the actual game state
    public GameState getGameState() { return gameState; }

    // Set the render zone of the window
    public void setRenderZone(double posX, double posY, double width, double height) {
        if(width <= 0 || height <= 0)   throw new IllegalArgumentException("Width or height must be > 0");

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(posX, posX + width, posY, posY + height, 1, -1);
        glMatrixMode(GL_MODELVIEW);
    }
}


TestGameState :
package net.parallelworlds.tests;

import net.parallelworlds.core.Game;
import net.parallelworlds.core.GameState;
import net.parallelworlds.core.Texture;
import org.lwjgl.Sys;
import org.lwjgl.util.Color;

import java.io.IOException;

import static org.lwjgl.opengl.GL11.*;

/**
 * Created by nanoc_000 on 23/10/2014.
 */
public class TestGameState implements GameState {
    private Texture testTexture;

    @Override
    public String getStateID() {
        return "TEST";
    }

    @Override
    public void create() {
        //Game.GetInstance().setClearColor((Color) Color.RED);
        Game.GetInstance().setRenderZone(-5, -5, 10, 10);
        try {
            testTexture = new Texture(Texture.loadImageOutJar("assets/gfx/font.png"));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    @Override
    public void destroy() {
        testTexture.destroy();
    }

    @Override
    public void update(float delta) {

    }

    @Override
    public void render() {
        testTexture.bind();

        glBegin(GL_QUADS);
            glTexCoord2f(1.0f,1.0f); glVertex2f(-1.0f, 1.0f);
            glTexCoord2f(1.0f,0.0f); glVertex2f(1.0f, 1.0f);
            glTexCoord2f(0.0f,0.0f); glVertex2f(1.0f, -1.0f);
            glTexCoord2f(0.0f,1.0f); glVertex2f(-1.0f, -1.0f);
        glEnd();
    }
}


It renders a white box on a black background instead of my texture.

Hope someone will find out the problem.

Good afternoon.

Cornix

1) Have you enabled texturing?

2) Have you enabled lighting?

3) Are you using custom shaders?

Nanocryk

1 & 2 : How do I enable them ?
3 : Not at the moment I'm working on the basics for now

You have all the code used here. GameState is just an interface.

Cornix

You enable textures the same way you enable everything else in OpenGL too. You use glEnable with the apropriate gl enum constant.
You should read the documentation, you can find it online.

Nanocryk

I enable it in the run() method

abcdef

I don't know if this will help but this is what I setup pre render for my 2d drawing

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);

and this is what I use at the beginning of each render cycle

glClear(GL_COLOR_BUFFER_BIT);

The secret is you don't want any depth testing in 2D so disable it and don't clear the buffer, I can't remember why I needed the BLEND enablement though, but I know it works with this setup

Nanocryk

I set the setup as yours and it don't work :/ I also tried with a non transparent image (JPEG) but it's the same result. I think it's from the texture loading but the loading itself is aparently a well known code and I used it before.

Edit : Aparently this part of the code is fine. Don't know why it act like that :/

Nanocryk

My bad it works. When I copied your setup I forgot to enable GL_TEXTURE_2D again --'
Is slick-util free to use in free or commercial projects ?