1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 // $Id: Sphere.java 4163 2009-03-25 01:14:55Z matt.yellen $ 33 package com.jme3.scene.shape; 34 35 import com.jme3.export.InputCapsule; 36 import com.jme3.export.JmeExporter; 37 import com.jme3.export.JmeImporter; 38 import com.jme3.export.OutputCapsule; 39 import com.jme3.math.FastMath; 40 import com.jme3.math.Vector3f; 41 import com.jme3.scene.Mesh; 42 import com.jme3.scene.VertexBuffer.Type; 43 import com.jme3.util.BufferUtils; 44 import com.jme3.util.TempVars; 45 import java.io.IOException; 46 import java.nio.FloatBuffer; 47 import java.nio.ShortBuffer; 48 49 /** 50 * <code>Sphere</code> represents a 3D object with all points equidistance 51 * from a center point. 52 * 53 * @author Joshua Slack 54 * @version $Revision: 4163 $, $Date: 2009-03-24 21:14:55 -0400 (Tue, 24 Mar 2009) $ 55 */ 56 public class Sphere extends Mesh { 57 58 public enum TextureMode { 59 60 /** 61 * Wrap texture radially and along z-axis 62 */ 63 Original, 64 /** 65 * Wrap texure radially, but spherically project along z-axis 66 */ 67 Projected, 68 /** 69 * Apply texture to each pole. Eliminates polar distortion, 70 * but mirrors the texture across the equator 71 */ 72 Polar 73 } 74 protected int vertCount; 75 protected int triCount; 76 protected int zSamples; 77 protected int radialSamples; 78 protected boolean useEvenSlices; 79 protected boolean interior; 80 /** the distance from the center point each point falls on */ 81 public float radius; 82 protected TextureMode textureMode = TextureMode.Original; 83 84 /** 85 * Serialization only. Do not use. 86 */ 87 public Sphere() { 88 } 89 90 /** 91 * Constructs a sphere. All geometry data buffers are updated automatically. 92 * Both zSamples and radialSamples increase the quality of the generated 93 * sphere. 94 * 95 * @param zSamples 96 * The number of samples along the Z. 97 * @param radialSamples 98 * The number of samples along the radial. 99 * @param radius 100 * The radius of the sphere. 101 */ 102 public Sphere(int zSamples, int radialSamples, float radius) { 103 this(zSamples, radialSamples, radius, false, false); 104 } 105 106 /** 107 * Constructs a sphere. Additional arg to evenly space latitudinal slices 108 * 109 * @param zSamples 110 * The number of samples along the Z. 111 * @param radialSamples 112 * The number of samples along the radial. 113 * @param radius 114 * The radius of the sphere. 115 * @param useEvenSlices 116 * Slice sphere evenly along the Z axis 117 * @param interior 118 * Not yet documented 119 */ 120 public Sphere(int zSamples, int radialSamples, float radius, boolean useEvenSlices, boolean interior) { 121 updateGeometry(zSamples, radialSamples, radius, useEvenSlices, interior); 122 } 123 124 public int getRadialSamples() { 125 return radialSamples; 126 } 127 128 public float getRadius() { 129 return radius; 130 } 131 132 /** 133 * @return Returns the textureMode. 134 */ 135 public TextureMode getTextureMode() { 136 return textureMode; 137 } 138 139 public int getZSamples() { 140 return zSamples; 141 } 142 143 /** 144 * builds the vertices based on the radius, radial and zSamples. 145 */ 146 private void setGeometryData() { 147 // allocate vertices 148 vertCount = (zSamples - 2) * (radialSamples + 1) + 2; 149 150 FloatBuffer posBuf = BufferUtils.createVector3Buffer(vertCount); 151 152 // allocate normals if requested 153 FloatBuffer normBuf = BufferUtils.createVector3Buffer(vertCount); 154 155 // allocate texture coordinates 156 FloatBuffer texBuf = BufferUtils.createVector2Buffer(vertCount); 157 158 setBuffer(Type.Position, 3, posBuf); 159 setBuffer(Type.Normal, 3, normBuf); 160 setBuffer(Type.TexCoord, 2, texBuf); 161 162 // generate geometry 163 float fInvRS = 1.0f / radialSamples; 164 float fZFactor = 2.0f / (zSamples - 1); 165 166 // Generate points on the unit circle to be used in computing the mesh 167 // points on a sphere slice. 168 float[] afSin = new float[(radialSamples + 1)]; 169 float[] afCos = new float[(radialSamples + 1)]; 170 for (int iR = 0; iR < radialSamples; iR++) { 171 float fAngle = FastMath.TWO_PI * fInvRS * iR; 172 afCos[iR] = FastMath.cos(fAngle); 173 afSin[iR] = FastMath.sin(fAngle); 174 } 175 afSin[radialSamples] = afSin[0]; 176 afCos[radialSamples] = afCos[0]; 177 178 TempVars vars = TempVars.get(); 179 Vector3f tempVa = vars.vect1; 180 Vector3f tempVb = vars.vect2; 181 Vector3f tempVc = vars.vect3; 182 183 // generate the sphere itself 184 int i = 0; 185 for (int iZ = 1; iZ < (zSamples - 1); iZ++) { 186 float fAFraction = FastMath.HALF_PI * (-1.0f + fZFactor * iZ); // in (-pi/2, pi/2) 187 float fZFraction; 188 if (useEvenSlices) { 189 fZFraction = -1.0f + fZFactor * iZ; // in (-1, 1) 190 } else { 191 fZFraction = FastMath.sin(fAFraction); // in (-1,1) 192 } 193 float fZ = radius * fZFraction; 194 195 // compute center of slice 196 Vector3f kSliceCenter = tempVb.set(Vector3f.ZERO); 197 kSliceCenter.z += fZ; 198 199 // compute radius of slice 200 float fSliceRadius = FastMath.sqrt(FastMath.abs(radius * radius 201 - fZ * fZ)); 202 203 // compute slice vertices with duplication at end point 204 Vector3f kNormal; 205 int iSave = i; 206 for (int iR = 0; iR < radialSamples; iR++) { 207 float fRadialFraction = iR * fInvRS; // in [0,1) 208 Vector3f kRadial = tempVc.set(afCos[iR], afSin[iR], 0); 209 kRadial.mult(fSliceRadius, tempVa); 210 posBuf.put(kSliceCenter.x + tempVa.x).put( 211 kSliceCenter.y + tempVa.y).put( 212 kSliceCenter.z + tempVa.z); 213 214 BufferUtils.populateFromBuffer(tempVa, posBuf, i); 215 kNormal = tempVa; 216 kNormal.normalizeLocal(); 217 if (!interior) // allow interior texture vs. exterior 218 { 219 normBuf.put(kNormal.x).put(kNormal.y).put( 220 kNormal.z); 221 } else { 222 normBuf.put(-kNormal.x).put(-kNormal.y).put( 223 -kNormal.z); 224 } 225 226 if (textureMode == TextureMode.Original) { 227 texBuf.put(fRadialFraction).put( 228 0.5f * (fZFraction + 1.0f)); 229 } else if (textureMode == TextureMode.Projected) { 230 texBuf.put(fRadialFraction).put( 231 FastMath.INV_PI 232 * (FastMath.HALF_PI + FastMath.asin(fZFraction))); 233 } else if (textureMode == TextureMode.Polar) { 234 float r = (FastMath.HALF_PI - FastMath.abs(fAFraction)) / FastMath.PI; 235 float u = r * afCos[iR] + 0.5f; 236 float v = r * afSin[iR] + 0.5f; 237 texBuf.put(u).put(v); 238 } 239 240 i++; 241 } 242 243 BufferUtils.copyInternalVector3(posBuf, iSave, i); 244 BufferUtils.copyInternalVector3(normBuf, iSave, i); 245 246 if (textureMode == TextureMode.Original) { 247 texBuf.put(1.0f).put( 248 0.5f * (fZFraction + 1.0f)); 249 } else if (textureMode == TextureMode.Projected) { 250 texBuf.put(1.0f).put( 251 FastMath.INV_PI 252 * (FastMath.HALF_PI + FastMath.asin(fZFraction))); 253 } else if (textureMode == TextureMode.Polar) { 254 float r = (FastMath.HALF_PI - FastMath.abs(fAFraction)) / FastMath.PI; 255 texBuf.put(r + 0.5f).put(0.5f); 256 } 257 258 i++; 259 } 260 261 vars.release(); 262 263 // south pole 264 posBuf.position(i * 3); 265 posBuf.put(0f).put(0f).put(-radius); 266 267 normBuf.position(i * 3); 268 if (!interior) { 269 normBuf.put(0).put(0).put(-1); // allow for inner 270 } // texture orientation 271 // later. 272 else { 273 normBuf.put(0).put(0).put(1); 274 } 275 276 texBuf.position(i * 2); 277 278 if (textureMode == TextureMode.Polar) { 279 texBuf.put(0.5f).put(0.5f); 280 } else { 281 texBuf.put(0.5f).put(0.0f); 282 } 283 284 i++; 285 286 // north pole 287 posBuf.put(0).put(0).put(radius); 288 289 if (!interior) { 290 normBuf.put(0).put(0).put(1); 291 } else { 292 normBuf.put(0).put(0).put(-1); 293 } 294 295 if (textureMode == TextureMode.Polar) { 296 texBuf.put(0.5f).put(0.5f); 297 } else { 298 texBuf.put(0.5f).put(1.0f); 299 } 300 301 updateBound(); 302 setStatic(); 303 } 304 305 /** 306 * sets the indices for rendering the sphere. 307 */ 308 private void setIndexData() { 309 // allocate connectivity 310 triCount = 2 * (zSamples - 2) * radialSamples; 311 ShortBuffer idxBuf = BufferUtils.createShortBuffer(3 * triCount); 312 setBuffer(Type.Index, 3, idxBuf); 313 314 // generate connectivity 315 int index = 0; 316 for (int iZ = 0, iZStart = 0; iZ < (zSamples - 3); iZ++) { 317 int i0 = iZStart; 318 int i1 = i0 + 1; 319 iZStart += (radialSamples + 1); 320 int i2 = iZStart; 321 int i3 = i2 + 1; 322 for (int i = 0; i < radialSamples; i++, index += 6) { 323 if (!interior) { 324 idxBuf.put((short) i0++); 325 idxBuf.put((short) i1); 326 idxBuf.put((short) i2); 327 idxBuf.put((short) i1++); 328 idxBuf.put((short) i3++); 329 idxBuf.put((short) i2++); 330 } else { // inside view 331 idxBuf.put((short) i0++); 332 idxBuf.put((short) i2); 333 idxBuf.put((short) i1); 334 idxBuf.put((short) i1++); 335 idxBuf.put((short) i2++); 336 idxBuf.put((short) i3++); 337 } 338 } 339 } 340 341 // south pole triangles 342 for (int i = 0; i < radialSamples; i++, index += 3) { 343 if (!interior) { 344 idxBuf.put((short) i); 345 idxBuf.put((short) (vertCount - 2)); 346 idxBuf.put((short) (i + 1)); 347 } else { // inside view 348 idxBuf.put((short) i); 349 idxBuf.put((short) (i + 1)); 350 idxBuf.put((short) (vertCount - 2)); 351 } 352 } 353 354 // north pole triangles 355 int iOffset = (zSamples - 3) * (radialSamples + 1); 356 for (int i = 0; i < radialSamples; i++, index += 3) { 357 if (!interior) { 358 idxBuf.put((short) (i + iOffset)); 359 idxBuf.put((short) (i + 1 + iOffset)); 360 idxBuf.put((short) (vertCount - 1)); 361 } else { // inside view 362 idxBuf.put((short) (i + iOffset)); 363 idxBuf.put((short) (vertCount - 1)); 364 idxBuf.put((short) (i + 1 + iOffset)); 365 } 366 } 367 } 368 369 /** 370 * @param textureMode 371 * The textureMode to set. 372 */ 373 public void setTextureMode(TextureMode textureMode) { 374 this.textureMode = textureMode; 375 setGeometryData(); 376 } 377 378 /** 379 * Changes the information of the sphere into the given values. 380 * 381 * @param zSamples the number of zSamples of the sphere. 382 * @param radialSamples the number of radial samples of the sphere. 383 * @param radius the radius of the sphere. 384 */ 385 public void updateGeometry(int zSamples, int radialSamples, float radius) { 386 updateGeometry(zSamples, radialSamples, radius, false, false); 387 } 388 389 public void updateGeometry(int zSamples, int radialSamples, float radius, boolean useEvenSlices, boolean interior) { 390 this.zSamples = zSamples; 391 this.radialSamples = radialSamples; 392 this.radius = radius; 393 this.useEvenSlices = useEvenSlices; 394 this.interior = interior; 395 setGeometryData(); 396 setIndexData(); 397 } 398 399 public void read(JmeImporter e) throws IOException { 400 super.read(e); 401 InputCapsule capsule = e.getCapsule(this); 402 zSamples = capsule.readInt("zSamples", 0); 403 radialSamples = capsule.readInt("radialSamples", 0); 404 radius = capsule.readFloat("radius", 0); 405 useEvenSlices = capsule.readBoolean("useEvenSlices", false); 406 textureMode = capsule.readEnum("textureMode", TextureMode.class, TextureMode.Original); 407 interior = capsule.readBoolean("interior", false); 408 } 409 410 public void write(JmeExporter e) throws IOException { 411 super.write(e); 412 OutputCapsule capsule = e.getCapsule(this); 413 capsule.write(zSamples, "zSamples", 0); 414 capsule.write(radialSamples, "radialSamples", 0); 415 capsule.write(radius, "radius", 0); 416 capsule.write(useEvenSlices, "useEvenSlices", false); 417 capsule.write(textureMode, "textureMode", TextureMode.Original); 418 capsule.write(interior, "interior", false); 419 } 420 } 421