Real World Terrain Generation

Hello i wanted to know how could i use the api of Mapbox in order to generate terrain from it, i tried to do this using only elevation but it obviously didn’t work and i’ve seen plugins and games using real world data in order to generate terrain. Here are some examples of it:

City Loader plugin has a integrated feature that can generate terrain using real world data.

So if you know a way to do this please share it with me in this topic so other people can see it too.

1 Like

Heightmaps wont work for me since most of the time the scale of it is not similar to that of real world terrain, and what im trying to achive is a way to use the data im requesting to generate terrain from it, like a chunk system.

Reliving this! I think it’d be cool to have that.

Hey! I’ve recently made a project inspired from AirX terrain generation.

You can get Elevation data using Bing Maps and then visualize it by connecting them by small triangles.


This turned out better than I thought!

2 Likes

Hey I’ve wondered how is that possible can you possibly give some explanation of how? like the script and some few templates (im sorta new to scripting)

i work with kuzey all it is, is just making triangles from height data nothing complicated im currently working to make roads and buildings work ive got some of it down

3 Likes

doesen’t it cost a lot of money to use bings elevation data?

No, it is totally free to use.

How do i get it, also like bing has removed the big elevation thing

amazon has a set of free tiles up to zoom 15:

https://s3.amazonaws.com/elevation-tiles-prod/terrarium/zoom/x/y.png

you just need to decode the pixel color to get the elevation in a range of 0 - 8848 meters.

Formula: (red * 256 + green + blue / 256) - 32768

Note that the RGB need to be in a range of 0 to 255.

how do i decode the pixel color in roblox?

1 Like

theres not a way of getting images through roblox http service so you would need to make a webserver like an api so it returns the decoded pixels there, but i’ve made one myself so if you want feel free to use it.

the way it works is with POST requests, You need to give it the Tile Coodinates, Zoom and Resolution.

My api will return an array of colors, heights and water pixels (The heights are already decoded).

local httpService = game:GetService("HttpService")

function deg2num(lon, lat, zoom)
	local n = 2 ^ zoom
	local lon_deg = tonumber(lon)
	local lat_rad = math.rad(lat)
	local xtile = math.floor(n * ((lon_deg + 180) / 360))
	local ytile = math.floor(n * (1 - (math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi)) / 2)
	return xtile, ytile
end

function num2deg(x, y, z)
	local n = 2 ^ z
	local lon_deg = x / n * 360.0 - 180.0
	local lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * y / n)))
	local lat_deg = lat_rad * 180.0 / math.pi
	return lon_deg, lat_deg
end

local url = "https://roearthserver01.pythonanywhere.com/Terrain/v2" -- My API Url
local latitude, longitude = 46.19842278192789, -122.19063690278452 -- The location you want to get the data of

local zoom = 12
local x, y = deg2num(longitude, latitude, zoom) -- Get X and Y Tiles
local resolution = 256 -- Resolution should be within a range of (2 - 256)

-- The PostAsync() method with my API needs to always have this order: (TileX, TileY, Zoom, Resolution)

local data = httpService:JSONDecode(
	httpService:PostAsync(url, httpService:JSONEncode({x, y, zoom, resolution}))
)

local heights = data.heights
local colors = data.colors

-- Print the pixel (1, 1) and (256, 256) in the decoded heightmap image
-- The decoded heightmap image has a range of (1 - Resolution), In this case is (1 - 256), Remember its using 1 - indexing like lua

print(heights[`{1},{1}`])
print(heights[`{256},{256}`])

If you want to visualize it then heres a code for it, Just be careful with the resolution since it might lag a LOT your studio, And i also recommend doing it on a new studio place.

local httpService = game:GetService("HttpService")

function deg2num(lon, lat, zoom)
	local n = 2 ^ zoom
	local lon_deg = tonumber(lon)
	local lat_rad = math.rad(lat)
	local xtile = math.floor(n * ((lon_deg + 180) / 360))
	local ytile = math.floor(n * (1 - (math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi)) / 2)
	return xtile, ytile
end

function num2deg(x, y, z)
	local n = 2 ^ z
	local lon_deg = x / n * 360.0 - 180.0
	local lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * y / n)))
	local lat_deg = lat_rad * 180.0 / math.pi
	return lon_deg, lat_deg
end

local url = "https://roearthserver01.pythonanywhere.com/Terrain/v2" -- My API Url
local latitude, longitude = 46.19842278192789, -122.19063690278452 -- The location you want to get the data of

local zoom = 14
local x, y = deg2num(longitude, latitude, zoom) -- Get X and Y Tiles
local resolution = 64 -- Resolution should be within a range of (2 - 256)

-- The PostAsync() method with my API needs to always have this order: (TileX, TileY, Zoom, Resolution)

local data = httpService:JSONDecode(
	httpService:PostAsync(url, httpService:JSONEncode({x, y, zoom, resolution}))
)

local heights = data.heights
local colors = data.colors

local metersPerPixel = (156543 / math.pow(2, zoom) * 256) / resolution -- the distance between each pixel in meters
local scale = 3.57 -- roblox units to meters

for y = 1, resolution - 1 do
	for x = 1, resolution - 1 do
		local point00 = Instance.new("Attachment")
		point00.WorldPosition = Vector3.new(x * metersPerPixel, heights[`{x},{y}`], y * metersPerPixel) * scale
		
		local point10 = Instance.new("Attachment")
		point10.WorldPosition = Vector3.new((x + 1) * metersPerPixel, heights[`{x + 1},{y}`], y * metersPerPixel) * scale
		
		local point01 = Instance.new("Attachment")
		point01.WorldPosition = Vector3.new(x * metersPerPixel, heights[`{x},{y + 1}`], (y + 1) * metersPerPixel) * scale
		
		local point11 = Instance.new("Attachment")
		point11.WorldPosition = Vector3.new((x + 1) * metersPerPixel, heights[`{x + 1},{y + 1}`], (y + 1) * metersPerPixel) * scale
		
		local rod0 = Instance.new("RodConstraint")
		rod0.Attachment0 = point00
		rod0.Attachment1 = point01
		rod0.Thickness = scale * 10
		rod0.Visible = true
		rod0.Length = (rod0.Attachment1.WorldPosition - rod0.Attachment0.WorldPosition).Magnitude
		
		local rod1 = Instance.new("RodConstraint")
		rod1.Attachment0 = point00
		rod1.Attachment1 = point01
		rod1.Thickness = scale * 10
		rod1.Visible = true
		rod1.Length = (rod1.Attachment1.WorldPosition - rod1.Attachment0.WorldPosition).Magnitude
		
		local rod2 = Instance.new("RodConstraint")
		rod2.Attachment0 = point00
		rod2.Attachment1 = point11
		rod2.Thickness = scale * 10
		rod2.Visible = true
		rod2.Length = (rod2.Attachment1.WorldPosition - rod2.Attachment0.WorldPosition).Magnitude
		
		local rod3 = Instance.new("RodConstraint")
		rod3.Attachment0 = point01
		rod3.Attachment1 = point11
		rod3.Thickness = scale * 10
		rod3.Visible = true
		rod3.Length = (rod3.Attachment1.WorldPosition - rod3.Attachment0.WorldPosition).Magnitude
		
		local rod4 = Instance.new("RodConstraint")
		rod4.Attachment0 = point10
		rod4.Attachment1 = point11
		rod4.Thickness = scale * 10 
		rod4.Visible = true
		rod4.Length = (rod4.Attachment1.WorldPosition - rod4.Attachment0.WorldPosition).Magnitude
		
		rod4.Parent = workspace.Terrain
		point00.Parent = workspace.Terrain
		point10.Parent = workspace.Terrain
		point01.Parent = workspace.Terrain
		point11.Parent = workspace.Terrain
		
		rod0.Parent = workspace.Terrain
		rod1.Parent = workspace.Terrain
		rod2.Parent = workspace.Terrain
		rod3.Parent = workspace.Terrain

	end
	
	task.wait()
end

task.wait(30)

workspace.Terrain:ClearAllChildren()
2 Likes

is there a catch or something? and also what does zoom do like?