I didn’t mean replace the entirety of @Vexture 's code with just my FillWedge.
I meant replace only the terrain:FillWedge part of his code with my custom function + use exact wedge size rather than adding on 15. That way you get the accurate fillwedge + the painting (based on steepness) that you desire. This is the code if I add in those edits:
local rate = .00001
local RunService = game:GetService("RunService")
local whitelist = workspace.Mesh:GetChildren()
local thickness = nil --No need to set this anymore. Terrain thickness will just depend on each wedgePart.Size.X.
local sea_level = .125 --From 0 to 1, where 0 is the lowest elevation of the map and 1 is the highest (gets denormalized in terms of the relative elevations of your map automatically)
local regionIncrement = 1024
function FillWedge(wedgeCFrame, wedgeSize, material) --Custom FillWedge with advantages over terrain:FillWedge
local terrain = workspace.Terrain
local Zlen, Ylen = wedgeSize.Z, wedgeSize.Y
local longerSide, shorterSide, isZlonger
if Zlen > Ylen then
longerSide, shorterSide, isZlonger = Zlen, Ylen, true
else
longerSide, shorterSide, isZlonger = Ylen, Zlen, false
end
local closestIntDivisor = math.max(1, math.floor(shorterSide/3))
local closestQuotient = shorterSide/closestIntDivisor
local scaledLength = closestQuotient*longerSide/shorterSide
local cornerPos = Vector3.new(0, -Ylen, Zlen)/2
for i = 1, closestIntDivisor - 1 do
local longest_baselen = (closestIntDivisor-i)*scaledLength
local size, cf = Vector3.new(math.max(3, wedgeSize.X), closestQuotient, longest_baselen)
if isZlonger then
cf = wedgeCFrame:toWorldSpace(CFrame.new(cornerPos) + Vector3.new(0, (i-0.5)*closestQuotient, -longest_baselen/2))
else
cf = wedgeCFrame:toWorldSpace(CFrame.Angles(math.pi/2, 0, 0) + cornerPos + Vector3.new(0, longest_baselen/2, -(i-0.5)*closestQuotient))
end
terrain:FillBlock(cf, size, material)
end
local diagSize = Vector3.new(math.max(3, wedgeSize.X), closestQuotient*scaledLength/math.sqrt(closestQuotient^2 + scaledLength^2), math.sqrt(Zlen^2 + Ylen^2)) --Vector3.new(3, 3, math.sqrt(Zlen^2 + Ylen^2))
local rv, bv = wedgeCFrame.RightVector, -(Zlen*wedgeCFrame.LookVector - Ylen*wedgeCFrame.UpVector).Unit
local uv = bv:Cross(rv).Unit
local diagPos = wedgeCFrame.p - uv*diagSize.Y/2
local diagCf = CFrame.fromMatrix(diagPos, rv, uv, bv)
terrain:FillBlock(diagCf, diagSize, material)
end
local function TimerWait(duration) --Framerate-dependent function that waits at the theoretically lowest possible step, Heartbeat:Wait(), until some time dt has passed.
local start = tick()
repeat RunService.Heartbeat:Wait() until tick() - start >= duration
return true
end
local function MinMaxAvg(data)
local min, max, average = data[1], data[#data], 0
for _, element in pairs(data) do
if element < min then
min = element
end
if element > max then
max = element
end
average = average + element
end
average = average / #data
return min, max, average
end
local function Normalize(Min, Max, Val)
local Normal = (Val - Min) / (Max - Min)
return Normal
end
local function Denormalize(Min, Max, Val)
local Denormal = (Val * (Max - Min)) + Min
return Denormal
end
local function ConvertTerrain()
local data = {}
for _, part in pairs(whitelist) do
table.insert(data, part.Position.Y)
if tick() % 5 > 4.5 then
TimerWait(rate)
end
end
local min, max, average = MinMaxAvg(data)
local orient, extents = workspace.Mesh:GetBoundingBox()
local MeshCenter, MeshSize = workspace.Mesh:GetModelCFrame().p, workspace.Mesh:GetExtentsSize()
local LowerBound = Vector3.new(MeshCenter.X - MeshSize.X/2, MeshCenter.Y - MeshSize.Y/2, MeshCenter.Z - MeshSize.Z/2)
local UpperBound = Vector3.new(MeshCenter.X + MeshSize.X/2, Denormalize(min, max, sea_level), MeshCenter.Z + MeshSize.Z/2)
--Enable for automatic sea level filling based on sea_level variable (VERY slow, recommend using Roblox's sea level method instead. My version of this functionality isn't complete.
--(It's also not guaranteed that it will be aligned with your map. That's why it's off by default.)
--[[for x = LowerBound.X, UpperBound.X, regionIncrement do
for z = LowerBound.Z, UpperBound.Z, regionIncrement do
if tick() % 5 > 4.5 then
TimerWait(rate)
end
for y = LowerBound.Y, UpperBound.Y, regionIncrement do
local Lower = Vector3.new(x, y, z)
local Upper = Vector3.new(x + regionIncrement, y + regionIncrement, z + regionIncrement)
local WaterRegion = Region3.new(Lower, Upper)
workspace.Terrain:FillRegion(WaterRegion:ExpandToGrid(4), 4, Enum.Material.Water)
end
end
end]]--
for _, part in pairs(whitelist) do
local Material
local cf, pos, size = part.CFrame, part.CFrame.p, part.Size --Vector3.new(thickness, part.Size.Y, part.Size.Z)
if math.abs(90 - math.abs(part.Orientation.Z)) > 35 or math.abs(0 - math.abs(part.Orientation.X)) > 35 then
Material = Enum.Material.Rock
else
Material = Enum.Material.Grass
end
if pos.Y < Denormalize(min, max, sea_level) + 6 then
if Material == Enum.Material.Grass then
Material = Enum.Material.Sand
elseif Material == Enum.Material.Rock then
Material = Enum.Material.Slate
end
end
FillWedge(cf, size, Material)
part:Destroy()
if tick() % 5 > 4.5 then
TimerWait(rate)
end
end
end
ConvertTerrain()
Here’s also a repo comparing the above edits to before:
(Disable new script and enable old script to compare the difference)
(You’ll notice the old version has a lot of holes & artefacts and appears a lot more jagged-looking. The new version is a lot smoother and preferable visually.)
CustomFillWedgeDemo.rbxl (340.8 KB)