Infinite Terrain Plugin

You need 2 Roblox accounts

First log into your game with 1 account and generate a huge amount of terrain after you have generated a huge amount of terrain then log in on your second account without logging out of your first account so that the server does not reset

If you use your slow phone as the second account it would be better

Thatā€™s exactly what I didā€¦

there must of been a problem with my scripts that prevented iphone from loading the terrain but i still think client sided terrain is better

On the client script there is a wait() after every chunk has loaded if you remove the wait then it will load faster

And when Roblox adds multi thread support it will get even faster but server sided terrain is always limited to how long it takes you to send all that data over the network

I personally care more about network performance and have never felt any performance degradation from generating the terrain on the client side

the noise function is not that hard for the CPU to compute

And the building of the terrain mesh has to be done on both server and client sided versions

So your sacrificing network performance in exchange for the math.noise function

but its upto you if you feel that server side is better then use it that way i personally think client side is better and ill keep using it that way

I am very, very interesting in using this plugin for a game I want to make. The plugin is extremely well thought out :+1:

However, I wanted to have different ā€œareasā€ or biomes(snow, grass, sea, artic) that each area could be different to each other. I found it impossible to do something like this. The only way I found away around it was to generate the biomes separately with different and then smooth the changes in the biomes. I did manage to do this on a small scale but since I wanted to have a big map I couldnā€™t find a way to do that efficiently.

The only other solution I had was to have biomes at different hieghts but that didnā€™t look that good as biomes started to forms ā€œringsā€ around other biomes and I couldnā€™t directly control the biomes size.

Other than that this is an amazing plugin.

Take a look at this video

Every time I try loading terrain it deletes everything in the models folder. Why is it doing this?

The model folder inside of workspace is only temporary while the game is running there should only be the model folder in replicated storage

It is in replicated storage, but when I generate it gets deleted, and the output window says ā€œModelName isnā€™t a valid member of TerrainModelsā€.

Every time I try to download the plugin, I get this error message:

devforumhelp1

Iā€™m gonna be honest, I have no clue what this error means

Anyone know of a way to fix this?

Thatā€™s very strange because there is nothing in my code that deletes the terrainmodels folder in replicated storage one option is if you edit the sourcecode It has models spawning in there and you can see how I done it another option is to send some screenshots and I can try and see what is wrong in your project or you can also chat with me in discord

This error looks like itā€™s a problem with Roblox not the plugin

Maybe if you Google the error it might give you some solutions

There are 2 ways to install the plugin

One way is to install it from the toolbox

Or a second way is to install it is from the download link on the first post

Do you get this error if you try installing both ways?

Yeah, Iā€™ve tried installing it from the toolbox as well as the link, but both have the same error.

Maybe you should try making a new thread under bug reports or help

1 Like

Not sure what happend, but I tried installing it again and it worked fine. Might have just been an internet problem.

This plugin is really great, however Iā€™ve been running into some flickering issues that are most visible at the flat parts of a generated terrain. My settings are similar to that of the ā€œArticā€ terrain youtube video, and this flickering effect seems to be most visible with ice and snow materials, however it still appears with grass materials.
The effect disappears if youā€™re close to it, but becomes really visible if you climb up on a mountain and look down.

Edit: Found the issue, it was the baseplate and itā€™s default texture making the flickering in the terrain, my mistake.

1 Like

How do you view the code for the server-side version? The provided link doesnā€™t allow editing. Iā€™m trying to do something similar. What modifications to the script were needed to get it working server-side?

Though I wonder if you can have the best of both approaches. What if both the client and server dynamically generate the same terrain? That way whether a particular voxel is generated client-side first or server-side and streamed to the client, the client will always have whichever is generated fastest. This would also solve the server-side physics problem in a more general way than selectively generating areas in the studio.

I have a problem with spwaning models. Here what not working by me.


The palmtrees are spawning underwater. Can i do something to fix it?

Here is my code:

Open
local modelCopies = game.ServerStorage.TerrainModels

local dataChild
local heightChild
local materialChild

for i, child in ipairs(script:GetChildren()) do
	if child.ClassName == "ModuleScript" then
		if child:GetAttribute("Data") == "Terrain" then
			dataChild = child
		end
	elseif child.ClassName == "Folder" then
		local data = child:GetAttribute("Data")
		if data == "Height" then
			heightChild = child
		elseif data == "Material" then
			materialChild = child
		end
	end
end
if dataChild == nil then return end

local data = require(dataChild)

local distance = 30
local chunkSize = 16
local heightData = {}
local materialData = {}
local loaded = {}
local modelsFolder
if #data.models > 0 then
	modelsFolder = Instance.new("Folder")
	modelsFolder.Name = "TerrainModels"
	modelsFolder.Parent = workspace
end

if heightChild then
	for i, child in ipairs(heightChild:GetDescendants()) do
		if child.ClassName ~= "ModuleScript" then continue end
		local data = require(child)
		local position = child:GetAttribute("Position")
		for i = 1, #data, 2 do
			local x = position.X + data[i]
			local zData = data[i + 1]
			if heightData[x] == nil then heightData[x] = {} end
			for j = 1, #zData, 2 do
				local z = position.Y + zData[j]
				local height = zData[j + 1]
				heightData[x][z] = height
			end
		end
	end
end

if materialChild then
	for i, child in ipairs(materialChild:GetDescendants()) do
		if child.ClassName ~= "ModuleScript" then continue end
		local data = require(child)
		local position = child:GetAttribute("Position")
		for i = 1, #data, 2 do
			local x = position.X + data[i]
			local zData = data[i + 1]
			if materialData[x] == nil then materialData[x] = {} end
			for j = 1, #zData, 2 do
				local z = position.Y + zData[j]
				local material = zData[j + 1]
				materialData[x][z] = material
			end
		end
	end
end

local function GetHeight(x:number, z:number):number
	if heightData[x] == nil then heightData[x] = {} end
	if heightData[x][z] ~= nil then return heightData[x][z] end	
	local height = 0
	for i, data in ipairs(data.noises) do
		local noise = math.noise(x * data[3], data[1], z * data[3])
		height += math.clamp(noise, data[4], data[5]) * data[2]
	end
	height += data.shift
	height = math.clamp(height, data.minimumHeight, data.maximumHeight)
	heightData[x][z] = height
	return height
end

local function Load(x:number, z:number):()
	local minimum = math.huge
	local maximum = -math.huge
	for xx = x-1, x+1 do
		for zz = z-1, z+1 do
			local height = GetHeight(xx, zz)
			minimum = math.min(minimum, height)
			maximum = math.max(maximum, height)
		end
	end
	local slope = maximum - minimum
	local height = heightData[x][z]
	local thickness = height - minimum + data.thickness
	local position = Vector3.new(x * 4, height, z * 4)
	local cFrame = CFrame.new(x * 4 + 2, height - thickness / 2, z * 4 + 2)
	local size = Vector3.new(4, thickness, 4)
	if materialData[x] ~= nil and materialData[x][z] ~= nil then
		workspace.Terrain:FillBlock(cFrame, size, materialData[x][z])
	else
		for i, materialData in ipairs(data.materials) do
			if height < materialData[2] or height >= materialData[3] then continue end
			if slope < materialData[4] or slope >= materialData[5] then continue end
			workspace.Terrain:FillBlock(cFrame, size, materialData[1])
			break
		end
	end
	for i, modelData in ipairs(data.models) do
		if math.fmod(x, modelData[2]) ~= 0 or math.fmod(z, modelData[2]) ~= 0 then continue end
		if height < modelData[3] or height >= modelData[4] then continue end
		if slope < modelData[5] or slope >= modelData[6] then continue end
		local load = true
		local offset = Vector3.new(0, 0, 0)
		local scale = Vector3.new(1, 1, 1)
		local rotation = Vector3.new(0, 0, 0)
		for i, data in ipairs(modelData[7]) do
			if data[1] == 1 then
				local noise = math.noise(x * data[3], data[2], z * data[3])
				if noise < data[4] or noise >= data[5] then load = false break end
			elseif data[1] == 2 then
				offset += Vector3.new(data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5], 0, 0)
			elseif data[1] == 3 then
				offset += Vector3.new(0, data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5], 0)
			elseif data[1] == 4 then
				offset += Vector3.new(0, 0, data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5])
			elseif data[1] == 5 then
				scale *= data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5]
			elseif data[1] == 6 then
				scale *= Vector3.new(data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5], 1, 1)
			elseif data[1] == 7 then
				scale *= Vector3.new(1, data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5], 1)
			elseif data[1] == 8 then
				scale *= Vector3.new(1, 1, data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5])
			elseif data[1] == 9 then
				rotation += Vector3.new(data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5], 0, 0)
			elseif data[1] == 10 then
				rotation += Vector3.new(0, data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5], 0)
			elseif data[1] == 11 then
				rotation += Vector3.new(0, 0, data[4] + math.noise(x * data[3], data[2], z * data[3]) * data[5])
			end
		end
		if load == false then continue end
		if scale.X <= 0 or scale.Y <= 0 or scale.Z <= 0 then continue end
		local clone = modelCopies[modelData[1]]:Clone()
		local pivotPosition = clone:GetPivot().Position
		for i, descendant in ipairs(clone:GetDescendants()) do
			if descendant:IsA("BasePart") == false then continue end
			descendant.PivotOffset += (descendant.PivotOffset.Position * scale) - descendant.PivotOffset.Position
			descendant.Position = pivotPosition + (descendant.Position - pivotPosition) * scale
			descendant.Size *= scale
		end
		clone:PivotTo(CFrame.new(position + offset) * CFrame.fromOrientation(math.rad(rotation.X), math.rad(rotation.Y), math.rad(rotation.Z)))
		clone.Parent = modelsFolder
		break
	end
	if height >= data.waterHeight then return end
	thickness = data.waterHeight - height
	local cFrame = CFrame.new(x * 4 + 2, height + thickness / 2, z * 4 + 2)
	local size = Vector3.new(4, thickness, 4)
	workspace.Terrain:FillBlock(cFrame, size, Enum.Material.Water)
end

local function LoadChunk(chunkX:number, chunkZ:number):()
	if loaded[chunkX] == nil then loaded[chunkX] = {} end
	if loaded[chunkX][chunkZ] ~= nil then return end
	loaded[chunkX][chunkZ] = true
	local startX = chunkX * chunkSize
	local startZ = chunkZ * chunkSize
	local endX = startX + chunkSize - 1
	local endZ = startZ + chunkSize - 1
	for x = startX, endX do
		for z = startZ, endZ do
			Load(x, z)
		end
	end
	task.wait()
end

local function LoadChunks(chunkX:number, chunkZ:number, hole:number?):()
	if hole == nil then hole = 0 LoadChunk(chunkX, chunkZ) end
	hole = hole * 2 + 1
	local x = chunkX + math.floor(hole/2)
	local z = chunkZ - math.floor(hole/2)
	local dx, dz = 1, 0
	local passed = hole - 1
	local length = hole
	local amount = (distance * 2 + 1) * (distance * 2 + 1) - hole * hole
	for i = 1, amount do
		x += dx z += dz
		if Vector2.new(chunkX - x, chunkZ - z).Magnitude < distance + 0.5 then LoadChunk(x, z) end
		passed += 1
		if passed == length then passed = 0 dx, dz = -dz, dx if dz == 0 then length += 1 end end
	end
end

local positions:{[string]:Vector2} = {}
local function CharacterAdded(char)
	local name = game.Players:GetPlayerFromCharacter(char).Name
	char:WaitForChild("Humanoid"):GetPropertyChangedSignal("MoveDirection"):Connect(function()
		local x = char.HumanoidRootPart.Position.X
		local z = char.HumanoidRootPart.Position.Z
		local magnitude = Vector2.new(positions[name].X - x, positions[name].Y - z).Magnitude
		if magnitude < chunkSize * 8 then return end
		positions[name] = Vector2.new(x, z)
		x = math.floor(positions[name].X / 4 / chunkSize)
		z = math.floor(positions[name].Y / 4 / chunkSize)
		if magnitude < distance * chunkSize * 2 then
			LoadChunks(x, z, math.floor(distance / 2 - 0.5))
		else
			LoadChunks(x, z)
		end
	end)
end

for _, v in ipairs(game.Players:GetChildren()) do
	if v.Character then
		CharacterAdded(v.Character)
	end
	v.CharacterAdded:Connect(CharacterAdded)
	positions[v.Name] = Vector2.new(math.huge, math.huge)
end
game.Players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(CharacterAdded)
	positions[plr.Name] = Vector2.new(math.huge, math.huge)
end)

At 14:34 on this video I show you how to not spawn models under the water

I dont want to get them underwater. I want to get them out of the water. How can i do that? Thanks for reading