Trouble with dynamically built geometry

I'm building a visualizer using some custom data that I am converting into a TriMesh object. However, if I add the TriMesh to the root node, nothing shows up. If I first export and immediately import the mesh, it shows up beautifully as expected. Thanks in advance for any help!



If you comment out the EXPORT/IMPORT block in the code, I get a JVM crash:


#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x031f9e38, pid=5556, tid=3912
#
# JRE version: 6.0_14-b08
# Java VM: Java HotSpot(TM) Client VM (14.0-b16 mixed mode, sharing windows-x86 )
# Problematic frame:
# C  [ig4icd32.dll+0x49e38]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#



The Java frames from the log indicates the following:


Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  org.lwjgl.opengl.GL11.nglDrawElements(IIILjava/nio/Buffer;IJ)V+0
j  org.lwjgl.opengl.GL11.glDrawElements(ILjava/nio/IntBuffer;)V+38
j  com.jme.renderer.lwjgl.LWJGLRenderer.draw(Lcom/jme/scene/TriMesh;)V+202
j  com.jme.scene.TriMesh.draw(Lcom/jme/renderer/Renderer;)V+23
j  com.jme.scene.Spatial.onDraw(Lcom/jme/renderer/Renderer;)V+120
j  com.jme.scene.Node.draw(Lcom/jme/renderer/Renderer;)V+44
j  com.jme.scene.Spatial.onDraw(Lcom/jme/renderer/Renderer;)V+120
j  com.jme.renderer.lwjgl.LWJGLRenderer.draw(Lcom/jme/scene/Spatial;)V+20
j  com.jme.app.SimpleGame.render(F)V+18
j  com.jme.app.BaseGame.start()V+46
j  Test.main([Ljava/lang/String;)V+9
v  ~StubRoutines::call_stub



My code is as follows:


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.scene.TexCoords;
import com.jme.scene.TriMesh;
import com.jme.util.export.xml.XMLExporter;
import com.jme.util.export.xml.XMLImporter;

public class Test extends SimpleGame {

   @Override
   protected void simpleInitGame() {

      int width = 10;
      int length = 10;
      int meshSize = width * length;
      float[] vertices = new float[3 * meshSize];
      /*
       * calculate and assign vertex locations here. The result is currently a
       * grid of points with varying heights. I'm not using normal terrain
       * rendering for texture flexibility and the fact that it may not always
       * be able to be reduced to a height field in the future.
       *
       * For this example, I have just made a very simple mesh calculation
       */
      for (int zi = 0; zi < length; zi++) {
         for (int xi = 0; xi < width; xi++) {
            int vertexIndex = zi * width * 3 + xi * 3;
            vertices[vertexIndex] = (float) xi;
            vertices[vertexIndex + 1] = (float) (xi - zi) / (float) 2
                  * (float) Math.random();
            vertices[vertexIndex + 2] = (float) zi;
         }
      }

      // Create index buffer to store the mesh faces
      ByteBuffer bb = ByteBuffer.allocateDirect((width - 1) * (length - 1)
            * 2 * 12);
      IntBuffer indices = bb.asIntBuffer();
      for (int zi = 0; zi < length - 1; zi++) {
         for (int xi = 0; xi < width - 1; xi++) {

            // triangle 1 - half of quad
            indices.put(zi * width + xi);
            indices.put((zi + 1) * width + xi);
            indices.put((zi + 1) * width + (xi + 1));

            // triangle 2 - the other half of the quad
            indices.put((zi + 1) * width + (xi + 1));
            indices.put(zi * width + (xi + 1));
            indices.put(zi * width + xi);
         }
      }
      indices.flip();

      // Put vertices into a direct bytebuffer
      bb = ByteBuffer.allocateDirect(meshSize * 12);
      for (int zi = 0; zi < length; zi++) {
         for (int xi = 0; xi < width; xi++) {
            int base = zi * width * 3 + xi * 3;
            bb.putFloat(vertices[base]);
            bb.putFloat(vertices[base + 1]);
            bb.putFloat(vertices[base + 2]);
         }
      }
      bb.flip();
      FloatBuffer vertBuffer = bb.asFloatBuffer();

      bb = ByteBuffer.allocateDirect(meshSize * 12);
      FloatBuffer normals = bb.asFloatBuffer();
      // just point the normals up for now
      for (int zi = 0; zi < length; zi++) {
         for (int xi = 0; xi < width; xi++) {
            normals.put(0);
            normals.put(1);
            normals.put(0);
         }
      }
      normals.flip();

      // Assign empty tex coords for now
      float[] texCoordsArray = new float[meshSize * 2];
      TexCoords texCoords = TexCoords.makeNew(texCoordsArray);
      texCoords.perVert = 2;
      
      //Create tri mesh
      TriMesh triMesh = new TriMesh("GeneratedTriMesh", vertBuffer, normals,
            null, texCoords, indices);

      // BEGIN EXPORT/IMPORT BLOCK
      try {
         XMLExporter.getInstance().save(triMesh, new File("meshOutput.xml"));
         triMesh = (TriMesh) XMLImporter.getInstance().load(
               new FileInputStream("meshOutput.xml"));
         triMesh.setModelBound(new BoundingBox());
         triMesh.updateModelBound();

      } catch (IOException e) {
         e.printStackTrace();
      }
      // END EXPORT/IMPORT BLOCK
      rootNode.attachChild(triMesh);
   }

   /**
    * @param args
    */
   public static void main(String[] args) {
      Test test = new Test();
      test.start();
   }

}

I am still unable to solve this issue. There has to be some little thing (or maybe big?) that I'm doing wrong. I've greatly simplified the example code that hits the problem. Any insight into what I'm doing wrong would be greatly appreciated.



The code given below causes my JVM to crash as soon as the rendering hits my added TriMesh. If you uncomment the code that writes it to a file and reads it back, it works like a charm. This workaround is insufficient for my purposes since it is very time consuming (and memory intensive) to write large meshes to disk and read them back.



import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.scene.TexCoords;
import com.jme.scene.TriMesh;
import com.jme.util.export.xml.XMLExporter;
import com.jme.util.export.xml.XMLImporter;

public class SimpleGenGeometryTest extends SimpleGame {

   @Override
   protected void simpleInitGame() {

      float[] vertices = new float[] {
            0f, 1f, 0f,
            0f, 0.5f, 5f,
            5f, -0.5f, 0f
      };

      // Create index buffer to store the mesh faces
      ByteBuffer bb = ByteBuffer.allocateDirect(3 * 4);
      IntBuffer indices = bb.asIntBuffer();
      indices.put(0);
      indices.put(1);
      indices.put(2);
      indices.flip();

      // Put vertices into a direct bytebuffer
      bb = ByteBuffer.allocateDirect(9 * 4);
      for (int i = 0; i < 9; i++) {
         bb.putFloat(vertices[i]);
      }
      bb.flip();
      FloatBuffer vertBuffer = bb.asFloatBuffer();

      bb = ByteBuffer.allocateDirect(9 * 4);
      FloatBuffer normals = bb.asFloatBuffer();
      // just point the normals up for now
      for (int i = 0; i < 3; i++) {
         normals.put(0);
         normals.put(1);
         normals.put(0);
      }
      normals.flip();

      // Assign empty tex coords for now
      float[] texCoordsArray = new float[] {
            0f, 0f,
            0f, 1f,
            1f, 0f
      };
      TexCoords texCoords = TexCoords.makeNew(texCoordsArray);
      texCoords.perVert = 2;

      // Create tri mesh
      TriMesh triMesh = new TriMesh("GeneratedTriMesh", vertBuffer, normals,
            null, texCoords, indices);
      triMesh.setModelBound(new BoundingBox());
      triMesh.updateModelBound();

      // BEGIN WORKAROUND
//      try {
//         XMLExporter.getInstance().save(triMesh, new File("meshOutput.xml"));
//         triMesh = (TriMesh) XMLImporter.getInstance().load(
//               new FileInputStream("meshOutput.xml"));
//
//      } catch (IOException e) {
//         e.printStackTrace();
//      }
      // END WORKAROUND
      rootNode.attachChild(triMesh);
   }

   /**
    * @param args
    */
   public static void main(String[] args) {
      SimpleGenGeometryTest test = new SimpleGenGeometryTest();
      test.setConfigShowMode(ConfigShowMode.AlwaysShow);
      test.start();
   }

}

Fixed it. The problem was that I was manually creating the direct byte buffers instead of using BufferUtil.createFloatBuffer(). The key difference was the ByteOrder being specified as the native byte order.



Whew!