Strange behaviour for meshes with 2 textures


Hi everybody,

Recently I have have started using textures.

I am noticing a strange behaviour for objects that have more than one texture applied to them.

The simplest example are cubes. In the following screenshot one of the tables (A) has only one texture applied to it. The other table (B) has a texture for the top and bottom sides, and a different one for the sides.

In the second table (B), the top and bottom sides are not solid. My airplane can traverse then. However for some reason the wooden sides can be hit (they are solid).



I imagine that they are two TextureStates implied, but I don't know if that should make any difference :? for jmePhysics2.

When I show bounds and physics everything seems ok to me. There is another box with the same two textures and the same problem.

In the other hand, I might need to avoid jmePhysics2 since it is starting to be killer :(. What a mess, I don't want to code physics myself but with 20 airplanes my computer struggles .

Any idea?

I believe each texture is a separate TriMesh in jME, because although you can have multiple batches in a trimesh, there is a single (shared) renderstate for all batches. Thus, testing against "the mesh" may only test against the first mesh in the collection of meshes loaded from a file.

Note that the collision functions also seem to only use the first batch from a TriMesh, so if there are multiple batches, only the first is collidable.

Thank you very much for your reply. It's a great hint.



I will need to check a few things but now I have a direction  :).

If you have more or less 'complex' geometry in your scene (e.g. a table top composed from multiple meshes) you should not use trimesh accuracy everywhere. You should model your physical world separately. E.g. by annotating physical shapes as node names in your graphical modeler. You can then use a single physical box for your table top again. This also helps a lot with performance!

Even if I postprocessed the geometry to assign single shapes (boxes, capsules, spheres…) to some objects, there will be some objects that have more than one texture which still need triangle accuracy.



I wonder why collisions only check the first batch in a TriMesh. To me it sounds like it should check all triangles. Is this a desirable feature?

(Of course in 2.0 there will not be this issue - no batches :slight_smile: )

This is not about textures actually, but batches in general - but you already figured that…


jjmontes said:

I wonder why collisions only check the first batch in a TriMesh. To me it sounds like it should check all triangles. Is this a desirable feature?

This is the lack of a feature :) - I was too lazy to take multiple batches into account. There is a TODO in GeomTriMesh where multiple batches must be honored. Patches welcome ;)

Hi again.



I implemented support for multiple batches. Unfortunately that is not helping.



My table is still not solid. Here is what I have:



Node: tableComputers
Locks: VolMshShdTrs
Transform: (18.741558, 14.212279, -5.6391196)[-90.00001, 0.0, 0.0]{8.0}
Children: Node/table_002  Node/curvedFoot_003  Node/curvedFoot_002 


Node: table_002
Locks: VolMshShdTrs
Transform: (62.741558, 34.05372, -6.039122)[-90.000015, -1.0E-5, -9.999996E-6]{8.0}
Children: SharedMesh/Cube_003 
SharedMesh: Cube_003
RenderStates: ShMT
0: /C:/Documents and Settings/jjmontes/Mis documentos/Proyectos/iw/data/levels/cid2/whitemetaltable.jpg - RGB888-DXT1x512x512
r.rgb = r.rgb * texture2D(tex0, gl_TexCoord[0].st).rgb;
r.a = r.a * texture2D(tex0, gl_TexCoord[0].st).a;

Locks: VolShdTrs
Transform: (62.741558, 34.05372, -6.039122)[-90.000015, -1.0E-5, -9.999996E-6]{8.0}
.......................
SharedBatch: null
RenderStates: ShMT
0: /C:/Documents and Settings/jjmontes/Mis documentos/Proyectos/iw/data/levels/cid2/darkwoodtable.jpg - RGB888-DXT1x512x512
r.rgb = r.rgb * texture2D(tex0, gl_TexCoord[0].st).rgb;
r.a = r.a * texture2D(tex0, gl_TexCoord[0].st).a;

Locks: VolShdTrs
Batches set: VINT0
Vertex: 16
Triangles: 8
.......................
SharedBatch: null
RenderStates: ShMT
0: /C:/Documents and Settings/jjmontes/Mis documentos/Proyectos/iw/data/levels/cid2/whitemetaltable.jpg - RGB888-DXT1x512x512
r.rgb = r.rgb * texture2D(tex0, gl_TexCoord[0].st).rgb;
r.a = r.a * texture2D(tex0, gl_TexCoord[0].st).a;

Locks: VolShdTrs
Batches set: VINT0
Vertex: 8
Triangles: 4



I guess the issue now is with shared meshes. If you point me in some direction (perhaps explaining it a bit) I will be glad to carry on research and solve it if I can.

Also, out of curiosity I would like to know why multiple batches are removed in 2.0 and how that will affect us (just a brief comment would do it).

As usual, thank you very much for your unvaluable help.

Batches were meant to be extremely lightweight scene objects without the overhead of bounding calcs, transforms, etc.  But in the end they just became the same thing as the Geometry object (and actually slowed things down a bit), so they have been removed in 2.0 to reduce the unnecessary complication of the scenegraph.

re SharedMesh:


        if ( triMesh instanceof SharedMesh ) {
            SharedMesh sharedMesh = (SharedMesh) triMesh;
            geom.updateData( sharedMesh.getTarget() );
        } else {
            geom.updateData( triMesh );
        }


instead of just geom.updateData( triMesh ); in OdeMesh.java might be enough. If not start investigating from there.

Well after a bit more research, it turns out that my initial patch was wrong. I wasn't pushing the correct indexes to ODE.



My patch is following your initial suggestion, and modifies updateData (TriMesh mesh). Basically, when copying geometry to ODE now all batches are being used.



This is the patched updateData that I am using (and so far working):



    /**
     * Recreates ODE vertices and indices lists for a given jME TriMesh object.
     * @param mesh The TriMesh to be built in ODE.
     */
    public void updateData( TriMesh mesh ) {

          int totalVerticesLength = 0;
          int totalIndicesLength = 0;
          
          // Walk batches and count indices and vertices
          for (int batchIndex = 0; batchIndex < mesh.getBatchCount(); batchIndex++) {
             TriangleBatch batch = mesh.getBatch (batchIndex);
             totalVerticesLength += (batch.getVertexCount() * 3);
          totalIndicesLength += (batch.getTriangleCount() * 3);
          }
       
          // Create vertices buffer
        if ( odeVertices != null ) {
          Ode.delete_floatArray( odeVertices );
          odeVertices = null;
        }
        odeVertices = Odejava.createSwigFloatArray( totalVerticesLength );
       
        // Create indices buffer
        if ( odeIndices != null ) {
          Ode.delete_intArray( odeIndices );
          odeIndices = null;
        }
        SWIGTYPE_p_int result = Ode.new_intArray( totalIndicesLength );
       
        // Retrieve mesh scale to bake vertex data
        float[] scale = {mesh.getWorldScale().x, mesh.getWorldScale().y, mesh.getWorldScale().z};
          
        // Walk batches and fill in vertices and index data
        int vertexOffset = 0;
        int indexOffset = 0;
       
          for (int batchIndex = 0; batchIndex < mesh.getBatchCount(); batchIndex++) {
             TriangleBatch batch = mesh.getBatch (batchIndex);
         
             IntBuffer indices = batch.getIndexBuffer();
          indices.rewind();
         
          FloatBuffer vertices = batch.getVertexBuffer();
          vertices.rewind();
         
          int partialVertexOffset = vertexOffset / 3;
          int verticesLength = batch.getVertexCount() * 3;
          for ( int i = 0; i < verticesLength / 3; i++ ) {
            Ode.floatArray_setitem( odeVertices, vertexOffset, vertices.get() * scale[0] );
            Ode.floatArray_setitem( odeVertices, vertexOffset + 1, vertices.get() * scale[1] );
            Ode.floatArray_setitem( odeVertices, vertexOffset + 2, vertices.get() * scale[2] );
            vertexOffset += 3;
          }
         
          int indicesLength = batch.getTriangleCount() * 3;
          for ( int i = 0; i < indicesLength / 3; i++ ) {
             Ode.intArray_setitem( result, indexOffset, indices.get() +  partialVertexOffset);
             Ode.intArray_setitem( result, indexOffset + 1, indices.get() + partialVertexOffset);
             Ode.intArray_setitem( result, indexOffset + 2, indices.get() + partialVertexOffset);
             indexOffset += 3;
         }
      }
          
      odeIndices = result;

      // Send to ODE
      Ode.dGeomTriMeshDataBuildSingle1( data, odeVertices.getVoidPointer(), 12,
              totalVerticesLength / 3, odeIndices.getVoidPointer(), totalIndicesLength, 12, null );
      if ( geomId != null ) { Ode.dGeomTriMeshSetData( geomId, data ); }
     }



Now, this doesn't seem to require any special action for shared meshes, since all vertex and index data is copied ( :?).

There is, however, something that is fooling me. Mainly because of my lack of understanding I guess. Have a look at function:

PhysicsCollisionGeometry PhysicsNode.generatePhysicsGeometry( Spatial spatial, Vector3f translation, Quaternion rotation, Vector3f scale, boolean useTriangleAccurateGeometries )

It contains a similar TODO note, and a hardcoded getBatch(0). This leads to a difficult situation: if the geometry with multiple batches is a TriMesh and triangle accuracy is required, then everything seems to work because ultimately createPhysicsGeometry is called and it will copy data (calling the patched function above).

However, this would probably fail for geometry with multiple batches if the Geometry is a Box, Sphere, etc. I think jME never builds those primitives using multiple batches but if it did, the code at generatePhysicsGeometry should be patched too to take multiple batches into account.

I know all this is a mess. Sorry for the fuzz.

According to renanse, I guess in the future will be easier to modify the Collada importer so it never uses batches. That way everything should work. @irrisor, I don't know what the target of a SharedMesh is :s (I have no clue about shared things, vbos, etc.., and I know I have to face that soon), but the updateData function seems to work just fine even if the mesh is a shared mesh (vertices and indices can be copied with no changes).


jjmontes said:
but the updateData function seems to work just fine even if the mesh is a shared mesh (vertices and indices can be copied with no changes).

good to hear that. nonetheless there should be a method to share physics meshes as well in the future...

So, I have tested this a bit more, and it seems to be working correctly.

I have also reviewed jmephysics tests.

Do you think the patch could go into the CVS?
jjmontes said:

Do you think the patch could go into the CVS?

sure, why not. I'll have a look at it as soon as I have some time for it.

Bump!



Any chance you review this irrisor? I truly thing it should go to the sources, I have been using this without problems so far, and I believe performance is not impacted.



Sorry for putting more load on you anyway.

Sorry for neglecting this that long - I'm really swamped with work. The patch is really good - committed to CVS. Thanks jjmontes!

It's a pleasure and an honour.