Hi, just a small question.
Will the Terrain/Heightmap-stuff be implemented in MiniB3D? Or is the only way a Meshterrain?
Will the Terrain/Heightmap-stuff be implemented in MiniB3D? Or is the only way a Meshterrain?
Graphics3D 800,600 SetBuffer BackBuffer() Global Camera = CreateCamera() PositionEntity Camera, 0, 200, -10 CameraRange Camera, 0.1, 1000.0 Local light = CreateLight(1) Local tex = LoadTexture("tex.png") Text 5, 5, "Loading terrain..." Flip Local Terrain = LoadMeshTerrain("hmap.png") PositionEntity Terrain, 0, 0, 0 ScaleEntity Terrain, 10, 500, 10 EntityTexture Terrain, tex Global MoveSpeed# = 5.0 Local old_ms = MilliSecs() Local renders Local fps Global mxs# Global mys# While Not KeyHit(1) Cls HandleInput RenderWorld renders = renders + 1 If MilliSecs() -old_ms >= 1000 old_ms = MilliSecs() fps = renders renders = 0 EndIf Text 5, 5, "FPS: " + fps Flip False Wend End Function handleInput() mxs = mxs + (MouseXSpeed() / 3.0) mys = mys + (MouseYSpeed() / 3.0) RotateEntity Camera, mys, -mxs, 0 MoveMouse GraphicsWidth() / 2, GraphicsHeight() / 2 MouseXSpeed() MouseYSpeed() If KeyDown(200) Or KeyDown(17) Or MouseDown(2) MoveEntity Camera, 0.0, 0.0, MoveSpeed EndIf If KeyDown(208) Or KeyDown(31) MoveEntity Camera, 0.0, 0.0, -MoveSpeed EndIf If KeyDown(32) Then MoveEntity Camera, MoveSpeed, 0.0, 0.0 If KeyDown(30) Then MoveEntity Camera, -MoveSpeed, 0.0, 0.0 End Function ;http://www.blitzbasic.com/Community/posts.php?topic=62118 Function LoadMeshTerrain(url$, parent = 0) Local img = LoadImage(url$) SetBuffer ImageBuffer(img) Local mesh = CreateMesh(parent) Local surf = CreateSurface(mesh) Local uscale# = 1.0 / ImageWidth(img) Local vscale# = 1.0 / ImageHeight(img) For x = 0 To ImageWidth(img) - 1 For y = 0 To ImageHeight(img) - 1 argb = ReadPixel(x, y) height# = (((argb Shl 16) And $FF) + ((argb Shl 8) And $FF) + (argb And $FF)) / (3.0 * 255.0) v = AddVertex(surf, x, height, y, x * uscale, y * vscale) VertexNormal surf, v, 0, 0, 0 Next Next For x = 0 To ImageWidth(img) - 2 For y = 0 To ImageHeight(img) - 2 v0 = x * ImageHeight(img) + y v1 = v0 + 1 v2 = v1 + ImageHeight(img) v3 = v0 + ImageHeight(img) t0 = AddTriangle(surf, v0, v1, v2) t1 = AddTriangle(surf, v0, v2, v3) Next Next For v = 0 To CountVertices(surf) - 1 nx# = VertexNX(surf, v) ny# = VertexNY(surf, v) nz# = VertexNZ(surf, v) d# = 1.0 / Sqr(nx * nx + ny * ny + nz * nz) VertexNormal surf, v, nx * d, ny * d, nz * d Next UpdateNormals mesh SetBuffer BackBuffer() FreeImage img Return mesh End Function
SuperStrict Import "c:\temp\MiniB3D-v041\minib3d.bmx" Graphics3D 800,600 Global Camera:TCamera = CreateCamera() PositionEntity Camera, 0, 200, -10 CameraRange Camera, 0.1, 1000.0 Local light:TLight = CreateLight(1) Local tex:TTexture = LoadTexture("tex.png") Text 5, 5, "Loading terrain..." Flip Local Terrain:TMesh = LoadTerrain("hmap.png") PositionEntity Terrain, 0, 0, 0 ScaleEntity Terrain, 10, 500, 10 EntityTexture Terrain,tex Global MoveSpeed:Float = 5.0 Local old_ms:Int = MilliSecs() Local renders:Int Local fps:Int Global mxs:Float Global mys:Float While Not KeyDown(KEY_ESCAPE) Cls HandleInput RenderWorld renders :+ 1 If MilliSecs() -old_ms >= 1000 old_ms = MilliSecs() fps = renders renders = 0 EndIf Text 5, 5, "FPS: " + String(fps) Flip False Wend End Function handleInput() mxs :+ (MouseXSpeed() / 3.0) mys :+ (MouseYSpeed() / 3.0) RotateEntity Camera, mys, -mxs, 0 MoveMouse GraphicsWidth() / 2, GraphicsHeight() / 2 MouseXSpeed() MouseYSpeed() If KeyDown(KEY_UP) Or KeyDown(KEY_W) Or MouseDown(2) MoveEntity Camera, 0.0, 0.0, MoveSpeed EndIf If KeyDown(KEY_DOWN) Or KeyDown(KEY_S) MoveEntity Camera, 0.0, 0.0, -MoveSpeed EndIf If KeyDown(KEY_D) Then MoveEntity Camera, MoveSpeed, 0.0, 0.0 If KeyDown(KEY_A) Then MoveEntity Camera, -MoveSpeed, 0.0, 0.0 EndFunction 'http://www.blitzbasic.com/Community/posts.php?topic=62118 Function LoadTerrain:TMesh(url:Object, parent:TEntity = Null) Local pix:TPixmap If TPixmap(url) pix = TPixmap(url) Else pix = LoadPixmap(url) If pix = Null Return Null EndIf Local mesh:TMesh = CreateMesh(parent) Local surf:TSurface = CreateSurface(mesh, Null) Local uscale:Float = 1.0 / pix.width Local vscale:Float = 1.0 / pix.height For Local x:Int = 0 To pix.width - 1 For Local y:Int = 0 To pix.height - 1 Local argb:Int = pix.ReadPixel(x, y) Local height:Float = (((argb Shl 16) & $FF) + ((argb Shl 8) & $FF) + (argb & $FF)) / (3.0 * 255.0) Local v:Int = AddVertex(surf, x, height, y, x * uscale, y * vscale) VertexNormal surf, v, 0, 0, 0 Next Next For Local x:Int = 0 To pix.width - 2 For Local y:Int = 0 To pix.height - 2 Local v0:Int = x * pix.height + y Local v1:Int = v0 + 1 Local v2:Int = v1 + pix.height Local v3:Int = v0 + pix.height Local t0:Int = AddTriangle(surf, v0, v1, v2) Local t1:Int = AddTriangle(surf, v0, v2, v3) Next Next For Local v:Int = 0 Until CountVertices(surf) Local nx:Float = surf.VertexNX(v) Local ny:Float = surf.VertexNY(v) Local nz:Float = surf.VertexNZ(v) Local d:Float = 1.0 / Sqr(nx * nx + ny * ny + nz * nz) VertexNormal surf, v, nx * d, ny * d, nz * d Next UpdateNormals mesh Return mesh End Function
Rem MiniB3d Chunked Terrain Mesh Test (Proof of concept / performance test) Notes: * Faster than non-chunked large terrain meshes, but probably not fast enough for any practical purposes. 512 x 512 heightmaps will reduce minib3d to a crawl. * The heightmap must have equal dimensions that are power of two (eg 128x128, 256x256, etc). * KEYS: WASD, Arrows. * MOUSE: Right Button EndRem SuperStrict Import "c:\temp\minib3d\minib3d.bmx" ' <- Change this to your minib3d path / module Graphics3D 800, 600 Text 5, 5, "Generating sample height map..." Flip createSampleHeightMap("temp_hmap.png", 128, 128, 1000, 32, False) Text 5, 25, "Setting up world and loading terrain..." Flip AmbientLight 255, 255, 255 Global Camera:TCamera = CreateCamera() CameraRange Camera, 0.1, 4000.0 CameraClsColor Camera, 64, 164, 196 '**** Terrain Stuff Local Terrain:TChunkedTerrain = New TChunkedTerrain Terrain.Load("temp_hmap.png") Terrain.TextureFromFile("temp_hmap.png", 1) ' <- Using the heightmap image for the texture also. Terrain.Scale 40.0, 1000.0, 40.0 Terrain.Position 0.0, 0.0, 0.0 '**** PositionEntity Camera, Terrain.X, 1000.0, Terrain.Z Global MoveSpeed:Float = 2.5 Local old_ms:Int = MilliSecs() Local renders:Int Local fps:Int Global mxs:Float Global mys:Float 'WireFrame True While Not KeyDown(KEY_ESCAPE) Cls HandleInput UpdateWorld RenderWorld renders :+ 1 If MilliSecs() -old_ms >= 1000 old_ms = MilliSecs() fps = renders renders = 0 EndIf Text 5, 5, "FPS: " + String(fps) text 5, 25, EntityX(Camera) + ", " + EntityZ(Camera) Flip False 'Delay 1 Wend FreeEntity Camera Camera = Null Terrain.Destroy Terrain = Null GCCollect End '********** CHUNKED TERRAIN CLASS ********** Type TChunkedTerrain Field Size:Int Field ChunkSize:Int Field Cols:Int Field Rows:Int Field Pivot:TPivot Field Chunks:TMesh[,] Field X:Float Field Y:Float Field Z:Float Field XScale:Float Field YScale:Float Field ZScale:Float Method New() X = 0.0 Y = 0.0 Z = 0.0 XScale = 1.0 YScale = 1.0 ZScale = 1.0 EndMethod Method Destroy() For Local col:Int = 0 To Cols - 1 For Local row:Int = 0 To Rows - 1 FreeEntity Chunks[col, row] Chunks[col, row] = Null Next Next FreeEntity Pivot Pivot = Null Chunks = Null GCCollect EndMethod 'Chunk Size should be less than the heightmap size and a power of two (eg, 16, 32, 64, etc) Method Load:Int(hmap_url:String, chunk_size:Int = 64) Local pix:TPixmap = LoadPixmap(hmap_url) If Not pix Return False Size = pix.height ChunkSize = chunk_size Cols = (Size / ChunkSize) Rows = (Size / ChunkSize) Pivot = CreatePivot() PositionEntity Pivot, X, Y, Z, True Chunks = New TMesh[Cols, Rows] For Local col:Int = 0 To Cols - 1 For Local row:Int = 0 To Rows - 1 Local px:Int = (col * ChunkSize) - col Local pz:Int = (row * ChunkSize) - row Local hmappm:TPixmap = PixmapWindow(pix, px, pz, ChunkSize + 1, ChunkSize + 1) Chunks[col, row] = CreateChunk(hmappm, Pivot) Local wx:Float = Float(px) Local wz:Float = Float(((rows - 1) * chunk_size) - pz) PositionEntity Chunks[col, row], wx, 0.0, wz, False Next Next Scale XScale, YScale, ZScale Return True EndMethod Method TextureFromFile:Int(tex_url:String = "", flags:Int = 1, frame:Int = 0, index:Int = 0) If Not pivot Return False Local tex:TPixmap = LoadPixmap(tex_url) If Not tex Then Return False tex = ResizePixmap(tex, Size, Size) For Local col:Int = 0 To Cols - 1 For Local row:Int = 0 To Rows - 1 Local px:Int = (col * ChunkSize) - col Local pz:Int = (row * ChunkSize) - row Local fname:String = "temp_chunk_tex_" + col + "_" + row + ".png" Local pm:TPixmap = PixmapWindow(tex, px, pz, ChunkSize + 1, ChunkSize + 1) SavePixmapPNG(pm, fname, 9) Local tex_chunk:TTexture = LoadTexture(fname, flags) ScaleTexture tex_chunk, pm.width, pm.height EntityTexture chunks[col, row], tex_chunk, frame, index DeleteFile fname Next Next Return True EndMethod Method Scale(sx:Float, sy:Float, sz:Float) If Not Pivot Return XScale = sx YScale = sy ZScale = sz ScaleEntity Pivot, XScale, YScale, ZScale EndMethod Method Position(wx:Float, wy:Float, wz:Float) If Not Pivot Return X = wx Y = wy Z = wz PositionEntity Pivot, X, Y, Z EndMethod Function CreateChunk:TMesh(pix:TPixmap, parent:TEntity = Null) Rem * Ported And slightly modified from Blitz3D code: <a href="http://www.blitzbasic.com/codearcs/codearcs.php?code=1609" target="_blank">http://www.blitzbasic.com/codearcs/codearcs.php?code=1609</a> EndRem If Not pix Return Null Local pixw:Int = PixmapWidth(pix) Local pixh:Int = PixmapHeight(pix) Local mesh:TMesh = CreateMesh(parent) Local surf:TSurface = CreateSurface(mesh) For Local ly:Int = 0 To pixh For Local lx:Int = 0 To pixw AddVertex surf, lx, 0, ly, (1.0 / lx), (1.0 / ly) Next Next For Local ly:Int = 1 To (pixw - 2) For Local lx:Int = 1 To (pixh - 2) AddTriangle surf, lx + ((pixw + 1) * ly), lx + ((pixw + 1) * ly) + (pixw + 1), (lx + 1) + ((pixw + 1) * ly) AddTriangle surf, (lx + 1) + ((pixw + 1) * ly), lx + ((pixw + 1) * ly) + (pixw + 1), (lx + 1)+((pixw + 1) * ly) + (pixw + 1) Next Next For Local lx:Int = 0 To (pixw - 1) For Local ly:Int = 0 To (pixh - 1) Local r:Byte = ReadPixel(pix, lx, pixh - 1 - ly) Local index:Int = lx + ((pixw + 1) * ly) VertexCoords surf, index, VertexX(surf, index), Float(r) / 255.0, VertexZ(surf, index) VertexTexCoords surf, index, lx, -ly Next Next UpdateNormals mesh Return Mesh End Function EndType '****************************** '****** MISCELLANEOUS FUNCTIONS (NOT required for Chunked Terrain class) ********* Function handleInput() mxs :+ (MouseXSpeed() / 3.0) mys :+ (MouseYSpeed() / 3.0) RotateEntity Camera, mys, -mxs, 0 MoveMouse GraphicsWidth() / 2, GraphicsHeight() / 2 MouseXSpeed() MouseYSpeed() If KeyDown(KEY_UP) Or KeyDown(KEY_W) Or MouseDown(2) MoveEntity Camera, 0.0, 0.0, MoveSpeed EndIf If KeyDown(KEY_DOWN) Or KeyDown(KEY_S) MoveEntity Camera, 0.0, 0.0, -MoveSpeed EndIf If KeyDown(KEY_D) Then MoveEntity Camera, MoveSpeed, 0.0, 0.0 If KeyDown(KEY_A) Then MoveEntity Camera, -MoveSpeed, 0.0, 0.0 EndFunction Function createSampleHeightMap(fname:String, width:Int = 128, height:Int = 128, numhills:Int = 1000, maxhsize:Int = 32, island:Int = True) 'Generate Local Map:Float[width, height] SeedRnd(MilliSecs()) If island numhills :* 0.2 maxhsize :* 0.5 EndIf For Local n:Int = 1 To numhills Local radius:Int = Rand(0, maxhsize) Local centrex:Int = Rand(0, Width - 1) Local centrez:Int = Rand(0, Height - 1) If island Local theta:Int = Rand(0, 359) Local distx:Int = Rand(0, (Width * 0.5) - radius) Local distz:Int = Rand(0, (Height * 0.5) - radius) centrex = (Width * 0.5) + (Cos(theta) * distx) centrez = (Height * 0.5) + (Sin(theta) * distz) EndIf Local startx:Int = centrex - radius If startx < 0 startx = 0 Local endx:Int = centrex + radius If endx > Width endx = Width Local startz:Int = centrez - radius If startz < 0 startz = 0 Local endz:Int = centrez + radius If endz > Height endz = Height For Local x:Int = startx To (endx - 1) For Local z:Int = startz To (endz - 1) Local y:Float = (radius * radius) - (((x - centrex) * (x - centrex)) + ((z - centrez) * (z - centrez))) If y > 0.0 Map[x, z] :+ y Next Next Next 'Normalise Local minv:Float = 10^38, maxv:Float = 10^-38 For Local x:Int = 0 To (Width - 1) For Local z:Int = 0 To (Height - 1) If Map[x, z] < minv minv = Map[x, z] Else If Map[x, z] > maxv maxv = Map[x, z] Next Next For Local x:Int = 0 To (Width - 1) For Local z:Int = 0 To (Height - 1) Map[x, z] = (Map[x, z] - minv) / (maxv - minv) Next Next 'Convert to byte Array Local arr:Byte[,] = New Byte[width, height] For Local x:Int = 0 To (Width - 1) For Local z:Int = 0 To (Height - 1) Local v:Int = Floor(Map[x, z] * 255) arr[x, z] = Byte(v) Next Next 'Render to pixmap and save to file Local pm:TPixmap = CreatePixmap(width, height, PF_RGB888) For Local x:Int = 0 To (Width - 1) For Local z:Int = 0 To (Height - 1) WritePixel(pm, x, z, arr[x, z] Shl 16 | arr[x, z] Shl 8 | arr[x, z]) Next Next SavePixmapPNG(pm, fname, 9) End Function
UpdateNormals mesh mesh.UpdateBuffer() Return Mesh