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 */
32package com.jme3.scene.shape;
33
34import com.jme3.math.Spline;
35import com.jme3.math.Vector3f;
36import com.jme3.scene.Mesh;
37import com.jme3.scene.VertexBuffer;
38import java.util.Iterator;
39import java.util.List;
40
41/**
42 * A <code>Curve</code> is a visual, line-based representation of a {@link Spline}.
43 * The underlying Spline will be sampled N times where N is the number of
44 * segments as specified in the constructor. Each segment will represent
45 * one line in the generated mesh.
46 *
47 * @author Nehon
48 */
49public class Curve extends Mesh {
50
51    private Spline spline;
52    private Vector3f temp = new Vector3f();
53
54    /**
55     * Serialization only. Do not use.
56     */
57    public Curve(){
58    }
59
60    /**
61     * Create a curve mesh.
62     * Use a CatmullRom spline model that does not cycle.
63     *
64     * @param controlPoints the control points to use to create this curve
65     * @param nbSubSegments the number of subsegments between the control points
66     */
67    public Curve(Vector3f[] controlPoints, int nbSubSegments) {
68        this(new Spline(Spline.SplineType.CatmullRom, controlPoints, 10, false), nbSubSegments);
69    }
70
71    /**
72     * Create a curve mesh from a Spline
73     *
74     * @param spline the spline to use
75     * @param nbSubSegments the number of subsegments between the control points
76     */
77    public Curve(Spline spline, int nbSubSegments) {
78        super();
79        this.spline = spline;
80        switch (spline.getType()) {
81            case CatmullRom:
82            	this.createCatmullRomMesh(nbSubSegments);
83                break;
84            case Bezier:
85            	this.createBezierMesh(nbSubSegments);
86            	break;
87            case Nurb:
88            	this.createNurbMesh(nbSubSegments);
89            	break;
90            case Linear:
91            default:
92            	this.createLinearMesh();
93                break;
94        }
95    }
96
97    private void createCatmullRomMesh(int nbSubSegments) {
98        float[] array = new float[((spline.getControlPoints().size() - 1) * nbSubSegments + 1) * 3];
99        short[] indices = new short[(spline.getControlPoints().size() - 1) * nbSubSegments * 2];
100        int i = 0;
101        int cptCP = 0;
102        for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
103            Vector3f vector3f = it.next();
104            array[i] = vector3f.x;
105            i++;
106            array[i] = vector3f.y;
107            i++;
108            array[i] = vector3f.z;
109            i++;
110            if (it.hasNext()) {
111                for (int j = 1; j < nbSubSegments; j++) {
112                    spline.interpolate((float) j / nbSubSegments, cptCP, temp);
113                    array[i] = temp.getX();
114                    i++;
115                    array[i] = temp.getY();
116                    i++;
117                    array[i] = temp.getZ();
118                    i++;
119                }
120            }
121            cptCP++;
122        }
123
124        i = 0;
125        int k = 0;
126        for (int j = 0; j < (spline.getControlPoints().size() - 1) * nbSubSegments; j++) {
127            k = j;
128            indices[i] = (short) k;
129            i++;
130            k++;
131            indices[i] = (short) k;
132            i++;
133        }
134
135        this.setMode(Mesh.Mode.Lines);
136        this.setBuffer(VertexBuffer.Type.Position, 3, array);
137        this.setBuffer(VertexBuffer.Type.Index, 2, indices);//(spline.getControlPoints().size() - 1) * nbSubSegments * 2
138        this.updateBound();
139        this.updateCounts();
140    }
141
142    /**
143	 * This method creates the Bezier path for this curve.
144	 *
145	 * @param nbSubSegments
146	 *            amount of subsegments between position control points
147	 */
148	private void createBezierMesh(int nbSubSegments) {
149		if(nbSubSegments==0) {
150			nbSubSegments = 1;
151		}
152		int centerPointsAmount = (spline.getControlPoints().size() + 2) / 3;
153
154		//calculating vertices
155		float[] array = new float[((centerPointsAmount - 1) * nbSubSegments + 1) * 3];
156		int currentControlPoint = 0;
157		List<Vector3f> controlPoints = spline.getControlPoints();
158		int lineIndex = 0;
159		for (int i = 0; i < centerPointsAmount - 1; ++i) {
160			Vector3f vector3f = controlPoints.get(currentControlPoint);
161			array[lineIndex++] = vector3f.x;
162			array[lineIndex++] = vector3f.y;
163			array[lineIndex++] = vector3f.z;
164			for (int j = 1; j < nbSubSegments; ++j) {
165				spline.interpolate((float) j / nbSubSegments, currentControlPoint, temp);
166				array[lineIndex++] = temp.getX();
167				array[lineIndex++] = temp.getY();
168				array[lineIndex++] = temp.getZ();
169			}
170			currentControlPoint += 3;
171		}
172		Vector3f vector3f = controlPoints.get(currentControlPoint);
173		array[lineIndex++] = vector3f.x;
174		array[lineIndex++] = vector3f.y;
175		array[lineIndex++] = vector3f.z;
176
177		//calculating indexes
178		int i = 0, k = 0;
179		short[] indices = new short[(centerPointsAmount - 1) * nbSubSegments << 1];
180		for (int j = 0; j < (centerPointsAmount - 1) * nbSubSegments; ++j) {
181			k = j;
182			indices[i++] = (short) k;
183			++k;
184			indices[i++] = (short) k;
185		}
186
187		this.setMode(Mesh.Mode.Lines);
188		this.setBuffer(VertexBuffer.Type.Position, 3, array);
189		this.setBuffer(VertexBuffer.Type.Index, 2, indices);
190		this.updateBound();
191		this.updateCounts();
192	}
193
194	/**
195	 * This method creates the Nurb path for this curve.
196	 * @param nbSubSegments
197	 *            amount of subsegments between position control points
198	 */
199	private void createNurbMesh(int nbSubSegments) {
200		float minKnot = spline.getMinNurbKnot();
201		float maxKnot = spline.getMaxNurbKnot();
202		float deltaU = (maxKnot - minKnot)/nbSubSegments;
203
204		float[] array = new float[(nbSubSegments + 1) * 3];
205
206		float u = minKnot;
207		Vector3f interpolationResult = new Vector3f();
208		for(int i=0;i<array.length;i+=3) {
209			spline.interpolate(u, 0, interpolationResult);
210			array[i] = interpolationResult.x;
211			array[i + 1] = interpolationResult.y;
212			array[i + 2] = interpolationResult.z;
213			u += deltaU;
214		}
215
216		//calculating indexes
217		int i = 0;
218		short[] indices = new short[nbSubSegments << 1];
219		for (int j = 0; j < nbSubSegments; ++j) {
220			indices[i++] = (short) j;
221			indices[i++] = (short) (j + 1);
222		}
223
224		this.setMode(Mesh.Mode.Lines);
225		this.setBuffer(VertexBuffer.Type.Position, 3, array);
226		this.setBuffer(VertexBuffer.Type.Index, 2, indices);
227		this.updateBound();
228		this.updateCounts();
229	}
230
231    private void createLinearMesh() {
232        float[] array = new float[spline.getControlPoints().size() * 3];
233        short[] indices = new short[(spline.getControlPoints().size() - 1) * 2];
234        int i = 0;
235        int cpt = 0;
236        int k = 0;
237        int j = 0;
238        for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
239            Vector3f vector3f = it.next();
240            array[i] = vector3f.getX();
241            i++;
242            array[i] = vector3f.getY();
243            i++;
244            array[i] = vector3f.getZ();
245            i++;
246            if (it.hasNext()) {
247                k = j;
248                indices[cpt] = (short) k;
249                cpt++;
250                k++;
251                indices[cpt] = (short) k;
252                cpt++;
253                j++;
254            }
255        }
256
257        this.setMode(Mesh.Mode.Lines);
258        this.setBuffer(VertexBuffer.Type.Position, 3, array);
259        this.setBuffer(VertexBuffer.Type.Index, 2, indices);
260        this.updateBound();
261        this.updateCounts();
262    }
263
264    /**
265     * This method returns the length of the curve.
266     * @return the length of the curve
267     */
268    public float getLength() {
269    	return spline.getTotalLength();
270    }
271}
272