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
33// $Id: Torus.java 4131 2009-03-19 20:15:28Z blaine.dev $
34package com.jme3.scene.shape;
35
36import com.jme3.export.InputCapsule;
37import com.jme3.export.JmeExporter;
38import com.jme3.export.JmeImporter;
39import com.jme3.export.OutputCapsule;
40import com.jme3.math.FastMath;
41import com.jme3.math.Vector3f;
42import com.jme3.scene.Mesh;
43import com.jme3.scene.VertexBuffer.Type;
44import com.jme3.util.BufferUtils;
45import java.io.IOException;
46import java.nio.FloatBuffer;
47import java.nio.ShortBuffer;
48
49/**
50 * An ordinary (single holed) torus.
51 * <p>
52 * The center is by default the origin.
53 *
54 * @author Mark Powell
55 * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
56 */
57public class Torus extends Mesh {
58
59    private int circleSamples;
60
61    private int radialSamples;
62
63    private float innerRadius;
64
65    private float outerRadius;
66
67    public Torus() {
68    }
69
70    /**
71     * Constructs a new Torus. Center is the origin, but the Torus may be
72     * transformed.
73     *
74     * @param circleSamples
75     *            The number of samples along the circles.
76     * @param radialSamples
77     *            The number of samples along the radial.
78     * @param innerRadius
79     *            The radius of the inner begining of the Torus.
80     * @param outerRadius
81     *            The radius of the outter end of the Torus.
82     */
83    public Torus(int circleSamples, int radialSamples,
84            float innerRadius, float outerRadius) {
85        super();
86        updateGeometry(circleSamples, radialSamples, innerRadius, outerRadius);
87    }
88
89    public int getCircleSamples() {
90        return circleSamples;
91    }
92
93    public float getInnerRadius() {
94        return innerRadius;
95    }
96
97    public float getOuterRadius() {
98        return outerRadius;
99    }
100
101    public int getRadialSamples() {
102        return radialSamples;
103    }
104
105    @Override
106    public void read(JmeImporter e) throws IOException {
107        super.read(e);
108        InputCapsule capsule = e.getCapsule(this);
109        circleSamples = capsule.readInt("circleSamples", 0);
110        radialSamples = capsule.readInt("radialSamples", 0);
111        innerRadius = capsule.readFloat("innerRadius", 0);
112        outerRadius = capsule.readFloat("outerRaidus", 0);
113    }
114
115    private void setGeometryData() {
116        // allocate vertices
117        int vertCount = (circleSamples + 1) * (radialSamples + 1);
118        FloatBuffer fpb = BufferUtils.createVector3Buffer(vertCount);
119        setBuffer(Type.Position, 3, fpb);
120
121        // allocate normals if requested
122        FloatBuffer fnb = BufferUtils.createVector3Buffer(vertCount);
123        setBuffer(Type.Normal, 3, fnb);
124
125        // allocate texture coordinates
126        FloatBuffer ftb = BufferUtils.createVector2Buffer(vertCount);
127        setBuffer(Type.TexCoord, 2, ftb);
128
129        // generate geometry
130        float inverseCircleSamples = 1.0f / circleSamples;
131        float inverseRadialSamples = 1.0f / radialSamples;
132        int i = 0;
133        // generate the cylinder itself
134        Vector3f radialAxis = new Vector3f(), torusMiddle = new Vector3f(), tempNormal = new Vector3f();
135        for (int circleCount = 0; circleCount < circleSamples; circleCount++) {
136            // compute center point on torus circle at specified angle
137            float circleFraction = circleCount * inverseCircleSamples;
138            float theta = FastMath.TWO_PI * circleFraction;
139            float cosTheta = FastMath.cos(theta);
140            float sinTheta = FastMath.sin(theta);
141            radialAxis.set(cosTheta, sinTheta, 0);
142            radialAxis.mult(outerRadius, torusMiddle);
143
144            // compute slice vertices with duplication at end point
145            int iSave = i;
146            for (int radialCount = 0; radialCount < radialSamples; radialCount++) {
147                float radialFraction = radialCount * inverseRadialSamples;
148                // in [0,1)
149                float phi = FastMath.TWO_PI * radialFraction;
150                float cosPhi = FastMath.cos(phi);
151                float sinPhi = FastMath.sin(phi);
152                tempNormal.set(radialAxis).multLocal(cosPhi);
153                tempNormal.z += sinPhi;
154                fnb.put(tempNormal.x).put(tempNormal.y).put(
155                        tempNormal.z);
156
157                tempNormal.multLocal(innerRadius).addLocal(torusMiddle);
158                fpb.put(tempNormal.x).put(tempNormal.y).put(
159                        tempNormal.z);
160
161                ftb.put(radialFraction).put(circleFraction);
162                i++;
163            }
164
165            BufferUtils.copyInternalVector3(fpb, iSave, i);
166            BufferUtils.copyInternalVector3(fnb, iSave, i);
167
168            ftb.put(1.0f).put(circleFraction);
169
170            i++;
171        }
172
173        // duplicate the cylinder ends to form a torus
174        for (int iR = 0; iR <= radialSamples; iR++, i++) {
175            BufferUtils.copyInternalVector3(fpb, iR, i);
176            BufferUtils.copyInternalVector3(fnb, iR, i);
177            BufferUtils.copyInternalVector2(ftb, iR, i);
178            ftb.put(i * 2 + 1, 1.0f);
179        }
180    }
181
182    private void setIndexData() {
183        // allocate connectivity
184        int triCount = 2 * circleSamples * radialSamples;
185
186        ShortBuffer sib = BufferUtils.createShortBuffer(3 * triCount);
187        setBuffer(Type.Index, 3, sib);
188
189        int i;
190        // generate connectivity
191        int connectionStart = 0;
192        int index = 0;
193        for (int circleCount = 0; circleCount < circleSamples; circleCount++) {
194            int i0 = connectionStart;
195            int i1 = i0 + 1;
196            connectionStart += radialSamples + 1;
197            int i2 = connectionStart;
198            int i3 = i2 + 1;
199            for (i = 0; i < radialSamples; i++, index += 6) {
200//                if (true) {
201                    sib.put((short)i0++);
202                    sib.put((short)i2);
203                    sib.put((short)i1);
204                    sib.put((short)i1++);
205                    sib.put((short)i2++);
206                    sib.put((short)i3++);
207
208//                    getIndexBuffer().put(i0++);
209//                    getIndexBuffer().put(i2);
210//                    getIndexBuffer().put(i1);
211//                    getIndexBuffer().put(i1++);
212//                    getIndexBuffer().put(i2++);
213//                    getIndexBuffer().put(i3++);
214//                } else {
215//                    getIndexBuffer().put(i0++);
216//                    getIndexBuffer().put(i1);
217//                    getIndexBuffer().put(i2);
218//                    getIndexBuffer().put(i1++);
219//                    getIndexBuffer().put(i3++);
220//                    getIndexBuffer().put(i2++);
221//                }
222            }
223        }
224    }
225
226    /**
227     * Rebuilds this torus based on a new set of parameters.
228     *
229     * @param circleSamples the number of samples along the circles.
230     * @param radialSamples the number of samples along the radial.
231     * @param innerRadius the radius of the inner begining of the Torus.
232     * @param outerRadius the radius of the outter end of the Torus.
233     */
234    public void updateGeometry(int circleSamples, int radialSamples, float innerRadius, float outerRadius) {
235        this.circleSamples = circleSamples;
236        this.radialSamples = radialSamples;
237        this.innerRadius = innerRadius;
238        this.outerRadius = outerRadius;
239        setGeometryData();
240        setIndexData();
241        updateBound();
242        updateCounts();
243    }
244
245    @Override
246    public void write(JmeExporter e) throws IOException {
247        super.write(e);
248        OutputCapsule capsule = e.getCapsule(this);
249        capsule.write(circleSamples, "circleSamples", 0);
250        capsule.write(radialSamples, "radialSamples", 0);
251        capsule.write(innerRadius, "innerRadius", 0);
252        capsule.write(outerRadius, "outerRadius", 0);
253    }
254
255}