How to filter this weird shape on terrain?

The way my terrain generator works is use perlin noise and make a matrix with the results. Depending on the distance, however, some weird shapes are created, such as this one:

I am not sure as to what I could do to filter something like this. Ideally I wanna fill such a space. All help is appreciated!

1 Like

Not knowing the code, its really hard to say, but just from looking I would think however you are ‘filling’ the terrain, you might be off a little on a float value or something to where it is not filling the terrain all the way to the bottom, or, where it looks like two columns lower and upper terrain collide, the half filled lower terrain is making a gap before the filling of the upper terrain.

The only way I could think to filter from this pic, is to read the entire column, and once you hit ‘solid’ reading down the column, make sure all voxels that are non empty (occumancy 0) are full, (100% occupancy), as you go down, stopping only when you hit an empty and resetting the conditions

A possible solution would be to average the value of the noise surrounding it.

That also has the potential to create even more situations like this.

The way this works is it gets a matrix (multiple tables of different Y values for every X) and just does Terrain:FillBlock() for each coordinate. I believe this may be terrain just trying to fill empty space caused by Perlin’s noise? Ideally, I would have some criteria to “fill” the parts of the matrix that cause this. Idk what criteria that could be though, and that’s where I am lost.

From the image it looks like you have a ‘start’ and ‘stop’ to each column, where you put grass at the start and fill with rock the rest of the way.

So how are you getting multiple ‘start’ (grass) area’s if you just have a matrix of X locations with Y values, or does each X have multiple Y locations where a column starts?

No, no!
Instead, what I am doing is checking for each element in the matrix, if the two coordinates above them on the Y axis are empty! If they are, I’ll generate grass, instead.

oh ok, so its a matrix where each X has a table of Y cells, where you can determine ‘spaces’ and add grass. I was thinking of it as a linear table with X elements and each element was a Y height value.

So, it looks like it is putting grass, thinking the area has 2 empty cells above it. (is the grass filling one of those empty cells?) Make sure the grass is overwriting the top layer of rock when you check for two empty above.

My problem is not really the grass. Instead, it is forming those squares. I dont know the circunstances in which similar stuff happens. The grass has nothing to do with it, it is mainly decoration.

I suppose I don’t really understand what is the ‘problem’ with the squares, considering you allow for spaces between layers (having some terrain above and below an empty area)
So is the problem that the empty space (the squares) is just so small, that you would like to fill it in? Or is it that yo don’t want empty space created if its not accessible (its an island of empty space) , or is it just the ‘sharpness’ of the squares? would you like them to be more ‘smooth’ of an opening?
I suppose I am just trying to understand how the squares are wrong, or why they shouldn’t be there. What would you like the correct result to look like in that situation?

I think that when you look to the image, you can see the squares look wrong. I would just like to know how to fill them in or kind of “disable” their existance. Not only is the space small, but it looks really weird. That is what I am trying to prevent.

Well some thoughts on this… First, if you are checking for 2 spaces of air above ground, when putting grass, this should not happen as there is only one space of air above those grass patches, (making the space look really small and ‘square’) I feel like the grass is overwriting one of the spaces of air, making it have only 1 space of air, or, the code where you check for 2 spaces of air is incorrect and its only checking for 1 space of air.

Another thing to think about, is since you have a matrix of values, you can parse it for small spaces (<= 2 spaces of air high) and see if those spaces have air or solid in the columns left and right. If its solid left and right, just fill in those cells. I would do this sort of check before I check for grass.

Also you could keep a variable that keeps a range of ‘open’ space start and end of each column, and if you find space in the next column and it doesn’t match up with any previous space start and end of the column before, then you know its an island of air most likely and you can fill it.

oh, and by using fill block you are not going to get any smooth edges.
https://developer.roblox.com/en-us/api-reference/function/Terrain/WriteVoxels
allows you to give occupancy to voxels so they are not so sharp looking.
I would apply it on any cell you write that has space above or below it.

In the same fashion, these unions are not supposed to be formed. I suspect the terrain tries to fill some empty gaps or has some sort of extra area.

The problem with this is I dont know what condition makes the filling. Any places I could find that?

This sounds like a really solid option. I will give it a shot and return some feedback!

Would it be something like this?

function map.Fill(list)
	local copy = list
	local Filtered = false
	for x, yey in pairs(copy) do
		local zeros = 0
		for y, val in pairs(yey) do
			if val == 0 then
				zeros += 1
			else
				if zeros > 0 and zeros <= 4 then
					Filtered = true
					for i=1, zeros do
						copy[x][y - i] = 1
					end
					zeros = 0
				end
			end
		end	
	end
	for y=1, #copy[1] do
		local zeros = 0
		for x, val in pairs(copy) do
			if val == 0 then
				zeros += 1
			else
				if zeros > 0 and zeros <= 4 then
					Filtered = true
					for i=1, zeros do
						copy[x-i][y] = 1
					end
					zeros = 0
				end
			end
		end
	end
	return copy, Filtered
end

This is still not working.
Should I read diagonals as well?

After some trial and error, I have come to this:

function map.Fill(list)
	local copy = list
	local threshold = 4
	local filtered = false
	local coollist = {{2,2}, {-2,2}, {2,-2}, {-2,-2}, {-2,0}, {2,0}, {0,-2}, {0,2}}
	for x, yey in pairs(copy) do
		for y, val in pairs(yey) do
			if val == 1 then
				for _, combo in pairs(coollist) do
					local v = 1
					local suc, er = pcall(function()
						v = copy[x+combo[1]][y+combo[2]]
						if v == nil then
							error("rip")
						end
					end)
					if v == 1 and suc then
						filtered = true
						copy[x+combo[1]][y+combo[2]] = 1
						copy[x + combo[1]/math.max(1, math.abs(combo[1]))][y + combo[2]/math.max(1, math.abs(combo[2]))] = 1
					end
				end
			end
		end
	end
	return copy, filtered
end

It seems to be filtering this problem just fine. Thanks for the support.

Sorry I was not able to help more, but the day became busy. Glad you found a solution, though.

1 Like