Code archives/Graphics/Heightmap Toolbox V0.3.6

This code has been declared by its author to be Public Domain code.

Download source code

Heightmap Toolbox V0.3.6 by impixi
(Posted 20 years ago)
A self-contained class of heightmap creation, processing, exporting, and importing utilities.
See below...

Comments

impixi
(Posted 20 years ago)
Heightmap Toolbox 0.3.6

(the core Heightmap class)

hmap03.6.bmx:

Rem

HeightMap Toolbox
V0.3.6

August 2008


PURPOSE

	A self-contained class of heightmap creation, processing, exporting, and 
	importing utilities.


RECENT CHANGES 


0.3.5

	* Removed COLORMAP_DISCREET constant				- Superceded by new STYLE_DISCREET constant
	* Removed COLORMAP_BLENDED constant				- Superceded by new STYLE_BLENDED constant
	* Removed TEXTUREMAP_DISCREET constant			- Superceded by new STYLE_DISCREET constant
	* Removed TEXTUREMAP_BLENDED constant 			- Superceded by new STYLE_BLENDED constant
	* Removed resize method						- inappropriate, superceded by the new Create function
	* Removed blendMapsRep	function					- superfluous
	* Removed createParticleSMap method				- superceded by generateParticleMap
	* Removed createParticleRMap method				- superceded by generateParticleMap
	
	* Added STYLE_DISCREET constant					
	* Added STYLE_BLENDED constant
	* Added METHOD_STICKY constant
	* Added METHOD_ROLLING constant
	* Added Create function						- more appropriate and convenient than resize
	* Added generate ParticleMap method				- combines both previous particle algorthims and adds some extra algorithm customization
	
	* Modified renderToTexturePixmap				- now accepts pre-existing pixmap arrays or urls
	
	* Renamed createRandomMap to generateRandomMap	- more appropriate name
	* Renamed createHillMap to generateHillMap		- more appropriate name
	* Renamed createFaultMap to generateFaultMap		- more appropriate name
	* Renamed createPerlinMap to generatePerlinMap	- more appropriate name
	* Renamed createMPDMap to generateMPDMap			- more appropriate name
	* Renamed TVector class to TSimpleVector	- minimize potential naming conflicts with other imported code
	
	
0.3.6
	
	* Removed exportPNG							- superfluous
	* Removed exportColorPNG						- superfluous
	* Removed exportTexturePNG						- superfluous
	* Removed exportShadowsPNG						- superfluous
	
	* Modified generatePerlinMap algorithm			- simplified: one less parameter
	
	
METHODS AND FUNCTIONS:
	
	
	create
	clone
	
	generateRandomMap
	generateHillMap
	generateParticleMap			
	generateFaultMap
	generatePerlinMap
	generateMPDMap
		recurseMPD
		MPD
	
	fill
	normalise
	smoothen
	flatten
	makeCoast
	invert
	islandise
	lowerCenter
	
	importPNG
	importImageFile
	importBin
	importPixmap
	exportBin

	blendMapsAdd	
	blendMapsSub	
	blendMapsHi
	blendMapsLo

	blendBrushRaise
	blendBrushLower
	blendBrushHi
	blendBrushLo

	renderToPixmap
	renderRectToPixmap
	renderToColorPixmap
	renderRectToColorPixmap
	renderToTexturePixmap
	renderShadowsToPixmap
		lockedLine


UTILITY CLASS

	TSimpleVector
		setXYZ
		normalise
		add
		sub
		crossProduct
		dotProduct
	
End Rem


SuperStrict


Import BRL.Random
Import BRL.PNGLoader
Import BRL.JPGLoader
Import BRL.TGALoader
Import BRL.BMPLoader
Import BRL.FileSystem


Const STYLE_DISCREET:Int = 0
Const STYLE_BLENDED:Int = 1

Const METHOD_STICKY:Int = 0
Const METHOD_ROLLING:Int = 1


Type THeightMap


  	Field Width:Int = 128				'Width (units) > 0
  	Field Height:Int = 128				'Height (units) > 0
  	Field Map:Float[Width, Height]  	'Matrix of height values (floats) >=0.0 <=1.0 normalised


	Function Create:THeightMap(w:Int = 256, h:Int = 256)
	Rem
	A convenience function that creates a new blank THeightmap an returns the result.
	w: width. > 0. Higher = slower performance And increased memory consumption. 
  	h: height. > 0. Higher = slower performance And increased memory consumption.
	Returns a new THeightMap instance.
	EndRem
			
		If Not ((w > 0) And (h > 0)) Return Null

		Local hm:THeightMap = New THeightMap

    		hm.Width = w
    		hm.Height = h

    		hm.Map = New Float[hm.Width, hm.Height]

		Return hm
	
	EndFunction


	Method clone:THeightMap()
	Rem
	Copies / clones the THeightMap instance and returns the result as a new object.
	Returns the clone as a THeightMap instance.
	EndRem
		
		Local cl:THeightMap = Create(Width, Height)
			
		For Local x:Int = 0 To cl.Width - 1
			For Local z:Int = 0 To cl.Height - 1
				cl.Map[x, z] = Map[x, z]
			Next
		Next
				
		Return cl
	
	EndMethod


  	Method fill(val:Float = 0.0)
	Rem
  	Set all heights To a specific value.
  	val: value To fill with. Use 0.0 To clear the map.
	EndRem

    		For Local x:Int = 0 To Width - 1
      		For Local z:Int = 0 To Height - 1
        			Map[x, z] = val
     			Next
    		Next

  	End Method
 

  	Method generateRandomMap(seed:Int = 0)
  	Rem 
  	Generate a map of random heights between 0.0 and 1.0.
  	seed: random seed. Use Millisecs() to generate a different result every time.
  	End Rem

    		SeedRnd(seed)

    		For Local x:Int = 0 To Width - 1
      		For Local z:Int = 0 To Height - 1
        			Map[x, z] = Rnd()
      			Next
    		Next

  	End Method


	Method generateHillMap(seed:Int = 0, numhills:Int = 1000, maxhsize:Int, island:Int = False)
   	Rem
  	Generate a map using the "hill algorithm".
  	seed: random seed. Use Millisecs() to generate a different result every time.
  	numhills: number of hills. (>1000) is best. Minimum 1.
  	maxhsize: maximum hill radius. For best results, value should be significantly 
  	less than the map's shortest dimension.
	island: generate an island-like result. True/False
  	End Rem

    		SeedRnd(seed)

    		fill(0.0)
  
		If island
			numhills :* 0.2
			maxhsize :* 0.5
		EndIf

    		For Local n:Int = 1 To numhills

	  		Local radius:Int = Rand(0, maxhsize)
			Local centrex:Int = Rand(0, Width - 1)
			Local centrez:Int = Rand(0, Height - 1)
		
			If island
				Local theta:Int = Rand(0, 359)
				Local distx:Int = Rand(0, (Width * 0.5) - radius)
				Local distz:Int = Rand(0, (Height * 0.5) - radius)
		  		centrex = (Width * 0.5) + (Cos(theta) * distx)
		  		centrez = (Height * 0.5) + (Sin(theta) * distz)
			EndIf

      		Local startx:Int = centrex - radius
      		If startx < 0 startx = 0

      		Local endx:Int = centrex + radius
	  		If endx > Width endx = Width

      		Local startz:Int = centrez - radius
      		If startz < 0 startz = 0

      		Local endz:Int = centrez + radius
      		If endz > Height endz = Height

      		For Local x:Int = startx To (endx - 1)
       			For Local z:Int = startz To (endz - 1)
	   	  			Local y:Float = (radius * radius) - (((x - centrex) * (x - centrex)) + ((z - centrez) * (z - centrez)))
	      			If y > 0.0 Map[x, z] :+ y
	    			Next
	  		Next
    
    		Next
  
   		normalise()
  
 	End Method


  	Method generateParticleMap(seed:Int = 0, particles:Int = 500, clusters:Int = 1000, mthd:Int = METHOD_STICKY, vals:Int[] = Null)
  	Rem 
  	seed: random seed. Use Millisecs() to generate a different result every time.
  	particles: number of particles to calculate per cluster. Minimum 1.
  	clusters: number of clusters: higher number gives greater map density. Minimum 1.
	mthd: algorithm method: METHOD_STICKY or METHOD_ROLLING.
	values: algorithm variation: array length should be 4. For best results use integers between -4 and 4.
  	End Rem

		If vals = Null Then vals = [1, -1, 1, -1]
		
		If vals.length < 4 Then vals = [1, -1, 1, -1]

		Local disp:Float = 1.0
  
    		SeedRnd(seed)

    		fill(0.0)
  
		If mthd = METHOD_STICKY
		
			For Local m:Int = 0 To (clusters - 1)
  
      			Local x:Int = Rand(0, Width - 1) 
      			Local z:Int = Rand(0, Height - 1)
	
      			For Local n:Int = 0 To (particles - 1)
	
        				If ((x < Width) And (z < Height) And (x >= 0) And (z >= 0)) Then Map[x, z] :+ disp
	
	    				Select Rand(0, 3)
          				Case 0
            					x :+ vals[0]
         					Case 1
            					x :+ vals[1]
          				Case 2
            					z :+ vals[2]
          				Case 3
            					z :+ vals[3]
        				End Select

				Next
			
			Next

		Else If mthd = METHOD_ROLLING
		
			For Local m:Int = 0 To (clusters - 1)
  
     	 		Local x:Int = Rand(0, Width - 1) 
      			Local z:Int = Rand(0, Height - 1)
	
      			For Local n:Int = 0 To (particles - 1)
	
        				If ((x < Width) And (z < Height) And (x >= 0) And (z >= 0))

	      				Local y:Float = Map[x, z] + disp
	      				Map[x, z] = y
	
	      				Local startx:Int = x - 1
          				If startx < 0 startx = 0

          				Local endx:Int = x + 1
          				If endx > Width endx = Width

          				Local startz:Int = z - 1
          				If startz < 0 startz = 0

          				Local endz:Int = z + 1
          				If endz > Height endz = Height

          				For Local x2:Int = startx To (endx - 1)
            					For Local z2:Int = startz To (endz - 1)
		      					Local y2:Float = Map[x2, z2]
              					If y2 < y Map[x2, z2] = y2 + disp
            					Next
          				Next
	
	    				EndIf
	
	    				Select Rand(0, 3)
         					Case 0
            					x :+ vals[0]
         					Case 1
            					x :+ vals[1]
          				Case 2
            					z :+ vals[2]
          				Case 3
            					z :+ vals[3]
        				End Select

     			Next

    			Next

		EndIf

    		normalise()

	EndMethod


  	Method generateFaultMap(seed:Int = 0, realism:Int = 500)
 	Rem 
  	Generate a map using the "fault line" algorithm.
  	seed: random seed. Use Millisecs() To generate a different result every time.
  	realism: number of fault lines: higher number (> 500) gives greater realism. Minimum 1.
  	End Rem

    		SeedRnd(seed)

    		fill(0.0)
  
    		Local d:Float = Sqr((Width * Width) + (Height * Height))
    		Local disp:Float = 10.0

    		For Local n:Int = 1 To realism
  
      		Local v:Float = Rnd(1,359)
	  		Local a:Float = Sin(v)
      		Local b:Float = Cos(v)
      		Local c:Float = (RndFloat() * d) - (d / 2)

      		For Local x:Int = 0 To (Width - 1)
        			For Local z:Int = 0 To (Height - 1)
          			If (((a * x) + (b * z) - c) > 0.0) Map[x, z] :+ disp Else Map[x, z] :- disp
        			Next
      		Next

    		Next

   		normalise()

 	End Method


  	Method generatePerlinMap(seed:Int = 0, multiplier:Float = 2.0)
 	Rem 
  	Generate a map using the "perlin noise" alogorithm.
  	PORTED and MODIFIED code from the blitzbasic.com code archives.
  	**** ORIGINAL B3D CODE AUTHOR: Shawn C. Swift ****
  	NOTE: This method will only work properly on maps with equal Width and 
  	Height dimensions that are a power of two: eg 256 x 256.
  	seed: random seed. Use Millisecs() to generate a different result every time.
  	multiplier: algorithm variation.
  	End Rem

    		If Not(Width = Height) Return

    		SeedRnd(seed)

    		Local size:Int = Width
    		Local NoiseMapSize:Int = Floor(size / 2)
    		Local max_height:Float = 1.0
		Local NoiseMap:Float[NoiseMapSize + 1, NoiseMapSize + 1]

		For Local x:Int = 0 To (size - 1)
    	  		For Local z:Int = 0 To (size - 1)
        			Map[x, z] = RndFloat()
	  		Next
  		Next
  
    		max_height :* multiplier#
		
    		Repeat
   
     		For Local Noise_Z:Int = 0 To NoiseMapSize
	    			For Local Noise_X:Int = 0 To NoiseMapSize
		    			NoiseMap[Noise_X, Noise_Z] = RndFloat() * max_height
				Next
	  		Next
	 
      		Local ScaleDifference:Int = size / NoiseMapSize
      		Local StepSize:Float = 1.0 / ScaleDifference
      	  
      		For Local Noise_Z:Int = 0 To (NoiseMapSize - 1)
	    			For Local Noise_X:Int = 0 To (NoiseMapSize - 1)
	
  	      			Local N1:Float = NoiseMap[Noise_X, Noise_Z]
		  			Local N2:Float = NoiseMap[(Noise_X + 1), Noise_Z]  
	      			Local N3:Float = NoiseMap[Noise_X, (Noise_Z + 1)]
	      			Local N4:Float = NoiseMap[(Noise_X + 1), (Noise_Z + 1)]
	      			Local Hx:Int = Noise_X * ScaleDifference
	      			Local Hz:Int = Noise_Z * ScaleDifference
	      			Local Iy:Float = 0.0
	
	      			For Local Height_Z:Int = 0 To (ScaleDifference - 1)
	
						Local ICy:Float = 1.0 - ((Cos(Iy * 180.0) + 1.0) / 2.0)
		    				Local Ix:Float = 0.0			
            
		   				For Local Height_X:Int = 0 To (ScaleDifference - 1)
		
			   				Local ICx:Float = 1.0 - ((Cos(Ix * 180.0) + 1.0) / 2.0)
  		       				Local Na:Float = N1 * (1.0 - ICx)
		       				Local Nb:Float = N2 * ICx
		       				Local Nc:Float = N3 * (1.0 - ICx)
	  	       				Local Nd:Float = N4 * ICx
							
		       				Local y:Float = Map[(Hx + Height_X), (Hz + Height_Z)]
		       				Map[(Hx + Height_X), (Hz + Height_Z)] = (y + (Na + Nb) * (1.0 - ICy) + (Nc + Nd) * ICy)
		       				Ix = Ix + StepSize
		    				
						Next
					
		    				Iy = Iy + StepSize	

	      			Next
	
	    			Next  	
     	 	Next
				
			NoiseMapSize :* 0.5
    			max_height :* multiplier
			
		Until (NoiseMapSize < 1)

		normalise()

  	End Method


  	Rem
  	generateMPDMap(seed, grain#)
  	recurseMPD(x0, y0, x2, y2, grain#, level#)
  	mpd#(x0, y0, x1, y1, x2, y2, grain#, level#)

  	PORTED and MODIFIED code from the BlitzCoder.com code database.

  	**** ORIGINAL B3D CODE AUTHOR: Simon Wetterlind 13-08-2002 ****
  	**** v.0.9.3 - Revised: 18-11-2002 by Simon Wetterlind. ****

  	These methods are used to generate a terrain map using the 
  	Midpoint Displacement (ie Diamond Square / Plasma) fractal algorithm.
  	grain# is the "graininess" i.e. the influence of randomness.

  	seed: random seed. Use Millisecs() to generate a different result every time.
  	End Rem
  	Method generateMPDMap(seed:Int = 0, grain:Float = 0.5)

     	fill(-1.0)

     	SeedRnd(seed)

     	Map[0, 0] = RndFloat()
     	Map[(Width - 1), 0] = RndFloat()
     	Map[0, (Height - 1)]= RndFloat()
     	Map[(Width - 1), (Height - 1)] = RndFloat()

     	recurseMPD(0, 0, Width - 1, Height - 1, grain, 1.0)

     	normalise()

  	End Method

  	Method recurseMPD(x0:Int, z0:Int, x2:Int, z2:Int, grain:Float, level:Float)

     	If (x2 - x0 < 2) And (z2 - z0 < 2) Then Return
     
     	Local v:Float, i:Float

     	' change the calculation of level# To (possibly) achieve 
     	' strange results
     	level = 2.0 * level
     
     	Local x1:Int = Ceil((x0 + x2) / 2.0)
     	Local z1:Int = Ceil((z0 + z2) / 2.0)
     
     	v = Map[x1, z0]
     	If v = -1.0 v = MPD(x0, z0, x1, z0, x2, z0, grain, level)
     	i = v
     
     	v = Map[x2, z1]
     	If v = -1.0 v = MPD(x2, z0, x2, z1, x2, z2, grain, level)
     	i :+ v
     
     	v = Map[x1, z2]
     	If v = -1.0 v = MPD(x0, z2, x1, z2, x2, z2, grain, level)
     	i :+ v

     	v = Map[x0, z1]
     	If v = -1.0 v = MPD(x0, z0, x0, z1, x0, z2, grain, level)
     	i :+ v

     	If Map[x1, z1] = -1.0 Map[x1, z1] = i / 4.0 + Rnd(-grain, grain) / level
 	
     	RecurseMPD(x0, z0, x1, z1, grain, level)
     	RecurseMPD(x1, z0, x2, z1, grain, level)
     	RecurseMPD(x1, z1, x2, z2, grain, level)
     	RecurseMPD(x0, z1, x1, z2, grain, level)
	
  	End Method

  	Method MPD:Float(x0:Int, z0:Int, x1:Int, z1:Int, x2:Int, z2:Int, grain:Float, level:Float)

     	Local r:Float = 0.0

     	r :+ (Map[x0, z0] + Map[x2, z2]) / 2.0
     
     	If r < 0.0 r = 0.0
     	If r > 1.0 r = 1.0
 
     	Map[x1, z1] = r
     
     	Return r
     
  	End Method


  	Method normalise()
  	Rem 
  	Normalise map values to between 0.0 and 1.0
  	End Rem
   		Local minv:Float = 10^38, maxv:Float = 10^-38
  
  		For Local x:Int = 0 To (Width - 1)
     		For Local z:Int = 0 To (Height - 1)
 	   			If Map[x, z] < minv minv = Map[x, z] Else If Map[x, z] > maxv maxv = Map[x, z]
     		Next
   		Next

   		For Local x:Int = 0 To (Width - 1)
     		For Local z:Int = 0 To (Height - 1)
	   			Map[x, z] = (Map[x, z] - minv) / (maxv - minv)
	 		Next
  		Next	
	
  	End Method


  	Method smoothen(k:Float = 0.5)
   	Rem 
  	Smooth the map values.
  	Some maps require more smoothing than others to create realistic terrains.
  	k: smoothing factor. Minumum 0.0 (extreme). Maxumum 1.0 (no smoothing).
  	End Rem

   	 	For Local x:Int = 1 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
	    			Map[x, z] = Map[(x - 1), z] * (1 - k) + Map[x, z] * k
      		Next
    		Next

    		For Local x:Int = (Width - 3) To 0 Step -1
      		For Local z:Int = 0 To (Height - 1)
	    			Map[x, z] = Map[(x + 1), z] * (1 - k) + Map[x, z] * k
      		Next
    		Next

    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 1 To (Height - 1)
	    			Map[x, z] = Map[x, (z - 1)] * (1 - k) + Map[x, z] * k
      		Next
    		Next

    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = (Height - 3) To 0 Step -1
	    			Map[x, z] = Map[x, (z + 1)] * (1 - k) + Map[x, z] * k
      		Next
    		Next

  	End Method


  	Method flatten(value:Float = 2.0)
  	Rem
  	'Flatten' the map values.
  	value: flattening amount. 1 = no flattening. Minimum 1. Suggested Maximum 4.
  	End Rem

    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
        			Map[x, z] = Map[x, z] ^ value
      		Next
    		Next

  	End Method
  

  	Method makeCoast(extent:Float = 0.5, depth:Float = 0.5)
  	Rem 
  	Create a crude coastline effect. For best results, smoothing should be applied after this process.
  	extent: extent of coastalisation. 0.0 to 1.0 = None To 100%.
  	depth: depth. 0.0 To 1.0 = Shallow to deep.
  	End Rem

		Local minv:Float = 10^38, maxv:Float = 10^-38
	
    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
	    			If Map[x, z] < minv minv = Map[x, z] Else If Map[x, z] > maxv maxv = Map[x, z]
	  		Next
		Next

		Local e:Float = (maxv - minv) * extent
		Local d:Float = (maxv - minv) * depth
	
    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
	    			If Map[x, z] <= (minv + e) Map[x, z] :- d
	  		Next
		Next   
   
    		normalise()

  	End Method


	Method invert()
	Rem
	Invert the map
	End Rem
		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
         			Map[x, z] = 1.0 - Map[x, z] 
      		Next
    		Next
	
	EndMethod


	Method islandise(amt:Float = 0.0)
	Rem
	Attempt to turn the heightmap into an island by applying an 'island mask'.
	amt: Height retention amount. Minimum 0.0, Maximum 1.0.
		 The higher the retention amount the greater the distortion will be.
	EndRem
		
		If amt < 0.0 Then amt = 0.0
		If amt > 1.0 Then amt = 1.0
		
		Local mask:THeightMap = Create(Width, Height)
		
		mask.generateHillMap(MilliSecs(), 1000, (Width * 0.25), True)
		
		For Local x:Int = 0 To (Width - 1)
			For Local z:Int = 0 To (Height - 1)
				
				Local val:Float = amt + mask.map[x, z]
				If val > 1.0 Then val = 1.0
				
				Map[x, z] = Map[x, z] * val
				
			Next
		Next

		mask = Null
		GCCollect
	
	EndMethod
	
	
	Method lowerCenter()
	Rem
	Lower the center (basically, the opposite of islandise)
	EndRem
		
		Local mask:THeightMap = Create(Width, Height)
		mask.generateHillMap(MilliSecs(), 500, (Width * 0.25), True)
		
		For Local x:Int = 0 To (Width - 1)
			For Local z:Int = 0 To (Height - 1)
				Map[x, z] = Map[x, z] * (1.0 - mask.map[x, z])
			Next
		Next

		mask = Null
		GCCollect
	
	EndMethod


  	Method importPNG:Int(url:String)
  	Rem
	Import from a PNG file.
	url: source file name. Only the blue component data is used.
	Returns True if successful.
  	End Rem

    		Local pm:TPixmap = LoadPixmapPNG(url)

    		If Not pm Return False      
  
		Return importPixmap(pm)

 	End Method
	

  	Method exportBin:Int(url:String = "test.bin")
  	Rem 
	Save heightmap data as a binary file. 
	File Structure:
    		1 * Int (Width)
    		1 * Int (Height) 
		Width * Height * Floats (height values) 
	url: target file name.
	Returns True if successful.
  	End Rem

    		Local result:Int = CreateFile(url)

    		If Not result Then Return False

    		Local file:TStream = OpenFile(url, False, True)

    		WriteInt(file, Width)
    		WriteInt(file, Height)

    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
        			WriteFloat(file, Map[x, z])
      		Next
    		Next
    
    		CloseFile(file)

    		Return True

  	End Method


  	Method importBin:Int(url:String = "test.bin")
  	Rem
	Load heightmap data from a binary file that was previously saved using exportBin. 
	url: source file name.
	Returns True if successful. 
  	End Rem

    		Local file:TStream = OpenFile(url, True, False)

    		If Not file Return False

		Width = ReadInt(file)
		Height = ReadInt(file)
		
		Map = New Float[Width, Height]

		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
        			Map[x, z] = ReadFloat(file)
      		Next
    		Next

		CloseFile(file)

    		Return True

  	End Method


	Method importImageFile:Int(url:String)
  	Rem
	Import from a jpg, png, tga or bmp file.
	url: source file name. Only the blue component data is used.
	Returns True if successful.
  	End Rem

    		Local pm:TPixmap = LoadPixmap(url)

    		If Not pm Return False      
    	
		Return importPixmap(pm)

 	End Method


	Method importPixmap:Int(pm:TPixmap)
  	Rem
	Import from an existing TPixmap.
	pm: source TPixmap. Only the blue component data is used.
	Returns True if successful.
  	End Rem

    		If Not pm Return False      
    	
		If (pm.format <> PF_RGB888) Then pm = ConvertPixmap(pm, PF_RGB888)

		Width = PixmapWidth(pm)
		Height = PixmapHeight(pm)
		
		Map = New Float[Width, Height]
   
    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
        			Local c:Int = ReadPixel(pm, x, z)
        			c :| c Shr 16
        			Map[x, z] = $0000 | c
      		Next
    		Next

    		normalise()

		Return True

 	End Method


  	Function blendMapsAdd(src1:THeightMap, src2:THeightMap, outp:THeightMap)
  	Rem 
  	Blends two maps using an additive process.
  	src1: source THeightMap instance #1.
  	src2: source THeightMap instance #2.
  	outp: output THeightMap instance into which the results will be stored.
  	End Rem

    		Local y1:Float, y2:Float
    
    		For Local x:Int = 0 To (outp.Width - 1)
      		For Local z:Int = 0 To (outp.Height - 1)
         			If (x < src1.Width) And (z < src1.Height) y1 = src1.Map[x, z] Else y1 = 0.0
         			If (x < src2.Width) And (z < src2.Height) y2 = src2.Map[x, z] Else y2 = 0.0
         			outp.Map[x, z] = y1 + y2
      		Next
    		Next
    
    		outp.normalise()
	
  	End Function


  	Function blendMapsSub(src1:THeightMap, src2:THeightMap, outp:THeightMap)
  	Rem 
  	Blends two maps using a subtractive process.
  	src1: source THeightMap instance #1.
  	src2: source THeightMap instance #2.
  	outp: output THeightMap instance into which the results will be stored.
  	End Rem

    		Local x:Int, z:Int
    		Local y1:Float, y2:Float
    
    		For Local x:Int = 0 To (outp.Width - 1)
      		For Local z:Int = 0 To (outp.Height - 1)
         			If (x < src1.Width) And (z < src1.Height) y1 = src1.Map[x, z] Else y1 = 0.0
         			If (x < src2.Width) And (z < src2.Height) y2 = src2.Map[x, z] Else y2 = 0.0
         			outp.Map[x, z] = y1 - y2
      		Next
  	  	Next
    
   	 	outp.normalise()

  	End Function


  	Function blendMapsHi(src1:THeightMap, src2:THeightMap, outp:THeightMap)
  	Rem 
  	Blends two maps, favouring the highest values.
  	src1: source THeightMap instance #1.
  	src2: source THeightMap instance #2.
  	outp: output THeightMap instance into which the results will be stored.
  	End Rem

    		Local y1:Float, y2:Float
    
    		For Local x:Int = 0 To (outp.Width - 1)
      		For Local z:Int = 0 To (outp.Height - 1)
         			If (x < src1.Width) And (z < src1.Height) y1 = src1.Map[x, z] Else y1 = 0.0
         			If (x < src2.Width) And (z < src2.Height) y2 = src2.Map[x, z] Else y2 = 0.0
         			If y1 > y2 outp.Map[x, z] = y1 Else outp.Map[x, z] = y2
      		Next
    		Next
    
    		outp.normalise()

  	End Function


  	Function blendMapsLo(src1:THeightMap, src2:THeightMap, outp:THeightMap)
  	Rem 
  	Blends two maps, favouring the lowest values.
  	src1: source THeightMap instance #1.
  	src2: source THeightMap instance #2.
  	outp: output THeightMap instance into which the results will be stored.
  	End Rem

    		Local y1:Float, y2:Float
    
  	  	For Local x:Int = 0 To (outp.Width - 1)
      		For Local z:Int = 0 To (outp.Height - 1)
    	     		If (x < src1.Width) And (z < src1.Height) y1 = src1.Map[x, z] Else y1 = 0.0
    	     		If (x < src2.Width) And (z < src2.Height) y2 = src2.Map[x, z] Else y2 = 0.0
    	     		If y1 < y2 outp.Map[x, z] = y1 Else outp.Map[x, z] = y2
	      	Next
	    	Next
    
    		outp.normalise()

  	End Function


  	Method renderToPixmap(pm:TPixmap = Null, offx:Int = 0, offz:Int = 0)
  	Rem
  	Render the map to a BlitzMax pixmap object (grey scale style).
  	pm: Destination TPixmap object. Must be the same dimensions as the heightmap. 
		The pixmap format should be PF_RGB888.
  	offx: x offset (pixels) into pm. >=0 and <pm width.
  	offz: z offset (pixels) into pm. >=0 and <pm height.
  	End Rem

		If Not pm Then Return

		Local pmw:Int = PixmapWidth(pm)
		Local pmh:Int = PixmapHeight(pm)

    		If Not ((offx >= 0) And (offx < pmw)) Then Return
    		If Not ((offz >= 0) And (offz < pmh)) Then Return

		If (pm.format <> PF_RGB888) Then pm = ConvertPixmap(pm, PF_RGB888)

    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 0 To (Height - 1)
				If ((offx + x) < pmw) And ((offz + z) < pmh)
          			Local v:Int = Floor(Map[x, z] * 255)
          			WritePixel(pm, (offx + x), (offz + z), v Shl 16 | v Shl 8 | v Shl 0)
        			EndIf
      		Next
    		Next

  	End Method


	Method renderRectToPixmap(pm:TPixmap = Null, offx:Int = 0, offz:Int = 0, rectx:Int = 0, rectz:Int = 0, rectx2:Int = 0, rectz2:Int = 0)
	Rem
	Render a rectangular heightmap section to a pixmap (greyscale style).
	pm: Destination TPixmap object.	The pixmap format should be PF_RGB888.
  	offx: x offset (pixels) into pm. >=0 and <pm width.
  	offz: z offset (pixels) into pm. >=0 and <pm height.
	rectx: top left x coordinate of heightmap
	rectz: top left z coordinate of heightmap
	rectx2: bottom right x coordinate of heightmap
	rectz2: bottom right z coordinate of heightmap
	EndRem
	
		If Not pm Then Return

		Local pmw:Int = PixmapWidth(pm)
		Local pmh:Int = PixmapHeight(pm)
	
  	  	If Not ((offx >= 0) And (offx < pmw)) Return
  	  	If Not ((offz >= 0) And (offz < pmh)) Return
  	  	If Not ((rectx >= 0) And (rectx < Width)) Return
 	   	If Not ((rectz >= 0) And (rectz < Height)) Return
	    	If Not (rectx2 >= rectx) Return
 	   	If Not (rectz2 >= rectz) Return

		If (pm.format <> PF_RGB888) Then pm = ConvertPixmap(pm, PF_RGB888)

   		For Local x:Int = rectx To rectx2
   			For Local z:Int = rectz To rectz2
				If ((offx + x) < pmw) And ((offz + z) < pmh)
       				Local v:Int = Floor(Map[x, z] * 255)
       				WritePixel(pm, (offx + x), (offz + z), v Shl 16 | v Shl 8 | v Shl 0)
       			EndIf
   			Next
   		Next

	End Method


	Method renderToColorPixmap(pm:TPixmap = Null, offx:Int = 0, offz:Int = 0, colarr:Int[] = Null, style:Int = STYLE_DISCREET)
	Rem
  	Render the map to a BlitzMax pixmap object (colormap style).
  	pm: Destination TPixmap object. Must be the same dimensions as the heightmap. 
		The pixmap format should be PF_RGB888.
  	offx: x offset (pixels) into pm. >=0 and <pm width.
  	offz: z offset (pixels) into pm. >=0 and <pm height.
	colarr: Array of integers - each integer represents a color in RGB format.
	style: 0 - discrete colors, 1 - blended colors (use constants STYLE_DISCREET, STYLE_BLENDED).
  	End Rem
		If Not pm Then Return

		Local pmw:Int = PixmapWidth(pm)
		Local pmh:Int = PixmapHeight(pm)

   		If Not ((offx >= 0) And (offx < pmw)) Then Return
   		If Not ((offz >= 0) And (offz < pmh)) Then Return

		If (pm.format <> PF_RGB888) Then pm = ConvertPixmap(pm, PF_RGB888)

		If (colarr.length <= 1) Or (colarr = Null) Then colarr = [$0000FF, $00FF00, $888888]
	
		Local div:Float = 1.0 / Float(colarr.length)
	
		Select style 
		
			Case STYLE_DISCREET
				For Local x:Int = 0 To (Width - 1)
   					For Local z:Int = 0 To (Height - 1)

						If ((offx + x) < pmw) And ((offz + z) < pmh)
				
       						For Local i:Int = 0 To colarr.length - 1
		
								Local lvl:Float = Float(i) * div
		
								If (Map[x, z] >= lvl) Then WritePixel pm, (offx + x), (offz + z), colarr[i]
							Next

						EndIf
		
					Next
				Next

			Case STYLE_BLENDED
	
	   			For Local x:Int = 0 To (Width - 1)
   					For Local z:Int = 0 To (Height - 1)
	
						If ((offx + x) < pmw) And ((offz + z) < pmh)

							If Map[x,z] > (1.0 - div) 
								WritePixel(pm, (offx + x), (offz + z), colarr[colarr.length - 1])
							Else
								Local val:Float = 0.0
								Local r:Int = 0
								Local g:Int = 0
								Local b:Int = 0
		
								For Local i:Int = 0 To (colarr.length - 1)
																
									Local ir:Byte = colarr[i]
									Local ig:Byte = colarr[i] Shr 8
									Local ib:Byte = colarr[i] Shr 16
						
									Local factor:Float = (div - Abs(val - Map[x, z])) / div
									If factor < 0.0 Then factor = 0.0 Else If factor > 1.0 Then factor = 1.0
												
									r :+ (factor * ir)
									g :+ (factor * ig) 
									b :+ (factor * ib) 
	
									val :+ div
						
								Next

								WritePixel(pm, (offx + x), (offz + z), b Shl 16 | g Shl 8 | r)
	
							EndIf
	
						EndIf
					
					Next
				Next
		
		EndSelect
		
	EndMethod 


	Method renderRectToColorPixmap(pm:TPixmap, offx:Int = 0, offz:Int = 0, ..
		rectx:Int = 0, rectz:Int = 0, rectx2:Int = 0, rectz2:Int = 0, colarr:Int[] = Null, style:Int = STYLE_DISCREET)
	Rem
	Render a rectangular heightmap section to a BlitzMax pixmap object (colormap style).
	pm: Destination TPixmap object.	The pixmap format should be PF_RGB888.
  	offx: x offset (pixels) into pm. >=0 and <pm width.
  	offz: z offset (pixels) into pm. >=0 and <pm height.
	rectx: top left x coordinate of heightmap
	rectz: top left z coordinate of heightmap
	rectx2: bottom right x coordinate of heightmap
	rectz2: bottom right z coordinate of heightmap
	style: 0 - discrete colors, 1 - blended colors (use constants STYLE_DISCREET, STYLE_BLENDED).
  	End Rem
		If Not pm Then Return

		Local pmw:Int = PixmapWidth(pm)
		Local pmh:Int = PixmapHeight(pm)

   	 	If Not ((offx >= 0) And (offx < pmw)) Return
  	  	If Not ((offz >= 0) And (offz < pmh)) Return
 	   	If Not ((rectx >= 0) And (rectx < Width)) Return
 	   	If Not ((rectz >= 0) And (rectz < Height)) Return
	    	If Not (rectx2 >= rectx) Return
 	   	If Not (rectz2 >= rectz) Return

		If (pm.format <> PF_RGB888) Then pm = ConvertPixmap(pm, PF_RGB888)

		If (colarr.length <= 1) Or (colarr = Null) Then colarr = [$0000FF, $00FF00, $888888]
	
		Local div:Float = 1.0 / Float(colarr.length)
	
		Select style 
		
			Case STYLE_DISCREET
    			For Local x:Int = rectx To rectx2
      				For Local z:Int = rectz To rectz2

						If ((offx + x) < pmw) And ((offz + z) < pmh)
				
       						For Local i:Int = 0 To colarr.length - 1
		
								Local lvl:Float = Float(i) * div
		
								If (Map[x, z] >= lvl) Then WritePixel pm, (offx + x), (offz + z), colarr[i]
							Next

						EndIf
		
					Next
				Next

			Case STYLE_BLENDED
	
    			For Local x:Int = rectx To rectx2
      				For Local z:Int = rectz To rectz2
	
						If ((offx + x) < pmw) And ((offz + z) < pmh)

							If Map[x,z] > (1.0 - div) 
								WritePixel(pm, (offx + x), (offz + z), colarr[colarr.length - 1])
							Else
								Local val:Float = 0.0
								Local r:Int = 0
								Local g:Int = 0
								Local b:Int = 0
		
								For Local i:Int = 0 To (colarr.length - 1)
																
									Local ir:Byte = colarr[i]
									Local ig:Byte = colarr[i] Shr 8
									Local ib:Byte = colarr[i] Shr 16
						
									Local factor:Float = (div - Abs(val - Map[x, z])) / div
									If factor < 0.0 Then factor = 0.0 Else If factor > 1.0 Then factor = 1.0
												
									r :+ (factor * ir)
									g :+ (factor * ig) 
									b :+ (factor * ib) 
	
									val :+ div
						
								Next

								WritePixel(pm, (offx + x), (offz + z), b Shl 16 | g Shl 8 | r)
	
							EndIf
	
						EndIf
					
					Next
				Next
		
		EndSelect
		
	EndMethod 


	Method renderToTexturePixmap(pm:TPixmap = Null, offx:Int = 0, offz:Int = 0, arr:Object[] = Null, style:Int = STYLE_DISCREET)
	Rem
  	Render the heightmap to a BlitzMax pixmap object (using textures).
  	pm: Destination TPixmap object. Must be the same dimensions as the heightmap. 
		The pixmap format should be PF_RGB888.
  	offx: x offset (pixels) into pm. >=0 and <pm width.
  	offz: z offset (pixels) into pm. >=0 and <pm height.
	arr: Array of urls (file names) OR pixmaps to use for texturing
			style: 0 - discrete edges, 1 - blended edges (use constants STYLE_DISCREET, STYLE_BLENDED).
  	End Rem

		If Not pm Then Return

		Local pmw:Int = PixmapWidth(pm)
		Local pmh:Int = PixmapHeight(pm)

   		If Not ((offx >= 0) And (offx < pmw)) Then Return
   		If Not ((offz >= 0) And (offz < pmh)) Then Return

		If (pm.format <> PF_RGB888) Then pm = ConvertPixmap(pm, PF_RGB888)

		If (arr.length <= 1) Or (arr = Null) Then Return
	
		Local textures:TPixmap[arr.length]
		
		For Local i:Int = 0 To (textures.length - 1)
		
			If String(arr[i]) 
				textures[i] = LoadPixmap(String(arr[i]))
			Else
				If TPixmap(arr[i]) Then textures[i] = TPixmap(arr[i])
			EndIf
				
			If Not textures[i] 
				textures[i] = CreatePixmap(Width, Height, PF_RGB888)
				Local r:Byte = Rand(64, 255)
				Local g:Byte = Rand(64, 255)
				Local b:Byte = Rand(64, 255)
				For Local x:Int = 0 To PixmapWidth(textures[i]) - 1
					For Local z:Int = 0 To PixmapHeight(textures[i]) - 1
						WritePixel textures[i], x, z, 0 Shl 24 | b Shl 16 | g Shl 8 | r
					Next
				Next
			EndIf	
				
			If textures[i].format <> PF_RGB888 Then textures[i] = ConvertPixmap(textures[i], PF_RGB888)
			If (Width <> PixmapWidth(textures[i])) Or (Height <> PixmapHeight(textures[i])) Then textures[i] = ResizePixmap(textures[i], Width, Height)
		Next
	
		Local div:Float = 1.0 / Float(textures.length)
	
		Select style 
		
			Case STYLE_DISCREET
			
				For Local x:Int = 0 To (Width - 1)
   					For Local z:Int = 0 To (Height - 1)

						If ((offx + x) < pmw) And ((offz + z) < pmh)
				
       						For Local i:Int = 0 To textures.length - 1
		
								Local lvl:Float = Float(i) * div
		
								If (Map[x, z] >= lvl) Then WritePixel pm, (offx + x), (offz + z), ReadPixel(textures[i], x, z)
							Next

						EndIf
		
					Next
				Next

			Case STYLE_BLENDED
	
	   			For Local x:Int = 0 To (Width - 1)
   					For Local z:Int = 0 To (Height - 1)
	
						If ((offx + x) < pmw) And ((offz + z) < pmh)

							If Map[x, z] > (1.0 - div) 
								WritePixel pm, (offx + x), (offz + z), ReadPixel(textures[textures.length - 1], x, z)
							Else
								Local val:Float = 0.0
								Local r:Int = 0
								Local g:Int = 0
								Local b:Int = 0
		
								For Local i:Int = 0 To (textures.length - 1)
														
									Local px:Int = ReadPixel(textures[i], x, z)					
																
									Local ir:Byte = px
									Local ig:Byte = px Shr 8
									Local ib:Byte = px Shr 16
						
									Local factor:Float = (div - Abs(val - Map[x, z])) / div
									If factor < 0.0 Then factor = 0.0 Else If factor > 1.0 Then factor = 1.0
												
									r :+ (factor * ir)
									g :+ (factor * ig) 
									b :+ (factor * ib) 
	
									val :+ div
						
								Next

								WritePixel(pm, (offx + x), (offz + z), b Shl 16 | g Shl 8 | r)
	
							EndIf
	
						EndIf
					
					Next
				Next

		EndSelect
				
		For Local i:Int = 0 To (textures.length - 1)
			textures[i] = Null
		Next
		
		GCCollect
				
	EndMethod 


  	Method blendBrushRaise(brush:THeightMap, offx:Int = 0, offz:Int = 0, p:Float = 0.1)
  	Rem 
  	Blends a brush (ie a small heightmap) with the heightmap using an additive process.
  	brush: brush THeightMap instance.
  	offx: x coordinate in heightmap to start brush blend 
  	offz: z coordinate in heightmap to start brush blend
  	p: brush pressure: 0.0 to 1.0. (0% to 100%)
  	End Rem

 	   	If Not ((offx >= 0) And (offx < Width)) Return
 	   	If Not ((offz >= 0) And (offz < Height)) Return
	    	If (p < 0.0) p = 0.0 Else If (p > 1.0) p = 1.0

	    	For Local x:Int = 0 To (brush.Width - 1)
      		For Local z:Int = 0 To (brush.Height - 1)

 	        		Local y1:Float = brush.Map[x, z] * p
	
  	       		If ((offx + x) < Width) And ((offz + z) < Height)
           			Local y2:Float = Map[offx + x, offz + z] + y1
           			If (y2 < 0.0) y2 = 0.0 Else If (y2 > 1.0) y2 = 1.0
           			Map[offx + x, offz + z] = y2
      	   		EndIf  
      		Next
   	 	Next

  	End Method


  	Method blendBrushLower(brush:THeightMap, offx:Int = 0, offz:Int = 0, p:Float = 0.1)
  	Rem 
  	Blends a brush (ie a small heightmap) with the heightmap using an subtractive process.
  	brush: brush THeightMap instance.
  	offx: x coordinate in heightmap To start brush blend 
  	offz: z coordinate in heightmap To start brush blend
  	p: brush pressure: 0.0 To 1.0. (0% To 100%)
  	End Rem

 	   	If Not ((offx >= 0) And (offx < Width)) Return
 	   	If Not ((offz >= 0) And (offz < Height)) Return
	    	If (p < 0.0) p = 0.0 Else If (p > 1.0) p = 1.0

	    	For Local x:Int = 0 To (brush.Width - 1)
      		For Local z:Int = 0 To (brush.Height - 1)

	         		Local y1:Float = brush.Map[x, z] * p

 	        		If ((offx + x) < Width) And ((offz + z) < Height)

           			Local y2:Float = Map[offx + x, offz + z] - y1
           			If (y2 < 0.0) Then y2 = 0.0 Else If (y2 > 1.0) Then y2 = 1.0 
           			Map[offx + x, offz + z] = y2

         			EndIf  

      		Next
    		Next

  	End Method


  	Method blendBrushHi(brush:THeightMap, offx:Int = 0, offz:Int = 0, clear:Int = False)
  	Rem 
  	Blends a brush (ie a small heightmap) with the heightmap favouring the highest value.
  	brush: brush THeightMap instance.
  	offx: x coordinate in heightmap To start brush blend 
  	offz: z coordinate in heightmap To start brush blend
  	clear: clear the values in the brush that were not blended: True/False
  	End Rem

	    	If Not ((offx >= 0) And (offx < Width)) Return
	    	If Not ((offz >= 0) And (offz < Height)) Return

	    	For Local x:Int = 0 To (brush.Width - 1)
      		For Local z:Int = 0 To (brush.Height - 1)
  	       		If ((offx + x) < Width) And ((offz + z) < Height)
					If brush.Map[x, z] > Map[offx + x, offz + z] 
						Map[offx + x, offz + z] = brush.Map[x, z]
					Else
						If clear Then brush.Map[x, z] = 0.0
					EndIf
  	       		EndIf  
  	    		Next
 	   	Next

  	End Method


  	Method blendBrushLo(brush:THeightMap, offx:Int = 0, offz:Int = 0, clear:Int = False)
  	Rem 
  	Blends a brush (ie a small heightmap) with the heightmap favouring the lowest value.
  	brush: brush THeightMap instance.
  	offx: x coordinate in heightmap To start brush blend 
  	offz: z coordinate in heightmap To start brush blend
  	clear: clear the values in the brush that were not blended: True/False
  	End Rem

	    	If Not ((offx >= 0) And (offx < Width)) Return
	    	If Not ((offz >= 0) And (offz < Height)) Return

 	   	For Local x:Int = 0 To (brush.Width - 1)
      		For Local z:Int = 0 To (brush.Height - 1)
	         		If ((offx + x) < Width) And ((offz + z) < Height)
					If brush.Map[x, z] < Map[offx + x, offz + z] 
						Map[offx + x, offz + z] = brush.Map[x, z]
					Else
						If clear Then brush.Map[x, z] = 1.0
					EndIf
  	       		EndIf  
      		Next
 	   	Next

  	End Method


	Method renderShadowsToPixmap(pm:TPixmap, sun:TSimpleVector = Null, k:Float = 0.5)
	Rem 
  	PORTED and MODIFIED code from the blitzbasic.com code archives.
  	****  ORIGINAL AUTHOR: Tim Fisher ****
	Render a lightmap/shadowmap onto a pixmap
	pm: Destination TPixmap object.	The pixmap format should be PF_RGBA8888.
	sun: Sun's relative position (as a TSimpleVector object)
	k#: smoothing factor. Minumum 0.0 (extreme). Maxumum 1.0 (no smoothing).
	End Rem

		If Not pm Then Return
		
		If (pm.format <> PF_RGB888) Then pm = ConvertPixmap(pm, PF_RGB888)

		Local pmw:Int = PixmapWidth(pm)
		Local pmh:Int = PixmapHeight(pm)

		If Not sun Then 
			sun = New TSimpleVector
			sun.setXYZ(1, 2, -1)
		EndIf

		Local tmp1:TSimpleVector = New TSimpleVector
		Local tmp2:TSimpleVector = New TSimpleVector

		Local norm:TSimpleVector[Width, Height]
		For Local i:Int = 0 To Width - 1 
			For Local j:Int = 0 To Height - 1
				norm[i, j] = New TSimpleVector
			Next
		Next
	
		Local v:TSimpleVector[9]
		Local f:TSimpleVector[9]
		For Local i:Int = 0 To (v.length - 1)
			v[i] = New TSimpleVector
			f[i] = New TSimpleVector
		Next
	
		For Local i:Int = 1 To (Width - 2)
			For Local j:Int = 1 To (Height - 2)

				v[0].setXYZ((i)-Width/2,Map[i, j]*255,(j)-Height/2)
				v[1].setXYZ((i-1)-Width/2,Map[i-1, j-1]*255,(j-1)-Height/2)
				v[2].setXYZ((i)-Width/2,Map[i, j-1]*255,(j-1)-Height/2)
				v[3].setXYZ((i+1)-Width/2,Map[i+1, j-1]*255,(j-1)-Height/2)
				v[4].setXYZ((i+1)-Width/2,Map[i+1, j]*255,(j)-Height/2)
				v[5].setXYZ((i+1)-Width/2,Map[i+1, j+1]*255,(j+1)-Height/2)
				v[6].setXYZ((i)-Width/2,Map[i, j+1]*255,(j+1)-Height/2)
				v[7].setXYZ((i-1)-Width/2,Map[i-1, j+1]*255,(j+1)-Height/2)
				v[8].setXYZ((i-1)-Width/2,Map[i-1, j]*255,(j)-Height/2)

				tmp1.sub(v[8], v[1])
				tmp2.sub(v[1], v[0])
				tmp1.normalise()
				tmp2.normalise()
				f[0].crossProduct(tmp1, tmp2)
				
				tmp1.sub(v[2], v[1])
				tmp2.sub(v[1], v[0])
				tmp1.normalise()
				tmp2.normalise()
				f[1].crossProduct(tmp1, tmp2)
								
				tmp1.sub(v[3], v[2])
				tmp2.sub(v[2], v[0])
				tmp1.normalise()
				tmp2.normalise()
				f[2].crossProduct(tmp1, tmp2)	

				tmp1.sub(v[4], v[3])
				tmp2.sub(v[3], v[0])
				tmp1.normalise()
				tmp2.normalise()
				f[3].crossProduct(tmp1, tmp2)	

				tmp1.sub(v[6], v[5])
				tmp2.sub(v[5], v[0])
				tmp1.normalise()
				tmp2.normalise()
				f[4].crossProduct(tmp1, tmp2)	

				tmp1.sub(v[0], v[5])
				tmp2.sub(v[5], v[4])
				tmp1.normalise()
				tmp2.normalise()
				f[5].crossProduct(tmp1, tmp2)	

				tmp1.sub(v[6], v[0])
				tmp2.sub(v[0], v[7])
				tmp1.normalise()
				tmp2.normalise()
				f[6].crossProduct(tmp1, tmp2)

				tmp1.sub(v[0], v[8])
				tmp2.sub(v[8], v[7])
				tmp1.normalise()
				tmp2.normalise()
				f[7].crossProduct(tmp1, tmp2)
				
				tmp1.add(f[1], f[2])
				tmp2.add(tmp1, f[3])
				tmp1.add(tmp2, f[4])
				tmp2.add(tmp1, f[6])
				tmp1.add(tmp2, f[7])
				tmp2.add(tmp1, f[0])
				tmp1.add(tmp2, f[5])
				tmp1.normalise()
			
				norm[i - 1, j - 1].X = tmp1.X * 128
				norm[i - 1, j - 1].Y = tmp1.Y * 128
				norm[i - 1, j - 1].Z = tmp1.Z * 128
			
			Next
		Next

		sun.normalise()
					
		Local lmap:Int[, ] = New Int[Width, Height]
		For Local x:Int = 0 To (Width - 1)
			For Local z:Int = 0 To (Height - 1)
				lmap[x, z] = 0
			Next
		Next
		
		For Local x:Int = 0 To (Width - 1)
			For Local z:Int = 0 To (Height - 1)
		
				Local f:Float = 0.1 
						
				tmp1.X = norm[x, z].X
				tmp1.Y = norm[x, z].Y
				tmp1.Z = norm[x, z].Z

				tmp1.normalise()

				f :+ (0.5 * (1.0 + TSimpleVector.dotProduct(tmp1, sun) ))
				If f > 1 Then f = 1
			
				If LockedLine(x, z, Map[x, z] * 255, sun) Then f :/ 2		' shade the shadow
			
				If f < 0 Then f = 0
			
				lmap[x, z] = Int(f * 255)
			Next
		Next
	
		For Local x:Int = 1 To (Width - 1)
    			For Local z:Int = 0 To (Height - 1)
	   			lmap[x, z] = lmap[(x - 1), z] * (1 - k) + lmap[x, z] * k
    			Next
    		Next

    		For Local x:Int = (Width - 3) To 0 Step -1
      		For Local z:Int = 0 To (Height - 1)
	    			lmap[x, z] = lmap[(x + 1), z] * (1 - k) + lmap[x, z] * k
      		Next
    		Next

    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = 1 To (Height - 1)
	    			lmap[x, z] = lmap[x, (z - 1)] * (1 - k) + lmap[x, z] * k
      		Next
    		Next

    		For Local x:Int = 0 To (Width - 1)
      		For Local z:Int = (Height - 3) To 0 Step -1
	  	  		lmap[x, z] = lmap[x, (z + 1)] * (1 - k) + lmap[x, z] * k
      		Next
    		Next
	
		For Local x:Int = 0 To (Width - 1)
			For Local z:Int = 0 To (Height - 1)
				WritePixel (pm, x, z, lmap[x, z] Shl 16 | lmap[x, z] Shl 8 | lmap[x, z])
			Next
		Next

	EndMethod


	Method LockedLine:Int(x1:Float, y1:Float, z1:Float, sun:TSimpleVector)
	Rem
	Utility function used by renderShadowsToPixmap
  	PORTED and MODIFIED code from the blitzbasic.com code archives.
	****  ORIGINAL AUTHOR: Tim Fisher ****
	EndRem
	
		Local x2:Float = sun.X
		Local y2:Float = sun.Y
		Local z2:Float = sun.Z
	
		While Not ((x1 > Width - 1) Or (y1 > Height - 1) Or (z1 > 255) Or (x1 < 0) Or (y1 < 0)) 
		
			If (Int((Map[x1, y1]) * 255) > z1) Then Return True
				
			x1 :+ (x2 * 2)
			y1 :+ (z2 * 2)
			z1 :+ (y2 * 2)
	
		Wend
	
		Return False
	
	EndMethod


End Type


Type TSimpleVector

	Rem
	Utility class used by shadow mapping code.
	PORTED and MODIFIED code from the blitzbasic.com code archives.
	****  ORIGINAL AUTHORS: Chroma and Tim Fisher ****
	EndRem

	Field tol:Float

	Field X:Float
	Field Y:Float
	Field Z:Float
	
	Method New()
		tol = 0.001
		X = 0.0
		Y = 0.0
		Z = 0.0
	EndMethod
	
	Method setXYZ(vx:Float = 0.0, vy:Float = 0.0, vz:Float = 0.0)
		X = vx
		Y = vy
		Z = vz
	EndMethod 

	Method normalise()
		Local mag:Float = Sqr((X * X) + (Y * Y) + (Z * Z))
		X :/ mag
		Y :/ mag
		Z :/ mag
	
		If (Abs(X) < tol) X = 0.0
		If (Abs(Y) < tol) Y = 0.0
		If (Abs(Z) < tol) Z = 0.0
	EndMethod

	Method add(v2:TSimpleVector, v3:TSimpleVector)
		X = v2.X + v3.X
		Y = v2.Y + v3.Y
		Z = v2.Z + v3.Z
	EndMethod
	
	Method sub(v2:TSimpleVector, v3:TSimpleVector)
		X = v2.X - v3.X
		Y = v2.Y - v3.Y
		Z = v2.Z - v3.Z
	EndMethod

	Method crossProduct(u:TSimpleVector, v:TSimpleVector)
		X =  u.Y * v.Z  -  u.Z * v.Y 
		Y = -u.X * v.Z  +  u.Z * v.X 
		Z =  u.X * v.Y  -  u.Y * v.X 
	EndMethod

	Function dotProduct:Float(u:TSimpleVector, v:TSimpleVector)
		Return (u.X * v.X) + (u.Y * v.Y) + (u.Z * v.Z)
	End Function

End Type



impixi
(Posted 20 years ago)
test1.bmx:

Rem

	Heightmap Toolbox Test #1 - Generation and Processing example.

	(*) - Indicates an essential step in creating and setting up a heightmap.
	(#) - Indicates an essential step in rendering a heightmap to the screen.

End Rem

' ------------- INITIALISATION -------------

SuperStrict

'(*) Import the core Heightmap Toolbox class
Import "hmap03.6.bmx"

'Set the application title
AppTitle = "Heightmap Generation and Processing Example"

'(#) Set the screen resolution
Graphics 800, 600, 0

'Set the blend mode
SetBlend(SOLIDBLEND)
    
'(*) Create a new blank THeightMap with dimensions width X height.
'For MOST algorithms (all except Perlin) the width and height can be 
'dissimilar values (eg 355, 200) but usually the width and height 
'are the same as each other and a power of two.
Global HMap1:THeightMap = THeightMap.Create(256, 256)

'(#) Declare and create a TPixmap object on which to render a greyscale representation of the heightmap.
'Note that the pixmap's width and height should be the same as the heightmap's width and height.
Global Pm1:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)

Cls

drawWait()

'(*) Generate a heightmap using the Midpoint Displacement algorithm
'There are several algorithms to choose from, some quicker than others, that produce
'differing results. See the handleKeys() function below for other methods.
HMap1.generateMPDMap(MilliSecs(), 0.5)

drawMenu()

'(#) Render a greyscale representation of the heightmap to the relevent TPixmap object
HMap1.renderToPixmap(Pm1, 0, 0)

'(#) Draw the pixmap onto the backbuffer
DrawPixmap(Pm1, 0, 0)

Flip

' ------------- MAIN LOOP -------------

While Not KeyHit(KEY_ESCAPE)

	handleKeys()
	drawScreen()
	
	Delay 1

Wend

End

' ------------- FUNCTIONS -------------

Function handleKeys()

	If KeyHit(KEY_1)
		drawWait()
		HMap1.generateMPDMap(MilliSecs(), 0.5)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf
	
	If KeyHit(KEY_2)
		drawWait()
		HMap1.generatePerlinMap(MilliSecs(), 1.7)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_3)
		drawWait()
		HMap1.generateHillMap(MilliSecs(), 500, (HMap1.Width * 0.25))
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_4)
		drawWait()
		HMap1.generateParticleMap(MilliSecs(), 500, 1000, METHOD_STICKY)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_5)
		drawWait()
		HMap1.generateParticleMap(MilliSecs(), 500, 1000, METHOD_ROLLING)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_6)
		drawWait()
		HMap1.generateFaultMap(MilliSecs(), 500)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_7)
		drawWait()
		HMap1.generateRandomMap(MilliSecs())
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_8)
		drawWait()
		HMap1.fill(0.0)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_F1)
		drawWait()
		If SavePixmapPNG(pm1, "test.png") Then ..
			Notify("test.png saved.") Else Notify("test.png could NOT be saved.")
		Return
	EndIf

	If KeyHit(KEY_F2)
		drawWait()
		Local result:Int = HMap1.exportBIN("test.bin")
		If result Notify("test.bin saved.") Else Notify("test.bin could NOT be saved.")
		Return
	EndIf

	If KeyHit(KEY_F5)
		drawWait()
		Local result:Int = HMap1.importPNG("test.png")
		If Not result Notify("test.png could NOT be imported. Check that the file exists.")
		Pm1 = ResizePixmap(Pm1, HMap1.Width, HMap1.Height)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_F6)
		drawWait()
		Local result:Int = HMap1.importBIN("test.bin")
		If Not result Notify("test.bin could NOT be imported. Check that the file exists.")
		Pm1 = ResizePixmap(Pm1, HMap1.Width, HMap1.Height)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_S)
		drawWait()
		HMap1.smoothen(0.8)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_F)
		drawWait()
		HMap1.flatten(1.5)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_L)
		drawWait()
		HMap1.lowerCenter()
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_C)
		drawWait()
		HMap1.makeCoast(0.25, 0.25)
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_I)
		drawWait()
		HMap1.islandise()
		HMap1.renderToPixmap(Pm1, 0, 0)
		Return
	EndIf


End Function

Function drawWait()

	DrawText "Please wait...", 0, 0
	Flip
	
EndFunction

Function drawScreen()

	Cls
	drawMenu()
	DrawPixmap(Pm1, 0, 0)
	Flip
	
EndFunction

Function drawMenu()

	DrawText "Press a key (ESC quits):", 5, 270
	DrawText "------------------------", 5, 285

	DrawText "(1) Midpoint Displacement", 5, 300
	DrawText "(2) Perlin", 5, 320
	DrawText "(3) Hill", 5, 340
	DrawText "(4) Sticky Particle", 5, 360
	DrawText "(5) Rolling Particle", 5, 380
	DrawText "(6) Faultline", 5, 400
	DrawText "(7) Random", 5, 420
	DrawText "(8) Clear", 5, 440
	
	DrawText "(S) Smoothen", 250, 300
	DrawText "(F) Flatten", 250, 320
	DrawText "(L) Lower Center", 250, 340
	DrawText "(C) Make Coast", 250, 360
	DrawText "(I) Islandise", 250, 380
	
	DrawText "(F1) Export PNG - test.png", 450, 300
	DrawText "(F2) Export BIN - test.bin", 450, 320
	DrawText "(F5) Import PNG - test.png", 450, 380
	DrawText "(F6) Import BIN - test.bin", 450, 400

End Function



test2.bmx:

Rem

	Heightmap Toolbox Test #2 - Blending example.

End Rem


' ------------- INITIALISATION -------------

SuperStrict

Import "hmap03.6.bmx"

AppTitle = "Blending Example"

Graphics 800, 600, 0
SetBlend(SOLIDBLEND)

Global HMap1:THeightMap = THeightMap.Create(256, 256)
Global HMap2:THeightMap = THeightMap.Create(256, 256)
Global HMapR:THeightMap = THeightMap.Create(256, 256)

Global Pm1:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)
Global Pm2:TPixmap = CreatePixmap(HMap2.Width, HMap2.Height, PF_RGB888)
Global PmR:TPixmap = CreatePixmap(HMapR.Width, HMapR.Height, PF_RGB888)

drawWait()

HMap1.generateHillMap MilliSecs(), 500, (HMap1.Width * 0.25)
HMap2.generateParticleMap MilliSecs(), 500, 1000, Rand(METHOD_STICKY, METHOD_ROLLING)

HMap1.renderToPixmap Pm1, 0, 0
HMap2.renderToPixmap Pm2, 0, 0

' ------------- MAIN LOOP -------------

While Not KeyHit(KEY_ESCAPE)

	handleKeys()
	drawScreen()
	
	Delay 1

Wend

End

' ------------- FUNCTIONS -------------

Function handleKeys()

	If KeyHit(KEY_1)
		drawWait()
		THeightMap.BlendMapsAdd(HMap1, HMap2, HMapR)
		HMapR.renderToPixmap(PmR, 0, 0)
		Return
	EndIf
	
	If KeyHit(KEY_2)
		drawWait()
		THeightMap.BlendMapsSub(HMap1, HMap2, HMapR)
		HMapR.renderToPixmap(PmR, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_3)
		drawWait()
		THeightMap.BlendMapsHi(HMap1, HMap2, HMapR)
		HMapR.renderToPixmap(PmR, 0, 0)
		Return
	EndIf

	If KeyHit(KEY_4)
		drawWait()
		THeightMap.BlendMapsLo(HMap1, HMap2, HMapR)
		HMapR.renderToPixmap(PmR, 0, 0)
		Return
	EndIf


End Function

Function drawWait()

	DrawText "Please wait...", 0, 0
	Flip
	
EndFunction

Function drawScreen()

	Cls
	drawMenu()
	DrawPixmap(Pm1, 0, 0)
	DrawPixmap(Pm2, Pm1.Width, 0)
	DrawPixmap(PmR, Pm1.Width + Pm2.Width, 0)
	Flip
	
EndFunction

Function drawMenu()

	DrawText "Press a key (ESC quits):", 5, 270
	DrawText "------------------------", 5, 285

	DrawText "(1) Blend Add", 5, 300
	DrawText "(2) Blend Sub", 5, 320
	DrawText "(3) Blend Hi", 5, 340
	DrawText "(4) Blend Lo", 5, 360

End Function


impixi
(Posted 20 years ago)
test3.bmx:

Rem

	Heightmap Toolbox Test #3 - Brush example.
	
	NOTE: 	A 'brush' in HeightMap Toolbox is just a small THeightMap instance.

End Rem

' ------------- INITIALISATION -------------

SuperStrict

Import "hmap03.6.bmx"

AppTitle = "Brush Example"

Graphics 800, 600, 0

Global HMap1:THeightMap = THeightMap.Create(256, 256)
HMap1.generateMPDMap(MilliSecs(), 0.5)

Global Brush:THeightMap = THeightMap.Create(32, 32)
Brush.generateHillMap(MilliSecs(), 100, 15, True)

Global Pm1:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)
HMap1.renderToPixmap(Pm1, 0, 0)

Global PmBrush:TPixmap = CreatePixmap(Brush.Width, Brush.Height, PF_RGB888)
Brush.renderToPixmap(PmBrush, 0, 0)

SetBlend ALPHABLEND
SetAlpha 1.0

HideMouse

' ------------- MAIN LOOP -------------

While Not KeyHit(KEY_ESCAPE)

  	Cls

	DrawText "Left Click - Raise", 5, 300
	DrawText "Right Click - Lower", 5, 320
	DrawText "[Space] - Highest", 5, 340
	DrawText "[Enter] - Lowest", 5, 360
	DrawText "I - Invert", 5, 380

  	Local mx:Int = MouseX()
  	Local my:Int = MouseY()

  	DrawPixmap(Pm1, 0, 0)
  	DrawPixmap(PmBrush, mx, my)

  	If MouseDown(1)
    	If mx >= 0 And mx < HMap1.Width
      		If my >= 0 And my < HMap1.Height
				HMap1.BlendBrushRaise(Brush, mx, my, 0.05)
				HMap1.renderRectToPixmap(Pm1, 0, 0, mx, my, (mx + (Brush.Width - 1)), (my + (Brush.Height - 1)))
      		EndIf
    	EndIf
  	EndIf
  
  	If MouseDown(2)
    	If mx >= 0 And mx < HMap1.Width
      		If my >= 0 And my < HMap1.Height
				HMap1.BlendBrushLower(Brush, mx, my, 0.05)
				HMap1.renderRectToPixmap(Pm1, 0, 0, mx, my, (mx + (Brush.Width - 1)), (my + (Brush.Height - 1)))
      		EndIf
    	EndIf
  	EndIf

	If KeyHit(KEY_SPACE)
	   	If mx >= 0 And mx < HMap1.Width
      		If my >= 0 And my < HMap1.Height
				HMap1.BlendBrushHi(Brush, mx, my)
				HMap1.renderRectToPixmap(Pm1, 0, 0, mx, my, (mx + (Brush.Width - 1)), (my + (Brush.Height - 1)))
			EndIf
		EndIf
	EndIf

	If KeyHit(KEY_ENTER)
	   	If mx >= 0 And mx < HMap1.Width
      		If my >= 0 And my < HMap1.Height
				HMap1.BlendBrushLo(Brush, mx, my)
				HMap1.renderRectToPixmap(Pm1, 0, 0, mx, my, (mx + (Brush.Width - 1)), (my + (Brush.Height - 1)))
			EndIf
		EndIf
	EndIf

	If KeyHit(KEY_I)
		Brush.invert()
		Brush.RenderToPixmap(PmBrush)
	EndIf

  	Flip

	Delay 1

Wend

End



test4.bmx:

Rem

	Heightmap Toolbox Test #4 - Shadow Map example.

End Rem

' ------------- INITIALISATION -------------

SuperStrict

Import "hmap03.6.bmx"

AppTitle = "Shadow Map Example"

Graphics 800, 600, 0
    
drawWait()

Global HMap1:THeightMap = THeightMap.Create(256, 256)
HMap1.generateHillMap(MilliSecs(), 500, (HMap1.Width * 0.25))

Global Pm1:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)
HMap1.renderToPixmap(Pm1, 0, 0)

Global PmShadow:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)
Global Sun:TSimpleVector = New TSimpleVector
Sun.setXYZ(1, 2, -1)

HMap1.renderShadowsToPixmap(PmShadow, Sun)

' ------------- MAIN LOOP -------------

While Not KeyHit(KEY_ESCAPE)

	handleKeys()
	drawScreen()

	Delay(1)

Wend

End

' ------------- FUNCTIONS -------------

Function handleKeys()

	If KeyHit(KEY_1)
		drawWait()
		HMap1.generateHillMap(MilliSecs(), 500, (HMap1.Width * 0.25))
		HMap1.renderToPixmap(Pm1, 0, 0)
		HMap1.renderShadowsToPixmap(PmShadow, Sun)
		Return
	EndIf

	If KeyHit(KEY_F1)
	
		drawWait()

		If SavePixmapPNG(PmShadow, "shadows.png") Then ..
			Notify("shadows.png saved.") Else Notify("shadows.png could NOT be saved.")

		If SavePixmapPNG(Pm1, "test.png") Then ..
			Notify("test.png saved.") Else Notify("test.png could NOT be saved.")
	
		Return
		
	EndIf

End Function

Function drawWait()

	DrawText "Please wait...", 0, 0
	Flip
	
EndFunction

Function drawScreen()

	Cls
	drawMenu()
	DrawPixmap(Pm1, 0, 0)
	DrawPixmap(pmShadow, Pm1.Width, 0)
	Flip
	
EndFunction

Function drawMenu()

	DrawText "Press a key (ESC quits):", 5, 270
	DrawText "------------------------", 5, 285
	DrawText "(1) Generate heightmap and shadow map", 5, 300
	DrawText "(F1) Export PNGs - test.png & shadows.png", 5, 320
	
End Function


impixi
(Posted 20 years ago)
test5.bmx:

Rem

	Heightmap Toolbox Test #5 - Color Map example.
	
End Rem

' ------------- INITIALISATION -------------

SuperStrict

Import "hmap03.6.bmx"

AppTitle = "Color Map Example"

Graphics 800, 600, 0
SetBlend SOLIDBLEND
    
Global HMap1:THeightMap = THeightMap.Create(256, 256)

Global PmColor:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)
Global PmColor2:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)

Global ColorArray:Int[] = [$0000FF, $80FF00, $00FF00, $888888, $AAAAAA, $FFFFFF]

drawWait()

HMap1.generateHillMap(MilliSecs(), 1000, (HMap1.Width * 0.25), True)
HMap1.renderToColorPixmap(PmColor, 0, 0, ColorArray, STYLE_DISCREET)
HMap1.renderToColorPixmap(PmColor2, 0, 0, ColorArray, STYLE_BLENDED)

' ------------- MAIN LOOP -------------

While Not KeyHit(KEY_ESCAPE)

	handleKeys()
	drawScreen()

	Delay 1

Wend

End

' ------------- FUNCTIONS -------------

Function handleKeys()

	If KeyHit(KEY_1)
	
		drawWait()
		HMap1.generateHillMap(MilliSecs(), 1000, (HMap1.Width * 0.25), True)
		HMap1.renderToColorPixmap(PmColor, 0, 0, ColorArray, STYLE_DISCREET)
		HMap1.renderToColorPixmap(PmColor2, 0, 0, ColorArray, STYLE_BLENDED)
		
		Return
		
	EndIf

	If KeyHit(KEY_F1)
	
		drawWait()

		If SavePixmapPNG(PMColor, "testc.png") Then ..
			Notify("testc.png saved.") Else Notify("testc.png could Not be saved.")

		If SavePixmapPNG(PMColor2, "testc2.png") Then .. 
			Notify("testc2.png saved.") Else Notify("testc2.png could NOT be saved.")
	
		Return
		
	EndIf

End Function

Function drawWait()

	DrawText "Please wait...", 0, 0
	Flip
	
EndFunction

Function drawScreen()

	Cls
	drawMenu()
	DrawPixmap PmColor, 0, 0
	DrawPixmap pmColor2, PmColor.width + 5, 0
	Flip
	
EndFunction

Function drawMenu()

	DrawText "Press a key (ESC quits):", 5, 270
	DrawText "------------------------", 5, 285
	DrawText "(1) Generate heightmap and color maps", 5, 300
	DrawText "(F1) Export PNGs - testc.png, testc2.png", 5, 320
	
End Function



test6.bmx:

Rem

	Heightmap Toolbox Test #6 - Texture Map example.
	
End Rem

' ------------- INITIALISATION -------------

SuperStrict

Import "hmap03.6.bmx"

AppTitle = "Texture Map Example"

Graphics 800, 600, 0
SetBlend(SOLIDBLEND)
    
Global HMap1:THeightMap = THeightMap.Create(256, 256)

Global PmTexture:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)
Global Pmtexture2:TPixmap = CreatePixmap(HMap1.Width, HMap1.Height, PF_RGB888)

genSampleTextures()

SetColor 255, 255, 255

Global TextureURLs:String[] = ["texture1.png", "texture2.png", "texture3.png", "texture4.png"]

drawWait()

HMap1.generateHillMap(MilliSecs(), 1000, (HMap1.Width * 0.25), True)
HMap1.renderToTexturePixmap(PmTexture, 0, 0, TextureURLs, STYLE_DISCREET)
HMap1.renderToTexturePixmap(PmTexture2, 0, 0, TextureURLs, STYLE_BLENDED)

' ------------- MAIN LOOP -------------

While Not KeyHit(KEY_ESCAPE)

	handleKeys()
	drawScreen()

	Delay 1

Wend

End

' ------------- FUNCTIONS -------------

Function handleKeys()

	If KeyHit(KEY_1)
		drawWait()
		HMap1.generateHillMap(MilliSecs(), 1000, (HMap1.Width * 0.25), True)
		HMap1.renderToTexturePixmap(PmTexture, 0, 0, TextureURLs, STYLE_DISCREET)
		HMap1.renderToTexturePixmap(PmTexture2, 0, 0, TextureURLs, STYLE_BLENDED)
		Return
	EndIf

	If KeyHit(KEY_F1)
		drawWait()
		If SavePixmapPNG(PmTexture, "tex.png") Then ..
			Notify("tex.png saved.") Else Notify("tex.png could Not be saved.")
		If SavePixmapPNG(PmTexture2, "tex2.png") Then ..
			Notify("tex2.png saved.") Else Notify("tex2.png could Not be saved.")	
		Return
	EndIf

End Function

Function drawWait()

	DrawText "Please wait...", 0, 0
	Flip
	
EndFunction

Function drawScreen()

	Cls
	drawMenu()
	DrawPixmap PmTexture, 0, 0
	DrawPixmap PmTexture2, PmTexture.width + 5, 0
	Flip
	
EndFunction

Function drawMenu()

	DrawText "Press a key (ESC quits):", 5, 270
	DrawText "------------------------", 5, 285
	DrawText "(1) Generate heightmap and texture maps", 5, 300
	DrawText "(F1) Export PNGs - tex.png, tex2.png", 5, 320
	
End Function


Function genSampleTextures()

	SetBlend SOLIDBLEND
	DrawText "Generating sample textures...", 0, 0
	Flip

	Cls
	For Local x:Int = 0 To HMap1.width - 1
		For Local z:Int = 0 To HMap1.height - 1
			Local v:Byte = Rand(128, 196)
			SetColor 88, 88, v
			Plot x, z
		Next
	Next
	SavePixmapPNG(GrabPixmap(0, 0, HMap1.width, HMap1.height), "texture1.png", 9)

	Cls
	For Local x:Int = 0 To HMap1.width - 1
		For Local z:Int = 0 To HMap1.height - 1
			SetColor 0, Rand(128, 196), 0
			Plot x, z
		Next
	Next
	SavePixmapPNG(GrabPixmap(0, 0, HMap1.width, HMap1.height), "texture2.png", 9)

	Cls
	For Local x:Int = 0 To HMap1.width - 1
		For Local z:Int = 0 To HMap1.height - 1
			SetColor 0, Rand(196, 255), 0
			Plot x, z
		Next
	Next
	SavePixmapPNG(GrabPixmap(0, 0, HMap1.width, HMap1.height), "texture3.png", 9)

	Cls
	For Local x:Int = 0 To HMap1.width - 1
		For Local z:Int = 0 To HMap1.height - 1
			Local v:Byte = Rand(128, 196)
			SetColor v, v, v
			Plot x, z
		Next
	Next
	SavePixmapPNG(GrabPixmap(0, 0, HMap1.width, HMap1.height), "texture4.png", 9)

	Cls

End Function


impixi
(Posted 20 years ago)
..

Booticus
(Posted 20 years ago)
Man thanks again for this Impixi! Always love a good terrain generation nugget!

impixi
(Posted 20 years ago)
..

impixi
(Posted 20 years ago)
..

impixi
(Posted 20 years ago)
..

Booticus
(Posted 20 years ago)
Rules. As always! Thanks Impixi!

impixi
(Posted 19 years ago)
Heightmap Toolbox has been updated to Version 0.3.2

Changes:

0.3.2
* Removed method exportX3D
* Removed method lowerPerimeter
* Added method Islandise
* Added method lowerCenter

impixi
(Posted 19 years ago)
Heightmap Toolbox has been updated to Version 0.3.3

Changes:

0.3.3
* Modified importPNG
* Modified importImageFile
* Modified exportShadowsPNG
* Modified exportPNG - now uses format PF_RGB888
* Modified exportColorPNG - now uses format PF_RGB888
* Modified exportTexturePNG - now uses format PF_RGB888
* Modified renderToPixmap - now uses format PF_RGB888
* Modified renderRectToPixmap - now uses format PF_RGB888
* Modified renderToColorPixmap - now uses format PF_RGB888
* Modified renderRectToColorPixmap - now uses format PF_RGB888
* Modified renderToTexturePixmap - now uses format PF_RGB888
* Added method importPixmap
* Added method clone

Macguffin
(Posted 18 years ago)
This is really excellent stuff, impixi. Thanks!

Riva
(Posted 18 years ago)
Thats what i was looking for.
Thanks.

One Question to the Perlin Noise Stuff.
I try to generate some PN Maps.
I setting the SEED value to any fixed Number (not Millisecs) and play around whit the scale and multipler values to see whats going on. But the Scale value dosn't work when you set seed to a fix value.
You can set scale to any value you want. No changes happen.

Sorry for my bad english.

impixi
(Posted 18 years ago)


But the Scale value dosn't work when you set seed to a fix value.




Ah yes, you're correct. I'm not sure why. Will have to investigate further...

impixi
(Posted 18 years ago)
Incidentally, Heightmap Toolbox V0.4 is sort of done, but I'm not sure whether to post it or not. There are significant changes, with some algorithms ported to C code, and the BlitzMax side of things now uses TBanks instead of 2d arrays. Consequently, some algorithms are significantly faster, while others are slightly slower.

Perhaps I need to release it as a pre-compiled module now? I don't know - that could be a real nuisance.

EDIT 26-Aug-08: For simplicity's sake I've reverted back to pure BlitzMax code. Version 0.3.6 incorporates the latest changes.

Riva
(Posted 18 years ago)
Hi impixi,

i find out why Scale dosn't Work with a fix Seed value.

SeedRnd(seed)  '<<<< 1
Local size:Int = Width
Local NoiseMapSize:Int = Floor(size / 2)
Local max_height:Float = scale
Local NoiseMap:Float[NoiseMapSize + 1, NoiseMapSize + 1]

For Local x:Int = 0 To (size - 1)
    For Local z:Int = 0 To (size - 1)
        Map[x, z] = RndFloat() * scale   '<<<<< 2
    Next
Next


Later in the code Rnd ist used again.
I think that scale will work with a fix Seed value if you set the
SeedRnd(seed) '<<<< 1
Line deeper under the 2 For Next Blocks.

impixi
(Posted 18 years ago)
No, that does not seem to make a difference for me, but thanks for your efforts anyway.

impixi
(Posted 18 years ago)
Heightmap Toolbox has been updated to Version 0.3.6

RECENT CHANGES

0.3.5

* Removed COLORMAP_DISCREET constant - Superceded by new STYLE_DISCREET constant
* Removed COLORMAP_BLENDED constant - Superceded by new STYLE_BLENDED constant
* Removed TEXTUREMAP_DISCREET constant - Superceded by new STYLE_DISCREET constant
* Removed TEXTUREMAP_BLENDED constant - Superceded by new STYLE_BLENDED constant
* Removed resize method - inappropriate, superceded by the new Create function
* Removed blendMapsRep function - superfluous
* Removed createParticleSMap method - superceded by generateParticleMap
* Removed createParticleRMap method - superceded by generateParticleMap

* Added STYLE_DISCREET constant
* Added STYLE_BLENDED constant
* Added METHOD_STICKY constant
* Added METHOD_ROLLING constant
* Added Create function - more appropriate and convenient than resize
* Added generate ParticleMap method - combines both previous particle algorthims and adds some extra algorithm customization

* Modified renderToTexturePixmap - now accepts pre-existing pixmap arrays or urls

* Renamed createRandomMap to generateRandomMap - more appropriate name
* Renamed createHillMap to generateHillMap - more appropriate name
* Renamed createFaultMap to generateFaultMap - more appropriate name
* Renamed createPerlinMap to generatePerlinMap - more appropriate name
* Renamed createMPDMap to generateMPDMap - more appropriate name
* Renamed TVector class to TSimpleVector - minimize potential naming conflicts with other imported code


0.3.6

* Removed exportPNG - superfluous
* Removed exportColorPNG - superfluous
* Removed exportTexturePNG - superfluous
* Removed exportShadowsPNG - superfluous

* Modified generatePerlinMap algorithm - simplified: one less parameter

Ian Thompson
(Posted 18 years ago)
Excellent stuff, very interesting, thank you. :)

Code Archives Forum