Rotating trees to terrain

first post here so yeah
so i want to make trees rotate to terrain and mine are just either floating or inside the ground


i been looking for at least a hour
heres the code for the trees

any help is appreciated!

Have you tried using the raycast normal?

These posts may help:

im not sure what im doing wrong but its printing out nil

Your rayDirection should be straight down, not diagonal. If you could paste your code, that would help better than a picture.

Example rayDirection:

local rayDirection = Vector3.new(0, -100, 0)

You should also be using CFrame.lookAt like the posts showed, not CFrame.new.

You’re also missing the code that decides the Y position, could you just paste your whole code?

i tried to have it pointing down but it makes the trees rotate 90 degrees to the right and then it errors

local c = 24
		local threshold = .94
		local NoiseThreshold = 0.9
		game.Workspace.Terrain:FillWedge(CFrame.new(x,y,z), Vector3.new(8,8,8), Enum.Material.Grass)

		local TreeNoise = math.noise(x * frequency * c / resolution, z * frequency * c / resolution, seed) + 0.5
		local TreeChance = math.random()

		if TreeNoise > NoiseThreshold and TreeChance > threshold then
			local Tree = game.ServerStorage.Objects.Tree:Clone()
			local TreeHeight = Tree.PrimaryPart.Size.Y
			local PartHeight = Part.Size.Y
			local TreeY = y + 4.5 + TreeHeight 
			Tree:PivotTo(CFrame.new(x,TreeY,z))
			Tree.Parent = workspace.Trees
			local rayOrigin = Tree.PrimaryPart.Position
			local rayDirection = Vector3.new(0, -50, 0)

			local raycastParams = RaycastParams.new()
			raycastParams.FilterDescendantsInstances = {Tree}
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			raycastParams.IgnoreWater = true
			local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
			print(raycastResult)
			print(rayOrigin)
			print(rayDirection)
		--	print(raycastResult.Normal)
			Tree:PivotTo(CFrame.lookAt(raycastResult.Position,raycastResult.Position + raycastResult.Normal,raycastResult.Position))
			--CFrame.lookAt(raycastResult.Position, raycastResult.Position + raycastResult.Normal)
			Tree.Parent = workspace.Trees
			local randidk = math.random(1,7)
			Tree.Leaves.UsePartColor = true
			if randidk == 1 then
				Tree.Leaves.Color = Color3.fromRGB(75, 151, 75)
			elseif randidk == 2 then
				Tree.Leaves.Color = Color3.fromRGB(136, 151, 60)
			elseif randidk == 3 then
				Tree.Leaves.Color = Color3.fromRGB(151, 101, 121)
			elseif randidk == 5 then
				Tree.Leaves.Color = Color3.fromRGB(173, 51, 51)
			elseif randidk == 6 then
				Tree.Leaves.Color = Color3.fromRGB(191, 145, 80)
			elseif randidk == 7 then
				Tree.Leaves.Color = Color3.fromRGB(148, 46, 85)
			end
		end

I have to play devil’s advocate here and ask: Do you really need the trees to rotate in accordance to the terrain’s normals?

In the real world, trees always grow upwards. On hills and mountains, the roots follow the ground while the trunk always faces up towards the sky.

Why do you want to make the trees rotate to the terrain?

(Disclaimer: Images are not mine and are purely for educational purpose)

2 Likes

I feel like this is the part of the code that is going wrong, can you add a debug print to the CFrame.lookAt and show the return to me?

Does it print these?

And also here

i beg you to use dictionaries please

I don’t need it, I just think it makes the game looked polished

sorry i’m messing with code i added some to make it a lil easier to understand

-- calculates tree spawning
		local y = (y1*y2*power*power)+y3
		local c = 24 -- c is some constant you use to customise how the noise feels
		local threshold = .94  -- This defines how dense the trees are (how close together, lower = more dense) [between 0 and 1]
		local NoiseThreshold = 0.9  -- This defines the size of the biome (lower = bigger) [between 0 and 1]
		game.Workspace.Terrain:FillWedge(CFrame.new(x,y,z), Vector3.new(8,8,8), Enum.Material.Grass)

		local TreeNoise = math.noise(x * frequency * c / resolution, z * frequency * c / resolution, seed) + 0.5
		local TreeChance = math.random()

		if TreeNoise > NoiseThreshold and TreeChance > threshold then
			-- Spawns trees
			local Tree = game.ServerStorage.Objects.Tree:Clone()
			local TreeHeight = Tree.PrimaryPart.Size.Y
			local PartHeight = Part.Size.Y
			local TreeY = y + 4.5 + TreeHeight 
			Tree:PivotTo(CFrame.new(x,TreeY,z))
			Tree.Parent = workspace.Trees
			-- raycast
			local rayOrigin = Tree.PrimaryPart.Position
			local rayDirection = Vector3.new(-90, -90, 0)

			local raycastParams = RaycastParams.new()
			raycastParams.FilterDescendantsInstances = {Tree}
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			raycastParams.IgnoreWater = true
			local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
			-- prints out if the raycast is nil the others idk why i put there
			print(raycastResult)
			print(rayOrigin)
			print(rayDirection)
		--	print(raycastResult.Normal)
			Tree:PivotTo(CFrame.new(raycastResult.Position,raycastResult.Position + raycastResult.Normal))
			--CFrame.lookAt(raycastResult.Position, raycastResult.Position + raycastResult.Normal)
			Tree.Parent = workspace.Trees
			local randidk = math.random(1,7)
			Tree.Leaves.UsePartColor = true
			-- makes the trees have different colors
			if randidk == 1 then
				Tree.Leaves.Color = Color3.fromRGB(75, 151, 75)
			elseif randidk == 2 then
				Tree.Leaves.Color = Color3.fromRGB(136, 151, 60)
			elseif randidk == 3 then
				Tree.Leaves.Color = Color3.fromRGB(151, 101, 121)
			elseif randidk == 5 then
				Tree.Leaves.Color = Color3.fromRGB(173, 51, 51)
			elseif randidk == 6 then
				Tree.Leaves.Color = Color3.fromRGB(191, 145, 80)
			elseif randidk == 7 then
				Tree.Leaves.Color = Color3.fromRGB(148, 46, 85)
			end
		end

also heres the error

i thought you meant like things on what they do oops.

i also got other objects like rocks ,bushes ,ect

This is really late, but does this work?

Code:

-- calculates tree spawning
local y = (y1*y2*power*power)+y3
local c = 24 -- c is some constant you use to customise how the noise feels
local threshold = .94  -- This defines how dense the trees are (how close together, lower = more dense) [between 0 and 1]
local NoiseThreshold = 0.9  -- This defines the size of the biome (lower = bigger) [between 0 and 1]
game.Workspace.Terrain:FillWedge(CFrame.new(x,y,z), Vector3.new(8,8,8), Enum.Material.Grass)

local TreeNoise = math.noise(x * frequency * c / resolution, z * frequency * c / resolution, seed) + 0.5
local TreeChance = math.random()

if TreeNoise > NoiseThreshold and TreeChance > threshold then
	-- Spawns trees
	local Tree = game.ServerStorage.Objects.Tree:Clone()
	local TreeHeight = Tree.PrimaryPart.Size.Y
	local PartHeight = Part.Size.Y
	
	local TreeY = y + 4.5 + TreeHeight 
	Tree:PivotTo(CFrame.new(x, TreeY, z))
	Tree.Leaves.UsePartColor = true
	Tree.Parent = workspace.Trees
	
	local rayOrigin = Tree.PrimaryPart.Position
	local rayDirection = Vector3.new(0, -90, 0)

	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {Tree}
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.IgnoreWater = true
	
	local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
	
	if raycastResult then
		Tree:PivotTo(CFrame.lookAt(raycastResult.Position,raycastResult.Position + raycastResult.Normal))
	end
	
	local randidk = math.random(7)
	
	-- makes the trees have different colors
	if randidk == 1 then
		Tree.Leaves.Color = Color3.fromRGB(75, 151, 75)
	elseif randidk == 2 then
		Tree.Leaves.Color = Color3.fromRGB(136, 151, 60)
	elseif randidk == 3 then
		Tree.Leaves.Color = Color3.fromRGB(151, 101, 121)
	elseif randidk == 5 then
		Tree.Leaves.Color = Color3.fromRGB(173, 51, 51)
	elseif randidk == 6 then
		Tree.Leaves.Color = Color3.fromRGB(191, 145, 80)
	elseif randidk == 7 then
		Tree.Leaves.Color = Color3.fromRGB(148, 46, 85)
	end
end

sadly no some of the trees are stuck inside of the ground and some didn’t raycast correctly

Firstly, do the raycast before parenting the tree to the workspace. Calculate the desired X,Z location, and raycast down from the sky. Then you don’t need to worry about adding the tree you’re trying to place to your exclude list. Probably, you want to test that the ray hit Instance is actually Terrain too, not an already-placed tree, or you’ll stack them up. Really, you need a system to prevent overlap, but that’s a different topic.

This isn’t correct. rayOrigin should be up high, a position with a Y value that you know is above the ground height at the targeted X,Z location.

Secondly, rayDirection needs to be a vector, not Euler Angles. You want something like Vector3.new(0,-1024,0) to raycast straight down. The exact length should be long enough to hit the ground from whatever rayOrigin height you use.

Lastly, if you just set the pivot of the tree, to the raycastResult.Position, then this is assuming the pivot point of the tree model is the point on the tree that you want at ground-level. If it’s a point halfway up the tree, the tree will end up sunk into the ground. Offset accordingly.

CFrame.lookAt() is problematic to use when you have a normal vector that is straight up (0,1,0), because the lookAt function is meant for looking horizontally, and allows specifying an UpVector, but you can’t specify a RightVector. If your normal is up (expect with flat ground), you’re at a singularity. The correct approach is to manually construct the full orientation from the normal and your desired (presumably randomized) tree y-axis rotation, using CFrame.fromMatrix().

Yeah, I told the OP about that a week ago, so I just thought they had already fixed it without looking at the code. Sorry about that.

This solution is fairly complicated though, and I’m not very good at CFrame manipulation. If you can send a code sample for OP then that would probably help them alot.