rem This sample program demonstrates using a geometry shader to create large amounts of foliage
rem from point data (which is significantly cheaper to transfer to the GPU than complete meshes).
rem The geometry shader uses these "control points" (which are represented by disjointed vertices
rem in a mesh vertex buffer) to expand into two crossed planes with a pre-determined grass texture 
rem and uses alpha testing for removing transparent pixels from said texture during rendering.
rem The only present optimization is the engine's built-in frustum culling which operates on the
rem individual grass patch objects; the geometry shader also rejects any grass points that 
rem lie above / below certain "tree line" limits, but the intent is that most of the grass won't
rem end up up there anyway due to weighting its spawning towards the allowed zone.
rem Note that setting up the relatively large amount of data can take a few seconds at startup; 
rem give it a while - the program hasn't hanged. It should run smoothly once loaded.
rem                                                     - Joel Sjöqvist ("Rudolpho"), 2014-07-21

INCLUDEGUARD DX11_CONSTANTS

rem Can change the terrain settings simply by changing these defines
#constant TERRAIN_WIDTH         2500.0
#constant TERRAIN_DEPTH         2500.0
#constant TERRAIN_MAX_HEIGHT    250.0
rem Can also change the number of grass tufts per grass patch
#constant GRASS_TUFTS_PER_PATCH 1000
rem These should really be constants but they can't be since they're derived from the constants above
global TREELINE_MIN             as float
global TREELINE_MAX             as float
       TREELINE_MIN             =  17.0 * TERRAIN_MAX_HEIGHT / 255.0
       TREELINE_MAX             =  112.0 * TERRAIN_MAX_HEIGHT / 255.0

rem Ensure that the DBPro engine doesn't issue automatic updates in do / repeat loops.
rem This has nothing to do with the DX11 plugin, we just don't want the DBPro core
rem to waste time updating when we're not using it anyway.
sync on
sync rate 0

rem Because the geometry shader was introduced with DirectX 10 and shader model 4, we can make this
rem application run in backwards-compatible mode with DX10 hardware.
dx11 init 1600, 900, false, "DX11 Plugin Samples: Foliage", DXVERSION_10_0
cam = dx11 get main camera()
dx11 set camera backdrop cam, 0xff006699

rem Load foliage shaders
vsFoliage = dx11 load vertex shader("fx/FloorCover.fx", "VS", "vs_4_0")
gsFoliage = dx11 load geometry shader("fx/FloorCover.fx", "GS", "gs_4_0")
psFoliage = dx11 load pixel shader("fx/FloorCover.fx", "PS", "ps_4_0")

rem Setup the "vertex" layout (or rather control point structure) that will describe a single
rem grass tuft to our geometry shader loaded above
vlFoliage = dx11 create vertex layout()
rem The "anchor" point is used like a pivot point; the geometry shader interprets it as the center bottom coordinate of the grass tuft
dx11 add vertex layout element vlFoliage, "ANCHOR",     0, DXGI_FORMAT_R32G32B32_FLOAT
rem The dimensions element describes the width and height of each grass quad in world space
dx11 add vertex layout element vlFoliage, "DIMENSIONS", 0, DXGI_FORMAT_R32G32_FLOAT
rem The Y angle of the grass tuft object this control point represents
dx11 add vertex layout element vlFoliage, "ANGLE",      0, DXGI_FORMAT_R32_FLOAT
rem An offset to the bending-over-time effect used to animate the grass tufts. Ensures that they will not all move in unison.
dx11 add vertex layout element vlFoliage, "BENDOFFSET", 0, DXGI_FORMAT_R32_FLOAT
rem There we go; 28 bytes per grass tuft object, not bad (would at least need 192, plus 48 for index data, for the same result using standard vertex meshes).
dx11 finalize vertex layout vlFoliage

rem Create shader technique for use with the foliage objects
techFoliage = dx11 create shader technique(vlFoliage, vsFoliage, psFoliage, gsFoliage)

rem Create a constant buffer for providing the sine of the current time to the foliage shader in order to animate the grass tufts.
rem We also provide a minimum and maximum world Y coordinate; any grass control point outside of this height range will not be 
rem expanded into a mesh by the geometry shader.
rem Note that since the first float in this buffer changes for each frame while the other two never change, they should really be put
rem in different cbuffers so that we don't have to waste time transfering the values that never change to the GPU over and over.
rem However, since the minimum memory amount that can be transfered as a discrete unit is 16 bytes and we're only using 12 here, 
rem it doesn't matter. Still good to keep in mind for larger constant buffers though!
cbFoliage = dx11 create cbuffer(12)
dx11 add constant buffer techFoliage, cbFoliage
dx11 set cbuffer float cbFoliage, 4, TREELINE_MIN
dx11 set cbuffer float cbFoliage, 8, TREELINE_MAX

rem Note that while it at first seems logic that we should disable backface culling for the grass rendering (so that grass tufts 
rem will be visible from all angles) this only works when faces are sent directly to the input assembler stage. In other words 
rem no backfaces generated by the geometry shader will be culled anyway. If this behaviour is desired, the geometry shader can 
rem determine whether a to-be-generated face will face away from the camera and if so reject it by returning from the shader stage 
rem before appending any vertex data to the output stream.

rem Create a single, large (1024x1024) terrain mesh from a height map
pmHeightMap = dx11 load pixelmap("media/heightmap.jpg")
terrain     = CreateTerrain(pmHeightMap, TERRAIN_WIDTH, TERRAIN_DEPTH, TERRAIN_MAX_HEIGHT, 96.0, 96.0)

rem Create grass chunks; we'll split them into 32x32 separate patches so that these can be individually frustum culled.
rem Other splits may yield better performance, feel free to try different values.
dim grass(-1) as dword
texGrass = dx11 load image("media/GrassTuft.png")
for x = 0 to 31
    for y = 0 to 31
        rem Distribute evenly
        xPos# = (-TERRAIN_WIDTH / 2) + (x / 32.0) * TERRAIN_WIDTH
        zPos# = (-TERRAIN_DEPTH / 2) + (y / 32.0) * TERRAIN_DEPTH
        grassPatch = CreateGrassChunk(vlFoliage, pmHeightMap, TERRAIN_MAX_HEIGHT / 255.0, xPos#, zPos#, GRASS_TUFTS_PER_PATCH, 100, TREELINE_MIN, TREELINE_MAX)
        dx11 set object texture grassPatch, texGrass
        dx11 set object sort mode grassPatch, DX11_SORTMODE_TRANSPARENT_FULL
        dx11 set object shader technique grassPatch, techFoliage
        array insert at bottom grass()
        grass(array count(grass())) = grassPatch
    next y
next x

rem Position main camera
dx11 set camera position cam, 0, 80, 0
dx11 set camera lookat cam, 0, 40, 250

rem Main loop
currentTick as double integer
lastTick    as double integer
firstTick   as double integer
firstTick   =  dx11 get high resolution tick()
currentTick =  firstTick - 1
while not dx11 quit()
    lastTick    = currentTick
    currentTick = dx11 get high resolution tick()
    delta#      = dx11 get high resolution delta time(lastTick, currentTick)
    
    rem Control camera using mouse
    mmx = dx11 get mouseMoveX()
    mmy = dx11 get mouseMoveY()
    mc  = dx11 is mouse button down(0) - dx11 is mouse button down(1)
    
    if mc <> 0 or dx11 is mouse button down(2)
        dx11 apply camera angle cam, mmy * 40 * delta#, mmx * 40 * delta#, 0.0
        dx11 move camera cam, mc * 30 * (dx11 is key down(DIK_LSHIFT) * 2 + 1) * delta#
    endif
    
    rem Keep the camera above the ground
    groundHeight# = GetTerrainHeight(pmHeightMap, TERRAIN_WIDTH, TERRAIN_DEPTH, TERRAIN_MAX_HEIGHT / 255.0, dx11 get camera position x(cam), dx11 get camera position z(cam))
    if dx11 get camera position y(cam) < groundHeight# + 2
        dx11 set camera position cam, dx11 get camera position x(cam), groundHeight# + 2, dx11 get camera position z(cam)
    endif
    
    rem Update the sine of the current time to the foliage constant buffer
    elapsedTime# = dx11 get high resolution delta time(firstTick, currentTick)
    dx11 set cbuffer float cbFoliage, 0, MATH_TWOPI * elapsedTime# * 0.2         ` Set the grass vertex animation to have a frequency of 0.2Hz
    
    dx11 draw text 0, 0, "FPS: " + str$(dx11 get screen fps())
    dx11 draw text 0, 20, "Camera at (" + str$(dx11 get camera position x(cam), 2) + ", " + str$(dx11 get camera position y(cam), 2) + ", " + str$(dx11 get camera position z(cam), 2) + ")"
    dx11 draw text 0, 40, "[c=0xffffffbe]Press LMB to move forward, RMB to move back or the middle mouse button to just look around."
    dx11 draw text 0, 56, "[c=0xffffffbe]Move the mouse while holding any of the above buttons to change camera angle."
    dx11 draw text 0, 72, "[c=0xffffffbe]Hold [c=0xffbebeff]shift[c=0xffffffbe] to increase movement speed."
    
    dx11 sync
endwhile

end



rem Helper function that creates a grass chunk-type object of the given 
rem radius and amount of grass billboards; the created object will use
rem the pointlist topology and depend on the FloorCover geometry shader
rem for creating crossed planes from these control points.
function CreateGrassChunk(vlGrass as dword, pmHeightMap as dword, heightFactor as float, posX as float, posZ as float, amount as dword, radius as float, minHeight as float, maxHeight as float)
    rem The mesh is just a collection of points, each of which represents a grass tuft.
    rem As such we will have as many indices as "vertices" (control points).
    mesh = dx11 create mesh(amount, amount, vlGrass, TOPOLOGY_POINTLIST)
    
    dx11 lock mesh indexdata mesh
    for i = 0 to amount - 1
        dx11 set mesh indexdata mesh, i, i
    next i
    dx11 unlock mesh indexdata mesh
    
    dx11 lock mesh vertexdata mesh
    anchorOffset    = dx11 get mesh vertexdata semantic offset(mesh, "ANCHOR")
    dimensionOffset = dx11 get mesh vertexdata semantic offset(mesh, "DIMENSIONS")
    angleOffset     = dx11 get mesh vertexdata semantic offset(mesh, "ANGLE")
    bendOffset      = dx11 get mesh vertexdata semantic offset(mesh, "BENDOFFSET")
    vecPos = dx11 create temp vector3(posX, 0.0, posZ)
    vecDir = dx11 create temp vector3()
    for p = 0 to amount - 1
        suitable = false
        attempts = 0
        repeat
            rem Figure out a position within the radius of the given center position
            dist# = frand(radius)
            rem We can end up with overly many temporary vectors that will linger
            rem in memory until the next call to DX11 SYNC if we create them in 
            rem a loop, so for efficiency I use the more tedious approach of 
            rem breaking the calculation down into pieces and writing them to the 
            rem same vector in stages here.
            dx11 set vector3 vecDir, frand(2.0) - 1.0, 0.0, frand(2.0) - 1.0
            vecDir = dx11 normalize vector3(vecDir, vecDir)
            dx11 set vector3 vecDir, dx11 get vector3 x(vecDir) * dist#, 0.0, dx11 get vector3 z(vecDir) * dist#
            rem (vecDir is now the target position of the grass tuft currently being created, relative to posX and posZ)
            px# = dx11 get vector3 x(vecDir)
            pz# = dx11 get vector3 z(vecDir)
            rem Check whether the terrain height is suitable at this location (not too high and not too low)
            h# = GetTerrainHeight(pmHeightMap, TERRAIN_WIDTH, TERRAIN_DEPTH, heightFactor, posX + px#, posZ + pz#)
            inc attempts
            rem Generate less grass the closer to the edges of the height range we get; this makes the grass appear more naturally placed.
            rem Still, it is possible that a grass chunk may be created in such a way that no position within it has a suitable height.
            rem Should this have a risk of happening, we opt to give up trying to find a suitable position after 10 attempts. The geometry 
            rem shader will recognize grass instances with "invalid" Y coordinates and simply not expand them into geometry. This is actually a 
            rem very efficient form of clipping so while perhaps unnecessary in this application (apart from it making things easier), it is
            rem still worth to demonstrate.
            d# = abs(((TREELINE_MAX - TREELINE_MIN) / 2) - h#) / (TREELINE_MAX - TREELINE_MIN)
            suitable = (h# >= TREELINE_MIN and h# <= TREELINE_MAX and (rnd(d# * 100) < 5)) or attempts > 10
        until suitable
        rem Randomize size and rotation for the grass tufts to create a more realistic feel to it
        size#   = 4 + frand(2.0)
        angle#  = frand(MATH_TWOPI)         ` Using radians for faster GPU processing
        dx11 set vector3 vecDir, px#, h#, pz#
        
        dx11 set mesh vertexdata vector3    mesh, p, anchorOffset,          vecDir
        dx11 set mesh vertexdata float      mesh, p, dimensionOffset,       size#
        dx11 set mesh vertexdata float      mesh, p, dimensionOffset + 4,   size#
        dx11 set mesh vertexdata float      mesh, p, angleOffset,           angle#
        dx11 set mesh vertexdata float      mesh, p, bendOffset,            frand(MATH_TWOPI)
    next p
    dx11 unlock mesh vertexdata mesh
    
    obj = dx11 create object(mesh)
    dx11 delete mesh mesh
    dx11 set object position obj, posX, 0.0, posZ
endfunction obj

rem Helper function that creates a single-mesh terrain from the provided 
rem height map and sets up pre-defined shaders and textures for it.
function CreateTerrain(pmHeightMap as dword, width as float, depth as float, maxHeight as float, uScale as float, vScale as float)
    rem We could just use the standard vertex layout, but since this will
    rem be a rather large mesh it is more efficient to only include per-vertex
    rem elements that we will actually need to render it.
    vlTerrain = dx11 create vertex layout()
    dx11 add vertex layout element vlTerrain, "POSITION",   DXGI_FORMAT_R32G32B32_FLOAT
    dx11 add vertex layout element vlTerrain, "TEXCOORD",   DXGI_FORMAT_R32G32_FLOAT
    dx11 finalize vertex layout vlTerrain
    
    terrainAcross   = dx11 get pixelmap width(pmHeightMap)
    terrainDown     = dx11 get pixelmap height(pmHeightMap)
    terrainHeight#  = maxHeight / 255.0
    vertsAcross     = terrainAcross + 1
    vertsDown       = terrainDown + 1
    uStep#          = uScale / terrainAcross
    vStep#          = vScale / terrainDown
    xStart#         = -width / 2.0
    yStart#         = -depth / 2.0
    xStep#          = width / terrainAcross
    yStep#          = depth / terrainDown
    terrainIndices  = terrainAcross * terrainDown * 6
    maxY#           = -99999.99
    
    mesh = dx11 create mesh(vertsAcross * vertsDown, terrainIndices, vlTerrain)
    
    rem Write vertex data
    dx11 lock mesh vertexdata mesh
    offsetPos       = dx11 get mesh vertexdata semantic offset(mesh, "POSITION")
    offsetTexcoord  = dx11 get mesh vertexdata semantic offset(mesh, "TEXCOORD")
    vecPos      = dx11 create temp vector3()
    vecTexcoord = dx11 create temp vector2()
    for y = 0 to vertsDown - 1
        for x = 0 to vertsAcross - 1
            v = y * vertsAcross + x
            rem Determine height from the height map and height factor
            height# = terrainHeight# * (dx11 get pixelmap pixel(pmHeightMap, x, y) && 0xff)
            rem Set the vertex data
            dx11 set vector3 vecPos,        xStart# + xStep# * x, height#, yStart# + yStep# * y
            dx11 set vector2 vecTexcoord,   uStep# * x, vStep# * y
            dx11 set mesh vertexdata vector3 mesh, v, offsetPos,        vecPos
            dx11 set mesh vertexdata vector2 mesh, v, offsetTexcoord,   vecTexcoord
            // Keep track of maximum terrain height
            if height# > maxY# then maxY# = height#
        next x
    next y
    dx11 unlock mesh vertexdata mesh
    
    rem Write index data
    dx11 lock mesh indexdata mesh
    indexCount = 0
    for y = 0 to terrainDown - 1
        for x = 0 to terrainAcross - 1
            dx11 set mesh indexdata mesh, indexCount,     y * vertsAcross + x
            dx11 set mesh indexdata mesh, indexCount + 1, (y + 1) * vertsAcross + x
            dx11 set mesh indexdata mesh, indexCount + 2, y * vertsAcross + x + 1
            dx11 set mesh indexdata mesh, indexCount + 3, (y + 1) * vertsAcross + x
            dx11 set mesh indexdata mesh, indexCount + 4, (y + 1) * vertsAcross + x + 1
            dx11 set mesh indexdata mesh, indexCount + 5, y * vertsAcross + x + 1
            inc indexCount, 6
        next x
    next y
    dx11 unlock mesh indexdata mesh
    
    rem Create terrain object
    obj = dx11 create object(mesh)
    rem It will be too large to fit into the world sectors, so we'll pay the 
    rem price of having to cull it manually each frame
    dx11 set object to catch all sector obj, true
    rem We don't need the mesh any longer from here on so we can delete it (actually the terrain object
    rem still references it, but by releasing our own reference we ensure it will be deleted along with
    rem the terrain object).
    dx11 delete mesh mesh
    
    rem The terrain is completely opaque and should thus be rendered before any transparent objects
    dx11 set object sort mode obj, DX11_SORTMODE_OPAQUE
    
    rem Apply textures
    rem (The shader will interpolate between these based on the terrain's relative height at each pixel)
    dx11 set object texture obj, dx11 load image("media/sand.png"),         0
    dx11 set object texture obj, dx11 load image("media/grass.png"),        1
    dx11 set object texture obj, dx11 load image("media/mud.png"),          2
    dx11 set object texture obj, dx11 load image("media/rock.png"),         3
    
    rem Load and apply terrain shaders
    vsTerrain   = dx11 load vertex shader("fx/TerrainShader.fx", "VS", "vs_4_0")
    psTerrain   = dx11 load pixel shader("fx/TerrainShader.fx", "PS", "ps_4_0")
    techTerrain = dx11 create shader technique(vlTerrain, vsTerrain, psTerrain)
    dx11 set object shader technique obj, techTerrain
    
    rem Finally, add a constant buffer so that we can tell the shader
    rem the actual maximum height of the terrain for texture blending
    cbTerrain   = dx11 create cbuffer(4)
    dx11 set cbuffer float cbTerrain, 0, maxY#
    dx11 add constant buffer techTerrain, cbTerrain
endfunction obj


rem Helper function for obtaining an interpolated height value from the terrain
function GetTerrainHeight(pmHeightMap as dword, terrainWidth as float, terrainDepth as float, heightFactor as float, xPos as float, zPos as float)
    rem The terrain object has its center at (0, 0, 0), while the height map has its upper left corner at (0, 0).
    rem We need to offset it in order to determine the vertex positions (which corresponds to the pixels in our
    rem height map) that are close to the given 3D X and Z positions.
    xPos = xPos + (terrainWidth / 2.0)
    zPos = zPos + (terrainDepth / 2.0)
    
    rem Is the given position outside of the terrain?
    if xPos < 0.0 or xPos > terrainWidth or zPos < 0.0 or zPos > terrainDepth then exitfunction 0.0
    
    rem Calculate dimensions of each terrain tile
    tileWidth# = terrainWidth / dx11 get pixelmap width(pmHeightMap)
    tileDepth# = terrainDepth / dx11 get pixelmap height(pmHeightMap)
    
    rem What tile does the current position correspond to?
    tileX   = int(xPos / tileWidth#)
    tileZ   = int(zPos / tileDepth#)
    tileX2  = int(xPos / (tileWidth# * 2)) * 2
    tileZ2  = int(zPos / (tileDepth# * 2)) * 2
    
    rem Alright, time to hold on to those suspenders....
    local fX as float
    local fZ as float
    fX      = (xPos - (tileX * tileWidth#)) / tileWidth#
    fZ      = (zPos - (tileZ * tileDepth#)) / tileDepth#
    height# = 0.0
    if tileX = tileX2 and tileZ = tileZ2
        if (((1 - fX) - fZ) - (fZ - (1 - fX))) > 0.0
            height# = GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ)) * fX) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ)) * fZ)
            exitfunction height#
        else
            height# = GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1)) * (1 - fZ)) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1)) * (1 - fX))
            exitfunction height#
        endif
    endif
    
    if (tileX2 - tileX) = -1 and (tileZ2 - tileZ) = -1
        if (((1 - fX) - fZ) - (fZ - (1 - fX))) > 0.0
            height# = GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ)) * fX) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ)) * fZ);
            exitfunction height#
        else
            height# = GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1)) * (1 - fZ)) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1)) * (1 - fX))
            exitfunction height#
        endif
    endif
    
    if (tileX2 - tileX) = -1 and (tileZ = tileZ2)
        if (((1 - fZ) + fX) - (fZ + (1 - fX))) > 0.0
            height# = GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ)) * (1 - fX)) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX +  1,tileZ + 1) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ)) * fZ)
            exitfunction height#
        else 
            height# = GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1)) * fX) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1)) * (1 - fZ))
            exitfunction height#
        endif
    endif
    
    if tileX = tileX2 and (tileZ2 - tileZ) = -1 
        if (((1  -  fZ)  +  fX)  -  (fZ  +  (1  -  fX))) > 0.0
            height# = GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ)) * (1 - fX)) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ)) * fZ)
            exitfunction height#
        else
            height# = GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX + 1, tileZ + 1) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1)) * fX) + ((GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ) - GetHeightMapHeight(pmHeightMap, heightFactor, tileX, tileZ + 1)) * (1 - fZ))
            exitfunction height#
        endif
    endif
endfunction 0.0

rem Helper function for making the GetInterpolatedTerrainHeight function more readable
function GetHeightMapHeight(pmHeightMap as dword, heightFactor as float, tileX as dword, tileZ as dword)
    if tileX >= dx11 get pixelmap width(pmHeightMap) or tileZ >= dx11 get pixelmap height(pmHeightMap) then exitfunction 0.0
    h# = heightFactor * (dx11 get pixelmap pixel(pmHeightMap, tileX, tileZ) && 0xff)
endfunction h#

rem Helper function for retrieving a random floating point number
function frand(fMax as float)
    v# = rnd(65535) / 65535.0 * fMax
endfunction v#

