How to detect which side of a part is most "up"

I want to know which vector (upvector, lookvector, rightvector, -upvector, -lookvector, or -rightvector) is the side of the part that faces most up. I need this for my code of adding snow on top of all parts. Some are rotated a little, so i used upvectors. But some are rotated more than 90 degrees and I can’t use upvector anymore because then it would be upside down (upside down snow doesn’t seem that realistic) please help me here is the code

for i,v in pairs(game.Workspace:GetChildren()) do
	if v:IsA("Part") and v.Name ~= "Baseplate" then
		local snow = v:Clone()
		snow.Parent = v
		snow.Size = snow.Size-Vector3.new(0,snow.Size.Y-0.1,0)
		snow.Anchored = true
		snow.BrickColor = BrickColor.new("White")
		snow.Material = "Sand"
		local num = (v.Size.Y/2)+0.05
		snow.CFrame = snow.CFrame + snow.CFrame.UpVector * num
	end
end

Please help ik your read this lol thanks!!!

1 Like

Couldn’t you just use the Orientation of the part to determine this?

1 Like

Sorry i kinda suck at math. I see the connection between orientation and vectors. But idk how to translate it. Can you help please?

1 Like

Why not just fire raycasts down from the sky and when that ray comes in contact with a part, you can use the NormalId to find out where to position the snow? (Haven’t used Raycasts in awhile so I’m not gonna pseudo-code it rn and look silly :P).

1 Like

I wrote an article on this a long time ago!

4 Likes

wow nice! i will try it out :slight_smile:

wow i think it is right. Is it this function?


local function getHighestFace(part)
	local highestFace
	local height = -math.huge
	
	for k, normalId in pairs(Enum.NormalId:GetEnumItems()) do
		local y = part.CFrame:pointToWorldSpace(Vector3.FromNormalId(normalId)).y
		if y > height then
			highestFace = normalId
			height = y
		end
	end
	
	return highestFace
end

That’s just one function I wrote to make the dice work - it’s not the whole thing. You could use that function in your own code; however the article has download a download link to the completed dice model at the bottom.

oh ok. But what does the function return?

It returns a NormalId. Notice how it iterates over Enum.NormalId:GetEnumItems()

ok. Is there a way I can implement that into my script here? I need to replace “upVector” with something else (the top side of the part)

for i,v in pairs(game.Workspace:GetChildren()) do
	if v:IsA("Part") and v.Name ~= "Baseplate" then
		local snow = v:Clone()
		snow.Parent = v
		snow.Size = snow.Size-Vector3.new(0,snow.Size.Y-0.1,0)
		snow.Anchored = true
		snow.BrickColor = BrickColor.new("White")
		snow.Material = "Sand"
		local num = (v.Size.Y/2)+0.05
		snow.CFrame = snow.CFrame + snow.CFrame.UpVector * num -- what do i replace this line with?
	end
end

Here’s my attempt, using Ozzypig’s getHighestFace method. It works, but I feel slightly uncomfortable with the math that I did to achieve it (namely, the “positive” normal variable thing that I do in order to figure out the size feels icky). I’m pretty awful with math so I’m sure there’s a far better way to do this portion.

local function getHighestFace(part)
	local highestFace
	local height = -math.huge

	for k, normalId in pairs(Enum.NormalId:GetEnumItems()) do
		local y = part.CFrame:pointToWorldSpace(Vector3.FromNormalId(normalId)).y
		if y > height then
			highestFace = normalId
			height = y
		end
	end

	return highestFace
end

local function addSnow(part, thickness)
	local normal = Vector3.FromNormalId(getHighestFace(part))
	local offset = normal * part.Size * 0.5

	local positive = if normal.X<0 or normal.Y<0 or normal.Z<0 then normal*-1 else normal

	local existing = part:FindFirstChild("SnowPart")
	local snow = existing or Instance.new("Part")

	if not existing then
		snow.Name = "SnowPart"
		snow.Anchored = true
		snow.BrickColor = BrickColor.new("White")
		snow.Material = Enum.Material.Sand
	end

	snow.Size = (positive*thickness)+(part.Size*-positive+part.Size)
	snow.CFrame = part.CFrame:ToWorldSpace(CFrame.new(offset))+(normal*thickness/2)

	snow.Parent = part
	return snow
end

for _,v in ipairs(workspace:GetChildren()) do
	if v:IsA("Part") and v.Name~="Baseplate" then
		addSnow(v, 0.25)
	end
end
4 Likes

There’s an error

line: snow.Size = (positivethickness)+(part.Size-positive+part.Size)

error: 14:17:16.818 ServerScriptService.Script:49: invalid argument #3 (NumberSequence expected, got Vector3) - Server - Script:49

Seems like you modified the code a bit, and I’m not sure what you changed. The code that I sent is only 43 lines long, while your error shows that you’re running some code that’s 49 lines long. I need to see the changes in order to figure out what’s wrong, because the script that I sent you works just fine.

1 Like

oh no it’s some comments above the code. I found out why. It’s because I have a part, and in that part there is a particle emitter called “Snow”. It broke your code. Can you make it so the name won’t be affected? Change this line of code:

	local existing = part:FindFirstChild("Snow");

then i will set as solution thanks! :slight_smile:

image

Just change “Snow” in these two spots to something else, like “SnowPart” or something.


Edit: Went back up and changed the code in my reply here to do just this.

Ok, since I can just put any random, I’ve decided it’s the correct one :slight_smile: thanks!