123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- --[[
- MIT License
- Copyright (c) 2019 Mitchell Davis <coding.jackalope@gmail.com>
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- --]]
- local Stats = require(SLAB_PATH .. ".Internal.Core.Stats")
- local DrawCommands = {}
- local LayerTable = {}
- local PendingBatches = {}
- local ActiveBatch = nil
- local Types =
- {
- Rect = 1,
- Triangle = 2,
- Text = 3,
- Scissor = 4,
- TransformPush = 5,
- TransformPop = 6,
- ApplyTransform = 7,
- Check = 8,
- Line = 9,
- TextFormatted = 10,
- IntersectScissor = 11,
- Cross = 12,
- Image = 13,
- SubImage = 14,
- Circle = 15,
- DrawCanvas = 16,
- Mesh = 17,
- TextObject = 18,
- Curve = 19,
- Polygon = 20
- }
- local Layers =
- {
- Normal = 1,
- ContextMenu = 2,
- MainMenuBar = 3,
- Dialog = 4,
- Debug = 5
- }
- local ActiveLayer = Layers.Normal
- local StatsCategory = 'Slab Draw'
- local function AddArc(Verts, CenterX, CenterY, Radius, Angle1, Angle2, Segments, X, Y)
- if Radius == 0 then
- table.insert(Verts, CenterX + X)
- table.insert(Verts, CenterY + Y)
- return
- end
- local Step = (Angle2 - Angle1) / Segments
- for Theta = Angle1, Angle2, Step do
- local Radians = math.rad(Theta)
- table.insert(Verts, math.sin(Radians) * Radius + CenterX + X)
- table.insert(Verts, math.cos(Radians) * Radius + CenterY + Y)
- end
- end
- local function GetLayerDebugInfo(Layer)
- local Result = {}
- Result['Channel Count'] = #Layer
- local Channels = {}
- for K, Channel in pairs(Layer) do
- local Collection = {}
- Collection['Batch Count'] = #Channel
- table.insert(Channels, Collection)
- end
- Result['Channels'] = Channels
- return Result
- end
- local function DrawRect(Rect)
- local StatHandle = Stats.Begin('DrawRect', StatsCategory)
- love.graphics.setColor(Rect.Color)
- love.graphics.rectangle(Rect.Mode, Rect.X, Rect.Y, Rect.Width, Rect.Height, Rect.Radius, Rect.Radius)
- Stats.End(StatHandle)
- end
- local function GetTriangleVertices(X, Y, Radius, Rotation)
- local Result = {}
- local Radians = math.rad(Rotation)
- local X1, Y1 = 0, -Radius
- local X2, Y2 = -Radius, Radius
- local X3, Y3 = Radius, Radius
- local PX1 = X1 * math.cos(Radians) - Y1 * math.sin(Radians)
- local PY1 = Y1 * math.cos(Radians) + X1 * math.sin(Radians)
- local PX2 = X2 * math.cos(Radians) - Y2 * math.sin(Radians)
- local PY2 = Y2 * math.cos(Radians) + X2 * math.sin(Radians)
- local PX3 = X3 * math.cos(Radians) - Y3 * math.sin(Radians)
- local PY3 = Y3 * math.cos(Radians) + X3 * math.sin(Radians)
- Result =
- {
- X + PX1, Y + PY1,
- X + PX2, Y + PY2,
- X + PX3, Y + PY3
- }
-
- return Result
- end
- local function DrawTriangle(Triangle)
- local StatHandle = Stats.Begin('DrawTriangle', StatsCategory)
- love.graphics.setColor(Triangle.Color)
- local Vertices = GetTriangleVertices(Triangle.X, Triangle.Y, Triangle.Radius, Triangle.Rotation)
- love.graphics.polygon(Triangle.Mode, Vertices)
- Stats.End(StatHandle)
- end
- local function DrawCheck(Check)
- local StatHandle = Stats.Begin('DrawCheck', StatsCategory)
- love.graphics.setColor(Check.Color)
- local Vertices =
- {
- Check.X - Check.Radius * 0.5, Check.Y,
- Check.X, Check.Y + Check.Radius,
- Check.X + Check.Radius, Check.Y - Check.Radius
- }
- love.graphics.line(Vertices)
- Stats.End(StatHandle)
- end
- local function DrawText(Text)
- local StatHandle = Stats.Begin('DrawText', StatsCategory)
- love.graphics.setFont(Text.Font)
- love.graphics.setColor(Text.Color)
- love.graphics.print(Text.Text, Text.X, Text.Y)
- Stats.End(StatHandle)
- end
- local function DrawTextFormatted(Text)
- local StatHandle = Stats.Begin('DrawTextFormatted', StatsCategory)
- love.graphics.setFont(Text.Font)
- love.graphics.setColor(Text.Color)
- love.graphics.printf(Text.Text, Text.X, Text.Y, Text.W, Text.Align)
- Stats.End(StatHandle)
- end
- local function DrawTextObject(Text)
- local StatHandle = Stats.Begin('DrawTextObject', StatsCategory)
- love.graphics.setColor(1, 1, 1, 1)
- love.graphics.draw(Text.Text, Text.X, Text.Y)
- Stats.End(StatHandle)
- end
- local function DrawLine(Line)
- local StatHandle = Stats.Begin('DrawLine', StatsCategory)
- love.graphics.setColor(Line.Color)
- local LineW = love.graphics.getLineWidth()
- love.graphics.setLineWidth(Line.Width)
- love.graphics.line(Line.X1, Line.Y1, Line.X2, Line.Y2)
- love.graphics.setLineWidth(LineW)
- Stats.End(StatHandle)
- end
- local function DrawCross(Cross)
- local StatHandle = Stats.Begin('DrawCross', StatsCategory)
- local X, Y = Cross.X, Cross.Y
- local R = Cross.Radius
- love.graphics.setColor(Cross.Color)
- love.graphics.line(X - R, Y - R, X + R, Y + R)
- love.graphics.line(X - R, Y + R, X + R, Y - R)
- Stats.End(StatHandle)
- end
- local function DrawImage(Image)
- local StatHandle = Stats.Begin('DrawImage', StatsCategory)
- love.graphics.setColor(Image.Color)
- love.graphics.draw(Image.Image, Image.X, Image.Y, Image.Rotation, Image.ScaleX, Image.ScaleY)
- Stats.End(StatHandle)
- end
- local function DrawSubImage(Image)
- local StatHandle = Stats.Begin('DrawSubImage', StatsCategory)
- love.graphics.setColor(Image.Color)
- love.graphics.draw(Image.Image, Image.Quad, Image.Transform)
- Stats.End(StatHandle)
- end
- local function DrawCircle(Circle)
- local StatHandle = Stats.Begin('DrawCircle', StatsCategory)
- love.graphics.setColor(Circle.Color)
- love.graphics.circle(Circle.Mode, Circle.X, Circle.Y, Circle.Radius, Circle.Segments)
- Stats.End(StatHandle)
- end
- local function DrawCurve(Curve)
- local StatHandle = Stats.Begin('DrawCurve', StatsCategory)
- love.graphics.setColor(Curve.Color)
- love.graphics.line(Curve.Points)
- Stats.End(StatHandle)
- end
- local function DrawPolygon(Polygon)
- local StatHandle = Stats.Begin('DrawPolygon', StatsCategory)
- love.graphics.setColor(Polygon.Color)
- love.graphics.polygon(Polygon.Mode, Polygon.Points)
- Stats.End(StatHandle)
- end
- local function DrawCanvas(Canvas)
- local StatHandle = Stats.Begin('DrawCanvas', StatsCategory)
- love.graphics.setBlendMode('alpha', 'premultiplied')
- love.graphics.setColor(1.0, 1.0, 1.0, 1.0)
- love.graphics.draw(Canvas.Canvas, Canvas.X, Canvas.Y)
- love.graphics.setBlendMode('alpha')
- Stats.End(StatHandle)
- end
- local function DrawMesh(Mesh)
- local StatHandle = Stats.Begin('DrawMesh', StatsCategory)
- love.graphics.setColor(1.0, 1.0, 1.0, 1.0)
- love.graphics.draw(Mesh.Mesh, Mesh.X, Mesh.Y)
- Stats.End(StatHandle)
- end
- local function DrawElements(Elements)
- local StatHandle = Stats.Begin('Draw Elements', StatsCategory)
- for K, V in pairs(Elements) do
- if V.Type == Types.Rect then
- DrawRect(V)
- elseif V.Type == Types.Triangle then
- DrawTriangle(V)
- elseif V.Type == Types.Text then
- DrawText(V)
- elseif V.Type == Types.Scissor then
- love.graphics.setScissor(V.X, V.Y, V.W, V.H)
- elseif V.Type == Types.TransformPush then
- love.graphics.push()
- elseif V.Type == Types.TransformPop then
- love.graphics.pop()
- elseif V.Type == Types.ApplyTransform then
- love.graphics.applyTransform(V.Transform)
- elseif V.Type == Types.Check then
- DrawCheck(V)
- elseif V.Type == Types.Line then
- DrawLine(V)
- elseif V.Type == Types.TextFormatted then
- DrawTextFormatted(V)
- elseif V.Type == Types.IntersectScissor then
- love.graphics.intersectScissor(V.X, V.Y, V.W, V.H)
- elseif V.Type == Types.Cross then
- DrawCross(V)
- elseif V.Type == Types.Image then
- DrawImage(V)
- elseif V.Type == Types.SubImage then
- DrawSubImage(V)
- elseif V.Type == Types.Circle then
- DrawCircle(V)
- elseif V.Type == Types.DrawCanvas then
- DrawCanvas(V)
- elseif V.Type == Types.Mesh then
- DrawMesh(V)
- elseif V.Type == Types.TextObject then
- DrawTextObject(V)
- elseif V.Type == Types.Curve then
- DrawCurve(V)
- elseif V.Type == Types.Polygon then
- DrawPolygon(V)
- end
- end
- Stats.End(StatHandle)
- end
- local function AssertActiveBatch()
- assert(ActiveBatch ~= nil, "DrawCommands.Begin was not called before commands were issued!")
- end
- local function DrawLayer(Layer, Name)
- if Layer.Channels == nil then
- return
- end
- local StatHandle = Stats.Begin('Draw Layer ' .. Name, StatsCategory)
- local Keys = {}
- for K, Channel in pairs(Layer.Channels) do
- table.insert(Keys, K)
- end
- table.sort(Keys)
- for Index, C in ipairs(Keys) do
- local Channel = Layer.Channels[C]
- if Channel ~= nil then
- for I, V in ipairs(Channel) do
- DrawElements(V.Elements)
- end
- end
- end
- Stats.End(StatHandle)
- end
- function DrawCommands.Reset()
- LayerTable = {}
- LayerTable[Layers.Normal] = {}
- LayerTable[Layers.ContextMenu] = {}
- LayerTable[Layers.MainMenuBar] = {}
- LayerTable[Layers.Dialog] = {}
- LayerTable[Layers.Debug] = {}
- ActiveLayer = Layers.Normal
- PendingBatches = {}
- ActiveBatch = nil
- end
- function DrawCommands.Begin(Options)
- Options = Options == nil and {} or Options
- Options.Channel = Options.Channel == nil and 1 or Options.Channel
- if LayerTable[ActiveLayer].Channels == nil then
- LayerTable[ActiveLayer].Channels = {}
- end
- if LayerTable[ActiveLayer].Channels[Options.Channel] == nil then
- LayerTable[ActiveLayer].Channels[Options.Channel] = {}
- end
- local Channel = LayerTable[ActiveLayer].Channels[Options.Channel]
- ActiveBatch = {}
- ActiveBatch.Elements = {}
- table.insert(Channel, ActiveBatch)
- table.insert(PendingBatches, 1, ActiveBatch)
- end
- function DrawCommands.End()
- if ActiveBatch ~= nil then
- love.graphics.setScissor()
- table.remove(PendingBatches, 1)
- ActiveBatch = nil
- if #PendingBatches > 0 then
- ActiveBatch = PendingBatches[1]
- end
- end
- end
- function DrawCommands.SetLayer(Layer)
- if Layer == 'Normal' then
- ActiveLayer = Layers.Normal
- elseif Layer == 'ContextMenu' then
- ActiveLayer = Layers.ContextMenu
- elseif Layer == 'MainMenuBar' then
- ActiveLayer = Layers.MainMenuBar
- elseif Layer == 'Dialog' then
- ActiveLayer = Layers.Dialog
- elseif Layer == 'Debug' then
- ActiveLayer = Layers.Debug
- end
- end
- function DrawCommands.Rectangle(Mode, X, Y, Width, Height, Color, Radius, Segments)
- AssertActiveBatch()
- if type(Radius) == 'table' then
- Segments = Segments == nil and 10 or Segments
- local Verts = {}
- local TL = Radius[1]
- local TR = Radius[2]
- local BR = Radius[3]
- local BL = Radius[4]
- TL = TL == nil and 0 or TL
- TR = TR == nil and 0 or TR
- BR = BR == nil and 0 or BR
- BL = BL == nil and 0 or BL
- AddArc(Verts, Width - BR, Height - BR, BR, 0, 90, Segments, X, Y)
- AddArc(Verts, Width - TR, TR, TR, 90, 180, Segments, X, Y)
- AddArc(Verts, TL, TL, TL, 180, 270, Segments, X, Y)
- AddArc(Verts, BL, Height - BL, BL, 270, 360, Segments, X, Y)
- DrawCommands.Polygon(Mode, Verts, Color)
- else
- local Item = {}
- Item.Type = Types.Rect
- Item.Mode = Mode
- Item.X = X
- Item.Y = Y
- Item.Width = Width
- Item.Height = Height
- Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
- Item.Radius = Radius and Radius or 0.0
- table.insert(ActiveBatch.Elements, Item)
- end
- end
- function DrawCommands.Triangle(Mode, X, Y, Radius, Rotation, Color)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Triangle
- Item.Mode = Mode
- Item.X = X
- Item.Y = Y
- Item.Radius = Radius
- Item.Rotation = Rotation
- Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Print(Text, X, Y, Color, Font)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Text
- Item.Text = Text
- Item.X = X
- Item.Y = Y
- Item.Color = Color and Color or {1.0, 1.0, 1.0, 1.0}
- Item.Font = Font
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Printf(Text, X, Y, W, Align, Color, Font)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.TextFormatted
- Item.Text = Text
- Item.X = X
- Item.Y = Y
- Item.W = W
- Item.Align = Align and Align or 'left'
- Item.Color = Color and Color or {1.0, 1.0, 1.0, 1.0}
- Item.Font = Font
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Scissor(X, Y, W, H)
- AssertActiveBatch()
- if W ~= nil then
- assert(W >= 0.0, "Cannot set scissor with negative width.")
- end
- if H ~= nil then
- assert(H >= 0.0, "Cannot set scissor with negative height.")
- end
- local Item = {}
- Item.Type = Types.Scissor
- Item.X = X
- Item.Y = Y
- Item.W = W
- Item.H = H
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.IntersectScissor(X, Y, W, H)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.IntersectScissor
- Item.X = X and X or 0.0
- Item.Y = Y and Y or 0.0
- Item.W = W and W or 0.0
- Item.H = H and H or 0.0
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.TransformPush()
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.TransformPush
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.TransformPop()
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.TransformPop
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.ApplyTransform(Transform)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.ApplyTransform
- Item.Transform = Transform
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Check(X, Y, Radius, Color)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Check
- Item.X = X
- Item.Y = Y
- Item.Radius = Radius
- Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Line(X1, Y1, X2, Y2, Width, Color)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Line
- Item.X1 = X1
- Item.Y1 = Y1
- Item.X2 = X2
- Item.Y2 = Y2
- Item.Width = Width
- Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Cross(X, Y, Radius, Color)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Cross
- Item.X = X
- Item.Y = Y
- Item.Radius = Radius
- Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Image(X, Y, Image, Rotation, ScaleX, ScaleY, Color)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Image
- Item.X = X
- Item.Y = Y
- Item.Image = Image
- Item.Rotation = Rotation
- Item.ScaleX = ScaleX
- Item.ScaleY = ScaleY
- Item.Color = Color and Color or {1.0, 1.0, 1.0, 1.0}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.SubImage(X, Y, Image, SX, SY, SW, SH, Rotation, ScaleX, ScaleY, Color)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.SubImage
- Item.Transform = love.math.newTransform(X, Y, Rotation, ScaleX, ScaleY)
- Item.Image = Image
- Item.Quad = love.graphics.newQuad(SX, SY, SW, SH, Image:getWidth(), Image:getHeight())
- Item.Color = Color and Color or {1.0, 1.0, 1.0, 1.0}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Circle(Mode, X, Y, Radius, Color, Segments)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Circle
- Item.Mode = Mode
- Item.X = X
- Item.Y = Y
- Item.Radius = Radius
- Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
- Item.Segments = Segments and Segments or 24
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.DrawCanvas(Canvas, X, Y)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.DrawCanvas
- Item.Canvas = Canvas
- Item.X = X
- Item.Y = Y
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Mesh(Mesh, X, Y)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Mesh
- Item.Mesh = Mesh
- Item.X = X
- Item.Y = Y
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Text(Text, X, Y)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.TextObject
- Item.Text = Text
- Item.X = X
- Item.Y = Y
- Item.Color = {0, 0, 0, 1}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Curve(Points, Color)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Curve
- Item.Points = Points
- Item.Color = Color ~= nil and Color or {0, 0, 0, 1}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Polygon(Mode, Points, Color)
- AssertActiveBatch()
- local Item = {}
- Item.Type = Types.Polygon
- Item.Mode = Mode
- Item.Points = Points
- Item.Color = Color ~= nil and Color or {0, 0, 0, 1}
- table.insert(ActiveBatch.Elements, Item)
- end
- function DrawCommands.Execute()
- local StatHandle = Stats.Begin('Execute', StatsCategory)
- DrawLayer(LayerTable[Layers.Normal], 'Normal')
- DrawLayer(LayerTable[Layers.ContextMenu], 'ContextMenu')
- DrawLayer(LayerTable[Layers.MainMenuBar], 'MainMenuBar')
- DrawLayer(LayerTable[Layers.Dialog], 'Dialog')
- DrawLayer(LayerTable[Layers.Debug], 'Debug')
- Stats.End(StatHandle)
- end
- function DrawCommands.GetDebugInfo()
- local Result = {}
- Result['Normal'] = GetLayerDebugInfo(LayerTable[Layers.Normal])
- Result['ContextMenu'] = GetLayerDebugInfo(LayerTable[Layers.ContextMenu])
- Result['MainMenuBar'] = GetLayerDebugInfo(LayerTable[Layers.MainMenuBar])
- Result['Dialog'] = GetLayerDebugInfo(LayerTable[Layers.Dialog])
- Result['Debug'] = GetLayerDebugInfo(LayerTable[Layers.Debug])
- return Result
- end
- return DrawCommands
|