--[[ Created By: Razorboots [[ This is an entire lighting engine simulated using raycasts and regular diffuse lighting calculations. A tutorial on how to get started can be found here: https://devforum.roblox.com/t/custom-baked-lighting-%E2%80%9Cengine%E2%80%9D/1748754 Have fun! ]] --# Point local lightmap = {} --# Services local RunService = game:GetService('RunService') local Lighting = game:GetService("Lighting") local ReplicatedStorage = game:GetService("ReplicatedStorage") --# References local Lightmap_Quad = script:WaitForChild("Lightmap_Quad") local Lightmap_Canvas = script:WaitForChild("Lightmap_Canvas") --# Starter Functions local function updateLightmapScales(newScale) lightmap.defaultRenderScale = newScale lightmap.renderScale = lightmap.defaultRenderScale lightmap.halfRenderScale = lightmap.renderScale*0.5 lightmap.doubleRenderScale = lightmap.renderScale*2 lightmap.extendedRenderScale = lightmap.doubleRenderScale + (lightmap.renderScale*.6) lightmap.finalRenderScale = (lightmap.renderScale+lightmap.extendedRenderScale)*48 lightmap.halfFinalRenderScale = lightmap.finalRenderScale/2 end --# Variables updateLightmapScales(2) lightmap.surfaceScale = 0.0001 lightmap.textureId = "rbxassetid://9300220537" raydist = 200 epsilon = 0.02 sundir = Lighting:GetSunDirection() lightmap.shadowDarkness = .35 lightmap.ambientMultiplier = 1.5 lightmap.shadowColor = Color3.new(0.0823529, 0.0941176, 0.117647) lightmap.ambientColor = Color3.new(0.239216, 0.2, 0.168627) lightmap.maxIterationTime = -1 -- If the script execution time goes over this number, then pause. Set this to -1 if you do not want your script to wait if the script to yield. lightmap.iterationYieldTime = 2 -- Pause time ^ lightmap.canvases = {} --# Functions local function rayCast(part, origin, direction, distance, ignoreList) local raycastParams = ignoreList local raycastResult = workspace:Raycast(origin, direction * distance, raycastParams) return raycastResult end local function calculateSurfaceLighting(origin, norm) local n = norm local l = sundir.Unit local r = (l - n * 2 * l:Dot(n)).Unit local diffuse = lightmap.ambientMultiplier * n:Dot(l) diffuse = math.max(diffuse, lightmap.shadowDarkness) return diffuse end local function calculateLocalSurfaceLighting(part, origin, norm, lightSource, lightSourceParentPart, lightIgnoreList) --origin = origin + (norm*-0.05) local result = rayCast(lightSourceParentPart, origin, (lightSourceParentPart.Position - origin), 1, lightIgnoreList) if result == nil then local distance = (lightSourceParentPart.Position - origin).Magnitude * (1 / lightSource.Brightness ) local attenuation = 1 / (1 + 0.09 * distance + 0.032 * (distance * distance)) return attenuation end end local lefts = { [Enum.NormalId.Top] = Vector3.FromNormalId(Enum.NormalId.Left), [Enum.NormalId.Back] = Vector3.FromNormalId(Enum.NormalId.Left), [Enum.NormalId.Right] = Vector3.FromNormalId(Enum.NormalId.Back), [Enum.NormalId.Bottom] = Vector3.FromNormalId(Enum.NormalId.Right), [Enum.NormalId.Front] = Vector3.FromNormalId(Enum.NormalId.Right), [Enum.NormalId.Left] = Vector3.FromNormalId(Enum.NormalId.Front) } function getTopLeft(hit, sid) local lnormal = Vector3.FromNormalId(sid) local cf = hit.CFrame + (hit.CFrame:vectorToWorldSpace(lnormal * (hit.Size/2))); local modi = (sid == Enum.NormalId.Top or sid == Enum.NormalId.Bottom) and -1 or 1; local left = lefts[sid]; local up = modi * left:Cross(lnormal); local tlcf = cf + hit.CFrame:vectorToWorldSpace((up + left) * hit.Size/2); -- returns: corner, 2D size, right vector, down vector, modification number (used for flipping top and bottom values which are special) return tlcf, Vector2.new((left * hit.Size).magnitude, (up * hit.Size).magnitude), hit.CFrame:vectorToWorldSpace(-left), hit.CFrame:vectorToWorldSpace(-up), modi; end function getSurfacePos(pos, ppart, sid, elementSize) local tlc, size, right, down, modi = getTopLeft(ppart, sid); -- convert to 2D local relative = pos - tlc.p; local x, y = right:Dot(relative)/size.x, down:Dot(relative)/size.y; x, y = modi < 1 and y or x, modi < 1 and x or y; return UDim2.new(x, -elementSize, y, -elementSize) end local function createLightMapPointFromInfo(lightMapPointInfo, canvas, ppart) if lightMapPointInfo then local quad = Lightmap_Quad:Clone() quad.Size = UDim2.new(0, lightmap.finalRenderScale, 0, lightmap.finalRenderScale) quad.Position = getSurfacePos(lightMapPointInfo[1].CFrame.p, ppart, canvas.Face, lightmap.halfFinalRenderScale) quad.ImageColor3 = Color3.new(lightMapPointInfo[2].Color3.R/canvas.Brightness, lightMapPointInfo[2].Color3.G/canvas.Brightness, lightMapPointInfo[2].Color3.B/canvas.Brightness) quad.ImageTransparency = lightMapPointInfo[2].Transparency quad.Image = lightmap.textureId quad.Parent = canvas end end local function createLightMapPoint(rayPoint, surfaceNormal) local lightmapPoint = { CFrame = CFrame.new(rayPoint.Position, rayPoint.Position + (surfaceNormal*lightmap.surfaceScale)) * CFrame.Angles(math.rad(-90), 0, 0), Color = Color3.new(), Transparency = 1 } local decal = { Color3 = Color3.new(0, 0, 0), Transparency = 0 } return lightmapPoint, decal end local function renderMapPoint(renderType, renderGlobalShadows, part, rayPoint, surfaceNormal, diffuseColor, lightSources, lightIgnoreList) local raycastResult = nil if renderGlobalShadows == true then raycastResult = rayCast(part, rayPoint.Position + (surfaceNormal*epsilon), sundir, raydist, lightIgnoreList) end local lightmapPoint = nil local decal = nil local inShadow = false if renderType == "FullLightMap" then lightmapPoint, decal = createLightMapPoint(rayPoint, surfaceNormal) end if raycastResult then if renderType == "OnlyShadow" or renderType == "Shadows+AllLights" then lightmapPoint, decal = createLightMapPoint(rayPoint, surfaceNormal) end inShadow = true decal.Transparency = lightmap.shadowDarkness decal.Color3 = lightmap.shadowColor else if renderType == "FullLightMap" then decal.Transparency = diffuseColor decal.Color3 = lightmap.ambientColor end end if renderGlobalShadows == false then if renderType == "FullLightMap" or renderType == "Shadows+AllLights" then if decal ~= nil then decal.Transparency = lightmap.shadowDarkness decal.Color3 = lightmap.shadowColor end end end if renderType == "OnlyShadow" and inShadow == true and renderGlobalShadows == true then if decal then decal.Transparency = math.min(decal.Transparency, 0.9) end return {lightmapPoint, decal} end if renderType == "OnlyShadow" and inShadow == false and renderGlobalShadows == true then if decal then decal.Transparency = math.min(decal.Transparency, 0.9) end return {lightmapPoint, decal} end if lightSources~= nil and #lightSources > 0 then for _, lightSource in pairs(lightSources) do if (rayPoint.Position - lightSource.Parent.Position).Magnitude <= lightSource.Range then if renderType == "Shadows+AllLights" then if inShadow == false then inShadow = true if renderGlobalShadows == false then lightmapPoint, decal = createLightMapPoint(rayPoint, surfaceNormal) decal.Transparency = lightmap.shadowDarkness decal.Color3 = lightmap.shadowColor end end end if renderType == "FullLightMap" or renderType == "Shadows+AllLights" then local localDiffuse = calculateLocalSurfaceLighting(part, rayPoint.Position + (surfaceNormal*epsilon), surfaceNormal, lightSource, lightSource.Parent, lightIgnoreList) if localDiffuse and decal then if renderType == "Shadows+AllLights" then if inShadow == false then inShadow = true if renderGlobalShadows == true then lightmapPoint, decal = createLightMapPoint(rayPoint, surfaceNormal) decal.Color3 = Color3.new(decal.Color3.R + math.min(lightSource.Color.R, (lightSource.Color.R)*localDiffuse), decal.Color3.G + math.min(lightSource.Color.G, (lightSource.Color.G)*localDiffuse), decal.Color3.B + math.min(lightSource.Color.B, (lightSource.Color.B)*localDiffuse)) decal.Transparency = localDiffuse end end end decal.Color3 = Color3.new(decal.Color3.R + math.min(lightSource.Color.R, (lightSource.Color.R)*localDiffuse), decal.Color3.G + math.min(lightSource.Color.G, (lightSource.Color.G)*localDiffuse), decal.Color3.B + math.min(lightSource.Color.B, (lightSource.Color.B)*localDiffuse)) decal.Transparency += localDiffuse end elseif renderType == "OnlyShadow" then local localOrigin = rayPoint.Position + (surfaceNormal*epsilon) if rayCast(lightSource.Parent, localOrigin, (lightSource.Parent.Position - localOrigin), 1, lightIgnoreList) ~= nil then if inShadow == false and decal == nil then inShadow = true lightmapPoint, decal = createLightMapPoint(rayPoint, surfaceNormal) decal.Transparency = lightmap.shadowDarkness decal.Color3 = lightmap.shadowColor break end end end end end end if decal then decal.Transparency = math.min(decal.Transparency, 0.9) end --decal.Color3 = Color3.new(math.min(decal.Color3.R, lightmap.ambientColor.R), math.min(decal.Color3.G, lightmap.ambientColor.G), math.min(decal.Color3.B, lightmap.ambientColor.B)) return {lightmapPoint, decal} end local function renderMapSurfaceParallel(mainScript, part, iterationScales, xyzFactors, relativePartOrigin, surfaceNormal, diffuseColor, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) for xCoord = lightmap.renderScale, iterationScales[1], lightmap.renderScale do local actor = Instance.new("Actor") mainScript.Parent = actor local bindable = Instance.new("BindableEvent") bindable.Parent = mainScript.Parent local coords = {} local startT = tick() bindable.Event:ConnectParallel(function() for zCoord = lightmap.renderScale, iterationScales[2], lightmap.renderScale do if lightmap.maxIterationTime ~= -1 then if tick() - startT > lightmap.maxIterationTime then wait(lightmap.iterationYieldTime) startT = tick() end end local vals = {0, 0, 0} for i, factor in pairs(xyzFactors) do if factor == "x" then vals[i] = xCoord elseif factor == "z" then vals[i] = zCoord end end --local rayPoint = relativePartOrigin * CFrame.new(xCoord, 0, zCoord) local rayPoint = relativePartOrigin * CFrame.new(vals[1], vals[2], vals[3]) coords[tostring(zCoord)] = {} local lightMapPointInfo = renderMapPoint(renderType, renderGlobalShadows, part, rayPoint, surfaceNormal, diffuseColor, lightSources, lightIgnoreList) coords[tostring(zCoord)].lightMapPointInfo = lightMapPointInfo end task.synchronize() for zCoord = lightmap.renderScale, iterationScales[2], lightmap.renderScale do if coords[tostring(zCoord)].lightMapPointInfo ~= nil then if coords[tostring(zCoord)].lightMapPointInfo[1] ~= nil then createLightMapPointFromInfo(coords[tostring(zCoord)].lightMapPointInfo, canvas, part) actor:Destroy() bindable:Destroy() end end end RunService.Stepped:Wait() end) bindable:Fire() end end --# Surface Functions function lightmap.renderTopMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) updateLightmapScales(renderScale) local size = Vector3.new(part.Size.X + lightmap.doubleRenderScale, part.Size.Y, part.Size.Z + lightmap.doubleRenderScale) local originPoint = part.CFrame * CFrame.new(size.X/2, size.Y/2, size.Z/2) local surfaceNormal = ((part.CFrame * CFrame.new(0, 1, 0)).Position - part.CFrame.Position).Unit local surfaceSize = Vector3.new(lightmap.renderScale+lightmap.extendedRenderScale, lightmap.surfaceScale, lightmap.renderScale+lightmap.extendedRenderScale) local relativePartOrigin = part.CFrame * CFrame.new(-size.X/2 - lightmap.halfRenderScale, size.Y/2, -size.Z/2 - lightmap.halfRenderScale) local diffuseColor = 0 if renderGlobalShadows == nil and renderGlobalShadows ~= false then renderGlobalShadows = true end renderType = renderType or "FullLightMap" if renderType == "FullLightMap" then diffuseColor = calculateSurfaceLighting(originPoint, surfaceNormal) end renderMapSurfaceParallel(mainScript, part, {size.X, size.Z}, {"x", nil, "z"}, relativePartOrigin, surfaceNormal, diffuseColor, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) canvas.Parent = part end function lightmap.renderBottomMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) updateLightmapScales(renderScale) local size = Vector3.new(part.Size.X + lightmap.doubleRenderScale, part.Size.Y, part.Size.Z + lightmap.doubleRenderScale) local originPoint = part.CFrame * CFrame.new(size.X/2, -size.Y/2, size.Z/2) local surfaceNormal = ((part.CFrame * CFrame.new(0, -1, 0)).Position - part.CFrame.Position).Unit local surfaceSize = Vector3.new(lightmap.renderScale+lightmap.extendedRenderScale, lightmap.surfaceScale, lightmap.renderScale+lightmap.extendedRenderScale) local relativePartOrigin = part.CFrame * CFrame.new(-size.X/2 - lightmap.halfRenderScale, -size.Y/2, -size.Z/2 - lightmap.halfRenderScale) local diffuseColor = 0 if renderGlobalShadows == nil and renderGlobalShadows ~= false then renderGlobalShadows = true end renderType = renderType or "FullLightMap" if renderType == "FullLightMap" then diffuseColor = calculateSurfaceLighting(originPoint, surfaceNormal) end renderMapSurfaceParallel(mainScript, part, {size.X, size.Z}, {"x", nil, "z"}, relativePartOrigin, surfaceNormal, diffuseColor, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) canvas.Parent = part end function lightmap.renderFrontMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) updateLightmapScales(renderScale) local size = Vector3.new(part.Size.X + lightmap.doubleRenderScale, part.Size.Y + lightmap.doubleRenderScale, part.Size.Z) local originPoint = part.CFrame * CFrame.new(size.X/2, size.Y/2, -size.Z/2) local surfaceNormal = ((part.CFrame * CFrame.new(0, 0, -1)).Position - part.CFrame.Position).Unit --local surfaceSize = Vector3.new(lightmap.renderScale+lightmap.extendedRenderScale, lightmap.renderScale+lightmap.extendedRenderScale, lightmap.surfaceScale) local relativePartOrigin = part.CFrame * CFrame.new(-size.X/2 - lightmap.halfRenderScale, -size.Y/2 - lightmap.halfRenderScale, -size.Z/2) local diffuseColor = 0 if renderGlobalShadows == nil and renderGlobalShadows ~= false then renderGlobalShadows = true end renderType = renderType or "FullLightMap" if renderType == "FullLightMap" then diffuseColor = calculateSurfaceLighting(originPoint, surfaceNormal) end renderMapSurfaceParallel(mainScript, part, {size.X, size.Y}, {"x", "z", nil}, relativePartOrigin, surfaceNormal, diffuseColor, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) canvas.Parent = part end function lightmap.renderBackMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) updateLightmapScales(renderScale) local size = Vector3.new(part.Size.X + lightmap.doubleRenderScale, part.Size.Y + lightmap.doubleRenderScale, part.Size.Z) local originPoint = part.CFrame * CFrame.new(size.X/2, size.Y/2, size.Z/2) local surfaceNormal = ((part.CFrame * CFrame.new(0, 0, 1)).Position - part.CFrame.Position).Unit --local surfaceSize = Vector3.new(lightmap.renderScale+lightmap.extendedRenderScale, lightmap.renderScale+lightmap.extendedRenderScale, lightmap.surfaceScale) local relativePartOrigin = part.CFrame * CFrame.new(-size.X/2 - lightmap.halfRenderScale, -size.Y/2 - lightmap.halfRenderScale, size.Z/2) local diffuseColor = 0 if renderGlobalShadows == nil and renderGlobalShadows ~= false then renderGlobalShadows = true end renderType = renderType or "FullLightMap" if renderType == "FullLightMap" then diffuseColor = calculateSurfaceLighting(originPoint, surfaceNormal) end renderMapSurfaceParallel(mainScript, part, {size.X, size.Y}, {"x", "z", nil}, relativePartOrigin, surfaceNormal, diffuseColor, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) canvas.Parent = part end function lightmap.renderRightMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) updateLightmapScales(renderScale) local size = Vector3.new(part.Size.X, part.Size.Y + lightmap.doubleRenderScale, part.Size.Z + lightmap.doubleRenderScale) local originPoint = part.CFrame * CFrame.new(size.X/2, size.Y/2, size.Z/2) local surfaceNormal = ((part.CFrame * CFrame.new(1, 0, 0)).Position - part.CFrame.Position).Unit --local surfaceSize = Vector3.new(lightmap.surfaceScale, lightmap.renderScale+lightmap.extendedRenderScale, lightmap.renderScale+lightmap.extendedRenderScale) local relativePartOrigin = part.CFrame * CFrame.new(size.X/2, -size.Y/2 - lightmap.halfRenderScale, -size.Z/2 - lightmap.halfRenderScale) local diffuseColor = 0 if renderGlobalShadows == nil and renderGlobalShadows ~= false then renderGlobalShadows = true end renderType = renderType or "FullLightMap" if renderType == "FullLightMap" then diffuseColor = calculateSurfaceLighting(originPoint, surfaceNormal) end renderMapSurfaceParallel(mainScript, part, {size.Y, size.Z}, {0, "x", "z"}, relativePartOrigin, surfaceNormal, diffuseColor, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) canvas.Parent = part end function lightmap.renderLeftMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) updateLightmapScales(renderScale) local size = Vector3.new(part.Size.X, part.Size.Y + lightmap.doubleRenderScale, part.Size.Z + lightmap.doubleRenderScale) local originPoint = part.CFrame * CFrame.new(size.X/2, size.Y/2, size.Z/2) local surfaceNormal = ((part.CFrame * CFrame.new(-1, 0, 0)).Position - part.CFrame.Position).Unit local surfaceSize = Vector3.new(lightmap.surfaceScale, lightmap.renderScale+lightmap.extendedRenderScale, lightmap.renderScale+lightmap.extendedRenderScale) local relativePartOrigin = part.CFrame * CFrame.new(-size.X/2, -size.Y/2 - lightmap.halfRenderScale, -size.Z/2 - lightmap.halfRenderScale) local diffuseColor = 0 if renderGlobalShadows == nil and renderGlobalShadows ~= false then renderGlobalShadows = true end renderType = renderType or "FullLightMap" if renderType == "FullLightMap" then diffuseColor = calculateSurfaceLighting(originPoint, surfaceNormal) end renderMapSurfaceParallel(mainScript, part, {size.Y, size.Z}, {0, "x", "z"}, relativePartOrigin, surfaceNormal, diffuseColor, lightSources, renderScale, renderType, renderGlobalShadows, lightIgnoreList, canvas) canvas.Parent = part end --# Execute function getLightmapCanvas(part, surfaceName) if surfaceName == "Top" then surfaceName = Enum.NormalId.Top elseif surfaceName == "Bottom" then surfaceName = Enum.NormalId.Bottom elseif surfaceName == "Right" then surfaceName = Enum.NormalId.Right elseif surfaceName == "Left" then surfaceName = Enum.NormalId.Left elseif surfaceName == "Front" then surfaceName = Enum.NormalId.Front elseif surfaceName == "Back" then surfaceName = Enum.NormalId.Back end for _, surface in pairs(part:GetChildren()) do if surface:IsA("SurfaceGui") then if surface.Name == "Lightmap_Canvas" and surface.Face == surfaceName then if #surface:GetChildren() > 0 then surface:ClearAllChildren() end return surface end end end local canvas = Lightmap_Canvas:Clone() canvas.Face = surfaceName table.insert(lightmap.canvases, canvas) return canvas end function lightmap.renderAllSurfaces(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows) lightmap.renderScale = renderScale or lightmap.defaultRenderScale local lightIgnoreList = {} for _, lightSource in pairs(lightSources) do table.insert(lightIgnoreList, lightSource.Parent) end local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = lightIgnoreList raycastParams.FilterType = Enum.RaycastFilterType.Blacklist if part:IsA("BasePart") then lightmap.renderTopMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Top")) lightmap.renderBottomMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Bottom")) lightmap.renderRightMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Right")) lightmap.renderLeftMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Left")) lightmap.renderFrontMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Front")) lightmap.renderBackMap(mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Back")) end for _, subPart in pairs(part:GetDescendants()) do if subPart:IsA("BasePart") then lightmap.renderTopMap(mainScript, subPart, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Top")) lightmap.renderBottomMap(mainScript, subPart, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Bottom")) lightmap.renderRightMap(mainScript, subPart, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Right")) lightmap.renderLeftMap(mainScript, subPart, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Left")) lightmap.renderFrontMap(mainScript, subPart, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Front")) lightmap.renderBackMap(mainScript, subPart, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, "Back")) end end end function lightmap.renderSpecificSurfaces(mainScript, surfaces, part, lightSources, renderScale, renderType, renderGlobalShadows) local lightIgnoreList = {} for _, lightSource in pairs(lightSources) do table.insert(lightIgnoreList, lightSource.Parent) end local raycastParams = RaycastParams.new() raycastParams.FilterDescendantsInstances = lightIgnoreList raycastParams.FilterType = Enum.RaycastFilterType.Blacklist if part:IsA("BasePart") then for _, surface in pairs(surfaces) do lightmap["render"..surface.."Map"](mainScript, part, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, surface)) end end for _, subPart in pairs(part:GetDescendants()) do if subPart:IsA("BasePart") then for _, surface in pairs(surfaces) do lightmap["render"..surface.."Map"](mainScript, subPart, lightSources, renderScale, renderType, renderGlobalShadows, raycastParams, getLightmapCanvas(part, surface)) end end end end function lightmap.removeAllLightmaps() for _, canvas in pairs(lightmap.canvases) do if canvas then canvas:Destroy() end end lightmap.canvases = {} end --# Finalize return lightmap