------------------------------------------------------------------------------------------------ -- Initialization ------------------------------------------------------------------------------------------------ local Debris = game:GetService("Debris") local Players = game:GetService("Players") local RunService = game:GetService("RunService") local ServerStorage = game:GetService("ServerStorage") local ReplicatedStorage = game:GetService("ReplicatedStorage") local CollectionService = game:GetService("CollectionService") local function getFlag(name) local flag = ServerStorage:FindFirstChild(name) return (flag and flag:IsA("BoolValue") and flag.Value) end local enableBevels = getFlag("EnableBevels") local debugMode = getFlag("DevTestMode") local bevelData = Instance.new("StringValue") bevelData.Name = "BevelData" bevelData.Archivable = false local bevelCache = ServerStorage:FindFirstChild("BevelCache") local bevelsReady = bevelCache and bevelCache:FindFirstChild("BevelsReady") if not bevelCache then bevelCache = Instance.new("Folder") bevelCache.Name = "BevelCache" bevelCache.Parent = ServerStorage end if not bevelsReady then bevelsReady = Instance.new("BoolValue") bevelsReady.Name = "BevelsReady" bevelsReady.Parent = bevelCache bevelsReady.Archivable = false end if not enableBevels then bevelData.Parent = ReplicatedStorage bevelsReady.Value = true return else local ids = {} local idMap = {} for _,part in pairs(bevelCache:GetChildren()) do if part:IsA("MeshPart") then local id = part.MeshId if not idMap[id] then idMap[id] = true table.insert(ids, id) end end end bevelData.Value = table.concat(ids, ";") bevelData.Parent = ReplicatedStorage end local regen = ServerStorage:FindFirstChild("Regeneration") if regen then local ready = regen:WaitForChild("Ready") while not ready.Value do ready.Changed:Wait() end end local loadBuildTools = ServerStorage:FindFirstChild("LoadBuildTools") local hasBuildTools = (loadBuildTools ~= nil) ------------------------------------------------------------------------------------------------ local edgeDepth = 1 / 15 local cornerDepth = edgeDepth * math.sqrt(8 / 3) local mirrorProps = { "Anchored", "CanCollide", "CastShadow", "CFrame", "CollisionGroupId", "CustomPhysicalProperties", "Color", "Locked", "Material", "Name", "Reflectance", "RotVelocity", "Transparency", "Velocity", } local surfaceProps = { "ParamA", "ParamB", "Surface", "SurfaceInput" } local bevelHash = "%.2f ~ %.2f ~ %.2f" local isStudio = RunService:IsStudio() local negateBase = Instance.new("Part") negateBase.Name = "__negateplane" negateBase.CanCollide = false negateBase.BottomSurface = 0 negateBase.Transparency = 1 negateBase.Anchored = true negateBase.TopSurface = 0 negateBase.Locked = true CollectionService:AddTag(negateBase, "NoBevels") for _,normalId in pairs(Enum.NormalId:GetEnumItems()) do local name = normalId.Name for _,surfaceProp in pairs(surfaceProps) do table.insert(mirrorProps, name .. surfaceProp) end end ------------------------------------------------------------------------------------------------ local overload = 0 local threshold = Vector3.new(30, 30, 30) if ServerStorage:FindFirstChild("BevelThreshold") then threshold = ServerStorage.BevelThreshold.Value end local function debugPrint(...) if debugMode then warn("[BEVELS DEBUG]:", ...) end end local function isPartOfHumanoid(object) local model = object:FindFirstAncestorOfClass("Model") if model then if model:FindFirstChildOfClass("Humanoid") then return true else return isPartOfHumanoid(model) end end return false end local function canGiveBevels(part) if part.Parent and part:IsA("Part") and not CollectionService:HasTag(part, "NoBevels") then if not isPartOfHumanoid(part) and not part:FindFirstChildWhichIsA("DataModelMesh") then local inThreshold = false local diff = threshold - part.Size if diff.X >= 0 and diff.Y >= 0 and diff.Z >= 0 then inThreshold = true end if inThreshold then if CollectionService:HasTag(part, "ForceBevels") then return true else return part.Shape.Name == "Block" and part.Transparency < 1 end end end end return false end local function createProxyPart(part, name, tag, sizeChange) local proxyPart = Instance.new("Part") proxyPart.Name = name proxyPart.Locked = true proxyPart.TopSurface = 0 proxyPart.Massless = true proxyPart.Transparency = 1 proxyPart.BottomSurface = 0 proxyPart.CanCollide = false proxyPart.CFrame = part.CFrame local size = part.Size if sizeChange then size = size + sizeChange end local proxyWeld = Instance.new("Weld") proxyWeld.Name = "ProxyWeld" proxyWeld.Part1 = proxyPart proxyWeld.Part0 = part if hasBuildTools then local mesh = Instance.new("SpecialMesh") mesh.Scale = size * 20 mesh.MeshType = "Brick" mesh.Offset = part.Size mesh.Parent = proxyPart proxyPart.Size = Vector3.new(.05, .05, .05) proxyWeld.C0 = CFrame.new(-mesh.Offset) else proxyPart.Size = part.Size end CollectionService:AddTag(proxyPart, tag) CollectionService:AddTag(proxyPart, "NoBevels") CollectionService:AddTag(proxyWeld, "GorillaGlue") proxyWeld.Parent = proxyPart proxyPart.Parent = part return proxyPart end local function createBevels(part, initializing) if not canGiveBevels(part) or isPartOfHumanoid(part) then return end local size = part.Size local sx, sy, sz = size.X, size.Y, size.Z local bevelKey = bevelHash:format(sx, sy, sz) local debugBox if debugMode then debugBox = Instance.new("BoxHandleAdornment") debugBox.Color3 = Color3.new(0, 2, 2) debugBox.AlwaysOnTop = true debugBox.Name = "DebugBox" debugBox.Size = size debugBox.ZIndex = 0 debugBox.Adornee = part debugBox.Parent = part end if not bevelCache:FindFirstChild(bevelKey) then local halfSize = size / 2 local planeScale = math.max(sx, sy, sz) local planes = {} local solverPart = part:Clone() solverPart.CFrame = CFrame.new() solverPart.BrickColor = BrickColor.new(-1) debugPrint("Solving:", bevelKey) for x = -1, 1 do local x0 = (x == 0) for y = -1, 1 do local y0 = (y == 0) for z = -1, 1 do local z0 = (z == 0) local isCenter = (x0 and y0 and z0) local isFace = ((x0 and y0) or (y0 and z0) or (z0 and x0)) if not (isCenter or isFace) then local isCorner = (not x0 and not y0 and not z0) local depth = isCorner and cornerDepth or edgeDepth local offset = Vector3.new(x, y, z) local cornerPos = (halfSize * offset) local plane = negateBase:Clone() plane.CFrame = CFrame.new(cornerPos, cornerPos + offset) plane.Size = Vector3.new(planeScale, planeScale, depth) plane.Parent = part table.insert(planes, plane) end end end end local success, union = pcall(function () return solverPart:SubtractAsync(planes, "Box") end) if success then union.Name = bevelKey union.UsePartColor = true union.Parent = bevelCache CollectionService:AddTag(union, "HasBevels") if debugBox then debugBox.Color3 = Color3.new(0, 2, 0) end elseif debugBox then debugBox.Color3 = Color3.new(2, 0, 0) end for _,plane in pairs(planes) do plane:Destroy() end overload = 0 else if debugBox then debugBox.Color3 = Color3.new(2, 0, 2) end overload = overload + 1 if overload % 10 == 0 then RunService.Heartbeat:Wait() end end local baseUnion = bevelCache:FindFirstChild(bevelKey) if baseUnion then local archivable = baseUnion.Archivable baseUnion.Archivable = true local union = baseUnion:Clone() baseUnion.Archivable = archivable for _,prop in ipairs(mirrorProps) do union[prop] = part[prop] end for _,joint in pairs(part:GetJoints()) do if joint:IsA("JointInstance") or joint:IsA("WeldConstraint") then if joint.Part0 == part then joint.Part0 = union elseif joint.Part1 == part then joint.Part1 = union end end end for _,child in pairs(part:GetChildren()) do if not child:IsA("TouchTransmitter") and not child:IsA("Texture") then if child:IsA("BaseScript") then child.Disabled = true end child.Parent = union if child:IsA("BaseScript") then child.Disabled = false end end end if not initializing then wait() end if CollectionService:HasTag(part, "DoUnlock") then union.Locked = false end if part.ClassName ~= "Part" then local holder = Instance.new("Weld") holder.Part0 = part holder.Part1 = union holder.Parent = part union.Anchored = false union.Massless = true union.Parent = part part.Transparency = 1 CollectionService:AddTag(holder, "GorillaGlue") else local parent = part.Parent part:Destroy() union.Parent = parent end elseif debugBox then debugBox.Color3 = Color3.new(2, 0, 0) end if debugBox then debugBox.Transparency = 0.5 Debris:AddItem(debugBox, 2) end end ------------------------------------------------------------------------------------------------ do warn("Solving bevels...") -- Collect all blocks currently in the workspace. local initialPass = {} local debugHint for _,desc in pairs(workspace:GetDescendants()) do if canGiveBevels(desc) then if not desc.Locked then CollectionService:AddTag(desc, "DoUnlock") desc.Locked = true end table.insert(initialPass, desc) end end if debugMode then debugHint = Instance.new("Hint") debugHint.Text = "Generating Bevels..." debugHint.Parent = workspace end -- Run through the initial bevel creation phase. for _,block in ipairs(initialPass) do createBevels(block, true) end if debugHint then debugHint:Destroy() end end -- Listen for new parts being added. workspace.DescendantAdded:Connect(createBevels) -- Allow regeneration to request bevel solving local bevelSolver = bevelCache:FindFirstChild("RequestSolve") if not bevelSolver then bevelSolver = Instance.new("BindableFunction") bevelSolver.Name = "RequestSolve" bevelSolver.Parent = bevelCache bevelSolver.Archivable = false end function bevelSolver.OnInvoke(inst) for _,desc in pairs(inst:GetDescendants()) do if desc:IsA("Part") then createBevels(desc) end end end if RunService:IsStudio() then local exportBin = Instance.new("Folder") exportBin.Name = "ExportBin" exportBin.Parent = ServerStorage for _,v in pairs(bevelCache:GetChildren()) do if v:IsA("TriangleMeshPart") and v.Archivable then v:Clone().Parent = exportBin end end wait(.1) for _,v in pairs(exportBin:GetChildren()) do if v:FindFirstChild("LOD") then v.LOD:Destroy() end end end -- Ready! warn("Bevels ready!") bevelsReady.Value = true ------------------------------------------------------------------------------------------------