Help with making train tracks follow a path

Hello!

Not too sure what category best fits this and sorry for the lengthly post but,

I’ve got this model thats just a bunch of edges that connect and create a path. I’m using the orange path as a practice go.

I’m wanting to import this into Roblox some how and make have a script that would automatically clone and make train tracks follow this path however I can’t seem to get it.

I’ve tried to each edge into a retangle by doing the following
I selected all edges and extruded downwards, I then applied the solidify modifier, deleted the extruded edges, bottom faces. I then selected all of the faces and then did checker deselect to have every second face, then I seperated the faces by selection which left me with 2 meshes and for both meshes I selected all the faces and extruded downwards again then I selected all faces and seperated by lose parts which left me with alot of rectangle like meshes. ^So this worked out all fine and nothing wrong with the model.

My idea was to have a script that loops through all the meshes and clones a base model of track, then it would set the models pivot point to the meshes position and then set the rail lengths to the same as the mesh and set the models rotation to be the same as the mesh. The problem with this is that when importing the meshes to Roblox the orientation imports as 0, 0, 0 which is wrong because alot of the rectangles are rotated. This is because the blender transformation properties are also 0, 0, 0 and unfortunetly there is no way to fix this and because of this the script would set the orientations of all of the tracks to 0, 0, 0 and the tracks would all face the same way and wouldn’t line up with each other.

I could do all this manually by dragging the track model onto the side the mesh and do this for all the meshes and then have a script that sets the positions and sizes but this would be extremely time consuming.

My other idea I’ve tried is to have the script do a raycast from the left of the mesh that gives me the normal that this raycasts hits and then find out the rotation that way. So far this is the best way but I’m having issues getting it perfect. Here is a placefile:
Track Layer.rbxl (204.4 KB) The script called TrackPlacer inside of ServerScriptService contains the code that places the track and some info.

My last idea is, would another 3D modelling software like autodesk maya or something have a feature that would set the transform properties to what they actually are instead of 0, 0, 0?

All I really need is a way to get the actual orientation of the meshes so the tracks could be laid properly.

Please help me! I’ve been stuck on this for a few days and gotten nowhere. Any help is appreciated!

2 Likes

Hi, again.

As of now there is no real “simple” method of doing this. One method that I can think of is to use editable meshes and see where the vertices of the path land, then using CFrames you can put the path on the specified vertex and rotate in a way that it faces the next vertex. It is recommended that you edit your mesh in such a way that it has uniform amount of vertices all around.

Or just do it in blender like I said in the old topics. There is no “simple” way of doing it. It’s all pretty hard.

2 Likes

Hi, I’ve created this new topic in hope that I’ve worded everything better and that it might be easier to figure out.

I have edited this topic to contain a placefile of the meshes and some code that attempts to place parts where the meshes are. The code is fairly simple but not simple enough for me to understand how to fix the problems in it. I have a message inside of the script that explains the problems with the script.

2 Likes

Alright, I’ll send you what I can do in an hour.

2 Likes

Alright, thanks!

2 Likes

How’d you go on it?

2 Likes

Well you see the task is not as simple as you expect. I have made a basic system to get points on the mesh. I wasn’t able to do much because well, duh. I got other stuff to do.

2 Likes

Yeah, no problem! Thanks for trying though.

2 Likes

Alright I was able to do something:

local AS = game:GetService("AssetService")
local mesh = AS:CreateEditableMeshAsync(workspace.MeshPart.MeshContent)
local meshPart = workspace.MeshPart

local offset = meshPart.Position

local facePoints = {}

for _,face in mesh:GetFaces() do
	local vertices = {}
	for _,vertex in mesh:GetFaceVertices(face) do
		table.insert(vertices,mesh:GetPosition(vertex)+offset)
	end
	if (vertices[1]-vertices[2]).Magnitude > 4 or (vertices[2]-vertices[3]).Magnitude > 4 or (vertices[3]-vertices[1]).Magnitude > 4 then
		continue
	end
	local centerPoint = Vector3.new((vertices[1].X+vertices[2].X+vertices[3].X)/3,(vertices[1].Y+vertices[2].Y+vertices[3].Y)/3,(vertices[1].Z+vertices[2].Z+vertices[3].Z)/3)
	table.insert(facePoints,centerPoint)
end

local ends = {}

for x,posA in facePoints do
	for y,posB in facePoints do
		if (posA-posB).Magnitude > 4 or x == y then continue end
		table.insert(ends,Vector3.new((posA.X+posB.X)/2,(posA.Y+posB.Y)/2,(posA.Z+posB.Z)/2))
	end
end

local endA,endB = ends[1],ends[4]

local part = Instance.new("Part")
part.BrickColor = BrickColor.Black()
part.CFrame = CFrame.new(endA:Lerp(endB,0.5),endB)
part.Size = Vector3.new(1,1,(endA-endB).Magnitude)
part.Parent = workspace
part.Anchored = true
meshPart:Destroy()
mesh:Destroy()

(Ugly code I know, I didn’t care to make it look good or optimized because it will run once.)

Basically, this makes a part between the ends of an unrotated mesh. Like so:


Now it’s your job to expand the code and make it work. I wasn’t able to because the mesh is owned by you and I am using editable meshes which gives access only to the person who made the mesh.

Here is the place file as well.
Track Layer.rbxl (202.8 KB)

This entire problem also told me how bad roblox is. They reinvented editable meshes in a much more HARDER way. :sob: It took me an entire day just to understand their new “improvement update”. :sob:

2 Likes

Alright thank you! I’ll see what I can do. Lol you didn’t have to spend an entire day trying to do it for me but thanks anyways much appreciated.

I’ll let you know how I go.

2 Likes

Hi uhh so… when I swapped out your mesh with my mesh it decides it doesn’t want to work.
EndA and EndB print as nil. Would you like me to send you my blender file with the bunch of segments and you just import a random one?

2 Likes

Uhhh, sure. Go ahead. Sorry for late reply.

1 Like

Its alright, I had some things to do that are outside of Roblox.

Here you go, don’t need to import them all for the test but might be good to import a few random ones.
The file is too big so i had to upload it to google drive

1 Like

It turns out that the issue was that your mesh was scaled too much on the Y (or Z blender wise) axis.

Here is the fixed code:

local AS = game:GetService("AssetService")
local mesh = AS:CreateEditableMeshAsync(workspace.MeshPart.MeshContent)
local meshPart = workspace.MeshPart

local offset = meshPart.Position

local function visualizeVertex(point)
	local p = Instance.new("Part")
	p.Parent = workspace
	p.Anchored = true
	p.Size = Vector3.one*0.25
	p.Position = point
	p.Material = Enum.Material.Neon
	p.Color = Color3.new(1,1,1)
	p.Shape = Enum.PartType.Ball
	p.Name = "Vertex"
end

local facePoints = {}

local size = mesh:GetSize()

local dist = math.min(size.X,size.Y,size.Z)+1

for _,face in mesh:GetFaces() do
	local vertices = {}
	for _,vertex in mesh:GetFaceVertices(face) do
		table.insert(vertices,mesh:GetPosition(vertex)+offset)
		visualizeVertex(mesh:GetPosition(vertex)+offset)
	end
	if (vertices[1]-vertices[2]).Magnitude > dist or (vertices[2]-vertices[3]).Magnitude > dist or (vertices[3]-vertices[1]).Magnitude > dist then
		continue
	end
	local centerPoint = Vector3.new((vertices[1].X+vertices[2].X+vertices[3].X)/3,(vertices[1].Y+vertices[2].Y+vertices[3].Y)/3,(vertices[1].Z+vertices[2].Z+vertices[3].Z)/3)
	table.insert(facePoints,centerPoint)
end

local ends = {}

for x,posA in facePoints do
	for y,posB in facePoints do
		if (posA-posB).Magnitude > dist or x == y then continue end
		table.insert(ends,Vector3.new((posA.X+posB.X)/2,(posA.Y+posB.Y)/2,(posA.Z+posB.Z)/2))
	end
end

local endA,endB = ends[1],ends[4]

local part = Instance.new("Part")
part.BrickColor = BrickColor.Black()
part.CFrame = CFrame.new(endA:Lerp(endB,0.5),endB)
part.Size = Vector3.new(1,1,(endA-endB).Magnitude)
part.Parent = workspace
part.Anchored = true
meshPart:Destroy()
mesh:Destroy()
1 Like

Alright, I’ll let you know how it goes

I am already checking it myself so will let you know how it goes.

Okay I made a bunch o mistakes. This is worse than your method…

For some reason it only made 196 parts and left 499. I am looking into it further to see what the issue could be.

Yeah I agree… One of the meshes the parts went like twice its size and then I tested it on one of the meshes that wern’t working with my method.

Maybe for my method it fails on some things because of the way some parts get imported… but idk I’ll see how I can continue with my method while I let you look into your method.

I’m not really good with all this CFrame and math stuff so we’ll see how we go.

Not sure whether you’ve thought of this but if you get an ordinary Roblox part and drag it onto the side of any mesh and look at the rotation of the part I think its correct, not sure how accurate it is though. I’m pretty sure that Roblox uses raycasting from the mouse to the meshes normals so thats kind of how my method works.

roblox doesn’t have any default Spline class unfortunately (unless im a boomer), you can probably find a module or plugin online though

Not sure what I’d use this for?

Anyways, the method I’ve got in the place file I provided does work but it has some issues that I don’t know how to fix so if someone wants to help me fix it or give me an idea how to get the rotation of these meshes then I’d be happy but I’ll see where I go