Hello, I’ve got this code loop throughs a bunch of meshes that uses a raycast that gets the Normal of the purple side (in other words, the front face) from RaycastResult.Normal. This code works fine for most of the meshes but for some meshes the raycast hits the wrong normal (One of the end normals). The reason for this is because the raycast hits from an angle on some meshes.
Raycast visualization
- Red: Is the Origin.
- Yellow: Is where the cast hit
- Blue: Shows the ray.
Image 1 - Purple shows what face is the ‘Front’ face.
Image 2 - Correct.
Image 3 - Correct and shows that the raycast casts from an angle.
Image 4 - Incorrect, hits the wrong face giving me the wrong normal.
FYI:
The meshes are uploaded with their orientations showing as 0, 0, 0 even though they’re not visually.
This code is meant to replace the meshes with ordinary Roblox parts. Setting the parts size, position and orientation to the same as the mesh and since the orientation on the mesh shows as 0, 0, 0, I have to have a raycast hit from a little bit to the side of the mesh and hit the mesh to get the surface normal, so it is important that I get the correct normal for this code to work correctly.
I think if the ray origin was parrarell of the currently looped mesh then I think it would be fine but I’m not sure how to fix it. I’ve been stuck on this for a while now and I’ve tried numerous things so please help if you can!
Here is the code
local function Loop(Folder)
for _, Mesh in Folder:GetChildren() do
if Mesh:IsA("MeshPart") then
local Segment = Instance.new("Part")
Segment.BrickColor = BrickColor.Black()
Segment.Size = Vector3.new(Mesh.Size.X, 2, 2)
Segment.Anchored = true
Segment.Name = Mesh.Name
local offset = 10
local localOrigin = Vector3.new(-Mesh.Size.X / 2 - offset, 0, 0)
local RayOrigin = Mesh.CFrame:PointToWorldSpace(localOrigin)
local RayDirection = Mesh.CFrame.RightVector * (Mesh.Size.X + 2 * offset)
local RaycastParams = RaycastParams.new()
RaycastParams.FilterDescendantsInstances = {Mesh}
RaycastParams.FilterType = Enum.RaycastFilterType.Include
local Result = workspace:Raycast(RayOrigin, RayDirection, RaycastParams)
if Result then
local Up = Result.Normal
if math.abs(Up.Magnitude - 1) > 0.01 then
warn("Invalid normal vector for mesh:", Mesh.Name)
end
local ArbitraryVector = Vector3.new(0, 1, 0)
if math.abs(Up:Dot(ArbitraryVector)) > 0.9 then
ArbitraryVector = Vector3.new(0, 0, 1)
end
local Right = Up:Cross(ArbitraryVector).Unit
if Right.Magnitude == 0 then
Right = Vector3.new(1, 0, 0)
end
local Forward = Right:Cross(Up).Unit
local RotationCFrame = CFrame.fromMatrix(Result.Position, Right, Up, Forward)
local CorrectionCFrame = CFrame.Angles(math.rad(90), 0, 0)
Segment.CFrame = RotationCFrame * CorrectionCFrame
Segment.Position = Mesh.Position
else
warn("Raycast did not hit the mesh for child:", Mesh.Name)
end
if not Result then
Segment.Parent = workspace.Converted.NoResult
else
Segment.Parent = workspace.Converted
end
do
local Model = Instance.new("Model", workspace.Debug)
Model.Name = Mesh.Name
local debugRayOrigin = Instance.new("Part")
debugRayOrigin.Size = Vector3.new(1, 1, 1)
debugRayOrigin.Position = RayOrigin
debugRayOrigin.Anchored = true
debugRayOrigin.BrickColor = BrickColor.Red()
debugRayOrigin.Name = "Origin"
debugRayOrigin.Shape = Enum.PartType.Ball
debugRayOrigin.Material = Enum.Material.Neon
debugRayOrigin.Parent = Model
if Result then
local debugRayHit = Instance.fromExisting(debugRayOrigin)
debugRayHit.Position = Result.Position
debugRayHit.BrickColor = BrickColor.Yellow()
debugRayHit.Name = "Hit"
debugRayHit.Parent = Model
local debugRayVisual = Instance.new("Part")
debugRayVisual.Size = Vector3.new(0.5, 0.5, Result.Distance)
debugRayVisual.CFrame = CFrame.new(RayOrigin + (Result.Position - RayOrigin) / 2, Result.Position)
debugRayVisual.Anchored = true
debugRayVisual.BrickColor = BrickColor.Blue()
debugRayVisual.Name = "Visual"
debugRayVisual.Parent = Model
end
end
task.wait(0.01)
end
end
end
Loop(workspace.Meshes)
print("FINISHED")