VoxBreaker | An OOP Voxel Destruction module

Its a major work in progress atm, but for one I’ve almost completely removed the “flashing”

8 Likes

that is amazing ur doing the lord’s work rn :sob: :pray:

4 Likes

Yoooo, can you message me the file for this, I wanna try it out and maybe look through the code to see what changed.

How did you manage to eliminate the flashing? What’s different than before? And was this with or without partcache/objectcache btw, this seems really cool.

1 Like

Ive completely rewritten the logic for the module, and its kind of alot to explain so ill spare you the details for now until its fully functional. And this uses object cache. And I would send you a file but there are some major bugs that im still trying to work out.

3 Likes

Woah, that looks so much smoother


im getting this error message how can i fix that? (some parts disappearing entirely without voxels)

1 Like

Literally out here saving so much time for people :sob::pray:

1 Like

Just for clarification, is it supposed to be buttery smooth like jujtusu shenanigans at the moment (meaning its a problem with my script) or is the lag normal. For me at least the game freezez for about 5 seconds when voxelizing LARGE areas bur other than that its usually fine. So im just wondering if lag is expected when voxleizing large areas

Yeah, with larger parts lag is expected. For now I’d just recommend dividing larger parts into smaller ones to circumvent the lag. Even with the newer version im working on there is still some delay on the destruction with large parts

3 Likes

I’m curious, what behavior causes flashing
I meet 2 situation of “Flash”
First 1 is some part flashing


Second 1 is global flashing which the whole part disapper


1 Like
local function ClonePart(block)
	local clone
	if PartCacheEnabled then
		clone = cache:GetPart()
		CopyProperties(block, clone)
	else
		clone = block:Clone()
	end
	return clone
end

local function CreateClones(block, offsetVectors, halfSize, Parent, model, partTable)
	for _, offsetVector in pairs(offsetVectors) do
		local clone = ClonePart(block)
		clone:SetAttribute("Voxel", true)
		if ReplicatedStorage.DebuggingMode.Value then
			clone.Color = Color3.fromRGB(math.random(1, 255), math.random(1, 255), math.random(1, 255))
		end
		clone.Parent = Parent or model
		clone.Size = halfSize
		clone.CFrame += block.CFrame:VectorToWorldSpace((halfSize / 2.0) * offsetVector)
		table.insert(partTable, clone)
	end
end

local function DivideBlock(block : Part, MinimumVoxelSize : number, Parent : Instance, TimeToReset : number) 

	local partTable = {} 
	local minimum = MinimumVoxelSize or miniumCubeSize

	if block.Size.X > minimum or block.Size.Y > minimum or block.Size.Z > minimum then
		if partCanSubdivide(block) then 
			partTable = CutPartinHalf(block, TimeToReset)
		else
			
			local model = block.Parent and block.Parent:IsA("Model") and block.Parent:GetAttribute("VoxelHolder") and block.Parent or Instance.new("Model")
			if not model.Parent then
				model.Name = "VoxelHolder"
				model.Parent = voxelFolder
				model:SetAttribute("VoxelHolder", true)
			end
			
			
			if TimeToReset and TimeToReset >= 0 then
				Destroy(model, TimeToReset)
			end

			local Threshold = 1.5 
			local largest = math.max(block.Size.X, block.Size.Y, block.Size.Z)
			local smallest = math.min(block.Size.X, block.Size.Y, block.Size.Z)

			
			if smallest == block.Size.Y and smallest * Threshold <= largest then
				local offsetVectors = {
					Vector3.new(-1, 0, 1), Vector3.new(1, 0, -1),
					Vector3.new(1, 0, 1), Vector3.new(-1, 0, -1)
				}
				local halfSize = Vector3.new(block.Size.X / 2, block.Size.Y, block.Size.Z / 2)
				CreateClones(block, offsetVectors, halfSize, Parent, model, partTable)

				
			elseif smallest == block.Size.X and smallest * Threshold <= largest then
				local offsetVectors = {
					Vector3.new(0, -1, 1), Vector3.new(0, 1, 1),
					Vector3.new(0, -1, -1), Vector3.new(0, 1, -1)
				}
				local halfSize = Vector3.new(block.Size.X, block.Size.Y / 2, block.Size.Z / 2)
				CreateClones(block, offsetVectors, halfSize, Parent, model, partTable)

				
			elseif smallest == block.Size.Z and smallest * Threshold <= largest then
				local offsetVectors = {
					Vector3.new(1, -1, 0), Vector3.new(1, 1, 0),
					Vector3.new(-1, -1, 0), Vector3.new(-1, 1, 0)
				}
				local halfSize = Vector3.new(block.Size.X / 2, block.Size.Y / 2, block.Size.Z)
				CreateClones(block, offsetVectors, halfSize, Parent, model, partTable)

			else
				local offsetVectors = {
					Vector3.new(1, 1, 1), Vector3.new(1, 1, -1),
					Vector3.new(1, -1, 1), Vector3.new(1, -1, -1),
					Vector3.new(-1, 1, 1), Vector3.new(-1, 1, -1),
					Vector3.new(-1, -1, 1), Vector3.new(-1, -1, -1)
				}
				local halfSize = block.Size / 2.0
				CreateClones(block, offsetVectors, halfSize, Parent, model, partTable)
			end

			
			if block.Parent and block.Parent:IsA("Model") and block.Parent:GetAttribute("VoxelHolder") then
				if PartCacheEnabled then
					for _, v in pairs(block:GetChildren()) do
						v:Destroy()
					end
					for name in pairs(block:GetAttributes()) do
						block:SetAttribute(name, nil)
					end
					cache:ReturnPart(block)
					block.Parent = PartFolder
				else
					block:Destroy()
				end
			else
				if not block:GetAttribute("Transparency") then
					block:SetAttribute("Transparency", block.Transparency)
					block:SetAttribute("Collide", block.CanCollide)
					block:SetAttribute("Query", block.CanQuery)
				end

				block.Transparency = 1
				block.CanCollide = false
				block.CanQuery = false
				block:SetAttribute(TagName, false)

				for _, v in block:GetChildren() do
					task.spawn(function()
						if v:IsA("SurfaceGui") then
							local enabled = v.Enabled
							v.Enabled = false
							repeat rs.Heartbeat:Wait() until model:HasTag("Destroying")
							v.Enabled = enabled
						elseif v:IsA("Decal") or v:IsA("Texture") then
							local transparency = v.Transparency
							v.Transparency = 1
							repeat rs.Heartbeat:Wait() until model:HasTag("Destroying")
							v.Transparency = transparency
						end
					end)
				end

				task.spawn(function()
					if TimeToReset >= 0 then
						repeat rs.Heartbeat:Wait() until model:HasTag("Destroying")
						if block:GetAttribute("Transparency") then
							block.Transparency = block:GetAttribute("Transparency")
						end
						if block:GetAttribute("CanQuery") then
							block.CanQuery = block:GetAttribute("Query")
						end
						if block:GetAttribute("Collide") then
							block.CanCollide = block:GetAttribute("Collide")
						end
						block:SetAttribute(TagName, true)
					end
				end)
			end
		end

	elseif not block.Parent:GetAttribute("VoxelHolder") and not block:GetAttribute("Voxel") then
		if block:GetAttribute("Transparency") then
			block.Transparency = block:GetAttribute("Transparency")
		end
		if block:GetAttribute("CanQuery") then
			block.CanQuery = block:GetAttribute("Query")
		end
		if block:GetAttribute("Collide") then
			block.CanCollide = block:GetAttribute("Collide")
		end

		local clone = ClonePart(block)
		clone.Parent = block.Parent	
		if TimeToReset and TimeToReset >= 0 then
			Destroy(clone, TimeToReset)
		end

		block.Transparency = 1
		block.CanCollide = false
		block.CanQuery = false
		block:SetAttribute(TagName, false)

		for _, v in block:GetChildren() do
			task.spawn(function()
				if v:IsA("SurfaceGui") then
					local enabled = v.Enabled
					v.Enabled = false
					task.wait(TimeToReset)
					v.Enabled = enabled
				elseif v:IsA("Decal") or v:IsA("Texture") then
					local transparency = v.Transparency
					v.Transparency = 1
					task.wait(TimeToReset)
					v.Transparency = transparency
				end
			end)
		end

		task.spawn(function()
			task.wait(TimeToReset)
			if block:GetAttribute("Transparency") then
				block.Transparency = block:GetAttribute("Transparency")
			end
			if block:GetAttribute("CanQuery") then
				block.CanQuery = block:GetAttribute("Query")
			end
			if block:GetAttribute("Collide") then
				block.CanCollide = block:GetAttribute("Collide")
			end
			block:SetAttribute(TagName, true)
		end)
	end

	return partTable
end
1 Like

oh and by the way fix that everything is just flinging you when you use part cache for some reason

When we want to make parts come out of the destruction like jujtusu shenanigans, are we supposed to make new part instances or does the module already have a system that does that?

What do you mean? Either of the hitbox constructors return touching voxels, which you can reference for whatever

So with those returned parts, do i need to clone them and parent them to workspace?

You can do whatever you want with them. If you want to do some heavy physics calculations then yes id recommend cloning the parts on the client and destroying the original ones.

Is the VoxBreaker rework going well?

Its mostly done I’ve just had some complications with other projects which have been taking up my time. As well as one major bug which I’ve been struggling to work out.

4 Likes

how to fix that voxel size is relative to walls size…
изображение
You can see that voxels are longer in one direction and thinner in another…
I want them to be cubic.

Alright so bit of an update, I’m sharing the current state of this new version here:
VoxBreaker 2.0.rbxm (24.3 KB)

I’m not heavily announcing this massive new update yet, as the current version I just sent, is heavily bugged. Though a vast improvement from the previous version of the module. I revamped essentially everything, and It is much more readable and performant.

The main thing that changed was the logic, in that now parts are no longer cloned, but I am instead creating a table of part information that is divided. Essentially, before, the module was repeatedly dividing and cloning parts in a single instant. Now, the module is just repeatedly dividing a table of part information. Since there is no instancing in this process, the performance is heavily increased. This idea was suggested by @527366

Additionally, the module now uses object cache instead of part cache. I made the move due to numerous people suggesting it, as well as object cache generally having better results visually.

And of course, the thing that I’ve been teasing at for a long while, the new algorithm is also here. This new version has support for both the original algorithm, Octrees, and the new algorithm, which I couldn’t really find a name for, but it was sourced from Voxel Destruct
Performance wise, I’m not exactly sure how much more performant this new algorithm is, but it does have major differences visually. The biggest of which is that this algorithm produces perfectly cubic voxels, which is a concern I have received from several people now.

There is one new method, ReturnPart() which allows you to return parts to the cache outside of the module. Use this instead of destroying the voxels.

When you open the module, you may notice a greedy mesher in there, but the module does not currently have greedy meshing capabilities. I haven’t had the time to make a greedy meshing function that works, but I have found a way to implement greedy meshing without there being major loads on performance.

And something else I shared previously, was the improvements to part resetting. The logic for part resetting has been rewritten completely, and is now almost perfectly seemless.

Also, the module now uses tags instead of attributes.

Overall, performance wise, this is a massive improvement to the previous version. But like I said, there are 2 major bugs, and several features I have yet to add which are already in the previous version of the module. The first of these bugs is that chunks of parts are not getting created, leaving holes between voxels. The second is that moveable hitboxes are not detecting some voxels. Both of these are 100% logic errors, I’ve just been struggling to figure them out.
And then this new version is missing key features, such as textures being cloned onto voxels, and setting reset times below zero, which would normally prevent voxels from resetting entirely.

The reason why I’m releasing this entirely unfinished version is due to a lack of time for me to work on this module. I am currently balancing several projects with college, and I have had very little time to work on this module. I have also had a lot of trouble in figuring out the current bugs, and so instead of agonizing over fixing them, I’ve decided to just release it in it’s current state since I know some of you have been waiting quite a while for this.

The complete version of this may take a long while to be fully completed due to how little time I have, and given how unfinished it is, I am only sharing this here. The github, marketplace model, and test place have not been updated, and you can still get the previous version of the module in any of those places.

7 Likes