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:
- Two brand new APIs to modify Voxel data
- Tooling updates to better edit water, especially at the shores
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: 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 aVoxelChannel
Dictionary
- Read:
-
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’sSolidMaterial
. It has a value between 0 (meaning empty) and 1 (meaning full). -
“SolidMaterial”
: The material of the voxel other than water. Its type isEnum.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:
-
Enum.Material.Water
is not supported inSolidMaterial
. A voxel that only contains water and no solid material will have aSolidMaterial
value ofEnum.Material.Air
and aLiquidOccupancy
value> 0
. At the shores, the Material would be the shore’s solid material (for exampleEnum.Material.Sand, Enum.Material.Rock
, etc.). -
If
“SolidOccupancy” = 1
, but the“SolidMaterial”
is different thanEnum.Material.Air
, thenLiquidOccupancy
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!