LWJGL Forum

Programming => OpenGL => Topic started by: Bob A Red Dino on March 22, 2012, 22:27:23

Title: Textures origin in strange position
Post by: Bob A Red Dino on March 22, 2012, 22:27:23
I'm been working with OpenGL for a a few weeks now, so I'm still very new to it.  From what read and seen, the origin for anything on the screen is supposed to be the lower left corner.  When I add textures to the display, they start in seemingly random positions.  I've tried removing all transformations from my code, and the problem persists.  I'm assuming that I made a stupid mistake somewhere, but I can't seem to find it.  Here's all the code that has anything to do with the display, if you want to to post anything else just ask.  Thanks in advance.

Game.java:

package game;

import java.util.Arrays;
import game.graphic.MainMenuGui;
import game.graphic.Window;
import game.io.InputHandler;
import game.io.Settings;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

public class Game {

private String[] threadNames = { "Game Thread", "Clock Thread" };
private Thread[] threads = new Thread[threadNames.length];
public boolean[] threadsRunning = new boolean[threads.length];
public static Game game = new Game();
public Settings settings;
public Window window;
public InputHandler inputHandler;

private long startTime;
public int runningTime;
public int prevRunningTime;
private int prevFrames;
private int totalFrames;
private int peakFPS;
public boolean commandsToRun;

public Game() {
settings = new Settings();
window = new Window(settings);
inputHandler = new InputHandler(settings);
}

public static void main(String[] args) {
game.threads[0] = game.new GameThread();
game.threads[0].start();
}

private void gameLoop() throws Throwable {
window.updateScreen();
inputHandler.checkInputs();
calcFPS();
}

private void loadGame() throws Throwable {
initThreads(1);
settings.loadSettings();
window.loadScreen();
displaySystemInfo();
GL11.glFlush();
window.setCurrentGui(new MainMenuGui(window, window.texRender));
}

private void runGame() {
try {
Arrays.fill(threadsRunning, true);
loadGame();
while(threadsRunning[0]) {
try {
gameLoop();
}
catch(OutOfMemoryError e) {
System.gc();
}
}
}
catch(Throwable throwable) {
crashGame(throwable);
}
finally {
stopGame();
}
}

private void initThreads(int num) {
threads[num] = new MinorThread(num);
threads[num].setDaemon(true);
threads[num].start();
}

public void beginShutdown() {
Arrays.fill(threadsRunning, false);
}

private void stopGame() {
settings.changeSetting("windowWidth", (short)window.getWindowWidth());
settings.changeSetting("windowHeight", (short)window.getWindowHeight());
settings.changeSetting("windowedFullscreen", (short)window.getFrameExtendedState());
settings.saveSettings();
Display.destroy();
System.gc();
System.exit(1);
}

private void crashGame(Throwable throwable) {
throwable.printStackTrace();
System.out.println("I crashed!");
System.exit(0);
}

private void displaySystemInfo() {
System.out.println("=====System Info=====");
System.out.println("OS: " + System.getProperty("os.name"));
System.out.println("GPU: " + GL11.glGetString(GL11.GL_VENDOR) + " " + GL11.glGetString(GL11.GL_RENDERER));
System.out.println("Java Version: " + System.getProperty("java.version"));
System.out.println("LWJGL Version: " + Sys.getVersion());
}

private void calcFPS() {
totalFrames++;
float loopTime = runningTime - prevRunningTime;
float loopFrames = totalFrames - prevFrames;
if(loopTime >= 100) {
int fps = (int)(loopFrames / (loopTime / 1000));
peakFPS = Math.max(fps, peakFPS);
window.setFrameTitle("FPS: " + fps + "  Peak FPS: " + peakFPS);
prevRunningTime = runningTime;
prevFrames = totalFrames;
}
}

private class GameThread extends Thread {

public GameThread() {
super(threadNames[0]);
}

public void run() {
game.runGame();
}
}

private class MinorThread extends Thread {

private int threadNum;

public MinorThread(int num) {
super(threadNames[num]);
threadNum = num;
}

private void threadLoop() {
switch(threadNum) {
case 1:
runningTime = (int)(System.nanoTime() / 1000000 - startTime);
break;
}
}

public void run() {
init();
while(threadsRunning[threadNum])
threadLoop();
}

private void init() {
switch(threadNum) {
case 1:
startTime = System.nanoTime() / 1000000;
break;
}
}
}
}



Window.java:

package game.graphic;

import java.awt.Canvas;
import java.awt.Frame;
import java.awt.Image;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.ImageIcon;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
import game.Game;
import game.io.Settings;

public class Window implements WindowListener {

private Settings settings;
public TextureRender texRender;
private Frame frame;
private Canvas canvas;
private Gui currentGui;
private Gui lastGui;
private int windowWidth;
private int windowHeight;
private boolean fullscreen;
private boolean frameChanged;

private int DEFAULT_WINDOW_WIDTH = 1024;
private int DEFAULT_WINDOW_HEIGHT = 632;

public Window(Settings settings) {
this.settings = settings;
texRender = new TextureRender();
}

public void updateScreen() throws Throwable {
windowWidth = canvas.getWidth();
windowHeight = canvas.getHeight();
glViewport(0, 0, windowWidth, windowHeight);
if(currentGui != lastGui) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
currentGui.loadGui();
}
else if(frameChanged)
currentGui.updateGui();
Display.update();
frameChanged = false;
lastGui = currentGui;
}

public void loadScreen() throws Throwable {
if(settings.getSettingValue("windowedFullscreen") == 0) {
windowWidth = settings.getSettingValue("windowWidth");
windowHeight = settings.getSettingValue("windowHeight");
}
else {
windowWidth = DEFAULT_WINDOW_WIDTH;
windowHeight = DEFAULT_WINDOW_HEIGHT;
}
initComponents();
windowWidth = canvas.getWidth();
windowWidth = canvas.getHeight();
Display.setParent(canvas);
Display.setDisplayMode(new DisplayMode(windowWidth, windowHeight));
Display.setInitialBackground(1f, 1f, 1f);
Display.create();
glOrtho(0, windowWidth, 0, windowHeight, 1, -1);
glLoadIdentity();
currentGui = new StartupGui(this, texRender);
updateScreen();
}

private void initComponents() {
frame = new Frame();
frame.setExtendedState(settings.getSettingValue("windowedFullscreen"));
frame.setSize(windowWidth + 16, windowHeight + 38);
frame.setLocationRelativeTo(null);
frame.addWindowListener(this);
Image icon = new ImageIcon(getClass().getResource("/resources/image/ui/icon.png")).getImage();
frame.setIconImage(icon);
canvas = new Canvas();
canvas.setFocusable(true);
canvas.requestFocus();
canvas.setIgnoreRepaint(true);
frame.add(canvas);
frame.setVisible(true);
}

public void setCurrentGui(Gui gui) {
currentGui = gui;
}

public void toggleFullscreen() {
settings.changeSetting("windowWidth", (short)windowWidth);
settings.changeSetting("windowHeight", (short)windowHeight);
try {
fullscreen = !fullscreen;
Display.setDisplayMode(fullscreen ? Display.getDesktopDisplayMode() : new DisplayMode(windowWidth, windowHeight));
Display.setFullscreen(fullscreen);
frame.setVisible(!fullscreen);
}
catch(LWJGLException e) {
System.out.println("Could not toggle fullscreen mode!");
}
}

public int getWindowWidth() {
return windowWidth;
}

public int getWindowHeight() {
return windowHeight;
}

public int getFrameExtendedState() {
return frame.getExtendedState();
}

public void setFrameTitle(String title) {
frame.setTitle(title);
}

public void windowClosing(WindowEvent e) {
Game.game.beginShutdown();
}

public void windowOpened(WindowEvent e) {}

public void windowClosed(WindowEvent e) {}

public void windowIconified(WindowEvent e) {}

public void windowDeiconified(WindowEvent e) {}

public void windowActivated(WindowEvent e) {}

public void windowDeactivated(WindowEvent e) {}
}



StartupGui.java:

package game.graphic;

import static org.lwjgl.opengl.GL11.*;
import java.io.IOException;

public class StartupGui extends Gui {

private Window window;
private TextureRender texRender;
private Texture logo;

public StartupGui(Window window, TextureRender texRender) throws IOException {
this.window = window;
this.texRender = texRender;
}

public void updateGui() throws Throwable {
glPushMatrix();
glBegin(GL_QUADS);
{
glTexCoord2f(0, 0);
glVertex2f(0, 0);

glTexCoord2f(0, logo.getHeight());
glVertex2f(0, logo.getHeight());

glTexCoord2f(logo.getWidth(), logo.getHeight());
glVertex2f(logo.getWidth(), logo.getHeight());

glTexCoord2f(logo.getWidth(), 0);
glVertex2f(logo.getWidth(), 0);
}
glEnd();
glPopMatrix();
}

public void loadGui() throws Throwable {
logo = texRender.getTexture("ui/icon.png");
logo.bind();
glMatrixMode(GL_PROJECTION);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glPushMatrix();
glBegin(GL_QUADS);
{
glTexCoord2f(0, 0);
glVertex2f(0, 0);

glTexCoord2f(0, logo.getHeight());
glVertex2f(0, logo.getHeight());

glTexCoord2f(logo.getWidth(), logo.getHeight());
glVertex2f(logo.getWidth(), logo.getHeight());

glTexCoord2f(logo.getWidth(), 0);
glVertex2f(logo.getWidth(), 0);
}
glEnd();
glPopMatrix();
}
}



MainMenuGui.java:

package game.graphic;

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

public class MainMenuGui extends Gui {

private Window window;
private TextureRender texRender;

public MainMenuGui(Window window, TextureRender texRender) throws Throwable {
this.window = window;
this.texRender = texRender;
}

public void updateGui() throws Throwable {
drawBackground();
}

public void loadGui() throws Throwable {
drawBackground();
}

private void drawBackground() throws Throwable {
Texture bg = texRender.getTexture("ui/bg.png");
bg.bind();
glMatrixMode(GL_PROJECTION);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glPushMatrix();
for(int i = 0; i * bg.getImageWidth() < window.getWindowWidth() / 5; i++) {
for(int j = 0; j * bg.getImageHeight() < window.getWindowHeight() / 5; j++) {
float x = i * bg.getWidth();
float y = -j * bg.getHeight();
glBegin(GL_QUADS);
{
glTexCoord2f(x, y);
glVertex2f(x, y);

glTexCoord2f(x, y + bg.getHeight());
glVertex2f(x, y + bg.getHeight());

glTexCoord2f(x + bg.getWidth(), y + bg.getHeight());
glVertex2f(x + bg.getWidth(), y + bg.getHeight());

glTexCoord2f(x + bg.getWidth(), y);
glVertex2f(x + bg.getWidth(), y);
}
glEnd();
}
}
glPopMatrix();
}
}



TextureRender.java:

package game.graphic;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Hashtable;
import javax.swing.ImageIcon;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;

public class TextureRender {

private HashMap<String, Texture> table = new HashMap<String, Texture>();
private IntBuffer textureIDBuffer = BufferUtils.createIntBuffer(1);
private ColorModel glAlphaColorModel;
private ColorModel glColorModel;

public TextureRender() {
glAlphaColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8, 8, 8 }, true, false,
ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);
glColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false,
ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
}

private int createTextureID() {
GL11.glGenTextures(textureIDBuffer);
return textureIDBuffer.get(0);
}

public Texture getTexture(String resourceName) throws IOException {
Texture tex = table.get(resourceName);
if(tex != null)
return tex;
tex = getTexture(resourceName, GL11.GL_TEXTURE_2D, GL11.GL_RGBA, GL11.GL_LINEAR, GL11.GL_LINEAR);
table.put(resourceName, tex);
return tex;
}

public Texture getTexture(String resourceName, int target, int dstPixelFormat, int minFilter, int magFilter) throws IOException {
int srcPixelFormat;
int textureID = createTextureID();
Texture texture = new Texture(target, textureID);
GL11.glBindTexture(target, textureID);
BufferedImage bufferedImage = loadImage(resourceName);
texture.setWidth(bufferedImage.getWidth());
texture.setHeight(bufferedImage.getHeight());
if(bufferedImage.getColorModel().hasAlpha())
srcPixelFormat = GL11.GL_RGBA;
else
srcPixelFormat = GL11.GL_RGB;
ByteBuffer textureBuffer = convertImageData(bufferedImage, texture);
if(target == GL11.GL_TEXTURE_2D) {
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
}
GL11.glTexImage2D(target, 0, dstPixelFormat, get2Fold(bufferedImage.getWidth()), get2Fold(bufferedImage.getHeight()), 0, srcPixelFormat,
GL11.GL_UNSIGNED_BYTE, textureBuffer);
return texture;
}

private static int get2Fold(int fold) {
int ret = 2;
while(ret < fold)
ret *= 2;
return ret;
}

private ByteBuffer convertImageData(BufferedImage bufferedImage, Texture texture) {
ByteBuffer imageBuffer;
WritableRaster raster;
BufferedImage texImage;
int texWidth = 2;
int texHeight = 2;
while(texWidth < bufferedImage.getWidth())
texWidth *= 2;
while(texHeight < bufferedImage.getHeight())
texHeight *= 2;
texture.setTextureHeight(texHeight);
texture.setTextureWidth(texWidth);
if(bufferedImage.getColorModel().hasAlpha()) {
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, texWidth, texHeight, 4, null);
texImage = new BufferedImage(glAlphaColorModel, raster, false, new Hashtable<String, Object>());
}
else {
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, texWidth, texHeight, 3, null);
texImage = new BufferedImage(glColorModel, raster, false, new Hashtable<String, Object>());
}
Graphics g = texImage.getGraphics();
g.setColor(new Color(0f, 0f, 0f, 0f));
g.fillRect(0, 0, texWidth, texHeight);
g.drawImage(bufferedImage, 0, 0, null);
byte[] data = ((DataBufferByte)texImage.getRaster().getDataBuffer()).getData();
imageBuffer = ByteBuffer.allocateDirect(data.length);
imageBuffer.order(ByteOrder.nativeOrder());
imageBuffer.put(data, 0, data.length);
imageBuffer.flip();
return imageBuffer;
}

private BufferedImage loadImage(String ref) throws IOException {
URL url = getClass().getResource("/resources/image/" + ref);
if(url == null)
throw new IOException("Cannot find: " + ref);
Image img = new ImageIcon(url).getImage();
BufferedImage bufferedImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics g = bufferedImage.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
return bufferedImage;
}
}



Texture.java:

package game.graphic;

import org.lwjgl.opengl.GL11;

public class Texture {
private int target;
private int textureID;
private int height;
private int width;
private int texWidth;
private int texHeight;
private float widthRatio;
private float heightRatio;

public Texture(int target, int textureID) {
this.target = target;
this.textureID = textureID;
}

public void bind() {
GL11.glBindTexture(target, textureID);
}

public void setHeight(int height) {
this.height = height;
setHeight();
}

public void setWidth(int width) {
this.width = width;
setWidth();
}

public int getImageHeight() {
return height;
}

public int getImageWidth() {
return width;
}

public float getHeight() {
return heightRatio;
}

public float getWidth() {
return widthRatio;
}

public void setTextureHeight(int texHeight) {
this.texHeight = texHeight;
setHeight();
}

public void setTextureWidth(int texWidth) {
this.texWidth = texWidth;
setWidth();
}

private void setHeight() {
if(texHeight != 0) {
heightRatio = ((float)height) / texHeight;
}
}

private void setWidth() {
if(texWidth != 0) {
widthRatio = ((float)width) / texWidth;
}
}
}


Here's an example of what's happening. This is the image loaded from StartupGui.java, without any translations, scaling, rotations, etc.
(http://i.imgur.com/WnLuL.png)
Title: Re: Textures origin in strange position
Post by: CodeBunny on March 23, 2012, 12:48:13
I don't have time to look through your code, but are you a) flipping your x-coordinate somehow while drawing, or b) loading the image mirrored horizontally?
Title: Re: Textures origin in strange position
Post by: matheus23 on March 23, 2012, 13:03:32
Maybe your Texture size isn't a power of 2? It should be 2x2, 4x4, 16x16, 32x32, 64x64 ... ;)
Title: Re: Textures origin in strange position
Post by: CodeBunny on March 23, 2012, 17:14:56
Actually, that brings up a good point. Is your question about why there is white space around the swords? If so, then yes, Power-of-two textures is the problem. Usually, loading a nPoT image onto a PoT texture results in the image being loaded onto the top-left of the texture (which is what I assumed you were asking about - why it was on the top-right and not the top-left).
Title: Re: Textures origin in strange position
Post by: Bob A Red Dino on March 23, 2012, 18:54:24
Thanks for the replies.  The texture is 64x64, and I'm not flipping it.  The Texture and TextureRender classes in my program are directly from the Space Invaders example in the tutorials section of lwjgl.org.
Title: Re: Textures origin in strange position
Post by: CodeBunny on March 24, 2012, 04:43:06
Do you mean texture or source image is 64x64? At a glance it looks like you mean the source image.

It looks like your image is being put onto a texture 2x its size.
Title: Re: Textures origin in strange position
Post by: matheus23 on March 24, 2012, 08:25:10
 Yes, the source Image is 64x64 ... I feel stupid for counting the pixels... I don't really get, why the Texture is bigger than the Image.. I'll take further look on the source now...

EDIT: Just seen your glOrtho(..... (last 2 values:) 1, -1);
I don't think the near value should be bigger, than the far value. Acctually gluOrtho2D() calls glOrtho(), with near/far values -1/1. Maybe that is the problem...
Either, you use gluOrtho2D() without near and far values, or you switch the -1 and 1...
EDIT2: Okey, switching -1 and 1 does not change anything...
Title: Re: Textures origin in strange position
Post by: CodeBunny on March 24, 2012, 13:02:41
Where's the code where you load the image onto a texture?
Title: Re: Textures origin in strange position
Post by: matheus23 on March 24, 2012, 19:38:41
@CodeBunny look at TextureRenderer.java
Title: Re: Textures origin in strange position
Post by: Fool Running on March 26, 2012, 13:20:12

Display.create();
glOrtho(0, windowWidth, 0, windowHeight, 1, -1);
glLoadIdentity();

I would guess this is your problem. You are setting up your view (with glOrtho) and then instantly clearing the setup (with glLoadIdentity).
You probably wanted something more like:

        glMatrixMode(GL_PROJECTION);
        glOrtho(0, windowWidth, 0, windowHeight, 1, -1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();