How do I fix this incorrect raycast origin and direction?

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")

Hi, does anybody have a solution?
I still haven’t been able to solve this.

Hi, does anybody have any ideas on how I can get a proper raycast? Its been a day and I’ve still been unable to achieve anything.

I’ve tried to look into editable meshes but that doesn’t seem to help me much either

You could maybe add parts as the hitboxes to the mesh part.

What do you mean by that?

I want it to raycast at hit the front side of the mesh. I’m pretty sure the problem is just the origin I basically want the origin to be forward of the front face of the mesh instead of on an angle from the mesh.

I was suggested to use the cross product method but I’m clueless on how it works.

the origin is the start position of the ray , the direction is the direction of the ray

I know this, the problem is, I can’t get the origin to start forward of that front face and I can’t se that origin to mesh.Position + Vector3.new(5, 0, 0) because for different meshes the rotation is different, therefore shooting from an angle and hitting the wrong surface normal. Using the look vector method doesn’t work either because the orientation properties show as 0, 0, 0 when it appears differently.

1 Like

Hi, does anybody have any other suggestions?

I was suggested by somebody to use the cross product but I’m not sure how and I’m not sure if it would work properly since the orientation is 0, 0, 0.

I’ve also considered using 2 raycast,
First one would do what it currently does, and the second would set the origin from the newly spawned part but I’m not sure how’d get an appropiate origin since some parts are rotated differently and look vector wouldn’t work correctly.

Hi, I found a better way to do what I wanted.

Heres the solution.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.