/*
* Copyright (c) 2010 Stephen A. Pratt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.critterai.nmgen;
/**
* Represents a set of related convex polygons within a bounded field.
* <p>
* The polygons are usually connected (share edges), but are expected to not
* intersect.
* </p>
* <p>
* The data within this class is unprotected. So care must be taken.
* </p>
* <p>
* <a href=
* "http://www.critterai.org/projects/nmgen/images/stage_polygon_mesh.png"
* target="_parent"> <img class="insert" height="465" src=
* "http://www.critterai.org/projects/nmgen/images/stage_polygon_mesh.jpg"
* width="620" /> </a>
* </p>
*
* @see PolyMeshFieldBuilder
*/
public final class PolyMeshField extends BoundedField {
/*
* Recast Reference: rcPolyMesh in Recast.h
*/
/**
* Represents an index value for a null (non-existent) vertex.
* <p>
* This is required since polygons held within {@link #polys} are variable
* in size. So all entries for polygons within {@link #polys} that have
* fewer vertices than {@link #maxVertsPerPoly()} are padded with this
* value. See {@link #polys} for more information.
* </p>
*/
public static final int NULL_INDEX = -1;
/**
* The maximum vertices per polygon held within the {@link #polys} array.
* ({@link #maxVertsPerPoly()} * 2) represents the stride of the array.
* <p>
* This value is informational only. There is no enforcement.
*/
private final int mMaxVertsPerPoly;
/**
* The vertices that make up the field in the form (x, y, z). Value
* defaults to null.
*/
public int[] verts = null;
/**
* Holds flattened polygon index and neighbor information. Value
* defaults to null.
* <p>
* Where mvpp = {@link #maxVertsPerPoly()}:
* </p>
* <ul>
* <li>Each polygon entry is 2*mvpp.</li>
* <li>The first mvpp of each entry contains the indices of the polygon. The
* first instance of {@link #NULL_INDEX} means the end of polygon indices
* for the entry.</li>
* <li>The second mvpp of each entry contains indices to neighbor polygons.
* A value of NULL_INDEX indicates no connection for that particular edge.</li>
* </ul>
* <p>
* Example:
* </p>
* <p>
* If mvpp = 6, the polygon has 4 vertices and 2 neighbor connections.<br/>
* Then for (1, 3, 4, 8, NULL_INDEX, NULL_INDEX, 18, NULL_INDEX, 21,
* NULL_INDEX, NULL_INDEX, NULL_INDEX)<br/>
* (1, 3, 4, 8) defines the polygon.<br/>
* Polygon 18 shares edge 1->3.<br/>
* Polygon 21 shares edge 4->8.<br/>
* Edges 3->4 and 8->1 are border edges. (Not shared with any other
* polygon.)<br/>
* </p>
*/
public int[] polys = null;
/**
* Holds membership region data for each polygon in the form (regionID).
* Value defaults to null.
*/
public int[] polyRegions = null;
/**
* Consructor
*
* @param gridBoundsMin
* The minimum bounds of the field in the form
* (minX, minY, minZ).
* @param gridBoundsMax
* The maximum bounds of the field in the form
* (maxX, maxY, maxZ).
* @param cellSize
* The size of the cells. (The grid that forms the base
* of the field.)
* @param cellHeight
* The height increment of the field.
* @param maxVertsPerPoly
* The maximum vertices per polygon. Value will
* be auto-clamped to >=3.
* @throws IllegalArgumentException
* If the bounds are null or the
* wrong size.
*/
public PolyMeshField(final float[] gridBoundsMin, final float[] gridBoundsMax,
final float cellSize, final float cellHeight, final int maxVertsPerPoly)
throws IllegalArgumentException {
super(gridBoundsMin, gridBoundsMax, cellSize, cellHeight);
this.mMaxVertsPerPoly = Math.max(maxVertsPerPoly, 3);
}
/**
* Gets the region ID of the polygon.
*
* @param polyIndex
* The index of the polygon.
* @return The region ID of the polygon, or -1 if the polygon index
* is invalid.
*/
public int getPolyRegion(final int polyIndex) {
if ((polyIndex < 0) || (polyIndex >= this.polyRegions.length)) {
return -1;
}
return this.polyRegions[polyIndex];
}
/**
* Gets an array containing the vertices of the polygon.
* <p>
* This is a costly convenience operation.
* </p>
*
* @param polyIndex
* The index of the polygon.
* @return An array containing the vertices of the polygon.
*/
public int[] getPolyVerts(final int polyIndex) {
final int pPoly = polyIndex * this.mMaxVertsPerPoly * 2;
if ((polyIndex < 0) || (pPoly >= this.polys.length)) {
return null;
}
// Determine the vertex count for this polygon.
final int polyVertCount = getPolyVertCount(pPoly, this.polys, this.mMaxVertsPerPoly);
final int[] result = new int[polyVertCount * 3];
// Get the vertices.
for (int i = 0; i < polyVertCount; i++) {
final int pVert = this.polys[pPoly + i] * 3;
result[i * 3] = this.verts[pVert];
result[(i * 3) + 1] = this.verts[pVert + 1];
result[(i * 3) + 2] = this.verts[pVert + 2];
}
return result;
}
/**
* The maximum vertices per polygon held within the {@link #polys} array.
* ({@link #maxVertsPerPoly()} * 2) represents the stride of the array.
* <p>
* This value is informational only. There is no enforcement.
*
* @return The maximum vertices per polygon held within the {@link #polys}
* array.
*/
public int maxVertsPerPoly() {
return this.mMaxVertsPerPoly;
}
/**
* The number of polygons in the {@link #polys} array.
*
* @return The number of vertices in the {@link #verts} array.
*/
public int polyCount() {
if (this.polys == null) {
return 0;
}
return this.polys.length / (2 * this.mMaxVertsPerPoly);
}
/**
* The number of vertices in the {@link #verts} array.
*
* @return The number of vertices in the {@link #verts} array.
*/
public int vertCount() {
if (this.verts == null) {
return 0;
}
return this.verts.length / 3;
}
/**
* Returns the vertex count for the specified polygon in the polygon array.
* <p>
* The array is assumed to to be well formed (correct size and layout).
* Otherwise behavior is undefined.
* </p>
* <p>
* Basically, this operation starts counting array entries starting at the
* pointer. It continues to count until it runs into a NULL_INDEX value or
* reaches maxVertsPerPoly, whichever occurs first.
* </p>
*
* @param polyPointer
* A pointer of the start of the polygon.
* @param polys
* An array of polygons in the standard format.
* (E.g. NULL_INDEX indicates end of the polygon and each entry is
* maxVertsPerPoly long.)
* @param maxVertsPerPoly
* The maximum number of vertices for a polygon
* in the array.
* @return The number of vertices for the specified polygon.
*/
static int getPolyVertCount(final int polyPointer, final int[] polys, final int maxVertsPerPoly) {
for (int i = 0; i < maxVertsPerPoly; i++) {
if (polys[polyPointer + i] == NULL_INDEX) {
// Ran into a null index. No more vertices for this polygon.
return i;
}
}
return maxVertsPerPoly;
}
}