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