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!!!

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

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

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

I wrote an article on this a long time ago!

6 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
5 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!