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: Dome.java 4131 2009-03-19 20:15:28Z blaine.dev $
33package com.jme3.scene.shape;
34
35import com.jme3.export.InputCapsule;
36import com.jme3.export.JmeExporter;
37import com.jme3.export.JmeImporter;
38import com.jme3.export.OutputCapsule;
39import com.jme3.math.FastMath;
40import com.jme3.math.Vector3f;
41import com.jme3.scene.Mesh;
42import com.jme3.scene.VertexBuffer.Type;
43import com.jme3.util.BufferUtils;
44import com.jme3.util.TempVars;
45import java.io.IOException;
46import java.nio.FloatBuffer;
47import java.nio.ShortBuffer;
48
49/**
50 * A hemisphere.
51 *
52 * @author Peter Andersson
53 * @author Joshua Slack (Original sphere code that was adapted)
54 * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
55 */
56public class Dome extends Mesh {
57
58    private int planes;
59    private int radialSamples;
60    /** The radius of the dome */
61    private float radius;
62    /** The center of the dome */
63    private Vector3f center;
64    private boolean insideView = true;
65
66    /**
67     * Serialization only. Do not use.
68     */
69    public Dome() {
70    }
71
72    /**
73     * Constructs a dome for use as a SkyDome. The SkyDome is centered at the origin
74     * and only visible from the inside.
75     * @param planes
76     *            The number of planes along the Z-axis. Must be >= 2.
77     *            Influences how round the arch of the dome is.
78     * @param radialSamples
79     *            The number of samples along the radial.
80     *            Influences how round the base of the dome is.
81     * @param radius
82     *            Radius of the dome.
83     * @see #Dome(java.lang.String, com.jme.math.Vector3f, int, int, float)
84     */
85    public Dome(int planes, int radialSamples, float radius) {
86        this(new Vector3f(0, 0, 0), planes, radialSamples, radius);
87    }
88
89    /**
90     * Constructs a dome visible from the inside, e.g. for use as a SkyDome.
91     * All geometry data buffers are updated automatically. <br>
92     * For a cone, set planes=2. For a pyramid, set radialSamples=4 and planes=2.
93     * Increasing planes and radialSamples increase the quality of the dome.
94     *
95     * @param center
96     *            Center of the dome.
97     * @param planes
98     *            The number of planes along the Z-axis. Must be >= 2.
99     *            Influences how round the arch of the dome is.
100     * @param radialSamples
101     *            The number of samples along the radial.
102     *            Influences how round the base of the dome is.
103     * @param radius
104     *            The radius of the dome.
105     */
106    public Dome(Vector3f center, int planes, int radialSamples,
107            float radius) {
108        super();
109        updateGeometry(center, planes, radialSamples, radius, true);
110    }
111
112    /**
113     * Constructs a dome. Use this constructor for half-sphere, pyramids, or cones.
114     * All geometry data buffers are updated automatically. <br>
115     * For a cone, set planes=2. For a pyramid, set radialSamples=4 and planes=2.
116     * Setting higher values for planes and radialSamples increases
117     * the quality of the half-sphere.
118     *
119     * @param center
120     *            Center of the dome.
121     * @param planes
122     *            The number of planes along the Z-axis. Must be >= 2.
123     *            Influences how round the arch of the dome is.
124     * @param radialSamples
125     *            The number of samples along the radial.
126     *            Influences how round the base of the dome is.
127     * @param radius
128     *            The radius of the dome.
129     * @param insideView
130     *            If true, the dome is only visible from the inside, like a SkyDome.
131     *            If false, the dome is only visible from the outside.
132     */
133    public Dome(Vector3f center, int planes, int radialSamples,
134            float radius, boolean insideView) {
135        super();
136        updateGeometry(center, planes, radialSamples, radius, insideView);
137    }
138
139    public Vector3f getCenter() {
140        return center;
141    }
142
143    /**
144     * Get the number of planar segments along the z-axis of the dome.
145     */
146    public int getPlanes() {
147        return planes;
148    }
149
150    /**
151     * Get the number of samples radially around the main axis of the dome.
152     */
153    public int getRadialSamples() {
154        return radialSamples;
155    }
156
157    /**
158     * Get the radius of the dome.
159     */
160    public float getRadius() {
161        return radius;
162    }
163
164    /**
165     * Are the triangles connected in such a way as to present a view out from the dome or not.
166     */
167    public boolean isInsideView() {
168        return insideView;
169    }
170
171    /**
172     * Rebuilds the dome with a new set of parameters.
173     *
174     * @param center the new center of the dome.
175     * @param planes the number of planes along the Z-axis.
176     * @param radialSamples the new number of radial samples of the dome.
177     * @param radius the new radius of the dome.
178     * @param insideView should the dome be set up to be viewed from the inside looking out.
179     */
180    public void updateGeometry(Vector3f center, int planes,
181            int radialSamples, float radius, boolean insideView) {
182        this.insideView = insideView;
183        this.center = center != null ? center : new Vector3f(0, 0, 0);
184        this.planes = planes;
185        this.radialSamples = radialSamples;
186        this.radius = radius;
187
188        int vertCount = ((planes - 1) * (radialSamples + 1)) + 1;
189
190        // Allocate vertices, allocating one extra in each radial to get the
191        // correct texture coordinates
192//        setVertexCount();
193//        setVertexBuffer(createVector3Buffer(getVertexCount()));
194
195        // allocate normals
196//        setNormalBuffer(createVector3Buffer(getVertexCount()));
197
198        // allocate texture coordinates
199//        getTextureCoords().set(0, new TexCoords(createVector2Buffer(getVertexCount())));
200
201        FloatBuffer vb = BufferUtils.createVector3Buffer(vertCount);
202        FloatBuffer nb = BufferUtils.createVector3Buffer(vertCount);
203        FloatBuffer tb = BufferUtils.createVector2Buffer(vertCount);
204        setBuffer(Type.Position, 3, vb);
205        setBuffer(Type.Normal, 3, nb);
206        setBuffer(Type.TexCoord, 2, tb);
207
208        // generate geometry
209        float fInvRS = 1.0f / radialSamples;
210        float fYFactor = 1.0f / (planes - 1);
211
212        // Generate points on the unit circle to be used in computing the mesh
213        // points on a dome slice.
214        float[] afSin = new float[(radialSamples)];
215        float[] afCos = new float[(radialSamples)];
216        for (int iR = 0; iR < radialSamples; iR++) {
217            float fAngle = FastMath.TWO_PI * fInvRS * iR;
218            afCos[iR] = FastMath.cos(fAngle);
219            afSin[iR] = FastMath.sin(fAngle);
220        }
221
222        TempVars vars = TempVars.get();
223        Vector3f tempVc = vars.vect3;
224        Vector3f tempVb = vars.vect2;
225        Vector3f tempVa = vars.vect1;
226
227        // generate the dome itself
228        int i = 0;
229        for (int iY = 0; iY < (planes - 1); iY++, i++) {
230            float fYFraction = fYFactor * iY; // in (0,1)
231            float fY = radius * fYFraction;
232            // compute center of slice
233            Vector3f kSliceCenter = tempVb.set(center);
234            kSliceCenter.y += fY;
235
236            // compute radius of slice
237            float fSliceRadius = FastMath.sqrt(FastMath.abs(radius * radius - fY * fY));
238
239            // compute slice vertices
240            Vector3f kNormal;
241            int iSave = i;
242            for (int iR = 0; iR < radialSamples; iR++, i++) {
243                float fRadialFraction = iR * fInvRS; // in [0,1)
244                Vector3f kRadial = tempVc.set(afCos[iR], 0, afSin[iR]);
245                kRadial.mult(fSliceRadius, tempVa);
246                vb.put(kSliceCenter.x + tempVa.x).put(
247                        kSliceCenter.y + tempVa.y).put(
248                        kSliceCenter.z + tempVa.z);
249
250                BufferUtils.populateFromBuffer(tempVa, vb, i);
251                kNormal = tempVa.subtractLocal(center);
252                kNormal.normalizeLocal();
253                if (insideView) {
254                    nb.put(kNormal.x).put(kNormal.y).put(kNormal.z);
255                } else {
256                    nb.put(-kNormal.x).put(-kNormal.y).put(-kNormal.z);
257                }
258
259                tb.put(fRadialFraction).put(fYFraction);
260            }
261            BufferUtils.copyInternalVector3(vb, iSave, i);
262            BufferUtils.copyInternalVector3(nb, iSave, i);
263            tb.put(1.0f).put(fYFraction);
264        }
265
266        vars.release();
267
268        // pole
269        vb.put(center.x).put(center.y + radius).put(center.z);
270        nb.put(0).put(insideView ? 1 : -1).put(0);
271        tb.put(0.5f).put(1.0f);
272
273        // allocate connectivity
274        int triCount = (planes - 2) * radialSamples * 2 + radialSamples;
275        ShortBuffer ib = BufferUtils.createShortBuffer(3 * triCount);
276        setBuffer(Type.Index, 3, ib);
277
278        // generate connectivity
279        int index = 0;
280        // Generate only for middle planes
281        for (int plane = 1; plane < (planes - 1); plane++) {
282            int bottomPlaneStart = ((plane - 1) * (radialSamples + 1));
283            int topPlaneStart = (plane * (radialSamples + 1));
284            for (int sample = 0; sample < radialSamples; sample++, index += 6) {
285                if (insideView){
286                    ib.put((short) (bottomPlaneStart + sample));
287                    ib.put((short) (bottomPlaneStart + sample + 1));
288                    ib.put((short) (topPlaneStart + sample));
289                    ib.put((short) (bottomPlaneStart + sample + 1));
290                    ib.put((short) (topPlaneStart + sample + 1));
291                    ib.put((short) (topPlaneStart + sample));
292                }else{
293                    ib.put((short) (bottomPlaneStart + sample));
294                    ib.put((short) (topPlaneStart + sample));
295                    ib.put((short) (bottomPlaneStart + sample + 1));
296                    ib.put((short) (bottomPlaneStart + sample + 1));
297                    ib.put((short) (topPlaneStart + sample));
298                    ib.put((short) (topPlaneStart + sample + 1));
299                }
300            }
301        }
302
303        // pole triangles
304        int bottomPlaneStart = (planes - 2) * (radialSamples + 1);
305        for (int samples = 0; samples < radialSamples; samples++, index += 3) {
306            if (insideView){
307                ib.put((short) (bottomPlaneStart + samples));
308                ib.put((short) (bottomPlaneStart + samples + 1));
309                ib.put((short) (vertCount - 1));
310            }else{
311                ib.put((short) (bottomPlaneStart + samples));
312                ib.put((short) (vertCount - 1));
313                ib.put((short) (bottomPlaneStart + samples + 1));
314            }
315        }
316
317        updateBound();
318    }
319
320    @Override
321    public void read(JmeImporter e) throws IOException {
322        super.read(e);
323        InputCapsule capsule = e.getCapsule(this);
324        planes = capsule.readInt("planes", 0);
325        radialSamples = capsule.readInt("radialSamples", 0);
326        radius = capsule.readFloat("radius", 0);
327        center = (Vector3f) capsule.readSavable("center", Vector3f.ZERO.clone());
328    }
329
330    @Override
331    public void write(JmeExporter e) throws IOException {
332        super.write(e);
333        OutputCapsule capsule = e.getCapsule(this);
334        capsule.write(planes, "planes", 0);
335        capsule.write(radialSamples, "radialSamples", 0);
336        capsule.write(radius, "radius", 0);
337        capsule.write(center, "center", Vector3f.ZERO);
338    }
339}