OSDN Git Service

f1817e8b8c8c263cdc33a63c12d5d91100d379e0
[mikumikustudio/MikuMikuStudio.git] / src / com / jme / scene / shape / GeoSphere.java
1 // Copyright © 2008 JMonkeyEngine, all rights reserved.
2 // See the accompanying LICENSE file for terms and conditions of use.
3 // $Id$
4 package com.jme.scene.shape;
5
6 import java.nio.FloatBuffer;
7 import java.nio.IntBuffer;
8
9 import com.jme.math.FastMath;
10 import com.jme.math.Vector3f;
11 import com.jme.scene.TexCoords;
12 import com.jme.scene.TriMesh;
13 import com.jme.util.geom.BufferUtils;
14
15 /**
16  * A polygon mesh approximating a sphere by recursive subdivision.
17  * <p>
18  * First approximation is an octahedron; each level of refinement increases the
19  * number of polygons by a factor of 4.
20  * <p>
21  * Shared vertices are not retained, so numerical errors may produce cracks
22  * between polygons at high subdivision levels.
23  * <p>
24  * TODO: texture co-ordinates could be nicer
25  * 
26  * @author John Leech - initial idea and original C implementation
27  * @author Irrisor - Java port and JME optimisation
28  * @version $Revision$, $Date$
29  */
30 public class GeoSphere extends TriMesh {
31
32     private static final long serialVersionUID = 1L;
33
34     private int numLevels;
35
36     private boolean usingIcosahedron = true;
37
38     /** <strong>NOT API:</strong> for internal use, do not call from user code. */
39     public GeoSphere() {}
40
41     /**
42      * @param name the name of the spatial
43      * @param ikosa
44      *            true to start with an 20 triangles, false to start with 8
45      *            triangles
46      * @param maxlevels
47      *            an integer >= 1 setting the recursion level
48      * @see jmetest.shape.TestGeoSphere
49      */
50     public GeoSphere(String name, boolean ikosa, int maxlevels) {
51         super(name);
52         updateGeometry(maxlevels, ikosa);
53     }
54
55     /**
56      * Compute the average of two vectors.
57      * 
58      * @param a
59      *            first vector
60      * @param b
61      *            second vector
62      * @return the average of two points
63      */
64     private Vector3f createMidpoint(Vector3f a, Vector3f b) {
65         return new Vector3f((a.x + b.x) * 0.5f, (a.y + b.y) * 0.5f,
66                 (a.z + b.z) * 0.5f);
67     }
68
69     public int getNumLevels() {
70         return numLevels;
71     }
72
73     /**
74      * TODO: radius is always 1
75      * 
76      * @return 1
77      */
78     public float getRadius() {
79         return 1;
80     }
81
82     public boolean isUsingIcosahedron() {
83         return usingIcosahedron;
84     }
85
86     private void put(Vector3f vec) {
87         FloatBuffer vertBuf = getVertexBuffer();
88         vertBuf.put(vec.x);
89         vertBuf.put(vec.y);
90         vertBuf.put(vec.z);
91
92         float length = vec.length();
93         FloatBuffer normBuf = getNormalBuffer();
94         float xNorm = vec.x / length;
95         normBuf.put(xNorm);
96         float yNorm = vec.y / length;
97         normBuf.put(yNorm);
98         float zNorm = vec.z / length;
99         normBuf.put(zNorm);
100
101         FloatBuffer texBuf = getTextureCoords(0).coords;
102         texBuf.put((FastMath.atan2(yNorm, xNorm) / (2 * FastMath.PI) + 1) % 1);
103         texBuf.put(zNorm / 2 + 0.5f);
104     }
105
106     private void updateGeometry(int maxLevels, boolean icosahedron) {
107         this.numLevels = maxLevels;
108         this.usingIcosahedron = icosahedron;
109         int initialTriangleCount = icosahedron ? 20 : 8;
110         int initialVertexCount = icosahedron ? 12 : 6;
111         // number of triangles = initialTriangleCount * 4^(maxlevels-1)
112         int triangleQuantity = initialTriangleCount << ((numLevels - 1) * 2);
113         setTriangleQuantity(triangleQuantity);
114         // number of vertBuf = (initialVertexCount + initialTriangleCount*4 +
115         // initialTriangleCount*4*4 + ...)
116         // = initialTriangleCount*(((4^maxlevels)-1)/(4-1)-1) +
117         // initialVertexCount
118         int vertQuantity = initialTriangleCount
119                 * (((1 << (numLevels * 2)) - 1) / (4 - 1) - 1)
120                 + initialVertexCount;
121         setVertexCount(vertQuantity);
122
123         FloatBuffer vertBuf = getVertexBuffer();
124         setVertexBuffer(vertBuf = BufferUtils.createVector3Buffer(vertBuf,
125                 vertQuantity));
126         setNormalBuffer(BufferUtils.createVector3Buffer(getNormalBuffer(),
127                 vertQuantity));
128         TexCoords textureCoords = getTextureCoords(0);
129                 setTextureCoords(new TexCoords(BufferUtils.createVector3Buffer(textureCoords != null ? textureCoords.coords : null,
130                 vertQuantity)), 0);
131
132         int pos = 0;
133
134         Triangle[] old;
135         if (icosahedron) {
136             int[] indices = new int[] {
137                     0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 1, 1, 10, 6, 2, 6,
138                     7, 3, 7, 8, 4, 8, 9, 5, 9, 10, 6, 2, 1, 7, 3, 2, 8, 4, 3, 9,
139                     5, 4, 10, 1, 5, 11, 7, 6, 11, 8, 7, 11, 9, 8, 11, 10, 9, 11,
140                     6, 10
141             };
142             float y = 0.4472f;
143             float a = 0.8944f;
144             float b = 0.2764f;
145             float c = 0.7236f;
146             float d = 0.8507f;
147             float e = 0.5257f;
148             put(new Vector3f(0, 1, 0));
149             put(new Vector3f(a, y, 0));
150             put(new Vector3f(b, y, -d));
151             put(new Vector3f(-c, y, -e));
152             put(new Vector3f(-c, y, e));
153             put(new Vector3f(b, y, d));
154             put(new Vector3f(c, -y, -e));
155             put(new Vector3f(-b, -y, -d));
156             put(new Vector3f(-a, -y, 0));
157             put(new Vector3f(-b, -y, d));
158             put(new Vector3f(c, -y, e));
159             put(new Vector3f(0, -1, 0));
160             pos += 12;
161             
162             Triangle[] ikosaedron = new Triangle[indices.length / 3];
163             for (int i = 0; i < ikosaedron.length; i++) {
164                 Triangle triangle = ikosaedron[i] = new Triangle();
165                 triangle.pt[0] = indices[i * 3];
166                 triangle.pt[1] = indices[i * 3 + 1];
167                 triangle.pt[2] = indices[i * 3 + 2];
168             }
169
170             old = ikosaedron;
171         } else {
172             /* Six equidistant points lying on the unit sphere */
173             final Vector3f XPLUS = new Vector3f(1, 0, 0); /* X */
174             final Vector3f XMIN = new Vector3f(-1, 0, 0); /* -X */
175             final Vector3f YPLUS = new Vector3f(0, 1, 0); /* Y */
176             final Vector3f YMIN = new Vector3f(0, -1, 0); /* -Y */
177             final Vector3f ZPLUS = new Vector3f(0, 0, 1); /* Z */
178             final Vector3f ZMIN = new Vector3f(0, 0, -1); /* -Z */
179
180             int xplus = pos++;
181             put(XPLUS);
182             int xmin = pos++;
183             put(XMIN);
184             int yplus = pos++;
185             put(YPLUS);
186             int ymin = pos++;
187             put(YMIN);
188             int zplus = pos++;
189             put(ZPLUS);
190             int zmin = pos++;
191             put(ZMIN);
192
193             Triangle[] octahedron = new Triangle[] {
194                     new Triangle(yplus, zplus, xplus),
195                     new Triangle(xmin, zplus, yplus),
196                     new Triangle(ymin, zplus, xmin),
197                     new Triangle(xplus, zplus, ymin),
198                     new Triangle(zmin, yplus, xplus),
199                     new Triangle(zmin, xmin, yplus),
200                     new Triangle(zmin, ymin, xmin),
201                     new Triangle(zmin, xplus, ymin) };
202
203             old = octahedron;
204         }
205
206         Vector3f pt0 = new Vector3f();
207         Vector3f pt1 = new Vector3f();
208         Vector3f pt2 = new Vector3f();
209
210         /* Subdivide each starting triangle (maxlevels - 1) times */
211         for (int level = 1; level < numLevels; level++) {
212             /* Allocate a next triangle[] */
213             Triangle[] next = new Triangle[old.length * 4];
214             for (int i = 0; i < next.length; i++) {
215                 next[i] = new Triangle();
216             }
217
218             /*
219              * Subdivide each polygon in the old approximation and normalize the
220              * next points thus generated to lie on the surface of the unit
221              * sphere. Each input triangle with vertBuf labelled [0,1,2] as
222              * shown below will be turned into four next triangles:
223              * 
224              * Make next points
225              *   a = (0+2)/2
226              *   b = (0+1)/2
227              *   c = (1+2)/2
228              *   
229              * 1   /\   Normalize a, b, c
230              *    /  \
231              * b /____\ c
232              * 
233              * Construct next triangles
234              * 
235              *    /\    /\   [0,b,a] 
236              *   /  \  /  \  [b,1,c]
237              *  /____\/____\ [a,b,c]
238              *  0 a 2 [a,c,2]
239              */
240             for (int i = 0; i < old.length; i++) {
241                 int newi = i * 4;
242                 Triangle oldt = old[i], newt = next[newi];
243
244                 BufferUtils.populateFromBuffer(pt0, vertBuf, oldt.pt[0]);
245                 BufferUtils.populateFromBuffer(pt1, vertBuf, oldt.pt[1]);
246                 BufferUtils.populateFromBuffer(pt2, vertBuf, oldt.pt[2]);
247                 Vector3f av = createMidpoint(pt0, pt2).normalizeLocal();
248                 Vector3f bv = createMidpoint(pt0, pt1).normalizeLocal();
249                 Vector3f cv = createMidpoint(pt1, pt2).normalizeLocal();
250                 int a = pos++;
251                 put(av);
252                 int b = pos++;
253                 put(bv);
254                 int c = pos++;
255                 put(cv);
256
257                 newt.pt[0] = oldt.pt[0];
258                 newt.pt[1] = b;
259                 newt.pt[2] = a;
260                 newt = next[++newi];
261
262                 newt.pt[0] = b;
263                 newt.pt[1] = oldt.pt[1];
264                 newt.pt[2] = c;
265                 newt = next[++newi];
266
267                 newt.pt[0] = a;
268                 newt.pt[1] = b;
269                 newt.pt[2] = c;
270                 newt = next[++newi];
271
272                 newt.pt[0] = a;
273                 newt.pt[1] = c;
274                 newt.pt[2] = oldt.pt[2];
275             }
276
277             /* Continue subdividing next triangles */
278             old = next;
279         }
280
281         IntBuffer indexBuffer = BufferUtils
282                 .createIntBuffer(triangleQuantity * 3);
283         setIndexBuffer(indexBuffer);
284
285         for (Triangle triangle : old) {
286             for (int aPt : triangle.pt) {
287                 indexBuffer.put(aPt);
288             }
289         }
290     }
291
292     static class Triangle {
293         int[] pt = new int[3]; /* Vertices of triangle */
294
295         public Triangle() {}
296
297         public Triangle(int pt0, int pt1, int pt2) {
298             pt[0] = pt0;
299             pt[1] = pt1;
300             pt[2] = pt2;
301         }
302     }
303
304 }