Texturing Geometry Instances

Does anyone know if you can apply different textures to individual instances of a Geometry instance? 



Applying textures to the individual objects whose batches become the new geometry batch instances does not work.  I could only find a way to apply the same texture to all instances by applying it to the parent node or to the mesh.

Ummm, have you tried using Spatials and texturing them and then adding them to a node?


Spatial sphere1 = new Sphere( "sphere1", 12, 12, 1 );
Spatial sphere2 = new Sphere( "sphere2", 12, 12, 1 );
sphere2.setLocalTranslation( new Vector3f( 1, 0, 0 ) );

Texture texture1 = new Texture();
texture1.setImage( TextureManager.loadImage( java.awt.Image, [true|false] );
Texture texture2 = new Texture();
texture12setImage( TextureManager.loadImage( java.awt.Image, [true|false] );

TextureState textureState1 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
textureState1.setTexture( texture1 );
TextureState textureState2 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
textureState2.setTexture( texture2 );

sphere1.setRenderState( textureState1 );
sphere2.setRenderState( textureState2 );

Node bothSpheres = new Node( "BothSpheres" );
bothSphere.attachChild( sphere1 );
bothSphere.attachChild( sphere2 );



Thats working for me.

I don't mean normal instances of spatials.  I  mean geometry instances such as those created in TestGeometryInstancing.

Ummm, that class  is just using a box (which is a spatial) and basically duplicating it (including all index and texture buffers),  in essence it IS creating a LOT of EXACT clones.  Therefore they are all using the same texture coordinates and the SAME texture.  You would probably need multiple geometryInstances

Thanks for the advise.  I am using this for a tile based game and so I created geometryBatchCreators for each type of tile and it works fine.

Glad I could help :slight_smile:

About 1 out of every 10 times I run my program with this new code it crashes and gives:



ar 6, 2008 6:30:58 PM com.jmex.game.DefaultUncaughtExceptionHandler uncaughtException

SEVERE: Main game loop broken by uncaught exception

java.lang.NullPointerException

at com.jme.renderer.lwjgl.LWJGLRenderer.draw(Unknown Source)

at com.jme.scene.batch.TriangleBatch.draw(Unknown Source)

at com.jme.scene.TriMesh.draw(Unknown Source)

at com.jme.scene.Spatial.onDraw(Unknown Source)

at com.jme.scene.Node.draw(Unknown Source)

at com.jme.scene.Spatial.onDraw(Unknown Source)

at com.jme.scene.Node.draw(Unknown Source)

at com.jme.scene.Spatial.onDraw(Unknown Source)

at com.jme.renderer.lwjgl.LWJGLRenderer.draw(Unknown Source)

at com.jmex.game.state.BasicGameState.render(Unknown Source)

at com.jmex.game.state.GameStateNode.render(Unknown Source)

at com.jmex.game.StandardGame.render(Unknown Source)

at com.jmex.game.StandardGame.run(Unknown Source)

at java.lang.Thread.run(Thread.java:619)



This creates a new mesh and adds it to a node for each different type of tile I have.




private void createIsland(int[][] map)
{
   GeometryBatchCreator   geometryBatchCreator;/** The batch creator */
   TriMesh               mesh;/** The mesh containing the created batch */
   
   FloatBuffer texCoords = BufferUtils.createVector2Buffer( 4 );
   texCoords.put(getUForPixel(texCordX1, texWidth)).put(getVForPixel(texCordY1, texHeight));
    texCoords.put(getUForPixel(texCordX1, texWidth)).put(getVForPixel(texCordY2, texHeight));
    texCoords.put(getUForPixel(texCordX2, texWidth)).put(getVForPixel(texCordY2, texHeight));
    texCoords.put(getUForPixel(texCordX2, texWidth)).put(getVForPixel(texCordY1, texHeight));
   
   // A box that will be instantiated
   Quad quad = new Quad( "Box", 64, 64 );
   
   quad.setTextureBuffer( 0, texCoords );
   
   for (int x = 0; x < states.getTextureStates().length; x++)
   {
      // The batch geometry creator
      geometryBatchCreator = new GeometryBatchCreator();
      
      for (int i = 0; i < map.length; i++)
      {
         for (int j = 0; j < map[i].length; j++)
         {
            if ( map[i][j] == x )
            {
               // Box instance attributes
               GeometryBatchInstanceAttributes attributes = new GeometryBatchInstanceAttributes(
                     new Vector3f( 64 * j , 0, 64 * i ), // Translation
                     new Vector3f( 1.0f, 1.0f, 1.0f ), // Scale
                     new Vector3f( -FastMath.PI/2, 0.0f, 0.0f ), // Rotation
                     new ColorRGBA( 1.0f, 1.0f, 1.0f, 0.0f ) ); // Color

               // Box instance (batch and attributes)
               GeometryBatchInstance instance = new GeometryBatchInstance( quad.getBatch( 0 ), attributes );

               // Add the instance
               geometryBatchCreator.addInstance( instance );
            }
         }
      }
      if (geometryBatchCreator.getInstances().size() != 0)
      {
         // Create a TriMesh
         mesh = new TriMesh();
         mesh.setRenderState( states.getTextureStates()[x] );
         mesh.updateRenderState();
         TriangleBatch batch = mesh.getBatch( 0 );
         batch.setModelBound( new BoundingBox() );

         // Create the batch's buffers
         batch.setIndexBuffer( BufferUtils.createIntBuffer( geometryBatchCreator.getNumIndices() ) );
         batch.setVertexBuffer( BufferUtils.createVector3Buffer( geometryBatchCreator.getNumVertices() ) );
         batch.setNormalBuffer( BufferUtils.createVector3Buffer( geometryBatchCreator.getNumVertices() ) );
         batch.setTextureBuffer( BufferUtils.createVector2Buffer( geometryBatchCreator.getNumVertices() ), 0 );
         batch.setColorBuffer( BufferUtils.createFloatBuffer( geometryBatchCreator.getNumVertices() * 4 ) );

         // Commit the instances to the mesh batch
         geometryBatchCreator.commit( batch );
         batch.updateModelBound();

         // Return the mesh
         attachChild( mesh );
      }
   }
}

Well, its a null-pointer during the render loop.



So it could be one of a 1000 things, however you do have THIS piece of information.  You could try wrapping possible problem areas with a try-catch and print results when an exception occurred.  Also, if you are doing something like looking for a node name make sure ALL nodes have names (or at least an empty string).


Spatial spatial = new Spatial();

Legal but creates a spatial with it's name equal to null.

Names should not be the issue (they are ok to be null). Build jME with debug info (line numbers) and run your program again. After that you can check what's really causing that NPE.


    private void setCallBacks() {
       
        ContactCallback callBack = new ContactCallback() {
            String [] nodeNames = new String[2];
           
            public boolean adjustContact( PendingContact c ) {
               
                    nodeNames[0] = c.getNode1().getName();
                    nodeNames[1] = c.getNode2().getName();

                c.setIgnored( false );
                return false;
            }
        };
       
        physicsSpace.getContactCallbacks().add( callBack );
    }



That will throw an null pointer exception if one of the nodes has been created without setting the name ;)


Build jME with debug info

How is this done??

QFT:

basixs said:


    private void setCallBacks() {
       
        ContactCallback callBack = new ContactCallback() {
            String [] nodeNames = new String[2];
           
            public boolean adjustContact( PendingContact c ) {
               
                    nodeNames[0] = c.getNode1().getName();
                    nodeNames[1] = c.getNode2().getName();

                c.setIgnored( false );
                return false;
            }
        };
       
        physicsSpace.getContactCallbacks().add( callBack );
    }




Enlighten me.  I'm missing where the NPE would happen in that code?  nodeNames would have a null entry, but I don't see where it would have a npe.

hehe, renanse beat me to it (even with explanation), but still:


basixs said:

That will throw an null pointer exception if one of the nodes has been created without setting the name ;)

No, it will not. But of course you could write a piece of code which throws NPEs if there are no names. What I was trying to say: no code in jME (or jME Physics) should rely on the names to be set (and only little parts to). At least LWJGLRenderer.draw (where the posted NPE arises) does not even touch the name.

basixs said:

Build jME with debug info

How is this done??

erm, that really depends on your IDE and you really should do it with all your classes - give the IDE/compiler doc a shot.

:-o 2 Admin posts in a minute, guess I said something wrong. What I was trying to say is that its possible to  have logic errors and to cause problems in jME that will cause weird errors like this.  Didn't mean to offend anyone…





    private void setCallBacks() {
       
        ContactCallback callBack = new ContactCallback() {
           
            public boolean adjustContact( PendingContact c ) {
               
               if ( c.getNode1().getName() == "test" || c.getNode2().getName() == "test"  ) {

               }

                c.setIgnored( false );
                return false;
            }
        };
       
        physicsSpace.getContactCallbacks().add( callBack );
    }



There, thats closer to what is in the examples anyway :P



Build jME with debug info


OOHHH, you just mean debugg,  gotcha

No need to worry.  I don't think anyone gets offended that easily. :stuck_out_tongue:


if ( c.getNode1().getName() == "test" || c.getNode2().getName() == "test"  ) {


That still wouldn't cause an NPE (and is bad string comparison anyhow.)  If you did "test".equals(c.getNode1().getName()) it would be correct string comparison and avoid null issues.

But yeah, you do have to be aware that names can be null.

Yup, thats how I do string comparisons also.



However, I believe thats how some of the physics examples do it.