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

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

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)


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.

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.







