terrain.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. -- Terrain generation code
  2. -- Used by maps
  3. if not exists("Support_MapGeneration_Loaded") then
  4. require("util")
  5. dofile("add-ons/_misc/createbrick.lua")
  6. pngImage = require("png")
  7. require("colormatch")
  8. Support_MapGeneration_Loaded = true
  9. end
  10. -- modter brick names
  11. local modterHeightName = {
  12. [2] = " Steep",
  13. [1] = "",
  14. [0.5] = " 1/2h",
  15. [0.75] = " 3/4h",
  16. [0.25] = " 1/4h",
  17. }
  18. local modterTypes = {
  19. ["Cube"] = true, ["Wedge"] = true, ["CornerA"] = true, ["CornerB"] = true, ["Ramp"] = true, ["Crest"] = true, ["Corner Wedge"] = true, ["Corner Wedge L"] = true, ["Corner Wedge R" ] = true,
  20. }
  21. local modterTypesNoInv = {
  22. ["Cube"] = true, ["Wedge"] = true,
  23. }
  24. local function modterUiNameFromSize(type, width, height)
  25. local ext = modterHeightName[height] or error("invalid modter cube height "..height)
  26. local pre = ""
  27. if type=="Cube" and ext=="" and width>4 then ext = " " end
  28. if type=="Cube" and ext=="" and width==4 then pre = " " end
  29. if type:find("Inv") then
  30. type = type:gsub(" *Inv.?", "")
  31. assert(modterTypes[type] and not modterTypesNoInv[type], "Invalid modter inv type "..type)
  32. type = type:gsub("Corner", "Cor")
  33. return pre..width.."x "..type..ext.." Inv."
  34. else
  35. assert(modterTypes[type], "Invalid modter type "..type)
  36. return pre..width.."x "..type..ext
  37. end
  38. end
  39. -- physical bricks
  40. local function getBrickString(brick)
  41. return table.concat( {
  42. brick.uiName,
  43. brick.pos.x, brick.pos.y, brick.pos.z,
  44. brick.angleId,
  45. }, "_").."|"
  46. end
  47. local function deleteBrick(brick)
  48. if ts.isObject(brick.id) then
  49. ts.callobj(brick.id, "delete")
  50. end
  51. brick.id = nil
  52. end
  53. local function makeBrick(brick)
  54. local id = createbrick(brick.uiName, brick.pos, brick.angleId, brick.color, true)
  55. if id and ts.isObject(id) then
  56. brick.id = id
  57. if brick.printName then
  58. setbrickprint(id, brick.printName)
  59. end
  60. else
  61. print("failed to create terrain brick at "..tostring(brick.pos)..": " ..brick.uiName)
  62. end
  63. end
  64. local function syncBricks(old, new)
  65. --assert(old.uiName == new.uiName, "attempt to sync bricks with different uiName")
  66. --assert(old.pos == new.pos, "attempt to sync bricks with different position: "..getBrickString(old)..", "..getBrickString(new))
  67. --assert(old.angleId == new.angleId, "attempt to sync bricks with different angleId")
  68. assert(getBrickString(old)==getBrickString(new), "attempt to sync bricks with different info: "..getBrickString(old)..", "..getBrickString(new))
  69. local id = old.id
  70. new.id = id
  71. if old.color ~= new.color then
  72. ts.callobj(id, "setColor", new.color)
  73. end
  74. if old.printName ~= new.printName and new.printName then
  75. setbrickprint(id, new.printName)
  76. end
  77. end
  78. -- virtual bricks
  79. local function realPosFromGridPos(x, y, z, mapLowerPos, cubeWidth, cubeHeight, notCube)
  80. local cubeWidthTU = cubeWidth/2
  81. local cubeSize = vector{cubeWidthTU, cubeWidthTU, cubeWidthTU*cubeHeight}
  82. local brickBottomCenterOffset = vector{cubeWidthTU/2, cubeWidthTU/2, 0}
  83. local brickPos = mapLowerPos + vector{x-1, y-1, z}*cubeSize + brickBottomCenterOffset
  84. if notCube then brickPos = brickPos + vector{0, 0, cubeWidth*cubeHeight/4 - 0.1} end
  85. return brickPos
  86. end
  87. local function addBrick(ter, uiName, brickPos, angleId, color, printName)
  88. local brick = {
  89. uiName = uiName,
  90. pos = brickPos,
  91. angleId = angleId,
  92. color = color,
  93. printName = printName,
  94. }
  95. ter[getBrickString(brick)] = brick
  96. end
  97. -- map translation - columns
  98. local function addColumnStep(step, brickType, angleId, ter, mapLowerPos, x, y, h1, h2, cubeWidth, cubeHeight, color, printName)
  99. if cubeHeight <= 2/step then
  100. while h2-h1 >= step do
  101. local brickPos = realPosFromGridPos(x, y, h1, mapLowerPos, cubeWidth, cubeHeight)
  102. local uiName = modterUiNameFromSize(brickType, cubeWidth, cubeHeight*step)
  103. addBrick(ter, uiName, brickPos, angleId, color, printName)
  104. h1 = h1 + step
  105. end
  106. end
  107. return h1
  108. end
  109. local function addColumn(brickType, angleId, ter, mapLowerPos, x, y, h1, h2, cubeWidth, cubeHeight, color, printName)
  110. if h2>0 then
  111. h1 = h1-1
  112. if h1<0 then h1 = 0 end
  113. if cubeHeight==0.5 then
  114. if cubeWidth<64 then
  115. h1 = addColumnStep(8, brickType, angleId, ter, mapLowerPos, x, y, h1, h2, cubeWidth, cubeHeight, color, printName) end -- because no 64x cube steep
  116. h1 = addColumnStep(4, brickType, angleId, ter, mapLowerPos, x, y, h1, h2, cubeWidth, cubeHeight, color, printName) end
  117. if cubeHeight==0.25 then
  118. h1 = addColumnStep(3, brickType, angleId, ter, mapLowerPos, x, y, h1, h2, cubeWidth, cubeHeight, color, printName) end -- for 0.75x
  119. h1 = addColumnStep(2, brickType, angleId, ter, mapLowerPos, x, y, h1, h2, cubeWidth, cubeHeight, color, printName)
  120. h1 = addColumnStep(1, brickType, angleId, ter, mapLowerPos, x, y, h1, h2, cubeWidth, cubeHeight, color, printName)
  121. end
  122. end
  123. -- map translation - cube marching
  124. local function getHeightMapBox3(box, getHeight)
  125. local heightMap = {}
  126. local minZ, maxZ = 1000, 0
  127. for x = box[1]-2, box[3]+2 do
  128. heightMap[x] = {}
  129. for y = box[2]-2, box[4]+2 do
  130. local z = getHeight(x, y)
  131. heightMap[x][y] = z
  132. if z > maxZ then maxZ = z end
  133. if z < minZ then minZ = z end
  134. end
  135. end
  136. local box3 = {box[1]-1, box[2]-1, math.floor(minZ)-2, box[3]+1, box[4]+1, math.ceil(maxZ)+1}
  137. box3 = map(box3, math.round)
  138. return heightMap, box3
  139. end
  140. local function getCornerStr(corners)
  141. return table.concat(map(corners, function(x) return x and "1" or "0" end))
  142. end
  143. local function getCornerFromStr(cstr)
  144. local t = {}
  145. for i = 1, #cstr do
  146. local c = cstr:sub(i, i)
  147. table.insert(t, c=="1")
  148. end
  149. return t
  150. end
  151. local cornerBricksInit = { -- uiName, angleId, coverageStr -X +X -Y +Y -Z +Z at given angle ID
  152. ["11111111"] = {"Cube" , 0, "111111"},
  153. ["10101111"] = {"Ramp" , 0, "010010"},
  154. ["00111111"] = {"Wedge" , 0, "010110"},
  155. ["10111111"] = {"CornerB" , 0, "010110"},
  156. ["00101011"] = {"CornerA" , 0, "000000"},
  157. ["10101011"] = {"CornerA" , 0, "000000"},
  158. ["00101111"] = {"CornerA" , 0, "000000"},
  159. ["00111011"] = {"CornerA" , 0, "000000"},
  160. ["10111110"] = {"Crest" , 1, "000010"},
  161. ["00111110"] = {"Corner Wedge" , 1, "000000"},
  162. ["10100011"] = {"Corner Wedge R", 0, "000000"},
  163. ["10001011"] = {"Corner Wedge L", 1, "000000"},
  164. ["00001010"] = true,
  165. ["00100011"] = true,
  166. ["00001011"] = true,
  167. ["00000000"] = true,
  168. ["00000010"] = true,
  169. ["00101010"] = true,
  170. ["10101010"] = true,
  171. ["00000011"] = true,
  172. ["00111100"] = true,
  173. ["00001111"] = true,
  174. ["10000010"] = true,
  175. ["10000011"] = true,
  176. }
  177. local function reorderStr(str, order)
  178. assert(#str == #order, "reorderStr - lengths are different")
  179. local str2t = {}
  180. for i = 1, #str do
  181. local idx = tonumber(order:sub(i, i))
  182. str2t[idx] = str:sub(i, i)
  183. end
  184. local str2 = table.concat(str2t)
  185. assert(#str2 == #order)
  186. return str2
  187. end
  188. local cornerStrRotations = {
  189. "12345678",
  190. "34781256",
  191. "78563412",
  192. "56127834",
  193. }
  194. local cornerStrZFlip = "21436587"
  195. function transformCornerStr(cstr, rot, inv)
  196. cstr = reorderStr(cstr, cornerStrRotations[rot+1] or error("transformCornerStr - invalid angle ID "..rot))
  197. if inv then cstr = reorderStr(cstr, cornerStrZFlip) end
  198. return cstr
  199. end
  200. local coverageStrRotations = {
  201. "123456",
  202. "431256",
  203. "214356",
  204. "342156",
  205. }
  206. local coverageStrZFlip = "123465"
  207. local function transformCoverageStr(cstr, rot, inv)
  208. cstr = reorderStr(cstr, coverageStrRotations[rot+1] or error("transformCoverageStr - invalid angle ID "..rot))
  209. if inv then cstr = reorderStr(cstr, coverageStrZFlip) end
  210. return cstr
  211. end
  212. local function addCornerBrick(cornerBricks, cstr, brick, rot, inv)
  213. cstr = transformCornerStr(cstr, rot, inv)
  214. if type(brick)=="table" then
  215. local brickType = brick[1]
  216. local angleId = brick[2]
  217. local coverageStr = brick[3]
  218. if not cornerBricks[cstr] then
  219. cornerBricks[cstr] = {brickType..(inv and " Inv" or ""), (angleId+rot)%4, transformCoverageStr(coverageStr, rot, inv)}
  220. end
  221. else
  222. cornerBricks[cstr] = true
  223. end
  224. end
  225. local function makeCornerBrickTable()
  226. local cornerBricks = {}
  227. for cstr, brick in pairs(cornerBricksInit) do
  228. addCornerBrick(cornerBricks, cstr, brick, 0, false)
  229. addCornerBrick(cornerBricks, cstr, brick, 1, false)
  230. addCornerBrick(cornerBricks, cstr, brick, 2, false)
  231. addCornerBrick(cornerBricks, cstr, brick, 3, false)
  232. addCornerBrick(cornerBricks, cstr, brick, 0, true )
  233. addCornerBrick(cornerBricks, cstr, brick, 1, true )
  234. addCornerBrick(cornerBricks, cstr, brick, 2, true )
  235. addCornerBrick(cornerBricks, cstr, brick, 3, true )
  236. end
  237. return cornerBricks
  238. end
  239. local cornerBricks = makeCornerBrickTable()
  240. function getBrickForCorners(corners)
  241. local cstr = getCornerStr(corners)
  242. local brick = cornerBricks[cstr]
  243. if brick then
  244. if type(brick) == "table" then
  245. return brick, cstr
  246. else
  247. return nil
  248. end
  249. else
  250. print("No brick for corner str "..cstr)
  251. return nil
  252. end
  253. end
  254. local function addBrickGrid(ter, box3, brickGrid, mapLowerPos, cubeWidth, cubeHeight, getColor, printName)
  255. local cubeWidthTU = cubeWidth/2
  256. local cubeSize = vector{cubeWidthTU, cubeWidthTU, cubeWidthTU*cubeHeight}
  257. local brickBottomCenterOffset = vector{cubeWidthTU/2, cubeWidthTU/2, 0}
  258. for x = box3[1]+1, box3[4]-1 do
  259. for y = box3[2]+1, box3[5]-1 do
  260. local color
  261. for z = box3[3]+1, box3[6]-1 do
  262. local brick = brickGrid[x][y][z]
  263. if brick then
  264. local brickType = brick[1]
  265. local angleId = brick[2]
  266. color = color or getColor(x, y)
  267. local uiName = modterUiNameFromSize(brickType, cubeWidth, cubeHeight)
  268. local brickPos = mapLowerPos + vector{x-1, y-1, z}*cubeSize + brickBottomCenterOffset
  269. addBrick(ter, uiName, brickPos, angleId, color, printName)
  270. end
  271. end
  272. end
  273. end
  274. end
  275. local adjacent2 = {
  276. vector{-0.5, -0.5}, vector{-0.5, 0.5},
  277. vector{ 0.5, -0.5}, vector{ 0.5, 0.5},
  278. }
  279. local function getCornerGridFromHeightMap(box3, heightMap)
  280. local cornerGrid = {}
  281. for x = box3[1]-0.5, box3[4]+0.5 do
  282. cornerGrid[x] = {}
  283. for y = box3[2]-0.5, box3[5]+0.5 do
  284. cornerGrid[x][y] = {}
  285. --local heightSum = 0
  286. --local heightMin = 1000
  287. --local heightMax = 0
  288. --local basepos = vector{x, y}
  289. --for _, adj in ipairs(adjacent2) do
  290. -- local pos = basepos + adj
  291. -- local height = heightMap[pos.x][pos.y]
  292. -- heightSum = heightSum + height
  293. -- if height < heightMin then heightMin = height end
  294. -- if height > heightMax then heightMax = height end
  295. --end
  296. --local heightAvg = heightSum/#adjacent2
  297. --local height = heightMax
  298. local height = heightMap[x-0.5][y-0.5]
  299. for z = box3[3]-0.5, box3[6]+0.5 do
  300. local ground = z <= height
  301. cornerGrid[x][y][z] = ground
  302. end
  303. end
  304. end
  305. return cornerGrid
  306. end
  307. local adjacent3 = {
  308. vector{-0.5, -0.5, -0.5}, vector{-0.5, -0.5, 0.5},
  309. vector{-0.5, 0.5, -0.5}, vector{-0.5, 0.5, 0.5},
  310. vector{ 0.5, -0.5, -0.5}, vector{ 0.5, -0.5, 0.5},
  311. vector{ 0.5, 0.5, -0.5}, vector{ 0.5, 0.5, 0.5},
  312. }
  313. local function getBrickGridFromCornerGrid(box3, cornerGrid)
  314. local brickGrid = {}
  315. for x = box3[1], box3[4] do
  316. brickGrid[x] = {}
  317. for y = box3[2], box3[5] do
  318. brickGrid[x][y] = {}
  319. for z = box3[6], box3[3], -1 do
  320. local basepos = vector{x, y, z}
  321. local corners = {}
  322. for i, adj in ipairs(adjacent3) do
  323. local pos = basepos + adj
  324. corners[i] = cornerGrid[pos.x][pos.y][pos.z]
  325. if corners[i]==nil then print("no corner at "..tostring(pos)) end
  326. end
  327. local brick, brickCStr = getBrickForCorners(corners)
  328. if brick then
  329. brickGrid[x][y][z] = brick
  330. local realCorners = getCornerFromStr(brickCStr)
  331. for i, adj in ipairs(adjacent3) do
  332. if not realCorners[i] then
  333. local pos = basepos + adj
  334. cornerGrid[pos.x][pos.y][pos.z] = false
  335. end
  336. end
  337. end
  338. end
  339. end
  340. end
  341. return brickGrid
  342. end
  343. local function addCornerVisBrick(ter, pos, colorId)
  344. addBrick(ter, "2x2x2f", pos, 0, colorId, nil)
  345. end
  346. local function addCornerGridVis(ter, box3, cornerGrid, mapLowerPos, cubeWidth, cubeHeight)
  347. for x = box3[1]-0.5, box3[4]+0.5 do
  348. for y = box3[2]-0.5, box3[5]+0.5 do
  349. for z = box3[3]-0.5, box3[6]+0.5 do
  350. local corner = cornerGrid[x][y][z]
  351. local pos = realPosFromGridPos(x, y, z, mapLowerPos, cubeWidth, cubeHeight, true)
  352. if corner then
  353. addCornerVisBrick(ter, pos, 5)
  354. else
  355. addCornerVisBrick(ter, pos, 32)
  356. end
  357. end
  358. end
  359. end
  360. end
  361. local adjacentFaces3 = {
  362. vector{-0.5, 0 , 0 }, vector{ 0.5, 0 , 0 },
  363. vector{ 0 , -0.5, 0 }, vector{ 0 , 0.5, 0 },
  364. vector{ 0 , 0 , -0.5}, vector{ 0 , 0 , 0.5},
  365. }
  366. local function getBrickCoverage(brick)
  367. local coverage = {}
  368. local cstr = brick[3] or error("brick has no coverage str: "..brick[1].." "..brick[2])
  369. for i, adj in ipairs(adjacentFaces3) do
  370. if cstr:sub(i, i)=="1" then
  371. table.insert(coverage, adj)
  372. end
  373. end
  374. return coverage
  375. end
  376. local function getCoverageGridAndRemoveCubes(box3, brickGrid)
  377. local coverGrid = {}
  378. for x = box3[1], box3[4] do
  379. for y = box3[2], box3[5] do
  380. for z = box3[3], box3[6] do
  381. local brick = brickGrid[x][y][z]
  382. if brick then
  383. if brick[1]=="Cube" then
  384. brickGrid[x][y][z] = nil
  385. end
  386. local coverage = getBrickCoverage(brick)
  387. local basePos = vector{x, y, z}
  388. for i, rel in ipairs(coverage) do
  389. local pos = basePos + rel
  390. coverGrid[pos.x] = coverGrid[pos.x] or {}
  391. coverGrid[pos.x][pos.y] = coverGrid[pos.x][pos.y] or {}
  392. coverGrid[pos.x][pos.y][pos.z] = (coverGrid[pos.x][pos.y][pos.z] or 0) + 1
  393. end
  394. end
  395. end
  396. end
  397. end
  398. return coverGrid
  399. end
  400. local function brickShouldBeCoverageCube(coverGrid, x, y, z)
  401. local ucf = 0
  402. local hasAir = false
  403. local basePos = vector{x, y, z}
  404. for i, adj in ipairs(adjacentFaces3) do
  405. local pos = basePos + adj
  406. local cov = coverGrid[pos.x] and coverGrid[pos.x][pos.y] and coverGrid[pos.x][pos.y][pos.z]
  407. if cov and cov==1 then
  408. ucf = ucf + 1
  409. end
  410. if (not cov) or cov==0 then
  411. hasAir = true
  412. end
  413. end
  414. return ucf>0 and not hasAir
  415. end
  416. local function addCoverageCubesToBrickGrid(ter, box3, coverGrid, brickGrid)
  417. for x = box3[1]+1, box3[4]-1 do
  418. for y = box3[2]+1, box3[5]-1 do
  419. for z = box3[3]+1, box3[6]-1 do
  420. local brick = brickGrid[x][y][z]
  421. if not brick then
  422. if brickShouldBeCoverageCube(coverGrid, x, y, z) then
  423. brickGrid[x][y][z] = {"Cube", 0, "111111"}
  424. end
  425. end
  426. end
  427. end
  428. end
  429. end
  430. local function addCoverageGridVis(ter, box3, coverGrid, mapLowerPos, cubeWidth, cubeHeight)
  431. print(box3)
  432. for x = box3[1]-0.5, box3[4]+0.5, 0.5 do
  433. for y = box3[2]-0.5, box3[5]+0.5, 0.5 do
  434. for z = box3[3]-0.5, box3[6]+0.5, 0.5 do
  435. if coverGrid[x] and coverGrid[x][y] and coverGrid[x][y][z] then
  436. local cov = coverGrid[x][y][z]
  437. local pos = realPosFromGridPos(x, y, z, mapLowerPos, cubeWidth, cubeHeight, true)
  438. addCornerVisBrick(ter, pos, cov)
  439. end
  440. end
  441. end
  442. end
  443. end
  444. local function addOptimizedColumns(ter, box3, brickGrid, mapLowerPos, cubeWidth, cubeHeight, getColor, printName)
  445. for x = box3[1]+1, box3[4]-1 do
  446. for y = box3[2]+1, box3[5]-1 do
  447. local color
  448. local firstType = nil
  449. local firstAngleId = nil
  450. local numOn = 0
  451. for z = box3[3], box3[6] do
  452. local brick = brickGrid[x][y][z]
  453. if brick and brick[1]==firstType and brick[2]==firstAngleId then
  454. numOn = numOn + 1
  455. brickGrid[x][y][z] = nil
  456. else
  457. if numOn>0 then
  458. assert(firstType and firstAngleId)
  459. local h1, h2 = z-numOn+1, z
  460. color = color or getColor(x, y)
  461. addColumn(firstType, firstAngleId, ter, mapLowerPos, x, y, h1, h2, cubeWidth, cubeHeight, color, printName)
  462. end
  463. if brick and (brick[1]=="Cube" or brick[1]=="Wedge") then
  464. firstType = brick[1]; firstAngleId = brick[2]; numOn = 1;
  465. brickGrid[x][y][z] = nil
  466. else
  467. firstType = nil; firstAngleId = nil; numOn = 0;
  468. end
  469. end
  470. end
  471. end
  472. end
  473. end
  474. function GenerateTerrain(mapLowerPos, box, cubeWidth, cubeHeight, getHeight, getColor, printName)
  475. local ter = {} -- map of brick string -> brick id
  476. local heightMap, box3 = getHeightMapBox3(box, getHeight)
  477. local cornerGrid = getCornerGridFromHeightMap(box3, heightMap)
  478. local brickGrid = getBrickGridFromCornerGrid(box3, cornerGrid)
  479. local coverGrid = getCoverageGridAndRemoveCubes(box3, brickGrid)
  480. addCoverageCubesToBrickGrid(ter, box3, coverGrid, brickGrid)
  481. addOptimizedColumns(ter, box3, brickGrid, mapLowerPos, cubeWidth, cubeHeight, getColor, printName)
  482. addBrickGrid(ter, box3, brickGrid, mapLowerPos, cubeWidth, cubeHeight, getColor, printName)
  483. --addCornerGridVis(ter, box3, cornerGrid, mapLowerPos, cubeWidth, cubeHeight)
  484. --addCoverageGridVis(ter, box3, coverGrid, mapLowerPos, cubeWidth, cubeHeight)
  485. return ter
  486. end
  487. -- interface to GenerateTerrain
  488. function DeleteTerrain(ter)
  489. for brickString, brick in pairs(ter) do
  490. deleteBrick(brick)
  491. end
  492. end
  493. function BuildTerrain(ter)
  494. for brickString, brick in pairs(ter) do
  495. makeBrick(brick)
  496. end
  497. end
  498. local function replaceTerrain2(old, new)
  499. for brickString, brick in pairs(new) do
  500. if (not old[brickString]) then
  501. makeBrick(brick)
  502. elseif (not old[brickString].id) then
  503. makeBrick(brick)
  504. elseif (not ts.isObject(old[brickString].id)) then
  505. makeBrick(brick)
  506. else
  507. syncBricks(old[brickString], brick)
  508. end
  509. end
  510. end
  511. function ReplaceTerrain(old, new)
  512. for brickString, brick in pairs(old) do
  513. if (not new[brickString]) then
  514. deleteBrick(brick)
  515. end
  516. end
  517. schedule(1000, replaceTerrain2, old, new)
  518. end
  519. local function getTestMap()
  520. local heightScale = 0.3
  521. local hmimg = pngImage("add-ons/support_mapgeneration/heightmap.png")
  522. local box = {1+2, 1+2, hmimg.width-2, hmimg.height-2}
  523. --local box = {1+2, 1+2, 3+4, 3+4}
  524. local heightMap = {}
  525. local colorMap = {}
  526. for x = box[1]-2, box[3]+2 do
  527. heightMap[x] = {}
  528. colorMap[x] = {}
  529. for y = box[2]-2, box[4]+2 do
  530. --heightMap[x][y] = (math.sin(x/2+2)+1 + math.sin(y)*1)*1 + 5
  531. local pix = hmimg.pixels[x][y].R * heightScale
  532. heightMap[x][y] = pix
  533. colorMap[x][y] = 5
  534. end
  535. end
  536. return box, heightMap, colorMap
  537. end
  538. function TestGenerateTerrain()
  539. local box, heightMap, colorMap = getTestMap()
  540. local printName = "modter/bricktop"
  541. local function getHeight(x, y) return heightMap[x][y] end
  542. local function getColor (x, y) return colorMap [x][y] end
  543. local ter = GenerateTerrain(vector{0, 0, 0}, box, 16, 0.5, getHeight, getColor, printName)
  544. if exists("TestTerrain") then
  545. ReplaceTerrain(TestTerrain, ter)
  546. else
  547. BuildTerrain(ter)
  548. end
  549. TestTerrain = ter
  550. end
  551. --TestGenerateTerrain()