New Voxel API to work with Shorelines

Hi Developers!

We have been hard at work solidifying our Shorelines technology and setting up the foundations that will allow us to make voxels more customizable in the future. To address your feedback on Shorelines, we are releasing some exciting updates today:

  1. Two brand new APIs to modify Voxel data
  2. Tooling updates to better edit water, especially at the shores :ocean:

We’ll detail those below and you can also find all information in our Creator Documentation. Please let us know if you encounter any issues.


API Update

As some of you may know, we encode data in voxels using a structure we call “Voxel Channels”. Channels hold information such as the voxel’s material and the occupancy of that material.

When we first released Shorelines, the old APIs didn’t allow you to query the channel in which water was encoded. So today we’re releasing two new API calls allowing you to read and write to any available channels:

ReadVoxelChannels(
	Region: Region3, 
	Resolution: number, 
	Channels: { VoxelChannel }) -> ReadVoxelChannelData

WriteVoxelChannels(
	Region: Region3, 
	Resolution: number, 
	Channels: WriteVoxelChannelData)

Another cool thing about these methods is that they are extendable to any number of channels. So in the future, if we add support for new channels, you’ll be able to access them with the new ReadVoxelChannels and WriteVoxelChannels methods too.

New Types

As part as this API update, we’ve included four new types to facilitate working with Voxel channels:

type VoxelChannel = “SolidOccupancy” | “SolidMaterial” | “LiquidOccupancy”

type VoxelChannelData<T> = { { { T } } } 

type ReadVoxelChannelData = {
    SolidOccupancy: VoxelChannelData<number>?,
    SolidMaterial: VoxelChannelData<Enum.Material>?,
    Size: Vector3,
    LiquidOccupancy: VoxelChannelData<number>?,
}

type WriteVoxelChannelData = {
    SolidOccupancy: VoxelChannelData<number>?,
    SolidMaterial: VoxelChannelData<Enum.Material>?,
    LiquidOccupancy: VoxelChannelData<number>?,
} 

Inputs

Both methods have the following inputs:

Region:
The bounding box from which voxel data is read or to which its written. This Region will always be automatically adjusted to fit the voxel grid.

  • Type: Region3
  • Minimum: Each region must have a minimum size of 4 x 4 x 4.
  • Maximum: The maximum size of the volume is capped at 2 ^ 22 (~4 million voxels). As an example, regions of size 1024 x 256 x 1024 and 2048 x 64 x 2048 are both valid, because they stay within the volume limit.

Resolution:
The resolution of the voxel grid.

  • Type: Number,
  • Valid values: :warning: At the moment, we only support a value of 4 and other values will throw an error.

Channels:
This field is the highlight of these new APIs. It allows you to directly access the different data elements (we call them channels) that can be encoded in a voxel and read or write its value.

  • Type:

    • Read: VoxelChannel List,
    • Write: WriteVoxelChannelData which is a VoxelChannel Dictionary
  • VoxelChannel usage: You can provide the channels you want to read as an array of VoxelChannel (which are strings) to be able to query multiple channels at once. At the moment we only support 3 channels:

    • “SolidOccupancy”: The occupancy of the voxel’s SolidMaterial. It has a value between 0 (meaning empty) and 1 (meaning full).
    • “SolidMaterial”: The material of the voxel other than water. Its type is Enum.Material.
    • “LiquidOccupancy”:
      Specifies the occupancy of water in a voxel. It is a value between 0 (no water) and 1 (full of water).

VoxelChannelData usage:
You will either receive a ReadVoxelChannelData from using ReadVoxelChannel(...) or have to input a WriteVoxelChannelData when using WriteVoxelChannel(...). Those types are built around the VoxelChannelData type.

VoxelChannelData is a 3D array. It contains the channel data of a voxel at the coordinates (x, y, z) of a terrain region. The size of the 3D array depends on the resolution and size of the region. It’s of size “Region.Size() / Resolution”.

Additional Notes:

  • :warning: Enum.Material.Water is not supported in SolidMaterial. A voxel that only contains water and no solid material will have a SolidMaterial value of Enum.Material.Air and a LiquidOccupancy value > 0. At the shores, the Material would be the shore’s solid material (for example Enum.Material.Sand, Enum.Material.Rock, etc.).
  • :warning: If “SolidOccupancy” = 1, but the “SolidMaterial” is different than Enum.Material.Air, then LiquidOccupancy will be set to 0.

How to use with Water

When writing or reading water specifically, you’ll want to work with the LiquidOccupancy channel. Voxels that only contain water will be assigned the solid material Enum.Material.Air and a LiquidOccupancy > 0. In cases where land and water meet, voxels can have a solid material, a solid material occupancy, and a liquid occupancy all at once for a better visual transition.

Here’s an example of code using those APIs to generate some extremely simplistic Terrain with hills and water.

```lua
local resolution = 4

local function GenerateVoxels(region: Region3)
	-- We will need access to all of the different voxel channels for this operation
local voxelChannels = game.Workspace.Terrain:ReadVoxelChannels(region, resolution, {
		"SolidOccupancy",
		"SolidMaterial",
		"LiquidOccupancy",
	})

	-- Extract the voxel channel information
	local occupancies = voxelChannels.SolidOccupancy
	local materials = voxelChannels.SolidMaterial
	local size = voxelChannels.Size
	local waterOccupancies = voxelChannels.LiquidOccupancy

	for x = 1, size.X, 1 do
		for y = 1, size.Y, 1 do
			-- At higher elevations, decrease the likelihood of materials spawning
			local materialCutoff = -1
			if y > size.Y * 0.25 then
				materialCutoff = -1 + ((y / size.Y) * 2)
			end

			for z = 1, size.Z, 1 do
				-- Generate noise and clamp it to reasonable values
				local noise = math.clamp(
					math.noise(
						(x / size.X) * 2,
						(y / size.Y) * 2,
						(z / size.Z) * 2
					), -1, 1)

				-- Set the material dependent on the noise
				if noise >= materialCutoff then
					occupancies[x][y][z] = 1
					materials[x][y][z] = Enum.Material.Grass
				else
					occupancies[x][y][z] = 0
					materials[x][y][z] = Enum.Material.Air

					if y < size.Y * 0.5 then
						waterOccupancies[x][y][z] = 1
					end
				end
			end
		end
	end

	-- Write the voxel channel data back to the terrain
	game.Workspace.Terrain:WriteVoxelChannels(region, resolution, {
		SolidOccupancy = occupancies,
		SolidMaterial = materials,
		LiquidOccupancy = waterOccupancies,
	})
end

GenerateVoxels(Region3.new(Vector3.new(-128, -32, -128), Vector3.new(128, 32, 128)))

Tooling Update

With this new API available, we were able to improve terrain tooling to better support water and the new shorelines technology. Tools now correctly write to SolidOccupancy and LiquidOccupancy when you’re editing your shores whether you’re editing water or solid materials. This will give you the ability to cleanly edit your shorelines going forward.


Thank you all for your feedback so far and thanks to @RickleSandwich, @ProtoValence, @Neutrinostack, @HyperHumanist, and @IgnisRBX for this awesome effort!

191 Likes

This topic was automatically opened after 10 minutes.

Are we going to be seeing custom terrain materials and proper fluid animations soon, this would open so many possibilities. :slight_smile:

46 Likes

Second the custom terrain materials. It would be a game changer.

19 Likes

Handy changes.

I’ve always wondered what the resolution is.
Is it for performance improvements?
Will it be available in the future?

6 Likes

Great changes! Always thrilled to see any updates to the current roblox terrain

5 Likes

How does the new API help make editing shorelines, including water and solid materials, easier and more precise in terrain tooling?

8 Likes

Oh so Roblox does use type declaration like this manually as well :thinking:

6 Likes

Just curious about performance, do the new apis add any overhead, or on a plus side are they more efficient?

4 Likes

When will we be able to change resolution for terrain?
Why are we limited to 4x4x4, this is far too large to create anything intricate.

7 Likes

We already have custom terrain materials??? Or are you possibly referring to something else?

1 Like

You can only override existing materials, you cannot add more variants.

11 Likes

I am so excited for the fluid updates, man!

The upcoming updates are going to be 100% fire. :firefighter: :fire:

2 Likes

Said this on the last post in regards to Voxel Terrain, so I’ll say it again.

These features are amazing, but tend to render useless for large scale projects. Voxel/Smooth terrain is absolutely cooked in terms of performance. Large scale maps result in absurd memory or frame drops if you choose to “hallow” out terrain.

Any intentions to fix this?

2 Likes

It’s been 4x4x4 since terrain was first added, I also dont think you fully appreciate just how hard decreasing the voxel size to 2x2x2 would be. To store a 500 x 500 x 500 stud area of terrain requires 1953125 voxels in the current 4x4x4 area. To decrease the terrain size to 2x2x2 would increase this to 15625000 voxels. Now while roblox does have LOD at further distance this information still has to be retained for that lod to correctly work.

6 Likes

Glad to see Terrain getting some attention. Hope we get terrain color paint soon :smiling_face_with_tear:

17 Likes

thats remnants from the old voxel terrain system

2 Likes

Whether that’s an issue depends on the actual use of the terrain though. It could be a place setting so that developers who need more detail can use it, while defaults remain at more reasonable values.

1 Like

Yesss so happy to see this change come in! Thank you to all involved making this one happen. I’m always editing terrain and water.

5 Likes

Is anybody else noticing foamy stuff bordering the shorelines in the gif they posted?

17 Likes