if SERVER then AddCSLuaFile() return end local bit_band = bit.band local surface_SetDrawColor = surface.SetDrawColor local surface_SetMaterial = surface.SetMaterial local surface_DrawTexturedRectUV = surface.DrawTexturedRectUV local surface_DrawTexturedRect = surface.DrawTexturedRect local render_CopyRenderTargetToTexture = render.CopyRenderTargetToTexture local math_min = math.min local math_max = math.max local DisableClipping = DisableClipping local type = type local SHADERS_VERSION = "SHADERS_VERSION_PLACEHOLDER" local SHADERS_GMA = [========[SHADERS_GMA_PLACEHOLDER]========] do local DECODED_SHADERS_GMA = util.Base64Decode(SHADERS_GMA) if not DECODED_SHADERS_GMA or #DECODED_SHADERS_GMA == 0 then print("Failed to load shaders!") -- this shouldn't happen return end file.Write("rndx_shaders_" .. SHADERS_VERSION .. ".gma", DECODED_SHADERS_GMA) game.MountGMA("data/rndx_shaders_" .. SHADERS_VERSION .. ".gma") end local function GET_SHADER(name) return SHADERS_VERSION:gsub("%.", "_") .. "_" .. name end local BLUR_RT = GetRenderTargetEx("RNDX" .. SHADERS_VERSION .. SysTime(), 1024, 1024, RT_SIZE_LITERAL, MATERIAL_RT_DEPTH_SEPARATE, bit.bor(2, 256, 4, 8 --[[4, 8 is clamp_s + clamp-t]]), 0, IMAGE_FORMAT_BGRA8888 ) local NEW_FLAG; do local flags_n = -1 function NEW_FLAG() flags_n = flags_n + 1 return 2 ^ flags_n end end local NO_TL, NO_TR, NO_BL, NO_BR = NEW_FLAG(), NEW_FLAG(), NEW_FLAG(), NEW_FLAG() -- Svetov/Jaffies's great idea! local SHAPE_CIRCLE, SHAPE_FIGMA, SHAPE_IOS = NEW_FLAG(), NEW_FLAG(), NEW_FLAG() local BLUR = NEW_FLAG() local RNDX = {} local shader_mat = [==[ screenspace_general { $pixshader "" $vertexshader "" $basetexture "" $texture1 "" $texture2 "" $texture3 "" // Mandatory, don't touch $ignorez 1 $vertexcolor 1 $vertextransform 1 " 1 then local inv = 1 / k TL, TR, BL, BR = TL * inv, TR * inv, BL * inv, BR * inv end return clamp0(TL), clamp0(TR), clamp0(BL), clamp0(BR) end end local function SetupDraw() local TL, TR, BL, BR = normalize_corner_radii() local start_rad, sweep_rad local sweep = END_ANGLE - START_ANGLE if sweep >= 360 then start_rad, sweep_rad = 0, -1 -- full circle, shader skips arc math else if sweep < 0 then sweep = sweep + 360 end start_rad = (START_ANGLE % 360) * 0.017453292519943295 sweep_rad = sweep * 0.017453292519943295 end local matrix = MATRIXES[MAT] MATRIX_SetUnpacked( matrix, BL, W, OUTLINE_THICKNESS or -1, sweep_rad, BR, H, SHADOW_INTENSITY, ROTATION, TR, SHAPE, BLUR_INTENSITY or 1.0, 0, TL, TEXTURE and 1 or 0, start_rad, 0 ) MATERIAL_SetMatrix(MAT, "$viewprojmat", matrix) if COL_R then surface_SetDrawColor(COL_R, COL_G, COL_B, COL_A) end surface_SetMaterial(MAT) end local MANUAL_COLOR = NEW_FLAG() local DEFAULT_DRAW_FLAGS = DEFAULT_SHAPE local function draw_rounded(x, y, w, h, col, flags, tl, tr, bl, br, texture, thickness) if col and col.a == 0 then return end RESET_PARAMS() if not flags then flags = DEFAULT_DRAW_FLAGS end local using_blur = bit_band(flags, BLUR) ~= 0 if using_blur then return RNDX.DrawBlur(x, y, w, h, flags, tl, tr, bl, br, thickness) end MAT = ROUNDED_MAT; if texture then MAT = ROUNDED_TEXTURE_MAT MATERIAL_SetTexture(MAT, "$basetexture", texture) TEXTURE = texture end W, H = w, h TL, TR, BL, BR = bit_band(flags, NO_TL) == 0 and tl or 0, bit_band(flags, NO_TR) == 0 and tr or 0, bit_band(flags, NO_BL) == 0 and bl or 0, bit_band(flags, NO_BR) == 0 and br or 0 SHAPE = SHAPES[bit_band(flags, SHAPE_CIRCLE + SHAPE_FIGMA + SHAPE_IOS)] or SHAPES[DEFAULT_SHAPE] OUTLINE_THICKNESS = thickness if bit_band(flags, MANUAL_COLOR) ~= 0 then COL_R = nil elseif col then COL_R, COL_G, COL_B, COL_A = col.r, col.g, col.b, col.a else COL_R, COL_G, COL_B, COL_A = 255, 255, 255, 255 end SetupDraw() -- https://github.com/Jaffies/rboxes/blob/main/rboxes.lua -- fixes setting $basetexture to ""(none) not working correctly return surface_DrawTexturedRectUV(x, y, w, h, -0.015625, -0.015625, 1.015625, 1.015625) end function RNDX.Draw(r, x, y, w, h, col, flags) return draw_rounded(x, y, w, h, col, flags, r, r, r, r) end function RNDX.DrawOutlined(r, x, y, w, h, col, thickness, flags) return draw_rounded(x, y, w, h, col, flags, r, r, r, r, nil, thickness or 1) end function RNDX.DrawTexture(r, x, y, w, h, col, texture, flags) return draw_rounded(x, y, w, h, col, flags, r, r, r, r, texture) end function RNDX.DrawMaterial(r, x, y, w, h, col, mat, flags) local tex = mat:GetTexture("$basetexture") if tex then return RNDX.DrawTexture(r, x, y, w, h, col, tex, flags) end end function RNDX.DrawCircle(x, y, r, col, flags) return RNDX.Draw(r / 2, x - r / 2, y - r / 2, r, r, col, (flags or 0) + SHAPE_CIRCLE) end function RNDX.DrawCircleOutlined(x, y, r, col, thickness, flags) return RNDX.DrawOutlined(r / 2, x - r / 2, y - r / 2, r, r, col, thickness, (flags or 0) + SHAPE_CIRCLE) end function RNDX.DrawCircleTexture(x, y, r, col, texture, flags) return RNDX.DrawTexture(r / 2, x - r / 2, y - r / 2, r, r, col, texture, (flags or 0) + SHAPE_CIRCLE) end function RNDX.DrawCircleMaterial(x, y, r, col, mat, flags) return RNDX.DrawMaterial(r / 2, x - r / 2, y - r / 2, r, r, col, mat, (flags or 0) + SHAPE_CIRCLE) end local USE_SHADOWS_BLUR = false local function draw_blur() if USE_SHADOWS_BLUR then MAT = SHADOWS_BLUR_MAT else MAT = ROUNDED_BLUR_MAT end COL_R, COL_G, COL_B, COL_A = 255, 255, 255, 255 SetupDraw() render_CopyRenderTargetToTexture(BLUR_RT) MATERIAL_SetFloat(MAT, BLUR_VERTICAL, 0) surface_DrawTexturedRect(X, Y, W, H) render_CopyRenderTargetToTexture(BLUR_RT) MATERIAL_SetFloat(MAT, BLUR_VERTICAL, 1) surface_DrawTexturedRect(X, Y, W, H) end function RNDX.DrawBlur(x, y, w, h, flags, tl, tr, bl, br, thickness) RESET_PARAMS() if not flags then flags = DEFAULT_DRAW_FLAGS end X, Y = x, y W, H = w, h TL, TR, BL, BR = bit_band(flags, NO_TL) == 0 and tl or 0, bit_band(flags, NO_TR) == 0 and tr or 0, bit_band(flags, NO_BL) == 0 and bl or 0, bit_band(flags, NO_BR) == 0 and br or 0 SHAPE = SHAPES[bit_band(flags, SHAPE_CIRCLE + SHAPE_FIGMA + SHAPE_IOS)] or SHAPES[DEFAULT_SHAPE] OUTLINE_THICKNESS = thickness draw_blur() end local function setup_shadows() X = X - SHADOW_SPREAD Y = Y - SHADOW_SPREAD W = W + (SHADOW_SPREAD * 2) H = H + (SHADOW_SPREAD * 2) TL = TL + (SHADOW_SPREAD * 2) TR = TR + (SHADOW_SPREAD * 2) BL = BL + (SHADOW_SPREAD * 2) BR = BR + (SHADOW_SPREAD * 2) end local function draw_shadows(r, g, b, a) if USING_BLUR then USE_SHADOWS_BLUR = true draw_blur() USE_SHADOWS_BLUR = false end MAT = SHADOWS_MAT if r == false then COL_R = nil else COL_R, COL_G, COL_B, COL_A = r, g, b, a end SetupDraw() -- https://github.com/Jaffies/rboxes/blob/main/rboxes.lua -- fixes having no $basetexture causing uv to be broken surface_DrawTexturedRectUV(X, Y, W, H, -0.015625, -0.015625, 1.015625, 1.015625) end function RNDX.DrawShadowsEx(x, y, w, h, col, flags, tl, tr, bl, br, spread, intensity, thickness) if col and col.a == 0 then return end local OLD_CLIPPING_STATE = DisableClipping(true) RESET_PARAMS() if not flags then flags = DEFAULT_DRAW_FLAGS end X, Y = x, y W, H = w, h SHADOW_SPREAD = spread or 30 SHADOW_INTENSITY = intensity or SHADOW_SPREAD * 1.2 TL, TR, BL, BR = bit_band(flags, NO_TL) == 0 and tl or 0, bit_band(flags, NO_TR) == 0 and tr or 0, bit_band(flags, NO_BL) == 0 and bl or 0, bit_band(flags, NO_BR) == 0 and br or 0 SHAPE = SHAPES[bit_band(flags, SHAPE_CIRCLE + SHAPE_FIGMA + SHAPE_IOS)] or SHAPES[DEFAULT_SHAPE] OUTLINE_THICKNESS = thickness setup_shadows() USING_BLUR = bit_band(flags, BLUR) ~= 0 if bit_band(flags, MANUAL_COLOR) ~= 0 then draw_shadows(false, nil, nil, nil) elseif col then draw_shadows(col.r, col.g, col.b, col.a) else draw_shadows(0, 0, 0, 255) end DisableClipping(OLD_CLIPPING_STATE) end function RNDX.DrawShadows(r, x, y, w, h, col, spread, intensity, flags) return RNDX.DrawShadowsEx(x, y, w, h, col, flags, r, r, r, r, spread, intensity) end function RNDX.DrawShadowsOutlined(r, x, y, w, h, col, thickness, spread, intensity, flags) return RNDX.DrawShadowsEx(x, y, w, h, col, flags, r, r, r, r, spread, intensity, thickness or 1) end local BASE_FUNCS; BASE_FUNCS = { Rad = function(self, rad) TL, TR, BL, BR = rad, rad, rad, rad return self end, Radii = function(self, tl, tr, bl, br) TL, TR, BL, BR = tl or 0, tr or 0, bl or 0, br or 0 return self end, Texture = function(self, texture) TEXTURE = texture return self end, Material = function(self, mat) local tex = mat:GetTexture("$basetexture") if tex then TEXTURE = tex end return self end, Outline = function(self, thickness) OUTLINE_THICKNESS = thickness return self end, Shape = function(self, shape) SHAPE = SHAPES[shape] or 2.2 return self end, Color = function(self, col_or_r, g, b, a) if type(col_or_r) == "number" then COL_R, COL_G, COL_B, COL_A = col_or_r, g or 255, b or 255, a or 255 else COL_R, COL_G, COL_B, COL_A = col_or_r.r, col_or_r.g, col_or_r.b, col_or_r.a end return self end, Blur = function(self, intensity) if not intensity then intensity = DEFAULT_BLUR_INTENSITY end intensity = math_max(intensity, 0) USING_BLUR, BLUR_INTENSITY = true, intensity return self end, Rotation = function(self, angle) ROTATION = math.rad(angle or 0) return self end, StartAngle = function(self, angle) START_ANGLE = angle or 0 return self end, EndAngle = function(self, angle) END_ANGLE = angle or 360 return self end, Shadow = function(self, spread, intensity) SHADOW_ENABLED, SHADOW_SPREAD, SHADOW_INTENSITY = true, spread or 30, intensity or (spread or 30) * 1.2 return self end, Clip = function(self, pnl) CLIP_PANEL = pnl return self end, Flags = function(self, flags) flags = flags or 0 -- Corner flags if bit_band(flags, NO_TL) ~= 0 then TL = 0 end if bit_band(flags, NO_TR) ~= 0 then TR = 0 end if bit_band(flags, NO_BL) ~= 0 then BL = 0 end if bit_band(flags, NO_BR) ~= 0 then BR = 0 end -- Shape flags local shape_flag = bit_band(flags, SHAPE_CIRCLE + SHAPE_FIGMA + SHAPE_IOS) if shape_flag ~= 0 then SHAPE = SHAPES[shape_flag] or SHAPES[DEFAULT_SHAPE] end -- Blur flag if bit_band(flags, BLUR) ~= 0 then BASE_FUNCS.Blur(self) end -- Manual color flag if bit_band(flags, MANUAL_COLOR) ~= 0 then COL_R = nil end return self end, } local RECT = { Rad = BASE_FUNCS.Rad, Radii = BASE_FUNCS.Radii, Texture = BASE_FUNCS.Texture, Material = BASE_FUNCS.Material, Outline = BASE_FUNCS.Outline, Shape = BASE_FUNCS.Shape, Color = BASE_FUNCS.Color, Blur = BASE_FUNCS.Blur, Rotation = BASE_FUNCS.Rotation, StartAngle = BASE_FUNCS.StartAngle, EndAngle = BASE_FUNCS.EndAngle, Clip = BASE_FUNCS.Clip, Shadow = BASE_FUNCS.Shadow, Flags = BASE_FUNCS.Flags, Draw = function(self) if END_ANGLE == START_ANGLE then return -- nothing to draw end local OLD_CLIPPING_STATE if SHADOW_ENABLED or CLIP_PANEL then -- if we are inside a panel, we need to draw outside of it OLD_CLIPPING_STATE = DisableClipping(true) end if CLIP_PANEL then local sx, sy = CLIP_PANEL:LocalToScreen(0, 0) local sw, sh = CLIP_PANEL:GetSize() render.SetScissorRect(sx, sy, sx + sw, sy + sh, true) end if SHADOW_ENABLED then setup_shadows() draw_shadows(COL_R, COL_G, COL_B, COL_A) elseif USING_BLUR then draw_blur() else if TEXTURE then MAT = ROUNDED_TEXTURE_MAT MATERIAL_SetTexture(MAT, "$basetexture", TEXTURE) end SetupDraw() surface_DrawTexturedRectUV(X, Y, W, H, -0.015625, -0.015625, 1.015625, 1.015625) end if CLIP_PANEL then render.SetScissorRect(0, 0, 0, 0, false) end if SHADOW_ENABLED or CLIP_PANEL then DisableClipping(OLD_CLIPPING_STATE) end end, GetMaterial = function(self) if SHADOW_ENABLED or USING_BLUR then error("You can't get the material of a shadowed or blurred rectangle!") end if TEXTURE then MAT = ROUNDED_TEXTURE_MAT MATERIAL_SetTexture(MAT, "$basetexture", TEXTURE) end SetupDraw() return MAT end, } local CIRCLE = { Texture = BASE_FUNCS.Texture, Material = BASE_FUNCS.Material, Outline = BASE_FUNCS.Outline, Color = BASE_FUNCS.Color, Blur = BASE_FUNCS.Blur, Rotation = BASE_FUNCS.Rotation, StartAngle = BASE_FUNCS.StartAngle, EndAngle = BASE_FUNCS.EndAngle, Clip = BASE_FUNCS.Clip, Shadow = BASE_FUNCS.Shadow, Flags = BASE_FUNCS.Flags, Draw = RECT.Draw, GetMaterial = RECT.GetMaterial, } local TYPES = { Rect = function(x, y, w, h) RESET_PARAMS() MAT = ROUNDED_MAT X, Y, W, H = x, y, w, h return RECT end, Circle = function(x, y, r) RESET_PARAMS() MAT = ROUNDED_MAT SHAPE = SHAPES[SHAPE_CIRCLE] X, Y, W, H = x - r / 2, y - r / 2, r, r r = r / 2 TL, TR, BL, BR = r, r, r, r return CIRCLE end } setmetatable(RNDX, { __call = function() return TYPES end }) -- Flags RNDX.NO_TL = NO_TL RNDX.NO_TR = NO_TR RNDX.NO_BL = NO_BL RNDX.NO_BR = NO_BR RNDX.SHAPE_CIRCLE = SHAPE_CIRCLE RNDX.SHAPE_FIGMA = SHAPE_FIGMA RNDX.SHAPE_IOS = SHAPE_IOS RNDX.BLUR = BLUR RNDX.MANUAL_COLOR = MANUAL_COLOR function RNDX.SetFlag(flags, flag, bool) flag = RNDX[flag] or flag if tobool(bool) then return bit.bor(flags, flag) else return bit.band(flags, bit.bnot(flag)) end end function RNDX.SetDefaultShape(shape) DEFAULT_SHAPE = shape or SHAPE_FIGMA DEFAULT_DRAW_FLAGS = DEFAULT_SHAPE end function RNDX.SetDefaultBlurIntensity(val) DEFAULT_BLUR_INTENSITY = math_max(0, tonumber(val) or 1.0) end function RNDX.GetDefaultBlurIntensity() return DEFAULT_BLUR_INTENSITY end return RNDX