Weird CFrame behavior

I made a system that sets plants according to a certain position and gives those plants the orientation of whatever they’re on.

The problem here is that the orientation is behaving kind of weird.

See, normally plants should be set like seen on the image below.

However, sometimes they get weird cframes:

I have no idea of what is happening and I’d appreciate some help, heres the code:

local params = RaycastParams.new()
params.CollisionGroup = “Atmosphere”
params.FilterType = Enum.RaycastFilterType.Include

local function fill(WEDGE, normal)

if normal == Enum.NormalId.Right then
	workspace.Terrain:FillWedge(WEDGE.CFrame - WEDGE.CFrame.RightVector * 1.9, WEDGE.Size + Vector3.new(2,3,3), WEDGE.Material)
	
	
	
	
	WEDGE:SetAttribute("Tangible", true)
	if workspace.CelestialBodies:FindFirstChild("Sphere" .. 1).PlantLifePossible.Value == true then
	for count = 1,2 do
		local plant = game.ReplicatedStorage:FindFirstChild("Planet" .. 1).Plants:GetChildren()[WEDGE:GetAttribute("PlantNumber")]:Clone()
		if plant then
		


		local lookv = WEDGE.CFrame.RightVector


			local vector1 = Vector3.new(WEDGE:GetAttribute("Offset" .. count), WEDGE:GetAttribute("Offset" .. count), WEDGE:GetAttribute("Offset" .. count))
		local vector2 = -vector1

		local vectors = {vector1, vector2}

		local vector = vectors[math.random(1,#vectors)]




			local origin = (WEDGE.Position + vector) - lookv * 1
	



		

		local ray = workspace:Raycast(origin, lookv * 100)
		
		
		if ray then
					if ray.Instance.Name == "Wedge" then
							plant:SetPrimaryPartCFrame(CFrame.new(ray.Position) * CFrame.Angles(math.rad(ray.Instance.Orientation.X),math.rad(ray.Instance.Orientation.Y),math.rad(ray.Instance.Orientation.Z)))
							plant:SetPrimaryPartCFrame(plant.PrimaryPart.CFrame * CFrame.Angles(0,0,math.rad(-90)))

						
					plant.Parent = WEDGE

if plant.Name == “Plant2” then
local tween = ts:Create(plant.Leaves, ti, {CFrame = plant.Leaves.CFrame * angles})
tween:Play()
local tween2 = ts:Create(plant.Stem, ti, {CFrame = plant.Stem.CFrame * angles})
tween2:Play()
else
if plant.Name == “Plant3” or plant.Name == “Plant4” or plant.Name == “Plant5” then

							local tween = ts:Create(plant.Leaves, ti2, {Size = plant.Leaves.Size + Size})
							tween:Play()
							local tween2 = ts:Create(plant.Leaves, ti, {CFrame = plant.Leaves.CFrame * angles})
							tween2:Play()
						else
							local tween = ts:Create(plant.Bigplant, ti, {CFrame = plant.Bigplant.CFrame * angles})
								tween:Play()
								
						end
						end

			end
			end


			
			end	
			end
		end
else
	if normal == Enum.NormalId.Left then
		workspace.Terrain:FillWedge(WEDGE.CFrame - WEDGE.CFrame.RightVector * -1.9, WEDGE.Size + Vector3.new(2,3,3), WEDGE.Material)
		WEDGE:SetAttribute("Tangible", true)
		
		
		end
end

end

local function empty(WEDGE, normal)

if normal == Enum.NormalId.Right then
	workspace.Terrain:FillWedge(WEDGE.CFrame - WEDGE.CFrame.RightVector * 1.9, WEDGE.Size + Vector3.new(2,3,3), Enum.Material.Air)
	WEDGE.Transparency = 0
	WEDGE:SetAttribute("Tangible", false)
	WEDGE:ClearAllChildren()
else
	if normal == Enum.NormalId.Left then
		workspace.Terrain:FillWedge(WEDGE.CFrame - WEDGE.CFrame.RightVector * -1.9, WEDGE.Size + Vector3.new(2,3,3), Enum.Material.Air)
		WEDGE.Transparency = 0
		WEDGE:SetAttribute("Tangible", false)
		WEDGE:ClearAllChildren()
	end
end

end

local function createrayorigins()

local descinstfl = game.Players.LocalPlayer.Character:GetDescendants()
local descinsttl = {}

for i, child in ipairs(descinstfl) do
	if child:IsA("Part") or child:IsA("Union") or child:IsA("MeshPart") then
		table.insert(descinsttl, child)
	end
end

local params = RaycastParams.new()
params.FilterDescendantsInstances = descinsttl
params.FilterType = Enum.RaycastFilterType.Exclude


game:GetService("RunService").RenderStepped:Connect(function()




	for i, pos in ipairs(game.Players.LocalPlayer.Character.RayAtt:GetChildren()) do
		if pos:IsA("Attachment") then
		local direction = (game.Players.LocalPlayer.Character.Head.CFrame.UpVector) * -1

		local ray = workspace:Raycast(pos.WorldCFrame.Position, direction * 5000, params)


		if ray then
				if ray.Instance.Parent.Parent.Name == "Planet" then
					if ray.Instance ~= workspace.Terrain then
					if ray.Instance:GetAttribute("Tangible") == false then
						
						local function GetNormalFromFace(part, normalId)
							return part.CFrame:VectorToWorldSpace(Vector3.FromNormalId(normalId))
						end
						
						local function NormalToFace(normalVector, part)

							local TOLERANCE_VALUE = 1 - 0.001
							local allFaceNormalIds = {
								Enum.NormalId.Front,
								Enum.NormalId.Back,
								Enum.NormalId.Bottom,
								Enum.NormalId.Top,
								Enum.NormalId.Left,
								Enum.NormalId.Right
							}    

							for _, normalId in pairs( allFaceNormalIds ) do
								-- If the two vectors are almost parallel,
								if GetNormalFromFace(part, normalId):Dot(normalVector) > TOLERANCE_VALUE then
									return normalId -- We found it!
								end
							end

							return nil -- None found within tolerance.

						end





						local normalid = NormalToFace(ray.Normal, ray.Instance)

						

							fill(ray.Instance, normalid)
							ray.Instance:SetAttribute("Tangible", true)

						end
						end
				end
		end
		end
		end
	
	for i, pos in ipairs(game.Players.LocalPlayer.Character.RayAttUn:GetChildren()) do
		if pos:IsA("Attachment") then
			local direction = (game.Players.LocalPlayer.Character.Head.CFrame.UpVector) * -1

			local ray = workspace:Raycast(pos.WorldCFrame.Position, direction * 5000, params)


			if ray then
				if ray.Instance.Parent.Parent.Name == "Planet" then
					if ray.Instance:GetAttribute("Tangible") == true then
						local distance = (game.Players.LocalPlayer.Character.PrimaryPart.Position - ray.Instance.Position).Magnitude - 200
						if distance > 150 then
							local function GetNormalFromFace(part, normalId)
								return part.CFrame:VectorToWorldSpace(Vector3.FromNormalId(normalId))
							end

							local function NormalToFace(normalVector, part)

								local TOLERANCE_VALUE = 1 - 0.001
								local allFaceNormalIds = {
									Enum.NormalId.Front,
									Enum.NormalId.Back,
									Enum.NormalId.Bottom,
									Enum.NormalId.Top,
									Enum.NormalId.Left,
									Enum.NormalId.Right
								}    

								for _, normalId in pairs( allFaceNormalIds ) do
									-- If the two vectors are almost parallel,
									if GetNormalFromFace(part, normalId):Dot(normalVector) > TOLERANCE_VALUE then
										return normalId -- We found it!
									end
								end

								return nil -- None found within tolerance.

							end





							local normalid = NormalToFace(ray.Normal, ray.Instance)



							empty(ray.Instance, normalid)
							ray.Instance:SetAttribute("Tangible", false)

							end
					end
				end
			end
		end
	end
	
end)

end

script.StartRayCast:GetPropertyChangedSignal(“Value”):Connect(createrayorigins)

this is a localscript that loads terrain and generates plants on top of it setting the plant’s orientation to the wedge raycast detects.

1 Like

I was able to solve this with some complex stuff I’m about to explain briefly

Please still try to explain the solution. This is so other Developers who come across this topic are still able to find solutions like you did.

1 Like

The detailed solution is:

As can be guessed from the code above, wedges that form terrain may have its rightvector poiting up or downwards, I found that the problem had something to do with weird cframe conversion.

What I basically did was to set plant’s primarypartCFrame to wedge.CFrame.

Then I set every mesh inside the plant model position to ray.Position and added an offset else it would be placed underground.

Heres the new code:

local params = RaycastParams.new()
params.CollisionGroup = “Atmosphere”
params.FilterType = Enum.RaycastFilterType.Include

local function fill(WEDGE, normal)

if normal == Enum.NormalId.Right then
	workspace.Terrain:FillWedge(WEDGE.CFrame - WEDGE.CFrame.RightVector * 1.9, WEDGE.Size + Vector3.new(2,3,3), WEDGE.Material)
	
	
	
	
	WEDGE:SetAttribute("Tangible", true)
	if workspace.CelestialBodies:FindFirstChild("Sphere" .. 1).PlantLifePossible.Value == true then
	for count = 1,4 do
		local plant = game.ReplicatedStorage:FindFirstChild("Planet" .. 1).Plants:GetChildren()[WEDGE:GetAttribute("PlantNumber")]:Clone()
		if plant then
		


		local lookv = WEDGE.CFrame.RightVector


			local vector1 = Vector3.new(WEDGE:GetAttribute("Offset" .. count), WEDGE:GetAttribute("Offset" .. count), WEDGE:GetAttribute("Offset" .. count))
		local vector2 = -vector1

		local vectors = {vector1, vector2}

		local vector = vectors[math.random(1,#vectors)]




			local origin = (WEDGE.Position + vector) - lookv * 1
	



		

		local ray = workspace:Raycast(origin, lookv * 100)
		
		
		if ray then
					if ray.Instance.Name == "Wedge" then
						local function GetNormalFromFace(part, normalId)
							return part.CFrame:VectorToWorldSpace(Vector3.FromNormalId(normalId))
						end

						local function NormalToFace(normalVector, part)

							local TOLERANCE_VALUE = 1 - 0.001
							local allFaceNormalIds = {
								Enum.NormalId.Front,
								Enum.NormalId.Back,
								Enum.NormalId.Bottom,
								Enum.NormalId.Top,
								Enum.NormalId.Left,
								Enum.NormalId.Right
							}    

							for _, normalId in pairs( allFaceNormalIds ) do
								-- If the two vectors are almost parallel,
								if GetNormalFromFace(part, normalId):Dot(normalVector) > TOLERANCE_VALUE then
									return normalId -- We found it!
								end
							end

							return nil -- None found within tolerance.

						end





						local normalid = NormalToFace(ray.Normal, ray.Instance)
						
						if normalid == Enum.NormalId.Right then
						
						
						plant:SetPrimaryPartCFrame(ray.Instance.CFrame * CFrame.Angles(0,0,math.rad(90)))
						else
							plant:SetPrimaryPartCFrame(ray.Instance.CFrame * CFrame.Angles(0,0,math.rad(-90)))

							end
						
					plant.Parent = WEDGE

if plant.Name == “Plant2” then

							plant.Leaves.Position = (ray.Position - plant.Caster.CFrame.LookVector * plant.Leaves.Size.Y/2)
								plant.Stem.Position = (ray.Position - plant.Caster.CFrame.LookVector * plant.Stem.Size.Y/2)
								
								local tween = ts:Create(plant.Leaves, ti, {CFrame = plant.Leaves.CFrame * angles})
								tween:Play()
								local tween2 = ts:Create(plant.Stem, ti, {CFrame = plant.Stem.CFrame * angles})
								tween2:Play()
					else
						if plant.Name == "Plant3" or plant.Name == "Plant4" or plant.Name == "Plant5" then
							

							
								if plant.Name == "Plant3" then
									plant.Leaves.Position = (ray.Position - plant.Caster.CFrame.LookVector * 14)
									local tween = ts:Create(plant.Leaves, ti2, {Size = plant.Leaves.Size + Size})
									tween:Play()
									local tween2 = ts:Create(plant.Leaves, ti, {CFrame = plant.Leaves.CFrame * angles})
									tween2:Play()
									else
									plant.Leaves.Position = (ray.Position - plant.Caster.CFrame.LookVector * plant.Leaves.Size.Y/2)
									local tween = ts:Create(plant.Leaves, ti2, {Size = plant.Leaves.Size + Size})
									tween:Play()
									local tween2 = ts:Create(plant.Leaves, ti, {CFrame = plant.Leaves.CFrame * angles})
									tween2:Play()
									end
						else
							
								
									plant.Bigplant.Position = (ray.Position - plant.Caster.CFrame.LookVector * plant.Bigplant.Size.Y/2)
									local tween = ts:Create(plant.Bigplant, ti, {CFrame = plant.Bigplant.CFrame * angles})
									tween:Play()
								
						end
						end

			end
			end


			
			end
			end
		end
else
	if normal == Enum.NormalId.Left then
		workspace.Terrain:FillWedge(WEDGE.CFrame - WEDGE.CFrame.RightVector * -1.9, WEDGE.Size + Vector3.new(2,3,3), WEDGE.Material)
		WEDGE:SetAttribute("Tangible", true)
		
		
		end
end

end

local function empty(WEDGE, normal)

if normal == Enum.NormalId.Right then
	workspace.Terrain:FillWedge(WEDGE.CFrame - WEDGE.CFrame.RightVector * 1.9, WEDGE.Size + Vector3.new(2,3,3), Enum.Material.Air)
	WEDGE.Transparency = 0
	WEDGE:SetAttribute("Tangible", false)
	WEDGE:ClearAllChildren()
else
	if normal == Enum.NormalId.Left then
		workspace.Terrain:FillWedge(WEDGE.CFrame - WEDGE.CFrame.RightVector * -1.9, WEDGE.Size + Vector3.new(2,3,3), Enum.Material.Air)
		WEDGE.Transparency = 0
		WEDGE:SetAttribute("Tangible", false)
		WEDGE:ClearAllChildren()
	end
end

end

game:GetService(“RunService”).RenderStepped:Connect(function()
for i, pos in ipairs(posi) do
local distance = (game.Players.LocalPlayer.Character.PrimaryPart.Position - pos).Magnitude - 3900
if distance < renderabledis then
local number1 = table.find(posesoriginal, pos)

		local chunkc = chunksoriginal[number1]
		
		local chunk = chunkc:Clone()
		

		
		
		
			chunk.Parent = workspace.CelestialBodies:FindFirstChild("Sphere" .. 1).Planet
		

		table.remove(posi, i)
		table.insert(usedposes, pos)
		
		


	end
end

end)

game:GetService(“RunService”).RenderStepped:Connect(function()
for i, pos in ipairs(usedposes) do
local distance = (game.Players.LocalPlayer.Character.PrimaryPart.Position - pos).Magnitude - 3900
if distance > renderabledis then
local number1 = table.find(posesoriginal, pos)

		local chunkc = chunksoriginal[number1]
		local sphere = workspace.CelestialBodies:FindFirstChild("Sphere" .. 1).Planet
		local chunk = sphere:FindFirstChild(chunkc.Name)


		chunk:Destroy()


		table.remove(usedposes, i)
		table.insert(posi, pos)


	end
end

end)

local function createrayorigins()

local descinstfl = game.Players.LocalPlayer.Character:GetDescendants()
local descinsttl = {}

for i, child in ipairs(descinstfl) do
	if child:IsA("Part") or child:IsA("Union") or child:IsA("MeshPart") then
		table.insert(descinsttl, child)
	end
end

local params = RaycastParams.new()
params.FilterDescendantsInstances = descinsttl
params.FilterType = Enum.RaycastFilterType.Exclude


game:GetService("RunService").RenderStepped:Connect(function()




	for i, pos in ipairs(game.Players.LocalPlayer.Character.RayAtt:GetChildren()) do
		if pos:IsA("Attachment") then
		local direction = (game.Players.LocalPlayer.Character.Head.CFrame.UpVector) * -1

		local ray = workspace:Raycast(pos.WorldCFrame.Position, direction * 5000, params)


		if ray then
				if ray.Instance.Parent.Parent.Name == "Planet" then
					if ray.Instance ~= workspace.Terrain then
					if ray.Instance:GetAttribute("Tangible") == false then
						
						local function GetNormalFromFace(part, normalId)
							return part.CFrame:VectorToWorldSpace(Vector3.FromNormalId(normalId))
						end
						
						local function NormalToFace(normalVector, part)

							local TOLERANCE_VALUE = 1 - 0.001
							local allFaceNormalIds = {
								Enum.NormalId.Front,
								Enum.NormalId.Back,
								Enum.NormalId.Bottom,
								Enum.NormalId.Top,
								Enum.NormalId.Left,
								Enum.NormalId.Right
							}    

							for _, normalId in pairs( allFaceNormalIds ) do
								-- If the two vectors are almost parallel,
								if GetNormalFromFace(part, normalId):Dot(normalVector) > TOLERANCE_VALUE then
									return normalId -- We found it!
								end
							end

							return nil -- None found within tolerance.

						end





						local normalid = NormalToFace(ray.Normal, ray.Instance)

						

							fill(ray.Instance, normalid)
							ray.Instance:SetAttribute("Tangible", true)

						end
						end
				end
		end
		end
		end
	
	for i, pos in ipairs(game.Players.LocalPlayer.Character.RayAttUn:GetChildren()) do
		if pos:IsA("Attachment") then
			local direction = (game.Players.LocalPlayer.Character.Head.CFrame.UpVector) * -1

			local ray = workspace:Raycast(pos.WorldCFrame.Position, direction * 5000, params)


			if ray then
				if ray.Instance.Parent.Parent.Name == "Planet" then
					if ray.Instance:GetAttribute("Tangible") == true then
						local distance = (game.Players.LocalPlayer.Character.PrimaryPart.Position - ray.Instance.Position).Magnitude - 200
						if distance > 150 then
							local function GetNormalFromFace(part, normalId)
								return part.CFrame:VectorToWorldSpace(Vector3.FromNormalId(normalId))
							end

							local function NormalToFace(normalVector, part)

								local TOLERANCE_VALUE = 1 - 0.001
								local allFaceNormalIds = {
									Enum.NormalId.Front,
									Enum.NormalId.Back,
									Enum.NormalId.Bottom,
									Enum.NormalId.Top,
									Enum.NormalId.Left,
									Enum.NormalId.Right
								}    

								for _, normalId in pairs( allFaceNormalIds ) do
									-- If the two vectors are almost parallel,
									if GetNormalFromFace(part, normalId):Dot(normalVector) > TOLERANCE_VALUE then
										return normalId -- We found it!
									end
								end

								return nil -- None found within tolerance.

							end





							local normalid = NormalToFace(ray.Normal, ray.Instance)



							empty(ray.Instance, normalid)
							ray.Instance:SetAttribute("Tangible", false)

							end
					end
				end
			end
		end
	end
	
end)

end

script.StartRayCast:GetPropertyChangedSignal(“Value”):Connect(createrayorigins)

1 Like

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