October 17, 2021, 09:44:25

Author Topic: [bb] Create a heightmap using the Diamond-Square algorithm by Zethrax [ 1+ years ago ]  (Read 1003 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
Title : Create a heightmap using the Diamond-Square algorithm
Author : Zethrax
Posted : 1+ years ago

Description : *** Blitz3D code ***

This code uses the Diamond-Square algorithm to create a 2D array filled with heightmap values in the range 0.0 to 1.0. This array is then used to generate an image based on the heightmap.

You can either use the heightmap data itself for whatever purpose you have in mind, or use the generated image.

The heightmap is useful for terrain heightmaps and cloud textures - amongst many other things

The code also includes an option to create rougher/smoother heightmaps, examples for filtering the heightmap data, examples of blending foreground and background colors in the resulting image, code to save the resulting image, etc.

If you're clever you could probably modify the code to generate three dimensional data. Create a 3D array with a vertical dimension. Use the existing 2D code to generate a base layer of 2D data and then use a 3D version with an extra loop for the vertical layers that extends the square and diamond steps into 3D space within the array (grabbing and factoring-in additional point source data obtained from the previously generated layer). You can also add some filtering, overlay, etc options to generate data for various effects (ore pockets, etc).

Another thing you can do if you're using the tileable version of the code is to create an endless terrain that continally wraps back onto itself. This would be a bit tricky as you would potentially need up to four versions of the terrain tile (when the player is near a corner) along with copies of all the entities placed on the tiles. There may be ways of doing this in an optimized fashion though. Something to experiment with.

REFERENCES:-
<a href="http://en.wikipedia.org/wiki/Diamond-square_algorithm" target="_blank">http://en.wikipedia.org/wiki/Diamond-square_algorithm[/url]
<a href="http://www.playfuljs.com/realistic-terrain-in-130-lines/" target="_blank">http://www.playfuljs.com/realistic-terrain-in-130-lines/[/url]
<a href="http://www.gameprogrammer.com/fractal.html" target="_blank">http://www.gameprogrammer.com/fractal.html[/url]
<a href="http://srchea.com/terrain-generation-the-diamond-square-algorithm-and-three-js" target="_blank">http://srchea.com/terrain-generation-the-diamond-square-algorithm-and-three-js[/url]
<a href="https://danielbeard.wordpress.com/2010/08/07/terrain-generation-and-smoothing/" target="_blank">https://danielbeard.wordpress.com/2010/08/07/terrain-generation-and-smoothing/[/url]
Code for the tileable version
Code: [Select]
; The 'CreateFractalHeightmapTileable' function creates a fractal heightmap image that can be used as a terrain heightmap or a cloud texture.
; You can also perturb this heightmap algorithm and data in various ways to achieve some interesting and useful results (see the 'filter' option for one example).
; The function uses the Diamond-Square algorithm to create the heightmap data.

; Note that the heightmap data produced by this code is tileable on both axiis.

; REFERENCES:-
; <a href="http://en.wikipedia.org/wiki/Diamond-square_algorithm" target="_blank">http://en.wikipedia.org/wiki/Diamond-square_algorithm</a>
; <a href="http://www.playfuljs.com/realistic-terrain-in-130-lines/" target="_blank">http://www.playfuljs.com/realistic-terrain-in-130-lines/</a>
; <a href="http://www.gameprogrammer.com/fractal.html" target="_blank">http://www.gameprogrammer.com/fractal.html</a>
; <a href="http://srchea.com/terrain-generation-the-diamond-square-algorithm-and-three-js" target="_blank">http://srchea.com/terrain-generation-the-diamond-square-algorithm-and-three-js</a>
; <a href="https://danielbeard.wordpress.com/2010/08/07/terrain-generation-and-smoothing/" target="_blank">https://danielbeard.wordpress.com/2010/08/07/terrain-generation-and-smoothing/</a>


; Declare the array used to store the heightmap data.
Dim A_heightmap#( 0, 0 )


Function CreateFractalHeightmapTileable( size = 512, seed = 0, roughness# = 0.5, filter# = 0.0 )
; PARAMETERS:-
; size% - (Int) Sets the number of squares on each side of the heightmap. Must be a power of 2 integer number greater than 3 (4, 8, 16, 32, etc). Defaults to 512.
; seed% - (Int) Sets the seed used with the random number generator. Defaults to grabbing the seed value from the millisecs system clock timer.
; - Using the same seed value will produce the same heightmap.
; roughness# - (Float) (Range: 0.0 - 1.0) Sets the roughness# of the resulting fractal terrain. 0.0 = Smooth. 1.0 = Rough. Defaults to 0.5.
; filter# - (Float) A quickie filter option I added to show what can be done with some fiddling and diddling. Value can be whatever you want.
; - 10.0 to 30.0 is recommended for some interesting results. 0.0 (the default) = no filtering.

; RETURNS:-
; The function returns an image handle. The image uses the heightmap data for the red, green, blue, and alpha values.
; - The alpha values in the returned image use the heightmap data, so they will be more transparent in darker areas of the image and less transparent in lighter areas.
; - This makes the alpha data useful for tranparent clouds.
; The 'A_heightmap' array is also available after the function has finished. This array contains all the heightmap data. The values it stores will be within the range 0.0 to 1.0 (inclusive).
; - Note that since the algorithm is actually working with the cornerpoint data the array will have an extra row and column. Either grab the first 'size' number of rows
; - (0 To size-1) or blend the data (using bilinear filtering or whatever).


; REQUIRES:-
; The 'SetSquareMidpoint' and 'SetDiamondMidpoint' functions below are required by this function.

; Note that the heightmap data produced by this code is tileable on both axiis.


Local squaresize = size, half, endpoint, x, y, scale# = 1.0, v#

; - Generate the seed value used for random numbers.
If seed ; If seed is non-zero (True)...
SeedRnd seed ; Seed the random number generator with the specified seed value.
Else ; If seed is zero (False)...
SeedRnd MilliSecs() ; Seed the random number generator from the millisecs system clock timer.
EndIf
;---

Dim A_heightmap( size, size ) ; Creates size+1 number of slots as required by the algorithm.

; - Create the 4 initial height values for the corner points of the starting square.
; Seed the corners with the same values for each corner (values must be the same for the data to tile).
v# = Rnd( 0.0, 1.0 )
A_heightmap( 0, 0 ) = v# ; Top-left point.
A_heightmap( size, 0 ) = v# ; Top-right point.
A_heightmap( 0, size ) = v# ; Bottom-left point.
A_heightmap( size, size ) =v# ; Bottom-right point.
;---

; The division loop.
; This loop progressively divides the array squares into four sub-squares until the current array square only contains one actual square
While squaresize > 1

endpoint = size - squaresize ; This will evaluate to zero on the first pass, so it will only loop once on that pass.
half = squaresize / 2

; Note that in the tutorials I looked at there seems to be some confusion about which of the steps below is called the square step, and which is called the diamond step.
; I've chosen to call the first step the square step and the second step the diamond step due to the shapes created by the points that the data is read from.

; Also note that the step value used with the 'For To Step' commands in Blitz3D needs to be a constant, so I can't use 'For' here
; (as I need to halve the step value for each pass through the division loop. I've used 'While' loops instead.

; - The square step.
; This step needs to be run first so that the diamond step can make use of the values generated here for the midpoints.
y = 0
While y <= endpoint
x = 0
While x <= endpoint

SetSquareMidpoint( x, y, squaresize, scale# )

x = x + squaresize
Wend
y = y + squaresize
Wend
; ---

; - The diamond step.
; This step needs to be run second so that the midpoint values generated in the square step are available.
y = 0
While y <= endpoint
x = 0
While x <= endpoint

SetDiamondMidpoint( x + half, y, size, half, scale# ) ; Top
SetDiamondMidpoint( x + squaresize, y + half, size, half, scale# ) ; Right
SetDiamondMidpoint( x + half, y + squaresize, size, half, scale# ) ; Bottom
SetDiamondMidpoint( x, y + half, size, half, scale# ) ; Left

x = x + squaresize
Wend
y = y + squaresize
Wend
; ---

scale# = scale# * roughness#
squaresize = squaresize / 2

Wend

; == If you just want the heightmap data in the 'A_heightmap' array then comment out or remove this section ==
; Creates an image of the specified size using the heightmap data (can be used as a terrain heightmap or cloud texture).
; You can save this image using the 'SaveImage' and 'SaveBuffer' commands.
; Note that the right column and bottom row of data in the array are not used.
Local blue, green, red
Local media = CreateImage( size, size )
SetBuffer ImageBuffer( media )
Local bank = CreateBank( 4 )
endpoint = size - 1
For y = 0 To endpoint
For x = 0 To endpoint
v# = A_heightmap( x, y )

; - Applies a basic filter to the data. One of many ways you can fiddle with it if you're a bit cunning.
If filter# > 0.0
v# = Floor( v# * filter# ) / filter# ; Diddle the data.
EndIf
;---

; Another rough filtering option that will result in areas with sharp drop-offs.
; If v# < 0.5 Then v# = v# * 0.5

; This option will let you blend the heightmap image with a background color (probably could be improved on).
; Sky blue color: Blue = 255, Green = 197, Red = 138
; blue = ( ( v# * 255 ) + ( ( 1.0 - v# ) * 255 ) )
; green = ( ( v# * 255 ) + ( ( 1.0 - v# ) * 197 ) )
; red = ( ( v# * 255 ) + ( ( 1.0 - v# ) * 138 ) )

; This option creates a standard unfiltered heightmap image.
blue = v# * 255
green = v# * 255
red = v# * 255

PokeByte bank, 0, blue ; Blue.
PokeByte bank, 1, green ; Green.
PokeByte bank, 2, red ; Red.
PokeByte bank, 3, v# * 255 ; Alpha. 0 = fully transparent. 255 = fully opaque.
c = PeekInt( bank, 0 )
WritePixel x, y, c
Next
Next
FreeBank bank
Return media
;======

End Function



Function SetSquareMidpoint( x, y, squaresize, scale# )
; PARAMETERS:-
; The x, y coordinates should be pointing to the array slot at the top-left corner of the square.
; squaresize - The width of the current square being worked on.
; scale# - The scale value to apply to the random number used to add noise to the midpoint value.

; This function does not return anything.

Local v# = A_heightmap( x, y ) ; Top-left point.
v# = v# + A_heightmap( x + squaresize, y ) ; Top-right point.
v# = v# + A_heightmap( x, y + squaresize ) ; Bottom-left point.
v# = v# + A_heightmap( x + squaresize, y + squaresize ) ; Bottom-right point.

; Average all four points of the square and then add the random modifier.
v = ( v# / 4.0 ) + Rnd( -scale#, scale# )

; Clip the value to be in the range 0.0 to 1.0.
; This should result in the overall data trending into this range (and not exceeding it) without abrupt cutoffs.
If v < 0.0 Then v = 0.0
If v > 1.0 Then v = 1.0

; Store the resulting value in the midpoint array slot.
A_heightmap( x + squaresize / 2, y + squaresize / 2 ) = v
End Function



Function SetDiamondMidpoint( x, y, max, half, scale# )
; PARAMETERS:-
; The x, y coordinates should be pointing to the array slot at the centerpoint of the diamond (the slot being written to).
; max - The index of the highest numbered array slot in either dimension.
; half - Half the width of the current square being worked on.
; scale# - The scale value to apply to the random number used to add noise to the midpoint value.

; This function does not return anything.

; Note that in the diamond step one of the four array read operations used to read the point values has the potential to be out of the bounds of the array if the diamond centerpoint
; is butting up against a side of the array.
; To deal with this issue we check each point of the diamond to see if is out of bounds and cull the read operation for that point and set an out of bounds flag if so. This results in
; only three of the point values being available to work with, so we just use those instead of the normal four values.
; Since the four major cornerpoints of the array are not accessed in the diamond step we never have a situation where more than one point
; of a diamond can be out of bounds for this step though.

; Abort if the bottom row or right column is being accessed. The data for those is populated when filling in the top row and left column respectively.
If ( x = max ) Or ( y = max ) Then Return


Local outofbounds = 0 ; Used to flag if one of the points on the diamond is outside the bounds of the array.
Local v# = 0.0

; Note that when working on the top row or left column we need to grab the out of bounds point value from its corresponding position on the other side of the array.

; Get top point.
If y = 0
outofbounds = 1
v = v + A_heightmap( x, max - half ) ; Grab the out of bounds point data from the other side of the array.
Else
v = v + A_heightmap( x, y - half )
EndIf

; Get right point.
v = v + A_heightmap( x + half, y )

; Get bottom point.
v = v + A_heightmap( x, y + half )

; Get left point.
If x = 0
outofbounds = 2
v = v + A_heightmap( max - half, y ) ; Grab the out of bounds point data from the other side of the array.
Else
v = v + A_heightmap( x - half, y )
EndIf

;--

; Average the point value and add the random modifier.
v = ( v / 4.0 ) + Rnd( -scale#, scale# )

; Clip the value to be in the range 0.0 to 1.0.
; This should result in the overall data trending into this range (and not exceeding it) without abrupt cutoffs.
If v < 0.0 Then v = 0.0
If v > 1.0 Then v = 1.0

; Note that the top-left cornerpoint of the array isn't touched by the diamond step, so we don't have to worry about handling more than one point at a time here.
Select outofbounds
Case 0 ; Not an edge point.
A_heightmap( x, y ) = v ; Set all points that aren't on an edge of the array.
Case 1 ; Top point.
A_heightmap( x, y ) = v ; Set top point.
A_heightmap( x, max ) = v ; Set corresponding bottom point.
Case 2 ; Left point.
A_heightmap( x, y ) = v; Set left point.
A_heightmap( max, y ) = v ; Set corresponding right point.
End Select
End Function



; -- Helper and demo functions --



;Function RGB( red, green, blue )
; ; This function takes the specified red, green, and blue colors and returns them as an integer that can be used with WritePixel and similar commands.
;
; Local c, bank = CreateBank( 4 )
; PokeByte bank, 0, blue ; Blue.
; PokeByte bank, 1, green ; Green.
; PokeByte bank, 2, red ; Red.
; PokeByte bank, 3, 0 ; Alpha.
; c = PeekInt( bank, 0 )
; FreeBank bank
; Return c
;End Function



Function ChangeColor( media, foreground_red, foreground_green, foreground_blue, background_red, background_green, background_blue )
; This function

Local blue, green, red, v#, x, y
SetBuffer ImageBuffer( media )
Local bank = CreateBank( 4 )
Local endpoint = ImageWidth( media ) - 1
For y = 0 To endpoint
For x = 0 To endpoint
v# = A_heightmap( x, y )

; This option will let you blend the heightmap image with a background color (probably could be improved on).
; Sky blue color: Blue = 255, Green = 197, Red = 138
blue = ( ( v# * foreground_blue ) + ( ( 1.0 - v# ) * background_blue ) )
green = ( ( v# * foreground_green ) + ( ( 1.0 - v# ) * background_green ) )
red = ( ( v# * foreground_red ) + ( ( 1.0 - v# ) * background_red ) )

PokeByte bank, 0, blue ; Blue.
PokeByte bank, 1, green ; Green.
PokeByte bank, 2, red ; Red.
PokeByte bank, 3, v# * 255 ; Alpha. 0 = fully transparent. 255 = fully opaque.
c = PeekInt( bank, 0 )
WritePixel x, y, c
Next
Next
FreeBank bank
SetBuffer BackBuffer()
End Function



; === DEMO ===



Graphics 512, 680, 0, 2

timer = CreateTimer( 60 )

size = 512 : d = 1

Repeat
pos = 0

seed = MilliSecs()

media = CreateFractalHeightmapTileable( size, seed, 0.5, 0.0 ) ; size, seed, roughness#, filter#

SetBuffer BackBuffer()

Repeat
Cls
Viewport 0, 0, size, size
Select d
Case 1
DrawBlock media, pos, 0
DrawBlock media, pos + size, 0
Case -1
DrawBlock media, 0, pos
DrawBlock media, 0, pos + size
End Select
Viewport 0, 0, GraphicsWidth(), GraphicsHeight()
Text 10, GraphicsHeight() - 160, "Random Seed: " + seed
Text 10, GraphicsHeight() - 140, "Finished - Click the screen to redo."
Text 10, GraphicsHeight() - 120, "Press the SPACEBAR to reset and change scroll direction."
Text 10, GraphicsHeight() - 100, "Press the ENTER key to save the image to a file."
Text 10, GraphicsHeight() - 80, "Use the close button or ESCAPE key to exit."
Text 10, GraphicsHeight() - 60, "1 - Change color to white (foreground) sky-blue (background)."
Text 10, GraphicsHeight() - 40, "2 - Change color to green (background) red (foreground)."
Text 10, GraphicsHeight() - 20, "3 - Change color to white (foreground) black (background)."

Flip
WaitTimer( timer )
pos = pos - 1
If pos = -size Then pos = 0
If KeyHit( 57 ) Then d = -d : pos = 0
If KeyHit( 2 ) Then ChangeColor( media, 255, 255, 255, 138, 197, 255 )
If KeyHit( 3 ) Then ChangeColor( media, 0, 255, 0, 255, 0, 0 )
If KeyHit( 4 ) Then ChangeColor( media, 255, 255, 255, 0, 0, 0 )
If KeyHit( 28 )
Cls
If SaveImage( media, seed + ".bmp" )
Print "File saved."
Else
Print "File could not be saved."
EndIf
Print "Click the screen to continue."
FlushMouse : WaitMouse : Delay( 100 ) : FlushMouse
EndIf
If KeyHit( 1 ) Then End
Until MouseHit( 1 )
FlushMouse
Forever

Code for the non-tileable version
Code: [Select]
; The 'CreateFractalHeightmapNoTile' function creates a fractal heightmap image that can be used as a terrain heightmap or a cloud texture.
; You can also perturb this heightmap algorithm and data in various ways to achieve some interesting and useful results (see the 'filter' option for one example).
; The function uses the Diamond-Square algorithm to create the heightmap data.

; Note that the heightmap data produced by this code is not tileable.
; This allows more variance with the height data on the corners and sides.

; REFERENCES:-
; <a href="http://en.wikipedia.org/wiki/Diamond-square_algorithm" target="_blank">http://en.wikipedia.org/wiki/Diamond-square_algorithm</a>
; <a href="http://www.playfuljs.com/realistic-terrain-in-130-lines/" target="_blank">http://www.playfuljs.com/realistic-terrain-in-130-lines/</a>
; <a href="http://www.gameprogrammer.com/fractal.html" target="_blank">http://www.gameprogrammer.com/fractal.html</a>
; <a href="http://srchea.com/terrain-generation-the-diamond-square-algorithm-and-three-js" target="_blank">http://srchea.com/terrain-generation-the-diamond-square-algorithm-and-three-js</a>
; <a href="https://danielbeard.wordpress.com/2010/08/07/terrain-generation-and-smoothing/" target="_blank">https://danielbeard.wordpress.com/2010/08/07/terrain-generation-and-smoothing/</a>


; Declare the array used to store the heightmap data.
Dim A_heightmap#( 0, 0 )


Function CreateFractalHeightmapNoTile( size = 512, seed = 0, roughness# = 0.5, filter# = 0.0 )
; PARAMETERS:-
; size% - (Int) Sets the number of squares on each side of the heightmap. Must be a power of 2 integer number greater than 3 (4, 8, 16, 32, etc). Defaults to 512.
; seed% - (Int) Sets the seed used with the random number generator. Defaults to grabbing the seed value from the millisecs system clock timer.
; - Using the same seed value will produce the same heightmap.
; roughness# - (Float) (Range: 0.0 - 1.0) Sets the roughness# of the resulting fractal terrain. 0.0 = Smooth. 1.0 = Rough. Defaults to 0.5.
; filter# - (Float) A quickie filter option I added to show what can be done with some fiddling and diddling. Value can be whatever you want.
; - 10.0 to 30.0 is recommended for some interesting results. 0.0 (the default) = no filtering.

; RETURNS:-
; The function returns an image handle. The image uses the heightmap data for the red, green, blue, and alpha values.
; - The alpha values in the returned image use the heightmap data, so they will be more transparent in darker areas of the image and less transparent in lighter areas.
; - This makes the alpha data useful for tranparent clouds.
; The 'A_heightmap' array is also available after the function has finished. This array contains all the heightmap data. The values it stores will be within the range 0.0 to 1.0 (inclusive).
; - Note that since the algorithm is actually working with the cornerpoint data the array will have an extra row and column. Either grab the first 'size' number of rows
; - (0 To size-1) or blend the data (using bilinear filtering or whatever).


; REQUIRES:-
; The 'SetSquareMidpoint' and 'SetDiamondMidpoint' functions below are required by this function.

; Note that the heightmap data produced by this code is not tileable.


Local squaresize = size, half, endpoint, x, y, scale# = 1.0, v#

; - Generate the seed value used for random numbers.
If seed ; If seed is non-zero (True)...
SeedRnd seed ; Seed the random number generator with the specified seed value.
Else ; If seed is zero (False)...
SeedRnd MilliSecs() ; Seed the random number generator from the millisecs system clock timer.
EndIf
;---

Dim A_heightmap( size, size ) ; Creates size+1 number of slots as required by the algorithm.

; - Create the 4 initial height values for the corner points of the starting square.
; Seed the corners with different values for each corner.
A_heightmap( 0, 0 ) = Rnd( 0.0, 1.0 ) ; Top-left point.
A_heightmap( size, 0 ) = Rnd( 0.0, 1.0 ) ; Top-right point.
A_heightmap( 0, size ) = Rnd( 0.0, 1.0 ) ; Bottom-left point.
A_heightmap( size, size ) = Rnd( 0.0, 1.0 ) ; Bottom-right point.
;---

; The division loop.
; This loop progressively divides the array squares into four sub-squares until the current array square only contains one actual square
While squaresize > 1

endpoint = size - squaresize ; This will evaluate to zero on the first pass, so it will only loop once on that pass.
half = squaresize / 2

; Note that in the tutorials I looked at there seems to be some confusion about which of the steps below is called the square step, and which is called the diamond step.
; I've chosen to call the first step the square step and the second step the diamond step due to the shapes created by the points that the data is read from.

; Also note that the step value used with the 'For To Step' commands in Blitz3D needs to be a constant, so I can't use 'For' here
; (as I need to halve the step value for each pass through the division loop. I've used 'While' loops instead.

; - The square step.
; This step needs to be run first so that the diamond step can make use of the values generated here for the midpoints.
y = 0
While y <= endpoint
x = 0
While x <= endpoint

SetSquareMidpoint( x, y, squaresize, scale# )

x = x + squaresize
Wend
y = y + squaresize
Wend
; ---

; - The diamond step.
; This step needs to be run second so that the midpoint values generated in the square step are available.
y = 0
While y <= endpoint
x = 0
While x <= endpoint

SetDiamondMidpoint( x + half, y, size, half, scale# ) ; Top
SetDiamondMidpoint( x + squaresize, y + half, size, half, scale# ) ; Right
SetDiamondMidpoint( x + half, y + squaresize, size, half, scale# ) ; Bottom
SetDiamondMidpoint( x, y + half, size, half, scale# ) ; Left

x = x + squaresize
Wend
y = y + squaresize
Wend
; ---

scale# = scale# * roughness#
squaresize = squaresize / 2

Wend

; == If you just want the heightmap data in the 'A_heightmap' array then comment out or remove this section ==
; Creates an image of the specified size using the heightmap data (can be used as a terrain heightmap or cloud texture).
; You can save this image using the 'SaveImage' and 'SaveBuffer' commands.
; Note that the right column and bottom row of data in the array are not used.
Local blue, green, red
Local media = CreateImage( size, size )
SetBuffer ImageBuffer( media )
Local bank = CreateBank( 4 )
endpoint = size - 1
For y = 0 To endpoint
For x = 0 To endpoint
v# = A_heightmap( x, y )

; - Applies a basic filter to the data. One of many ways you can fiddle with it if you're a bit cunning.
If filter# > 0.0
v# = Floor( v# * filter# ) / filter# ; Diddle the data.
EndIf
;---

; Another rough filtering option that will result in areas with sharp drop-offs.
; If v# < 0.5 Then v# = v# * 0.5

; This option will let you blend the heightmap image with a background color (probably could be improved on).
; Sky blue color: Blue = 255, Green = 197, Red = 138
; blue = ( ( v# * 255 ) + ( ( 1.0 - v# ) * 255 ) )
; green = ( ( v# * 255 ) + ( ( 1.0 - v# ) * 197 ) )
; red = ( ( v# * 255 ) + ( ( 1.0 - v# ) * 138 ) )

; This option creates a standard unfiltered heightmap image.
blue = v# * 255
green = v# * 255
red = v# * 255

PokeByte bank, 0, blue ; Blue.
PokeByte bank, 1, green ; Green.
PokeByte bank, 2, red ; Red.
PokeByte bank, 3, v# * 255 ; Alpha. 0 = fully transparent. 255 = fully opaque.
c = PeekInt( bank, 0 )
WritePixel x, y, c
Next
Next
FreeBank bank
Return media
;======

End Function



Function SetSquareMidpoint( x, y, squaresize, scale# )
; PARAMETERS:-
; The x, y coordinates should be pointing to the array slot at the top-left corner of the square.
; squaresize - The width of the current square being worked on.
; scale# - The scale value to apply to the random number used to add noise to the midpoint value.

; This function does not return anything.

Local v# = A_heightmap( x, y ) ; Top-left point.
v# = v# + A_heightmap( x + squaresize, y ) ; Top-right point.
v# = v# + A_heightmap( x, y + squaresize ) ; Bottom-left point.
v# = v# + A_heightmap( x + squaresize, y + squaresize ) ; Bottom-right point.

; Average all four points of the square and then add the random modifier.
v = ( v# / 4.0 ) + Rnd( -scale#, scale# )

; Clip the value to be in the range 0.0 to 1.0.
; This should result in the overall data trending into this range (and not exceeding it) without abrupt cutoffs.
If v < 0.0 Then v = 0.0
If v > 1.0 Then v = 1.0

; Store the resulting value in the midpoint array slot.
A_heightmap( x + squaresize / 2, y + squaresize / 2 ) = v
End Function



Function SetDiamondMidpoint( x, y, max, half, scale# )
; PARAMETERS:-
; The x, y coordinates should be pointing to the array slot at the centerpoint of the diamond (the slot being written to).
; max - The index of the highest numbered array slot in either dimension.
; half - Half the width of the current square being worked on.
; scale# - The scale value to apply to the random number used to add noise to the midpoint value.

; This function does not return anything.

; Note that in the diamond step one of the four array read operations used to read the point values has the potential to be out of the bounds of the array if the diamond centerpoint
; is butting up against a side of the array.
; To deal with this issue we check each point of the diamond to see if is out of bounds and cull the read operation for that point and set an out of bounds flag if so. This results in
; only three of the point values being available to work with, so we just use those instead of the normal four values.
; Since the four major cornerpoints of the array are not accessed in the diamond step we never have a situation where more than one point
; of a diamond can be out of bounds for this step though.


Local outofbounds = False ; Used to flag if one of the points on the diamond is outside the bounds of the array.
Local v# = 0.0

; Get top point.
If y = 0
outofbounds = True
Else
v = v + A_heightmap( x, y - half )
EndIf

; Get right point.
If x = max
outofbounds = True
Else
v = v + A_heightmap( x + half, y )
EndIf

; Get bottom point.
If y = max
outofbounds = True
Else
v = v + A_heightmap( x, y + half )
EndIf

; Get left point.
If x = 0
outofbounds = True
Else
v = v + A_heightmap( x - half, y )
EndIf

;--

If outofbounds ; If one diamond point was out of bounds then just use the average of the three remaining points.
v = ( v / 3.0 ) + Rnd( -scale#, scale# )
Else ; If all the points were in bounds then we use the average of all four points
v = ( v / 4.0 ) + Rnd( -scale#, scale# )
EndIf

; Clip the value to be in the range 0.0 to 1.0.
; This should result in the overall data trending into this range (and not exceeding it) without abrupt cutoffs.
If v < 0.0 Then v = 0.0
If v > 1.0 Then v = 1.0

A_heightmap( x, y ) = v
End Function



; -- Helper and demo functions --
; (Functions that are only required for the demo code.)



;Function RGB( red, green, blue )
; ; This function takes the specified red, green, and blue colors and returns them as an integer that can be used with WritePixel and similar commands.
;
; Local c, bank = CreateBank( 4 )
; PokeByte bank, 0, blue ; Blue.
; PokeByte bank, 1, green ; Green.
; PokeByte bank, 2, red ; Red.
; PokeByte bank, 3, 0 ; Alpha.
; c = PeekInt( bank, 0 )
; FreeBank bank
; Return c
;End Function



Function ChangeColor( media, foreground_red, foreground_green, foreground_blue, background_red, background_green, background_blue )
; This function

Local blue, green, red, v#, x, y
SetBuffer ImageBuffer( media )
Local bank = CreateBank( 4 )
Local endpoint = ImageWidth( media ) - 1
For y = 0 To endpoint
For x = 0 To endpoint
v# = A_heightmap( x, y )

; This option will let you blend the heightmap image with a background color (probably could be improved on).
; Sky blue color: Blue = 255, Green = 197, Red = 138
blue = ( ( v# * foreground_blue ) + ( ( 1.0 - v# ) * background_blue ) )
green = ( ( v# * foreground_green ) + ( ( 1.0 - v# ) * background_green ) )
red = ( ( v# * foreground_red ) + ( ( 1.0 - v# ) * background_red ) )

PokeByte bank, 0, blue ; Blue.
PokeByte bank, 1, green ; Green.
PokeByte bank, 2, red ; Red.
PokeByte bank, 3, v# * 255 ; Alpha. 0 = fully transparent. 255 = fully opaque.
c = PeekInt( bank, 0 )
WritePixel x, y, c
Next
Next
FreeBank bank
SetBuffer BackBuffer()

End Function



; === DEMO ===



Graphics 512, 660, 0, 2

;Repeat
; seed = MilliSecs()
;
; media = CreateFractalHeightmapNoTile( 512, seed, 0.5, 0.0 ) ; size, seed, roughness#, filter#
;
; SetBuffer BackBuffer()
; DrawBlock media, 0, 0
; Text 10, GraphicsHeight() - 60, "Random Seed: " + seed
; Text 10, GraphicsHeight() - 40, "Finished - Click the screen to redo."
; Text 10, GraphicsHeight() - 20, "Use the close button to exit."
; Flip
;
; WaitMouse : Delay( 20 ) : FlushMouse
; Cls
;Forever

Repeat
seed = MilliSecs()

media = CreateFractalHeightmapNoTile( 512, seed, 0.5, 0.0 ) ; size, seed, roughness#, filter#

SetBuffer BackBuffer()

Repeat
Cls
DrawBlock media, 0, 0
Text 10, GraphicsHeight() - 140, "Random Seed: " + seed
Text 10, GraphicsHeight() - 120, "Finished - Click the screen to redo."
Text 10, GraphicsHeight() - 100, "Press the ENTER key to save the image to a file."
Text 10, GraphicsHeight() - 80, "Use the close button or ESCAPE key to exit."
Text 10, GraphicsHeight() - 60, "1 - Change color to white (foreground) sky-blue (background)."
Text 10, GraphicsHeight() - 40, "2 - Change color to red (foreground) green (background)."
Text 10, GraphicsHeight() - 20, "3 - Change color to white (foreground) black (background)."
Flip
If KeyHit( 2 ) Then ChangeColor( media, 255, 255, 255, 138, 197, 255 )
If KeyHit( 3 ) Then ChangeColor( media, 255, 0, 0, 0, 255, 0 )
If KeyHit( 4 ) Then ChangeColor( media, 255, 255, 255, 0, 0, 0 )
If KeyHit( 28 )
Cls
If SaveImage( media, seed + ".bmp" )
Print "File saved."
Else
Print "File could not be saved."
EndIf
Print "Click the screen to continue."
FlushMouse : WaitMouse : Delay( 100 ) : FlushMouse
EndIf
If KeyHit( 1 ) Then End
Delay( 20 )
Until MouseHit( 1 )
Forever


Code :
Code: BlitzBasic
  1. The code is at: http://www.blitzbasic.com/codearcs/codearcs.php?code=3172


Comments :


Blitzplotter(Posted 1+ years ago)

 This looks very intriguing, thanks Zethrax.


 

SimplePortal 2.3.6 © 2008-2014, SimplePortal