How to get bones inside radius?

I know I could use GetPartBoundsInRadius but this and other WorldRoot methods only detect BaseParts, I want to detect bones inside an area for optimizing a sea mesh but looping through 36 thousand + items of a table is not an option, because only 3 thousand or less will be animated.

To understand better the issue look at this:

This is how I’m animating my mesh right now, it works, and it’s optimized, but if I move that block in the image, the “rendering center”, the animated vertices can only decrease because my code detects the magnitude between every one of these ~2000 bones and the center, if it’s bigger than a certain value, “render distance”, then the vertex will not be animated anymore.

However doing the opposite, checking the magnitude between the ~36000 not animated bones and the center to animate them back if the magnitude is smaller than the render distance is not optimal and causes a lot of lag.

A solution I found would be actually iterating over these not animated bones but along a 10-frame interval, which reduces lag but creates this weird effect:

When moving in slower velocities the effect is smaller but still visible.

So, my final question is in the title, if you need any additional information please ask me, any help is appreciated.

1 Like

Hmm did you try other ways of optimising??

One solution i can think of is having a flat piece of ocean that isn’t animated. Then, add an animated ocean slightly above the flat sea and CFrame the animated sea along with the player

I think this would create a weird effect, like the ocean is moving with you, and lower waves will clip with the flat sea under it.

Yes, there are other optimizing techniques that I’ll apply, but this one is crucial, I got the idea from here.

The answer above the one I linked explains another method, using LOD and a chunk system, if you look at terrain water in Studio with wireframe view you’ll see that Roblox uses this too, but I don’t think I can do it, especially with how little access to geometry we have in Roblox Studio.

The answer by @Sirewags seems way simpler, that is if I manage to get the closest bones from the center in an optimized way.

1 Like

I used a Quadtree module to get all of the planes within the camera’s frustum and add them to a table which is updated prior to every frame being rendered.

local CamCF = Camera.CFrame
	local Horizontal = math.rad(Camera.MaxAxisFieldOfView)/2
	local Vertical = math.rad(Camera.FieldOfView)/2

	local np = (CamCF*CFrame.new(0,0,Camera.NearPlaneZ))
	local p1 = {np.LookVector, DistancePlanePoint(np.Position, np.LookVector, Vector3.new())}	--near plane

	local fp = (CamCF*CFrame.new(0,0,-AutoRenderDistance))
	local p2 = {-fp.LookVector, DistancePlanePoint(fp.Position, -fp.LookVector, Vector3.new())}	--far plane

	local rp = CamCF*CFrame.Angles(0,-Horizontal,0)
	local p3 = {-rp.RightVector, DistancePlanePoint(rp.Position, -rp.RightVector, Vector3.new())}

	local lp = CamCF*CFrame.Angles(0,Horizontal,0)
	local p4 = {lp.RightVector, DistancePlanePoint(lp.Position, lp.RightVector, Vector3.new())}

	local lowp = CamCF*CFrame.Angles(-Vertical,0,0)
	local p5 = {lowp.UpVector, DistancePlanePoint(lowp.Position, lowp.UpVector, Vector3.new())}

	local upp = CamCF*CFrame.Angles(Vertical,0,0)
	local p6 = {-upp.UpVector, DistancePlanePoint(upp.Position, -upp.UpVector, Vector3.new())}

return {p1,p2,p3,p4,p5,p6}

Next I get all of the bones that are within the camera frustum.

for i,MainTree in pairs(Quadtrees) do
	MainTree:findWithinFrustum(FrustumPlanes, Bones)
end

Then I loop through all of the loaded planes in the zones (note that I also have a zone/regions system to have different wave behaviours)

for _,Zone in ipairs(Zones) do
	if (Zone.Position-Camera.CFrame.Position).Magnitude-RenderDistance<= Zone.Size.Z/2 then
		table.insert(NearZones, Zone)
	end
end

Finally I then calculate the waves (or in other words transform the bones) using this.

if v:IsA("Bone") then
	Origin = v.WorldPosition
	Scale = GetScale(Camera.CFrame.Position, Origin, RenderDistance, FadeDistance)
				
	for _,Zone in ipairs(NearZones) do
		DesiredScale = 1-GetScale(Zone.Position, Origin, Zone.Size.Z/2, Zone.Fade.Value)*(1-Zone.Scale.Value/100)
		Scale = Scale*DesiredScale
	end
				
	if Scale <= 0.05 then
		v.Transform = CFrame.new()
	else
		p = GerstnerWave.GetTransform(Waves, Origin, SyncedTime + (tick() - StartTime))
		v.Transform = CFrame.new(p*Scale)
	end
end

This should give a result similar to this, where the waves gently fade away out of the render distance.

As I said it’s really a huge pain to do this properly, but I guess it works. Feel free to contact me again if you need any more support - I’m happy to help

2 Likes

I understood most of it except the Quadtree module code, the variables are extremely abbreviated, could you explain what the variables np, fp, rp, lp, lowp and upp represent? The p1, p2, etc, too.

Also, what is Camera.NearPlaneZ?

These might be some dumb doubts but I never played around with the camera frustum, it’s really out of my comfort zone, so I’ll try my best to understand, with your help of course.

The variable np is just an abbreviation for one of the planes, in that example np just means “near plane” in a similar way to that fp means “far plane” etc…

This is a really poor image that I just found on google, but a quadtree essentially allows you to break each node down into 4 children. You can kind of see what this means in the image i’ve attached.
Mediauadtree.gif
I suggest that you do some of your own research, because realistically if you don’t understand what’s happening then you aren’t going to be able to learn from it.

1 Like

It’s just that I have a big issue with abbreviated variables, probably because English is not my main language, but I’ll research about this, thank you for the idea.

I researched mostly camera frustums, I know each plane is defined by its normal and the “d” component, but I don’t know how to get the “d”.

I found this video about frustum culling, I watched it while reading your code, which made me understand everything but the “d” (it’s not properly explained in the video how to calculate d, just a brief explanation about what it is) in your code it is the DistancePlanePoint function, what this function does?

Also, why do you need to use quadtree on this? Couldn’t you just get the
seaplanes/bones inside the camera frustum and calculate the waves on them? It would already work for optimization, wouldn’t it?

Edit: searching a bit more I got this:

local module = {}

local function distancePlaneFromOrigin(planePoint, normal)
	return normal:Dot(planePoint)
end

function module.getFrustum(camera, renderDistance)
	local cameraCF = camera.CFrame
	
	local cameraHorizontalLimit = math.rad(camera.MaxAxisFieldOfView) / 2
	local cameraverticalLimit = math.rad(camera.FieldOfView) / 2

	local nearPlane = (cameraCF * CFrame.new(0, 0, camera.NearPlaneZ))
	local p1 = {nearPlane.LookVector, distancePlaneFromOrigin(nearPlane.Position, nearPlane.LookVector)}

	local farPlane = (cameraCF * CFrame.new(0, 0, -renderDistance))
	local p2 = {-farPlane.LookVector, distancePlaneFromOrigin(farPlane.Position, -farPlane.LookVector)}

	local rightPlane = cameraCF * CFrame.Angles(0, -cameraHorizontalLimit, 0)
	local p3 = {-rightPlane.RightVector, distancePlaneFromOrigin(rightPlane.Position, -rightPlane.RightVector)}

	local leftPlane = cameraCF * CFrame.Angles(0, cameraHorizontalLimit, 0)
	local p4 = {leftPlane.RightVector, distancePlaneFromOrigin(leftPlane.Position, leftPlane.RightVector)}

	local lowPlane = cameraCF * CFrame.Angles(-cameraverticalLimit, 0, 0)
	local p5 = {lowPlane.UpVector, distancePlaneFromOrigin(lowPlane.Position, lowPlane.UpVector)}

	local upPlane = cameraCF * CFrame.Angles(cameraverticalLimit, 0, 0)
	local p6 = {-upPlane.UpVector, distancePlaneFromOrigin(upPlane.Position, -upPlane.UpVector)}
	
	return {p1,p2,p3,p4,p5,p6}
end

function module.findInFrustum(planes, objectPos)
	for i, plane in planes do
		local planeNormal = plane[1]
		local planeD = plane[2]
		
		if (objectPos:Dot(planeNormal)) + planeD <= 0 then
			return false
		end
	end
	
	return true
end

return module

but I’m checking if a random object is inside the camera frustum and it’s not working, always returns false, what am I doing wrong?

Edit: inverted the result of the distancePlaneFromOrigin function and now it works, please ignore this, I’ll implement it on my seaplanes, but I’m still curious about why using quadtree and more details about the implementation, as I asked above, thank you again for the patience and help.

The DistancePlanePoint function simply returns ((P-OP):Dot(N))/N.Magnitude

P represents the frustumPlane.Position
OP represents the frustumPlane.LookVector
N represents the Normal

I use a quadtree mainly out of personal preference but it’s also more optimized. You could try your method, not sure if it would run slower and leave you with a lot of latency, but nonetheless give it a go and see what happens. Feel free to reply again if you need any more help

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.