Terrain
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
Enables or disables terrain decoration.
MaterialColors represents the editor for the Material Color feature, and cannot be edited by scripts.
To get the color of a material, use: Terrain:GetMaterialColor() To set the color of a material, use: Terrain:SetMaterialColor()
Displays the boundaries of the largest possible editable region.
The tint of the Terrain water.
Controls how opaque the Terrain's water reflections are.
The transparency of the Terrain water.
Sets the maximum height of the Terrain water waves in studs.
Sets how many times the Terrain water waves will move up and down per minute.
Methods
Returns the world position of the center of the terrain cell (x, y, z).
Returns the position of the lower-left-forward corner of the grid cell (x, y, z).
Stores a chunk of terrain into a TerrainRegion object so it can be loaded back later. Note: TerrainRegion data does not replicate between server and client.
Fills a ball of smooth terrain in a given space.
Fills a block of smooth terrain with a given location, rotation, size, and material.
Fills a cylinder of smooth terrain in a given space.
Fills a Region3 space with smooth terrain.
Fills a wedge-shaped volume of Terrain with the given Material and the area's CFrame and Size.
Returns current terrain material color for specified terrain material.
Applies a chunk of terrain to the Terrain object. Note: TerrainRegion data does not replicate between server and client.
Returns a certain region of smooth terrain in table format.
Replaces the terrain of a material within a region with another material.
Sets current terrain material color for specified terrain material.
Returns the grid cell location that contains the point position.
Returns the grid cell location that contains the point position, preferring empty grid cells when position is on a grid edge.
Returns the grid cell location that contains the point position, preferring non-empty grid cells when position is on a grid edge.
Sets a certain region of smooth terrain using table format.
Events
Properties
Decoration
Currently enables or disables geometric grass on the Grass terrain material, although future modifications of this property may control additional decorative features.
MaterialColors
MaterialColors represents the editor for the Material Color feature, and cannot be edited by scripts.
To get the color of a material, use: Terrain:GetMaterialColor()
To set the color of a material, use: Terrain:SetMaterialColor()
MaxExtents
Displays the boundaries of the largest possible editable region.
WaterColor
The tint of the Terrain water.
WaterReflectance
Controls how opaque the Terrain's water reflections are.
WaterTransparency
The transparency of the Terrain water.
WaterWaveSize
Sets the maximum height of the Terrain water waves in studs. This is currently constrained to between 0 and 1.
WaterWaveSpeed
Sets how many times the Terrain water waves will move up and down per minute. This is currently constrained to between 0 and 100.
Methods
Clear
Clears the terrain.
Returns
CopyRegion
Stores a chunk of terrain into a TerrainRegion object so it can be loaded back later. Note: TerrainRegion data does not replicate between server and client.
Returns
Code Samples
local terrainRegion = workspace.Terrain:CopyRegion(workspace.Terrain.MaxExtents)
workspace.Terrain:Clear()
task.wait(5)
workspace.Terrain:PasteRegion(terrainRegion, workspace.Terrain.MaxExtents.Min, true)
FillBall
Fills a ball of smooth terrain in a given space.
Returns
Code Samples
workspace.Terrain:FillBall(Vector3.new(0, 0, 0), 5, Enum.Material.Grass)
FillBlock
Fills a block of smooth terrain with a given location, rotation, size, and material.
Returns
FillCylinder
Fills a cylinder of smooth terrain in a given space. The space is defined using a CFrame, height, and radius.
Usage
workspace.Terrain:FillCylinder(CFrame.new(0, 50, 0), 5, 30, Enum.Material.Asphalt)
Returns
FillWedge
FillWedge fills a wedge-shaped volume of Terrain with the given Material and the area's CFrame and Size. The orientation of the wedge is the same as an equivalent WedgePart.

In the image above, a floating chunk of Terrain was created by calling this function as in the following code. A transparent, pink part with the Front surface marked with a Motor indicates the provided CFrame and Size.
workspace.Terrain:FillWedge(CFrame.new(0, 50, 0), Vector3.new(20, 20, 20), Enum.Material.Asphalt)
Returns
GetMaterialColor
Returns the current terrain material color for the specified terrain material.
Returns
PasteRegion
Applies a chunk of terrain to the Terrain object. Note: TerrainRegion data does not replicate between server and client.
Returns
Code Samples
local terrainRegion = workspace.Terrain:CopyRegion(workspace.Terrain.MaxExtents)
workspace.Terrain:Clear()
task.wait(5)
local corner = Vector3int16.new(-32000, -32000, -32000)
local pasteEmptyRegion = false
workspace.Terrain:PasteRegion(terrainRegion, corner, pasteEmptyRegion)
ReadVoxels
Returns a certain region of smooth terrain in table format.
Returns
Returns raw voxel data as two 3D arrays.
- materials - 3D array of Material from the target area. Also contains a Size field, equal to the dimensions of the nested arrays.
- occupancies - 3D array of occupancy values from the target area. Also contains a Size field, equal to the dimensions of the nested arrays.
Code Samples
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)
ReplaceMaterial
ReplaceMaterial replaces terrain of a certain Material within a Region3 with another material. Essentially, it is a find-and-replace operation on Terrain materials.
Constraints
When calling this method, the resolution parameter must be exactly 4. Additionally, the Region3 must be aligned to the terrain materials grid, i.e. the components of the Region3's minimum and maximum points must be divisible by 4. Use Region3:ExpandToGrid() to make a region compatible with this function.
Parameters
Returns
Code Samples
local MIN = Vector3.new(-20, -20, -20)
local MAX = Vector3.new(20, 20, 20)
local materialToReplace = Enum.Material.Grass
local replacementMaterial = Enum.Material.Asphalt
workspace.Terrain:ReplaceMaterial(Region3.new(MIN, MAX), 4, materialToReplace, replacementMaterial)
WriteVoxels
Sets a certain region of smooth terrain using table format.
Parameters
Target region to write to. Must be aligned to the voxel grid. Will throw an error if region is too large.
Voxel resolution. Must be 4.
3D array of Enum.Material. Dimensions must exactly match the size of the target region in voxels.
3D array of voxel occupancies (number between 0 and 1). Dimensions must exactly match the size of the target region in voxels.
Returns
Code Samples
local region = Region3.new(Vector3.new(0, 0, 0), Vector3.new(16, 28, 20)):ExpandToGrid(4)
local RESOLUTION = 4
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 },
},
}
workspace.Terrain:WriteVoxels(region, RESOLUTION, materials, occupancies)
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))