[SOLVED AGAIN] Where are the texture coordinates going wrong?

So I am making a game engine, and fairly early on I came across a problem that is really boggling me. I have a test sphere implemented in-game by a class called “StaticMesh”, which contains a model parsed from an obj file and a texture. This sphere was created and exported in blender (where it looked fine), but when I attach a texture to the static mesh the texture coordinates just break.

EDIT: With help form StrideColossus I fixed the previous problem, and I thought I’d fixed the problem after that, but now it appears as though I haven’t. I used slick’s “flip” option on one of my school computers (a mac) and it solved the problem, but when I took my project back home to my computer (windows) the “flip” option ceased to work. I tried it again yesterday on a mac at school and the code is still good, but sure enough, when I take it home we’re back to “flip” not working. I’d assume this is a system related issue, but maybe there’s a fix?

This is what it looked like in blender (the black lines mark the seams)

And this is what it looks like in my engine

I’ve combed over the code, and can’t find any reason as to why it would act like this, but maybe some fresh eyes can spot a problem. Here’s the “StaticMesh” class


package neatEngine.game;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import neatEngine.Console;
import neatEngine.graphics.Polygon;
import neatEngine.graphics.TextureImage;
import neatEngine.math.Vector2f;
import neatEngine.math.Vector3f;
import static org.lwjgl.opengl.GL11.*;

/*
 * An environment component that has static geometry.
 * <p>
 * The static mesh should be used for models who's geometry and textures don't
 * change.
 * 
 * @author Kwauhn (Quinn Desforge-Major)
 * @version 0.01
 */
public class StaticMesh {
	private Vector3f p;
	private List<Vector3f> v;
	private List<Vector3f> n;
	private List<Vector2f> t;
	private List<Polygon> f;
	private TextureImage ti;
	
	/*
	 * Creates a new static mesh from the specified resource at the specified
	 * position.
	 * 
	 * @param r the resource file containing the mesh information
	 * @param tir the resource file containing the texture image
	 * @param p the vector representing the position of this static mesh
	 */
	public StaticMesh(String r, String tir, Vector3f p){
		this.p = p;
		v = new ArrayList<Vector3f>();
		n = new ArrayList<Vector3f>();
		t = new ArrayList<Vector2f>();
		f = new ArrayList<Polygon>();
		ti = new TextureImage(tir);
		
		int lineNumber = 0;
		try{
			BufferedReader reader = new BufferedReader(new FileReader(new File(r)));
			String line;
			while((line = reader.readLine()) != null){
				if(line.startsWith("v ")){
					String[] indices = line.split(" ");
					v.add(new Vector3f(Float.valueOf(indices[1]), Float.valueOf(indices[2]), Float.valueOf(indices[3])));
				}else if(line.startsWith("vn ")){
					String[] indices = line.split(" ");
					n.add(new Vector3f(Float.valueOf(indices[1]), Float.valueOf(indices[2]), Float.valueOf(indices[3])));
				}else if(line.startsWith("vt ")){
					String[] indices = line.split(" ");
					t.add(new Vector2f(Float.valueOf(indices[1]), Float.valueOf(indices[2])));
				}else if(line.startsWith("f ")){
					String[] points = line.split(" ");
					String[] point1 = points[1].split("/");
					String[] point2 = points[2].split("/");
					String[] point3 = points[3].split("/");
					Vector3f vertexIndices = new Vector3f(Integer.valueOf(point1[0]), Integer.valueOf(point2[0]), Integer.valueOf(point3[0]));
					Vector3f normalIndices = new Vector3f(Integer.valueOf(point1[2]), Integer.valueOf(point2[2]), Integer.valueOf(point3[2]));
					Vector3f textureIndices = new Vector3f(Integer.valueOf(point1[1]), Integer.valueOf(point2[1]), Integer.valueOf(point3[1]));
					f.add(new Polygon(vertexIndices, normalIndices, textureIndices));
				}
				lineNumber++;
			}
			reader.close();
		}catch(Exception e){
			Console.logError(e, "Could not load static mesh file: Error at " + lineNumber);
			e.printStackTrace();
		}
	}
	
	/*
	 * Renders this static mesh.
	 */
	public void render(){
		ti.bind();
		glPushMatrix();
		glTranslatef(p.getX(), p.getY(), p.getZ());
		glBegin(GL_TRIANGLES);
		{
			for(int i = 0; i < f.size(); i++){
				Vector3f v1 = v.get((int)(f.get(i).getVertexIndices().getX()) - 1);
				Vector3f v2 = v.get((int)(f.get(i).getVertexIndices().getY()) - 1);
				Vector3f v3 = v.get((int)(f.get(i).getVertexIndices().getZ()) - 1);
				Vector3f n1 = n.get((int)(f.get(i).getNormalIndices().getX()) - 1);
				Vector3f n2 = n.get((int)(f.get(i).getNormalIndices().getY()) - 1);
				Vector3f n3 = n.get((int)(f.get(i).getNormalIndices().getZ()) - 1);
				Vector2f t1 = t.get((int)(f.get(i).getTextureIndices().getX()) - 1);
				Vector2f t2 = t.get((int)(f.get(i).getTextureIndices().getY()) - 1);
				Vector2f t3 = t.get((int)(f.get(i).getTextureIndices().getZ()) - 1);
				
			    glNormal3f(n1.getX(), n1.getY(), n1.getZ());	glTexCoord2f(t1.getX(), t1.getY());	   glVertex3f(v1.getX(), v1.getY(), v1.getZ());
			    glNormal3f(n2.getX(), n2.getY(), n2.getZ());	glTexCoord2f(t2.getX(), t2.getY());	   glVertex3f(v2.getX(), v2.getY(), v2.getZ());
			    glNormal3f(n3.getX(), n3.getY(), n3.getZ());	glTexCoord2f(t3.getX(), t3.getY());    glVertex3f(v3.getX(), v3.getY(), v3.getZ());
			}
		}
		glEnd();
		glPopMatrix();
		glDisable(GL_TEXTURE_2D);
	}
	
	/*
	 * Returns the vector representing the position of this static mesh.
	 * 
	 * @return the vector representing the position of this static mesh
	 */
	public Vector3f getPosition(){
		return p;
	}
	
	/*
	 * Sets the current position vector to the one specified.
	 * 
	 * @param p the new position vector
	 */
	public void setPosition(Vector3f p){
		this.p = p;
	}
}

And here’s the “TextureImage” class


package neatEngine.graphics;

import java.io.File;
import java.io.FileInputStream;
import neatEngine.Console;
import org.newdawn.slick.opengl.*;

/*
 * This class makes texture handling easy and simple.
 * <p>
 * Texture Image uses the Slick Utility library to handle textures, and contains
 * some simple fail-safes to avoid fatal errors.
 * 
 * @author Kwauhn (Quinn Desforge-Major)
 * @version 0.01
 */
public class TextureImage {
	private Texture t;
	private String r;
	
	/*
	 * Creates a new Texture Image from the file with the provided path.
	 * 
	 * @param r the path of the image resource
	 */
	public TextureImage(String r){
		this.r = r;
		try{
			t = TextureLoader.getTexture("PNG", new FileInputStream(new File(r)), true);
		}catch(Exception e){
			Console.logError(e, "Could not load texture from \"" + r + "\"");
			t = null;
		}
	}
	
	/*
	 * Binds the texture for drawing with OpenGL.
	 */
	public void bind(){
		if(t != null){
			t.bind();
		}
	}
	
	/*
	 * Reloads the texture file (for runtime editing).
	 */
	public void reload(){
		try{
			t = TextureLoader.getTexture("PNG", new FileInputStream(new File(r)), true);
		}catch(Exception e){
			Console.logError(e, "Could not load texture from \"" + r + "\"");
			t = null;
		}
	}
}

Any help is appreciated. Staring at this code isn’t really helping the project get along, and there’s a lot to do! Thanks for reading :slight_smile:

Upon further investigation of the obj file, it appears as though blender set every UV index any given face to the same texture coordinate. Does anyone know how to fix this?

Are you sure everything it’s ok in these lines?

96 Vector2f t1 = t.get((int)(f.get(i).getTextureIndices().getX()) - 1);
97 Vector2f t2 = t.get((int)(f.get(i).getTextureIndices().getX()) - 1);
98 Vector2f t3 = t.get((int)(f.get(i).getTextureIndices().getX()) - 1);

Thank you! You saved me a lot of trouble looking in the wrong place, however, now the sphere looks like this

A can see the texture is actually being applied to the surface, it’s just in the wrong places :frowning: I’ll keep looking into it. Thanks so much for pointing that out!

The ‘seams’ look to be there now but are way fatter than the one in Blender, maybe it’s something to do with how you’re loading the texture image? Can you post that code as well.

As an aside that’s a lot of line.splits there ;), suggest use some intermediate variables: your code will be a lot cleaner and therefore easier to read and maintain, plus a little faster to load too.

Thanks for the tips on performance :slight_smile: I’ll keep that in mind when I’m cleaning up code.

As for the texture loader, I’m using slick-util, so I wouldn’t think it would be anything with that. I guess you never know though, this is the “TextureImage” class


package neatEngine.graphics;

import java.io.File;
import java.io.FileInputStream;

import neatEngine.Console;

import org.newdawn.slick.opengl.*;

/*
 * This class makes texture handling easy and simple.
 * <p>
 * Texture Image uses the Slick Utility library to handle textures, and contains
 * some simple fail-safes to avoid fatal errors.
 * 
 * @author Kwauhn (Quinn Desforge-Major)
 * @version 0.01
 */
public class TextureImage {
	private Texture t;
	private String r;
	
	/*
	 * Creates a new Texture Image from the file with the provided path.
	 * 
	 * @param r the path of the image resource
	 */
	public TextureImage(String r){
		this.r = r;
		try{
			t = TextureLoader.getTexture("PNG", new FileInputStream(new File(r)));
		}catch(Exception e){
			Console.logError(e, "Could not load texture from \"" + r + "\"");
			t = null;
		}
	}
	
	/*
	 * Binds the texture for drawing with OpenGL.
	 */
	public void bind(){
		if(t != null){
			t.bind();
		}
	}
	
	/*
	 * Reloads the texture file (for runtime editing).
	 */
	public void reload(){
		try{
			t = TextureLoader.getTexture("PNG", new FileInputStream(new File(r)));
		}catch(Exception e){
			Console.logError(e, "Could not load texture from \"" + r + "\"");
			t = null;
		}
	}
}

The performance is a bonus, I was thinking more about maintainability - be a lot easier to change and fix the code if it’s neater.

Never used Slick but it’s unlikely to be a problem with that as you say. Maybe try using the ‘flip’ parameter in getTexture()? (though it doesn’t look like that’s the problem)

SUCCESS! Thank you so much :smiley: now I can continue with the shader stuff.

I never would have guessed that was the problem, how did you know?

OpenGL generally needs to have images inverted, you’ll see that ‘flip’ option in most engines or libraries (including my own custom stuff that does it by default).
Glad you got it sorted.

  • stride

Slick’s flip option doesn’t seem to work on my windows computer. I added an edit above and updated the code, does anyone know why it refuses to work on my machine?

I have NO IDEA why, but if the extension of the image is anything but “PNG” slick loads the image properly (even though it’s a png file).

Seriously, what the hell :stuck_out_tongue: