local RunService = game:GetService("RunService") local Players = game:GetService("Players") local player = Players.LocalPlayer local camera = workspace.CurrentCamera local CONFIG = { UpdateInterval = 0.15, MaxDistance = 180, NearDistance = 70, FOVAngle = 90, MinSize = 5, MaxObjectsPerFrame = 15, MaxTotalObjects = 300, RegisterDelay = 0.5, } local trackedObjects = {} local objectLookup = {} local lastUpdate = 0 local currentIndex = 1 local isRegistering = false local excludedClasses = { "Terrain", "Camera", "Sound", "Script", "LocalScript", "ModuleScript", "Attachment", "Constraint", "WeldConstraint", "Motor6D", "Decal", "Texture" } local excludedNames = { ["Baseplate"] = true, ["Terrain"] = true, } local function shouldExclude(obj) if not obj:IsA("BasePart") then return true end for _, class in ipairs(excludedClasses) do if obj:IsA(class) then return true end end if excludedNames[obj.Name] then return true end if obj.Transparency >= 0.98 then return true end if obj.Size.Magnitude < CONFIG.MinSize then return true end local model = obj:FindFirstAncestorOfClass("Model") if model then if Players:GetPlayerFromCharacter(model) then return true end if model:FindFirstChildOfClass("Humanoid") then return true end end return false end local function registerObject(obj) if shouldExclude(obj) then return end if objectLookup[obj] then return end if #trackedObjects >= CONFIG.MaxTotalObjects then return end local tracked = { obj = obj, origTrans = obj.Transparency, origColl = obj.CanCollide, visible = true, } table.insert(trackedObjects, tracked) objectLookup[obj] = tracked end local function isInFOV(objPos, camPos, camLook) local toObject = (objPos - camPos) local distance = toObject.Magnitude if distance < 10 then return true end toObject = toObject.Unit local dot = camLook:Dot(toObject) return dot > 0.3 end local function setVisibility(tracked, visible) if not tracked or not tracked.obj or not tracked.obj.Parent then return end if tracked.visible == visible then return end local obj = tracked.obj local success = pcall(function() if visible then obj.Transparency = tracked.origTrans obj.CanCollide = tracked.origColl else obj.Transparency = 1 obj.CanCollide = false end end) if success then tracked.visible = visible end end local function performCulling() if not camera or not camera.Parent then return end local camPos = camera.CFrame.Position local camLook = camera.CFrame.LookVector local totalObjects = #trackedObjects if totalObjects == 0 then return end for i = 1, CONFIG.MaxObjectsPerFrame do if currentIndex > totalObjects then currentIndex = 1 break end local tracked = trackedObjects[currentIndex] if tracked and tracked.obj and tracked.obj.Parent then local objPos = tracked.obj.Position local distance = (camPos - objPos).Magnitude local shouldBeVisible = distance <= CONFIG.MaxDistance if shouldBeVisible and distance > CONFIG.NearDistance then shouldBeVisible = isInFOV(objPos, camPos, camLook) end setVisibility(tracked, shouldBeVisible) end currentIndex = currentIndex + 1 end end local function cleanupInvalidObjects() for i = #trackedObjects, 1, -1 do local tracked = trackedObjects[i] if not tracked.obj or not tracked.obj.Parent then objectLookup[tracked.obj] = nil table.remove(trackedObjects, i) end end end local function batchRegister() if isRegistering then return end isRegistering = true local count = 0 for _, obj in ipairs(workspace:GetDescendants()) do if count >= CONFIG.MaxTotalObjects then break end registerObject(obj) count = count + 1 if count % 50 == 0 then task.wait(0.2) end end isRegistering = false print("✓ Occlusion: " .. #trackedObjects .. " objetos registrados") end local pendingObjects = {} local lastRegisterTime = 0 workspace.DescendantAdded:Connect(function(obj) if isRegistering then return end if #trackedObjects >= CONFIG.MaxTotalObjects then return end table.insert(pendingObjects, obj) end) task.spawn(function() while task.wait(CONFIG.RegisterDelay) do if #pendingObjects > 0 and not isRegistering then local currentTime = tick() if currentTime - lastRegisterTime >= CONFIG.RegisterDelay then local toRegister = math.min(#pendingObjects, 10) for i = 1, toRegister do local obj = table.remove(pendingObjects, 1) if obj and obj.Parent then registerObject(obj) end end lastRegisterTime = currentTime end end end end) workspace.DescendantRemoving:Connect(function(obj) if objectLookup[obj] then objectLookup[obj] = nil end end) task.spawn(function() while task.wait(15) do if not isRegistering then cleanupInvalidObjects() end end end) RunService.RenderStepped:Connect(function() local currentTime = tick() if currentTime - lastUpdate >= CONFIG.UpdateInterval then performCulling() lastUpdate = currentTime end end) task.spawn(batchRegister) _G.OcclusionStats = function() local visible = 0 for _, tracked in ipairs(trackedObjects) do if tracked.visible then visible = visible + 1 end end return { Total = #trackedObjects, Visible = visible, Hidden = #trackedObjects - visible, Pending = #pendingObjects, } end print("✓ Occlusion Culling Optimizado")