PageRenderTime 322ms CodeModel.GetById 81ms app.highlight 96ms RepoModel.GetById 125ms app.codeStats 0ms

/src/away3d/extrusions/Elevation.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 388 lines | 253 code | 55 blank | 80 comment | 29 complexity | 38dd50227b260a62a3c8f08b24ff992d MD5 | raw file
  1package away3d.extrusions
  2{
  3	import away3d.core.base.Geometry;
  4	import away3d.core.base.SubGeometry;
  5	import away3d.entities.Mesh;
  6	import away3d.materials.MaterialBase;
  7	
  8	import flash.display.BitmapData;
  9	
 10	/**
 11	 * Class Elevation generates (and becomes) a mesh from an heightmap.
 12	 */
 13	public class Elevation extends Mesh
 14	{
 15		private var _segmentsW:uint;
 16		private var _segmentsH:uint;
 17		private var _width:Number;
 18		private var _height:Number;
 19		private var _depth:Number;
 20		private var _heightMap:BitmapData;
 21		private var _smoothedHeightMap:BitmapData;
 22		private var _activeMap:BitmapData;
 23		private var _minElevation:uint;
 24		private var _maxElevation:uint;
 25		protected var _geomDirty:Boolean = true;
 26		protected var _uvDirty:Boolean = true;
 27		private var _subGeometry:SubGeometry;
 28		
 29		/**
 30		 * @param    material        MaterialBase. The Mesh (Elevation) material
 31		 * @param    heightMap        BitmapData. The heightmap to generate the mesh from
 32		 * @param    width                [optional] Number. The width of the mesh. Default is 1000.
 33		 * @param    height            [optional] Number. The height of the mesh. Default is 100.
 34		 * @param    depth            [optional] Number. The depth of the mesh. Default is 1000.
 35		 * @param    segmentsW    [optional] uint. The subdivision of the mesh along the x axis. Default is 30.
 36		 * @param    segmentsH        [optional] uint. The subdivision of the mesh along the y axis. Default is 30.
 37		 * @param    maxElevation    [optional] uint. The maximum color value to be used. Allows canyon like elevations instead of mountainious. Default is 255.
 38		 * @param    minElevation    [optional] uint. The minimum color value to be used. Default is 0.
 39		 * @param    smoothMap    [optional] Boolean. If surface tracking is used, an internal smoothed version of the map is generated,
 40		 * prevents irregular height readings if original map is blowed up or is having noise. Default is false.
 41		 */
 42		public function Elevation(material:MaterialBase, heightMap:BitmapData, width:Number = 1000, height:Number = 100, depth:Number = 1000, segmentsW:uint = 30, segmentsH:uint = 30, maxElevation:uint = 255, minElevation:uint = 0, smoothMap:Boolean = false)
 43		{
 44			_subGeometry = new SubGeometry();
 45			super(new Geometry(), material);
 46			this.geometry.addSubGeometry(_subGeometry);
 47			
 48			_heightMap = heightMap;
 49			_activeMap = _heightMap;
 50			_segmentsW = segmentsW;
 51			_segmentsH = segmentsH;
 52			_width = width;
 53			_height = height;
 54			_depth = depth;
 55			_maxElevation = maxElevation;
 56			_minElevation = minElevation;
 57			
 58			buildUVs();
 59			buildGeometry();
 60			
 61			if (smoothMap)
 62				generateSmoothedHeightMap();
 63		}
 64		
 65		/**
 66		 * Locks elevation factor beneath this color reading level. Default is 0;
 67		 */
 68		public function set minElevation(val:uint):void
 69		{
 70			if (_minElevation == val)
 71				return;
 72			
 73			_minElevation = val;
 74			invalidateGeometry();
 75		}
 76		
 77		public function get minElevation():uint
 78		{
 79			return _minElevation;
 80		}
 81		
 82		/**
 83		 * Locks elevation factor above this color reading level. Default is 255;
 84		 * Allows to build "canyon" like landscapes with no additional work on heightmap source.
 85		 */
 86		public function set maxElevation(val:uint):void
 87		{
 88			if (_maxElevation == val)
 89				return;
 90			
 91			_maxElevation = val;
 92			invalidateGeometry();
 93		}
 94		
 95		public function get maxElevation():uint
 96		{
 97			return _maxElevation;
 98		}
 99		
100		/**
101		 * The number of segments that make up the plane along the Y or Z-axis, depending on whether yUp is true or
102		 * false, respectively. Defaults to 1.
103		 */
104		public function get segmentsH():uint
105		{
106			return _segmentsH;
107		}
108		
109		public function set segmentsH(value:uint):void
110		{
111			_segmentsH = value;
112			invalidateGeometry();
113			invalidateUVs();
114		}
115		
116		/**
117		 * The width of the terrain plane.
118		 */
119		public function get width():Number
120		{
121			return _width;
122		}
123		
124		public function set width(value:Number):void
125		{
126			_width = value;
127			invalidateGeometry();
128		}
129		
130		public function get height():Number
131		{
132			return _height;
133		}
134		
135		public function set height(value:Number):void
136		{
137			_height = value;
138		}
139		
140		/**
141		 * The depth of the terrain plane.
142		 */
143		public function get depth():Number
144		{
145			return _depth;
146		}
147		
148		public function set depth(value:Number):void
149		{
150			_depth = value;
151			invalidateGeometry();
152		}
153		
154		/**
155		 * Reading the terrain height from a given x z position
156		 * for surface tracking purposes
157		 *
158		 * @see away3d.extrusions.Elevation.smoothHeightMap
159		 */
160		public function getHeightAt(x:Number, z:Number):Number
161		{
162			var col:uint = _activeMap.getPixel((x/_width + .5)*(_activeMap.width - 1), (-z/_depth + .5)*(_activeMap.height - 1)) & 0xff;
163			return (col > _maxElevation)? (_maxElevation/0xff)*_height : ((col < _minElevation)? (_minElevation/0xff)*_height : (col/0xff)*_height);
164		}
165		
166		/**
167		 * Generates a smoother representation of the geometry using the original heightmap and subdivision settings.
168		 * Allows smoother readings for surface tracking if original heightmap has noise, causing choppy camera movement.
169		 *
170		 * @see away3d.extrusions.Elevation.getHeightAt
171		 */
172		public function generateSmoothedHeightMap():BitmapData
173		{
174			if (_smoothedHeightMap)
175				_smoothedHeightMap.dispose();
176			_smoothedHeightMap = new BitmapData(_heightMap.width, _heightMap.height, false, 0);
177			
178			var w:uint = _smoothedHeightMap.width;
179			var h:uint = _smoothedHeightMap.height;
180			var i:uint;
181			var j:uint;
182			var k:uint;
183			var l:uint;
184			
185			var px1:uint;
186			var px2:uint;
187			var px3:uint;
188			var px4:uint;
189			
190			var lockx:uint;
191			var locky:uint;
192			
193			_smoothedHeightMap.lock();
194			
195			var incXL:Number;
196			var incXR:Number;
197			var incYL:Number;
198			var incYR:Number;
199			var pxx:Number;
200			var pxy:Number;
201			
202			for (i = 0; i < w + 1; i += _segmentsW) {
203				
204				if (i + _segmentsW > w - 1)
205					lockx = w - 1;
206				else
207					lockx = i + _segmentsW;
208				
209				for (j = 0; j < h + 1; j += _segmentsH) {
210					
211					if (j + _segmentsH > h - 1)
212						locky = h - 1;
213					else
214						locky = j + _segmentsH;
215					
216					if (j == 0) {
217						px1 = _heightMap.getPixel(i, j) & 0xFF;
218						px1 = (px1 > _maxElevation)? _maxElevation : ((px1 < _minElevation)? _minElevation : px1);
219						px2 = _heightMap.getPixel(lockx, j) & 0xFF;
220						px2 = (px2 > _maxElevation)? _maxElevation : ((px2 < _minElevation)? _minElevation : px2);
221						px3 = _heightMap.getPixel(lockx, locky) & 0xFF;
222						px3 = (px3 > _maxElevation)? _maxElevation : ((px3 < _minElevation)? _minElevation : px3);
223						px4 = _heightMap.getPixel(i, locky) & 0xFF;
224						px4 = (px4 > _maxElevation)? _maxElevation : ((px4 < _minElevation)? _minElevation : px4);
225					} else {
226						px1 = px4;
227						px2 = px3;
228						px3 = _heightMap.getPixel(lockx, locky) & 0xFF;
229						px3 = (px3 > _maxElevation)? _maxElevation : ((px3 < _minElevation)? _minElevation : px3);
230						px4 = _heightMap.getPixel(i, locky) & 0xFF;
231						px4 = (px4 > _maxElevation)? _maxElevation : ((px4 < _minElevation)? _minElevation : px4);
232					}
233					
234					for (k = 0; k < _segmentsW; ++k) {
235						incXL = 1/_segmentsW*k;
236						incXR = 1 - incXL;
237						
238						for (l = 0; l < _segmentsH; ++l) {
239							incYL = 1/_segmentsH*l;
240							incYR = 1 - incYL;
241							
242							pxx = ((px1*incXR) + (px2*incXL))*incYR;
243							pxy = ((px4*incXR) + (px3*incXL))*incYL;
244							
245							//_smoothedHeightMap.setPixel(k+i, l+j, pxy+pxx << 16 |  0xFF-(pxy+pxx) << 8 | 0xFF-(pxy+pxx) );
246							_smoothedHeightMap.setPixel(k + i, l + j, pxy + pxx << 16 | pxy + pxx << 8 | pxy + pxx);
247						}
248					}
249				}
250			}
251			_smoothedHeightMap.unlock();
252			
253			_activeMap = _smoothedHeightMap;
254			
255			return _smoothedHeightMap;
256		}
257		
258		/*
259		 * Returns the smoothed heightmap
260		 */
261		public function get smoothedHeightMap():BitmapData
262		{
263			return _smoothedHeightMap;
264		}
265		
266		private function buildGeometry():void
267		{
268			var vertices:Vector.<Number>;
269			var indices:Vector.<uint>;
270			var x:Number, z:Number;
271			var numInds:uint;
272			var base:uint;
273			var tw:uint = _segmentsW + 1;
274			var numVerts:uint = (_segmentsH + 1)*tw;
275			var uDiv:Number = (_heightMap.width - 1)/_segmentsW;
276			var vDiv:Number = (_heightMap.height - 1)/_segmentsH;
277			var u:Number, v:Number;
278			var y:Number;
279			
280			if (numVerts == _subGeometry.numVertices) {
281				vertices = _subGeometry.vertexData;
282				indices = _subGeometry.indexData;
283			} else {
284				vertices = new Vector.<Number>(numVerts*3, true);
285				indices = new Vector.<uint>(_segmentsH*_segmentsW*6, true);
286			}
287			
288			numVerts = 0;
289			var col:uint;
290			
291			for (var zi:uint = 0; zi <= _segmentsH; ++zi) {
292				for (var xi:uint = 0; xi <= _segmentsW; ++xi) {
293					x = (xi/_segmentsW - .5)*_width;
294					z = (zi/_segmentsH - .5)*_depth;
295					u = xi*uDiv;
296					v = (_segmentsH - zi)*vDiv;
297					
298					col = _heightMap.getPixel(u, v) & 0xff;
299					y = (col > _maxElevation)? (_maxElevation/0xff)*_height : ((col < _minElevation)? (_minElevation/0xff)*_height : (col/0xff)*_height);
300					
301					vertices[numVerts++] = x;
302					vertices[numVerts++] = y;
303					vertices[numVerts++] = z;
304					
305					if (xi != _segmentsW && zi != _segmentsH) {
306						base = xi + zi*tw;
307						indices[numInds++] = base;
308						indices[numInds++] = base + tw;
309						indices[numInds++] = base + tw + 1;
310						indices[numInds++] = base;
311						indices[numInds++] = base + tw + 1;
312						indices[numInds++] = base + 1;
313					}
314				}
315			}
316			
317			_subGeometry.autoDeriveVertexNormals = true;
318			_subGeometry.autoDeriveVertexTangents = true;
319			_subGeometry.updateVertexData(vertices);
320			_subGeometry.updateIndexData(indices);
321		}
322		
323		/**
324		 * @inheritDoc
325		 */
326		private function buildUVs():void
327		{
328			var uvs:Vector.<Number> = new Vector.<Number>();
329			var numUvs:uint = (_segmentsH + 1)*(_segmentsW + 1)*2;
330			
331			if (_subGeometry.UVData && numUvs == _subGeometry.UVData.length)
332				uvs = _subGeometry.UVData;
333			else
334				uvs = new Vector.<Number>(numUvs, true);
335			
336			numUvs = 0;
337			for (var yi:uint = 0; yi <= _segmentsH; ++yi) {
338				for (var xi:uint = 0; xi <= _segmentsW; ++xi) {
339					uvs[numUvs++] = xi/_segmentsW;
340					uvs[numUvs++] = 1 - yi/_segmentsH;
341				}
342			}
343			
344			_subGeometry.updateUVData(uvs);
345		}
346		
347		/**
348		 * Invalidates the primitive's geometry, causing it to be updated when requested.
349		 */
350		protected function invalidateGeometry():void
351		{
352			_geomDirty = true;
353			invalidateBounds();
354		}
355		
356		/**
357		 * Invalidates the primitive's uv coordinates, causing them to be updated when requested.
358		 */
359		protected function invalidateUVs():void
360		{
361			_uvDirty = true;
362		}
363	
364	/**
365	 * Updates the geometry when invalid.
366	 */
367	/*
368	 private function updateGeometry() : void
369	 {
370	 buildGeometry();
371	 _geomDirty = false;
372	 }
373	 *
374	 */
375	
376	/**
377	 * Updates the uv coordinates when invalid.
378	 */
379	/*
380	 private function updateUVs() : void
381	 {
382	 buildUVs();
383	 _uvDirty = false;
384	 }
385	 *
386	 */
387	}
388}