123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- require("terrain")
- -- Map object code
- -- Generates terrain using terrain.lua
- local function generateMapChunk(map, x, y)
- local box = {
- (x-1)*map.chunkSize + 1,
- (y-1)*map.chunkSize + 1,
- (x )*map.chunkSize ,
- (y )*map.chunkSize ,
- }
- local ter = GenerateTerrain(map.lowerPosition, box, map.cubeWidth, map.cubeHeight, map.getHeight, map.getColor, map.printName)
- return ter
- end
- function LoadMapChunk(map, x, y)
- assert(not (map.chunks[x] and map.chunks[x][y]), "LoadMapChunk: Chunk is already loaded: "..x.." "..y)
- map.chunks[x] = map.chunks[x] or {}
- map.chunks[x][y] = generateMapChunk(map, x, y)
- BuildTerrain(map.chunks[x][y])
- end
- function UnloadMapChunk(map, x, y)
- --assert(map.chunks[x] and map.chunks[x][y], "UnloadMapChunk: Chunk is not loaded: "..x.." "..y)
- if map.chunks[x] and map.chunks[x][y] then
- DeleteTerrain(map.chunks[x][y])
- map.chunks[x][y] = nil
- end
- end
- function UpdateMapChunk(map, x, y)
- --assert(map.chunks[x] and map.chunks[x][y], "UpdateMapChunk: Chunk is not loaded")
- if map.chunks[x] and map.chunks[x][y] then
- local newChunk = generateMapChunk(map, x, y)
- local oldChunk = map.chunks[x][y]
- ReplaceTerrain(oldChunk, newChunk)
- map.chunks[x][y] = newChunk
- else
- LoadMapChunk(map, x, y)
- end
- end
- function UnloadAllMapChunks(map)
- local chunkCoords = {}
- for x, xChunks in pairs(map.chunks) do
- for y, chunk in pairs(xChunks) do
- table.insert(chunkCoords, {x, y})
- end
- end
- for _, v in ipairs(chunkCoords) do
- UnloadMapChunk(map, v[1], v[2])
- end
- end
- local function setMapDimensions(map, w, h)
- map.heightmapWidth = w
- map.heightmapHeight = h
- map.xChunks = map.heightmapWidth * map.scale.x / map.chunkSize
- map.yChunks = map.heightmapHeight * map.scale.y / map.chunkSize
- assert(map.xChunks%1==0 and map.yChunks%1==0, "Map dimensions after scaling are not divisible by chunk size")
- end
- local function getPixel(img, x, y, scale)
- local px = math.clamp(math.round(x/scale.x), 1, img.width )
- local py = math.clamp(math.round(y/scale.y), 1, img.height)
- return img.pixels[px][py]
- end
- function SetMapHeightmap(map, heightMapFn)
- check(map, "table") check(heightMapFn, "string")
- print("Reading png: "..heightMapFn)
- local heightMapImg = pngImage(heightMapFn) or error("Invalid height map file \""..heightMapFn.."\"")
- map.getHeight = function(x, y)
- return getPixel(heightMapImg, x, y, map.scale).R * map.scale.z
- end
- assert(not map.heightmapWidth or (map.heightmapWidth == heightMapImg.width and map.heightmapHeight == heightMapImg.height), "trying to load a heightmap of different size")
- setMapDimensions(map, heightMapImg.width, heightMapImg.height)
- print("Done")
- end
- function SetMapColormap(map, colorMapFn)
- check(map, "table")
- print("Reading png: "..colorMapFn)
- if type(colorMapFn)=="string" then
- local colorMapImg = pngImage(colorMapFn) or error("Invalid color map file \""..colorMapFn.."\"")
- assert(map.heightmapWidth, "must load heightmap before colormap")
- assert(colorMapImg.width == map.heightmapWidth and colorMapImg.height == map.heightmapHeight, "Color map and height map are different sizes")
- map.getColor = function(x, y)
- local pixel = getPixel(colorMapImg, x, y, map.scale)
- local color = { pixel.R/255, pixel.G/255, pixel.B/255, 1 }
- return FindClosestColor(color)
- end
- map.colorMapImg = colorMapImg
- elseif type(colorMapFn)=="number" then
- local color = colorMapFn
- map.getColor = function(x, y)
- return color
- end
- map.colorMapImg = nil
- else error("invalid color map type: "..type(colorMapFn).." (must be filename or number)") end
- print("Done")
- end
- local function readMapFile(fn)
- local fi = io.open(fn, "r")
- if not fi then error("Could not open \""..fn.."\"") end
- local txt = fi:read("*a")
- fi:close()
- txt = txt:gsub("\r\n$", "")
- return txt
- end
- local function getDataPixel(x, y, mapData, map)
- local px = math.clamp(math.round(x/map.scale.x), 1, map.heightmapWidth ) - 1
- local py = math.clamp(math.round(y/map.scale.y), 1, map.heightmapHeight) - 1
- local offset = (py*map.heightmapWidth + px)*6
- local str = mapData:sub(offset+1, offset+6)
- local num = decodeBase64(str)
- return num
- end
- function SetMapDataFile(map, width, height, mapFn)
- check(map, "table") check(width, "number") check(height, "number") check(mapFn, "string")
- local mapData = readMapFile(mapFn)
- local expectedSize = width*height*6
- assert(#mapData == expectedSize, "map data is wrong size (expected "..expectedSize.." but got "..(#mapData))
- setMapDimensions(map, width, height)
- map.getColor = function(x, y)
- local n = getDataPixel(x, y, mapData, map)
- local r, g, b = math.floor(n/256^3), math.floor(n/256^2)%256, math.floor(n/256)%256
- return FindClosestColor({r/255, g/255, b/255})
- end
- map.getHeight = function(x, y)
- local n = getDataPixel(x, y, mapData, map)
- local h = (n%256)*map.scale.z
- return h
- end
- end
- function CreateMap(lowerPosition, scale, cubeWidth, cubeHeight, printName, chunkSize)
- check(lowerPosition, "vector") check(scale, "vector") check(cubeWidth, "number") check(cubeHeight, "number") checkopt(printName, "string") checkopt(chunkSize, "number")
- printName = printName or "modter/brickTop"
- chunkSize = chunkSize or 16
-
- -- map object
- local map = {
- scale = scale,
- cubeWidth = cubeWidth,
- cubeHeight = cubeHeight,
- printName = printName,
- chunkSize = chunkSize,
- lowerPosition = lowerPosition,
- chunks = {},
- }
- return map
- end
|