local screen = require("Screen") local color = require("Color") local filesystem = require("Filesystem") local system = require("System") local GUI = require("GUI") -------------------------------------------------------------------------------- local workspace, wallpaper = select(1, ...), select(2, ...) local configPath = filesystem.path(system.getCurrentScript()) .. "Config.cfg" local config = { backgroundColor = 0x000000, sphereRadius = 20, sphereColor = 0xFFFFFF, spherePos = {20, 0, 0}, lightDistance = 5, lightColor = 0xFFFF40, diffuseIntensity = 12, ambientIntensity = 0.1, specularIntensity = 0.5, specularPower = 1, speed = 5 } if filesystem.exists(configPath) then for key, value in pairs(filesystem.readTable(configPath)) do config[key] = value end end local function saveConfig() filesystem.writeTable(configPath, config) end -------------------------------------------------------------------------------- local function vecNormalize(vec) local length = (vec[1]^2 + vec[2]^2 + vec[3]^2)^0.5 if length < 0.00001 then length = 0.00001 end return { vec[1] / length, vec[2] / length, vec[3] / length } end local function vecSubtract(a, b) return {a[1] - b[1], a[2] - b[2], a[3] - b[3]} end local function vecAdd(a, b) return {a[1] + b[1], a[2] + b[2], a[3] + b[3]} end local function vecScalarProduct(vec, scalar) return {vec[1] * scalar, vec[2] * scalar, vec[3] * scalar} end local function vecHadamardProduct(a, b) return {a[1] * b[1], a[2] * b[2], a[3] * b[3]} end local function vecDotProduct(a, b) return a[1] * b[1] + a[2] * b[2] + a[3] * b[3] end local function vecSqrDistance(a, b) return (a[1] - b[1])^2 + (a[2] - b[2])^2 + (a[3] - b[3])^2 end local function vecIntegerToRGB(integer) local r, g, b = color.integerToRGB(integer) return { r / 0xFF, g / 0xFF, b / 0xFF } end local function clamp(value, min, max) if value < min then return min end if value > max then return max end return value end local function vecRGBToInteger(vec) return color.RGBToInteger( math.floor(clamp(vec[1], 0, 1) * 0xFF), math.floor(clamp(vec[2], 0, 1) * 0xFF), math.floor(clamp(vec[3], 0, 1) * 0xFF) ) end -------------------------------------------------------------------------------- local startTime, lightSpinRadius, sphereRadiusSqr, lightColorVec, sphereColorVec, colorProduct, viewPos = computer.uptime() local function precalculateValues() lightSpinRadius = config.sphereRadius + config.lightDistance sphereRadiusSqr = config.sphereRadius^2 lightColorVec = vecIntegerToRGB(config.lightColor) sphereColorVec = vecIntegerToRGB(config.sphereColor) colorProduct = vecHadamardProduct(lightColorVec, sphereColorVec) viewPos = {0, 0, 500} end precalculateValues() -------------------------------------------------------------------------------- wallpaper.draw = function(wallpaper) local t = config.speed * (computer.uptime() - startTime) / 10 local lightPos = {lightSpinRadius * math.cos(t), lightSpinRadius * 0.5 * math.cos(t), lightSpinRadius * math.sin(t)} local cx, cy = wallpaper.x + math.floor(wallpaper.width / 2), 2 * wallpaper.y + wallpaper.height screen.drawRectangle(wallpaper.x, wallpaper.y, wallpaper.width, wallpaper.height, config.backgroundColor, 0, " ") for x = -config.sphereRadius, config.sphereRadius do for y = -config.sphereRadius, config.sphereRadius do if x^2 + y^2 <= sphereRadiusSqr then local fragPos = {x, y, (config.sphereRadius^2 - x^2 - y^2)^0.5} local L = vecNormalize(vecSubtract(lightPos, fragPos)) local N = vecNormalize(fragPos) local V = vecNormalize(vecSubtract(viewPos, fragPos)) local LN = vecDotProduct(L, N) local Lr = vecNormalize(vecSubtract(vecScalarProduct(N, 2 * LN), L)) local distance = vecSqrDistance(lightPos, fragPos) local diffuse = clamp((LN / distance) * config.diffuseIntensity, 0, 1) local specular = clamp(vecDotProduct(Lr, V), 0, 1)^config.specularPower * config.specularIntensity local fragColor = vecScalarProduct(colorProduct, diffuse) fragColor = vecAdd(fragColor, vecScalarProduct(colorProduct, config.ambientIntensity)) fragColor = vecAdd(fragColor, vecScalarProduct(lightColorVec, specular)) screen.semiPixelSet(cx + x, cy + y, vecRGBToInteger(fragColor)) end end end if lightPos[1]^2 + lightPos[2]^2 > sphereRadiusSqr or lightPos[3] > (config.sphereRadius^2 - lightPos[1]^2 - lightPos[2]^2)^0.5 then screen.semiPixelSet(cx + math.floor(lightPos[1]), cy + math.floor(lightPos[2]), config.lightColor) end end wallpaper.configure = function(layout) local function addColorSelector(configValue, title) layout:addChild(GUI.colorSelector(1, 1, 36, 3, config[configValue], title)).onColorSelected = function(_, object) config[configValue] = object.color saveConfig() precalculateValues() end end local function addSlider(configValue, title, minValue, maxValue, roundValues) local slider = layout:addChild( GUI.slider( 1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, minValue, maxValue, config[configValue], false, title ) ) slider.roundValues = roundValues slider.onValueChanged = function() if roundValues then config[configValue] = math.floor(slider.value) else config[configValue] = slider.value end saveConfig() precalculateValues() end end addColorSelector("backgroundColor", "Background color") addColorSelector("sphereColor", "Sphere color" ) addColorSelector("lightColor", "Light color" ) addSlider("sphereRadius", "Sphere radius: ", 5, 50, true ) addSlider("lightDistance", "Light distance: ", 0, 50, true ) addSlider("diffuseIntensity", "Diffuse intensity: ", 0, 50, false) addSlider("ambientIntensity", "Ambient intensity: ", 0, 1, false) addSlider("specularIntensity", "Specular intensity: ", 0, 5, false) addSlider("specularPower", "Specular power: ", 1, 50, true ) addSlider("speed", "Speed: ", 1, 10, false) end --------------------------------------------------------------------------------