Texture2dArray support

I want to ask if anyone already used 2d texture arrays in jme? If so what am I missing with this code?: Of course I would very much like this to be in the core once it works, as it would greatly benefit terrains for example. (far less uniforms needed then, only one for alphamaparray, one for surfaces, one for surfacenormals) and allow to have like 50 different surfaces within one terrain.



Not to mention that it allows to create and use texture atlases very efficently what would benefit all those minecraft like games out here.





Patc for jme to add testcase and proper wrapper classes.

Code:
Index: src/test/jme3test/texture/2dArraytexture.j3md =================================================================== --- src/test/jme3test/texture/2dArraytexture.j3md (revision 0) +++ src/test/jme3test/texture/2dArraytexture.j3md (revision 0) @@ -0,0 +1,16 @@ +MaterialDef My MaterialDef { + + MaterialParameters { + Texture3D Texture + } + + Technique { + VertexShader GLSL100: jme3test/texture/2dArraytexture.vert + FragmentShader GLSL100: jme3test/texture/2dArraytexture.frag + + WorldParameters { + WorldViewProjectionMatrix + } + } + +} Index: src/test/jme3test/texture/2dArraytexture.vert =================================================================== --- src/test/jme3test/texture/2dArraytexture.vert (revision 0) +++ src/test/jme3test/texture/2dArraytexture.vert (revision 0) @@ -0,0 +1,11 @@ +uniform mat4 g_WorldViewProjectionMatrix; + +attribute vec3 inTexCoord; +attribute vec3 inPosition; + +varying vec3 texCoord; + +void main(){ + gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition,1.0); + texCoord=inTexCoord; +} No newline at end of file Index: src/test/jme3test/texture/2dArraytexture.frag =================================================================== --- src/test/jme3test/texture/2dArraytexture.frag (revision 0) +++ src/test/jme3test/texture/2dArraytexture.frag (revision 0) @@ -0,0 +1,7 @@ +uniform sampler2DArray m_Texture; + +varying vec3 texCoord; + +void main(){ + gl_FragColor= texture2DArray(m_Texture,vec3(texCoord.xy,texCoord.z*10)); +} No newline at end of file Index: src/test/jme3test/texture/TestTexture2DArrayLoading.java =================================================================== --- src/test/jme3test/texture/TestTexture2DArrayLoading.java (revision 0) +++ src/test/jme3test/texture/TestTexture2DArrayLoading.java (revision 0) @@ -0,0 +1,104 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package jme3test.texture; + +import com.jme3.app.SimpleApplication; +import com.jme3.bounding.BoundingBox; +import com.jme3.light.PointLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.VertexBuffer; +import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.shape.Sphere; +import com.jme3.texture.Image; +import com.jme3.texture.Image.Format; +import com.jme3.texture.Texture; +import com.jme3.texture.Texture2DArray; +import com.jme3.texture.Texture3D; +import com.jme3.util.BufferUtils; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.ArrayList; + +public class TestTexture2DArrayLoading extends SimpleApplication { + + public static void main(String[] args) { + TestTexture2DArrayLoading app = new TestTexture2DArrayLoading(); + app.start(); + } + + @Override + public void simpleInitApp() { + //mouseInput.setCursorVisible(true); + flyCam.setMoveSpeed(10); + //creating a sphere + Sphere sphere = new Sphere(32, 32, 1); + //getting the boundingbox + sphere.updateBound(); + BoundingBox bb = (BoundingBox) sphere.getBound(); + Vector3f min = bb.getMin(null); + float[] ext = new float[]{bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2}; + //we need to change the UV coordinates (the sphere is assumet to be inside the 3D image box) + sphere.clearBuffer(Type.TexCoord); + VertexBuffer vb = sphere.getBuffer(Type.Position); + FloatBuffer fb = (FloatBuffer) vb.getData(); + float[] uvCoordinates = BufferUtils.getFloatArray(fb); + //now transform the coordinates so that they are in the range of <0; 1> + for (int i = 0; i < uvCoordinates.length; i += 3) { + uvCoordinates[i] = (uvCoordinates[i] - min.x) / ext[0]; + uvCoordinates[i + 1] = (uvCoordinates[i + 1] - min.y) / ext[1]; + uvCoordinates[i + 2] = (uvCoordinates[i + 2] - min.z) / ext[2]; + } + //apply new texture coordinates + VertexBuffer uvCoordsBuffer = new VertexBuffer(Type.TexCoord); + uvCoordsBuffer.setupData(Usage.Static, 3, com.jme3.scene.VertexBuffer.Format.Float, + BufferUtils.createFloatBuffer(uvCoordinates)); + sphere.setBuffer(uvCoordsBuffer); + //create geometry, and apply material and our 3D texture + Geometry g = new Geometry("sphere", sphere); + Material material = new Material(assetManager, "jme3test/texture/2dArraytexture.j3md"); + try { + Texture texture = this.getTexture(); + material.setTexture("Texture", texture); + } catch (IOException e) { + e.printStackTrace(); + } + g.setMaterial(material); + rootNode.attachChild(g); + //add some light so that it is visible + PointLight light = new PointLight(); + light.setColor(ColorRGBA.White); + light.setPosition(new Vector3f(5, 5, 5)); + light.setRadius(20); + rootNode.addLight(light); + light = new PointLight(); + light.setColor(ColorRGBA.White); + light.setPosition(new Vector3f(-5, -5, -5)); + light.setRadius(20); + rootNode.addLight(light); + } + + /** + * This method creates a RGB8 texture with the sizes of 10x10x10 pixels. + */ + private Texture getTexture() throws IOException { + ArrayList<ByteBuffer> data = new ArrayList<ByteBuffer>(1); + ByteBuffer bb = BufferUtils.createByteBuffer(10 * 10 * 10 * 3);//all data must be inside one buffer + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10 * 10; ++j) { + bb.put((byte) (255f*i/10f)); + bb.put((byte) (255f*i/10f)); + bb.put((byte) (255f)); + } + } + bb.rewind(); + data.add(bb); + return new Texture2DArray(new Image(Format.RGB8, 10, 10, 10, data)); + } +} No newline at end of file Index: src/core/com/jme3/texture/Texture2DArray.java =================================================================== --- src/core/com/jme3/texture/Texture2DArray.java (revision 0) +++ src/core/com/jme3/texture/Texture2DArray.java (revision 0) @@ -0,0 +1,132 @@ +package com.jme3.texture; + +import java.io.IOException; + +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.texture.Texture.MagFilter; +import com.jme3.texture.Texture.MinFilter; +import com.jme3.texture.Texture.Type; +import com.jme3.texture.Texture.WrapAxis; +import com.jme3.texture.Texture.WrapMode; + +public class Texture2DArray extends Texture { + private WrapMode wrapS = WrapMode.EdgeClamp; + private WrapMode wrapT = WrapMode.EdgeClamp; + + + public Texture2DArray(){ + super(); + } + + public Texture2DArray(Image img) { + super(); + setImage(img); + if (img.getFormat().isDepthFormat()) { + setMagFilter(MagFilter.Nearest); + setMinFilter(MinFilter.NearestNoMipMaps); + } + } + + public Texture2DArray(int width, int height, int depth, Image.Format format) { + this(new Image(format, width, height, depth, null)); + } + + public Texture2DArray(int width, int height, int depth, int numSamples, Image.Format format) { + this(new Image(format, width, height, depth, null)); + getImage().setMultiSamples(numSamples); + } + + public void setWrap(WrapAxis axis, WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } else if (axis == null) { + throw new IllegalArgumentException("axis can not be null."); + } + switch (axis) { + case S: + this.wrapS = mode; + break; + case T: + this.wrapT = mode; + break; + case R: + throw new RuntimeException("no third axis wrapmode supported"); + } + } + + public void setWrap(WrapMode mode) { + if (mode == null) { + throw new IllegalArgumentException("mode can not be null."); + } + this.wrapS = mode; + this.wrapT = mode; + } + + public WrapMode getWrap(WrapAxis axis) { + switch (axis) { + case S: + return wrapS; + case T: + return wrapT; + case R: + throw new RuntimeException("no third axis wrapmode supported"); + } + throw new IllegalArgumentException("invalid WrapAxis: " + axis); + } + + @Override + public Type getType() { + return Type.TwoDimensionalArray; + } + + + @Override + public boolean equals(Object other) { + if (!(other instanceof Texture3D)) { + return false; + } + Texture2DArray that = (Texture2DArray) other; + if (this.getWrap(WrapAxis.S) != that.getWrap(WrapAxis.S)) { + return false; + } + if (this.getWrap(WrapAxis.T) != that.getWrap(WrapAxis.T)) { + return false; + } + return super.equals(other); + } + + @Override + public Texture createSimpleClone() { + Texture2DArray clone = new Texture2DArray(); + createSimpleClone(clone); + return clone; + } + + @Override + public Texture createSimpleClone(Texture rVal) { + rVal.setWrap(WrapAxis.S, wrapS); + rVal.setWrap(WrapAxis.T, wrapT); + return super.createSimpleClone(rVal); + } + + + @Override + public void write(JmeExporter e) throws IOException { + super.write(e); + OutputCapsule capsule = e.getCapsule(this); + capsule.write(wrapS, "wrapS", WrapMode.EdgeClamp); + capsule.write(wrapT, "wrapT", WrapMode.EdgeClamp); + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + InputCapsule capsule = e.getCapsule(this); + wrapS = capsule.readEnum("wrapS", WrapMode.class, WrapMode.EdgeClamp); + wrapT = capsule.readEnum("wrapT", WrapMode.class, WrapMode.EdgeClamp); + } + +} Index: src/core/com/jme3/asset/TextureKey.java =================================================================== --- src/core/com/jme3/asset/TextureKey.java (revision 8391) +++ src/core/com/jme3/asset/TextureKey.java (working copy) @@ -39,6 +39,7 @@ import com.jme3.texture.Texture; import com.jme3.texture.Texture.Type; import com.jme3.texture.Texture2D; +import com.jme3.texture.Texture2DArray; import com.jme3.texture.Texture3D; import com.jme3.texture.TextureCubeMap; import java.io.IOException; @@ -52,6 +53,7 @@ private boolean asTexture3D; private int anisotropy; private Texture.Type textureTypeHint=Texture.Type.TwoDimensional; + private boolean asTextureArray;
 public TextureKey(String name, boolean flipY) {
     super(name);

@@ -104,6 +106,8 @@
tex = new TextureCubeMap();
} else if (isAsTexture3D()) {
tex = new Texture3D();

  •    } else if (isAsTextureArray()) {
    
  •        tex = new Texture2DArray();
       } else {
           tex = new Texture2D();
       }
    

@@ -155,7 +159,15 @@
public void setAsTexture3D(boolean asTexture3D) {
this.asTexture3D = asTexture3D;
}
+

  • public boolean isAsTextureArray() {

  •    return asTextureArray;
    
  • }

  • public void setAsTextureArray(boolean b) {

  •   this.asTextureArray = b;
    
  • }

  • @Override
    public boolean equals(Object other) {
    if (!(other instanceof TextureKey)) {
    @@ -191,4 +203,5 @@
    asCube = ic.readBoolean("as_cubemap", false);
    anisotropy = ic.readInt("anisotropy", 0);
    }

}

Hi!



I suggest you take a look at this topic : transformation-shader-contribution.



In your example you dont define the inTexCoord variable in your material definition, also you have to update inTexCoord inside your code with something like this : material.setVector2(“TranslateAmount”, new Vector2f(offset_x, offset_y));



where translateAmount is the field you defined in the material definition file for inTexCoord in your shader.



also 2d Texture arrays are called “sprite sheets”, I use this little nifty tool to create them : Sprite sheet packer, it converts all the images inside one directory to one sprite sheet, complete with a txt file with all the pixel_offsets and width and lengths.



I personally use this technique for animated textures btw.

Well I hope thay just missed your post, I did at least, else I would be using them already ^^



Thaks for the link to the old post, I will at least certainly use this.

What is Texture2d Array and where to use it?

Think of one textue consisting out of several textures, the 3th texture coordinat then decidies with texture to use,. good for batching stuff.

Its a bit difficult to use because if you do use it, you essentially make GPUs lower than OGL3.2 (if i recall) unsupported. I was looking for a more flexible solution which could allow usage of both.



Anybody have any ideas?

Also we support texture3D, and the can be used as a texture2DArray.

See the test cases in the test repo.

maybe we could has some convenient class to generate one from several texture 2D



@phate666 I missed your posts too, and you’r right about PSSM in the issue tracker, and Texture2DArrays would remove the terrain 16 maps limit too…

@Momoko_Fan, even if it’s 3.2 specific, we have a way to find out if the hardware supports it anyway and use a fallback implementation if it doesn’t.

We can’t ignore new opengl features because we want 2.0 compliance. We just have to let the users know that if they use those features their games will need ogl 3.2.

Texture3d is not usable for texture atlases, because it mipmaps between the height layers , while the 2d array only mipmaps each layer.

oh ok, didn’t know that.

Well… i guess we can’t avoid adding this to the core Kirill!!!

Well also where is the problem with breaking opengl2.0 ? If its an optional feature, and we clearly state that it needs opengl 3.0 each developer can decide for themself if they want to support 2.0 or not.

We support Texture3D but its not an OpenGL3.2 feature.

I guess its okay to add Texture2DArray support, but please don’t change the existing materials …

I won’t change existing material, we’re in beta now…got to watch what we’re doing :wink:

Well basic question, when can i expect this to be in the svn version? Since I dont have commit rights i need to wait :confused:

i’ll do it this week

EmpirePhoenix said:
Think of one textue consisting out of several textures, the 3th texture coordinat then decidies with texture to use,. good for batching stuff.


the 3th texture coordinate...batching stuff...a little clarification would help. :)
EmpirePhoenix said:
Think of one textue consisting out of several textures, the 3th texture coordinat then decidies with texture to use,. good for batching stuff.


the 3th texture coordinate...batching stuff...a little clarification would help. :)