; 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 squareWhile 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, redLocal media = CreateImage( size, size )SetBuffer ImageBuffer( media )Local bank = CreateBank( 4 )endpoint = size - 1For 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 NextNextFreeBank bankReturn media;======End FunctionFunction 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 ) = vEnd FunctionFunction 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 SelectEnd 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 FunctionFunction ChangeColor( media, foreground_red, foreground_green, foreground_blue, background_red, background_green, background_blue ); This function Local blue, green, red, v#, x, ySetBuffer ImageBuffer( media )Local bank = CreateBank( 4 )Local endpoint = ImageWidth( media ) - 1For 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 NextNextFreeBank bankSetBuffer BackBuffer()End Function; === DEMO ===Graphics 512, 680, 0, 2timer = CreateTimer( 60 )size = 512 : d = 1Repeat 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 ) FlushMouseForever
; 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 squareWhile 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, redLocal media = CreateImage( size, size )SetBuffer ImageBuffer( media )Local bank = CreateBank( 4 )endpoint = size - 1For 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 NextNextFreeBank bankReturn media;======End FunctionFunction 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 ) = vEnd FunctionFunction 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 ) = vEnd 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 FunctionFunction ChangeColor( media, foreground_red, foreground_green, foreground_blue, background_red, background_green, background_blue ); This function Local blue, green, red, v#, x, ySetBuffer ImageBuffer( media )Local bank = CreateBank( 4 )Local endpoint = ImageWidth( media ) - 1For 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 NextNextFreeBank bankSetBuffer 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;ForeverRepeat 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