Evenly Distributing Points Across A Surface

I’m currently attempting to create a script to do what the title says.

bear with me since this does get a litttle confusing… (i’m horrible at explaing) :sweat_smile:

How I’m approaching this is by getting the size of the part and creating an attachment for every n’th studs on each axis. This fills the entire volume of the part with attachments separated by “n” studs each. i.e:
image
(i scaled the part up to make the attachments less dense and made it slightly transparent for visibility of the attachments)

(original photo, n set to 1)
image

So to combat this, I use some pretty simple mechanics (line 23-35) to delete every attachment inside the volume of the part, leaving me with only attachments on the surface. Then I raycast each attachment with the X axis canceled out, and then the Z axis canceled out to ensure I hit every spot from every angle on the surface. If a ray hits the surface of the part then a new index in a dictionary is created from the string version of the intersecting position (where the ray hit the part). The value of the index is saved as the unmodified intersecting position to save time. This prevents multiple attachments from being created in the same spot. Then the dictionary is looped through and creates an attachment for each index.

What I just explained is illustrated in the diagram below with… let’s say the Z axis canceled out. (i’m not a very good drawer either)


The example shown is with the z axis canceled out.

  • Each number represents a layer, or pair of outer points(the points that were left after deleting the ones inside the volume of the part) raycasting with the z axis canceled out.
  • Each blue line represents a ray that hits.
  • Each purple line represents a ray that misses.
  • Each green triangle represents a new attachment created.
  • Each red dot represents an original surface attachment(aka outer point).

To avoid creating multiple points with the same attachment(i already went over this but since this is an edit i’ll keep this in),

  • each green triangles’ position is indexed into a dictionary with tostring(raycastResult.Position)
  • then that dictionary is looped through and creates points for each index.

This method works fairly well with spheres, but not other shapes.

(n set to 0.5)
image
image

It’s also very inconsistent with other shapes as well, as the back of the dominous is untouched, and the front is completely covered. The cube has none, I thought that may be because the points were perfectly on top of the surface which may be messing with the raycast. So I made them move 1 whole stud back away from the center of the part and it still didn’t work. For some reason the sides of the cylinder won’t work. Rotating the cylinder does not fix it either.
image

Here is the script I use to distribute the points.

local part = script.Parent
part.Anchored = true
local padding = .5 --this is the "n" variable
local size = {}
local positions = {}
size.X = part.Size.X
size.Y = part.Size.Y
size.Z = part.Size.Z
size.Sum = (size.X+size.Y+size.Z)

local clone = part:Clone()
clone.Parent = nil
--clone.Shape = Enum.PartType.Block
--local effectiveness = (part:GetMass()/clone:GetMass())
clone:Destroy()

for X = -(size.X/2), (size.X/2), padding do
	for Y = -(size.Y/2), (size.Y/2), padding do
		for Z = -(size.Z/2), (size.Z/2), padding do
			local attachment = Instance.new("Attachment", part)
			attachment.Name = "AeroAttachment"
			attachment.Position = Vector3.new(X,Y,Z)
			local isInside = true
			if math.abs(attachment.Position.X) == (size.X/2) then
				isInside = false
			end
			if math.abs(attachment.Position.Y) == (size.Y/2) then
				isInside = false
			end
			if math.abs(attachment.Position.Z) == (size.Z/2) then
				isInside = false
			end
			if isInside then
				attachment:Destroy()
			end
			--local pressurepoint = Instance.new("Part", attachment)
			--pressurepoint.Anchored = true
			--pressurepoint.CFrame = attachment.WorldCFrame
			--pressurepoint.Massless = true
			--pressurepoint.CanCollide = false
			--pressurepoint.CanQuery = false
			--pressurepoint.Size = Vector3.new(.001,.001,.001)
			--pressurepoint.Transparency = 1
			--local weld = Instance.new("WeldConstraint", pressurepoint)
			--weld.Part0 = part
			--weld.Part1 = pressurepoint
			--pressurepoint.Name = "Pressure"
			--pressurepoint.Anchored = false
		end
	end
end

if true--[[part.Shape ~= Enum.PartType.Block]] then
	local function addsurfacepoints(factor)
		for i, v in pairs(part:GetChildren()) do
			if v.Name == "AeroAttachment" then
				local params = RaycastParams.new()
				params.FilterType = Enum.RaycastFilterType.Whitelist
				params.FilterDescendantsInstances = {part}

				--(v.WorldPosition+((v.WorldPosition-part.Position).Unit*.001))--this is me moving the position used to caculate the direction back because of the cube problem mentioned before

				local result = game.Workspace:Raycast(v.WorldPosition, ((part.Position*factor-(v.WorldPosition+((v.WorldPosition-part.Position).Unit*.001))*factor).Unit*(part.Position*factor-(v.WorldPosition+((v.WorldPosition-part.Position).Unit*.001))*factor).Magnitude), params)

				if result then
					positions[tostring(result.Position)] = result.Position
				else
					print("fkdlgh")
				end

			end
		end
	end
	addsurfacepoints(Vector3.new(1,1,0))
	addsurfacepoints(Vector3.new(0,1,1))
	for i, v in pairs(part:GetChildren()) do
		if v.Name == "AeroAttachment" then
			v:Destroy()
		end
	end
	for i, v in pairs(positions) do
		local attachment = Instance.new("Attachment", part)
		attachment.WorldPosition = v
	end
	print(positions)
end

For the dominous which is a meshpart, I just commented out the lines that reference it’s shape and put true in the if statement.

You could generate an outer grid only, and based on those you can raycast to the center of the mesh with the mesh being whitelisted, then the rayresult position would be the attachment position.

image

Like in this image over here, keep the outer face points, but ignore the ones that are inner.

1 Like

That’s exactly what I did except without canceling the Z and X axis
here’s the results(basically the same thing happened):
image
image

what my method did was this:

what your method did was this:
image

the further away the outer face points are from the surface of the part, the more concentrated towards the center the raycasted points will be:
image
see how in that corner there are points missing, whereas in the one with zero offset distance away from the surface there are points filling it in:
image
that’s because the points are being concentrated towards the center making points farther away from the center more scarce

i hope that makes at least a little bit of sense :sweat_smile:

oh yeah ignore the extra sphere that was from earlier testing