2 * Copyright (c) 2003-2009 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 package com.jme.scene;
35 import java.io.IOException;
36 import java.io.Serializable;
37 import java.nio.FloatBuffer;
38 import java.nio.IntBuffer;
39 import java.util.ArrayList;
40 import java.util.logging.Logger;
42 import com.jme.bounding.CollisionTree;
43 import com.jme.bounding.CollisionTreeManager;
44 import com.jme.intersection.CollisionResults;
45 import com.jme.math.FastMath;
46 import com.jme.math.Ray;
47 import com.jme.math.Triangle;
48 import com.jme.math.Vector3f;
49 import com.jme.renderer.Renderer;
50 import com.jme.system.JmeException;
51 import com.jme.util.export.InputCapsule;
52 import com.jme.util.export.JMEExporter;
53 import com.jme.util.export.JMEImporter;
54 import com.jme.util.export.OutputCapsule;
55 import com.jme.util.geom.BufferUtils;
58 * <code>TriMesh</code> defines a geometry mesh. This mesh defines a three
59 * dimensional object via a collection of points, colors, normals and textures.
60 * The points are referenced via a indices array. This array instructs the
61 * renderer the order in which to draw the points, creating triangles based on the mode set.
64 * @author Joshua Slack
65 * @version $Id: TriMesh.java,v 1.69 2007/08/02 21:54:36 nca Exp $
67 public class TriMesh extends Geometry implements Serializable {
68 private static final Logger logger = Logger.getLogger(TriMesh.class
71 private static final long serialVersionUID = 2L;
75 * Every three vertices referenced by the indexbuffer will be considered
76 * a stand-alone triangle.
80 * The first three vertices referenced by the indexbuffer create a
81 * triangle, from there, every additional vertex is paired with the two
82 * preceding vertices to make a new triangle.
86 * The first three vertices (V0, V1, V2) referenced by the indexbuffer
87 * create a triangle, from there, every additional vertex is paired with
88 * the preceding vertex and the initial vertex (V0) to make a new
94 protected transient IntBuffer indexBuffer;
95 protected Mode mode = Mode.Triangles;
96 protected int triangleQuantity;
99 * Empty Constructor to be used internally only.
106 * Constructor instantiates a new <code>TriMesh</code> object.
109 * the name of the scene element. This is required for
110 * identification and comparision purposes.
112 public TriMesh(String name) {
117 * Constructor instantiates a new <code>TriMesh</code> object. Provided
118 * are the attributes that make up the mesh all attributes may be null,
119 * except for vertices and indices.
122 * the name of the scene element. This is required for
123 * identification and comparision purposes.
125 * the vertices of the geometry.
127 * the normals of the geometry.
129 * the colors of the geometry.
131 * the texture coordinates of the mesh.
133 * the indices of the vertex array.
135 public TriMesh(String name, FloatBuffer vertices, FloatBuffer normal,
136 FloatBuffer color, TexCoords coords, IntBuffer indices) {
140 reconstruct(vertices, normal, color, coords);
142 if (null == indices) {
143 logger.severe("Indices may not be null.");
144 throw new JmeException("Indices may not be null.");
146 setIndexBuffer(indices);
147 logger.info("TriMesh created.");
151 * Recreates the geometric information of this TriMesh from scratch. The
152 * index and vertex array must not be null, but the others may be. Every 3
153 * indices define an index in the <code>vertices</code> array that
154 * refrences a vertex of a triangle.
157 * The vertex information for this TriMesh.
159 * The normal information for this TriMesh.
161 * The color information for this TriMesh.
163 * The texture information for this TriMesh.
165 * The index information for this TriMesh.
167 public void reconstruct(FloatBuffer vertices, FloatBuffer normal,
168 FloatBuffer color, TexCoords coords, IntBuffer indices) {
169 super.reconstruct(vertices, normal, color, coords);
171 if (null == indices) {
172 logger.severe("Indices may not be null.");
173 throw new JmeException("Indices may not be null.");
175 setIndexBuffer(indices);
178 public void setMode(Mode mode) {
182 public Mode getMode() {
186 public IntBuffer getIndexBuffer() {
190 public void setIndexBuffer(IntBuffer indices) {
191 this.indexBuffer = indices;
192 recalcTriangleQuantity();
195 protected void recalcTriangleQuantity() {
196 if (indexBuffer == null) {
197 triangleQuantity = 0;
203 triangleQuantity = indexBuffer.limit() / 3;
207 triangleQuantity = indexBuffer.limit() - 2;
213 * Returns the number of triangles contained in this mesh.
216 public int getTriangleCount() {
217 return triangleQuantity;
220 public void setTriangleQuantity(int triangleQuantity) {
221 this.triangleQuantity = triangleQuantity;
225 * <code>draw</code> calls super to set the render state then passes
226 * itself to the renderer. LOGIC: 1. If we're not RenderQueue calling draw
227 * goto 2, if we are, goto 3 2. If we are supposed to use queue, add to
228 * queue and RETURN, else 3 3. call super draw 4. tell renderer to draw me.
231 * the renderer to display
233 public void draw(Renderer r) {
234 if (!r.isProcessingQueue()) {
235 if (r.checkAndAdd(this))
244 * Clears the buffers of this TriMesh. The buffers include its indexBuffer
247 public void clearBuffers() {
248 super.clearBuffers();
249 setIndexBuffer(null);
253 * determines if a collision between this trimesh and a given spatial occurs
254 * if it has true is returned, otherwise false is returned.
256 public boolean hasCollision(Spatial scene, boolean checkTriangles) {
257 if (this == scene || !isCollidable || !scene.isCollidable()) {
260 if (getWorldBound().intersects(scene.getWorldBound())) {
261 if (scene instanceof Node) {
262 Node parent = (Node) scene;
263 for (int i = 0; i < parent.getQuantity(); i++) {
264 if (hasCollision(parent.getChild(i), checkTriangles)) {
272 if (!checkTriangles) {
276 return hasTriangleCollision((TriMesh) scene);
283 * determines if this TriMesh has made contact with the give scene. The
284 * scene is recursively transversed until a trimesh is found, at which time
285 * the two trimesh OBBTrees are then compared to find the triangles that
288 public void findCollisions(Spatial scene, CollisionResults results) {
289 if (this == scene || !isCollidable || !scene.isCollidable()) {
293 if (getWorldBound().intersects(scene.getWorldBound())) {
294 if (scene instanceof Node) {
295 Node parent = (Node) scene;
296 for (int i = 0; i < parent.getQuantity(); i++) {
297 findCollisions(parent.getChild(i), results);
300 results.addCollision(this, (Geometry) scene);
306 * This function checks for intersection between this trimesh and the given
307 * one. On the first intersection, true is returned.
310 * The intersection testing mesh.
311 * @return True if they intersect.
313 public boolean hasTriangleCollision(TriMesh toCheck) {
314 CollisionTree thisCT = CollisionTreeManager.getInstance()
315 .getCollisionTree(this);
316 CollisionTree checkCT = CollisionTreeManager.getInstance()
317 .getCollisionTree(toCheck);
319 if (thisCT == null || checkCT == null || !isCollidable
320 || !toCheck.isCollidable()) {
323 thisCT.getBounds().transform(worldRotation, worldTranslation,
324 worldScale, thisCT.getWorldBounds());
325 return thisCT.intersect(checkCT);
329 * This function finds all intersections between this trimesh and the
330 * checking one. The intersections are stored as Integer objects of Triangle
331 * indexes in each of the parameters.
334 * The TriMesh to check.
336 * The array of triangle indexes intersecting in this mesh.
338 * The array of triangle indexes intersecting in the given mesh.
340 public void findTriangleCollision(TriMesh toCheck,
341 ArrayList<Integer> thisIndex, ArrayList<Integer> otherIndex) {
343 CollisionTree myTree = CollisionTreeManager.getInstance()
344 .getCollisionTree(this);
345 CollisionTree otherTree = CollisionTreeManager.getInstance()
346 .getCollisionTree(toCheck);
348 if (myTree == null || otherTree == null) {
352 myTree.getBounds().transform(worldRotation, worldTranslation,
353 worldScale, myTree.getWorldBounds());
354 myTree.intersect(otherTree, thisIndex, otherIndex);
358 * Stores in the <code>storage</code> array the indices of triangle
359 * <code>i</code>. If <code>i</code> is an invalid index, or if
360 * <code>storage.length<3</code>, then nothing happens
363 * The index of the triangle to get.
365 * The array that will hold the i's indexes.
367 public void getTriangle(int i, int[] storage) {
368 if (i < getTriangleCount() && storage.length >= 3) {
369 IntBuffer indices = getIndexBuffer();
370 storage[0] = indices.get(getVertIndex(i, 0));
371 storage[1] = indices.get(getVertIndex(i, 1));
372 storage[2] = indices.get(getVertIndex(i, 2));
377 * Stores in the <code>vertices</code> array the vertex values of triangle
378 * <code>i</code>. If <code>i</code> is an invalid triangle index,
384 public void getTriangle(int i, Vector3f[] vertices) {
385 if (vertices == null) {
386 vertices = new Vector3f[3];
388 if (i < getTriangleCount() && i >= 0) {
389 for (int x = 0; x < 3; x++) {
390 if (vertices[x] == null) {
391 vertices[x] = new Vector3f();
394 BufferUtils.populateFromBuffer(vertices[x], getVertexBuffer(),
395 getIndexBuffer().get(getVertIndex(i, x)));
401 * <code>findTrianglePick</code> determines the triangles of this trimesh
402 * that are being touched by the ray. The indices of the triangles are
403 * stored in the provided ArrayList.
406 * the ray to test. The direction of the ray must be normalized
409 * the indices to the triangles.
411 public void findTrianglePick(Ray toTest, ArrayList<Integer> results) {
412 if (worldBound == null || !isCollidable) {
416 if (worldBound.intersects(toTest)) {
417 CollisionTree ct = CollisionTreeManager.getInstance()
418 .getCollisionTree(this);
420 ct.getBounds().transform(getWorldRotation(),
421 getWorldTranslation(), getWorldScale(),
422 ct.getWorldBounds());
423 ct.intersect(toTest, results);
429 * Return this mesh object as triangles. Every 3 vertices returned compose a
433 * a storage array to place the results in
434 * @return view of current mesh as group of triangle vertices
436 public Vector3f[] getMeshAsTrianglesVertices(Vector3f[] verts) {
437 int maxCount = getTriangleCount() * 3;
438 if (verts == null || verts.length != maxCount)
439 verts = new Vector3f[maxCount];
440 getIndexBuffer().rewind();
441 for (int i = 0; i < maxCount; i++) {
442 if (verts[i] == null)
443 verts[i] = new Vector3f();
444 int index = getVertIndex(i / 3, i % 3);
445 BufferUtils.populateFromBuffer(verts[i], getVertexBuffer(),
446 getIndexBuffer().get(index));
451 protected int getVertIndex(int triangle, int point) {
452 int index = 0, i = (triangle * 3) + point;
458 index = (i / 3) + (i % 3);
465 index = ((i - index) / 3) + index;
472 public int[] getTriangleIndices(int[] indices) {
473 int maxCount = getTriangleCount();
474 if (indices == null || indices.length != maxCount)
475 indices = new int[maxCount];
477 for (int i = 0, tLength = maxCount; i < tLength; i++) {
483 public Triangle[] getMeshAsTriangles(Triangle[] tris) {
484 int maxCount = getTriangleCount();
485 if (tris == null || tris.length != maxCount)
486 tris = new Triangle[maxCount];
488 for (int i = 0, tLength = maxCount; i < tLength; i++) {
489 Vector3f vec1 = new Vector3f();
490 Vector3f vec2 = new Vector3f();
491 Vector3f vec3 = new Vector3f();
493 Triangle t = tris[i];
495 t = new Triangle(getVector(i * 3 + 0, vec1), getVector(
496 i * 3 + 1, vec2), getVector(i * 3 + 2, vec3));
499 t.set(0, getVector(i * 3 + 0, vec1));
500 t.set(1, getVector(i * 3 + 1, vec2));
501 t.set(2, getVector(i * 3 + 2, vec3));
503 // t.calculateCenter();
509 private Vector3f getVector(int index, Vector3f store) {
510 int vertIndex = getVertIndex(index / 3, index % 3);
511 BufferUtils.populateFromBuffer(store, getVertexBuffer(),
512 getIndexBuffer().get(vertIndex));
516 public int getMaxIndex() {
517 if (indexBuffer == null)
522 return triangleQuantity * 3;
525 triangleQuantity = indexBuffer.limit() - 2;
526 return triangleQuantity + 2;
532 * Returns a random point on the surface of a randomly selected triangle on
536 * The resulting selected point
538 * Used in calculations to minimize memory creation overhead
539 * @return The resulting selected point
541 public Vector3f randomPointOnTriangles(Vector3f fill, Vector3f work) {
542 if (getVertexBuffer() == null || getIndexBuffer() == null)
544 int tri = (int) (FastMath.nextRandomFloat() * getTriangleCount());
545 int pntA = getIndexBuffer().get(getVertIndex(tri, 0));
546 int pntB = getIndexBuffer().get(getVertIndex(tri, 1));
547 int pntC = getIndexBuffer().get(getVertIndex(tri, 2));
549 float b = FastMath.nextRandomFloat();
550 float c = FastMath.nextRandomFloat();
560 fill = new Vector3f();
562 BufferUtils.populateFromBuffer(work, getVertexBuffer(), pntA);
566 BufferUtils.populateFromBuffer(work, getVertexBuffer(), pntB);
570 BufferUtils.populateFromBuffer(work, getVertexBuffer(), pntC);
574 localToWorld(fill, fill);
580 * Used with Serialization. Do not call this directly.
583 * @throws IOException
584 * @see java.io.Serializable
586 private void writeObject(java.io.ObjectOutputStream s) throws IOException {
587 s.defaultWriteObject();
588 if (getIndexBuffer() == null)
591 s.writeInt(getIndexBuffer().limit());
592 getIndexBuffer().rewind();
593 for (int x = 0, len = getIndexBuffer().limit(); x < len; x++)
594 s.writeInt(getIndexBuffer().get());
599 * Used with Serialization. Do not call this directly.
602 * @throws IOException
603 * @throws ClassNotFoundException
604 * @see java.io.Serializable
606 private void readObject(java.io.ObjectInputStream s) throws IOException,
607 ClassNotFoundException {
608 s.defaultReadObject();
609 int len = s.readInt();
611 setIndexBuffer(null);
613 IntBuffer buf = BufferUtils.createIntBuffer(len);
614 for (int x = 0; x < len; x++)
615 buf.put(s.readInt());
620 public void write(JMEExporter e) throws IOException {
622 OutputCapsule capsule = e.getCapsule(this);
623 capsule.write(indexBuffer, "indexBuffer", null);
624 capsule.write(mode, "mode", Mode.Triangles);
627 public void read(JMEImporter e) throws IOException {
629 InputCapsule capsule = e.getCapsule(this);
630 indexBuffer = capsule.readIntBuffer("indexBuffer", null);
631 recalcTriangleQuantity();
632 mode = (Mode) capsule.readEnum("mode", Mode.class, Mode.Triangles);