Following on from Kezzla, I also have some code that might inspire some ideas for features. The concept here is to treat the terrain as a series of individualized tiles, each tile contains their own shader, texture and vertex dense structure.
Besides providing an unlimited amount of detail and variation; it is also means for associating events with terrain locations. For example, one tile could represent quicksand, and another could represent rock; each would fire their own event trigger when the player stands on them.
I have demonstrated this idea in a TGC
code snippet entry; and will illustrate it again here.
Quote: "---- Requires Matrix1
---- Uses VertexData, Memblock and Vector3 Commands
Known issue: Some PCs might display nothing on the screen at first; just move forward, for some reason DBP's culling system thinks the terrain is not in the screen.
This snippet features a surface object I am using in my game. Here it is being used as a terrain which has event handling and shaders in mind. The terrain is broken down into a series of limbs and has meta-data such as walking speed and item drops for tiles in the terrain.
Rather than being a huge single limb in-explicitly described object, the surface is defined as an array of limbs that can be treated individually, given seperate shaders and properties. You can also apply a single shader to the whole object and supply separate textures for each tile.
The surface can be used as water by animating the vertices in the loop. The smoothness of the generated mesh can be improved by increasing the Quality variable, increasing the size of the heightmap and making use of Vector3 interpolation commands or vertex shaders. The tiles are not simply single polygons, but sub-terrains, so realism can be simulated effectively.
To create LOD, create two or more surfaces at different levels of detail and either convert limbs to objects, then use the Add Lod To Object command; or you can keep the limbs intact and manually fade transition the limbs nearest to high quality, and the limbs far away in low quality.
The example features a simple game of search and find. There are some hidden objects located in the map. The walking movement demonstrates variable walk speeds and object intersection.
Each tile could also implement footfall sounds. Crouch is activated by pressing spacebar.
It would be a good idea to cook the mesh using a PhysX plugin to perform hardware accelerated collision; creating a low poly surface to act as a hidden collision object.
Post up any comments or suggestions."
Code:
` Tile Surface
`======================
` By Chris Tate
` MATRIX1 Required
#CONSTANT WALK_SPEED_FIELD 0
#CONSTANT ITEM_FIELD 4
TotalItems = 0 ` A good way to see how variables are used is to do a CTRL+F Find
Quality = 10
TilesX = 10
TilesZ = 10
Size = 150
ItemSpawnChance = 50
PlayerHeight# = 1.5
DefaultCroach# = 0.25
DistanceVector = New Vector3()
fDefaultSpeed# = 0.1
ItemsFound = 0
TerrainHeight# = 6.0
` Make random terrain map - by default the bitmap is backdrop is black prior to creating objects
` This is a very random way to create a random texture
` Not much thought put into it; but it will do, I am more concerned with
` the actual terrain, not the bitmap
` consider using a perlin noise plugin (WLGFX created one)
Global HeightMapSize = 128
Randomize Timer() ` For Rnd() command
Random Seed Timer() ` For Random() command
For x = 0 to HeightMapSize-1
For y = 0 to HeightMapSize-1
f# = Sin( 4 * HiTimer(1000000) )*0.3
Inc f#, Cos(x*Sin(y))*0.3
c# = Min( Random( ( 0.2 ) + 0.02 ) + F# * 0.2, 0.3 )
c# = c# + Random( 0.03 )
c = Min(c# * 128, 80)
Dot x, y, Rgb(c,c,c)
Next y
Next x
` Draw a blob
Fill Circle HeightMapSize * 0.25, HeightMapSize * 0.15, HeightMapSize * 0.3, Rgb(200,200,200)
Fill Circle HeightMapSize * 0.25, HeightMapSize * 0.4, HeightMapSize * 0.2, Rgb(100,100,100)
` Soften up with cheap blur effect; could have downsampled a bitmap
Blur Bitmap 0, 2
Get Image 1, 0, 0, HeightMapSize, HeightMapSize
Cls
Repeat
Paste Image 1, 0, 0
Text 0, 130, "Height map"
Text 0, 150, "Press any key"
Until Scancode()
` Lets simply save the colour to an array
` Assume we need to load image from file
Save Image "Terrain.bmp", 1
Load Image "Terrain.bmp", 2
Global Dim Pixels( HeightMapSize-1 , HeightMapSize-1 ) as Integer
ReadPixels(2)
Cls
Repeat
DrawPixels(0,0)
Text 0, 130, "Pixels in memory"
Text 0, 150, "Press any key"
Until Scancode()
` Create simple custom terrain/matrix of planes
Sync On : Sync Rate 60
` Create the terrain template
MAKE OBJECT PLANE 1, Size, Size , Quality, Quality, TilesX, TilesZ
Make Memblock 1, TilesX * TilesZ * 8
` Randomize the RND function to the seed of the current time
Randomize Timer()
` Make some items
Global Dim Items(2)
Items(0) = 300000
Items(1) = 300001
Items(2) = 300002
Make Object Sphere Items(0) , 4
Color Object Items(0) , RGB(255,0,0)
Make Mesh From Object Items(0), Items(0)
Delete Object Items(0)
Make Object Cube Items(1) , 4
Color Object Items(1) , RGB(0,0,255)
Make Mesh From Object Items(1), Items(1)
Delete Object Items(1)
Make Object Cone Items(2) , 4 : Make Mesh From Object Items(2), Items(2)
Delete Object Items(2)
fSpacing# = (HeightMapSize * 1.0) / Size
` Show Message "Spacing: " + Str$(fSpacing#,1)
fStart# = Size * -0.5
` Create vector for vertices
VertexPos = New Vector3()
PixelPos = New Vector3()
TilePos = New Vector3()
TileFloor = New Vector3()
Calc = New Vector3()
For i = 0 to Get Limb Count(1)
` Get position
Set Vector3 To Limb Offset TilePos, 1, i
` Paint the tile number I
Color Limb 1, i, Rgb( RND(255), 128, 0)
` Height
Lock VertexData For Limb 1, i
For v = 0 to Get VertexData Vertex Count()
Set Vector3 VertexPos, Get VertexData Position X(v) + (Size/2), Get VertexData Position Y(v), Get VertexData Position Z(v) + (Size/2)
Add Vector3 VertexPos, VertexPos, TilePos
`Show Message "Vertex position " + Vector3$(VertexPos, 2)
` Get the world location
Copy Vector3 PixelPos, VertexPos
` Get location in image
Multiply Vector3 PixelPos, fSpacing#
`Show Message "Image position " + Vector3$(VertexPos, 0)
` Get the colour
px = Clamp( X Vector3(PixelPos), 0, HeightMapSize - 1 )
py = Clamp( Z Vector3(PixelPos), 0, HeightMapSize - 1 )
h# = ( RGBR(Pixels(px,py)) ) * 0.0039215 ` Roughly * ( 1 / 255.0 )
` Show Message "Pixel color: " + Pixel$(px,py)
` Show Message "Height: " + Str$(h#)
h# = h# * TerrainHeight#
RealTerrainHeight# = Max(h#, RealTerrainHeight# )
` Use the Vector3 interpolator commands to smoothen the terrain if desired
Set VertexData Position v, Get VertexData Position X(v), Get VertexData Position Y(v) + h#, Get VertexData Position Z(v)
Next v
Unlock VertexData
` Give the tile a name
Set Limb Name 1, i, "Area " + Str$ ( i )
` Give tile walking speed
Write Memblock Float 1, (i*8)+WALK_SPEED_FIELD, Random(0.5) + 0.2
` Put an item on some of the tiles, and hide them
If ItemSpawnChance > 0
If RND(100) => ItemSpawnChance
item = 100000+i
Write Memblock DWord 1, (i*8)+ITEM_FIELD, item
Make Object item, Items(Rnd(2)), 0 ` Create some items out of a random mesh
Hide Object item
Inc TotalItems
Endif
EndIf
Next i
` It is very important to set normals after modifying an object
` this is so the renderer knows how to shade the object properly
` Unless you want a cartoon looking effect, do not remove this line
` otherwise the terrain will be shadeless, although coloured
Set Object Normals 1
` If we want to save this tileset, might as well save the memblock with it
Add Memblock To Object 1, 1
Delete Memblock 1
` Let's assume we loaded the object from file; let`s load the memblock too
Get Memblock From Object 1, 1
` Make player
Make Object Box 2, 3, 8, 3
PlayerPos = New Vector3() ` Matrix1 will return a vector ID for us
PlayerAng = New Vector3()
Do
Paste Image 1, Screen Width() - Image Width(1), 0 ` Consider using Advanced2D plugin, faster
Croach# = Spacekey() * -DefaultCroach#
` Not a perfect ground check, but will do for now
GroungHeight# = (999999.0 - Intersect Object( 1, X Vector3(PlayerPos), 999999.0, Z Vector3(PlayerPos), X Vector3(PlayerPos), -999999.0, Z Vector3(PlayerPos)))
` GroungHeight# = GroungHeight# `* RealTerrainHeight#
` We could use direct variables , but let's use vectors to best
` integrate seemlessly with many future shaders, advanced math operations
` and PhysX plugins
Set Vector3 To Camera Position PlayerPos, 0
Set Vector3 To Camera Rotation PlayerAng, 0
Position Object At Vector3 2, PlayerPos
Rotate Object To Vector3 2, PlayerAng
Set Cursor 0, 0
Print "FPS: "; Screen FPS()
Print "Move with arrow keys, spacebar to croach. Find the items."
Print ItemsFound; " items found out of "; TotalItems
Print "Ground Height: "; Str$(GroungHeight#, 2 )
Print "Number of limbs: " + Str$( Get Limb Count(1) + 1 )
Print "Current walk speed: "; Str$( fSpeed#, 4 )
Print "Terrain height: "; TerrainHeight#
Print "Terrain real height: "; RealTerrainHeight#
fSpeed# = fDefaultSpeed#
iPrints = 0
` Update tiles and items
For i = 0 to Get Limb Count(1)
` Get tile position for calculations
Set Vector3 To Limb Offset TilePos, 1, i
` Note the previous function name is not ideal if the terrain
` can be moved, you need to add the object position to the
` vector , it seems the Set Vector3 To Limb Position function
` is broken
` Lets use vectors to calculate a simple distance
Subtract Vector3 DistanceVector, TilePos, PlayerPos
iDist = Length Vector3( DistanceVector )
` Which could of been: (But I like using vectors)
remstart
fDist_X# = X1# - X2#
fDist_Y# = Y1# - Y2#
fDist_Z# = Z1# - Z2#
// Could use faster method
fDist# = Sqrt(fDist_X# * fDist_X# + fDist_Y# * fDist_Y# + fDist_Z# * fDist_Z#)
remend
` Show the item, if there is one
` item = 100000+i - We could do this, but lets assume we don't
` know the object ID
item = Memblock DWord( 1, (i*8)+ITEM_FIELD )
If iDist < 50 And iPrints < 5
Print "Distance to " ; Limb Name$( 1, i) ; " is " ; iDist ; " Position: ";
Print Vector3$(TilePos, 2)
Inc iPrints
` Get walk speed
If iDist < 40
fSpeed# = (fSpeed# + Memblock Float( 1, (i*8)+ WALK_SPEED_FIELD )) / 2.0
Endif
` Search for local hidden item drops
If iDist < 10
If item > 0
If Object exist(item)
If Object Visible( item ) = 0
Inc ItemsFound ` You found the object!
Show Object item
`Set Vector3 TileFloor, X Vector3(TilePos), Intersect Object( 1, X Vector3(TileFloor), 999999.0, Z Vector3(TileFloor), X Vector3(TileFloor), -999999.0, Z Vector3(TileFloor)), Z Vector3(TilePos)
Position Object At Vector3 item, TilePos
Move Object Up item, Camera Position Y()
Endif
Endif
EndIf
EndIf
Else
If iPrints = 5
Print "..."
Inc iPrints
Endif
Endif
Next i
Control Camera Using Arrowkeys 0,fSpeed# * 0.2, 2
` Keep on ground and in the map with a bit of padding
Position Camera Clamp(Camera Position X(), -Size+0.01, Size-0.01), GroungHeight# + PlayerHeight# + Croach# , Clamp(Camera Position Z(), -Size+0.01, Size-0.01)
Sync
Nice Sleep 2
Loop
//============================================================
Function Dist#( x1#,y1#,z1#,x2#,y2#,z2#)
` Here is an alternative distance function
fDist_X# = X1# - X2#
fDist_Y# = Y1# - Y2#
fDist_Z# = Z1# - Z2#
// Could use faster method
fDistance# = Sqrt(fDist_X# * fDist_X# + fDist_Y# * fDist_Y# + fDist_Z# * fDist_Z#)
EndFunction fDistance#
//============================================================
Function Vector3$(v, iDecimals)
s$ = Str$( X Vector3(v), iDecimals ) + ", " + Str$( Y Vector3(v), iDecimals ) + ", " + Str$( Z Vector3(v), iDecimals )
Endfunction s$
//============================================================
Function ReadPixels(iImage)
m = 1+iImage
w = Image Width(iImage) - 1
h = Image Height(iImage) - 1
Make Memblock From Image m, iImage
s = get memblock size( m )
y = 0 : x = 0
for i = 12 to s - 1 step 4
` After the last column, increase the row id and reset the column to zero
if x > w
inc y
x = 0
endif
` Get the pixel data, from blue, green, red to alpha; then display the result
b = memblock byte( m, i)
g = memblock byte( m, i+1)
r = memblock byte( m, i+2)
a = memblock byte( m, i+3)
` Next column of pixels
Pixels(x,y) = Rgb(a,r,g,b)
inc x
Next i
Delete Memblock m
Endfunction
//============================================================
Function DrawPixels(iScreenX,iScreenY)
For x = 0 to HeightMapSize - 1
For y = 0 to HeightMapSize - 1
Dot iScreenX+x,iScreenY+y,Pixels(x,y)
Next y
Next x
Endfunction
//============================================================
Function Pixel$(px,py)
s$ = Str$(px) + ", " + Str$(py) + " = RGB {"
s$ = s$ + Str$(RGBR( Pixels(px,py))) + ","
s$ = s$ + Str$(RGBG( Pixels(px,py))) + ","
s$ = s$ + Str$(RGBB( Pixels(px,py))) + "}"
Endfunction s$