DrawCommands.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. --[[
  2. MIT License
  3. Copyright (c) 2019 Mitchell Davis <coding.jackalope@gmail.com>
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  18. SOFTWARE.
  19. --]]
  20. local DrawCommands = {}
  21. local LayerTable = {}
  22. local PendingBatches = {}
  23. local ActiveBatch = nil
  24. local Types =
  25. {
  26. Rect = 1,
  27. Triangle = 2,
  28. Text = 3,
  29. Scissor = 4,
  30. TransformPush = 5,
  31. TransformPop = 6,
  32. ApplyTransform = 7,
  33. Check = 8,
  34. Line = 9,
  35. TextFormatted = 10,
  36. IntersectScissor = 11,
  37. Cross = 12,
  38. Image = 13,
  39. SubImage = 14,
  40. Circle = 15,
  41. DrawCanvas = 16,
  42. Mesh = 17,
  43. TextObject = 18,
  44. Curve = 19,
  45. Polygon = 20
  46. }
  47. local Layers =
  48. {
  49. Normal = 1,
  50. ContextMenu = 2,
  51. MainMenuBar = 3,
  52. Dialog = 4,
  53. Debug = 5
  54. }
  55. local ActiveLayer = Layers.Normal
  56. local function AddArc(Verts, CenterX, CenterY, Radius, Angle1, Angle2, Segments, X, Y)
  57. if Radius == 0 then
  58. table.insert(Verts, CenterX + X)
  59. table.insert(Verts, CenterY + Y)
  60. return
  61. end
  62. local Step = (Angle2 - Angle1) / Segments
  63. for Theta = Angle1, Angle2, Step do
  64. local Radians = math.rad(Theta)
  65. table.insert(Verts, math.sin(Radians) * Radius + CenterX + X)
  66. table.insert(Verts, math.cos(Radians) * Radius + CenterY + Y)
  67. end
  68. end
  69. local function GetLayerDebugInfo(Layer)
  70. local Result = {}
  71. Result['Channel Count'] = #Layer
  72. local Channels = {}
  73. for K, Channel in pairs(Layer) do
  74. local Collection = {}
  75. Collection['Batch Count'] = #Channel
  76. table.insert(Channels, Collection)
  77. end
  78. Result['Channels'] = Channels
  79. return Result
  80. end
  81. local function DrawRect(Rect)
  82. love.graphics.setColor(Rect.Color)
  83. love.graphics.rectangle(Rect.Mode, Rect.X, Rect.Y, Rect.Width, Rect.Height, Rect.Radius, Rect.Radius)
  84. end
  85. local function GetTriangleVertices(X, Y, Radius, Rotation)
  86. local Result = {}
  87. local Radians = math.rad(Rotation)
  88. local X1, Y1 = 0, -Radius
  89. local X2, Y2 = -Radius, Radius
  90. local X3, Y3 = Radius, Radius
  91. local PX1 = X1 * math.cos(Radians) - Y1 * math.sin(Radians)
  92. local PY1 = Y1 * math.cos(Radians) + X1 * math.sin(Radians)
  93. local PX2 = X2 * math.cos(Radians) - Y2 * math.sin(Radians)
  94. local PY2 = Y2 * math.cos(Radians) + X2 * math.sin(Radians)
  95. local PX3 = X3 * math.cos(Radians) - Y3 * math.sin(Radians)
  96. local PY3 = Y3 * math.cos(Radians) + X3 * math.sin(Radians)
  97. Result =
  98. {
  99. X + PX1, Y + PY1,
  100. X + PX2, Y + PY2,
  101. X + PX3, Y + PY3
  102. }
  103. return Result
  104. end
  105. local function DrawTriangle(Triangle)
  106. love.graphics.setColor(Triangle.Color)
  107. local Vertices = GetTriangleVertices(Triangle.X, Triangle.Y, Triangle.Radius, Triangle.Rotation)
  108. love.graphics.polygon(Triangle.Mode, Vertices)
  109. end
  110. local function DrawCheck(Check)
  111. love.graphics.setColor(Check.Color)
  112. local Vertices =
  113. {
  114. Check.X - Check.Radius * 0.5, Check.Y,
  115. Check.X, Check.Y + Check.Radius,
  116. Check.X + Check.Radius, Check.Y - Check.Radius
  117. }
  118. love.graphics.line(Vertices)
  119. end
  120. local function DrawText(Text)
  121. love.graphics.setFont(Text.Font)
  122. love.graphics.setColor(Text.Color)
  123. love.graphics.print(Text.Text, Text.X, Text.Y)
  124. end
  125. local function DrawTextFormatted(Text)
  126. love.graphics.setFont(Text.Font)
  127. love.graphics.setColor(Text.Color)
  128. love.graphics.printf(Text.Text, Text.X, Text.Y, Text.W, Text.Align)
  129. end
  130. local function DrawTextObject(Text)
  131. love.graphics.setColor(Text.Color)
  132. love.graphics.draw(Text.Text, Text.X, Text.Y)
  133. end
  134. local function DrawLine(Line)
  135. love.graphics.setColor(Line.Color)
  136. local LineW = love.graphics.getLineWidth()
  137. love.graphics.setLineWidth(Line.Width)
  138. love.graphics.line(Line.X1, Line.Y1, Line.X2, Line.Y2)
  139. love.graphics.setLineWidth(LineW)
  140. end
  141. local function DrawCross(Cross)
  142. local X, Y = Cross.X, Cross.Y
  143. local R = Cross.Radius
  144. love.graphics.setColor(Cross.Color)
  145. love.graphics.line(X - R, Y - R, X + R, Y + R)
  146. love.graphics.line(X - R, Y + R, X + R, Y - R)
  147. end
  148. local function DrawImage(Image)
  149. love.graphics.setColor(Image.Color)
  150. love.graphics.draw(Image.Image, Image.X, Image.Y, Image.Rotation, Image.ScaleX, Image.ScaleY)
  151. end
  152. local function DrawSubImage(Image)
  153. love.graphics.setColor(Image.Color)
  154. love.graphics.draw(Image.Image, Image.Quad, Image.Transform)
  155. end
  156. local function DrawCircle(Circle)
  157. love.graphics.setColor(Circle.Color)
  158. love.graphics.circle(Circle.Mode, Circle.X, Circle.Y, Circle.Radius, Circle.Segments)
  159. end
  160. local function DrawCurve(Curve)
  161. love.graphics.setColor(Curve.Color)
  162. love.graphics.line(Curve.Points)
  163. end
  164. local function DrawPolygon(Polygon)
  165. love.graphics.setColor(Polygon.Color)
  166. love.graphics.polygon(Polygon.Mode, Polygon.Points)
  167. end
  168. local function DrawElements(Elements)
  169. for K, V in pairs(Elements) do
  170. if V.Type == Types.Rect then
  171. DrawRect(V)
  172. elseif V.Type == Types.Triangle then
  173. DrawTriangle(V)
  174. elseif V.Type == Types.Text then
  175. DrawText(V)
  176. elseif V.Type == Types.Scissor then
  177. love.graphics.setScissor(V.X, V.Y, V.W, V.H)
  178. elseif V.Type == Types.TransformPush then
  179. love.graphics.push()
  180. elseif V.Type == Types.TransformPop then
  181. love.graphics.pop()
  182. elseif V.Type == Types.ApplyTransform then
  183. love.graphics.applyTransform(V.Transform)
  184. elseif V.Type == Types.Check then
  185. DrawCheck(V)
  186. elseif V.Type == Types.Line then
  187. DrawLine(V)
  188. elseif V.Type == Types.TextFormatted then
  189. DrawTextFormatted(V)
  190. elseif V.Type == Types.IntersectScissor then
  191. love.graphics.intersectScissor(V.X, V.Y, V.W, V.H)
  192. elseif V.Type == Types.Cross then
  193. DrawCross(V)
  194. elseif V.Type == Types.Image then
  195. DrawImage(V)
  196. elseif V.Type == Types.SubImage then
  197. DrawSubImage(V)
  198. elseif V.Type == Types.Circle then
  199. DrawCircle(V)
  200. elseif V.Type == Types.DrawCanvas then
  201. love.graphics.setBlendMode('alpha', 'premultiplied')
  202. love.graphics.setColor(1.0, 1.0, 1.0, 1.0)
  203. love.graphics.draw(V.Canvas, V.X, V.Y)
  204. love.graphics.setBlendMode('alpha')
  205. elseif V.Type == Types.Mesh then
  206. love.graphics.setColor(1.0, 1.0, 1.0, 1.0)
  207. love.graphics.draw(V.Mesh, V.X, V.Y)
  208. elseif V.Type == Types.TextObject then
  209. DrawTextObject(V)
  210. elseif V.Type == Types.Curve then
  211. DrawCurve(V)
  212. elseif V.Type == Types.Polygon then
  213. DrawPolygon(V)
  214. end
  215. end
  216. end
  217. local function AssertActiveBatch()
  218. assert(ActiveBatch ~= nil, "DrawCommands.Begin was not called before commands were issued!")
  219. end
  220. local function DrawLayer(Layer, Name)
  221. if Layer.Channels == nil then
  222. return
  223. end
  224. local Keys = {}
  225. for K, Channel in pairs(Layer.Channels) do
  226. table.insert(Keys, K)
  227. end
  228. table.sort(Keys)
  229. for Index, C in ipairs(Keys) do
  230. local Channel = Layer.Channels[C]
  231. if Channel ~= nil then
  232. for I, V in ipairs(Channel) do
  233. DrawElements(V.Elements)
  234. end
  235. end
  236. end
  237. end
  238. function DrawCommands.Reset()
  239. LayerTable = {}
  240. LayerTable[Layers.Normal] = {}
  241. LayerTable[Layers.ContextMenu] = {}
  242. LayerTable[Layers.MainMenuBar] = {}
  243. LayerTable[Layers.Dialog] = {}
  244. LayerTable[Layers.Debug] = {}
  245. ActiveLayer = Layers.Normal
  246. PendingBatches = {}
  247. ActiveBatch = nil
  248. end
  249. function DrawCommands.Begin(Options)
  250. Options = Options == nil and {} or Options
  251. Options.Channel = Options.Channel == nil and 1 or Options.Channel
  252. if LayerTable[ActiveLayer].Channels == nil then
  253. LayerTable[ActiveLayer].Channels = {}
  254. end
  255. if LayerTable[ActiveLayer].Channels[Options.Channel] == nil then
  256. LayerTable[ActiveLayer].Channels[Options.Channel] = {}
  257. end
  258. local Channel = LayerTable[ActiveLayer].Channels[Options.Channel]
  259. ActiveBatch = {}
  260. ActiveBatch.Elements = {}
  261. table.insert(Channel, ActiveBatch)
  262. table.insert(PendingBatches, 1, ActiveBatch)
  263. end
  264. function DrawCommands.End()
  265. if ActiveBatch ~= nil then
  266. love.graphics.setScissor()
  267. table.remove(PendingBatches, 1)
  268. ActiveBatch = nil
  269. if #PendingBatches > 0 then
  270. ActiveBatch = PendingBatches[1]
  271. end
  272. end
  273. end
  274. function DrawCommands.SetLayer(Layer)
  275. if Layer == 'Normal' then
  276. ActiveLayer = Layers.Normal
  277. elseif Layer == 'ContextMenu' then
  278. ActiveLayer = Layers.ContextMenu
  279. elseif Layer == 'MainMenuBar' then
  280. ActiveLayer = Layers.MainMenuBar
  281. elseif Layer == 'Dialog' then
  282. ActiveLayer = Layers.Dialog
  283. elseif Layer == 'Debug' then
  284. ActiveLayer = Layers.Debug
  285. end
  286. end
  287. function DrawCommands.Rectangle(Mode, X, Y, Width, Height, Color, Radius, Segments)
  288. AssertActiveBatch()
  289. if type(Radius) == 'table' then
  290. Segments = Segments == nil and 10 or Segments
  291. local Verts = {}
  292. local TL = Radius[1]
  293. local TR = Radius[2]
  294. local BR = Radius[3]
  295. local BL = Radius[4]
  296. TL = TL == nil and 0 or TL
  297. TR = TR == nil and 0 or TR
  298. BR = BR == nil and 0 or BR
  299. BL = BL == nil and 0 or BL
  300. AddArc(Verts, Width - BR, Height - BR, BR, 0, 90, Segments, X, Y)
  301. AddArc(Verts, Width - TR, TR, TR, 90, 180, Segments, X, Y)
  302. AddArc(Verts, TL, TL, TL, 180, 270, Segments, X, Y)
  303. AddArc(Verts, BL, Height - BL, BL, 270, 360, Segments, X, Y)
  304. DrawCommands.Polygon(Mode, Verts, Color)
  305. else
  306. local Item = {}
  307. Item.Type = Types.Rect
  308. Item.Mode = Mode
  309. Item.X = X
  310. Item.Y = Y
  311. Item.Width = Width
  312. Item.Height = Height
  313. Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
  314. Item.Radius = Radius and Radius or 0.0
  315. table.insert(ActiveBatch.Elements, Item)
  316. end
  317. end
  318. function DrawCommands.Triangle(Mode, X, Y, Radius, Rotation, Color)
  319. AssertActiveBatch()
  320. local Item = {}
  321. Item.Type = Types.Triangle
  322. Item.Mode = Mode
  323. Item.X = X
  324. Item.Y = Y
  325. Item.Radius = Radius
  326. Item.Rotation = Rotation
  327. Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
  328. table.insert(ActiveBatch.Elements, Item)
  329. end
  330. function DrawCommands.Print(Text, X, Y, Color, Font)
  331. AssertActiveBatch()
  332. local Item = {}
  333. Item.Type = Types.Text
  334. Item.Text = Text
  335. Item.X = X
  336. Item.Y = Y
  337. Item.Color = Color and Color or {1.0, 1.0, 1.0, 1.0}
  338. Item.Font = Font
  339. table.insert(ActiveBatch.Elements, Item)
  340. end
  341. function DrawCommands.Printf(Text, X, Y, W, Align, Color, Font)
  342. AssertActiveBatch()
  343. local Item = {}
  344. Item.Type = Types.TextFormatted
  345. Item.Text = Text
  346. Item.X = X
  347. Item.Y = Y
  348. Item.W = W
  349. Item.Align = Align and Align or 'left'
  350. Item.Color = Color and Color or {1.0, 1.0, 1.0, 1.0}
  351. Item.Font = Font
  352. table.insert(ActiveBatch.Elements, Item)
  353. end
  354. function DrawCommands.Scissor(X, Y, W, H)
  355. AssertActiveBatch()
  356. if W ~= nil then
  357. assert(W >= 0.0, "Cannot set scissor with negative width.")
  358. end
  359. if H ~= nil then
  360. assert(H >= 0.0, "Cannot set scissor with negative height.")
  361. end
  362. local Item = {}
  363. Item.Type = Types.Scissor
  364. Item.X = X
  365. Item.Y = Y
  366. Item.W = W
  367. Item.H = H
  368. table.insert(ActiveBatch.Elements, Item)
  369. end
  370. function DrawCommands.IntersectScissor(X, Y, W, H)
  371. AssertActiveBatch()
  372. local Item = {}
  373. Item.Type = Types.IntersectScissor
  374. Item.X = X and X or 0.0
  375. Item.Y = Y and Y or 0.0
  376. Item.W = W and W or 0.0
  377. Item.H = H and H or 0.0
  378. table.insert(ActiveBatch.Elements, Item)
  379. end
  380. function DrawCommands.TransformPush()
  381. AssertActiveBatch()
  382. local Item = {}
  383. Item.Type = Types.TransformPush
  384. table.insert(ActiveBatch.Elements, Item)
  385. end
  386. function DrawCommands.TransformPop()
  387. AssertActiveBatch()
  388. local Item = {}
  389. Item.Type = Types.TransformPop
  390. table.insert(ActiveBatch.Elements, Item)
  391. end
  392. function DrawCommands.ApplyTransform(Transform)
  393. AssertActiveBatch()
  394. local Item = {}
  395. Item.Type = Types.ApplyTransform
  396. Item.Transform = Transform
  397. table.insert(ActiveBatch.Elements, Item)
  398. end
  399. function DrawCommands.Check(X, Y, Radius, Color)
  400. AssertActiveBatch()
  401. local Item = {}
  402. Item.Type = Types.Check
  403. Item.X = X
  404. Item.Y = Y
  405. Item.Radius = Radius
  406. Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
  407. table.insert(ActiveBatch.Elements, Item)
  408. end
  409. function DrawCommands.Line(X1, Y1, X2, Y2, Width, Color)
  410. AssertActiveBatch()
  411. local Item = {}
  412. Item.Type = Types.Line
  413. Item.X1 = X1
  414. Item.Y1 = Y1
  415. Item.X2 = X2
  416. Item.Y2 = Y2
  417. Item.Width = Width
  418. Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
  419. table.insert(ActiveBatch.Elements, Item)
  420. end
  421. function DrawCommands.Cross(X, Y, Radius, Color)
  422. AssertActiveBatch()
  423. local Item = {}
  424. Item.Type = Types.Cross
  425. Item.X = X
  426. Item.Y = Y
  427. Item.Radius = Radius
  428. Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
  429. table.insert(ActiveBatch.Elements, Item)
  430. end
  431. function DrawCommands.Image(X, Y, Image, Rotation, ScaleX, ScaleY, Color)
  432. AssertActiveBatch()
  433. local Item = {}
  434. Item.Type = Types.Image
  435. Item.X = X
  436. Item.Y = Y
  437. Item.Image = Image
  438. Item.Rotation = Rotation
  439. Item.ScaleX = ScaleX
  440. Item.ScaleY = ScaleY
  441. Item.Color = Color and Color or {1.0, 1.0, 1.0, 1.0}
  442. table.insert(ActiveBatch.Elements, Item)
  443. end
  444. function DrawCommands.SubImage(X, Y, Image, SX, SY, SW, SH, Rotation, ScaleX, ScaleY, Color)
  445. AssertActiveBatch()
  446. local Item = {}
  447. Item.Type = Types.SubImage
  448. Item.Transform = love.math.newTransform(X, Y, Rotation, ScaleX, ScaleY)
  449. Item.Image = Image
  450. Item.Quad = love.graphics.newQuad(SX, SY, SW, SH, Image:getWidth(), Image:getHeight())
  451. Item.Color = Color and Color or {1.0, 1.0, 1.0, 1.0}
  452. table.insert(ActiveBatch.Elements, Item)
  453. end
  454. function DrawCommands.Circle(Mode, X, Y, Radius, Color, Segments)
  455. AssertActiveBatch()
  456. local Item = {}
  457. Item.Type = Types.Circle
  458. Item.Mode = Mode
  459. Item.X = X
  460. Item.Y = Y
  461. Item.Radius = Radius
  462. Item.Color = Color and Color or {0.0, 0.0, 0.0, 1.0}
  463. Item.Segments = Segments and Segments or 24
  464. table.insert(ActiveBatch.Elements, Item)
  465. end
  466. function DrawCommands.DrawCanvas(Canvas, X, Y)
  467. AssertActiveBatch()
  468. local Item = {}
  469. Item.Type = Types.DrawCanvas
  470. Item.Canvas = Canvas
  471. Item.X = X
  472. Item.Y = Y
  473. table.insert(ActiveBatch.Elements, Item)
  474. end
  475. function DrawCommands.Mesh(Mesh, X, Y)
  476. AssertActiveBatch()
  477. local Item = {}
  478. Item.Type = Types.Mesh
  479. Item.Mesh = Mesh
  480. Item.X = X
  481. Item.Y = Y
  482. table.insert(ActiveBatch.Elements, Item)
  483. end
  484. function DrawCommands.Text(Text, X, Y)
  485. AssertActiveBatch()
  486. local Item = {}
  487. Item.Type = Types.TextObject
  488. Item.Text = Text
  489. Item.X = X
  490. Item.Y = Y
  491. Item.Color = {0, 0, 0, 1}
  492. table.insert(ActiveBatch.Elements, Item)
  493. end
  494. function DrawCommands.Curve(Points, Color)
  495. AssertActiveBatch()
  496. local Item = {}
  497. Item.Type = Types.Curve
  498. Item.Points = Points
  499. Item.Color = Color ~= nil and Color or {0, 0, 0, 1}
  500. table.insert(ActiveBatch.Elements, Item)
  501. end
  502. function DrawCommands.Polygon(Mode, Points, Color)
  503. AssertActiveBatch()
  504. local Item = {}
  505. Item.Type = Types.Polygon
  506. Item.Mode = Mode
  507. Item.Points = Points
  508. Item.Color = Color ~= nil and Color or {0, 0, 0, 1}
  509. table.insert(ActiveBatch.Elements, Item)
  510. end
  511. function DrawCommands.Execute()
  512. DrawLayer(LayerTable[Layers.Normal], 'Normal')
  513. DrawLayer(LayerTable[Layers.ContextMenu], 'ContextMenu')
  514. DrawLayer(LayerTable[Layers.MainMenuBar], 'MainMenuBar')
  515. DrawLayer(LayerTable[Layers.Dialog], 'Dialog')
  516. DrawLayer(LayerTable[Layers.Debug], 'Debug')
  517. end
  518. function DrawCommands.GetDebugInfo()
  519. local Result = {}
  520. Result['Normal'] = GetLayerDebugInfo(LayerTable[Layers.Normal])
  521. Result['ContextMenu'] = GetLayerDebugInfo(LayerTable[Layers.ContextMenu])
  522. Result['MainMenuBar'] = GetLayerDebugInfo(LayerTable[Layers.MainMenuBar])
  523. Result['Dialog'] = GetLayerDebugInfo(LayerTable[Layers.Dialog])
  524. Result['Debug'] = GetLayerDebugInfo(LayerTable[Layers.Debug])
  525. return Result
  526. end
  527. return DrawCommands