About Monkey 2 › Forums › Monkey 2 Code Library › PlayfulJSTerrain port
Tagged: fractal, generator, heightmap, multithreading, playfuljs, procedural, terrain, threaded, threads
This topic contains 3 replies, has 2 voices, and was last updated by
Amon
8 months ago.
-
AuthorPosts
-
August 14, 2018 at 9:07 pm #15303
This is a heightmap generator by PlayfulJS (see links in code). Screenshot below.
The author doesn’t attach a license of any kind (he treats it as public domain), which won’t suit everyone, but this port is of course provided on the same basis…
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195' -----------------------------------------------------------------------------' Start of PlayfulJSTerrain' -----------------------------------------------------------------------------Class PlayfulJSTerrain' Hunter Loftis' http://www.playfuljs.com/realistic-terrain-in-130-lines/' https://github.com/hunterloftis/playfuljs-demos/blob/gh-pages/terrain/index.htmlField size:IntField max:IntField map:Float[]Field roughness:FloatField upper:FloatField lower:FloatMethod New (seed:ULong, pixmap_size:Int = 1024, in_roughness:Float = 0.5)Assert (IsPow2 (pixmap_size), "PlayfulJSTerrain.New (pixmap_size) must be power of 2!")SeedRnd (seed)roughness = in_roughnesssize = pixmap_size + 1max = size - 1map = New Float[size * size]Generate ()EndMethod GetHeight:Float (x:Int, y:Int)If (x < 0 Or x > max Or y < 0 Or y > max) Then Return -1Return map[x + size * y]EndMethod SetHeight (x:Int, y:Int, val:Float)map[x + size * y] = valIf val < lower Then lower = valIf val > upper Then upper = valEndMethod Generate ()lower = 0upper = 0SetHeight (0, 0, max)SetHeight (max, 0, max * 0.5)SetHeight (max, max, 0)SetHeight (0, max, max * 0.5)Divide (max)EndMethod Divide (size:Int)Local half:Int = size / 2 ' Weird failed optimisation: "If size < 2 Then Return" at start is slower than this with half < 1 check!If half < 1 Then ReturnLocal scale:Float = roughness * sizeLocal scale2:Float = scale * 2 ' Quick pre-calc for the two loops below...Local x:IntLocal y:IntFor y = half To max Step sizeFor x = half To max Step sizeSquare (x, y, half, Rnd () * scale2 - scale)NextNextFor y = 0 To max Step halfFor x = (y + half) Mod size To max Step sizeDiamond (x, y, half, Rnd () * scale2 - scale)NextNext' Recursive call until too small (see "If half < 1")...Divide (size / 2)EndMethod Square (x:Int, y:Int, in_size:Int, offset:Float)Local total:FloatLocal count:IntLocal xms:Int = x - in_sizeLocal yms:Int = y - in_sizeLocal xps:Int = x + in_sizeLocal yps:Int = y + in_sizeLocal h0:Float = GetHeight (xms, yms)Local h1:Float = GetHeight (xps, yms)Local h2:Float = GetHeight (xps, yps)Local h3:Float = GetHeight (xms, yps)If h0 <> -1 Then total = total + h0; count = count + 1If h1 <> -1 Then total = total + h1; count = count + 1If h2 <> -1 Then total = total + h2; count = count + 1If h3 <> -1 Then total = total + h3; count = count + 1If Not count Then Return ' Don't divide by zero!SetHeight (x, y, total / count + offset)EndMethod Diamond (x:Int, y:Int, in_size:Int, offset:Float)Local total:FloatLocal count:IntLocal h0:Float = GetHeight (x, y - in_size)Local h1:Float = GetHeight (x + in_size, y)Local h2:Float = GetHeight (x, y + in_size)Local h3:Float = GetHeight (x - in_size, y)If h0 <> -1 Then total = total + h0; count = count + 1If h1 <> -1 Then total = total + h1; count = count + 1If h2 <> -1 Then total = total + h2; count = count + 1If h3 <> -1 Then total = total + h3; count = count + 1If Not count Then Return ' Don't divide by zero!SetHeight (x, y, total / count + offset)EndMethod RenderPixmap:Pixmap ()Local pix:Pixmap = New Pixmap (max, max, PixelFormat.I8)Local rgb:FloatLocal color:Color = New Color (0.0, 0.0, 0.0)For Local y:Int = 0 Until maxFor Local x:Int = 0 Until maxIf (upper - lower) = 0 ' Make sure range is valid for TransformRange!rgb = 0.0Elsergb = TransformRange (GetHeight (x, y), lower, upper, 0.0, 1.0)Endifcolor.R = rgbcolor.G = rgbcolor.B = rgbpix.SetPixel (x, y, color)NextNextReturn pixEndMethod TransformRange:Float (input_value:Float, from_min:Float, from_max:Float, to_min:Float, to_max:Float)' Algorithm via jerryjvl at https://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratioLocal from_delta:Float = from_max - from_min ' Input range, eg. 0.0 - 1.0Local to_delta:Float = to_max - to_min ' Output range, eg. 5.0 - 10.0Assert (from_delta <> 0.0, "TransformRange: Invalid input range!")Return (((input_value - from_min) * to_delta) / from_delta) + to_minEndMethod IsPow2:Long (value:Long)Return Not (value & (value - 1))EndEnd' -----------------------------------------------------------------------------' End of PlayfulJSTerrain' -----------------------------------------------------------------------------You can get a simple heightmap by doing this:
Monkey1234Local terrain:PlayfulJSTerrain = New PlayfulJSTerrain (seed, pixmap_size, roughness) ' eg. 1, 1024, 0.5Local heightmap:Pixmap = terrain.RenderPixmap ()heightmap.Save (DesktopDir () + "\mx2__rendered__heightmap.png")… and here it is in runnable mojo3d demo form — paste into Ted2Go and run it! (Tested in latest GitHub develop release.)
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358#Import "<std>"#Import "<mojo>"#Import "<mojo3d>"Using std..Using mojo..Using mojo3d..#RemBasic usage to generate 2D PNG heightmap (size must be power of 2):Local terrain:PlayfulJSTerrain = New PlayfulJSTerrain (seed, pixmap_size, roughness) ' eg. 1, 1024, 0.5Local heightmap:Pixmap = terrain.RenderPixmap ()heightmap.Save (DesktopDir () + "\mx2__rendered__heightmap.png")#End' -----------------------------------------------------------------------------' Start of PlayfulJSTerrain' -----------------------------------------------------------------------------Class PlayfulJSTerrain' Hunter Loftis' http://www.playfuljs.com/realistic-terrain-in-130-lines/' https://github.com/hunterloftis/playfuljs-demos/blob/gh-pages/terrain/index.htmlField size:IntField max:IntField map:Float[]Field roughness:FloatField upper:FloatField lower:FloatMethod New (seed:ULong, pixmap_size:Int = 1024, in_roughness:Float = 0.5)Assert (IsPow2 (pixmap_size), "PlayfulJSTerrain.New (pixmap_size) must be power of 2!")SeedRnd (seed)roughness = in_roughnesssize = pixmap_size + 1max = size - 1map = New Float[size * size]Generate ()EndMethod GetHeight:Float (x:Int, y:Int)If (x < 0 Or x > max Or y < 0 Or y > max) Then Return -1Return map[x + size * y]EndMethod SetHeight (x:Int, y:Int, val:Float)map[x + size * y] = valIf val < lower Then lower = valIf val > upper Then upper = valEndMethod Generate ()lower = 0upper = 0SetHeight (0, 0, max)SetHeight (max, 0, max * 0.5)SetHeight (max, max, 0)SetHeight (0, max, max * 0.5)Divide (max)EndMethod Divide (size:Int)Local half:Int = size / 2 ' Weird failed optimisation: "If size < 2 Then Return" at start is slower than this with half < 1 check!If half < 1 Then ReturnLocal scale:Float = roughness * sizeLocal scale2:Float = scale * 2 ' Quick pre-calc for the two loops below...Local x:IntLocal y:IntFor y = half To max Step sizeFor x = half To max Step sizeSquare (x, y, half, Rnd () * scale2 - scale)NextNextFor y = 0 To max Step halfFor x = (y + half) Mod size To max Step sizeDiamond (x, y, half, Rnd () * scale2 - scale)NextNext' Recursive call until too small (see "If half < 1")...Divide (size / 2)EndMethod Square (x:Int, y:Int, in_size:Int, offset:Float)Local total:FloatLocal count:IntLocal xms:Int = x - in_sizeLocal yms:Int = y - in_sizeLocal xps:Int = x + in_sizeLocal yps:Int = y + in_sizeLocal h0:Float = GetHeight (xms, yms)Local h1:Float = GetHeight (xps, yms)Local h2:Float = GetHeight (xps, yps)Local h3:Float = GetHeight (xms, yps)If h0 <> -1 Then total = total + h0; count = count + 1If h1 <> -1 Then total = total + h1; count = count + 1If h2 <> -1 Then total = total + h2; count = count + 1If h3 <> -1 Then total = total + h3; count = count + 1If Not count Then Return ' Don't divide by zero!SetHeight (x, y, total / count + offset)EndMethod Diamond (x:Int, y:Int, in_size:Int, offset:Float)Local total:FloatLocal count:IntLocal h0:Float = GetHeight (x, y - in_size)Local h1:Float = GetHeight (x + in_size, y)Local h2:Float = GetHeight (x, y + in_size)Local h3:Float = GetHeight (x - in_size, y)If h0 <> -1 Then total = total + h0; count = count + 1If h1 <> -1 Then total = total + h1; count = count + 1If h2 <> -1 Then total = total + h2; count = count + 1If h3 <> -1 Then total = total + h3; count = count + 1If Not count Then Return ' Don't divide by zero!SetHeight (x, y, total / count + offset)EndMethod RenderPixmap:Pixmap ()Local pix:Pixmap = New Pixmap (max, max, PixelFormat.I8)Local rgb:FloatLocal color:Color = New Color (0.0, 0.0, 0.0)For Local y:Int = 0 Until maxFor Local x:Int = 0 Until maxIf (upper - lower) = 0 ' Make sure range is valid for TransformRange!rgb = 0.0Elsergb = TransformRange (GetHeight (x, y), lower, upper, 0.0, 1.0)Endifcolor.R = rgbcolor.G = rgbcolor.B = rgbpix.SetPixel (x, y, color)NextNextReturn pixEndMethod TransformRange:Float (input_value:Float, from_min:Float, from_max:Float, to_min:Float, to_max:Float)' Algorithm via jerryjvl at https://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratioLocal from_delta:Float = from_max - from_min ' Input range, eg. 0.0 - 1.0Local to_delta:Float = to_max - to_min ' Output range, eg. 5.0 - 10.0Assert (from_delta <> 0.0, "TransformRange: Invalid input range!")Return (((input_value - from_min) * to_delta) / from_delta) + to_minEndMethod IsPow2:Long (value:Long)Return Not (value & (value - 1))EndEnd' -----------------------------------------------------------------------------' End of PlayfulJSTerrain' -----------------------------------------------------------------------------' Helper function to generate checkerboard Pixmap...Function CheckerPixmap:Pixmap (color0:Color, color1:Color)Local pixels:Pixmap = New Pixmap (256, 256, PixelFormat.RGBA8)pixels.Clear (color0)Local pixel_toggle:Int = 0For Local gp_y:Int = 0 Until pixels.HeightFor Local gp_x:Int = 0 Until pixels.WidthIf pixel_toggle Then pixels.SetPixel (gp_x, gp_y, color1)pixel_toggle = 1 - pixel_toggleNextpixel_toggle = 1 - pixel_toggleNextReturn pixelsEnd' Demo...Class MyWindow Extends WindowField scene:SceneField camera:CameraField light:LightField ground:ModelField seed:Int = 0Field turn:Bool = TrueMethod New( title:String="PlayfulJSTerrain to Monkey2!",width:Int=1024,height:Int=768,flags:WindowFlags=WindowFlags.Center )' Method New( title:String="PlayfulJSTerrain to Monkey2!",width:Int=App.DesktopSize.X,height:Int=App.DesktopSize.Y,flags:WindowFlags=WindowFlags.Fullscreen )Super.New( title,width,height,flags )EndMethod OnCreateWindow() Overridescene=New Scenescene.ClearColor = Color.Skyscene.FogColor = Color.Skyscene.FogNear = 256scene.FogFar = 1024camera=New Camera( Self )camera.AddComponent<FlyBehaviour>()camera.Move( 0,300,0 )camera.Far = 1024.0camera.Rotate (45, 0, 0)light=New Lightlight.CastsShadow=Truelight.Move (-512, 256, 0)' GenerateTerrain returns a mojo3d Model...ground = GenerateTerrain (seed, 1024)light.PointAt(ground)Mouse.PointerVisible = FalseEndMethod OnRender( canvas:Canvas ) OverrideIf turn Then ground.Rotate (0.0, 0.125, 0.0)If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()If Keyboard.KeyHit (Key.T) Then turn = Not turn ' Toggle turn variable on/off...If Keyboard.KeyHit (Key.G)seed = seed + 1ground.Destroy ()ground = GenerateTerrain (seed, 1024)Endif' Hitting S demonstrates simple non-3D usage to generate a heightmap...If Keyboard.KeyHit (Key.S)' Create a terrain with same details as 3D one...Local terrain:PlayfulJSTerrain = New PlayfulJSTerrain (seed) ' Defaults to size = 1024 x 1024, roughness = 0.5' Render to Pixmap and save to desktop...Local heightmap:Pixmap = terrain.RenderPixmap ()heightmap.Save (DesktopDir () + "\mx2__rendered__heightmap.png")EndifRequestRender()scene.Update()camera.Render( canvas )canvas.DrawText( "Seed=" + seed + ", FPS="+App.FPS,20,20 )canvas.DrawText( "G to generate new terrain",20,40 )canvas.DrawText( "S to save heightmap to desktop",20,60 )canvas.DrawText( "T to toggle rotation",20,80 )canvas.DrawText( "Cursors + A/Z to move (slowly!)",20,100 )canvas.DrawText( "Esc to exit",20,140 )EndEnd' For demo only...Function GenerateTerrain:Model (seed:ULong, size:Int = 1024, roughness:Float = 0.5, color0:Color = New Color (0.8, 0.5, 0.1), color1:Color = New Color (0.75, 0.35, 0.05))Local terrain:PlayfulJSTerrain = New PlayfulJSTerrain (seed, size, roughness)Local heightmap:Pixmap = terrain.RenderPixmap ()If Not heightmap Then RuntimeError ("Failed to generate heightmap!")heightmap.FlipY () ' 2D Y (increases downwards) translates to 3D Z (increases upwards)Local terrain_material:PbrMaterial = New PbrMaterial ()terrain_material.ColorTexture = New Texture (CheckerPixmap (color0, color1), TextureFlags.None)Local terrain_height:Float = 256.0 ' TODO!Local height_box:Boxf = New Boxf (-heightmap.Width * 0.5, 0.0, -heightmap.Height * 0.5, heightmap.Width * 0.5, terrain_height, heightmap.Height * 0.5)Return Model.CreateTerrain (heightmap, height_box, New PbrMaterial (terrain_material))EndFunction Main()New AppInstanceNew MyWindowApp.Run()EndAttachments:
August 14, 2018 at 11:22 pm #15304Experimental
Multi-threaded the terrain generation (for 3D demo)!
Model.CreateTerrain is the slowest part in generating the terrain, so this does the heightmap generation/mesh creation in a background thread, meaning there’s only a small jerk while the new terrain swaps in. Not sure what causes the jerk — uploading to graphics card, maybe? Still much nicer than the previous demo.
Hit G to generate new terrain while running.
Threading is probably only in the GitHub develop branch, and is experimental. Anyway, just quickly hacked it in out of curiosity!
Note the ground? checks for ease of use here, avoiding complex flags while the thread does its thing. I can simply turn the ground using ground?.Rotate (0.0, 0.125, 0.0) and if ground:Model is still loading/Null then it has no effect. Mark recommends against over-use of ? checks!
(I realised GenerateTerrain also hard-codes the height for now (256), easy enough to modify, of course.)
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419#Import "<std>"#Import "<mojo>"#Import "<mojo3d>"#Import "<thread>"Using std..Using mojo..Using mojo3d..#RemBasic usage to generate 2D PNG heightmap (size must be power of 2):Local terrain:PlayfulJSTerrain = New PlayfulJSTerrain (seed, pixmap_size, roughness) ' eg. 1, 1024, 0.5Local heightmap:Pixmap = terrain.RenderPixmap ()heightmap.Save (DesktopDir () + "\mx2__rendered__heightmap.png")#End' -----------------------------------------------------------------------------' Start of PlayfulJSTerrain' -----------------------------------------------------------------------------Class PlayfulJSTerrain' Hunter Loftis' http://www.playfuljs.com/realistic-terrain-in-130-lines/' https://github.com/hunterloftis/playfuljs-demos/blob/gh-pages/terrain/index.htmlField size:IntField max:IntField map:Float[]Field roughness:FloatField upper:FloatField lower:FloatMethod New (seed:ULong, pixmap_size:Int = 1024, in_roughness:Float = 0.5)Assert (IsPow2 (pixmap_size), "PlayfulJSTerrain.New (pixmap_size) must be power of 2!")SeedRnd (seed)roughness = in_roughnesssize = pixmap_size + 1max = size - 1map = New Float[size * size]Generate ()EndMethod GetHeight:Float (x:Int, y:Int)If (x < 0 Or x > max Or y < 0 Or y > max) Then Return -1Return map[x + size * y]EndMethod SetHeight (x:Int, y:Int, val:Float)map[x + size * y] = valIf val < lower Then lower = valIf val > upper Then upper = valEndMethod Generate ()lower = 0upper = 0SetHeight (0, 0, max)SetHeight (max, 0, max * 0.5)SetHeight (max, max, 0)SetHeight (0, max, max * 0.5)Divide (max)EndMethod Divide (size:Int)Local half:Int = size / 2 ' Weird failed optimisation: "If size < 2 Then Return" at start is slower than this with half < 1 check!If half < 1 Then ReturnLocal scale:Float = roughness * sizeLocal scale2:Float = scale * 2 ' Quick pre-calc for the two loops below...Local x:IntLocal y:IntFor y = half To max Step sizeFor x = half To max Step sizeSquare (x, y, half, Rnd () * scale2 - scale)NextNextFor y = 0 To max Step halfFor x = (y + half) Mod size To max Step sizeDiamond (x, y, half, Rnd () * scale2 - scale)NextNext' Recursive call until too small (see "If half < 1")...Divide (size / 2)EndMethod Square (x:Int, y:Int, in_size:Int, offset:Float)Local total:FloatLocal count:IntLocal xms:Int = x - in_sizeLocal yms:Int = y - in_sizeLocal xps:Int = x + in_sizeLocal yps:Int = y + in_sizeLocal h0:Float = GetHeight (xms, yms)Local h1:Float = GetHeight (xps, yms)Local h2:Float = GetHeight (xps, yps)Local h3:Float = GetHeight (xms, yps)If h0 <> -1 Then total = total + h0; count = count + 1If h1 <> -1 Then total = total + h1; count = count + 1If h2 <> -1 Then total = total + h2; count = count + 1If h3 <> -1 Then total = total + h3; count = count + 1If Not count Then Return ' Don't divide by zero!SetHeight (x, y, total / count + offset)EndMethod Diamond (x:Int, y:Int, in_size:Int, offset:Float)Local total:FloatLocal count:IntLocal h0:Float = GetHeight (x, y - in_size)Local h1:Float = GetHeight (x + in_size, y)Local h2:Float = GetHeight (x, y + in_size)Local h3:Float = GetHeight (x - in_size, y)If h0 <> -1 Then total = total + h0; count = count + 1If h1 <> -1 Then total = total + h1; count = count + 1If h2 <> -1 Then total = total + h2; count = count + 1If h3 <> -1 Then total = total + h3; count = count + 1If Not count Then Return ' Don't divide by zero!SetHeight (x, y, total / count + offset)EndMethod RenderPixmap:Pixmap ()Local pix:Pixmap = New Pixmap (max, max, PixelFormat.I8)Local rgb:FloatLocal color:Color = New Color (0.0, 0.0, 0.0)For Local y:Int = 0 Until maxFor Local x:Int = 0 Until maxIf (upper - lower) = 0 ' Make sure range is valid for TransformRange!rgb = 0.0Elsergb = TransformRange (GetHeight (x, y), lower, upper, 0.0, 1.0)Endifcolor.R = rgbcolor.G = rgbcolor.B = rgbpix.SetPixel (x, y, color)NextNextReturn pixEndMethod TransformRange:Float (input_value:Float, from_min:Float, from_max:Float, to_min:Float, to_max:Float)' Algorithm via jerryjvl at https://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratioLocal from_delta:Float = from_max - from_min ' Input range, eg. 0.0 - 1.0Local to_delta:Float = to_max - to_min ' Output range, eg. 5.0 - 10.0Assert (from_delta <> 0.0, "TransformRange: Invalid input range!")Return (((input_value - from_min) * to_delta) / from_delta) + to_minEndMethod IsPow2:Long (value:Long)Return Not (value & (value - 1))EndEnd' -----------------------------------------------------------------------------' End of PlayfulJSTerrain' -----------------------------------------------------------------------------' Helper function to generate checkerboard Pixmap...Function CheckerPixmap:Pixmap (color0:Color, color1:Color)Local pixels:Pixmap = New Pixmap (256, 256, PixelFormat.RGBA8)pixels.Clear (color0)Local pixel_toggle:Bool = FalseFor Local gp_y:Int = 0 Until pixels.HeightFor Local gp_x:Int = 0 Until pixels.WidthIf pixel_toggle Then pixels.SetPixel (gp_x, gp_y, color1)pixel_toggle = Not pixel_toggleNextpixel_toggle = Not pixel_toggleNextReturn pixelsEnd' Demo...Class MyWindow Extends WindowField scene:SceneField camera:CameraField light:LightField seed:Int = 0Field turn:Bool = TrueField ground:ModelField ground_thread:ThreadField render_timer:IntMethod New( title:String="PlayfulJSTerrain to Monkey2!",width:Int=1024,height:Int=768,flags:WindowFlags=WindowFlags.Center )' Method New( title:String="PlayfulJSTerrain to Monkey2!",width:Int=App.DesktopSize.X,height:Int=App.DesktopSize.Y,flags:WindowFlags=WindowFlags.Fullscreen )Super.New( title,width,height,flags )EndMethod GenerateTerrain_Threaded ()' Local ticks:Int = Millisecs ()Local tg:Model = GenerateTerrain (seed, 1024)ground?.Destroy ()ground = tg' Print "Terrain rendering thread took " + (Millisecs () - ticks) + " ms"' About a second...EndMethod OnCreateWindow() Overridescene=New Scenescene.ClearColor = Color.Skyscene.FogColor = Color.Skyscene.FogNear = 256scene.FogFar = 1024camera=New Camera( Self )camera.Move( 0,300,0 )camera.Far = 1024.0camera.Rotate (45, 0, 0)light=New Lightlight.CastsShadow=Truelight.Move (-512, 256, 0)' GenerateTerrain returns a mojo3d Model...'ground = GenerateTerrain (seed, 1024)ground_thread = New Thread (GenerateTerrain_Threaded)Mouse.PointerVisible = False' render_timer = Millisecs ()EndMethod OnRender( canvas:Canvas ) Override' Local render_time:Int = Millisecs () - render_timer' render_timer = Millisecs ()' If render_time > 17' Print "Rendering exceeded 17 ms, taking " + render_time + " ms"' EndifIf turn Then ground?.Rotate (0.0, 0.125, 0.0)If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()If Keyboard.KeyHit (Key.T) Then turn = Not turn ' Toggle turn variable on/off...If Keyboard.KeyHit (Key.G)' One at a time, please...If Not ground_thread.Runningseed = seed + 1ground_thread = New Thread (GenerateTerrain_Threaded)EndifEndif' Hitting S demonstrates simple non-3D usage to generate a heightmap...If Keyboard.KeyHit (Key.S)' Create a terrain with same details as 3D one...Local terrain:PlayfulJSTerrain = New PlayfulJSTerrain (seed) ' Defaults to size = 1024 x 1024, roughness = 0.5' Render to Pixmap and save to desktop...Local heightmap:Pixmap = terrain.RenderPixmap ()heightmap.Save (DesktopDir () + "\mx2__rendered__heightmap.png")EndifIf Keyboard.KeyDown (Key.A)camera.Move (0.0, 0.0, 1.0)ElseIf Keyboard.KeyDown (Key.Z)camera.Move (0.0, 0.0, -1.0)EndifEndifIf Keyboard.KeyDown (Key.Left)camera.Rotate (0.0, 1.0, 0.0)ElseIf Keyboard.KeyDown (Key.Right)camera.Rotate (0.0, -1.0, 0.0)EndifEndifIf Keyboard.KeyDown (Key.Up)camera.Rotate (1.0, 0.0, 0.0, True)ElseIf Keyboard.KeyDown (Key.Down)camera.Rotate (-1.0, 0.0, 0.0, True)EndifEndifRequestRender()scene.Update()camera.Render( canvas )canvas.DrawText( "Seed=" + seed + ", FPS="+App.FPS,20,20 )canvas.DrawText( "G to generate new terrain",20,40 )canvas.DrawText( "S to save heightmap to desktop",20,60 )canvas.DrawText( "T to toggle rotation",20,80 )canvas.DrawText( "Cursors + A/Z to move (slowly!)",20,100 )canvas.DrawText( "Esc to exit",20,140 )If ground_thread.Runningcanvas.DrawText( "Generating new terrain...",20,180 )EndifEndEnd' For demo only...Function GenerateTerrain:Model (seed:ULong, size:Int = 1024, roughness:Float = 0.5, color0:Color = New Color (0.8, 0.5, 0.1), color1:Color = New Color (0.75, 0.35, 0.05))Local terrain:PlayfulJSTerrain = New PlayfulJSTerrain (seed, size, roughness)Local heightmap:Pixmap = terrain.RenderPixmap ()If Not heightmap Then RuntimeError ("Failed to generate heightmap!")heightmap.FlipY () ' 2D Y (increases downwards) translates to 3D Z (increases upwards)Local terrain_material:PbrMaterial = New PbrMaterial ()terrain_material.ColorTexture = New Texture (CheckerPixmap (color0, color1), TextureFlags.None)Local terrain_height:Float = 256.0 ' TODO!Local height_box:Boxf = New Boxf (-heightmap.Width * 0.5, 0.0, -heightmap.Height * 0.5, heightmap.Width * 0.5, terrain_height, heightmap.Height * 0.5)Return Model.CreateTerrain (heightmap, height_box, New PbrMaterial (terrain_material))EndFunction Main()New AppInstanceNew MyWindowApp.Run()EndAugust 16, 2018 at 12:39 pm #15310Updated to fix jagged edges — threaded demo source and binary updated too. (Non-threaded ‘should’ just run from Ted2Go in recent releases, without needing GitHub develop branch.)
August 18, 2018 at 1:22 am #15313This is cool.
-
AuthorPosts
You must be logged in to reply to this topic.
