001/*****************************************************************************
002 *                          J3D.org Copyright (c) 2000
003 *                                Java Source
004 *
005 * This source is licensed under the GNU LGPL v2.1
006 * Please read http://www.gnu.org/copyleft/lgpl.html for more information
007 *
008 ****************************************************************************/
009
010package jahuwaldt.j3d.geom;
011
012// Standard imports
013import org.jogamp.vecmath.Vector3f;
014
015// Application specific imports
016// none
017
018/**
019 * Abstract base representation of geometry generator of box raw coordinate
020 * and geometry normals.
021 * <p>
022 *
023 * Curved surfaces would like to generate a smooth object most of the time.
024 * To do this, the normal values at each vertex are made to smooth the values
025 * for each set of faces that use that value (ie the effect is averaged between
026 * all the sharing faces). The typical approach to do this is to work with a
027 * value called creaseAngle. If the angle between two surfaces is less that
028 * the creaseAngle, a smoothed normal is generated. If greater, the normal is
029 * perpendicular to the face. If we are playing with different numbers of
030 * facets in an object, this gets rather annoying at times as some pieces may
031 * or may not be faceted. At the same time there is a performance hit for
032 * generating the normals as you have to check every face and build a lot of
033 * extra data before you start doing normal calculations.
034 * <p>
035 *
036 * This library takes a much simplified approach - let the geometry generator
037 * implementation decide. Our aim is for speed here and if we have to decide
038 * in a general fashion for every normal calculation, that could be a huge
039 * impact.
040 * <P>
041 *
042 * Obvious limitations to this are shapes like the cube or a near-degenerate
043 * cone that ends up as a pyramid. The smoothing of normals may be there, but
044 * no matter how hard you try, the differences between the face angles will
045 * just be too great.
046 *
047 * @author Justin Couch
048 * @version $Revision: 1.6-JAH1 $
049 */
050public abstract class GeometryGenerator
051{
052    /** Working values for the normal generation */
053    private final Vector3f normal;
054    private final Vector3f v0;
055    private final Vector3f v1;
056
057    protected GeometryGenerator()
058    {
059        v0 = new Vector3f();
060        v1 = new Vector3f();
061        normal = new Vector3f();
062    }
063
064    /**
065     * Get the number of vertices that this generator will create for the
066     * shape given in the definition.
067     *
068     * @param data The data to base the calculations on
069     * @return The vertex count for the object
070     * @throws UnsupportedTypeException The generator cannot handle the type
071     *   of geometry you have requested.
072     */
073    public abstract int getVertexCount(GeometryData data)
074        throws UnsupportedTypeException;
075
076    /**
077     * Generate a new set of geometry items based on the passed data. If the
078     * data does not contain the right minimum array lengths an exception will
079     * be generated. If the array reference is null, this will create arrays
080     * of the correct length and assign them to the return value.
081     *
082     * @param data The data to base the calculations on
083     * @throws InvalidArraySizeException The array is not big enough to contain
084     *   the requested geometry
085     * @throws UnsupportedTypeException The generator cannot handle the type
086     *   of geometry you have requested
087     */
088    public abstract void generate(GeometryData data)
089        throws UnsupportedTypeException, InvalidArraySizeException;
090
091    /**
092     * Convenience method to create a normal for the given vertex coordinates
093     * and normal array. This performs a cross product of the two vectors
094     * described by the middle and two end points.
095     *
096     * @param coords The coordinate array to read values from
097     * @param p The index of the middle point
098     * @param p1 The index of the first point
099     * @param p2 The index of the second point
100     * @return A temporary value containing the normal value
101     */
102    protected Vector3f createFaceNormal(float[] coords, int p, int p1, int p2)
103    {
104        v0.x = coords[p1]     - coords[p];
105        v0.y = coords[p1 + 1] - coords[p + 1];
106        v0.z = coords[p1 + 2] - coords[p + 2];
107
108        v1.x = coords[p]     - coords[p2];
109        v1.y = coords[p + 1] - coords[p2 + 1];
110        v1.z = coords[p + 2] - coords[p2 + 2];
111
112        normal.cross(v0, v1);
113        normal.normalize();
114
115        return normal;
116    }
117
118
119    /**
120     * Convenience method to create a normal for the given vertex coordinates
121     * and normal array and using a 2D array of coordinate values. This
122     * performs a cross product of the two vectors described by the middle
123     * and two end points.
124     *
125     * @param coords The coordinate array to read values from
126     * @param w The reference into the first array dimension for p
127     * @param p The index of the middle point
128     * @param w1 The reference into the first array dimension for p1
129     * @param p1 The index of the first point
130     * @param w2 The reference into the first array dimension for p2
131     * @param p2 The index of the second point
132     * @return A temporary value containing the normal value
133     */
134    protected Vector3f createFaceNormal(float[][] coords,
135                                        int w, int p,
136                                        int w1, int p1,
137                                        int w2, int p2)
138    {
139        v0.x = coords[w1][p1]     - coords[w][p];
140        v0.y = coords[w1][p1 + 1] - coords[w][p + 1];
141        v0.z = coords[w1][p1 + 2] - coords[w][p + 2];
142
143        v1.x = coords[w][p]     - coords[w2][p2];
144        v1.y = coords[w][p + 1] - coords[w2][p2 + 1];
145        v1.z = coords[w][p + 2] - coords[w2][p2 + 2];
146
147        normal.cross(v0, v1);
148        normal.normalize();
149
150        return normal;
151    }
152
153    /**
154     * Create a normal based on the given vertex position, assuming that it is
155     * a point in space, relative to the origin. This will create a normal that
156     * points directly along the vector from the origin to the point.
157     *
158     * @param coords The coordinate array to read values from
159     * @param p The index of the point to calculate
160     * @return A temporary value containing the normal value
161     */
162    protected Vector3f createRadialNormal(float[] coords, int p)
163    {
164        float x = coords[p];
165        float y = coords[p + 1];
166        float z = coords[p + 2];
167
168        float mag = x * x + y * y + z * z;
169
170        if(mag != 0.0)
171        {
172            mag = 1.0f / ((float) Math.sqrt(mag));
173            normal.x = x * mag;
174            normal.y = y * mag;
175            normal.z = z * mag;
176        }
177        else
178        {
179            normal.x = 0;
180            normal.y = 0;
181            normal.z = 0;
182        }
183
184        return normal;
185    }
186
187    /**
188     * Create a normal based on the given vertex position, assuming that it is
189     * a point in space, relative to the given point. This will create a normal
190     * that points directly along the vector from the given point to the
191     * coordinate.
192     *
193     * @param coords The coordinate array to read values from
194     * @param p The index of the point to calculate
195     * @param origin The origin to calculate relative to
196     * @param originOffset The offset into the origin array to use
197     * @return A temporary value containing the normal value
198     */
199    protected Vector3f createRadialNormal(float[] coords,
200                                          int p,
201                                          float[] origin,
202                                          int originOffset)
203    {
204        float x = coords[p] - origin[originOffset];
205        float y = coords[p + 1] - origin[originOffset + 1];
206        float z = coords[p + 2] - origin[originOffset + 2];
207
208        float mag = x * x + y * y + z * z;
209
210        if(mag != 0.0)
211        {
212            mag = 1.0f / ((float) Math.sqrt(mag));
213            normal.x = x * mag;
214            normal.y = y * mag;
215            normal.z = z * mag;
216        }
217        else
218        {
219            normal.x = 0;
220            normal.y = 0;
221            normal.z = 0;
222        }
223
224        return normal;
225    }
226}