Added a unit test and fixed indentation.

fix-456
stophe 8 years ago
parent 61c22d5709
commit ebaad20f2f
  1. 51
      jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java
  2. 78
      jme3-core/src/test/java/com/jme3/scene/ShapeGeometryTest.java

@ -209,15 +209,15 @@ public class Cylinder extends Mesh {
* @param inverted is the cylinder is meant to be viewed from the inside. * @param inverted is the cylinder is meant to be viewed from the inside.
*/ */
public void updateGeometry(int axisSamples, int radialSamples, public void updateGeometry(int axisSamples, int radialSamples,
float topRadius, float bottomRadius, float height, boolean closed, boolean inverted) float topRadius, float bottomRadius, float height, boolean closed, boolean inverted) {
{
// Ensure there's at least two axis samples and 3 radial samples, and positive dimensions. // Ensure there's at least two axis samples and 3 radial samples, and positive dimensions.
if( axisSamples < 2 if( axisSamples < 2
|| radialSamples < 3 || radialSamples < 3
|| topRadius <= 0 || topRadius <= 0
|| bottomRadius <= 0 || bottomRadius <= 0
|| height <= 0 ) || height <= 0 ) {
throw new IllegalArgumentException("Cylinders must have at least 2 axis samples and 3 radial samples, and positive dimensions."); throw new IllegalArgumentException("Cylinders must have at least 2 axis samples and 3 radial samples, and positive dimensions.");
}
this.axisSamples = axisSamples; this.axisSamples = axisSamples;
this.radialSamples = radialSamples; this.radialSamples = radialSamples;
@ -231,8 +231,7 @@ public class Cylinder extends Mesh {
int verticesCount = axisSamples * (radialSamples +1); int verticesCount = axisSamples * (radialSamples +1);
// Triangles: Two per side rectangle, which is the product of numbers of samples. // Triangles: Two per side rectangle, which is the product of numbers of samples.
int trianglesCount = axisSamples * radialSamples * 2 ; int trianglesCount = axisSamples * radialSamples * 2 ;
if( closed ) if( closed ) {
{
// If there are caps, add two additional rims and two summits. // If there are caps, add two additional rims and two summits.
verticesCount += 2 + 2 * (radialSamples +1); verticesCount += 2 + 2 * (radialSamples +1);
// Add one triangle per radial sample, twice, to form the caps. // Add one triangle per radial sample, twice, to form the caps.
@ -241,8 +240,7 @@ public class Cylinder extends Mesh {
// Compute the points along a unit circle: // Compute the points along a unit circle:
float[][] circlePoints = new float[radialSamples+1][2]; float[][] circlePoints = new float[radialSamples+1][2];
for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) {
{
float angle = FastMath.TWO_PI / radialSamples * circlePoint; float angle = FastMath.TWO_PI / radialSamples * circlePoint;
circlePoints[circlePoint][0] = FastMath.cos(angle); circlePoints[circlePoint][0] = FastMath.cos(angle);
circlePoints[circlePoint][1] = FastMath.sin(angle); circlePoints[circlePoint][1] = FastMath.sin(angle);
@ -263,8 +261,7 @@ public class Cylinder extends Mesh {
// The normal in A and D is simply orthogonal to AD, which means we can get it once per sample. // The normal in A and D is simply orthogonal to AD, which means we can get it once per sample.
// //
Vector3f[] circleNormals = new Vector3f[radialSamples+1]; Vector3f[] circleNormals = new Vector3f[radialSamples+1];
for (int circlePoint = 0; circlePoint < radialSamples+1; circlePoint++) for (int circlePoint = 0; circlePoint < radialSamples+1; circlePoint++) {
{
// The normal is the orthogonal to the side, which can be got without trigonometry. // The normal is the orthogonal to the side, which can be got without trigonometry.
// The edge direction is oriented so that it goes up by Height, and out by the radius difference; let's use // The edge direction is oriented so that it goes up by Height, and out by the radius difference; let's use
// those values in reverse order. // those values in reverse order.
@ -278,13 +275,11 @@ public class Cylinder extends Mesh {
int currentIndex = 0; int currentIndex = 0;
// Add a circle of points for each axis sample. // Add a circle of points for each axis sample.
for(int axisSample = 0; axisSample < axisSamples; axisSample++ ) for(int axisSample = 0; axisSample < axisSamples; axisSample++ ) {
{
float currentHeight = -height / 2 + height * axisSample / (axisSamples-1); float currentHeight = -height / 2 + height * axisSample / (axisSamples-1);
float currentRadius = bottomRadius + (topRadius - bottomRadius) * axisSample / (axisSamples-1); float currentRadius = bottomRadius + (topRadius - bottomRadius) * axisSample / (axisSamples-1);
for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) {
{
// Position, by multipliying the position on a unit circle with the current radius. // Position, by multipliying the position on a unit circle with the current radius.
vertices[currentIndex*3] = circlePoints[circlePoint][0] * currentRadius; vertices[currentIndex*3] = circlePoints[circlePoint][0] * currentRadius;
vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * currentRadius; vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * currentRadius;
@ -311,11 +306,9 @@ public class Cylinder extends Mesh {
} }
// If closed, add duplicate rims on top and bottom, with normals facing up and down. // If closed, add duplicate rims on top and bottom, with normals facing up and down.
if (closed) if (closed) {
{
// Bottom // Bottom
for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) {
{
vertices[currentIndex*3] = circlePoints[circlePoint][0] * bottomRadius; vertices[currentIndex*3] = circlePoints[circlePoint][0] * bottomRadius;
vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * bottomRadius; vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * bottomRadius;
vertices[currentIndex*3 +2] = -height/2; vertices[currentIndex*3 +2] = -height/2;
@ -330,8 +323,7 @@ public class Cylinder extends Mesh {
currentIndex++; currentIndex++;
} }
// Top // Top
for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) {
{
vertices[currentIndex*3] = circlePoints[circlePoint][0] * topRadius; vertices[currentIndex*3] = circlePoints[circlePoint][0] * topRadius;
vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * topRadius; vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * topRadius;
vertices[currentIndex*3 +2] = height/2; vertices[currentIndex*3 +2] = height/2;
@ -375,10 +367,8 @@ public class Cylinder extends Mesh {
// Add the triangles indexes. // Add the triangles indexes.
short[] indices = new short[trianglesCount * 3]; short[] indices = new short[trianglesCount * 3];
currentIndex = 0; currentIndex = 0;
for (short axisSample = 0; axisSample < axisSamples - 1; axisSample++) for (short axisSample = 0; axisSample < axisSamples - 1; axisSample++) {
{ for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) {
for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++)
{
indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint); indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint);
indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint + 1); indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint + 1);
indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint); indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint);
@ -389,16 +379,14 @@ public class Cylinder extends Mesh {
} }
} }
// Add caps if needed. // Add caps if needed.
if(closed) if(closed) {
{
short bottomCapIndex = (short) (verticesCount - 2); short bottomCapIndex = (short) (verticesCount - 2);
short topCapIndex = (short) (verticesCount - 1); short topCapIndex = (short) (verticesCount - 1);
int bottomRowOffset = (axisSamples) * (radialSamples +1 ); int bottomRowOffset = (axisSamples) * (radialSamples +1 );
int topRowOffset = (axisSamples+1) * (radialSamples +1 ); int topRowOffset = (axisSamples+1) * (radialSamples +1 );
for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) {
{
indices[currentIndex++] = (short) (bottomRowOffset + circlePoint +1); indices[currentIndex++] = (short) (bottomRowOffset + circlePoint +1);
indices[currentIndex++] = (short) (bottomRowOffset + circlePoint); indices[currentIndex++] = (short) (bottomRowOffset + circlePoint);
indices[currentIndex++] = bottomCapIndex; indices[currentIndex++] = bottomCapIndex;
@ -411,17 +399,14 @@ public class Cylinder extends Mesh {
} }
// If inverted, the triangles and normals are all reverted. // If inverted, the triangles and normals are all reverted.
if (inverted) if (inverted) {
{ for (int i = 0; i < indices.length / 2; i++) {
for (int i = 0; i < indices.length / 2; i++)
{
short temp = indices[i]; short temp = indices[i];
indices[i] = indices[indices.length - 1 - i]; indices[i] = indices[indices.length - 1 - i];
indices[indices.length - 1 - i] = temp; indices[indices.length - 1 - i] = temp;
} }
for(int i = 0; i< normals.length; i++) for(int i = 0; i< normals.length; i++) {
{
normals[i] = -normals[i]; normals[i] = -normals[i];
} }
} }

@ -0,0 +1,78 @@
/*
* Copyright (c) 2009-2017 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.scene;
import com.jme3.collision.CollisionResults;
import com.jme3.math.FastMath;
import com.jme3.math.Ray;
import com.jme3.math.Vector3f;
import com.jme3.scene.shape.Cylinder;
import java.util.Random;
import org.junit.Test;
/**
* Ensures that geometries behave correctly, by casting rays and ensure they don't break.
*
* @author Christophe Carpentier
*/
public class ShapeGeometryTest {
protected static final int NUMBER_OF_TRIES = 1000;
@Test
public void testCylinders() {
Random random = new Random();
// Create a cylinder, cast a random ray, and ensure everything goes well.
Node scene = new Node("Scene Node");
for (int i = 0; i < NUMBER_OF_TRIES; i++) {
scene.detachAllChildren();
Cylinder cylinder = new Cylinder(2, 8, 1, 1, true);
Geometry geometry = new Geometry("cylinder", cylinder);
geometry.rotate(FastMath.HALF_PI, 0, 0);
scene.attachChild(geometry);
// Cast a random ray, and count successes and IndexOutOfBoundsExceptions.
Vector3f randomPoint = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat());
Vector3f randomDirection = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat());
randomDirection.normalizeLocal();
Ray ray = new Ray(randomPoint, randomDirection);
CollisionResults collisionResults = new CollisionResults();
// If the geometry is invalid, this should throw various exceptions.
scene.collideWith(ray, collisionResults);
}
}
}
Loading…
Cancel
Save