/*
* 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);
// }
}