This code is the logical sequel of this post : once you've generated the map + heightmap of a terrain, why not generate the 3D mesh ?
Creating a terrain mesh should be relatively easy (affect each point of the heightmap to a point of the terrain matrix, then texture it with the map).
What's a little trickier is to create a planet mesh, because it's quite unpractical to try to wrap a square map on a spherical object...
So here's what I did:
- create a cube;
- subdivide each side of the cube as much as possible (to heighten level of detail)
- "spherify" the mesh, by normalizing the distance between the vertices and the mesh's center;
- generate 6 heightmaps (using a slightly modified version of the "random terrain" algorithm), one for each side of the cube, and apply them to the sides;
- cut out the sides to make them 6 different meshes;
- Generate 6 maps from the heightmaps and apply them as textures to these meshes;
Et voila !
Controls:
- Mouse: rotates the planet
- + / - / mouse wheel: zoom / unzoom
- F1 : switches between wireframe and normal mode
- Space : starts the next phase of the process
Notes:
- Set the #PLANETSEED constant to another value to generate a different planet
- Set the #RELIEFEXAGERATION constant to a lower value to get a less "cartoonish" result
- Set the #USEAPIDRAWING constant to #False if you compile with 4.40, Mac, or Linux
And don't forget to turn the Debugger Off, or else it will be really slow!
(oops, code's too big; I have to split it in two parts)
Part 1:
Code: Select all
; Author: Kelebrindae
; Date: july,9, 2008
; PB version: v4.10
; OS: Windows XP
; ---------------------------------------------------------------------------------------------------------------
; Purpose:
; ---------------------------------------------------------------------------------------------------------------
; - Starts from a cube
; - Subdivides each side of the cube to increase detail
; - "Spherify" the cube
; - Then, generates 6 heightmaps and apply them to each "side" of the sphere (the "side" attribute for each poly
; of the mesh is inherited from the cube mesh).
; - As PB can't texture each "side" with a different material, cut out the sphere into 6 sub-mesh (one per "side")
; - Generate 6 textures from the heightmaps, and apply them to the "sides" entities.
;
; Presto! Instant planet!
;
;- Constants
#e = 2.718281828459045235
#SQRT03=0.577350269189625764
#MAXMESH=10
#PLANETSEED=4321 ; change this to create a different planet
#WATERLEVEL=127
#RELIEFEXAGERATION=0.2 ; Change this to increase/decrease relief height
#USEAPIDRAWING = #True ; set this to false in 4.40, or to compile under Mac or Linux
Enumeration
#TOPSIDE
#BOTTOMSIDE
#RIGHTSIDE
#LEFTSIDE
#FRONTSIDE
#BACKSIDE
EndEnumeration
;- Data structures
Structure VoronoiPoint2D ; used to create the Voronoi Diagram
x.l
y.l
dist.f
EndStructure
Structure Vector3 ; used in vector maths (normalization)
x.f
y.f
z.f
EndStructure
Structure Vertex ; used to generate meshes
px.f
py.f
pz.f
nx.f
ny.f
nz.f
couleur.l
U.f
V.f
EndStructure
Structure Polygon ; used to generate meshes
numVert1.w
numVert2.w
numVert3.w
EndStructure
Structure duplicateVert ; used to store lists of vertices that are at the same position
numVert.w
nbDup.w
*PtrListVert.w
done.b
EndStructure
Structure dynMesh_struct ; used to store infos about dynamically generated meshes
id.s
numMesh.l
sizeX.f
sizeY.f
sizeZ.f
nbVert.l
nbTri.l
*vertexBuffer.Vertex
*dupVert.duplicateVert
*polygonBuffer.Polygon
*faceSideBuffer.b ; 1=up, 2=bottom,3=left,4=right,5=front,6=back
EndStructure
Structure geostat_struct ; used to store infos about terrains
latitude.f
slope.l
temperature.f
height.l
probaterrain.f[10]
isWater.b
couleur.l
EndStructure
Structure terrain_struct ; used to store specifications for each terrain type
name.s
heightAv.f
heightVt.f
slopeAv.f
slopeVt.f
temperatureAv.f
temperatureVt.f
couleur.l
EndStructure
Structure myBITMAPINFO
bmiHeader.BITMAPINFOHEADER
bmiColors.RGBQUAD[1]
EndStructure
;- Global definitions
Global Dim dynMesh.dynMesh_struct(#MAXMESH)
Global Dim couleur(6)
couleur(#TOPSIDE)=RGB(255,0,0)
couleur(#BOTTOMSIDE)=RGB(0,255,0)
couleur(#LEFTSIDE)=RGB(0,0,255)
couleur(#RIGHTSIDE)=RGB(255,255,0)
couleur(#FRONTSIDE)=RGB(255,0,255)
couleur(#BACKSIDE)=RGB(0,255,255)
Global CameraMode.b
Global planetMesh.l,planetEntity.l = 0,newMesh.l,newEntity.l=10
Global *ptrPoly.Polygon,*ptrVert.vertex,*ptrSide.b
Global anglex.f = 0:angley.f = 0
Global side.b,phase.b
Global maxgrid.l = 512 ; must be a power of 2
Global Dim diamond.b(maxgrid,maxgrid)
Global Dim voronoi.b(maxgrid,maxgrid)
Global Dim heightmap.b(6,maxgrid,maxgrid)
Global Dim geostat.geostat_struct(6,maxgrid,maxgrid)
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 = -15,teq.f = 50
;- ---------- Procedures ----------
EnableExplicit
;- --- Vector procedures ---
;************************************************************************************
; Name: NormalizeVector
; Purpose: Normalizes a vector to the length 1 without changing its orientation.
; Parameters:
; - vector to normalize
;************************************************************************************
Procedure.l NormalizeVector(*Vec1.Vector3)
Protected length.f
length.f = Sqr(*Vec1\x * *Vec1\x + *Vec1\y * *Vec1\y + *Vec1\z * *Vec1\z)
*Vec1\x / length
*Vec1\y / length
*Vec1\z / length
EndProcedure
; Same thing, Macro version
Macro NORME(V)
(Sqr(V\x * V\x + V\y * V\y + V\z * V\z))
EndMacro
;- --- Heightmaps generation ---
;************************************************************************************
; 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(maxgrid.l,dispersion.f,seed.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
Protected dispStep.f,min.f=999999,max.f=-999999,ratio.f
Protected Dim temp.f(maxgrid,maxgrid)
RandomSeed(seed)
gridstep=maxgrid
dispStep=dispersion
; 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
; Diamond value
temp(i,j)=average+u*dispStep
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+temp(i2,j)+temp(i2,j)
Else
If i2>maxgrid
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+temp(i,j2)+temp(i,j2)
Else
If j2>maxgrid
average+temp(i,j1)+temp(i,j1)
Else
average+temp(i,j1)+temp(i,j2)
EndIf
EndIf
average=average/4
; calculate random value between -1 And 1
u=Random(16384)/8192-1
temp(i,j)=average+u*dispStep
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
dispStep/2
gridstep/2
Wend
; Keep values in Byte range (between 0 and 255)
For i=0 To maxgrid-1
For j=0 To maxgrid-1
; 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
ratio=255.0/(max-min)
For i=0 To maxgrid-1
For j=0 To maxgrid-1
n=(temp(i,j)-min)*ratio
If n<0
diamond(i,j)=0
Else
If n>255
diamond(i,j)=255
Else
diamond(i,j)=n
EndIf
EndIf
Next j
Next i
EndProcedure
;************************************************************************************
; Name: makeVoronoi
; Purpose: Creates a modified Voronoi diagram that can be used to simulate tectonic
; plates
; Parameters:
; - size of the array (a square of size x size)
; - number of regions
; - number of points in each region (a point is the "center" of a Voronoi plate)
; - seed: albeit the diagram is random, a seed always produces the same result
; Return-value: none, but the result is stored in the "voronoi" array
;************************************************************************************
Procedure makeVoronoi(maxgrid.l,nbregion.l,nbpts.l,seed.l)
Protected NewList ftpoint.VoronoiPoint2D()
Protected Dim coef.f(nbregion*nbregion*nbpts)
Protected i.l,j.l,k.l,n.l
Protected min.f=999999,max.f=-999999,ratio.f
Protected Dim temp.f(maxgrid,maxgrid)
RandomSeed(seed)
coef(0)=-1
coef(1)=-2
coef(2)=0.5
; In each region of the grid, place some points
For i=0 To nbregion-1
For j =0 To nbregion-1
n=Random(nbpts)
For k=1 To n
AddElement(ftpoint())
ftpoint()\x=i*(maxgrid/nbregion)+Random(maxgrid/nbregion)
ftpoint()\y=j*(maxgrid/nbregion)+Random(maxgrid/nbregion)
;Debug StrF(ftpoint()\x)+","+StrF(ftpoint()\y)
Next k
Next j
Next i
; For each cell of the grid, compute distance to each point, sort points according to distance.
; => value = coef * distance
For i=0 To maxgrid-1
For j=0 To maxgrid-1
ForEach ftpoint()
ftpoint()\dist = Sqr((ftpoint()\x-i)*(ftpoint()\x-i)+(ftpoint()\y-j)*(ftpoint()\y-j))
Next
SortStructuredList(ftpoint(),0,OffsetOf(VoronoiPoint2D\dist),#PB_Sort_Float)
k=0
ForEach ftpoint()
temp(i,j) + coef(k)*ftpoint()\dist
k+1
Next
; 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
; Keep values in Byte range (between 0 and 255)
ratio=255.0/(max-min)
For i=0 To maxgrid-1
For j=0 To maxgrid-1
n=(temp(i,j)-min)*ratio
If n<0
voronoi(i,j)=0
Else
If n>255
voronoi(i,j)=255
Else
voronoi(i,j)=n
EndIf
EndIf
Next j
Next i
EndProcedure
;************************************************************************************
; Name: MakeHeightmap
; Purpose: Mix a diamond-square array and a Voronoi diagram to obtain the final heightmap
; Parameters:
; - number of the heightmap
; - size of the array (a square of size x size); Diamond, Voronoi, and Heightmap must
; have the same size
; - use the diamond-square array or not
; - use the Voronoi diagram or not
; Return-value: none, but the result is stored in the "heightmap" array
;************************************************************************************
Procedure makeHeightmap(numHmap.b,maxgrid.l,Diamond.b,Voronoi.b)
Protected i.l,j.l,k1.l,k2.l
Protected min.f=999999,max.f=-999999,ratio.f,temp.f
Protected Dim temp.f(maxgrid,maxgrid)
; for each point of the array:
For i=0 To maxgrid-1
For j=0 To maxgrid-1
; both arrays will be used
If Voronoi=#True And Diamond=#True
k1=(diamond(i,j) & 255)
k2=(voronoi(i,j) & 255)
temp(i,j) = k1*k2
Else
; only diamond-square
If Diamond=#True
k1=(diamond(i,j) & 255)
temp(i,j) = k1
Else
; only Voronoi
If Voronoi=#True
k2=(voronoi(i,j) & 255)
temp(i,j) = k2
EndIf
EndIf
EndIf
; Store 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
; The heightmap values must be between 0 and 255
ratio = 255.0/(max-min)
For i=0 To maxgrid-1
For j=0 To maxgrid-1
temp = (temp(i,j)-min)*ratio
;Debug temp
If temp<0
heightmap(numHmap,i,j)=0
Else
If temp>255
heightmap(numHmap,i,j)=255
Else
heightmap(numHmap,i,j)=temp
EndIf
EndIf
Next j
Next i
EndProcedure
;************************************************************************************
; Name: eraseSeam
; Purpose: Blends the seam between two heightmaps
; Parameters:
; - number of the first heightmap
; - side of the first heightmap to blend (top, bottom, left, or right)
; - number of the second heightmap
; - side of the second heightmap to blend (top, bottom, left, or right)
; - size of the array (a square of size x size); both Heightmaps must
; have the same size
; - percentage of that size that will be blended
; Return-value: none
;************************************************************************************
Procedure eraseSeam(numHmap1.l,side1.s,numHmap2.l,side2.s,maxgrid.l,percent.b)
Protected i.l,j.l,width.l,seamwidth.l,color.l
Protected x1.l,y1.l,x2.l,y2.l
Protected h1.l,h2.l,h.l
Protected ratio1.f,ratio2.f
width = maxgrid-1
seamwidth = ( width*percent/100 )/2
For i=0 To seamwidth
ratio1=(seamwidth-(i/2))/seamwidth
ratio2=(i/2)/seamwidth
For j=0 To maxgrid-1
Select side1
Case "top"
x1 = j
y1 = seamwidth-i
Case "bottom"
x1 = j
y1 = width-seamwidth+i
Case "left"
x1 = seamwidth-i
y1 = j
Case "right"
x1 = width-seamwidth+i
y1 = j
EndSelect
Select side2
Case "top"
x2 = j
y2 = seamwidth-i
If side1 = "top" Or side1 = "right"
x2=maxgrid-1-j
EndIf
Case "bottom"
x2 = j
y2 = width-seamwidth+i
If side1 = "bottom" Or side1 = "left"
x2=maxgrid-1-j
EndIf
Case "left"
x2 = seamwidth-i
y2 = j
If side1 = "bottom" Or side1 = "left"
x2=maxgrid-1-j
EndIf
Case "right"
x2 = width-seamwidth+i
y2 = j
If side1 = "top" Or side1 = "right"
x2=maxgrid-1-j
EndIf
EndSelect
h1=heightmap(numHmap1,x1,y1) & 255
h2=heightmap(numHmap2,x2,y2) & 255
h=h1*ratio1 + h2*ratio2
heightmap(numHmap1,x1,y1)=h
h=h2*ratio1 + h1*ratio2
heightmap(numHmap2,x2,y2)=h
Next j
Next i
EndProcedure
;************************************************************************************
; 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,waterLevel.l,tpol.f,teq.f,seed.l,side.b)
Protected heightAboveWater.l
Protected equivHeightToTemp.f = -0.4; temperature loss when you climb up 1 unit
Protected i.l,j.l,k.l
Protected x.l,y.l,newx.l,newy.l
Protected p.l,h1.l,h2.l,maxp.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,icegradient.f,icecolor.l
Protected vector3.Vector3
RandomSeed(seed)
maxgrid-1
For i=0 To maxgrid
For j = 0 To maxgrid
; Latitude
If side=#TOPSIDE Or side=#BOTTOMSIDE
vector3\x = i-(maxgrid/2)
vector3\y = maxgrid/2
vector3\z = j-(maxgrid/2)
NormalizeVector(@vector3)
vector3\x*maxgrid/2
vector3\y*maxgrid/2
vector3\z*maxgrid/2
geostat(numHmap,i,j)\latitude = Cos(vector3\y/maxgrid *#PI)
Else
vector3\x = i-(maxgrid/2)
vector3\y = j-(maxgrid/2)
vector3\z = maxgrid/2
NormalizeVector(@vector3)
vector3\x*maxgrid/2
vector3\y*maxgrid/2
vector3\z*maxgrid/2
geostat(numHmap,i,j)\latitude = Cos(vector3\y/maxgrid *#PI)
EndIf
; Altitude
geostat(numHmap,i,j)\height = heightmap(numHmap,i,j)&255
; Slope
h1= geostat(numHmap,i,j)\height
geostat(numHmap,i,j)\slope=0
If i>0
h2=heightmap(numHmap,i-1,j)&255
p=Abs( h1 - h2 )
If p>geostat(numHmap,i,j)\slope
geostat(numHmap,i,j)\slope = p
EndIf
EndIf
If i<maxgrid
h2=heightmap(numHmap,i+1,j)&255
p=Abs( h1 - h2 )
If p>geostat(numHmap,i,j)\slope
geostat(numHmap,i,j)\slope = p
EndIf
EndIf
If j>0
h2=heightmap(numHmap,i,j-1)&255
p=Abs( h1 - h2 )
If p>geostat(numHmap,i,j)\slope
geostat(numHmap,i,j)\slope = p
EndIf
EndIf
If j<maxgrid
h2=heightmap(numHmap,i,j+1)&255
p=Abs( h1 - h2 )
If p>geostat(numHmap,i,j)\slope
geostat(numHmap,i,j)\slope = p
EndIf
EndIf
If geostat(numHmap,i,j)\slope > maxp
maxp = geostat(numHmap,i,j)\slope
EndIf
; Temperature
heightAboveWater=geostat(numHmap,i,j)\height - waterlevel
If heightAboveWater <0
heightAboveWater = 0
EndIf
geostat(numHmap,i,j)\temperature = tpol + geostat(numHmap,i,j)\latitude*(teq-tpol) + equivHeightToTemp*heightAboveWater
If geostat(numHmap,i,j)\height<waterlevel
; This point is under sea level
geostat(numHmap,i,j)\isWater=2
; underwater points => shades of blue
red1=0:green1=3:blue1=92
red2=0:green2=130:blue2=220
red=red1+(red2-red1)*(geostat(numHmap,i,j)\height/waterlevel)
green=green1+(green2-green1)*(geostat(numHmap,i,j)\height/waterlevel)
blue=blue1+(blue2-blue1)*(geostat(numHmap,i,j)\height/waterlevel)
; water depth causes temperature variation
geostat(numHmap,i,j)\temperature-(geostat(numHmap,i,j)\height-waterlevel)*0.075
Else
; This point is above sea level
geostat(numHmap,i,j)\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 - geostat(numHmap,i,j)\temperature)/terrain(k)\temperatureVt
exp + Abs(terrain(k)\slopeAv - geostat(numHmap,i,j)\slope)/terrain(k)\slopeVt
exp + Abs(terrain(k)\heightAv*waterLevel - geostat(numHmap,i,j)\height)/terrain(k)\heightVt
geostat(numHmap,i,j)\probaterrain[k] = Pow(#e,-exp)
cumul+geostat(numHmap,i,j)\probaterrain[k]
Next k
; Mix terrains colors according to probability
For k=1 To 5
geostat(numHmap,i,j)\probaterrain[k]/cumul
blue+Red(terrain(k)\couleur)*geostat(numHmap,i,j)\probaterrain[k]
green+Green(terrain(k)\couleur)*geostat(numHmap,i,j)\probaterrain[k]
red+Blue(terrain(k)\couleur)*geostat(numHmap,i,j)\probaterrain[k]
Next k
EndIf
geostat(numHmap,i,j)\couleur = RGB(red,green,blue)
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
; Add ice/snow, slope marking
For i=0 To maxgrid
For j=0 To maxgrid
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 * 3
green-geostat(numHmap,i,j)\slope * 3
blue-geostat(numHmap,i,j)\slope * 3
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
geostat(numHmap,i,j)\couleur = RGB(blue,green,red)
Next j
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
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
For j=maxgrid-1 To 0 Step-1
For i=0 To maxgrid-1
Select imageType
Case "altitude"
red = geostat(numHmap,i,j)\height
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(red,green,blue)
*pixel\l=color
*pixel + 4
CompilerElse
color=RGB(blue,green,red)
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