"FindVerticesWithinSphere" method is confusing

Hello! I’m just starting to discover EditableMeshes and how they work. While implementing mesh deformation i had trouble figuring out this function.

The documentation says that “FindVerticesWithinSphere finds all vertices within a specific sphere and returns a list of stable vertex IDs.” which is exactly what i need. The explanation and the purpouse of the function are clear.

The real problem is one of the arguments, more specifically the “radius”. The docs simply state that it is the “Radius of the sphere.” However the size is not consistent, i put in 1/4 and it takes all the verticies in about a 1 stud area, I put 1 it’s suddently considering all the verticies in a 3 stud area. I don’t see the pattern here, what’s going on?

Try setting the mesh scale to 1,1,1

Doesn’t tell me anything, it doesnt seem to be based on the size of the mesh

its based on the vertex positions you initially gave. you should try to keep the coordinates between -0.5 and 0.5 so FindVerticesWithinSphere is actually accurate, since that also checks against the local vertex positions and not the real size. this is just the way it works and theres no changing it.

if you have the coordinates between -0.5 and 0.5 you can just set the meshpart size as if it’s a part and get proper behavior

The position is accurate, i know it because i visualized the verticies, i tried dividing the radius by two and now it works? I’m not exactly sure what’s going on
a1310983773f5360cea8efb5c76fc4d25d571b16
879d07e3d2930082e1a05bfd47e2db35b3179891

1 Like

you didn’t give a single line of code, so I cant really help you out

The HitRaycast function fires a ray and gets the verticies on the position given a radius. It also manipulates the position fo the verticies because this is actually a module for realistic gore

local Gore = {}

-- Services
local AssetService = game:GetService("AssetService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
-- Modules
local Noise = require(ReplicatedStorage.Shared.Utility.Noise)
local AssetsDealer = require(ReplicatedStorage.Shared.AssetsDealer)
-- Constants
local RAYCAST_PARAMS = RaycastParams.new()

local function debugNode(position)
	local node = AssetsDealer.GetMesh("Misc/Node")
	node.Parent = workspace
	if position then
		node.Position = position
	end
	Debris:AddItem(node,5)
	return node
end

local function debugAttachment(position)
	local attachment = Instance.new("Attachment")
	attachment.Parent = workspace.Terrain
	if position then
		attachment.WorldPosition = position
	end
	return attachment
end

local function getVerticiesPositions(editableMesh: EditableMesh,verts: {number})
	local positions = {}
	for _,vertexId in verts do
		positions[vertexId] = editableMesh:GetPosition(vertexId)
	end
	return positions
end

local function mergeCloseVerticies(editableMesh: EditableMesh)
	local vertsPositions = getVerticiesPositions(editableMesh, editableMesh:GetVertices())
	for vertex_id,position in vertsPositions do
		local closeVerticies = editableMesh:GetAdjacentVertices(vertex_id)
		for _,closeVertexId in closeVerticies do
			if closeVertexId ~= vertex_id then
				local closeVertexPosition = vertsPositions[closeVertexId]
				if (closeVertexPosition-position).Magnitude < 0.05 then
					editableMesh:SetPosition(vertex_id,closeVertexPosition)
					break
				end
			end
		end
	end
end

local function getBloodMesh(part: MeshPart)
	local bloodMesh = part:FindFirstChild("BloodMesh")
	if not bloodMesh then
		bloodMesh = part:Clone()
		bloodMesh.Name = "BloodMesh"
		bloodMesh.Parent = part
		bloodMesh.Color = Color3.new(.7,0,0)
		bloodMesh.Size -= Vector3.one/5
	end
	return bloodMesh
end

local function raycast(origin,direction)
	local raycast = workspace:Raycast(origin,direction,RAYCAST_PARAMS)
	if raycast then
		if raycast.Instance then
			local mesh = raycast.Instance:FindFirstChild("Mesh") 
			if mesh and mesh:IsA("MeshPart") then
				return raycast.Position,mesh,raycast.Normal
			end
		end
	end
end

function Gore.GetEditableMesh(part: MeshPart)
	assert(part:IsA("MeshPart"),`Part "{part.Name}" must be a meshpart to work with EditableMesh.`)

	local editableMesh = part:FindFirstChildOfClass("EditableMesh") :: EditableMesh
	-- If no editablemesh is found one will be created.
	if not editableMesh then
		editableMesh = AssetService:CreateEditableMeshAsync(part.MeshId)
		editableMesh.Parent = part
	end

	return editableMesh
end

local function getLocalRadius(part,radius)
	local size = part.Size
	return math.max(size.X,size.Y,size.Z)*radius
end

function Gore.HitRaycast(origin: Vector3, direction: Vector3, radius: number)
	radius = radius or 1
	local localRadius = radius/2

	assert(typeof(origin) == "Vector3",`Origin must be a Vector3`)
	assert(typeof(direction) == "Vector3",`Direction must be a Vector3`)
	assert(typeof(radius) == "number",`Radius must be a number`)

	local position,part,normal = raycast(origin,direction)

	if not position or not part or not normal then warn("Raycast failed.") return end

	local editableMesh = Gore.GetEditableMesh(part)
	local bloodMesh = getBloodMesh(part)
	local bloodEditableMesh = Gore.GetEditableMesh(bloodMesh)

	local localPosition = part.CFrame:PointToObjectSpace(position)
	local localDirection = part.CFrame:VectorToObjectSpace(direction)
	--local biggestAxis = math.max(part.Size.X,part.Size.Y,part.Size.Z)
	--assert(localRadius > 0, `Local radius is zero`)

	local attachment = debugAttachment()
	attachment.Parent = part
	attachment.CFrame = CFrame.lookAlong(localPosition,normal)

	local localTarget = localDirection.Unit
	--local node = debugNode(position)
	--node.Size = Vector3.one*radius
	--node.Color = Color3.new(0,0,1)
	
	local verts = editableMesh:FindVerticesWithinSphere(localPosition,localRadius)	

	if #verts <= 0 then warn(`No vertices found in radius`) return end

	local vertsPositions = getVerticiesPositions(editableMesh,verts)

	--assert(#vertsPositions > 0, `Failed to get vertex positions.`)

	print("hit")
	for _, vertexId in verts do
		local pos = vertsPositions[vertexId]
		
		--local node = debugNode(part.CFrame:PointToWorldSpace(pos))
		--node.Size = Vector3.one/5
		
		local biggestNormalAxis = math.max(normal.X,normal.Y,normal.Z)
		local normalX,normalY
		if biggestNormalAxis == normal.X then
			normalX = normal.Y; normalY = normal.Z
		elseif biggestNormalAxis == normal.Y then
			normalX = normal.X; normalY = normal.Z
		else
			normalX = normal.X; normalY = normal.Y
		end
		
		
		local distance = (pos-localPosition).Magnitude
		
		if distance > localRadius then warn(distance) end
		
		local distanceFactor = 1-math.clamp(distance/localRadius,0,1)
		local noise = math.clamp(Noise.Get2D(normalX,normalY,.3),0,1)
		local factor = math.clamp((distanceFactor*0.4)^0.9 - noise*0.3,0,0.7)
		
		local newPos = pos:Lerp(localTarget,factor)
		local newBloodPos = pos:Lerp(localTarget,math.clamp(factor-0.2,0,1))
		--editableMesh:RemoveVertex(vertexId)

		editableMesh:SetPosition(vertexId,newPos)
		bloodEditableMesh:SetPosition(vertexId,newBloodPos)
	end

	mergeCloseVerticies(editableMesh,vertsPositions)
	mergeCloseVerticies(bloodEditableMesh,vertsPositions)
end

return Gore

Also i didn’t provide any code because i just wanted to understand how the function worked, which i still dont get