/* * Copyright (c) 2003-2009 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //package com.jme.scene; import java.io.IOException; import java.io.Serializable; //import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.ArrayList; import java.util.logging.Logger; import com.jme.bounding.CollisionTree; import com.jme.bounding.CollisionTreeManager; import com.jme.intersection.CollisionResults; import com.jme.math.FastMath; import com.jme.math.Ray; import com.jme.math.Triangle; import com.jme.math.Vector3f; //import com.jme.renderer.Renderer; import com.jme.system.JmeException; import com.jme.util.export.InputCapsule; import com.jme.util.export.JMEExporter; import com.jme.util.export.JMEImporter; import com.jme.util.export.OutputCapsule; import com.jme.util.geom.BufferUtils; // import com.jme.scene.TexCoords; import javax.media.opengl.GL; import com.jme.renderer.ColorRGBA; /** * TriMesh defines a geometry mesh. This mesh defines a three * dimensional object via a collection of points, colors, normals and textures. * The points are referenced via a indices array. This array instructs the * renderer the order in which to draw the points, creating triangles based on the mode set. * * @author Mark Powell * @author Joshua Slack * @version $Id: TriMesh.java 4640 2009-08-29 02:28:57Z blaine.dev $ */ public class TriMesh extends Geometry implements Serializable { /** * draw renders a TriMesh object including * it's normals, colors, textures and vertices. * * @see Renderer#draw(TriMesh) * @param tris * the mesh to render. */ void DrawParticles(iCameraPane display, Object3D geo, boolean selected, boolean rotate) // TriMesh tris) { display.DrawParticles(this, geo, selected, rotate); } static final long serialVersionUID = 0; boolean IsStatic() { return false; } private static final Logger logger = Logger.getLogger(TriMesh.class.getName()); // private static final long serialVersionUID = 2L; public enum Mode { /** * Every three vertices referenced by the indexbuffer will be considered * a stand-alone triangle. */ Triangles, /** * The first three vertices referenced by the indexbuffer create a * triangle, from there, every additional vertex is paired with the two * preceding vertices to make a new triangle. */ Strip, /** * The first three vertices (V0, V1, V2) referenced by the indexbuffer * create a triangle, from there, every additional vertex is paired with * the preceding vertex and the initial vertex (V0) to make a new * triangle. */ Fan; } protected transient IntBuffer indexBuffer; protected Mode mode = Mode.Triangles; protected int triangleQuantity; /** * Empty Constructor to be used internally only. */ public TriMesh() { super(); } /** * Constructor instantiates a new TriMesh object. * * @param name * the name of the scene element. This is required for * identification and comparison purposes. */ public TriMesh(String name) { super(name); } /** * Constructor instantiates a new TriMesh object. Provided * are the attributes that make up the mesh all attributes may be null, * except for vertices and indices. * * @param name * the name of the scene element. This is required for * identification and comparison purposes. * @param vertices * the vertices of the geometry. * @param normal * the normals of the geometry. * @param color * the colors of the geometry. * @param coords * the texture coordinates of the mesh. * @param indices * the indices of the vertex array. */ public TriMesh(String name, FloatBuffer vertices, FloatBuffer normal, FloatBuffer color, TexCoords coords, IntBuffer indices) { super(name); reconstruct(vertices, normal, color, coords); if (null == indices) { logger.severe("Indices may not be null."); throw new JmeException("Indices may not be null."); } setIndexBuffer(indices); logger.info("TriMesh created."); } /** * Recreates the geometric information of this TriMesh from scratch. The * index and vertex array must not be null, but the others may be. Every 3 * indices define an index in the vertices array that * references a vertex of a triangle. * * @param vertices * The vertex information for this TriMesh. * @param normal * The normal information for this TriMesh. * @param color * The color information for this TriMesh. * @param coords * The texture information for this TriMesh. * @param indices * The index information for this TriMesh. */ public void reconstruct(FloatBuffer vertices, FloatBuffer normal, FloatBuffer color, TexCoords coords, IntBuffer indices) { super.reconstruct(vertices, normal, color, coords); if (null == indices) { logger.severe("Indices may not be null."); throw new JmeException("Indices may not be null."); } setIndexBuffer(indices); } public void setMode(Mode mode) { this.mode = mode; } public Mode getMode() { return mode; } public IntBuffer getIndexBuffer() { return indexBuffer; } public void setIndexBuffer(IntBuffer indices) { this.indexBuffer = indices; recalcTriangleQuantity(); } protected void recalcTriangleQuantity() { if (indexBuffer == null) { triangleQuantity = 0; return; } switch (mode) { case Triangles: triangleQuantity = indexBuffer.limit() / 3; break; case Strip: case Fan: triangleQuantity = indexBuffer.limit() - 2; break; } } /** * Returns the number of triangles contained in this mesh. */ @Override public int getTriangleCount() { return triangleQuantity; } public void setTriangleQuantity(int triangleQuantity) { this.triangleQuantity = triangleQuantity; } // /** // * draw calls super to set the render state then passes // * itself to the renderer. LOGIC: 1. If we're not RenderQueue calling draw // * goto 2, if we are, goto 3 2. If we are supposed to use queue, add to // * queue and RETURN, else 3 3. call super draw 4. tell renderer to draw me. // * // * @param r // * the renderer to display // */ // public void draw(Renderer r) // { // if (!r.isProcessingQueue()) // { // if (r.checkAndAdd(this)) // { // return; // } // } // // super.draw(r); // r.draw(this); // } float[] texmat = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; final boolean supportsVBO = false; private FloatBuffer prevVerts; private FloatBuffer prevNorms; private FloatBuffer prevColor; private FloatBuffer[] prevTex; private int prevNormMode = GL.GL_ZERO; /** * Prepares the GL Context for rendering this geometry. This involves * initializing the VBO and obtaining the buffer data. * * @param g * the geometry to process. * @return true if VBO is used for indicis, false if not */ // protected boolean predrawGeometry(GL gl) // { // // final GL gl = GLU.getCurrentGL(); // Geometry g = this; // //// RenderContext context = display.getCurrentContext(); //// RendererRecord rendRecord = (RendererRecord) context.getRendererRecord(); // //// VBOInfo vbo = !generatingDisplayList ? g.getVBOInfo() : null; //// if (vbo != null && supportsVBO()) //// { //// prepVBO(g); //// } // //// indicesVBO = false; // // // set up data to be sent to card // // first to go is vertices // int oldLimit = -1; // FloatBuffer vertices = g.getVertexBuffer(); // if (vertices != null) // { // oldLimit = vertices.limit(); // // make sure only the necessary verts are sent through on old cards. // vertices.limit(g.getVertexCount() * 3); // } // if (false) // (supportsVBO && vbo != null && vbo.getVBOVertexID() > 0)) // { // use // // VBO //// gl.glEnableClientState(GL.GL_VERTEX_ARRAY); //// rendRecord.setBoundVBO(vbo.getVBOVertexID()); //// gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0); // } else if (vertices == null) // { // gl.glDisableClientState(GL.GL_VERTEX_ARRAY); // } else if (prevVerts != vertices) // { // // verts have changed // gl.glEnableClientState(GL.GL_VERTEX_ARRAY); // // ensure no VBO is bound // if (supportsVBO) // { //// rendRecord.setBoundVBO(0); // } // vertices.rewind(); // gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertices); // TODO Check assumed GL_FLOAT // } // if (oldLimit != -1) // { // vertices.limit(oldLimit); // } // prevVerts = vertices; // //// // apply fogging coordinate if support and the buffer is set for this //// // tri mesh //// if (supportsFogCoords) //// { //// oldLimit = -1; //// FloatBuffer fogCoords = g.getFogBuffer(); //// if (fogCoords != null) //// { //// oldLimit = fogCoords.limit(); //// // make sure only the necessary verts are sent through on old cards. //// fogCoords.limit(g.getVertexCount()); //// } //// if ((supportsVBO && vbo != null && vbo.getVBOVertexID() > 0)) //// { // use //// // VBO //// gl.glEnableClientState(GL.GL_FOG_COORDINATE_ARRAY_EXT); //// rendRecord.setBoundVBO(vbo.getVBOVertexID()); //// gl.glFogCoordPointerEXT(GL.GL_FLOAT, 0, 0); //// } else if (fogCoords == null) //// { //// gl.glDisableClientState(GL.GL_FOG_COORDINATE_ARRAY_EXT); //// } else if (prevFogCoords != fogCoords) //// { //// // fog coords have changed //// gl.glEnableClientState(GL.GL_FOG_COORDINATE_ARRAY_EXT); //// // ensure no VBO is bound //// if (supportsVBO) //// { //// rendRecord.setBoundVBO(0); //// } //// fogCoords.rewind(); //// gl.glFogCoordPointerEXT(0, 0, g.getFogBuffer()); //// } //// if (oldLimit != -1) //// { //// fogCoords.limit(oldLimit); //// } //// prevFogCoords = fogCoords; //// } // // if (g instanceof TriMesh) // { //// if ((supportsVBO && vbo != null && vbo.getVBOIndexID() > 0)) //// { // use VBO //// indicesVBO = true; //// rendRecord.setBoundElementVBO(vbo.getVBOIndexID()); //// } else if (supportsVBO) //// { //// rendRecord.setBoundElementVBO(0); //// } // } // //// Spatial.NormalsMode normMode = g.getNormalsMode(); // if (true) // normMode != Spatial.NormalsMode.Off) // { //// applyNormalMode(normMode, g); // FloatBuffer normals = g.getNormalBuffer(); // oldLimit = -1; // if (normals != null) // { // // make sure only the necessary normals are sent through on old // // cards. // oldLimit = normals.limit(); // normals.limit(g.getVertexCount() * 3); // } // if (false) //(supportsVBO && vbo != null && vbo.getVBONormalID() > 0)) // { // use // // VBO //// gl.glEnableClientState(GL.GL_NORMAL_ARRAY); //// rendRecord.setBoundVBO(vbo.getVBONormalID()); //// gl.glNormalPointer(GL.GL_FLOAT, 0, 0); // } else if (normals == null) // { // gl.glDisableClientState(GL.GL_NORMAL_ARRAY); // } else if (prevNorms != normals) // { // // textures have changed // gl.glEnableClientState(GL.GL_NORMAL_ARRAY); // // ensure no VBO is bound // if (supportsVBO) // { //// rendRecord.setBoundVBO(0); // } // normals.rewind(); // gl.glNormalPointer(GL.GL_FLOAT, 0, normals); // TODO Check assumed GL_FLOAT // } // if (oldLimit != -1) // { // normals.limit(oldLimit); // } // prevNorms = normals; // } else // { // if (prevNormMode == GL.GL_RESCALE_NORMAL) // { // gl.glDisable(GL.GL_RESCALE_NORMAL); // prevNormMode = GL.GL_ZERO; // } else if (prevNormMode == GL.GL_NORMALIZE) // { // gl.glDisable(GL.GL_NORMALIZE); // prevNormMode = GL.GL_ZERO; // } // oldLimit = -1; // gl.glDisableClientState(GL.GL_NORMAL_ARRAY); // prevNorms = null; // } // // FloatBuffer colors = g.getColorBuffer(); // oldLimit = -1; // if (colors != null) // { // // make sure only the necessary colors are sent through on old // // cards. // oldLimit = colors.limit(); // colors.limit(g.getVertexCount() * 4); // } // if (false) // (supportsVBO && vbo != null && vbo.getVBOColorID() > 0)) // { // use // // VBO //// gl.glEnableClientState(GL.GL_COLOR_ARRAY); //// rendRecord.setBoundVBO(vbo.getVBOColorID()); //// gl.glColorPointer(4, GL.GL_FLOAT, 0, 0); // } else if (colors == null) // { // gl.glDisableClientState(GL.GL_COLOR_ARRAY); // // // Disabling a color array causes the current color to be undefined. // // So enforce a current color here. // ColorRGBA defCol = g.getDefaultColor(); // if (defCol != null) // { //// rendRecord.setCurrentColor(defCol); // } else // { // // no default color, so set to white. //// rendRecord.setCurrentColor(1, 1, 1, 1); // } // } else if (prevColor != colors) // { // // colors have changed // gl.glEnableClientState(GL.GL_COLOR_ARRAY); // // ensure no VBO is bound // if (supportsVBO) // { //// rendRecord.setBoundVBO(0); // } // colors.rewind(); // gl.glColorPointer(4, GL.GL_FLOAT, 0, colors); // TODO Check assumed GL_FLOAT // } // if (oldLimit != -1) // { // colors.limit(oldLimit); // } // prevColor = colors; // //// TextureState ts = (TextureState) context.currentStates[RenderState.StateType.Texture.ordinal()]; //// int offset = 0; //// if (ts != null) //// { //// offset = ts.getTextureCoordinateOffset(); //// //// for (int i = 0; i < ts.getNumberOfSetTextures() && i < TextureState.getNumberOfFragmentTexCoordUnits(); i++) //// { //// TexCoords texC = g.getTextureCoords(i + offset); //// oldLimit = -1; //// if (texC != null) //// { //// // make sure only the necessary texture coords are sent //// // through on old cards. //// oldLimit = texC.coords.limit(); //// texC.coords.limit(g.getVertexCount() * texC.perVert); //// } //// if (capabilities.GL_ARB_multitexture) //// { //// gl.glClientActiveTexture(GL.GL_TEXTURE0 + i); //// } //// if ((supportsVBO && vbo != null && vbo.getVBOTextureID(i) > 0)) //// { // use //// // VBO //// gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY); //// rendRecord.setBoundVBO(vbo.getVBOTextureID(i)); //// gl.glTexCoordPointer(texC.perVert, GL.GL_FLOAT, 0, 0); //// } else if (texC == null) //// { //// gl.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY); //// } else if (prevTex[i] != texC.coords) //// { //// // textures have changed //// gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY); //// // ensure no VBO is bound //// if (supportsVBO) //// { //// rendRecord.setBoundVBO(0); //// } //// // set data //// texC.coords.rewind(); //// gl.glTexCoordPointer(texC.perVert, GL.GL_FLOAT, 0, texC.coords); // TODO Check assumed GL_FLOAT //// } else //// { //// gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY); //// } //// prevTex[i] = texC != null ? texC.coords : null; //// if (oldLimit != -1) //// { //// texC.coords.limit(oldLimit); //// } //// } //// //// if (ts.getNumberOfSetTextures() < prevTextureNumber) //// { //// for (int i = ts.getNumberOfSetTextures(); i < prevTextureNumber; i++) //// { //// if (capabilities.GL_ARB_multitexture) //// { //// gl.glClientActiveTexture(GL.GL_TEXTURE0 + i); //// } //// gl.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY); //// } //// } //// //// prevTextureNumber = ts.getNumberOfSetTextures() < TextureState.getNumberOfFixedUnits() ? ts.getNumberOfSetTextures() //// : TextureState.getNumberOfFixedUnits(); //// } // // return false; // indicesVBO; // } /** * Clears the buffers of this TriMesh. The buffers include its indexBuffer * only. */ public void clearBuffers() { super.clearBuffers(); setIndexBuffer(null); } // /** // * determines if a collision between this trimesh and a given spatial occurs // * if it has true is returned, otherwise false is returned. // */ // public boolean hasCollision( // Spatial scene, boolean checkTriangles, int requiredOnBits) // { // if (this == scene || !isCollidable(requiredOnBits) || !scene.isCollidable(requiredOnBits)) // { // return false; // } // if (getWorldBound().intersects(scene.getWorldBound())) // { // if (scene instanceof Node) // { // Node parent = (Node) scene; // for (int i = 0; i < parent.getQuantity(); i++) // { // if (hasCollision(parent.getChild(i), // checkTriangles, requiredOnBits)) // { // return true; // } // } // // return false; // } // // if (!checkTriangles) // { // return true; // } // // return hasTriangleCollision((TriMesh) scene, requiredOnBits); // } // // return false; // } // // /** // * determines if this TriMesh has made contact with the give scene. The // * scene is recursively transversed until a trimesh is found, at which time // * the two trimesh OBBTrees are then compared to find the triangles that // * hit. // */ // public void findCollisions( // Spatial scene, CollisionResults results, int requiredOnBits) // { // if (this == scene || !isCollidable(requiredOnBits) || !scene.isCollidable(requiredOnBits)) // { // return; // } // // if (getWorldBound().intersects(scene.getWorldBound())) // { // if (scene instanceof Node) // { // Node parent = (Node) scene; // for (int i = 0; i < parent.getQuantity(); i++) // { // findCollisions(parent.getChild(i), results, requiredOnBits); // } // } else // { // results.addCollision(this, (Geometry) scene); // } // } // } /** * Convenience wrapper for hasTriangleCollision(TriMesh, int) using default * collision mask (bit 1 set). */ final public boolean hasTriangleCollision(TriMesh toCheck) { return hasTriangleCollision(toCheck, 1); } /** * This function checks for intersection between this trimesh and the given * one. On the first intersection, true is returned. * * @param toCheck The intersection testing mesh. * @param requiredOnBits Collision will only be considered if both 'this' * and 'toCheck' have these bits of their collision masks set. * @return True if they intersect. */ public boolean hasTriangleCollision(TriMesh toCheck, int requiredOnBits) { // CollisionTree thisCT = CollisionTreeManager.getInstance().getCollisionTree(this); // CollisionTree checkCT = CollisionTreeManager.getInstance().getCollisionTree(toCheck); // // if (thisCT == null || checkCT == null || !isCollidable(requiredOnBits) || !toCheck.isCollidable(requiredOnBits)) // { // return false; // } // thisCT.getBounds().transform(worldRotation, worldTranslation, // worldScale, thisCT.getWorldBounds()); // return thisCT.intersect(checkCT); return false; } /** * This function finds all intersections between this trimesh and the * checking one. The intersections are stored as Integer objects of Triangle * indexes in each of the parameters. * * @param toCheck * The TriMesh to check. * @param thisIndex * The array of triangle indexes intersecting in this mesh. * @param otherIndex * The array of triangle indexes intersecting in the given mesh. */ public void findTriangleCollision(TriMesh toCheck, ArrayList thisIndex, ArrayList otherIndex) { // CollisionTree myTree = CollisionTreeManager.getInstance().getCollisionTree(this); // CollisionTree otherTree = CollisionTreeManager.getInstance().getCollisionTree(toCheck); // // if (myTree == null || otherTree == null) // { // return; // } // // myTree.getBounds().transform(worldRotation, worldTranslation, // worldScale, myTree.getWorldBounds()); // myTree.intersect(otherTree, thisIndex, otherIndex); } /** * Stores in the storage array the indices of triangle * i. If i is an invalid index, or if * storage.length<3, then nothing happens * * @param i * The index of the triangle to get. * @param storage * The array that will hold the i's indexes. */ public void getTriangle(int i, int[] storage) { if (i < getTriangleCount() && storage.length >= 3) { IntBuffer indices = getIndexBuffer(); storage[0] = indices.get(getVertIndex(i, 0)); storage[1] = indices.get(getVertIndex(i, 1)); storage[2] = indices.get(getVertIndex(i, 2)); } } /** * Stores in the vertices array the vertex values of triangle * i. If i is an invalid triangle index, * nothing happens. * * @param i * @param vertices */ public void getTriangle(int i, Vector3f[] vertices) { if (vertices == null) { vertices = new Vector3f[3]; } if (i < getTriangleCount() && i >= 0) { for (int x = 0; x < 3; x++) { if (vertices[x] == null) { vertices[x] = new Vector3f(); } //BufferUtils.populateFromBuffer(vertices[x], getVertexBuffer(), getIndexBuffer().get(getVertIndex(i, x))); getVertexBuffer().get(getIndexBuffer().get(getVertIndex(i, x)), vertices[x]); } } } /** * findTrianglePick determines the triangles of this trimesh * that are being touched by the ray. The indices of the triangles are * stored in the provided ArrayList. * * @param toTest * the ray to test. The direction of the ray must be normalized * (length 1). * @param results * the indices to the triangles. */ public void findTrianglePick(Ray toTest, ArrayList results) { // if (worldBound == null || !isCollidable()) // { // return; // } // // if (worldBound.intersects(toTest)) // { // CollisionTree ct = CollisionTreeManager.getInstance().getCollisionTree(this); // if (ct != null) // { // ct.getBounds().transform(getWorldRotation(), // getWorldTranslation(), getWorldScale(), // ct.getWorldBounds()); // ct.intersect(toTest, results); // } // } } /** * Return this mesh object as triangles. Every 3 vertices returned compose a * single triangle. * * @param verts * a storage array to place the results in * @return view of current mesh as group of triangle vertices */ public Vector3f[] getMeshAsTrianglesVertices(Vector3f[] verts) { int maxCount = getTriangleCount() * 3; if (verts == null || verts.length != maxCount) { verts = new Vector3f[maxCount]; } getIndexBuffer().rewind(); for (int i = 0; i < maxCount; i++) { if (verts[i] == null) { verts[i] = new Vector3f(); } int index = getVertIndex(i / 3, i % 3); //BufferUtils.populateFromBuffer(verts[i], getVertexBuffer(), getIndexBuffer().get(index)); getVertexBuffer().get(getIndexBuffer().get(index), verts[i]); } return verts; } protected int getVertIndex(int triangle, int point) { int index = 0, i = (triangle * 3) + point; switch (mode) { case Triangles: index = i; break; case Strip: index = (i / 3) + (i % 3); break; case Fan: if (i % 3 == 0) { index = 0; } else { index = (i % 3); index = ((i - index) / 3) + index; } break; } return index; } public int[] getTriangleIndices(int[] indices) { int maxCount = getTriangleCount(); if (indices == null || indices.length != maxCount) { indices = new int[maxCount]; } for (int i = 0, tLength = maxCount; i < tLength; i++) { indices[i] = i; } return indices; } public Triangle[] getMeshAsTriangles(Triangle[] tris) { int maxCount = getTriangleCount(); if (tris == null || tris.length != maxCount) { tris = new Triangle[maxCount]; } for (int i = 0, tLength = maxCount; i < tLength; i++) { Vector3f vec1 = new Vector3f(); Vector3f vec2 = new Vector3f(); Vector3f vec3 = new Vector3f(); Triangle t = tris[i]; if (t == null) { t = new Triangle(getVector(i * 3 + 0, vec1), getVector( i * 3 + 1, vec2), getVector(i * 3 + 2, vec3)); tris[i] = t; } else { t.set(0, getVector(i * 3 + 0, vec1)); t.set(1, getVector(i * 3 + 1, vec2)); t.set(2, getVector(i * 3 + 2, vec3)); } // t.calculateCenter(); t.setIndex(i); } return tris; } private Vector3f getVector(int index, Vector3f store) { int vertIndex = getVertIndex(index / 3, index % 3); //BufferUtils.populateFromBuffer(store, getVertexBuffer(), getIndexBuffer().get(vertIndex)); getVertexBuffer().get(getIndexBuffer().get(vertIndex), store); return store; } public int getMaxIndex() { if (indexBuffer == null) { return -1; } switch (mode) { case Triangles: return triangleQuantity * 3; case Strip: case Fan: triangleQuantity = indexBuffer.limit() - 2; return triangleQuantity + 2; } return -1; } /** * Returns a random point on the surface of a randomly selected triangle on * the mesh * * @param fill * The resulting selected point * @param work * Used in calculations to minimize memory creation overhead * @return The resulting selected point */ public Vector3f randomPointOnTriangles(Vector3f fill, Vector3f work) { if (getVertexBuffer() == null || getIndexBuffer() == null) { return null; } int tri = (int) (FastMath.nextRandomFloat() * getTriangleCount()); int pntA = getIndexBuffer().get(getVertIndex(tri, 0)); int pntB = getIndexBuffer().get(getVertIndex(tri, 1)); int pntC = getIndexBuffer().get(getVertIndex(tri, 2)); float b = FastMath.nextRandomFloat(); float c = FastMath.nextRandomFloat(); if (b + c > 1) { b = 1 - b; c = 1 - c; } float a = 1 - b - c; if (fill == null) { fill = new Vector3f(); } //BufferUtils.populateFromBuffer(work, getVertexBuffer(), pntA); getVertexBuffer().get(pntA, work); work.multLocal(a); fill.set(work); //BufferUtils.populateFromBuffer(work, getVertexBuffer(), pntB); getVertexBuffer().get(pntB, work); work.multLocal(b); fill.addLocal(work); //BufferUtils.populateFromBuffer(work, getVertexBuffer(), pntC); getVertexBuffer().get(pntC, work); work.multLocal(c); fill.addLocal(work); // localToWorld(fill, fill); return fill; } /** * Used with Serialization. Do not call this directly. * * @param s * @throws IOException * @see java.io.Serializable */ private void writeObject(java.io.ObjectOutputStream s) throws IOException { s.defaultWriteObject(); if (getIndexBuffer() == null) { s.writeInt(0); } else { s.writeInt(getIndexBuffer().limit()); getIndexBuffer().rewind(); for (int x = 0, len = getIndexBuffer().limit(); x < len; x++) { s.writeInt(getIndexBuffer().get()); } } } /** * Used with Serialization. Do not call this directly. * * @param s * @throws IOException * @throws ClassNotFoundException * @see java.io.Serializable */ private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int len = s.readInt(); if (len == 0) { setIndexBuffer(null); } else { IntBuffer buf = BufferUtils.createIntBuffer(len); for (int x = 0; x < len; x++) { buf.put(s.readInt()); } setIndexBuffer(buf); } } // public void write(JMEExporter e) throws IOException // { // super.write(e); // OutputCapsule capsule = null; // e.getCapsule(this); // capsule.write(indexBuffer, "indexBuffer", null); // //-- capsule.write(mode, "mode", Mode.Triangles); // } // // public void read(JMEImporter e) throws IOException // { // super.read(e); // InputCapsule capsule = null; // e.getCapsule(this); // indexBuffer = capsule.readIntBuffer("indexBuffer", null); // recalcTriangleQuantity(); // //-- mode = (Mode) capsule.readEnum("mode", Mode.class, Mode.Triangles); // } }