• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.jme3.terrain;
33 
34 import com.jme3.export.*;
35 import com.jme3.math.Vector2f;
36 import com.jme3.math.Vector3f;
37 import com.jme3.scene.Mesh;
38 import com.jme3.scene.VertexBuffer.Type;
39 import com.jme3.util.BufferUtils;
40 import java.io.IOException;
41 import java.nio.BufferUnderflowException;
42 import java.nio.FloatBuffer;
43 import java.nio.IntBuffer;
44 
45 /**
46  * Constructs heightfields to be used in Terrain.
47  */
48 public class GeoMap implements Savable {
49 
50     protected float[] hdata;
51     protected int width, height, maxval;
52 
GeoMap()53     public GeoMap() {}
54 
55     @Deprecated
GeoMap(FloatBuffer heightData, int width, int height, int maxval)56     public GeoMap(FloatBuffer heightData, int width, int height, int maxval){
57         hdata = new float[heightData.limit()];
58         heightData.get(hdata);
59         this.width = width;
60         this.height = height;
61         this.maxval = maxval;
62     }
63 
GeoMap(float[] heightData, int width, int height, int maxval)64     public GeoMap(float[] heightData, int width, int height, int maxval){
65         this.hdata = heightData;
66         this.width = width;
67         this.height = height;
68         this.maxval = maxval;
69     }
70 
71     @Deprecated
getHeightData()72     public FloatBuffer getHeightData(){
73         if (!isLoaded())
74             return null;
75         return BufferUtils.createFloatBuffer(hdata);
76     }
77 
getHeightArray()78     public float[] getHeightArray(){
79         if (!isLoaded())
80             return null;
81         return hdata;
82     }
83 
84     /**
85      * @return The maximum possible value that <code>getValue()</code> can
86      * return. Mostly depends on the source data format (byte, short, int, etc).
87      */
getMaximumValue()88     public int getMaximumValue(){
89         return maxval;
90     }
91 
92     /**
93      * Returns the height value for a given point.
94      *
95      * MUST return the same value as getHeight(y*getWidth()+x)
96      *
97      * @param x the X coordinate
98      * @param y the Y coordinate
99      * @returns an arbitrary height looked up from the heightmap
100      *
101      * @throws NullPointerException If isLoaded() is false
102      */
getValue(int x, int y)103     public float getValue(int x, int y) {
104         return hdata[y*width+x];
105     }
106 
107     /**
108      * Returns the height value at the given index.
109      *
110      * zero index is top left of map,
111      * getWidth()*getHeight() index is lower right
112      *
113      * @param i The index
114      * @returns an arbitrary height looked up from the heightmap
115      *
116      * @throws NullPointerException If isLoaded() is false
117      */
getValue(int i)118     public float getValue(int i) {
119         return hdata[i];
120     }
121 
122 
123     /**
124      * Returns the width of this Geomap
125      *
126      * @returns the width of this Geomap
127      */
getWidth()128     public int getWidth() {
129         return width;
130     }
131 
132     /**
133      * Returns the height of this Geomap
134      *
135      * @returns the height of this Geomap
136      */
getHeight()137     public int getHeight() {
138         return height;
139     }
140 
141     /**
142      * Returns true if the Geomap data is loaded in memory
143      * If false, then the data is unavailable- must be loaded with load()
144      * before the methods getHeight/getNormal can be used
145      *
146      * @returns wether the geomap data is loaded in system memory
147      */
isLoaded()148     public boolean isLoaded() {
149         return true;
150     }
151 
152     /**
153      * Creates a normal array from the normal data in this Geomap
154      *
155      * @param store A preallocated FloatBuffer where to store the data (optional), size must be >= getWidth()*getHeight()*3
156      * @returns store, or a new FloatBuffer if store is null
157      *
158      * @throws NullPointerException If isLoaded() or hasNormalmap() is false
159      */
writeNormalArray(FloatBuffer store, Vector3f scale)160     public FloatBuffer writeNormalArray(FloatBuffer store, Vector3f scale) {
161 
162         if (store!=null){
163             if (store.remaining() < getWidth()*getHeight()*3)
164                 throw new BufferUnderflowException();
165         }else{
166             store = BufferUtils.createFloatBuffer(getWidth()*getHeight()*3);
167         }
168         store.rewind();
169 
170         Vector3f oppositePoint = new Vector3f();
171         Vector3f adjacentPoint = new Vector3f();
172         Vector3f rootPoint = new Vector3f();
173         Vector3f tempNorm = new Vector3f();
174         int normalIndex = 0;
175 
176         for (int y = 0; y < getHeight(); y++) {
177             for (int x = 0; x < getWidth(); x++) {
178                 rootPoint.set(x, getValue(x,y), y);
179                 if (y == getHeight() - 1) {
180                     if (x == getWidth() - 1) {  // case #4 : last row, last col
181                         // left cross up
182 //                            adj = normalIndex - getWidth();
183 //                            opp = normalIndex - 1;
184                         adjacentPoint.set(x, getValue(x,y-1), y-1);
185                         oppositePoint.set(x-1, getValue(x-1, y), y);
186                     } else {                    // case #3 : last row, except for last col
187                         // right cross up
188 //                            adj = normalIndex + 1;
189 //                            opp = normalIndex - getWidth();
190                         adjacentPoint.set(x+1, getValue(x+1,y), y);
191                         oppositePoint.set(x, getValue(x,y-1), y-1);
192                     }
193                 } else {
194                     if (x == getWidth() - 1) {  // case #2 : last column except for last row
195                         // left cross down
196                         adjacentPoint.set(x-1, getValue(x-1,y), y);
197                         oppositePoint.set(x, getValue(x,y+1), y+1);
198 //                            adj = normalIndex - 1;
199 //                            opp = normalIndex + getWidth();
200                     } else {                    // case #1 : most cases
201                         // right cross down
202                         adjacentPoint.set(x, getValue(x,y+1), y+1);
203                         oppositePoint.set(x+1, getValue(x+1,y), y);
204 //                            adj = normalIndex + getWidth();
205 //                            opp = normalIndex + 1;
206                     }
207                 }
208 
209 
210 
211                 tempNorm.set(adjacentPoint).subtractLocal(rootPoint)
212                         .crossLocal(oppositePoint.subtractLocal(rootPoint));
213                 tempNorm.multLocal(scale).normalizeLocal();
214 //                    store.put(tempNorm.x).put(tempNorm.y).put(tempNorm.z);
215                 BufferUtils.setInBuffer(tempNorm, store,
216                         normalIndex);
217                 normalIndex++;
218             }
219         }
220 
221         return store;
222     }
223 
224     /**
225      * Creates a vertex array from the height data in this Geomap
226      *
227      * The scale argument specifies the scale to use for the vertex buffer.
228      * For example, if scale is 10,1,10, then the greatest X value is getWidth()*10
229      *
230      * @param store A preallocated FloatBuffer where to store the data (optional), size must be >= getWidth()*getHeight()*3
231      * @param scale Created vertexes are scaled by this vector
232      *
233      * @returns store, or a new FloatBuffer if store is null
234      *
235      * @throws NullPointerException If isLoaded() is false
236      */
writeVertexArray(FloatBuffer store, Vector3f scale, boolean center)237     public FloatBuffer writeVertexArray(FloatBuffer store, Vector3f scale, boolean center) {
238 
239         if (store!=null){
240             if (store.remaining() < width*height*3)
241                 throw new BufferUnderflowException();
242         }else{
243             store = BufferUtils.createFloatBuffer(width*height*3);
244         }
245 
246         assert hdata.length == height*width;
247 
248         Vector3f offset = new Vector3f(-getWidth() * scale.x * 0.5f,
249                                        0,
250                                        -getWidth() * scale.z * 0.5f);
251         if (!center)
252             offset.zero();
253 
254         int i = 0;
255         for (int z = 0; z < height; z++){
256             for (int x = 0; x < width; x++){
257                 store.put( (float)x*scale.x + offset.x );
258                 store.put( (float)hdata[i++]*scale.y );
259                 store.put( (float)z*scale.z + offset.z );
260             }
261         }
262 
263         return store;
264     }
265 
getUV(int x, int y, Vector2f store)266     public Vector2f getUV(int x, int y, Vector2f store){
267         store.set( (float)x / (float)getWidth(),
268                    (float)y / (float)getHeight() );
269         return store;
270     }
271 
getUV(int i, Vector2f store)272     public Vector2f getUV(int i, Vector2f store){
273         return store;
274     }
275 
writeTexCoordArray(FloatBuffer store, Vector2f offset, Vector2f scale)276     public FloatBuffer writeTexCoordArray(FloatBuffer store, Vector2f offset, Vector2f scale){
277         if (store!=null){
278             if (store.remaining() < getWidth()*getHeight()*2)
279                 throw new BufferUnderflowException();
280         }else{
281             store = BufferUtils.createFloatBuffer(getWidth()*getHeight()*2);
282         }
283 
284         if (offset == null)
285             offset = new Vector2f();
286 
287         Vector2f tcStore = new Vector2f();
288         for (int y = 0; y < getHeight(); y++){
289             for (int x = 0; x < getWidth(); x++){
290                 getUV(x,y,tcStore);
291                 store.put( offset.x + tcStore.x * scale.x );
292                 store.put( offset.y + tcStore.y * scale.y );
293             }
294 
295         }
296 
297         return store;
298     }
299 
writeIndexArray(IntBuffer store)300     public IntBuffer writeIndexArray(IntBuffer store){
301         int faceN = (getWidth()-1)*(getHeight()-1)*2;
302 
303         if (store!=null){
304             if (store.remaining() < faceN*3)
305                 throw new BufferUnderflowException();
306         }else{
307             store = BufferUtils.createIntBuffer(faceN*3);
308         }
309 
310         int i = 0;
311         for (int z = 0; z < getHeight()-1; z++){
312             for (int x = 0; x < getWidth()-1; x++){
313                 store.put(i).put(i+getWidth()).put(i+getWidth()+1);
314                 store.put(i+getWidth()+1).put(i+1).put(i);
315                 i++;
316 
317                 // TODO: There's probably a better way to do this..
318                 if (x==getWidth()-2) i++;
319             }
320         }
321         store.flip();
322 
323         return store;
324     }
325 
createMesh(Vector3f scale, Vector2f tcScale, boolean center)326     public Mesh createMesh(Vector3f scale, Vector2f tcScale, boolean center){
327         FloatBuffer pb = writeVertexArray(null, scale, center);
328         FloatBuffer tb = writeTexCoordArray(null, Vector2f.ZERO, tcScale);
329         FloatBuffer nb = writeNormalArray(null, scale);
330         IntBuffer ib = writeIndexArray(null);
331         Mesh m = new Mesh();
332         m.setBuffer(Type.Position, 3, pb);
333         m.setBuffer(Type.Normal, 3, nb);
334         m.setBuffer(Type.TexCoord, 2, tb);
335         m.setBuffer(Type.Index, 3, ib);
336         m.setStatic();
337         m.updateBound();
338         return m;
339     }
340 
write(JmeExporter ex)341     public void write(JmeExporter ex) throws IOException {
342         OutputCapsule oc = ex.getCapsule(this);
343         oc.write(hdata, "hdataarray", null);
344         oc.write(width, "width", 0);
345         oc.write(height, "height", 0);
346         oc.write(maxval, "maxval", 0);
347     }
348 
read(JmeImporter im)349     public void read(JmeImporter im) throws IOException {
350         InputCapsule ic = im.getCapsule(this);
351         hdata = ic.readFloatArray("hdataarray", null);
352         if (hdata == null) {
353             FloatBuffer buf = ic.readFloatBuffer("hdata", null);
354             if (buf != null) {
355                 hdata = new float[buf.limit()];
356                 buf.get(hdata);
357             }
358         }
359         width = ic.readInt("width", 0);
360         height = ic.readInt("height", 0);
361         maxval = ic.readInt("maxval", 0);
362     }
363 
364 
365 }
366