Sphere Generation

Code
-- Sphere
-- Quoteory
-- August 8, 2019

--[[
	

--]]



local Sphere = {}

local spherePart = Instance.new("Part")
spherePart.Anchored = true
spherePart.CastShadow = false
spherePart.Material = Enum.Material.Slate

function inRadius(Mag, minRadius, maxRadius) -- returns if a Part is between the min and max radius, or if it's less than the min radius (inner sphere)
	local targetRadius = (Mag >= minRadius and Mag <= maxRadius)
	local innerRadius = Mag < minRadius
	
	if targetRadius then
		return "targetRadius"
	elseif innerRadius then
		return true
	end
	
end

function newSpherePart(Size, CF, Trans, Color, Parent)
	
	local newPart = spherePart:Clone()
	newPart.Size = Size
	newPart.CFrame = CF
	newPart.Transparency = Trans
	newPart.BrickColor = Color
	newPart.Parent = Parent
	
	return newPart	
	
end

function Sphere:Create(Radius, PartSize, Parts, Position)
	
	local Parts = math.floor(Parts/3 + 0.5) -- amount of parts that shape will be
	local centerCFrame = CFrame.new(Position)
	local Size = Vector3.new(PartSize, PartSize, PartSize) -- the size of the sphere parts
    local Width = PartSize * Parts -- width of the presphere (like a cube)

	-- this generates parts in a cube and detects if they are within a certain radius and does stuff based on that
	for x = 1, Parts do
		for y = 1, Parts do	
			for z = 1, Parts do
				
				local Offset = CFrame.new(-(Width/2)-PartSize/2, -(Width/2)-PartSize/2, -(Width/2)-PartSize/2)
           		local CF = centerCFrame * CFrame.new(PartSize * x, PartSize*y, PartSize * z) * Offset
			
				local minRadius, maxRadius = Radius - PartSize, Radius + PartSize
				-- min and max radius, think of it like the spheres outer shell
				
				local isInRadius = inRadius((centerCFrame.Position - CF.Position).magnitude, minRadius, maxRadius)
				-- calls inRadius() and gets a return value
				
				
				if isInRadius then -- doesn't spawn a part if it's not within radius

					if isInRadius == "targetRadius" then
						newSpherePart(Size, CF, 0, BrickColor.DarkGray(), workspace)
					else
						newSpherePart(Size, CF, 0, BrickColor.Gray(), workspace)			
					end
	
				end
				
					
			end
		end
	end
	
end



return Sphere

the way I’m currently generating the sphere is by going through a loop on each axis and generating a part if it’s within a radius (so like generating a cube then checking if it’s within a radius and if it is spawn it)

it looks exactly how I want it to be in game (it’s not 100% even but that works better for what I need anyways)

is there a better method of doing this, or a way to make this version better?

image

cut in half
image

also I should note in use all of these are just going to be positions on the server and generated on the client (grey center is just gonna be empty)

tell me if you have any questions about the code and I’ll explain it to you

Edit: Figured out why the sphere wasn’t generating evenly

local Parts = Parts/3 -- amount of parts that shape will be

should of been

local Parts = math.floor(Parts/3 + 0.5) -- amount of parts that shape will be

image

5 Likes

Maybe not a better method, per se, but if you want to make the computation and instantiation as quick as possible, there is a very obvious symmetry you can exploit. You only need to build 1/8 of the sphere and then just clone it to the other 7 quadrants (octants really) with simple reflection of the coordinates. If the dimension is an odd number of blocks, you have to special case the blocks on the bisecting planes so that you don’t calculate or place them redundantly. There is even more symmetry within the 1/8th wedge, but it’s not as trivial as flipping signs, so probably not worth hyper-optimizing for.

You also don’t need to distance check all the voxels, you only need to find where the boundary is. In any given row, you have a continuous run, so if you start from the outside, as soon as you find a block that’s inside the radius, you know the rest of the row is too and can just place all the blocks. The absolute most optimal way is probably to compute the boundary block for the row first, just by line-sphere intersection, rather than by linear search. But the latter is obviously easier to code.

4 Likes

Calculating the distance requires calculating a square root (even if using Vector3.Magnitude), which can be slow. You can calculate the square of the distance from the center, and then compare that to the square of the radius. You only need to the square of the radius once, because it’s always the same. Calculating the square of the distance might be slower because Vector3.Magnitude is a built in function while you’ll have to calculate the square of the distance in Lua.

You can force the calculation of the square of the magnitude of a vector v to be done in C++ with v:Dot(v), which will be ~twice as fast as v.Magnitude^2, and more like 5x as fast as v.x^2+v.y^2+v.z^2 or v.x*v.x+v.y*v.y+v.z*v.z

3 Likes