Hello good. I’m using Editable Mesh to make a road system that I had already designed months ago, but due to the changes in the API it was obsolete for a long time, I already solved it, and I saw that my curves were very bad, until I discovered the stock of the Bezier curves, then, since I don’t know how to calculate them or how to put them into practice, I took help from ChatGPT 4; And, I have a very good result, but not the best, I was wondering if anyone knows how to solve this or how I can improve.
Code:
local AssetService = game:GetService("AssetService")
function bezier(t, P0, P1, P2, P3)
local u = 1 - t
local tt = t * t
local uu = u * u
local p = (uu * u * P0) + (3 * uu * t * P1) + (3 * u * tt * P2) + (tt * t * P3)
return p
end
function rotateUV(uv, rotationAngle, scale)
local radians = math.rad(rotationAngle)
local cosAngle = math.cos(radians)
local sinAngle = math.sin(radians)
local scaledX = uv.X * scale.X
local scaledY = uv.Y * scale.Y
local rotatedX = cosAngle * (scaledX - 0.5) - sinAngle * (scaledY - 0.5) + 0.5
local rotatedY = sinAngle * (scaledX - 0.5) + cosAngle * (scaledY - 0.5) + 0.5
return Vector2.new(rotatedX, rotatedY)
end
function generateBezierRoadWithUV(partA:BasePart, partB:BasePart, segments, textureRotation, scale)
local editableMesh = AssetService:CreateEditableMesh({FixedSize = false})
local P0 = partA.Position
local P3 = partB.Position
local directionA = partA.CFrame.LookVector
local directionB = partB.CFrame.LookVector
local distance = (P3 - P0).Magnitude
local controlOffset = distance * 1
local P1 = P0 + directionA * controlOffset + partA.CFrame.UpVector * (partA.Size.Y / 2)
local P2 = P3 - directionB * controlOffset + partB.CFrame.UpVector * (partB.Size.Y / 2)
local roadWidth = partA.Size.X
local points = {}
for i = 0, segments do
local t = i / segments
table.insert(points, bezier(t, P0, P1, P2, P3))
end
local triangles = {}
local uvs = {}
for i = 1, segments do
local p1 = points[i]
local p2 = points[i + 1]
local direction = (p2 - p1).Unit
local perpendicular = Vector3.new(-direction.Z, 0, direction.X)
local right1 = p1 + perpendicular * (roadWidth / 2)
local left1 = p1 - perpendicular * (roadWidth / 2)
local right2 = p2 + perpendicular * (roadWidth / 2)
local left2 = p2 - perpendicular * (roadWidth / 2)
table.insert(triangles, {left1, right1, left2})
table.insert(triangles, {right1, right2, left2})
local u1 = (i - 1) / segments
local u2 = i / segments
local uv1 = Vector2.new(0, u1)
local uv2 = Vector2.new(1, u1)
local uv3 = Vector2.new(0, u2)
local uv4 = Vector2.new(1, u2)
uv1 = rotateUV(uv1, textureRotation, scale)
uv2 = rotateUV(uv2, textureRotation, scale)
uv3 = rotateUV(uv3, textureRotation, scale)
uv4 = rotateUV(uv4, textureRotation, scale)
table.insert(uvs, {uv1, uv2, uv3})
table.insert(uvs, {uv2, uv4, uv3})
end
for faceIndex, tri in pairs(triangles) do
local v1 = editableMesh:AddVertex(tri[1])
local v2 = editableMesh:AddVertex(tri[2])
local v3 = editableMesh:AddVertex(tri[3])
local faceId = editableMesh:AddTriangle(v1, v2, v3)
local facesUvs = editableMesh:GetFaceUVs(faceId)
local uv = uvs[faceIndex]
for i, uvPoint in pairs(uv) do
editableMesh:SetUV(facesUvs[i], uvPoint)
end
end
return editableMesh
end
local Hijos = workspace:WaitForChild("Folder"):GetChildren()--Nodes
local segments = 50
local textureRotation = 90
local textureScale = Vector2.new(1, 10)
repeat
Hijos = workspace:WaitForChild("Folder"):GetChildren()
task.wait(0)
until #Hijos >= 3
for i, Parts in pairs(Hijos) do
local PartA = Parts
local PartB = Hijos[i + 1]
if PartB then
local editableMesh = generateBezierRoadWithUV(PartA, PartB, segments, textureRotation, textureScale)
local finalMesh = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
finalMesh.PivotOffset = CFrame.new(finalMesh.CenterOfMass)
finalMesh.Anchored = true
finalMesh.Parent = workspace
local Road = game.ReplicatedStorage.SurfaceAppearance:Clone()
Road.Parent = finalMesh
PartA.Transparency = 1
PartB.Transparency = 1
end
end
Results: