Terrain

Show Deprecated
Not Creatable

Terrain lets you create dynamically morphable environments with little to no lag. It is currently based on a 4×4×4 grid of cells, where each cell has a number between 0 and 1 representing how much the geometry should occupy the cell, and the material of the cell. The occupancy determines how the cell will morph together with surrounding cells, and the result is the illusion of having no grid constraint.

For more information, see Terrain.

Summary

Properties

Properties inherited from BasePart

Properties

Properties inherited from PVInstance

Properties

Methods

Methods inherited from BasePart

Methods

Methods inherited from PVInstance

Methods

Events

Events inherited from BasePart

Events

Properties

Decoration

Not Scriptable
Read Parallel

GrassLength

Not Scriptable
Read Parallel

MaterialColors

BinaryString
Not Scriptable
Read Parallel

MaxExtents

Read Only
Not Replicated
Read Parallel

WaterColor

Read Parallel

WaterReflectance

Read Parallel

WaterTransparency

Read Parallel

WaterWaveSize

Read Parallel

WaterWaveSpeed

Read Parallel

Methods

CellCenterToWorld

Parameters


Returns

CellCornerToWorld

Parameters


Returns

Clear

()

Returns

()

ClearVoxelsAsync_beta

()

Parameters

region: Region3
channelIds: Array

Returns

()

CopyRegion

Parameters

region: Region3int16

Returns

Code Samples

Terrain:CopyRegion

local terrainRegion = workspace.Terrain:CopyRegion(workspace.Terrain.MaxExtents)
workspace.Terrain:Clear()
task.wait(5)
workspace.Terrain:PasteRegion(terrainRegion, workspace.Terrain.MaxExtents.Min, true)

CountCells


Returns

FillBall

()

Parameters

center: Vector3
radius: number
material: Enum.Material

Returns

()

Code Samples

Filling a Ball of Terrain

local Workspace = game:GetService("Workspace")
-- Creates a ball of grass at (0,0,-10) with a radius of 10 studs
Workspace.Terrain:FillBall(Vector3.new(0, 0, -10), 10, Enum.Material.Grass)

FillBlock

()

Parameters

cframe: CFrame
size: Vector3
material: Enum.Material

Returns

()

FillCylinder

()

Parameters

cframe: CFrame
height: number
radius: number
material: Enum.Material

Returns

()

FillRegion

()

Parameters

region: Region3
resolution: number
material: Enum.Material

Returns

()

FillWedge

()

Parameters

cframe: CFrame
size: Vector3
material: Enum.Material

Returns

()

GetMaterialColor

Write Parallel

Parameters

material: Enum.Material

Returns

IterateVoxelsAsync_beta

Parameters

region: Region3
resolution: number
channelIds: Array

Returns

ModifyVoxelsAsync_beta

Parameters

region: Region3
resolution: number
channelIds: Array

Returns

PasteRegion

()

Parameters

corner: Vector3int16
pasteEmptyCells: boolean

Returns

()

Code Samples

Create, Copy and Paste Terrain

--[[
Note: The use of int16 variants for these API is the result of legacy code.
The underlying voxel grid system uses Vector3int32 (Vector3).
]]
local Workspace = game:GetService("Workspace")
local Terrain = Workspace.Terrain
-- Create a simple terrain region (a 10x10x10 block of grass)
local initialRegion = Region3.new(Vector3.zero, Vector3.one * 10)
Terrain:FillRegion(initialRegion, 4, Enum.Material.Grass)
-- Copy the region using Terrain:CopyRegion
local copyRegion = Region3int16.new(Vector3int16.new(0, 0, 0), Vector3int16.new(10, 10, 10))
local copiedRegion = Terrain:CopyRegion(copyRegion)
-- Define where to paste the region (in this example, offsetting by 5 studs on the X-axis)
local newRegionCorner = Vector3int16.new(5, 0, 0)
-- Paste the region using Terrain:PasteRegion
Terrain:PasteRegion(copiedRegion, newRegionCorner, true)

ReadVoxelChannels

Write Parallel

Parameters

region: Region3
resolution: number
channelIds: Array

Returns

Code Samples

Terrain:ReadVoxelChannels()

local REGION_START = Vector3.new(-20, -20, -20)
local REGION_END = Vector3.new(20, 20, 20)
local function printRegion(terrain, region)
local channelOutput = terrain:ReadVoxelChannels(region, 4, { "SolidOccupancy", "SolidMaterial", "LiquidOccupancy" })
local size = channelOutput.Size
for x = 1, size.X do
for y = 1, size.Y do
for z = 1, size.Z do
print(
("(%2i, %2i, %2i): %.2f %s %.2f"):format(
x,
y,
z,
channelOutput.SolidOccupancy[x][y][z],
channelOutput.SolidMaterial[x][y][z].Name,
channelOutput.LiquidOccupancy[x][y][z]
)
)
end
end
end
end
local region = Region3.new(REGION_START, REGION_END)
printRegion(workspace.Terrain, region)

ReadVoxels

Write Parallel

Parameters

region: Region3
resolution: number

Returns

Code Samples

Terrain:ReadVoxels() Code Example

local REGION_START = Vector3.new(-20, -20, -20)
local REGION_END = Vector3.new(20, 20, 20)
local function printRegion(terrain, region)
local materials, occupancies = terrain:ReadVoxels(region, 4)
local size = materials.Size -- Same as occupancies.Size
for x = 1, size.X, 1 do
for y = 1, size.Y, 1 do
for z = 1, size.Z, 1 do
print(("(%2i, %2i, %2i): %.2f %s"):format(x, y, z, occupancies[x][y][z], materials[x][y][z].Name))
end
end
end
end
local region = Region3.new(REGION_START, REGION_END)
printRegion(workspace.Terrain, region)

ReadVoxelsAsync_beta

Parameters

region: Region3
resolution: number
channelIds: Array

Returns

ReplaceMaterial

()

Parameters

region: Region3
resolution: number
sourceMaterial: Enum.Material
targetMaterial: Enum.Material

Returns

()

Code Samples

Terrain:ReplaceMaterial

local Workspace = game:GetService("Workspace")
local terrain = Workspace.Terrain
local region = Region3.new(Vector3.new(-20, -20, -20), Vector3.new(20, 20, 20))
local resolution = 4
local materialToReplace = Enum.Material.Grass
local replacementMaterial = Enum.Material.Asphalt
terrain:ReplaceMaterial(region, resolution, materialToReplace, replacementMaterial)

SetMaterialColor

()

Parameters

material: Enum.Material
value: Color3

Returns

()

WorldToCell

Parameters

position: Vector3

Returns

WorldToCellPreferEmpty

Parameters

position: Vector3

Returns

WorldToCellPreferSolid

Parameters

position: Vector3

Returns

WriteVoxelChannels

()

Parameters

region: Region3
resolution: number
channels: Dictionary

Returns

()

Code Samples

Terrain:WriteVoxelChannels()

local region = Region3.new(Vector3.new(0, 0, 0), Vector3.new(64, 32, 64))
local RESOLUTION = 4
local OCC_EPSILON = 1 / 256
local function generateRandomTerrainInRegion(regionInput)
local region = regionInput:ExpandToGrid(4)
local size = region.Size / 4
local solidMaterials = {}
local solidOccupancies = {}
local waterOcc = {}
for x = 1, size.X do
table.insert(solidMaterials, {})
table.insert(solidOccupancies, {})
table.insert(waterOcc, {})
for y = 1, size.Y do
table.insert(solidMaterials[x], {})
table.insert(solidOccupancies[x], {})
table.insert(waterOcc[x], {})
for z = 1, size.Z do
local mat = if math.random() < 0.5 then Enum.Material.Air else Enum.Material.Sand
local occ = 0
local water = math.random()
if mat == Enum.Material.Sand then
occ = math.random() / 2 + 0.5
if occ > 1 - OCC_EPSILON then
water = 0 -- Solids cannot contain water
end
else
occ = 0
end
table.insert(solidMaterials[x][y], mat)
table.insert(solidOccupancies[x][y], occ)
table.insert(waterOcc[x][y], water)
end
end
end
return { SolidMaterial = solidMaterials, SolidOccupancy = solidOccupancies, LiquidOccupancy = waterOcc }
end
local regionContent = generateRandomTerrainInRegion(region)
workspace.Terrain:WriteVoxelChannels(region, 4, regionContent)

WriteVoxels

()

Parameters

region: Region3
resolution: number
materials: Array
occupancy: Array

Returns

()

Code Samples

Example

local Workspace = game:GetService("Workspace")
local terrain = Workspace.Terrain
local resolution = 4
local region = Region3.new(Vector3.new(0, 0, 0), Vector3.new(16, 28, 20)):ExpandToGrid(resolution)
local materials = {
{
{
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
},
{ Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock },
{ Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock },
{ Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand },
{ Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand },
{ Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud },
{ Enum.Material.Air, Enum.Material.Air, Enum.Material.Air, Enum.Material.Air, Enum.Material.Air },
},
{
{
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
},
{ Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock },
{ Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock },
{ Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand },
{ Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand },
{ Enum.Material.Mud, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Mud },
{ Enum.Material.Air, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Air },
},
{
{
Enum.Material.CrackedLava,
Enum.Material.Sand,
Enum.Material.Sand,
Enum.Material.Sand,
Enum.Material.CrackedLava,
},
{ Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock },
{ Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock },
{ Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand },
{ Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand },
{ Enum.Material.Mud, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Mud },
{ Enum.Material.Air, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Snow, Enum.Material.Air },
},
{
{
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
Enum.Material.CrackedLava,
},
{ Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock },
{ Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock, Enum.Material.Rock },
{ Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand },
{ Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand, Enum.Material.Sand },
{ Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud, Enum.Material.Mud },
{ Enum.Material.Air, Enum.Material.Air, Enum.Material.Air, Enum.Material.Air, Enum.Material.Air },
},
}
local occupancies = {
{
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 0.5, 0.5, 0.5, 0.5, 0.5 },
{ 0, 0, 0, 0, 0 },
},
{
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 0.5, 1, 1, 1, 0.5 },
{ 0, 1, 1, 1, 0 },
},
{
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 0.5, 1, 1, 1, 0.5 },
{ 0, 1, 1, 1, 0 },
},
{
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1 },
{ 0.5, 0.5, 0.5, 0.5, 0.5 },
{ 0, 0, 0, 0, 0 },
},
}
terrain:WriteVoxels(region, resolution, materials, occupancies)
Maximum Region Size

local REGION_START = Vector3.new(-20, -20, -20)
local REGION_END = Vector3.new(20, 20, 20)
local CFRAME = CFrame.new(0, 20, 0)
local SIZE = 50
local function getRegionVolumeVoxels(region)
local resolution = 4
local size = region.Size
return (size.x / resolution) * (size.y / resolution) * (size.z / resolution)
end
local function isRegionTooLargeForReadWriteVoxels(region)
return getRegionVolumeVoxels(region) > 4194304
end
local function isRegionTooLarge(region)
return getRegionVolumeVoxels(region) > 67108864
end
-- Helper function to get an axis-aligned Region3 from the given cframe and size
local function getAABBRegion(cframe, size)
local inv = cframe:Inverse()
local x = size * inv.RightVector
local y = size * inv.UpVector
local z = size * inv.LookVector
local w = math.abs(x.X) + math.abs(x.Y) + math.abs(x.Z)
local h = math.abs(y.X) + math.abs(y.Y) + math.abs(y.Z)
local d = math.abs(z.X) + math.abs(z.Y) + math.abs(z.Z)
local pos = cframe.Position
local halfSize = Vector3.new(w, h, d) / 2
return Region3.new(pos - halfSize, pos + halfSize):ExpandToGrid(4)
end
-- Specific functions for checking individual methods
local function isRegionTooLargeForFillBall(cframe, radius)
local diameter = radius * 2
return isRegionTooLarge(getAABBRegion(cframe, Vector3.new(diameter, diameter, diameter)))
end
local function isRegionTooLargeForFillBlock(cframe, size)
return isRegionTooLarge(getAABBRegion(cframe, size))
end
local function isRegionTooLargeForFillCylinder(cframe, height, radius)
local diameter = radius * 2
return isRegionTooLarge(getAABBRegion(cframe, Vector3.new(diameter, height, diameter)))
end
local function isRegionTooLargeForFillRegion(region)
return isRegionTooLarge(region)
end
local function isRegionTooLargeForFillWedge(cframe, size)
return isRegionTooLarge(getAABBRegion(cframe, size))
end
local function isRegionTooLargeForReplaceMaterial(region)
return isRegionTooLarge(region)
end
local region = Region3.new(REGION_START, REGION_END)
print(isRegionTooLargeForReadWriteVoxels(region))
print(isRegionTooLargeForFillBall(CFRAME, SIZE))
print(isRegionTooLargeForFillBlock(CFRAME, SIZE))
print(isRegionTooLargeForFillCylinder(CFRAME, SIZE, SIZE))
print(isRegionTooLargeForFillRegion(region))
print(isRegionTooLargeForFillWedge(CFRAME, SIZE))
print(isRegionTooLargeForReplaceMaterial(region))

WriteVoxelsAsync_beta

Parameters

region: Region3
resolution: number
channelIds: Array

Returns

Events