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}