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 * Generator of Cone raw coordinates and geometry normals.
020 * <p>
021 *
022 * The generator is used to create cone shapes for the code. It only generates
023 * the geometry array. Internally the representation uses a triangle array
024 * and generates all the values requested - textures oordinates, normals and
025 * coordinates. Normals are always generated per vertex calculated rather than
026 * per-face.
027 * <p>
028 *
029 * The height of the cone is along the Y axis with the point in the positive
030 * Y direstion and starting at the origin. The radius is around the X-Z plane
031 * and is centered on the origin.
032 * <p>
033 *
034 * When generating strips, we create a strip for all the top and a strip for
035 * all the bottom (if used). Generating quads are a bit useless for this
036 * geometry type, but is supported non-the-less for completeness.
037 *
038 * @author Justin Couch
039 * @version $Revision: 1.7 $
040 */
041public class ConeGenerator extends GeometryGenerator
042{
043    /** Default number of segments used in the cone */
044    private static final int DEFAULT_FACETS = 16;
045
046    /** The height of the code */
047    private float coneHeight;
048
049    /** The radius of the bottom of the cone */
050    private float bottomRadius;
051
052    /** Flag to indicate if the geometry should create the base of the cone */
053    private boolean useBottom;
054
055    /** Flag to indicate if the geometry should create the top of the cone */
056    private boolean useTop;
057
058    /** The number of sections used around the cone */
059    private int facetCount;
060
061    /** The points on the base of the cone for each facet in [x, z] */
062    private float[] baseCoordinates;
063
064    /** The number of values used in the base coordinate array */
065    private int numBaseValues;
066
067    /**
068     * The 2D texture coordinates for the sphere. These match the order of
069     * vertex declaration in the quadCoordinates field thus making life
070     * easy for dealing with half spheres
071     */
072    private float[] texCoordinates2D;
073
074    /** The number of values used in the 2D tex coord array */
075    private int numTexCoords2D;
076
077    /**
078     * The 3D texture coordinates for the sphere. These match the order of
079     * vertex declaration in the quadCoordinates field thus making life
080     * easy for dealing with half spheres
081     */
082    private float[] texCoordinates3D;
083
084    /** The number of values used in the 2D tex coord array */
085    private int numTexCoords3D;
086
087    /** Flag indicating base values have changed */
088    private boolean baseChanged;
089
090    /** Flag to indicate the facet count or half settings have changed */
091    private boolean facetsChanged;
092
093    /**
094     * Construct a default cone of height 2 and bottom radius of 1.
095     * It also includes the bottom faces and uses 16 segments around the base
096     * of the cone.
097     */
098    public ConeGenerator()
099    {
100        this(2, 1, DEFAULT_FACETS, true, true);
101    }
102
103    /**
104     * Create a custom cone of the given height and radius that includes
105     * the bottom faces. There are 16 segments around the base of the cone.
106     *
107     * @param height The height of the cone to generate
108     * @param radius The radius of the bottom of the cone
109     */
110    public ConeGenerator(float height, float radius)
111    {
112        this(height, radius, DEFAULT_FACETS, true, true);
113    }
114
115    /**
116     * Create a custom cone of the given height and radius and can control
117     * the number of facets in the cone. The cone always has a base.
118     * The minimum number of facets is 3.
119     *
120     * @param height The height of the cone to generate
121     * @param radius The radius of the bottom of the cone
122     * @param facets The number of facets on the side of the cone
123     * @throws IllegalArgumentException The number of facets is less than 3
124     */
125    public ConeGenerator(float height, float radius, int facets)
126    {
127        this(height, radius, facets, true, true);
128    }
129
130    /**
131     * Create a custom cone of the given height and radius and can toggle the
132     * use of the bottom faces. There are 16 segments around the base of the
133     * cone.
134     *
135     * @param height The height of the cone to generate
136     * @param radius The radius of the bottom of the cone
137     * @param hasTop  True if to generate faces for the top
138     * @param hasBottom  True if to generate faces for the bottom
139     */
140    public ConeGenerator(float height, float radius, boolean hasTop, boolean hasBottom)
141    {
142        this(height, radius, DEFAULT_FACETS, hasTop, hasBottom);
143    }
144
145    /**
146     * Create a custom cone of the given height and radius and can toggle the
147     * use of the bottom faces and control the number of facets in the cone.
148     * The minimum number of facets is 3.
149     *
150     * @param height The height of the cone to generate
151     * @param radius The radius of the bottom of the cone
152     * @param facets The number of facets on the side of the cone
153     * @param hasTop  True if to generate faces for the top
154     * @param hasBottom  True if to generate faces for the bottom
155     * @throws IllegalArgumentException The number of facets is less than 3
156     *    or the radius is not positive
157     */
158    public ConeGenerator(float height,
159                         float radius,
160                         int facets,
161                         boolean hasTop,
162                         boolean hasBottom)
163    {
164        if(facets < 3)
165            throw new IllegalArgumentException("Number of facets is < 3");
166
167        if(radius <= 0)
168            throw new IllegalArgumentException("Radius is negative or zero");
169
170        facetCount = facets;
171        coneHeight = height;
172        bottomRadius = radius;
173        useBottom = hasBottom;
174        useTop = hasTop;
175        baseChanged = true;
176        facetsChanged = true;
177    }
178
179    /**
180     * Get the dimensions of the cone. These are returned as 2 values of
181     * height and radius respectively for the array. A new array is
182     * created each time so you can do what you like with it.
183     *
184     * @return The current size of the cone
185     */
186    public float[] getDimensions()
187    {
188        return new float[] { coneHeight, bottomRadius };
189    }
190
191    /**
192     * Check to see that this cone has a bottom in use or not
193     *
194     * @return true if there is a bottom in use
195     */
196    public boolean hasBottom()
197    {
198        return useBottom;
199    }
200
201    /**
202     * Change the dimensions of the cone to be generated. Calling this will
203     * make the points be re-calculated next time you ask for geometry or
204     * normals.
205     *
206     * @param height The height of the cone to generate
207     * @param radius The radius of the bottom of the cone
208     * @param hasBottom  True if to generate faces for the bottom
209     * @throws IllegalArgumentException The radius is not positive
210     */
211    public void setDimensions(float height, float radius, boolean hasBottom)
212    {
213        if(radius <= 0)
214            throw new IllegalArgumentException("Radius is negative or zero");
215
216        coneHeight = height;
217        bottomRadius = radius;
218
219        baseChanged = true;
220
221        if(useBottom != hasBottom)
222            facetsChanged = true;
223
224        useBottom = hasBottom;
225    }
226
227    /**
228     * Change the number of facets used to create this cone. This will cause
229     * the geometry to be regenerated next time they are asked for.
230     * The minimum number of facets is 3.
231     *
232     * @param facets The number of facets on the side of the cone
233     * @throws IllegalArgumentException The number of facets is less than 3
234     */
235    public void setFacetCount(int facets)
236    {
237        if(facets < 3)
238            throw new IllegalArgumentException("Number of facets is < 3");
239
240        if(facetCount != facets)
241        {
242            baseChanged = true;
243            facetsChanged = true;
244        }
245
246        facetCount = facets;
247    }
248
249    /**
250     * Get the number of vertices that this generator will create for the
251     * shape given in the definition.
252     *
253     * @param data The data to base the calculations on
254     * @return The vertex count for the object
255     * @throws UnsupportedTypeException The generator cannot handle the type
256     *   of geometry you have requested.
257     */
258    public int getVertexCount(GeometryData data)
259        throws UnsupportedTypeException
260    {
261        int ret_val = 0;
262        int bottom_mul = useBottom ? 2 : 1;
263
264        switch(data.geometryType)
265        {
266            case GeometryData.TRIANGLES:
267                ret_val = facetCount * 3 * bottom_mul;
268                break;
269            case GeometryData.QUADS:
270                ret_val = facetCount * 4 * bottom_mul;
271                break;
272
273            // These all have the same vertex count
274            case GeometryData.TRIANGLE_STRIPS:
275            case GeometryData.TRIANGLE_FANS:
276            case GeometryData.INDEXED_TRIANGLES:
277            case GeometryData.INDEXED_QUADS:
278            case GeometryData.INDEXED_TRIANGLE_STRIPS:
279            case GeometryData.INDEXED_TRIANGLE_FANS:
280                ret_val = (facetCount + 1) * 2;
281                if(useBottom)
282                    ret_val += facetCount + 2;
283                break;
284
285            default:
286                throw new UnsupportedTypeException("Unknown geometry type: " +
287                                                   data.geometryType);
288        }
289
290        return ret_val;
291    }
292
293    /**
294     * Generate a new set of geometry items based on the passed data. If the
295     * data does not contain the right minimum array lengths an exception will
296     * be generated. If the array reference is null, this will create arrays
297     * of the correct length and assign them to the return value.
298     *
299     * @param data The data to base the calculations on
300     * @throws InvalidArraySizeException The array is not big enough to contain
301     *   the requested geometry
302     * @throws UnsupportedTypeException The generator cannot handle the type
303     *   of geometry you have requested
304     */
305    public void generate(GeometryData data)
306        throws UnsupportedTypeException, InvalidArraySizeException
307    {
308        switch(data.geometryType)
309        {
310            case GeometryData.TRIANGLES:
311                unindexedTriangles(data);
312                break;
313//            case GeometryData.QUADS:
314//                unindexedQuads(data);
315//                break;
316//            case GeometryData.TRIANGLE_STRIPS:
317//                triangleStrips(data);
318//                break;
319//            case GeometryData.TRIANGLE_FANS:
320//                triangleFans(data);
321//                break;
322            case GeometryData.INDEXED_QUADS:
323                indexedQuads(data);
324                break;
325            case GeometryData.INDEXED_TRIANGLES:
326                indexedTriangles(data);
327                break;
328            case GeometryData.INDEXED_TRIANGLE_STRIPS:
329                indexedTriangleStrips(data);
330                break;
331            case GeometryData.INDEXED_TRIANGLE_FANS:
332                indexedTriangleFans(data);
333                break;
334
335            default:
336                throw new UnsupportedTypeException("Unknown geometry type: " +
337                                                   data.geometryType);
338        }
339    }
340
341
342    /**
343     * Generate a new set of points for an unindexed quad array
344     *
345     * @param data The data to base the calculations on
346     * @throws InvalidArraySizeException The array is not big enough to contain
347     *   the requested geometry
348     */
349    private void unindexedTriangles(GeometryData data)
350        throws InvalidArraySizeException
351    {
352        generateUnindexedTriCoordinates(data);
353
354        if((data.geometryComponents & GeometryData.NORMAL_DATA) != 0)
355            generateUnindexedTriNormals(data);
356
357        if((data.geometryComponents & GeometryData.TEXTURE_2D_DATA) != 0)
358            generateUnindexedTriTexture2D(data);
359        else if((data.geometryComponents & GeometryData.TEXTURE_3D_DATA) != 0)
360            generateTriTexture3D(data);
361    }
362
363
364    /**
365     * Generate a new set of points for an unindexed quad array
366     *
367     * @param data The data to base the calculations on
368     * @throws InvalidArraySizeException The array is not big enough to contain
369     *   the requested geometry
370     */
371    private void unindexedQuads(GeometryData data)
372        throws InvalidArraySizeException
373    {
374    }
375
376    /**
377     * Generate a new set of points for an indexed quad array. Uses the same
378     * points as an indexed triangle, but repeats the top coordinate index.
379     *
380     * @param data The data to base the calculations on
381     * @throws InvalidArraySizeException The array is not big enough to contain
382     *   the requested geometry
383     */
384    private void indexedQuads(GeometryData data)
385        throws InvalidArraySizeException
386    {
387        generateIndexedTriCoordinates(data);
388
389        if((data.geometryComponents & GeometryData.NORMAL_DATA) != 0)
390            generateIndexedTriNormals(data);
391
392        if((data.geometryComponents & GeometryData.TEXTURE_2D_DATA) != 0)
393            generateIndexedTexture2D(data);
394        else if((data.geometryComponents & GeometryData.TEXTURE_3D_DATA) != 0)
395            generateTriTexture3D(data);
396
397        // now let's do the index list
398        int index_size = (useTop ? facetCount * 4 : 0 ) + (useBottom ? facetCount * 4 : 0);
399
400        if(data.indexes == null)
401            data.indexes = new int[index_size];
402        else if(data.indexes.length < index_size)
403            throw new InvalidArraySizeException("Coordinates",
404                                                data.indexes.length,
405                                                index_size);
406
407        int[] indexes = data.indexes;
408        data.indexesCount = index_size;
409        int idx = 0;
410        int vtx = 0;
411
412        if (useTop)
413        {
414            // each face consists of an anti-clockwise
415            for(int i = facetCount; --i >= 0; )
416            {
417                int start = idx;
418                indexes[idx++] = vtx++;
419                indexes[idx++] = vtx++;
420                indexes[idx++] = vtx + 1;
421                indexes[idx++] = vtx;
422            }
423        }
424
425        if(!useBottom)
426            return;
427
428        int middle = (facetCount + 1) << 1;
429        vtx++;
430
431        for(int i = facetCount; --i >= 0; )
432        {
433            indexes[idx++] = vtx++;
434            indexes[idx++] = middle;
435            indexes[idx++] = middle;
436            indexes[idx++] = vtx + 1;
437        }
438    }
439
440    /**
441     * Generate a new set of points for an indexed triangle array
442     *
443     * @param data The data to base the calculations on
444     * @throws InvalidArraySizeException The array is not big enough to contain
445     *   the requested geometry
446     */
447    private void indexedTriangles(GeometryData data)
448        throws InvalidArraySizeException
449    {
450        generateIndexedTriCoordinates(data);
451
452        if((data.geometryComponents & GeometryData.NORMAL_DATA) != 0)
453            generateIndexedTriNormals(data);
454
455        if((data.geometryComponents & GeometryData.TEXTURE_2D_DATA) != 0)
456            generateIndexedTexture2D(data);
457        else if((data.geometryComponents & GeometryData.TEXTURE_3D_DATA) != 0)
458            generateTriTexture3D(data);
459
460        // now let's do the index list
461        int index_size = 0;
462
463        if (useTop)
464            index_size = (facetCount + 1) * 3;
465
466        if(useBottom)
467            index_size <<= 1;
468
469        if(data.indexes == null)
470            data.indexes = new int[index_size];
471        else if(data.indexes.length < index_size)
472            throw new InvalidArraySizeException("Coordinates",
473                                                data.indexes.length,
474                                                index_size);
475
476        int[] indexes = data.indexes;
477        data.indexesCount = index_size;
478        int idx = 0;
479        int vtx = 0;
480
481        if (useTop)
482        {
483            // each face consists of an anti-clockwise
484            for(int i = facetCount; --i >= 0; )
485            {
486                indexes[idx++] = vtx++;
487                indexes[idx++] = vtx++;
488                indexes[idx++] = vtx + 1;
489            }
490        }
491
492        if(!useBottom)
493            return;
494
495        int middle = vtx++;
496        for(int i = facetCount + 1; --i >= 0; )
497        {
498            indexes[idx++] = vtx + 1;
499            indexes[idx++] = middle;
500            indexes[idx++] = vtx++;
501        }
502    }
503
504    /**
505     * Generate a new set of points for a triangle strip array. Each side is a
506     * strip of two faces.
507     *
508     * @param data The data to base the calculations on
509     * @throws InvalidArraySizeException The array is not big enough to contain
510     *   the requested geometry
511     */
512    private void triangleStrips(GeometryData data)
513        throws InvalidArraySizeException
514    {
515    }
516
517    /**
518     * Generate a new set of points for a triangle fan array. Each facet on the
519     * side of the cone is a single fan, but the base is one big fan.
520     *
521     * @param data The data to base the calculations on
522     * @throws InvalidArraySizeException The array is not big enough to contain
523     *   the requested geometry
524     */
525    private void triangleFans(GeometryData data)
526        throws InvalidArraySizeException
527    {
528    }
529
530    /**
531     * Generate a new set of points for an indexed triangle strip array. We
532     * build the strip from the existing points, and there's no need to
533     * re-order the points for the indexes this time.
534     *
535     * @param data The data to base the calculations on
536     * @throws InvalidArraySizeException The array is not big enough to contain
537     *   the requested geometry
538     */
539    private void indexedTriangleStrips(GeometryData data)
540        throws InvalidArraySizeException
541    {
542        generateIndexedTriCoordinates(data);
543
544        if((data.geometryComponents & GeometryData.NORMAL_DATA) != 0)
545            generateIndexedTriNormals(data);
546
547        if((data.geometryComponents & GeometryData.TEXTURE_2D_DATA) != 0)
548            generateIndexedTexture2D(data);
549        else if((data.geometryComponents & GeometryData.TEXTURE_3D_DATA) != 0)
550            generateTriTexture3D(data);
551
552        // now let's do the index list
553        int index_size = 0;
554        int num_strips = 0;
555
556        if (useTop)
557        {
558            num_strips++;
559            index_size = (facetCount + 1) * 2;
560        }
561
562        if(useBottom)
563        {
564            num_strips++;
565            index_size <<= 1;
566        }
567
568        if(data.indexes == null)
569            data.indexes = new int[index_size];
570        else if(data.indexes.length < index_size)
571            throw new InvalidArraySizeException("Indexes",
572                                                data.indexes.length,
573                                                index_size);
574
575        if(data.stripCounts == null)
576            data.stripCounts = new int[num_strips];
577        else if(data.stripCounts.length < num_strips)
578            throw new InvalidArraySizeException("Strip counts",
579                                                data.stripCounts.length,
580                                                num_strips);
581
582        int[] indexes = data.indexes;
583        int[] strip_counts = data.stripCounts;
584        data.indexesCount = index_size;
585        data.numStrips = num_strips;
586        int idx = 0;
587        int vtx = 0;
588
589        if (useTop)
590        {
591            // each face consists of an anti-clockwise triangle
592            for(int i = 0; i <= facetCount; i++)
593            {
594                indexes[idx++] = vtx++;
595                indexes[idx++] = vtx++;
596            }
597
598            strip_counts[0] = (facetCount + 1) << 1;
599        }
600
601        if(!useBottom)
602            return;
603
604        // Single big fan on the bottom.
605        // wind the bottom in reverse order so that it can be seen
606        int middle = vtx + 2;
607
608        vtx = data.vertexCount - 1;
609        for(int i = facetCount + 1; --i >= 0; ) {
610            indexes[idx++] = middle;
611            indexes[idx++] = vtx--;
612        }
613
614        strip_counts[1] = (facetCount + 1) << 1;
615    }
616
617    /**
618     * Generate a new set of points for an indexed triangle fan array. We
619     * build the strip from the existing points, and there's no need to
620     * re-order the points for the indexes this time. As for the simple fan,
621     * we use the first index, the lower-right corner as the apex for the fan.
622     *
623     * @param data The data to base the calculations on
624     * @throws InvalidArraySizeException The array is not big enough to contain
625     *   the requested geometry
626     */
627    private void indexedTriangleFans(GeometryData data)
628        throws InvalidArraySizeException
629    {
630        generateIndexedTriCoordinates(data);
631
632        if((data.geometryComponents & GeometryData.NORMAL_DATA) != 0)
633            generateIndexedTriNormals(data);
634
635        if((data.geometryComponents & GeometryData.TEXTURE_2D_DATA) != 0)
636            generateIndexedTexture2D(data);
637        else if((data.geometryComponents & GeometryData.TEXTURE_3D_DATA) != 0)
638            generateTriTexture3D(data);
639
640        // now let's do the index list
641        int index_size = (useTop ? facetCount * 3 : 0) + ((useBottom) ? facetCount + 2 : 0);
642        int num_strips = (useTop ? facetCount : 0) + ((useBottom) ? 1 : 0);
643
644        if(data.indexes == null)
645            data.indexes = new int[index_size];
646        else if(data.indexes.length < index_size)
647            throw new InvalidArraySizeException("Indexes",
648                                                data.indexes.length,
649                                                index_size);
650
651        if(data.stripCounts == null)
652            data.stripCounts = new int[num_strips];
653        else if(data.stripCounts.length < num_strips)
654            throw new InvalidArraySizeException("Strip counts",
655                                                data.stripCounts.length,
656                                                num_strips);
657
658        int[] indexes = data.indexes;
659        int[] stripCounts = data.stripCounts;
660        data.indexesCount = index_size;
661        data.numStrips = num_strips;
662        int idx = 0;
663        int vtx = 0;
664
665        if (useTop)
666        {
667            // each face consists of an anti-clockwise triangle
668            for(int i = 0; i < facetCount; i++)
669            {
670                indexes[idx++] = vtx++;
671                indexes[idx++] = vtx++;
672                indexes[idx++] = vtx + 1;
673                stripCounts[i] = 3;
674            }
675        }
676
677        if(useBottom)
678        {
679            // Single big fan on the bottom.
680            // wind the bottom in reverse order so that it can be seen
681            int middle = vtx + 2;
682            indexes[idx++] = middle;
683            stripCounts[num_strips - 1] = facetCount + 2;
684
685            vtx = data.vertexCount - 1;
686            for(int i = facetCount + 1; --i >= 0; )
687                indexes[idx++] = vtx--;
688        }
689    }
690
691    //------------------------------------------------------------------------
692    // Coordinate generation routines
693    //------------------------------------------------------------------------
694
695    /**
696     * Generates new set of points suitable for use in an unindexed array. Each
697     * base coordinate will appear twice in this list. The first half of the
698     * array is the top, the second half, the bottom.
699     *
700     * @param data The data to base the calculations on
701     * @throws InvalidArraySizeException The array is not big enough to contain
702     *   the requested geometry
703     */
704    private void generateUnindexedTriCoordinates(GeometryData data)
705        throws InvalidArraySizeException
706    {
707        int vtx_cnt = getVertexCount(data);
708
709        if(data.coordinates == null)
710            data.coordinates = new float[vtx_cnt * 3];
711        else if(data.coordinates.length < vtx_cnt * 3)
712            throw new InvalidArraySizeException("Coordinates",
713                                                data.coordinates.length,
714                                                vtx_cnt * 3);
715
716        float[] coords = data.coordinates;
717        data.vertexCount = vtx_cnt;
718
719        regenerateBase();
720
721        int count = 0;
722        int i = 0;
723        int base_count = 0;
724        float height_2 = coneHeight / 2;
725
726        if (useTop)
727        {
728            // Reverse loop count because it is *much* faster than the forward
729            // version.
730            for(i = facetCount; --i >= 0; )
731            {
732                //side coords
733                coords[count++] = 0;
734                coords[count++] = height_2;
735                coords[count++] = 0;
736
737                coords[count++] = baseCoordinates[base_count++];
738                coords[count++] = -height_2;
739                coords[count++] = baseCoordinates[base_count++];
740
741                coords[count++] = baseCoordinates[base_count];
742                coords[count++] = -height_2;
743                coords[count++] = baseCoordinates[base_count + 1];
744            }
745        }
746
747        // The last set of coordinates reuses the first two base coords
748        //side coords
749        if(useBottom)
750        {
751            base_count = 0;
752
753            for(i = facetCount; --i >= 0;)
754            {
755                coords[count++] = baseCoordinates[base_count++];
756                coords[count++] = -height_2;
757                coords[count++] = baseCoordinates[base_count++];
758
759                coords[count++] = 0;
760                coords[count++] = -height_2;
761                coords[count++] = 0;
762
763                coords[count++] = baseCoordinates[base_count];
764                coords[count++] = -height_2;
765                coords[count++] = baseCoordinates[base_count + 1];
766            }
767        }
768    }
769
770    /**
771     * Generate a new set of points for use in an indexed array. The first
772     * index will always be the cone tip - parallel for each face so that we
773     * can get the smoothing right. If the array is to use the bottom,
774     * a second set of coordinates will be produced separately for the base
775     * so that independent surface normals can be used. These values will
776     * start at vertexCount / 2 with the first value as 0,0,0 (the center of
777     * the base) and then all the following values as the base.
778     */
779    private void generateIndexedTriCoordinates(GeometryData data)
780        throws InvalidArraySizeException
781    {
782        int vtx_cnt = getVertexCount(data);
783
784        if(data.coordinates == null)
785            data.coordinates = new float[vtx_cnt * 3];
786        else if(data.coordinates.length < vtx_cnt * 3)
787            throw new InvalidArraySizeException("Coordinates",
788                                                data.coordinates.length,
789                                                vtx_cnt * 3);
790
791        float[] coords = data.coordinates;
792        data.vertexCount = vtx_cnt;
793
794        regenerateBase();
795
796        int base_offset = 1;
797        int count = 0;
798        int base_count = 0;
799        int i;
800        float height_2 = coneHeight / 2;
801
802        if (useTop)
803        {
804            for(i = facetCount + 1; --i >= 0; )
805            {
806                coords[count++] = 0;
807                coords[count++] = height_2;
808                coords[count++] = 0;
809
810                coords[count++] = baseCoordinates[base_count++];
811                coords[count++] = -height_2;
812                coords[count++] = baseCoordinates[base_count++];
813            }
814        }
815
816        if(useBottom)
817        {
818            coords[count++] = 0;
819            coords[count++] = -height_2;
820            coords[count++] = 0;
821
822            base_count = 0;
823            for(i = facetCount + 1; --i >= 0; )
824            {
825               coords[count++] = baseCoordinates[base_count++];
826               coords[count++] = -height_2;
827               coords[count++] = baseCoordinates[base_count++];
828            }
829        }
830    }
831
832    //------------------------------------------------------------------------
833    // Normal generation routines
834    //------------------------------------------------------------------------
835
836    /**
837     * Generate a new set of normals for a normal set of unindexed points.
838     * Smooth normals are used for the sides at the average between the faces.
839     * Bottom normals always point down.
840     * <p>
841     * This must always be called after the coordinate generation. The
842     * top normal of the cone is always perpendicular to the face.
843     *
844     * @param data The data to base the calculations on
845     * @throws InvalidArraySizeException The array is not big enough to contain
846     *   the requested geometry
847     */
848    private void generateUnindexedTriNormals(GeometryData data)
849        throws InvalidArraySizeException
850    {
851        int vtx_cnt = data.vertexCount * 3;
852
853        if(data.normals == null)
854            data.normals = new float[vtx_cnt];
855        else if(data.normals.length < vtx_cnt)
856            throw new InvalidArraySizeException("Normals",
857                                                data.normals.length,
858                                                vtx_cnt);
859
860        int i;
861        float[] normals = data.normals;
862        Vector3f norm = new Vector3f();
863        int count = 0;
864        vtx_cnt = 0;
865
866        if (useTop)
867        {
868            for(i = facetCount; --i >= 0; )
869            {
870                norm = createFaceNormal(data.coordinates,
871                                        vtx_cnt + 3,
872                                        vtx_cnt,
873                                        vtx_cnt + 6);
874
875                normals[count++] = norm.x;
876                normals[count++] = norm.y;
877                normals[count++] = norm.z;
878
879                createBottomRadialNormal(data.coordinates, vtx_cnt + 3, norm);
880
881                normals[count++] = norm.x;
882                normals[count++] = norm.y;
883                normals[count++] = norm.z;
884
885                createBottomRadialNormal(data.coordinates, vtx_cnt + 6, norm);
886
887                normals[count++] = norm.x;
888                normals[count++] = norm.y;
889                normals[count++] = norm.z;
890
891                vtx_cnt += 9;
892            }
893        }
894
895        // Now generate the bottom if we need it.
896        if(!useBottom)
897            return;
898
899        for(i = facetCount; --i >= 0; )
900        {
901            // The three vertices of the base in an unrolled loop
902            normals[count++] = 0;
903            normals[count++] = -1;
904            normals[count++] = 0;
905
906            normals[count++] = 0;
907            normals[count++] = -1;
908            normals[count++] = 0;
909
910            normals[count++] = 0;
911            normals[count++] = -1;
912            normals[count++] = 0;
913        }
914    }
915
916    /**
917     * Create a normal based on the given vertex position, assuming that it is
918     * a point in space, relative to the origin of the bottom. This will create a normal that
919     * points directly along the vector from the origin to the point.
920     *
921     * @param coords The coordinate array to read values from
922     * @param p The index of the point to calculate
923     * @return A temporary value containing the normal value
924     */
925    private void createBottomRadialNormal(float[] coords, int p, Vector3f normal)
926    {
927        float x = coords[p];
928        float y = coneHeight / 2 - coords[p + 1];
929        float z = coords[p + 2];
930
931        float mag = x * x + y * y + z * z;
932
933        if(mag != 0.0)
934        {
935            mag = 1.0f / ((float) Math.sqrt(mag));
936            normal.x = x * mag;
937            normal.y = y * mag;
938            normal.z = z * mag;
939        }
940        else
941        {
942            normal.x = 0;
943            normal.y = 0;
944            normal.z = 0;
945        }
946    }
947
948    /**
949     * Generate a new set of normals for a normal set of indexed points.
950     * Handles both flat and smooth shading of normals. Flat just has them
951     * perpendicular to the face. Smooth has them at the value at the
952     * average between the faces. Bottom normals always point down.
953     * <p>
954     * This must always be called after the coordinate generation. The
955     * top normal of the cone is always perpendicular to the face.
956     *
957     * @param data The data to base the calculations on
958     * @throws InvalidArraySizeException The array is not big enough to contain
959     *   the requested geometry
960     */
961    private void generateIndexedTriNormals(GeometryData data)
962        throws InvalidArraySizeException
963    {
964        int vtx_cnt = getVertexCount(data) * 3;
965
966        if(data.normals == null)
967            data.normals = new float[vtx_cnt];
968        else if(data.normals.length < vtx_cnt)
969            throw new InvalidArraySizeException("Normals",
970                                                data.normals.length,
971                                                vtx_cnt);
972
973        int i;
974        float[] normals = data.normals;
975        Vector3f norm;
976        int count = 0;
977
978        if (useBottom)
979        {
980            for(i = facetCount + 1; --i >= 0; )
981            {
982                norm = createFaceNormal(data.coordinates,
983                                        count + 3,
984                                        count,
985                                        count + 9);
986
987                normals[count++] = norm.x;
988                normals[count++] = norm.y;
989                normals[count++] = norm.z;
990
991                norm = createRadialNormal(data.coordinates, count);
992
993                normals[count++] = norm.x;
994                normals[count++] = norm.y;
995                normals[count++] = norm.z;
996            }
997        }
998
999        // Now generate the bottom if we need it.
1000        if(!useBottom)
1001            return;
1002
1003        normals[count++] = 0;
1004        normals[count++] = -1;
1005        normals[count++] = 0;
1006
1007        for(i = facetCount + 1; --i >= 0; )
1008        {
1009            normals[count++] = 0;
1010            normals[count++] = -1;
1011            normals[count++] = 0;
1012        }
1013    }
1014
1015    //------------------------------------------------------------------------
1016    // Texture coordinate generation routines
1017    //------------------------------------------------------------------------
1018
1019    /**
1020     * Generate a new set of texCoords for a normal set of unindexed triangle
1021     * points.
1022     *
1023     * @param data The data to base the calculations on
1024     * @throws InvalidArraySizeException The array is not big enough to contain
1025     *   the requested geometry
1026     */
1027    private void generateUnindexedTriTexture2D(GeometryData data)
1028        throws InvalidArraySizeException
1029    {
1030        int vtx_cnt = data.vertexCount * 2;
1031
1032        if(data.textureCoordinates == null)
1033            data.textureCoordinates = new float[vtx_cnt];
1034        else if(data.textureCoordinates.length < vtx_cnt)
1035            throw new InvalidArraySizeException("2D Texture coordinates",
1036                                                data.textureCoordinates.length,
1037                                                vtx_cnt);
1038
1039        float[] tex_coords = data.textureCoordinates;
1040
1041        recalc2DTexture();
1042
1043        int i;
1044        int pos;
1045        int count = 0;
1046
1047        if (useTop)
1048        {
1049            for(i = 0; i < facetCount; i++) {
1050                pos = i * 4;
1051
1052                tex_coords[count++] = texCoordinates2D[pos];
1053                tex_coords[count++] = texCoordinates2D[pos + 1];
1054
1055                tex_coords[count++] = texCoordinates2D[pos + 2];
1056                tex_coords[count++] = texCoordinates2D[pos + 3];
1057
1058                tex_coords[count++] = texCoordinates2D[pos + 6];
1059                tex_coords[count++] = texCoordinates2D[pos + 7];
1060            }
1061        }
1062
1063        if(!useBottom)
1064            return;
1065
1066        // The base
1067        int offset;
1068        if (useTop)
1069            offset = (facetCount + 1) * 4;
1070        else
1071            offset = 0;
1072
1073        for(i = 0; i < facetCount; i++) {
1074            pos = i * 2 + offset + 2;
1075
1076            tex_coords[count++] = texCoordinates2D[pos];
1077            tex_coords[count++] = texCoordinates2D[pos + 1];
1078
1079            tex_coords[count++] = texCoordinates2D[offset];
1080            tex_coords[count++] = texCoordinates2D[offset + 1];
1081
1082            tex_coords[count++] = texCoordinates2D[pos + 2];
1083            tex_coords[count++] = texCoordinates2D[pos + 3];
1084        }
1085    }
1086
1087    /**
1088     * Generates new set of points suitable for use in an indexed array.
1089     * This array is your basic shape, but with the bottom part mirrored if
1090     * need be.
1091     *
1092     * @param data The data to base the calculations on
1093     * @throws InvalidArraySizeException The array is not big enough to contain
1094     *   the requested geometry
1095     */
1096    private void generateIndexedTexture2D(GeometryData data)
1097        throws InvalidArraySizeException
1098    {
1099        int vtx_cnt = data.vertexCount * 2;
1100
1101        if(data.textureCoordinates == null)
1102            data.textureCoordinates = new float[vtx_cnt];
1103        else if(data.textureCoordinates.length < vtx_cnt)
1104            throw new InvalidArraySizeException("2D Texture coordinates",
1105                                                data.textureCoordinates.length,
1106                                                vtx_cnt);
1107
1108        float[] tex_coords = data.textureCoordinates;
1109
1110        recalc2DTexture();
1111
1112        System.arraycopy(texCoordinates2D,
1113                         0,
1114                         data.textureCoordinates,
1115                         0,
1116                         numTexCoords2D);
1117    }
1118
1119    /**
1120     * Generate a new set of texCoords for a normal set of unindexed points. Each
1121     * normal faces directly perpendicular for each point. This makes each face
1122     * seem flat.
1123     * <p>
1124     * This must always be called after the coordinate generation.
1125     *
1126     * @param data The data to base the calculations on
1127     * @throws InvalidArraySizeException The array is not big enough to contain
1128     *   the requested geometry
1129     */
1130    private void generateTriTexture3D(GeometryData data)
1131        throws InvalidArraySizeException
1132    {
1133        int vtx_cnt = data.vertexCount * 2;
1134
1135        if(data.textureCoordinates == null)
1136            data.textureCoordinates = new float[vtx_cnt];
1137        else if(data.textureCoordinates.length < vtx_cnt)
1138            throw new InvalidArraySizeException("3D Texture coordinates",
1139                                                data.textureCoordinates.length,
1140                                                vtx_cnt);
1141
1142        float[] texCoords = data.textureCoordinates;
1143    }
1144
1145    /**
1146     * Regenerate the base coordinate points. These are the flat circle that
1147     * makes up the base of the code. The coordinates are generated based on
1148     * the 2 PI divided by the number of facets to generate.
1149     */
1150    private final void regenerateBase()
1151    {
1152        if(!baseChanged)
1153            return;
1154
1155        baseChanged = false;
1156        numBaseValues = (facetCount + 1) * 2;
1157
1158        if((baseCoordinates == null) ||
1159           (numBaseValues > baseCoordinates.length))
1160        {
1161            baseCoordinates = new float[numBaseValues];
1162        }
1163
1164
1165         // local constant to make math calcs faster
1166        double segment_angle = 2.0 * Math.PI / facetCount;
1167        int count = 0;
1168        float x, z;
1169        double angle;
1170        int i;
1171        double halfCount = (Math.PI / 2 - Math.PI / (facetCount / 2));
1172
1173        // Reverse loop count because it is *much* faster than the forward
1174        // version.
1175        for(i = facetCount; --i >= 0; )
1176        {
1177            angle = segment_angle * i;
1178
1179            x = (float)(bottomRadius * Math.cos(angle - halfCount));
1180            z = (float)(bottomRadius * Math.sin(angle - halfCount));
1181
1182            baseCoordinates[count++] = x;
1183            baseCoordinates[count++] = z;
1184        }
1185
1186        baseCoordinates[count++] = baseCoordinates[0];
1187        baseCoordinates[count++] = baseCoordinates[1];
1188    }
1189
1190    /**
1191     * Recalculate the 2D texture coordinates IAW the coordinate values. This
1192     * starts by using the circumference as a T value of 0.5 to indicate it is
1193     * halfway through the texture (we are starting at the middle of the
1194     * sphere!). Then, if we have a bottom, we calculate the T from 0 to 0.5.
1195     * thus the coordinates are for the top half of the sphere, followed by
1196     * the bottom half.
1197     */
1198    private void recalc2DTexture()
1199    {
1200        if(!facetsChanged)
1201            return;
1202
1203        // not a good idea because we should also leave this set to recalc
1204        // the 3D coordinates.
1205        facetsChanged = false;
1206        int vtx_count = 0;
1207
1208        if (useTop)
1209            vtx_count = (facetCount + 1) << 1;
1210
1211        if(useBottom)
1212            vtx_count += (facetCount + 1) << 1;
1213
1214        if((texCoordinates2D == null) ||
1215           (vtx_count * 2 > texCoordinates2D.length))
1216        {
1217            texCoordinates2D = new float[vtx_count * 2];
1218        }
1219
1220        // local constant to make math calcs faster
1221        float segment_angle = 1 / (float)facetCount;
1222        float angle = (float)(2.0 * Math.PI / facetCount);
1223
1224        int count = 0;
1225        int i, k;
1226        float s, a;
1227        float[] bottom_s = new float[facetCount + 1];
1228        float[] bottom_t = new float[facetCount + 1];
1229
1230        if (useTop)
1231        {
1232            for(i = 0; i < facetCount; i++)
1233            {
1234                s = i * segment_angle;
1235
1236                texCoordinates2D[count++] = s;
1237                texCoordinates2D[count++] = 1;
1238
1239                texCoordinates2D[count++] = s;
1240                texCoordinates2D[count++] = 0;
1241
1242                a = i * angle;
1243                bottom_s[i] = (float)(0.5f - bottomRadius * Math.cos(a) / 2);
1244                bottom_t[i] = (float)(0.5f - bottomRadius * Math.sin(a) / 2);
1245            }
1246
1247            texCoordinates2D[count++] = 1;
1248            texCoordinates2D[count++] = 1;
1249
1250            texCoordinates2D[count++] = 1;
1251            texCoordinates2D[count++] = 0;
1252
1253            bottom_s[facetCount] = bottom_s[0];
1254            bottom_t[facetCount] = bottom_t[0];
1255        }
1256
1257        if(useBottom)
1258        {
1259            // bottom is a flat square that is based with the centre at
1260            // the centre of the cone. Start with the centre point first
1261            texCoordinates2D[count++] = 0.5f;
1262            texCoordinates2D[count++] = 0.5f;
1263
1264            for(i = 0; i <= facetCount; i++)
1265            {
1266                texCoordinates2D[count++] = bottom_s[i];
1267                texCoordinates2D[count++] = bottom_t[i];
1268            }
1269        }
1270
1271        numTexCoords2D = count;
1272    }
1273}