Issues with WriteVoxels

I keep running into this annoying error
12:11:41.501 - Bad argument materials[1][1] to ‘WriteVoxels’ (2-element 12:11:41.501 - Script ‘ServerScriptService.Script’, Line 53 - global WriteVoxelsarray expected)

So I’ve tried just, making these generate an array with only 2 “dimensions” and that gave me a slightly different error

12:14:22.108 - Bad argument materials[1][1] to ‘WriteVoxels’ (1-element array expected)

This doesn’t really seem to work how the wiki made it out to work. And I can’t really find any better documentation on how this works. can anyone help?

function GenerateOccupancy(h)
	local A = {}
	A[1] = {}
	local hRound = math.ceil(h/4)
	local hRemainder = 1%(h/4)
	for i = 1,hRound do
		A[1][i] = {}
		if i == hRound then
			A[1][i][1] = hRemainder
		else
			A[1][i][1] = 1
		end
	end
	return A
end

function GenerateMaterial(h,material)
	local A = {}
	A[1] = {}
	local hRound = math.ceil(h/4)
	local hRemainder = 1%(h/4)
	for i = 1,hRound do
		A[1][i] = {}
		if i == hRound then
			A[1][i][1] = Enum.Material.Grass
		else
			A[1][i][1] = Enum.Material.Rock
		end
	end
	return A
end

function WriteVoxels(x,y,height,material)
	print(Unit)
	local Region = Region3.new(Vector3.new(x,0,y),Vector3.new(x + Unit,height,y + Unit))
	Region = Region:ExpandToGrid(4)
	game.Workspace.Terrain:WriteVoxels(Region,4,GenerateMaterial(height,material),GenerateOccupancy(height))
end

What is the value of the Unit variable?

Oh yeah I should probably share that. It’s 4.

from the top of my code:

local MapSize = 2048
local MapResolution = 512
local Unit = MapSize/MapResolution

Right, so the issue here is that you’re not filling the 3D array with default data. You HAVE to fill the entire 3D array, even if the voxels are just air.

I don’t understand what part I’m not filling though?

I had that issue before, it has a different error output.

12:59:24.831 - Bad argument materials to ‘WriteVoxels’ (2x9x2 array expected)

What it’s “expecting” is a 1x9x1 array (in this case) but when I provide it that, I just get these weird errors about 1 or 2 element arrays expected

I’ll explain using an analogy

What you’re doing

local region = Region3.new(
	Vector3.new(0,0,0),
	Vector3.new(12,12,12)
)

local occupancy = {}
occupancy[1] = {}
occupancy[1][1] = {}
occupancy[1][1][1] = 1

local materials = {}
materials[1] = {}
materials[1][1] = {}
materials[1][1][1] = Enum.Material.Grass

-- "What about the rest of the voxel data? Don't we need to fill that with default values?"
-- "Who cares, what could go wrong? :D!"
workspace.Terrain:WriteVoxels(region,4,materials,occupancy) -- *BOOM*

Note how these arrays look in the Lua debugger.
They’re very empty, and its supposed to be a 3D 3x3x3 array!

What you should actually do

local region = Region3.new(
	Vector3.new(0,0,0),
	Vector3.new(12,12,12)
)

local materials,occupancy = workspace.Terrain:ReadVoxels(region,4)
materials[1][1][1] = Enum.Material.Grass
occupancy[1][1][1] = 1

workspace.Terrain:WriteVoxels(region,4,materials,occupancy)

Notice how the arrays look when populated with full data.
You weren’t filling in every single voxel of the 3D array.

Ok that’s great and all but I’m not making the connection between that and what I’m doing. I’m literally generating one single Y column of terrain. The region is 4x36x4. I am giving it an array that is a complete 1x9x1, 3 Dimensional array.Where is the empty space? How do I even view that debugger thing?

Here is my entire code start to end

local MapSize = 2048
local MapResolution = 512
local Unit = MapSize/MapResolution

local Seed = 24.6+math.pi

function MajorHeight(x,y)
	local Resolution = .005
	return (1 + math.noise(x*Resolution, y*Resolution, Seed + 17.6*math.pi))*.5
end

function MinorHeight(x,y)
	local Resolution = .02
	return (1 + math.noise(x*Resolution, y*Resolution, Seed + 17.6*math.pi))*.5
end

function GenerateOccupancy(h)
	local A = {}
	A[1] = {}
	local hRound = math.ceil(h/4)
	local hRemainder = 1%(h/4)
	print(hRound)
	for i = 1,hRound do
		A[1][i] = {}
		if i == hRound then
			A[1][i][1] = hRemainder
		else
			A[1][i][1] = 1
		end
	end
	return A
end

function GenerateMaterial(h,material)
	local A = {}
	A[1] = {}
	local hRound = math.ceil(h/4)
	local hRemainder = 1%(h/4)
	print(hRound)
	for i = 1,hRound do
		A[1][i] = {}
		if i == hRound then
			A[1][i][1] = Enum.Material.Grass
		else
			A[1][i][1] = Enum.Material.Rock
		end
	end
	return A
end

function WriteVoxels(x,y,height,material)
	print(Unit)
	local Region = Region3.new(Vector3.new(x,0,y),Vector3.new(x + Unit,height,y + Unit))
	Region = Region:ExpandToGrid(4)
	game.Workspace.Terrain:WriteVoxels(Region,4,GenerateMaterial(height,material),GenerateOccupancy(height))
end

function generatePoint(x,y)
	local GeneralHeight = MajorHeight(x,y)
	local Height = MinorHeight(x,y)*(100*GeneralHeight) + (GeneralHeight * 100)
	WriteVoxels(x,y,Height,Enum.Material.Grass)
	--game.Workspace.Terrain:FillBlock(CFrame.new(x*Unit,Height/2,y*Unit),Vector3.new(Unit,Height,Unit),Enum.Material.Grass)
end

for x = -MapResolution*.5,MapResolution*.5 do
	for y = -MapResolution*.5,MapResolution*.5 do
		generatePoint(x,y)
	end
end

Where am I actually doing something wrong? I understand your point, but I understood that from the beginning. That does not even appear to be the error I’m receiving.

It appears to create one column just fine

Alright so I figured out what was going on and managed to get it working.

local MapSize = 2048
local MapResolution = 512
local Unit = MapSize/MapResolution

local Seed = 24.6+math.pi

function MajorHeight(x,y)
	local Resolution = .005
	return (1 + math.noise(x*Resolution, y*Resolution, Seed + 17.6*math.pi))*.5
end

function MinorHeight(x,y)
	local Resolution = .02
	return (1 + math.noise(x*Resolution, y*Resolution, Seed + 17.6*math.pi))*.5
end

function GenerateOccupancy(occupancy,h)
	local hRound = math.ceil(h/4)
	local hRemainder = 1%(h/4)
	for i = 1,hRound do
		if i == hRound then
			occupancy[1][i][1] = hRemainder
		else
			occupancy[1][i][1] = 1
		end
	end
	return occupancy
end

function GenerateMaterial(materials,h,material)
	local hRound = math.ceil(h/4)
	local hRemainder = 1%(h/4)
	for i = 1,hRound do
		if i == hRound then
			materials[1][i][1] = Enum.Material.Grass
		else
			materials[1][i][1] = Enum.Material.Rock
		end
	end

	return materials
end

function WriteVoxels(x,y,height,material)
	local Region = Region3.new(Vector3.new(x,0,y),Vector3.new(x + Unit,height,y + Unit))
	Region = Region:ExpandToGrid(4)
	
	local materials,occupancy = workspace.Terrain:ReadVoxels(Region,4)
	materials = GenerateMaterial(materials,height)
	occupancy = GenerateOccupancy(occupancy,height)
	
	workspace.Terrain:WriteVoxels(Region,4,materials,occupancy)
end

function generatePoint(x,y)
	local GeneralHeight = MajorHeight(x,y)
	local Height = MinorHeight(x,y)*(100*GeneralHeight) + (GeneralHeight * 100)
	WriteVoxels(x,y,Height,Enum.Material.Grass)
end

local RunService = game:GetService("RunService")

for x = -MapResolution*.5,MapResolution*.5 do
	for y = -MapResolution*.5,MapResolution*.5 do
		generatePoint(x,y)
	end
	RunService.Heartbeat:Wait()
end

For some reason it was periodically making regions that were 1x9x2 instead of 1x9x1. Its probably some floating point issue. You were also forgetting to write voxels above hRound, but using ReadVoxels as a reference works fine.

4 Likes

Interesting, well this mostly works though there still seems to be some odd float point issues with this

Also odd that the terrain looks TOTALLY different from how it was before using FillBlock. I was only doing this to get rid of the odd terracing effect but this seems to look a lot worse actually

:frowning:

Well you’re filling each voxel’s occupancy up to 1, so you need to find a way to tie the unrounded height level into the occupancy of the top cell.

Edit: NVM.

For the hRemainder, it should be (h/4)%1, not 1%(h/4)

Every time I run this script I get this issue before it even gets to the midway point

13:57:02.755 - ServerScriptService.Script:36: attempt to index field ‘?’ (a nil value)

Which is really odd

Copy and paste this exactly as is:

local MapSize = 2048
local MapResolution = 512
local Unit = MapSize/MapResolution

local Seed = 24.6+math.pi

function MajorHeight(x,y)
	local Resolution = .005
	return (1 + math.noise(x*Resolution, y*Resolution, Seed + 17.6*math.pi))*.5
end

function MinorHeight(x,y)
	local Resolution = .02
	return (1 + math.noise(x*Resolution, y*Resolution, Seed + 17.6*math.pi))*.5
end

function GenerateOccupancy(occupancy,h)
	local hRound = math.ceil(h/4)
	local hRemainder = (h/4)%1
	for i = 1,hRound do
		if i > hRound-2 then
			occupancy[1][i][1] = hRemainder
		else
			occupancy[1][i][1] = 1
		end
	end
	return occupancy
end

function GenerateMaterial(materials,h,material)
	local hRound = math.ceil(h/4)
	local hRemainder = 1%(h/4)
	for i = 1,hRound do
		if i == hRound then
			materials[1][i][1] = Enum.Material.Grass
		else
			materials[1][i][1] = Enum.Material.Rock
		end
	end

	return materials
end

function WriteVoxels(x,y,height,material)
	local Region = Region3.new(Vector3.new(x,0,y),Vector3.new(x + Unit,height,y + Unit))
	Region = Region:ExpandToGrid(4)
	
	local materials,occupancy = workspace.Terrain:ReadVoxels(Region,4)
	materials = GenerateMaterial(materials,height)
	occupancy = GenerateOccupancy(occupancy,height)
	
	workspace.Terrain:WriteVoxels(Region,4,materials,occupancy)
end

function generatePoint(x,y)
	local GeneralHeight = MajorHeight(x,y)
	local Height = MinorHeight(x,y)*(100*GeneralHeight) + (GeneralHeight * 100)
	WriteVoxels(x,y,Height,Enum.Material.Grass)
end

local RunService = game:GetService("RunService")

for x = -MapResolution*.5,MapResolution*.5 do
	for y = -MapResolution*.5,MapResolution*.5 do
		generatePoint(x,y)
	end
	RunService.Heartbeat:Wait()
end

Ooh, ok yeah that works

It seems like it’s like, 1/4 the original area though which is odd as well. Any idea why that seems to have changed?

Yeah it’s in a 512x512 area, and it seems to be modifying some areas over and over and over

I think I found where I did something dumb actually

¯\_(ツ)_/¯

Something seems to be rounding incorrectly.

Figured it out

I wasn’t multiplying the X or Y by 4 for the new writevoxels function

1 Like

What the heck that magical error is appearing again

14:17:20.980 - ServerScriptService.Script:38: attempt to index field ‘?’ (a nil value)

Only if I set it to generate to certain heights. Must be a float point error??

This is so whack I’m gonna freak out, the float point errors are making it EXTREMELY difficult to do this

If you’re trying to stylize the code to look like yours, make sure you’re actually doing it right.

It’s all good I found the issue. sometimes readvoxels was returning one less spot in the ray than the math.ceiling was estimating so I just checked if it exists before attempting to write to it

The terrain generation is going nice now

2 Likes