[PB 4.31] Zoomable random terrain

Share your advanced PureBasic knowledge/code with the community.
Kelebrindae
Enthusiast
Enthusiast
Posts: 151
Joined: Tue Apr 01, 2008 3:23 pm

[PB 4.31] Zoomable random terrain

Post by Kelebrindae »

Hi,

This little program generates a random terrain (using a diamond-square algorithm to produce a 2D Perlin-like noise), and computes temperature and slope for each point of the map. Then, using altitude/temperature/slope, it determines which kind of terrain is predominant (desert, forest, tundra, etc..) and colors the map accordingly.
You can also zoom on the generated map (up to x4096), and the program will generate all the needed informations on-the-fly.

There's a bug in this code, one I didn't manage to fix: even though the details are correctly generated during the zooming process, the "pixels" of the initial map still appear, causing large square patterns; And I don't know how to blend these patterns seamlessly with its neighbours.

If someone's got an idea... :)

Code: Select all

; Author: Kelebrindae
; Date: august, 05, 2008
; PB version: v4.20
; OS: Windows XP

; ---------------------------------------------------------------------------------------------------------------
; Purpose:
; ---------------------------------------------------------------------------------------------------------------
; - Generates an heightmap from a diamond-square algorithm.
; - Computes altitude, slope, temperature for each cell of the heightmap.
; - Determines what terrain type (grass, forest, mountain, etc.) is predominant in this 
;   altitute/slope/temperature configuration.
; - Draw an image from these datas.
; - You can zoom the map to show smaller areas (up to x4096)
;
; It should look like a (very simple) earth-like map.
;
; ---------------------------------------------------------------------------------------------------------------
; Known Problems:
; ---------------------------------------------------------------------------------------------------------------
; - Rivers disappear when you zoom
; - Square patterns (the "pixels" in the initial zoom level) appears in the "slope" maps, 
;   affecting relief coloring, and I don't know how to "blend" them seamlessly


;- Constants
#e = 2.718281828459045235
#MAXUNITSIZE = 4096
#USEAPIDRAWING = #True  ; set this to false in 4.40, or to compile under Mac or Linux

Enumeration
  #Window_0
EndEnumeration

Enumeration
  #Image_2
  #Image_1
  #BT_close
  #RS_height
  #RS_slope
  #RS_temp
  #FL_teq
  #teqLabel
  #FL_seed
  #SeedLabel
  #FL_tpol
  #TpolLabel
  #LabelAverage
  #LabelVariance
  #FL_heightAv
  #FL_heightVt
  #FL_tempAv
  #FL_tempVt
  #FL_slopeAv
  #FL_slopeVt
  #heightLabel
  #tempLabel
  #slopeLabel
  #CB_terrain
  #terrainLabel
  #Text_17
  #Text_18
  #RT_terrain
  #BT_redraw
  #FL_sealevel
  #sealevelLabel
  #FL_temploss
  #templossLabel
  #TB_anim
EndEnumeration

;- Fonts
Global FontID1
FontID1 = LoadFont(1, "Arial", 11)
Global FontID2
FontID2 = LoadFont(2, "Arial Narrow", 48)
Global FontID3
FontID3 = LoadFont(4, "Arial", 11, #PB_Font_Bold)

;- Data structures
Structure geostat_struct
  latitude.f
  slope.f
  temperature.f
  height.f
  probaterrain.f[10]
  isWater.b
  couleur.l
EndStructure

Structure geotype_struct
  minheight.f
  maxheight.f
EndStructure

Structure terrain_struct
  name.s
  heightAv.f
  heightVt.f
  slopeAv.f
  slopeVt.f
  temperatureAv.f
  temperatureVt.f
  couleur.l
EndStructure

CompilerIf #USEAPIDRAWING = #True
  Structure myBITMAPINFO
    bmiHeader.BITMAPINFOHEADER
    bmiColors.RGBQUAD[1]
  EndStructure
CompilerEndif

;- Global definitions
maxgrid = 512 ; must be a power of 2
Global Dim geostat.geostat_struct(6,maxgrid,maxgrid)
Global Dim geotype.geotype_struct(6)
Global Dim terrain.terrain_struct(10)

; Tundra
terrain(1)\name = "Tundra"
terrain(1)\heightAv = 1
terrain(1)\heightVt = 4
terrain(1)\slopeAv = 0
terrain(1)\slopeVt = 2.5
terrain(1)\temperatureAv = 0
terrain(1)\temperatureVt = 1.5
terrain(1)\couleur = $79A700
; Herbe
terrain(2)\name = "Grass"
terrain(2)\heightAv = 1
terrain(2)\heightVt = 3
terrain(2)\slopeAv = 0
terrain(2)\slopeVt = 2
terrain(2)\temperatureAv = 18
terrain(2)\temperatureVt = 3.5
terrain(2)\couleur = $5AD00F
; Forêt
terrain(3)\name = "Forest"
terrain(3)\heightAv = 1.1
terrain(3)\heightVt = 3
terrain(3)\slopeAv = 5
terrain(3)\slopeVt = 3
terrain(3)\temperatureAv = 25
terrain(3)\temperatureVt = 2
terrain(3)\couleur = $3EA300
; Désert
terrain(4)\name = "Desert"
terrain(4)\heightAv = 1.3
terrain(4)\heightVt = 5
terrain(4)\slopeAv = 0
terrain(4)\slopeVt = 1.25
terrain(4)\temperatureAv = 50
terrain(4)\temperatureVt = 2
terrain(4)\couleur = $E5DFAD
; Montagne
terrain(5)\name = "Mountain"
terrain(5)\heightAv = 1.7
terrain(5)\heightVt = 2
terrain(5)\slopeAv = 20
terrain(5)\slopeVt = 3
terrain(5)\temperatureAv = 10
terrain(5)\temperatureVt = 2
terrain(5)\couleur = $BBBBBB

Global tpol.f = -20,teq.f = 50,equivHeightToTemp.f = -0.4; temperature loss when you climb up 1 unit
Global waterLevel.l=127,seed.l=4321
Global zoomlevel.l=0,unitSize.f = #MAXUNITSIZE/Pow(2,zoomlevel)
Global xori.f,yori.f

EnableExplicit

;- ---------- Procedures ----------
;- --- Heightmap generation ---

;************************************************************************************
; Name: noise3D
; Purpose: Pseudo-Random Number Generator (PRNG)
; Parameters:
;   - x,y,z : coords of a point, or any other triplet of floats
; Return-value: a "random" number between -1 and 1
;************************************************************************************
Procedure.f noise3D(x.f,y.f,z.f)

  Protected n.l,n2.q
 
  ; " n = Math.floor(x+y*57+z*131); "
  n2=Int(x*1.7+y*57+z*131)
  n=n2%2147483647

  ; " n = (n<<13)^n "
  n=(n<<13)!n

  ; " (n*(n*n*15731+789221)+1376312589) & 0x7FFFFFFF "
  n=(n*(n*n*15731+789221)+1376312589) & $7FFFFFFF

  ProcedureReturn (1.0-n/1073741824.0) 
    
EndProcedure

;************************************************************************************
; Name: makeDiamondSquare
; Purpose: Creates an array of fractal noise (looks like Perlin) that can be used as
;          a heightmap
; Parameters:
;   - size of the array (a square of size x size)
;   - dispersion
;   - seed: albeit the noise is random, a seed always produces the same result
; Return-value: none, but the result is stored in the "diamond" array
;************************************************************************************
Procedure makeDiamondSquare(numHmap.l,maxgrid.l,unitSize.f,seed.l,xori.l,yori.l)

  Protected gridstep.l,mid.l,n.l
  Protected i.l,i1.l,i2.l
  Protected j.l,j1.l,j2.l,js.l
  Protected u.f,average.f,addVal.f
  Protected noiseScale.f,min.f=999999,max.f=-999999,ratio.f
  Protected Dim temp.f(maxgrid,maxgrid)
  
  
  gridstep=maxgrid
  noiseScale=unitSize/2

  i=0:j=0
  temp(i,j)=noise3D(xori+(i*unitSize),yori+(j*unitSize),seed)*noiseScale
  i=maxgrid:j=0
  temp(i,j)=noise3D(xori+(i*unitSize),yori+(j*unitSize),seed)*noiseScale
  i=0:j=maxgrid
  temp(i,j)=noise3D(xori+(i*unitSize),yori+(j*unitSize),seed)*noiseScale
  i=maxgrid:j=maxgrid
  temp(i,j)=noise3D(xori+(i*unitSize),yori+(j*unitSize),seed)*noiseScale


  ; main loop
  While gridstep>1
    mid=gridstep/2

    ; Diamond step - calculates new diamond corners from squares
    i=mid
    i1=i-mid
    i2=i+mid
    
    While i<maxgrid
      j=mid
      j1=j-mid
      j2=j+mid
      
      While j<maxgrid
        ; Average of surrounding points
        average=(temp(i1,j1)+temp(i1,j2)+temp(i2,j1)+temp(i2,j2))/4
        
        ; calculate random values between -1 and 1
        ;u=Random(16384)/8192 - 1
        u=noise3D(xori+(i*unitSize),yori+(j*unitSize),seed)
        
        ; Diamond value
        temp(i,j)=average+u*noiseScale
        j+gridstep
        j1+gridstep
        j2+gridstep
      Wend
      
      i+gridstep
      i1+gridstep
      i2+gridstep
    Wend

    ; square Step - calculates new square corners from diamonds
    i=0
    i1=i-mid
    i2=i+mid
    js=0
    
    While i<maxgrid
      js=mid-js ; toggle start values of j loop
      j=js
      j1=j-mid
      j2=j+mid
      
      While j<maxgrid;+1
        average=0
        If i1<0  ; check For need To wrap around i value
          ;average+noise3D(xori+(i1*unitSize),yori+(j*unitSize),seed)*noiseScale+temp(i2,j)
          average+temp(i2,j)+temp(i2,j)
          ;Debug "i1,j = " + Str(i1)+","+Str(j)+ " / "+StrF(noise3D(xori+(i1*unitSize),yori+(j*unitSize),seed)*noiseScale) + " / " + StrF(average)
        Else
          If i2>maxgrid
            ;average+temp(i1,j)+noise3D(xori+(i2*unitSize),yori+(j*unitSize),seed)*noiseScale
            average+temp(i1,j)+temp(i1,j)
          Else
            average+temp(i1,j)+temp(i2,j)
          EndIf
        EndIf
        
        If j1<0  ; check For need To wrap around j value
          ;average+noise3D(xori+(i*unitSize),yori+(j1*unitSize),seed)*noiseScale+temp(i,j2)
          average+temp(i,j2)+temp(i,j2)
        Else
          If j2>maxgrid
            ;average+temp(i,j1)+noise3D(xori+(i*unitSize),yori+(j2*unitSize),seed)*noiseScale
            average+temp(i,j1)+temp(i,j1)
          Else
            average+temp(i,j1)+temp(i,j2)
          EndIf
        EndIf     
        average/4
        
        ; calculate random value between -1 And 1
        ;u=Random(16384)/8192-1
        u=noise3D(xori+(i*unitSize),yori+(j*unitSize),seed)
        
        temp(i,j)=average+u*noiseScale
        ;temp(maxgrid,j)=temp(0,j) ; copy opposite edge
        j+gridstep
        j1+gridstep
        j2+gridstep
        
      Wend
      ;If j=maxgrid
      ;  temp(i,j)=temp(i,0) ; copy opposite edge
      ;EndIf
      i+mid
      i1+mid
      i2+mid
    Wend

    noiseScale/2
    gridstep/2
  Wend

; Keep values in Byte range (between 0 and 255)
  For i=0 To maxgrid
    For j=0 To maxgrid
      ; Min /max values
      If temp(i,j)<min
        min=temp(i,j)
      Else
        If temp(i,j)>max
          max=temp(i,j)
        EndIf
      EndIf   
    Next j
  Next i
  
  addVal = -(min+(waterLevel/255.0)*(max-min))
  Debug "min\max = " + StrF(min)+" \ "+StrF(max) + " | " + StrF(addVal)
;   ratio=255.0/(max-min)
  For i=0 To maxgrid-1
    For j=0 To maxgrid-1
      geostat(numhMap,i,j)\height=temp(i,j)+addVal
;       n=(temp(i,j)-min)*ratio
;       If n<0
;         heightmap(numhMap,i,j)=0
;       Else
;         If n>255
;           heightmap(numhMap,i,j)=255
;         Else
;           heightmap(numhMap,i,j)=n
;         EndIf
;       EndIf
    Next j
  Next i
  
  geotype(numhMap)\minheight=min+addVal
  geotype(numhMap)\maxheight=max+addVal
 
EndProcedure




;- --- Image generation ---

;************************************************************************************
; Name: terrainLatitude
; Purpose: determine the latitude of a point (i.e. map it on a sphere and see how close
;          it is from the north pole)
; Parameters:
;   - point of the map
;   - north/south coordinates of the map at the current zoom level
;   - distance between the "northest" and the "southest" point on the map 
;     (i.e. scale of the map at the current zoom level)
;************************************************************************************
Macro terrainLatitude(matrixPoint,coordNS,distanceNS)
  matrixPoint#\latitude = Cos((coordNS-distanceNS/2)/distanceNS *#PI)
EndMacro

;************************************************************************************
; Name: terrainTemperature
; Purpose: determine the temperature of a point, according to its latitude and altitude
; Parameters:
;   - point of the map
;   - altitude of the deepest point in the map
;************************************************************************************
Macro terrainTemperature(matrixPoint,minH)
  ; Temperature
  h=(matrixPoint#\height - minH)*ratio
  If matrixPoint#\height >= 0
    heightAboveWater=h - waterlevel
  Else
    heightAboveWater = 0
  EndIf
  matrixPoint#\temperature = tpol + matrixPoint#\latitude*(teq-tpol) + equivHeightToTemp*heightAboveWater
EndMacro

;************************************************************************************
; Name: terrainSlope
; Purpose: determine the slope of a point, according to the points around it
; Parameters:
;   - point of the map
;   - coords i,j of the point
;************************************************************************************
Macro terrainSlope(matrixName,i,j)
  h1= matrixName#(i,j)\height
  matrixName#(i,j)\slope=0
  p=0
  
  If i>0
    h2=matrixName#(i-1,j)\height
    p=Abs( h1 - h2 )
;     If p>matrixName#(i,j)\slope
;       matrixName#(i,j)\slope = p
;     EndIf
  EndIf

  If i<maxgrid
    h2=matrixName#(i+1,j)\height
    p+Abs( h1 - h2 )
;     p=Abs( h1 - h2 )
;     If p>matrixName#(i,j)\slope
;       matrixName#(i,j)\slope = p
;     EndIf
  EndIf

  If j>0
    h2=matrixName#(i,j-1)\height
    p+Abs( h1 - h2 )
;     p=Abs( h1 - h2 )
;     If p>matrixName#(i,j)\slope
;       matrixName#(i,j)\slope = p
;     EndIf
  EndIf

  If j<maxgrid
    h2=matrixName#(i,j+1)\height
    p+Abs( h1 - h2 )
;     p=Abs( h1 - h2 )
;     If p>matrixName#(i,j)\slope
;       matrixName#(i,j)\slope = p
;     EndIf
  EndIf
  matrixName#(i,j)\slope=p/4.0
  matrixName#(i,j)\slope*((#MAXUNITSIZE/unitSize)/4)
EndMacro

;************************************************************************************
; Name: terrainColor
; Purpose: determine the color of a point, according to its terrain type, temperature, etc..
; Parameters:
;   - point of the map
;************************************************************************************
Macro terrainColor(matrixPoint)
  ; Color
  If matrixPoint#\height<0
    ; This point is under sea level
    matrixPoint#\isWater=2
  
    ; underwater points => shades of blue
    red1=0:green1=3:blue1=92
    red2=0:green2=130:blue2=220
    
    red=red1+(red2-red1)*(h /waterlevel)
    green=green1+(green2-green1)*(h /waterlevel)
    blue=blue1+(blue2-blue1)*(h /waterlevel)
  
    ; water depth causes temperature variation
    matrixPoint#\temperature-(h -waterlevel)*0.075
  Else       
    ; This point is above sea level
    matrixPoint#\isWater=0
    
    ; Probability computation for each terrain type
    cumul=0:red=0:green=0:blue=0
    For k=1 To 5
      exp = Abs(terrain(k)\temperatureAv - matrixPoint#\temperature)/terrain(k)\temperatureVt
      exp + Abs(terrain(k)\slopeAv - matrixPoint#\slope)/terrain(k)\slopeVt
      exp + Abs(terrain(k)\heightAv*waterLevel - h)/terrain(k)\heightVt
      matrixPoint#\probaterrain[k] = Pow(#e,-exp)
      
      cumul+matrixPoint#\probaterrain[k]
    Next k
    
    ; Mix terrains colors according to probability
    For k=1 To 5
      matrixPoint#\probaterrain[k]/cumul
      blue+Red(terrain(k)\couleur)*matrixPoint#\probaterrain[k]
      green+Green(terrain(k)\couleur)*matrixPoint#\probaterrain[k]
      red+Blue(terrain(k)\couleur)*matrixPoint#\probaterrain[k]
    Next k           
  EndIf

  matrixPoint#\couleur = RGB(red,green,blue)
EndMacro


;************************************************************************************
; Name: ComputeGeoStats
; Purpose: Compute height, slope, temperature, ice/snow, terrain type and color for
;          each cell of an heightmap
; Parameters:
;   - number of the heightmap
;   - size of the array (a square of size x size)
;   - sea level (between 0-255)
;   - Temperature at poles (usually cold)
;   - Temperature at equator (usually hot)
;   - seed: albeit the map is random, a seed always produces the same result
; Return-value:  none, but the results are stored in the "geostat" array
;************************************************************************************
Procedure ComputeGeoStats(numHmap.b,maxgrid.l,zoomlevel.l,waterLevel.l,tpol.f,teq.f,seed.l)

  Protected i.l,j.l,k.l
  Protected x.l,y.l,newx.l,newy.l
  Protected p.f,h1.f,h2.f,ratio.f;,maxp.f
  Protected heightAboveWater.l,h.l
  Protected red1.l,red2.l,green1.l,green2.l,blue1.l,blue2.l,red.f,green.f,blue.f
  Protected exp.f,cumul.f,unitSize.f = #MAXUNITSIZE/Pow(2,zoomlevel)

  RandomSeed(seed)
  
  ratio = 255.0/(geotype(numHmap)\maxHeight-geotype(numHmap)\minHeight)
  maxgrid-1
  For i=0 To maxgrid
    For j = 0 To maxgrid
    
      ; Slope
      h1= geostat(numHmap,i,j)\height
      geostat(numHmap,i,j)\slope=0
      p=0
      
      If i>0
        h2=geostat(numHmap,i-1,j)\height
        p=Abs( h1 - h2 )
;         If p>geostat(numHmap,i,j)\slope
;           geostat(numHmap,i,j)\slope = p
;         EndIf
      EndIf

      If i<maxgrid
        h2=geostat(numHmap,i+1,j)\height
        p+Abs( h1 - h2 )
;         If p>geostat(numHmap,i,j)\slope
;           geostat(numHmap,i,j)\slope = p
;         EndIf
      EndIf

      If j>0
        h2=geostat(numHmap,i,j-1)\height
        p+Abs( h1 - h2 )
;         If p>geostat(numHmap,i,j)\slope
;           geostat(numHmap,i,j)\slope = p
;         EndIf
      EndIf

      If j<maxgrid
        h2=geostat(numHmap,i,j+1)\height
        p+Abs( h1 - h2 )
;         If p>geostat(numHmap,i,j)\slope
;           geostat(numHmap,i,j)\slope = p
;         EndIf
      EndIf
      geostat(numHmap,i,j)\slope=p/4.0
      geostat(numHmap,i,j)\slope*((#MAXUNITSIZE/unitSize)/4)

      ; Latitude
      terrainLatitude(geostat(numHmap,i,j),(j*#MAXUNITSIZE),(maxgrid*#MAXUNITSIZE))
      
      ; Temperature
      terrainTemperature(geostat(numHmap,i,j),geotype(numHmap)\minHeight)      

      ; Terrain Type
      terrainColor(geostat(numHmap,i,j)) 
      
    Next j
  Next i
  
  ; Generate a few rivers
  j=0
  For i=1 To Random(6)
    Repeat
      x=Random(maxgrid)
      y=Random(maxgrid)
      j+1
    Until geostat(numHmap,x,y)\isWater<>2 Or j=1000
    
    Repeat
      geostat(numHmap,x,y)\couleur = RGB(0,130,220)
      geostat(numHmap,x,y)\isWater=1
      h1=999
      newx=0:newy=0
      If x>0
        If geostat(numHmap,x-1,y)\isWater <>1 And geostat(numHmap,x-1,y)\height < h1
          newx=x-1:newy=y
          h1=geostat(numHmap,newx,newy)\height
        EndIf 
      EndIf
      If x<maxgrid
        If geostat(numHmap,x+1,y)\isWater <>1 And geostat(numHmap,x+1,y)\height < h1
          newx=x+1:newy=y
          h1=geostat(numHmap,newx,newy)\height
        EndIf 
      EndIf
      If y>0
        If geostat(numHmap,x,y-1)\isWater <>1 And geostat(numHmap,x,y-1)\height < h1
          newx=x:newy=y-1
          h1=geostat(numHmap,newx,newy)\height
        EndIf 
      EndIf
      If y<maxgrid
        If geostat(numHmap,x,y+1)\isWater <>1 And geostat(numHmap,x,y+1)\height < h1
          newx=x:newy=y+1
          h1=geostat(numHmap,newx,newy)\height
        EndIf 
      EndIf
      x=newx:y=newy
      
    Until geostat(numHmap,x,y)\isWater=2 Or (newx=0 And newy=0)
  Next i

EndProcedure


;************************************************************************************
; Name: MakeTerrainImage
; Purpose: Draw the map
; Parameters:
;   - number of the image
;   - number of the heightmap / geostats array
;   - size of the array (a square of size x size)
;   - Type of map to draw: "terrain","altitude","slope", "temperature"
; Return-value:  none
;************************************************************************************
Procedure MakeTerrainImage(numImage.l,numHmap.b,maxgrid.l,imageType.s)

  Protected i.l,j.l,color.l,icecolor.l
  Protected red.f,green.f,blue.f,icegradient.f,ratio.f
  
  CompilerIf #USEAPIDRAWING = #True
    Protected hDC.l,hBmp.l,*mem.l,picl_X.l,picl_Y.l,picl_D.l
    Protected bmi.myBITMAPINFO,*pixel.LONG
  
    hDC=StartDrawing(ImageOutput(numImage))
    hBmp = ImageID(numImage)
    picl_X = ImageWidth(numImage)
    picl_Y = ImageHeight(numImage)+1
    picl_D = ImageDepth(numImage)  
    
    *mem = AllocateMemory(picl_X*picl_Y*4)
    bmi.myBITMAPINFO
    bmi\bmiHeader\biSize   = SizeOf(BITMAPINFOHEADER)
    bmi\bmiHeader\biWidth  = picl_X
    bmi\bmiHeader\biHeight = picl_Y
    bmi\bmiHeader\biPlanes = 1
    bmi\bmiHeader\biBitCount = 32
    bmi\bmiHeader\biCompression = #BI_RGB
    GetDIBits_(hDC,hBmp,1,picl_Y,*mem,bmi,#DIB_RGB_COLORS)
  
    *pixel = *mem
  CompilerElse
    StartDrawing(ImageOutput(numImage))
  CompilerEndIf
  
  ratio = 255.0/(geotype(numHmap)\maxHeight-geotype(numHmap)\minHeight)
  For j=maxgrid-1 To 0 Step-1
    For i=0 To maxgrid-1
    
      Select imageType
      
        Case "altitude"
          red = (geostat(numHmap,i,j)\height - geotype(numHmap)\minHeight)*ratio
          green = red:blue = Red ; (greyscale)

          
        Case "temperature"
          If i>1 And i<maxgrid-1 And j>1 And j<maxgrid-1 And geostat(numHmap,i,j)\isWater>0 And (geostat(numHmap,i-1,j)\isWater=0 Or geostat(numHmap,i+1,j)\isWater=0 Or geostat(numHmap,i,j-1)\isWater=0 Or geostat(numHmap,i,j+1)\isWater=0)
            color=0
          Else
            red = (geostat(numHmap,i,j)\temperature - tpol)*255/(teq-tpol)
            If red>255
              red=255
            EndIf
            If red<0
              red=0
            EndIf
            blue = 255-red
            green=blue/2
          EndIf
          
        Case "slope"
          red = geostat(numHmap,i,j)\slope*32
          If red > 255
            red = 255
          EndIf
          green = red:blue = Red ; (greyscale)
          
        Case "terrain"
          ; Add ice/snow, slope marking
            red=Red(geostat(numHmap,i,j)\couleur)
            green=Green(geostat(numHmap,i,j)\couleur)
            blue=Blue(geostat(numHmap,i,j)\couleur)
          
            ; Ice / snow
            If geostat(numHmap,i,j)\temperature < 0
              If geostat(numHmap,i,j)\temperature < 0
                icegradient= -geostat(numHmap,i,j)\temperature / 5
              EndIf
              
              If icegradient>1
                icegradient=1
              EndIf         
              icecolor=247+Random(8)
              
              red=red*(1-icegradient) + icecolor*icegradient
              green=green*(1-icegradient) + icecolor*icegradient
              blue=blue*(1-icegradient) + icecolor*icegradient        
            EndIf 
            
            ; Slopes are darker
            If geostat(numHmap,i,j)\isWater<2    
              red-geostat(numHmap,i,j)\slope * 4
              green-geostat(numHmap,i,j)\slope * 4
              blue-geostat(numHmap,i,j)\slope * 4        
            EndIf     
              
            ; RGB must stay between 0-255
            If red<0 
              red=0
            Else
              If red>255
                red=255
              EndIf
            EndIf
            If green<0 
              green=0
            Else
              If green>255
                green=255
              EndIf
            EndIf
            If blue<0 
              blue=0
            Else
              If blue>255
                blue=255
              EndIf
            EndIf
          
      EndSelect
      
      CompilerIf #USEAPIDRAWING = #True
        color=RGB(blue,green,red)
        *pixel\l=color
        *pixel + 4
      CompilerElse
        color=RGB(red,green,blue)
        Plot(i,j,color)
      CompilerEndIf
    Next i
  Next j
  
  CompilerIf #USEAPIDRAWING = #True
    SetDIBits_(hDC,hBmp,1,maxgrid+1,*mem,bmi,#DIB_RGB_COLORS)
  CompilerEndIf
  
  StopDrawing()
  
  CompilerIf #USEAPIDRAWING = #True
    FreeMemory(*mem)
  CompilerEndIf

EndProcedure


;- --- Zoom ---
; Let's consider a part of the grid:
; 
; A  ab B
; ac m  bd
; C  cd D
; 
; -> ABCD are the known heights of the parent.
; -> efghi are the heights you want to fill. 
; With Diamond Square, you are generating those heights are a function of the adjacent heights:
; 
; m  = ds4 ( A, B, C, D)
; ab = ds2 ( A, B )
; ac = ds2 ( A, C )
; bd = ds2 ( B, D )
; cd = ds2 ( C, D )
; 
; ds2 and ds4 do the following: they average their arguments, and add a small pseudo-random displacement 
; that is scaled by the frequency of the grid. In other words:
; 
; ds2 ( N, M ) = (N + M) / 2 + h * scale
; ds4 ( N, M, O, P ) = (N + M + O + P) / 4 + h * scale
; 
; You can choose scale as you want to make terrain more or less rough, the idea being that it gets lower and lower 
; each time you go deeper in the zoom level.
Procedure ZoomX2(numHmap.l,maxgrid.l,zoomlevel.l,seed.f,iori.l,jori.l,xori.f,yori.f,unitsize.f)
  Protected Dim temp.geostat_struct(maxgrid,maxgrid)
  Protected i.l,j.l,k.l,n.l,iloc.l,jloc.l
  Protected A.f,B.f,C.f,D.f,noiseScale.f
  Protected heightAboveWater.l,h.l
  Protected exp.f,cumul.f
  Protected p.f,h1.f,h2.f
  Protected red1.l,red2.l,green1.l,green2.l,blue1.l,blue2.l,red.f,green.f,blue.f
  Protected ratio.f = 255.0/(geotype(numHmap)\maxHeight-geotype(numHmap)\minHeight)
  Protected totalNS.l=maxgrid*#MAXUNITSIZE
   
  noiseScale=unitSize/(#MAXUNITSIZE/4)

  For j=0 To maxgrid-2 Step 2
    For i=0 To maxgrid-2 Step 2
      iloc=iori+i/2:jloc=jori+j/2
      A=geostat(numhMap,iloc,jloc)\height
      temp(i,j)\height=A
      temp(i,j)\latitude=geostat(numhMap,iloc,jloc)\latitude
      temp(i,j)\slope=geostat(numhMap,iloc,jloc)\slope
      temp(i,j)\temperature=geostat(numhMap,iloc,jloc)\temperature
      temp(i,j)\isWater=geostat(numhMap,iloc,jloc)\isWater
      temp(i,j)\couleur=geostat(numhMap,iloc,jloc)\couleur
      For k=0 To 5
        temp(i,j)\probaterrain[k]=geostat(numhMap,iloc,jloc)\probaterrain[k]
      Next k
      
      iloc=iori+1+i/2
      B=geostat(numhMap,iloc,jloc)\height
      temp(i+2,j)\height=B
      temp(i+2,j)\latitude=geostat(numhMap,iloc,jloc)\latitude
      temp(i+2,j)\slope=geostat(numhMap,iloc,jloc)\slope
      temp(i+2,j)\temperature=geostat(numhMap,iloc,jloc)\temperature
      temp(i+2,j)\isWater=geostat(numhMap,iloc,jloc)\isWater
      temp(i+2,j)\couleur=geostat(numhMap,iloc,jloc)\couleur
      For k=0 To 5
        temp(i+2,j)\probaterrain[k]=geostat(numhMap,iloc,jloc)\probaterrain[k]
      Next k
      
      iloc=iori+i/2:jloc=jori+1+j/2
      C=geostat(numhMap,iloc,jloc)\height
      temp(i,j+2)\height=C
      temp(i,j+2)\latitude=geostat(numhMap,iloc,jloc)\latitude
      temp(i,j+2)\slope=geostat(numhMap,iloc,jloc)\slope
      temp(i,j+2)\temperature=geostat(numhMap,iloc,jloc)\temperature
      temp(i,j+2)\isWater=geostat(numhMap,iloc,jloc)\isWater
      temp(i,j+2)\couleur=geostat(numhMap,iloc,jloc)\couleur
      For k=0 To 5
        temp(i,j+2)\probaterrain[k]=geostat(numhMap,iloc,jloc)\probaterrain[k]
      Next k
      
      iloc=iori+1+i/2
      D=geostat(numhMap,iloc,jloc)\height
      temp(i+2,j+2)\height=D
      temp(i+2,j+2)\latitude=geostat(numhMap,iloc,jloc)\latitude
      temp(i+2,j+2)\slope=geostat(numhMap,iloc,jloc)\slope
      temp(i+2,j+2)\temperature=geostat(numhMap,iloc,jloc)\temperature
      temp(i+2,j+2)\isWater=geostat(numhMap,iloc,jloc)\isWater
      temp(i+2,j+2)\couleur=geostat(numhMap,iloc,jloc)\couleur
      For k=0 To 5
        temp(i+2,j+2)\probaterrain[k]=geostat(numhMap,iloc,jloc)\probaterrain[k]
      Next k
      
      
;       A=temp(i,j)\height
;       B=temp(i+2,j)\height
;       C=temp(i,j+2)\height
;       D=temp(i+2,j+2)\height
;       Debug Str(i)+","+Str(j)
;       Debug "A = "+StrF(A)+" | B = "+StrF(B)+" | C = "+StrF(C)+" | D = "+StrF(D)
;       Debug "ab = "+StrF(temp(i+1,j)\height)+" | ac = "+StrF(temp(i,j+1)\height)+" | bd = "+StrF(temp(i+2,j+1)\height)+" | cd = "+StrF(temp(i+1,j+2)\height)+" | m = "+StrF(temp(i+1,j+1)\height)
;       iloc=iori+i/2:jloc=jori+j/2
      
      
      ; Height / latitude / temperature for new points
      If i=0
        temp(i,j+1)\height = (A+C)/2  + noise3D(xori+(i*unitSize),yori+((j+1)*unitSize),seed) * noiseScale ;ac
        terrainLatitude(temp(i,j+1),(yori+((j+1)*unitSize)),totalNS)
        terrainTemperature(temp(i,j+1),geotype(numHmap)\minHeight)
      EndIf      
      If j=0
        temp(i+1,j)\height = (A+B)/2  + noise3D(xori+((i+1)*unitSize),yori+(j*unitSize),seed) * noiseScale ; ab
        terrainLatitude(temp(i+1,j),(yori+(j*unitSize)),totalNS)
        terrainTemperature(temp(i+1,j),geotype(numHmap)\minHeight)
      EndIf
      
      temp(i+1,j+1)\height=(A+B+C+D)/4  + noise3D(xori+((i+1)*unitSize),yori+((j+1)*unitSize),seed) * noiseScale ; m
      terrainLatitude(temp(i+1,j+1),(yori+((j+1)*unitSize)),totalNS)
      terrainTemperature(temp(i+1,j+1),geotype(numHmap)\minHeight)
      
      temp(i+2,j+1)\height = (B+D)/2  + noise3D(xori+((i+2)*unitSize),yori+((j+1)*unitSize),seed) * noiseScale ; bd
      terrainLatitude(temp(i+2,j+1),(yori+((j+1)*unitSize)),totalNS)
      terrainTemperature(temp(i+2,j+1),geotype(numHmap)\minHeight)
      
      temp(i+1,j+2)\height = (C+D)/2  + noise3D(xori+((i+1)*unitSize),yori+((j+2)*unitSize),seed) * noiseScale ; cd
      terrainLatitude(temp(i+1,j+2),(yori+((j+2)*unitSize)),totalNS)
      terrainTemperature(temp(i+1,j+2),geotype(numHmap)\minHeight)
            
      ; Slope
      terrainSlope(temp,i,j)
      terrainSlope(temp,i+1,j)
      terrainSlope(temp,i,j+1)
      terrainSlope(temp,i+1,j+1)
      
      ; Terrain types
      terrainColor(temp(i,j)) 
      terrainColor(temp(i+1,j)) 
      terrainColor(temp(i,j+1)) 
      terrainColor(temp(i+1,j+1))      
      
;       Debug "-> m = "+StrF(temp(i+1,j+1)\height)+" (noise = "+StrF(temp(i+1,j+1)\height-(A+B+C+D)/4)+") Temp° = " + StrF(temp(i+1,j+1)\temperature)
;       Debug "-> ab = "+StrF(temp(i+1,j)\height)+" (noise = "+StrF(temp(i+1,j)\height-(A+B)/2)+") Temp° = " + StrF(temp(i+1,j)\temperature)
;       Debug "-> ac = "+StrF(temp(i,j+1)\height)+" (noise = "+StrF(temp(i,j+1)\height-(A+C)/2)+") Temp° = " + StrF(temp(i,j+1)\temperature)
;       Debug "-> bd = "+StrF(temp(i+2,j+1)\height)+" (noise = "+StrF(temp(i+2,j+1)\height-(B+D)/2)+") Temp° = " + StrF(temp(i+2,j+1)\temperature)
;       Debug "-> cd = "+StrF(temp(i+1,j+2)\height)+" (noise = "+StrF(temp(i+1,j+2)\height-(C+D)/2)+") Temp° = " + StrF(temp(i+1,j+2)\temperature)
;       Debug ""
      
    Next i
  Next j

  ; Copy the zoomed portion into the Geostat array
  For i=0 To maxgrid-1
    For j=0 To maxgrid-1
      geostat(numhMap,i,j)\height=temp(i,j)\height
      geostat(numhMap,i,j)\latitude=temp(i,j)\latitude
      geostat(numhMap,i,j)\slope=temp(i,j)\slope
      geostat(numhMap,i,j)\temperature=temp(i,j)\temperature
      geostat(numhMap,i,j)\isWater=temp(i,j)\isWater
      geostat(numhMap,i,j)\couleur=temp(i,j)\couleur
      For k=0 To 5
        geostat(numhMap,i,j)\probaterrain[k]=temp(i,j)\probaterrain[k]
      Next k
    Next j
  Next i

EndProcedure
DisableExplicit


;- ---------- Main program ----------
;- Initial map design
makeDiamondSquare(0,maxgrid,unitsize,seed,0,0)
ComputeGeoStats(0,maxgrid,zoomlevel,waterLevel,tpol,teq,seed)

CreateImage(0,maxgrid,maxgrid,32)
CreateImage(1,maxgrid,maxgrid,32)
MakeTerrainImage(0,0,maxgrid,"terrain")
MakeTerrainImage(1,0,maxgrid,"altitude")


;- Window & Gadgets
OpenWindow(#Window_0, 215, 143, 1049, 710, "Terrain generator",  #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar )
  ImageGadget(#Image_1, 15, 5, 512, 512, 1)
  ImageGadget(#Image_2, 530, 5, 512, 512, 0)
  TextGadget(#SeedLabel, 20, 530, 40, 20, "Seed:", #PB_Text_Right)
  SetGadgetFont(#SeedLabel, FontID1)
  StringGadget(#FL_seed, 65, 525, 85, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_seed, FontID1)
  StringGadget(#FL_tpol, 290, 525, 45, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_tpol, FontID1)
  TextGadget(#TpolLabel, 160, 530, 125, 20, "Polar temperature:", #PB_Text_Right)
  SetGadgetFont(#TpolLabel, FontID1)
  StringGadget(#FL_teq, 475, 525, 50, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_teq, FontID1)
  TextGadget(#teqLabel, 345, 530, 130, 20, "Equat. temperature:", #PB_Text_Right)
  SetGadgetFont(#teqLabel, FontID1)
  StringGadget(#FL_sealevel, 660, 525, 50, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_sealevel, FontID1)
  TextGadget(#sealevelLabel, 530, 530, 130, 20, "Sea level (0->255):", #PB_Text_Right)
  SetGadgetFont(#sealevelLabel, FontID1)
  StringGadget(#FL_temploss, 970, 525, 60, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_temploss, FontID1)
  TextGadget(#templossLabel, 720, 530, 250, 20, "Temperature loss by unit above water:", #PB_Text_Right)
  SetGadgetFont(#templossLabel, FontID1)
  
  OptionGadget(#RS_height, 660, 600, 85, 20, "Altitude")
  SetGadgetFont(#RS_height, FontID1)
  SetGadgetState(#RS_height,1)
  OptionGadget(#RS_slope, 750, 600, 85, 20, "Slope")
  SetGadgetFont(#RS_slope, FontID1)
  OptionGadget(#RS_temp, 840, 600, 110, 20, "Temperature")
  SetGadgetFont(#RS_temp, FontID1)
  
  Frame3DGadget(#RT_terrain, 15, 555, 415, 110, "Individual terrain settings")
  ComboBoxGadget(#CB_terrain, 25, 610, 105, 300)
  SetGadgetFont(#CB_terrain, FontID1)
  TextGadget(#terrainLabel, 25, 587, 95, 20, "Terrain Type:")
  SetGadgetFont(#terrainLabel, FontID1)
  TextGadget(#Text_17, 137, 613, 30, 20, "=>")
  SetGadgetFont(#Text_17, FontID5)
  TextGadget(#Text_18, 170, 580, 25, 75, "{")
  SetGadgetFont(#Text_18, FontID2)
  SetGadgetFont(#RT_terrain, FontID1)
  TextGadget(#LabelAverage, 285, 565, 60, 20, "Average")
  SetGadgetFont(#LabelAverage, FontID1)
  TextGadget(#LabelVariance, 355, 565, 60, 20, "Variance")
  SetGadgetFont(#LabelVariance, FontID1)
  StringGadget(#FL_heightAv, 290, 585, 50, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_heightAv, FontID1)
  StringGadget(#FL_heightVt, 360, 585, 50, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_heightVt, FontID1)
  StringGadget(#FL_tempAv, 290, 610, 50, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_tempAv, FontID1)
  StringGadget(#FL_tempVt, 360, 610, 50, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_tempVt, FontID1)
  StringGadget(#FL_slopeAv, 290, 635, 50, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_slopeAv, FontID1)
  StringGadget(#FL_slopeVt, 360, 635, 50, 25, "", #PB_String_Numeric)
  SetGadgetFont(#FL_slopeVt, FontID1)
  TextGadget(#heightLabel, 195, 587, 90, 20, "Altitude:", #PB_Text_Right)
  SetGadgetFont(#heightLabel, FontID1)
  TextGadget(#tempLabel, 195, 612, 90, 20, "Temperature:", #PB_Text_Right)
  SetGadgetFont(#tempLabel, FontID1)
  TextGadget(#slopeLabel, 195, 637, 90, 20, "Slope:", #PB_Text_Right)
  SetGadgetFont(#slopeLabel, FontID1)
  
  ButtonGadget(#BT_redraw, 435, 595, 95, 30, "&Redraw")
  SetGadgetFont(#BT_redraw, FontID1)
  ButtonGadget(#BT_close, 470, 675, 95, 30, "&Close")
  SetGadgetFont(#BT_close, FontID1)

  CheckBoxGadget(#TB_anim,660,630, 250, 25, "Animated zoom")
  SetGadgetState(#TB_anim,1)
  SetGadgetFont(#TB_anim, FontID1)

For i=1 To 5
  AddGadgetItem(#CB_terrain,-1,terrain(i)\name)
Next i

SetGadgetText(#FL_seed,Str(seed))
SetGadgetText(#FL_tpol,StrF(tpol,2))
SetGadgetText(#FL_teq,StrF(teq,2))
SetGadgetText(#FL_seaLevel,Str(waterLevel))
SetGadgetText(#FL_temploss,StrF(equivHeightToTemp,3))

SetGadgetState(#image_1,ImageID(0))
SetGadgetState(#image_2,ImageID(1))


;- Zoom area sprite
InitSprite()
OpenWindowedScreen(WindowID(#Window_0),15,5,512,512,0,0,0)
StartDrawing(ScreenOutput())
  DrawImage(ImageID(0),0,0)
StopDrawing()

CreateSprite(1,256,256)
StartDrawing(SpriteOutput(1))
  Box(0,0,255,255,$000000)
  DrawingMode(#PB_2DDrawing_Outlined)
  Box(0,0,255,255,$00AA00):Box(2,2,251,251,$00AA00)
  Box(1,1,253,253,$00DD00)
StopDrawing()


;- Main loop
Repeat
  
  EventID = WaitWindowEvent()      
  Select EventID
    
    Case 0         
      Delay(1)
    
    Case #PB_Event_CloseWindow
      Quit = 1
      
    Case #PB_Event_Gadget
      
      Select EventGadget()
                     
        Case #RS_height
          MakeTerrainImage(1,0,maxgrid,"altitude")          
          SetGadgetState(#image_2,ImageID(1))   
        Case #RS_temp
          MakeTerrainImage(1,0,maxgrid,"temperature")          
          SetGadgetState(#image_2,ImageID(1))   
        Case #RS_slope
          MakeTerrainImage(1,0,maxgrid,"slope")          
          SetGadgetState(#image_2,ImageID(1))   
               
        Case #CB_terrain
          n=GetGadgetState(#CB_terrain)+1
          If n>0
            SetGadgetText(#FL_heightAv,StrF(terrain(n)\heightAv,2))
            SetGadgetText(#FL_heightVt,StrF(terrain(n)\heightVt,2))
            SetGadgetText(#FL_tempAv,StrF(terrain(n)\temperatureAv,2))
            SetGadgetText(#FL_tempVt,StrF(terrain(n)\temperatureVt,2))
            SetGadgetText(#FL_slopeAv,StrF(terrain(n)\slopeAv,2))
            SetGadgetText(#FL_slopeVt,StrF(terrain(n)\slopeVt,2))
          EndIf
        
        Case #FL_heightAv
          n=GetGadgetState(#CB_terrain)+1
          If n>0
            terrain(n)\heightAv = ValF(GetGadgetText(#FL_heightAv))
          EndIf
        Case #FL_heightVt
          n=GetGadgetState(#CB_terrain)+1
          If n>0
            terrain(n)\heightVt = ValF(GetGadgetText(#FL_heightVt))
          EndIf
        Case #FL_tempAv
          n=GetGadgetState(#CB_terrain)+1
          If n>0
            terrain(n)\temperatureAv = ValF(GetGadgetText(#FL_tempAv))
          EndIf
        Case #FL_tempVt
          n=GetGadgetState(#CB_terrain)+1
          If n>0
            terrain(n)\temperatureVt = ValF(GetGadgetText(#FL_tempVt))
          EndIf
        Case #FL_slopeAv
          n=GetGadgetState(#CB_terrain)+1
          If n>0
            terrain(n)\slopeAv = ValF(GetGadgetText(#FL_slopeAv))
          EndIf
        Case #FL_heightVt
          n=GetGadgetState(#CB_terrain)+1
          If n>0
            terrain(n)\slopeVt = ValF(GetGadgetText(#FL_slopeVt))
          EndIf

        Case #BT_redraw
          seed = Val(GetGadgetText(#FL_seed))
          tpol = ValF(GetGadgetText(#FL_tpol))
          teq = ValF(GetGadgetText(#FL_teq))
          waterLevel = Val(GetGadgetText(#FL_seaLevel))
          equivHeightToTemp = ValF(GetGadgetText(#FL_temploss))
          zoomLevel=0:unitSize=#MAXUNITSIZE/Pow(2,zoomLevel)
          xori=0:yori=0
          
          makeDiamondSquare(0,maxgrid,unitsize,seed,0,0)
          ComputeGeoStats(0,maxgrid,zoomLevel,waterLevel,tpol,teq,seed)
          
          MakeTerrainImage(0,0,maxgrid,"terrain")
          If GetGadgetState(#RS_height)=1
            MakeTerrainImage(1,0,maxgrid,"altitude")
          EndIf
          If GetGadgetState(#RS_temp)=1
            MakeTerrainImage(1,0,maxgrid,"temperature")
          EndIf
          If GetGadgetState(#RS_slope)=1
            MakeTerrainImage(1,0,maxgrid,"slope")
          EndIf
          SetGadgetState(#image_1,ImageID(0))
          SetGadgetState(#image_2,ImageID(1))
        
        ;- Left-click on map to zoom       
        Case #image_1
          x=xzoom:y=yzoom
          If x>=0 And y>=0 And x<=maxgrid-255 And y<=maxgrid-255 And UnitSize>1
            xori+(x*unitSize):yori+(y*unitSize)
            zoomlevel+1
            unitSize=#MAXUNITSIZE/Pow(2,zoomlevel)
            ZoomX2(0,maxgrid,zoomlevel,seed,x,y,xori,yori,unitsize)

            MakeTerrainImage(0,0,maxgrid,"terrain")
            If GetGadgetState(#RS_height)=1
              MakeTerrainImage(1,0,maxgrid,"altitude")
            EndIf
            If GetGadgetState(#RS_temp)=1
              MakeTerrainImage(1,0,maxgrid,"temperature")
            EndIf
            If GetGadgetState(#RS_slope)=1
              MakeTerrainImage(1,0,maxgrid,"slope")
            EndIf
            
            ; Animated zoom
            If GetGadgetState(#TB_anim)=1
              For i=256 To 1 Step-16
                StartDrawing(ScreenOutput())
                  DrawImage(ImageID(0),x-((256-i)*(x/256.0)),y-((256-i)*(y/256.0)),maxgrid-i,maxgrid-i)
                StopDrawing()
                FlipBuffers()
              Next i
            EndIf
            
            StartDrawing(ScreenOutput())
              DrawImage(ImageID(0),0,0)
            StopDrawing()                
            FlipBuffers()
            
            SetGadgetState(#image_1,ImageID(0))
            SetGadgetState(#image_2,ImageID(1))
          EndIf
                                       
        Case #BT_close
          quit=1          
          
      EndSelect 
      
  EndSelect
  
  ;- Move zoom area
  If ElapsedMilliseconds()-timer>40
    StartDrawing(ScreenOutput())
      DrawImage(ImageID(0),0,0)
      xzoom+127:yzoom+127
      If xzoom>=0 And yzoom>=0 And xzoom<maxgrid And yzoom<maxgrid
        DrawingMode(#PB_2DDrawing_Transparent)
        DrawText(5,5,"x,y = "+Str(xori+xzoom*unitsize)+","+Str(yori+yzoom*unitsize)+" \ "+"Zoom x"+Str(Pow(2,zoomlevel))+", 1 pixel = "+StrF(unitSize,2),$FFFF00)
        DrawText(5,20,"Lat. = "+ReplaceString(StrF(90-geostat(0,xzoom,yzoom)\latitude*90,2),".","°")+"' / Height = "+StrF(geostat(0,xzoom,yzoom)\height,2)+" / Slope = "+StrF(geostat(0,xzoom,yzoom)\slope,2)+" / Temp. = "+StrF(geostat(0,xzoom,yzoom)\temperature,2)+"°C",$FFFF00)
      EndIf
    StopDrawing()
    xzoom=WindowMouseX(#Window_0)-15-127
    yzoom=WindowMouseY(#Window_0)-5-127
    If xzoom>=0 And xzoom<maxgrid-255 And yzoom>=0 And yzoom<maxgrid-255
      DisplayTransparentSprite(1,xzoom,yzoom)
    EndIf
    FlipBuffers()
    timer=ElapsedMilliseconds()
  EndIf

Until Quit
[EDIT] fixed a forgotten "CompilerIf #USEAPIDRAWING = #True" in the "myBITMAPINFO" structure definition.
Last edited by Kelebrindae on Fri Oct 09, 2009 7:50 pm, edited 1 time in total.
User avatar
Rook Zimbabwe
Addict
Addict
Posts: 4322
Joined: Tue Jan 02, 2007 8:16 pm
Location: Cypress TX
Contact:

Re: [PB 4.31] Zoomable random terrain

Post by Rook Zimbabwe »

Highly COOL!

NICE WORK Kelindbrae!

(and FIRST!!!) :mrgreen:
Binarily speaking... it takes 10 to Tango!!!

Image
http://www.bluemesapc.com/
dige
Addict
Addict
Posts: 1391
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: [PB 4.31] Zoomable random terrain

Post by dige »

Wow..that rocks! :shock:
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Re: [PB 4.31] Zoomable random terrain

Post by djes »

Impressive!
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Re: [PB 4.31] Zoomable random terrain

Post by Hroudtwolf »

Respect! Nice work.
Thank you for sharing.

Regards
Wolf
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: [PB 4.31] Zoomable random terrain

Post by idle »

that's really cool.
Kelebrindae
Enthusiast
Enthusiast
Posts: 151
Joined: Tue Apr 01, 2008 3:23 pm

Re: [PB 4.31] Zoomable random terrain

Post by Kelebrindae »

Thanks to all of you! :D

The next step is in the fifth part of the serie "post all my old code before the 4.40 is out"...
WilliamL
Addict
Addict
Posts: 1252
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: [PB 4.31] Zoomable random terrain

Post by WilliamL »

Might work on a Mac if...

Code: Select all

Structure myBITMAPINFO
  bmiHeader.BITMAPINFOHEADER ; Line 108: Structure not found: BITMAPINFOHEADER
  bmiColors.RGBQUAD[1] ; Line 109: Structure not found: RGBQUAD
EndStructure
could be replaced with something that doesn't give an error. Of course, if this is changed, there may be other incompatibilities that still cause problems.
MacBook Pro-M1 (2021), Sequoia 15.4, PB 6.20
Kelebrindae
Enthusiast
Enthusiast
Posts: 151
Joined: Tue Apr 01, 2008 3:23 pm

Re: [PB 4.31] Zoomable random terrain

Post by Kelebrindae »

@WilliamL:
Oh yes, my bad: I forgot to encase this definition in a "CompilerIf #USEAPIDRAWING = #True" :oops:

I have fixed the code; you can try again...
WilliamL
Addict
Addict
Posts: 1252
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: [PB 4.31] Zoomable random terrain

Post by WilliamL »

Thanks for the update.

I set

Code: Select all

#USEAPIDRAWING = #False
and it is just about working.

Now I get an Invalid Memory Access in line 960 (in vers. 4.31, 4.40PPCb3, 4.40x86b3)

Code: Select all

ImageGadget(#Image_1, 15, 5, 512, 512, 1)
This may not be worth pursuing as it may be a bug in the Mac version of pb that has been reported before.

(what do you think Freak?)
MacBook Pro-M1 (2021), Sequoia 15.4, PB 6.20
Post Reply