function Invisicam.Cleanup(S)for S,k in pairs(S.savedHits)do S.LocalTransparencyModifier=k end end function Invisicam.Update(w,S,k,F)if not w.enabled or not w.char then return k,F end w.camera=game.Workspace.CurrentCamera if not w.humanoidRootPart then local S=w.char:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if S and S.RootPart then w.humanoidRootPart=S.RootPart else w.humanoidRootPart=w.char:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")if not w.humanoidRootPart then return k,F end end local W W=w.humanoidRootPart.AncestryChanged:Connect(function(S,k)if S==w.humanoidRootPart and not k then w.humanoidRootPart=nil if W and W.Connected then W:Disconnect()W=nil end end end)end if not w.torsoPart then w:CheckTorsoReference()if not w.torsoPart then return k,F end end local W={}w.behaviorFunction(w,W)local p={}local z={w.char}local function a(S)p[S]=true if not w.savedHits[S]then w.savedHits[S]=S.LocalTransparencyModifier end end local f local E=0 local c={}local u={}local Z=TARGET_TRANSPARENCY local G=TARGET_TRANSPARENCY if USE_STACKING_TRANSPARENCY then local S=w.headPart and w.headPart.CFrame.p or W[1]local k=w.torsoPart and w.torsoPart.CFrame.p or W[2]f=w.camera:GetPartsObscuringTarget({S,k},z)for S=1,#f,1 do local k=f[S]E=E+1 c[k]=true for S,k in pairs(k:GetChildren())do if k:IsA("\068\101\099\097\108")or k:IsA("\084\101\120\116\117\114\101")then E=E+1 break end end end if E>0 then Z=math.pow(.5*TARGET_TRANSPARENCY+(.5*TARGET_TRANSPARENCY)/E,1/E)G=math.pow(.5*TARGET_TRANSPARENCY_PERIPHERAL+(.5*TARGET_TRANSPARENCY_PERIPHERAL)/E,1/E)end end f=w.camera:GetPartsObscuringTarget(W,z)local A={}for S=1,#f,1 do local k=f[S]A[k]=c[k]and Z or G if k.Transparency<A[k]then a(k)end for S,F in pairs(k:GetChildren())do if F:IsA("\068\101\099\097\108")or F:IsA("\084\101\120\116\117\114\101")then if F.Transparency<A[k]then A[F]=A[k]a(F)end end end end for S,k in pairs(w.savedHits)do if p[S]then S.LocalTransparencyModifier=S.Transparency<1 and(A[S]-S.Transparency)/(1.0-S.Transparency)or 0 else S.LocalTransparencyModifier=k w.savedHits[S]=nil end end return k,F end return Invisicam
-- Want to turn off Invisicam? Be sure to call this after.
function Invisicam:Cleanup()
	for hit, originalFade in pairs(self.savedHits) do
		hit.LocalTransparencyModifier = originalFade
	end
end

function Invisicam:Update(dt, desiredCameraCFrame, desiredCameraFocus)
	-- Bail if there is no Character
	if not self.enabled or not self.char then
		return desiredCameraCFrame, desiredCameraFocus
	end

	self.camera = game.Workspace.CurrentCamera

	-- TODO: Move this to a GetHumanoidRootPart helper, probably combine with CheckTorsoReference
	-- Make sure we still have a HumanoidRootPart
	if not self.humanoidRootPart then
		local humanoid = self.char:FindFirstChildOfClass("Humanoid")
		if humanoid and humanoid.RootPart then
			self.humanoidRootPart = humanoid.RootPart
		else
			-- Not set up with Humanoid? Try and see if there's one in the Character at all:
			self.humanoidRootPart = self.char:FindFirstChild("HumanoidRootPart")
			if not self.humanoidRootPart then
				-- Bail out, since we're relying on HumanoidRootPart existing
				return desiredCameraCFrame, desiredCameraFocus
			end
		end

		-- TODO: Replace this with something more sensible
		local ancestryChangedConn
		ancestryChangedConn = self.humanoidRootPart.AncestryChanged:Connect(function(child, parent)
			if child == self.humanoidRootPart and not parent then
				self.humanoidRootPart = nil
				if ancestryChangedConn and ancestryChangedConn.Connected then
					ancestryChangedConn:Disconnect()
					ancestryChangedConn = nil
				end
			end
		end)
	end

	if not self.torsoPart then
		self:CheckTorsoReference()
		if not self.torsoPart then
			-- Bail out, since we're relying on Torso existing, should never happen since we fall back to using HumanoidRootPart as torso
			return desiredCameraCFrame, desiredCameraFocus
		end
	end

	-- Make a list of world points to raycast to
	local castPoints = {}
	self.behaviorFunction(self, castPoints)

	-- Cast to get a list of objects between the camera and the cast points
	local currentHits = {}
	local ignoreList = { self.char }
	local function add(hit)
		currentHits[hit] = true
		if not self.savedHits[hit] then
			self.savedHits[hit] = hit.LocalTransparencyModifier
		end
	end

	local hitParts
	local hitPartCount = 0

	-- Hash table to treat head-ray-hit parts differently than the rest of the hit parts hit by other rays
	-- head/torso ray hit parts will be more transparent than peripheral parts when USE_STACKING_TRANSPARENCY is enabled
	local headTorsoRayHitParts = {}
	local partIsTouchingCamera = {}

	local perPartTransparencyHeadTorsoHits = TARGET_TRANSPARENCY
	local perPartTransparencyOtherHits = TARGET_TRANSPARENCY

	if USE_STACKING_TRANSPARENCY then
		-- This first call uses head and torso rays to find out how many parts are stacked up
		-- for the purpose of calculating required per-part transparency
		local headPoint = self.headPart and self.headPart.CFrame.p or castPoints[1]
		local torsoPoint = self.torsoPart and self.torsoPart.CFrame.p or castPoints[2]
		hitParts = self.camera:GetPartsObscuringTarget({ headPoint, torsoPoint }, ignoreList)

		-- Count how many things the sample rays passed through, including decals. This should only
		-- count decals facing the camera, but GetPartsObscuringTarget does not return surface normals,
		-- so my compromise for now is to just let any decal increase the part count by 1. Only one
		-- decal per part will be considered.
		for i = 1, #hitParts do
			local hitPart = hitParts[i]
			hitPartCount = hitPartCount + 1 -- count the part itself
			headTorsoRayHitParts[hitPart] = true
			for _, child in pairs(hitPart:GetChildren()) do
				if child:IsA("Decal") or child:IsA("Texture") then
					hitPartCount = hitPartCount + 1 -- count first decal hit, then break
					break
				end
			end
		end

		if hitPartCount > 0 then
			perPartTransparencyHeadTorsoHits =
				math.pow(((0.5 * TARGET_TRANSPARENCY) + (0.5 * TARGET_TRANSPARENCY / hitPartCount)), 1 / hitPartCount)
			perPartTransparencyOtherHits = math.pow(
				((0.5 * TARGET_TRANSPARENCY_PERIPHERAL) + (0.5 * TARGET_TRANSPARENCY_PERIPHERAL / hitPartCount)),
				1 / hitPartCount
			)
		end
	end

	-- Now get all the parts hit by all the rays
	hitParts = self.camera:GetPartsObscuringTarget(castPoints, ignoreList)

	local partTargetTransparency = {}

	-- Include decals and textures
	for i = 1, #hitParts do
		local hitPart = hitParts[i]

		partTargetTransparency[hitPart] = headTorsoRayHitParts[hitPart] and perPartTransparencyHeadTorsoHits
			or perPartTransparencyOtherHits

		-- If the part is not already as transparent or more transparent than what invisicam requires, add it to the list of
		-- parts to be modified by invisicam
		if hitPart.Transparency < partTargetTransparency[hitPart] then
			add(hitPart)
		end

		-- Check all decals and textures on the part
		for _, child in pairs(hitPart:GetChildren()) do
			if child:IsA("Decal") or child:IsA("Texture") then
				if child.Transparency < partTargetTransparency[hitPart] then
					partTargetTransparency[child] = partTargetTransparency[hitPart]
					add(child)
				end
			end
		end
	end

	-- Invisibilize objects that are in the way, restore those that aren't anymore
	for hitPart, originalLTM in pairs(self.savedHits) do
		if currentHits[hitPart] then
			-- LocalTransparencyModifier gets whatever value is required to print the part's total transparency to equal perPartTransparency
			hitPart.LocalTransparencyModifier = (hitPart.Transparency < 1)
					and ((partTargetTransparency[hitPart] - hitPart.Transparency) / (1.0 - hitPart.Transparency))
				or 0
		else -- Restore original pre-invisicam value of LTM
			hitPart.LocalTransparencyModifier = originalLTM
			self.savedHits[hitPart] = nil
		end
	end

	-- Invisicam does not change the camera values
	return desiredCameraCFrame, desiredCameraFocus
end

return Invisicam
