Large sprites and texture size

I've been fiddling around with jme2 and I'm just trying to do some proof of concept-ish type things to figure out what I'm capable of pulling off. Using the forums I've been able to find most of the help I've needed without an account so far. But last night I ran into a bit of a showstopper and I wasn't able to get a firm idea of what to do (for once!).



So I'm basically planning an RPG/survival game of sorts with an angled gods-eye view of the player (30-45 degrees, haven't decided if I'm going parallel projection or not. Sorta dig the perspective feel with the sprites). I'm only using sprites for characters and simple models for everything else (just doing what I know). I've gotten a lot of the sprite animation figured out with just some quickie 64x64 animated sprites. However, the issue I've ran into is that I get a grey-jaggy around the edge of my characters if I'm using a non power of 2 texture size. I thought I was coding it wrong, but then I flopped my image into a 512x512 and it was fine. - Now normally this wouldn't be an issue right? Well, the problem is that I'd planned on having fairly detailed character sprites so the camera can zoom in and out without a terrible loss of quality. So for even a character bigger than 512x512, I'd have to bump up to 1024x1024.



Wow. That's huge. And a massive waste of space since my characters would probably take about 1/4 of that once I'm done. It would be great if I could even do things like 128x512 or something because the characters are fairly narrow in comparison to height. But it seems like the jaggies return if it's not square and power of two.



Is there anything I can do with this?



*- One thing: The choice here to use sprites is an artistic style move, nothing against 3d.

If I understand correctly your problem, you can pack more than one sprite image in a single texture image. For example, you can build a 512x512 image that contains four 128x512 frames and use that image to paint four sprites. You control which part of the image goes where via texture coordinates.



For a flat quad lying on the YZ plane:



float[] vertices = { 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0 };



you can use a texcoord generation function like this:


private float[] uvs(float imageSize, float x0, float y0, float x1, float y1) {
    float minU = x0 / imageSize;
    float minV = y0 / imageSize;
    float maxU = x1 / imageSize;
    float maxV = y1 / imageSize;
    return new float[] {
        minU, minV,
        minU, maxV,
        maxU, maxV,
        maxU, maxV,
        maxU, minV,
        minU, minV,
    };
}



to map portions of a (flipped) image. In the case of the four 128x512 slices:

float[] firstSlice = uvs(512, 0, 0, 128, 512);
float[] secondSlice = uvs(512, 128, 0, 256, 512)
float[] thirdSlice = uvs(512, 256, 0, 384, 512);
float[] fourthSlice =uvs(512, 384, 0, 512, 512);

Here's a test case:


import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.scene.TexCoords;
import com.jme.scene.TriMesh;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
import com.jme.util.geom.BufferUtils;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

public class Test extends SimpleGame {

    public static void main(String[] args) {
        Test test = new Test();
        test.setConfigShowMode(ConfigShowMode.ShowIfNoConfig);
        test.start();
    }

    private float[] uvs(float imageSize, float x0, float y0, float x1, float y1) {
        float minU = x0 / imageSize;
        float minV = y0 / imageSize;
        float maxU = x1 / imageSize;
        float maxV = y1 / imageSize;
        return new float[]{
                    minU, minV,
                    minU, maxV,
                    maxU, maxV,
                    maxU, maxV,
                    maxU, minV,
                    minU, minV,};
    }

    @Override
    protected void simpleInitGame() {
        float[] vertices = {
            0, 0, 0,
            0, 1, 0,
            1, 1, 0,
            1, 1, 0,
            1, 0, 0,
            0, 0, 0
        };
        float[] uvs = uvs(512, 384, 0, 512, 512);

        float[] normals = {
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,
            0, 0, -1
        };
        int[] indices = {
            0, 1, 2,
            3, 4, 5,
            6, 7, 8
        };

        FloatBuffer uvBuffer = BufferUtils.createFloatBuffer(uvs);
        TexCoords tc = new TexCoords(uvBuffer, 2);
        Texture texture = TextureManager.loadTexture(getClass().getResource("texture.png"), true);
        FloatBuffer vbuffer = BufferUtils.createFloatBuffer(vertices);
        FloatBuffer nbuffer = BufferUtils.createFloatBuffer(normals);
        IntBuffer ibuffer = BufferUtils.createIntBuffer(indices);
        TriMesh mesh = new TriMesh("quad", vbuffer, nbuffer, null, tc, ibuffer);
        TextureState ts = this.display.getRenderer().createTextureState();
        ts.setTexture(texture, 0);
        mesh.setModelBound(new BoundingBox());
        mesh.setRenderState(ts);
        mesh.updateRenderState();
        mesh.updateModelBound();
        rootNode.attachChild(mesh);
        rootNode.updateGeometricState(0, true);
        rootNode.updateRenderState();
    }
}



The texture.png image is:

The issue with using non-power-of-2 textures is that a lot of video cards do not support them. At least OpenGL2 is required, which is ATI/NV cards after 2003 and for Intel cards after 2006. Most likely your card doesn't support the entire extension, so things like filtering or mipmapping might not be supported for NPOT textures, explaining the gray edges. In most cases using a 1024x1024 texture instead of 512x512 isn't a big deal. For a simple game like this you won't run out of VRAM and with DXT compression you can reduce the size of the texture further.

Oh alrighty. I had a cute test running with 64x64 sprites in a 512x512 file, so I guess that explains it - the sprite sheet itself has to have the power of 2's size, not necessarily the sections. I hadn't gone that far with it yet. Whew. I was a touch worried there for a moment since I like this engine so far! :lol:



I guess the next question to ask is how large can I make the sprite sheet itself? Does it really matter at all? I don't really know exactly how many frames I'll be drawing out just yet, but I assume it will be a rather large sheet. Probably bigger than 4096x4096. I guess if worst comes to worst I can just have different sheets for different things, since of course certain characters will have boatloads more sprites than others.



Thanks for the example also pgi, I get the concept.



I'm actually a touch surprised my 8800gt doesn't fall into the category you're listing out Momoko. Maybe they slacked off and left some things out? I dunno. Either way, I still want it to work on different cards, so good or bad it's something I'd have to consider.



Thanks guys :smiley:

That's strangeā€¦ I have a 8400M GS and it works perfectly fine with NPOT. I wouldn't recommend going as high as 4096 since some video cards might not support it. 2048 is fine.