map.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. require("terrain")
  2. -- Map object code
  3. -- Generates terrain using terrain.lua
  4. local function generateMapChunk(map, x, y)
  5. local box = {
  6. (x-1)*map.chunkSize + 1,
  7. (y-1)*map.chunkSize + 1,
  8. (x )*map.chunkSize ,
  9. (y )*map.chunkSize ,
  10. }
  11. local ter = GenerateTerrain(map.lowerPosition, box, map.cubeWidth, map.cubeHeight, map.getHeight, map.getColor, map.printName)
  12. return ter
  13. end
  14. function LoadMapChunk(map, x, y)
  15. assert(not (map.chunks[x] and map.chunks[x][y]), "LoadMapChunk: Chunk is already loaded: "..x.." "..y)
  16. map.chunks[x] = map.chunks[x] or {}
  17. map.chunks[x][y] = generateMapChunk(map, x, y)
  18. BuildTerrain(map.chunks[x][y])
  19. end
  20. function UnloadMapChunk(map, x, y)
  21. --assert(map.chunks[x] and map.chunks[x][y], "UnloadMapChunk: Chunk is not loaded: "..x.." "..y)
  22. if map.chunks[x] and map.chunks[x][y] then
  23. DeleteTerrain(map.chunks[x][y])
  24. map.chunks[x][y] = nil
  25. end
  26. end
  27. function UpdateMapChunk(map, x, y)
  28. --assert(map.chunks[x] and map.chunks[x][y], "UpdateMapChunk: Chunk is not loaded")
  29. if map.chunks[x] and map.chunks[x][y] then
  30. local newChunk = generateMapChunk(map, x, y)
  31. local oldChunk = map.chunks[x][y]
  32. ReplaceTerrain(oldChunk, newChunk)
  33. map.chunks[x][y] = newChunk
  34. else
  35. LoadMapChunk(map, x, y)
  36. end
  37. end
  38. function UnloadAllMapChunks(map)
  39. local chunkCoords = {}
  40. for x, xChunks in pairs(map.chunks) do
  41. for y, chunk in pairs(xChunks) do
  42. table.insert(chunkCoords, {x, y})
  43. end
  44. end
  45. for _, v in ipairs(chunkCoords) do
  46. UnloadMapChunk(map, v[1], v[2])
  47. end
  48. end
  49. local function setMapDimensions(map, w, h)
  50. map.heightmapWidth = w
  51. map.heightmapHeight = h
  52. map.xChunks = map.heightmapWidth * map.scale.x / map.chunkSize
  53. map.yChunks = map.heightmapHeight * map.scale.y / map.chunkSize
  54. assert(map.xChunks%1==0 and map.yChunks%1==0, "Map dimensions after scaling are not divisible by chunk size")
  55. end
  56. local function getPixel(img, x, y, scale)
  57. local px = math.clamp(math.round(x/scale.x), 1, img.width )
  58. local py = math.clamp(math.round(y/scale.y), 1, img.height)
  59. return img.pixels[px][py]
  60. end
  61. function SetMapHeightmap(map, heightMapFn)
  62. check(map, "table") check(heightMapFn, "string")
  63. print("Reading png: "..heightMapFn)
  64. local heightMapImg = pngImage(heightMapFn) or error("Invalid height map file \""..heightMapFn.."\"")
  65. map.getHeight = function(x, y)
  66. return getPixel(heightMapImg, x, y, map.scale).R * map.scale.z
  67. end
  68. assert(not map.heightmapWidth or (map.heightmapWidth == heightMapImg.width and map.heightmapHeight == heightMapImg.height), "trying to load a heightmap of different size")
  69. setMapDimensions(map, heightMapImg.width, heightMapImg.height)
  70. print("Done")
  71. end
  72. function SetMapColormap(map, colorMapFn)
  73. check(map, "table")
  74. print("Reading png: "..colorMapFn)
  75. if type(colorMapFn)=="string" then
  76. local colorMapImg = pngImage(colorMapFn) or error("Invalid color map file \""..colorMapFn.."\"")
  77. assert(map.heightmapWidth, "must load heightmap before colormap")
  78. assert(colorMapImg.width == map.heightmapWidth and colorMapImg.height == map.heightmapHeight, "Color map and height map are different sizes")
  79. map.getColor = function(x, y)
  80. local pixel = getPixel(colorMapImg, x, y, map.scale)
  81. local color = { pixel.R/255, pixel.G/255, pixel.B/255, 1 }
  82. return FindClosestColor(color)
  83. end
  84. map.colorMapImg = colorMapImg
  85. elseif type(colorMapFn)=="number" then
  86. local color = colorMapFn
  87. map.getColor = function(x, y)
  88. return color
  89. end
  90. map.colorMapImg = nil
  91. else error("invalid color map type: "..type(colorMapFn).." (must be filename or number)") end
  92. print("Done")
  93. end
  94. local function readMapFile(fn)
  95. local fi = io.open(fn, "r")
  96. if not fi then error("Could not open \""..fn.."\"") end
  97. local txt = fi:read("*a")
  98. fi:close()
  99. txt = txt:gsub("\r\n$", "")
  100. return txt
  101. end
  102. local function getDataPixel(x, y, mapData, map)
  103. local px = math.clamp(math.round(x/map.scale.x), 1, map.heightmapWidth ) - 1
  104. local py = math.clamp(math.round(y/map.scale.y), 1, map.heightmapHeight) - 1
  105. local offset = (py*map.heightmapWidth + px)*6
  106. local str = mapData:sub(offset+1, offset+6)
  107. local num = decodeBase64(str)
  108. return num
  109. end
  110. function SetMapDataFile(map, width, height, mapFn)
  111. check(map, "table") check(width, "number") check(height, "number") check(mapFn, "string")
  112. local mapData = readMapFile(mapFn)
  113. local expectedSize = width*height*6
  114. assert(#mapData == expectedSize, "map data is wrong size (expected "..expectedSize.." but got "..(#mapData))
  115. setMapDimensions(map, width, height)
  116. map.getColor = function(x, y)
  117. local n = getDataPixel(x, y, mapData, map)
  118. local r, g, b = math.floor(n/256^3), math.floor(n/256^2)%256, math.floor(n/256)%256
  119. return FindClosestColor({r/255, g/255, b/255})
  120. end
  121. map.getHeight = function(x, y)
  122. local n = getDataPixel(x, y, mapData, map)
  123. local h = (n%256)*map.scale.z
  124. return h
  125. end
  126. end
  127. function CreateMap(lowerPosition, scale, cubeWidth, cubeHeight, printName, chunkSize)
  128. check(lowerPosition, "vector") check(scale, "vector") check(cubeWidth, "number") check(cubeHeight, "number") checkopt(printName, "string") checkopt(chunkSize, "number")
  129. printName = printName or "modter/brickTop"
  130. chunkSize = chunkSize or 16
  131. -- map object
  132. local map = {
  133. scale = scale,
  134. cubeWidth = cubeWidth,
  135. cubeHeight = cubeHeight,
  136. printName = printName,
  137. chunkSize = chunkSize,
  138. lowerPosition = lowerPosition,
  139. chunks = {},
  140. }
  141. return map
  142. end