tianhaobu пре 3 месеци
родитељ
комит
0a3b300baf
100 измењених фајлова са 3744 додато и 1 уклоњено
  1. 21 0
      datafiles/bulb_license.txt
  2. 80 1
      ddmyx.yyp
  3. 85 0
      scripts/BulbAmbienceSprite/BulbAmbienceSprite.gml
  4. 13 0
      scripts/BulbAmbienceSprite/BulbAmbienceSprite.yy
  5. 11 0
      scripts/BulbApplyLightingToSurface/BulbApplyLightingToSurface.gml
  6. 13 0
      scripts/BulbApplyLightingToSurface/BulbApplyLightingToSurface.yy
  7. 58 0
      scripts/BulbDrawLitSurface/BulbDrawLitSurface.gml
  8. 13 0
      scripts/BulbDrawLitSurface/BulbDrawLitSurface.yy
  9. 102 0
      scripts/BulbDynamicOccluder/BulbDynamicOccluder.gml
  10. 13 0
      scripts/BulbDynamicOccluder/BulbDynamicOccluder.yy
  11. 12 0
      scripts/BulbDynamicOccluder/bulb_create_static_occluder.yy
  12. 96 0
      scripts/BulbLight/BulbLight.gml
  13. 13 0
      scripts/BulbLight/BulbLight.yy
  14. 85 0
      scripts/BulbLightOverlay/BulbLightOverlay.gml
  15. 13 0
      scripts/BulbLightOverlay/BulbLightOverlay.yy
  16. 86 0
      scripts/BulbLightOverlay/BulbShadowOverlay.gml
  17. 26 0
      scripts/BulbNormalMapClear/BulbNormalMapClear.gml
  18. 13 0
      scripts/BulbNormalMapClear/BulbNormalMapClear.yy
  19. 8 0
      scripts/BulbNormalMapDrawSelf/BulbNormalMapDrawSelf.gml
  20. 13 0
      scripts/BulbNormalMapDrawSelf/BulbNormalMapDrawSelf.yy
  21. 11 0
      scripts/BulbNormalMapDrawSprite/BulbNormalMapDrawSprite.gml
  22. 13 0
      scripts/BulbNormalMapDrawSprite/BulbNormalMapDrawSprite.yy
  23. 26 0
      scripts/BulbNormalMapDrawSpriteExt/BulbNormalMapDrawSpriteExt.gml
  24. 13 0
      scripts/BulbNormalMapDrawSpriteExt/BulbNormalMapDrawSpriteExt.yy
  25. 7 0
      scripts/BulbNormalMapShaderReset/BulbNormalMapShaderReset.gml
  26. 13 0
      scripts/BulbNormalMapShaderReset/BulbNormalMapShaderReset.yy
  27. 9 0
      scripts/BulbNormalMapShaderSet/BulbNormalMapShaderSet.gml
  28. 13 0
      scripts/BulbNormalMapShaderSet/BulbNormalMapShaderSet.yy
  29. 531 0
      scripts/BulbRenderer/BulbRenderer.gml
  30. 13 0
      scripts/BulbRenderer/BulbRenderer.yy
  31. 84 0
      scripts/BulbShadowOverlay/BulbShadowOverlay.gml
  32. 13 0
      scripts/BulbShadowOverlay/BulbShadowOverlay.yy
  33. 9 0
      scripts/BulbSpecularMapDrawSelf/BulbSpecularMapDrawSelf.gml
  34. 13 0
      scripts/BulbSpecularMapDrawSelf/BulbSpecularMapDrawSelf.yy
  35. 12 0
      scripts/BulbSpecularMapDrawSprite/BulbSpecularMapDrawSprite.gml
  36. 13 0
      scripts/BulbSpecularMapDrawSprite/BulbSpecularMapDrawSprite.yy
  37. 15 0
      scripts/BulbSpecularMapDrawSpriteExt/BulbSpecularMapDrawSpriteExt.gml
  38. 13 0
      scripts/BulbSpecularMapDrawSpriteExt/BulbSpecularMapDrawSpriteExt.yy
  39. 8 0
      scripts/BulbSpecularMapShaderReset/BulbSpecularMapShaderReset.gml
  40. 13 0
      scripts/BulbSpecularMapShaderReset/BulbSpecularMapShaderReset.yy
  41. 10 0
      scripts/BulbSpecularMapShaderSet/BulbSpecularMapShaderSet.gml
  42. 13 0
      scripts/BulbSpecularMapShaderSet/BulbSpecularMapShaderSet.yy
  43. 91 0
      scripts/BulbStaticOccluder/BulbStaticOccluder.gml
  44. 13 0
      scripts/BulbStaticOccluder/BulbStaticOccluder.yy
  45. 53 0
      scripts/BulbSunlight/BulbSunlight.gml
  46. 13 0
      scripts/BulbSunlight/BulbSunlight.yy
  47. 49 0
      scripts/__BulbAddOcclusionHard/__BulbAddOcclusionHard.gml
  48. 13 0
      scripts/__BulbAddOcclusionHard/__BulbAddOcclusionHard.yy
  49. 58 0
      scripts/__BulbAddOcclusionSoft/__BulbAddOcclusionSoft.gml
  50. 13 0
      scripts/__BulbAddOcclusionSoft/__BulbAddOcclusionSoft.yy
  51. 27 0
      scripts/__BulbConfig/__BulbConfig.gml
  52. 13 0
      scripts/__BulbConfig/__BulbConfig.yy
  53. 18 0
      scripts/__BulbConstants/__BulbConstants.gml
  54. 13 0
      scripts/__BulbConstants/__BulbConstants.yy
  55. 16 0
      scripts/__BulbError/__BulbError.gml
  56. 13 0
      scripts/__BulbError/__BulbError.yy
  57. 6 0
      scripts/__BulbRectInRect/__BulbRectInRect.gml
  58. 13 0
      scripts/__BulbRectInRect/__BulbRectInRect.yy
  59. 207 0
      scripts/__BulbRendererDefineAccumulateHard/__BulbRendererDefineAccumulateHard.gml
  60. 13 0
      scripts/__BulbRendererDefineAccumulateHard/__BulbRendererDefineAccumulateHard.yy
  61. 200 0
      scripts/__BulbRendererDefineAccumulateHardNoStencil/__BulbRendererDefineAccumulateHardNoStencil.gml
  62. 13 0
      scripts/__BulbRendererDefineAccumulateHardNoStencil/__BulbRendererDefineAccumulateHardNoStencil.yy
  63. 175 0
      scripts/__BulbRendererDefineAccumulateSoft/__BulbRendererDefineAccumulateSoft.gml
  64. 13 0
      scripts/__BulbRendererDefineAccumulateSoft/__BulbRendererDefineAccumulateSoft.yy
  65. 79 0
      scripts/__BulbRendererDefineHDR/__BulbRendererDefineHDR.gml
  66. 13 0
      scripts/__BulbRendererDefineHDR/__BulbRendererDefineHDR.yy
  67. 198 0
      scripts/__BulbRendererDefineLight/__BulbRendererDefineLight.gml
  68. 13 0
      scripts/__BulbRendererDefineLight/__BulbRendererDefineLight.yy
  69. 52 0
      scripts/__BulbRendererDefineNormal/__BulbRendererDefineNormal.gml
  70. 13 0
      scripts/__BulbRendererDefineNormal/__BulbRendererDefineNormal.yy
  71. 141 0
      scripts/__BulbRendererDefineOverlayUnderlay/__BulbRendererDefineOverlayUnderlay.gml
  72. 13 0
      scripts/__BulbRendererDefineOverlayUnderlay/__BulbRendererDefineOverlayUnderlay.yy
  73. 148 0
      scripts/__BulbRendererDefineVertexBuffers/__BulbRendererDefineVertexBuffers.gml
  74. 13 0
      scripts/__BulbRendererDefineVertexBuffers/__BulbRendererDefineVertexBuffers.yy
  75. 54 0
      scripts/__BulbSystem/__BulbSystem.gml
  76. 13 0
      scripts/__BulbSystem/__BulbSystem.yy
  77. 15 0
      scripts/__BulbTrace/__BulbTrace.gml
  78. 13 0
      scripts/__BulbTrace/__BulbTrace.yy
  79. 4 0
      shaders/__shdBulbHardShadows/__shdBulbHardShadows.fsh
  80. 18 0
      shaders/__shdBulbHardShadows/__shdBulbHardShadows.vsh
  81. 12 0
      shaders/__shdBulbHardShadows/__shdBulbHardShadows.yy
  82. 4 0
      shaders/__shdBulbHardShadowsSunlight/__shdBulbHardShadowsSunlight.fsh
  83. 17 0
      shaders/__shdBulbHardShadowsSunlight/__shdBulbHardShadowsSunlight.vsh
  84. 12 0
      shaders/__shdBulbHardShadowsSunlight/__shdBulbHardShadowsSunlight.yy
  85. 10 0
      shaders/__shdBulbIntensity/__shdBulbIntensity.fsh
  86. 13 0
      shaders/__shdBulbIntensity/__shdBulbIntensity.vsh
  87. 12 0
      shaders/__shdBulbIntensity/__shdBulbIntensity.yy
  88. 12 0
      shaders/__shdBulbKawaseDown/__shdBulbKawaseDown.fsh
  89. 11 0
      shaders/__shdBulbKawaseDown/__shdBulbKawaseDown.vsh
  90. 12 0
      shaders/__shdBulbKawaseDown/__shdBulbKawaseDown.yy
  91. 20 0
      shaders/__shdBulbKawaseDownWithThreshold/__shdBulbKawaseDownWithThreshold.fsh
  92. 11 0
      shaders/__shdBulbKawaseDownWithThreshold/__shdBulbKawaseDownWithThreshold.vsh
  93. 12 0
      shaders/__shdBulbKawaseDownWithThreshold/__shdBulbKawaseDownWithThreshold.yy
  94. 15 0
      shaders/__shdBulbKawaseUp/__shdBulbKawaseUp.fsh
  95. 11 0
      shaders/__shdBulbKawaseUp/__shdBulbKawaseUp.vsh
  96. 12 0
      shaders/__shdBulbKawaseUp/__shdBulbKawaseUp.yy
  97. 29 0
      shaders/__shdBulbLightWithNormalMap/__shdBulbLightWithNormalMap.fsh
  98. 15 0
      shaders/__shdBulbLightWithNormalMap/__shdBulbLightWithNormalMap.vsh
  99. 12 0
      shaders/__shdBulbLightWithNormalMap/__shdBulbLightWithNormalMap.yy
  100. 10 0
      shaders/__shdBulbLightWithoutNormalMap/__shdBulbLightWithoutNormalMap.fsh

+ 21 - 0
datafiles/bulb_license.txt

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Julian Adams
+
+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.

+ 80 - 1
ddmyx.yyp

@@ -10,6 +10,18 @@
   },
   "defaultScriptType":1,
   "Folders":[
+    {"$GMFolder":"","%Name":"3rdParties","folderPath":"folders/3rdParties.yy","name":"3rdParties","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Bulb","folderPath":"folders/3rdParties/Bulb.yy","name":"Bulb","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"(System)","folderPath":"folders/3rdParties/Bulb/(System).yy","name":"(System)","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"(Pay no attention to that man behind the curtain)","folderPath":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain).yy","name":"(Pay no attention to that man behind the curtain)","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Renderer Definitions","folderPath":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy","name":"Renderer Definitions","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Shaders","folderPath":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders.yy","name":"Shaders","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Kawase","folderPath":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Kawase.yy","name":"Kawase","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Light","folderPath":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Light.yy","name":"Light","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Shadow","folderPath":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Shadow.yy","name":"Shadow","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Tonemapping","folderPath":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Tonemapping.yy","name":"Tonemapping","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Normal Map","folderPath":"folders/3rdParties/Bulb/Normal Map.yy","name":"Normal Map","resourceType":"GMFolder","resourceVersion":"2.0",},
+    {"$GMFolder":"","%Name":"Specular Map","folderPath":"folders/3rdParties/Bulb/Normal Map/Specular Map.yy","name":"Specular Map","resourceType":"GMFolder","resourceVersion":"2.0",},
     {"$GMFolder":"","%Name":"CoreObjects","folderPath":"folders/CoreObjects.yy","name":"CoreObjects","resourceType":"GMFolder","resourceVersion":"2.0",},
     {"$GMFolder":"","%Name":"Enemy","folderPath":"folders/Enemy.yy","name":"Enemy","resourceType":"GMFolder","resourceVersion":"2.0",},
     {"$GMFolder":"","%Name":"oBossHex","folderPath":"folders/Enemy/oBossHex.yy","name":"oBossHex","resourceType":"GMFolder","resourceVersion":"2.0",},
@@ -40,7 +52,9 @@
   "ForcedPrefabProjectReferences":[
     {"link":"io.gamemaker.gmparticlepresets-1.0.0","name":"io.gamemaker.gmparticlepresets-1.0.0","path":"io.gamemaker.gmparticlepresets-1.0.0.yyp",},
   ],
-  "IncludedFiles":[],
+  "IncludedFiles":[
+    {"$GMIncludedFile":"","%Name":"bulb_license.txt","CopyToMask":-1,"filePath":"datafiles","name":"bulb_license.txt","resourceType":"GMIncludedFile","resourceVersion":"2.0",},
+  ],
   "isEcma":false,
   "LibraryEmitters":[],
   "MetaData":{
@@ -100,6 +114,43 @@
     {"id":{"name":"rTest_3","path":"rooms/rTest_3/rTest_3.yy",},},
     {"id":{"name":"rTest_backup","path":"rooms/rTest_backup/rTest_backup.yy",},},
     {"id":{"name":"rTitle","path":"rooms/rTitle/rTitle.yy",},},
+    {"id":{"name":"__BulbAddOcclusionHard","path":"scripts/__BulbAddOcclusionHard/__BulbAddOcclusionHard.yy",},},
+    {"id":{"name":"__BulbAddOcclusionSoft","path":"scripts/__BulbAddOcclusionSoft/__BulbAddOcclusionSoft.yy",},},
+    {"id":{"name":"__BulbConfig","path":"scripts/__BulbConfig/__BulbConfig.yy",},},
+    {"id":{"name":"__BulbConstants","path":"scripts/__BulbConstants/__BulbConstants.yy",},},
+    {"id":{"name":"__BulbError","path":"scripts/__BulbError/__BulbError.yy",},},
+    {"id":{"name":"__BulbRectInRect","path":"scripts/__BulbRectInRect/__BulbRectInRect.yy",},},
+    {"id":{"name":"__BulbRendererDefineAccumulateHard","path":"scripts/__BulbRendererDefineAccumulateHard/__BulbRendererDefineAccumulateHard.yy",},},
+    {"id":{"name":"__BulbRendererDefineAccumulateHardNoStencil","path":"scripts/__BulbRendererDefineAccumulateHardNoStencil/__BulbRendererDefineAccumulateHardNoStencil.yy",},},
+    {"id":{"name":"__BulbRendererDefineAccumulateSoft","path":"scripts/__BulbRendererDefineAccumulateSoft/__BulbRendererDefineAccumulateSoft.yy",},},
+    {"id":{"name":"__BulbRendererDefineHDR","path":"scripts/__BulbRendererDefineHDR/__BulbRendererDefineHDR.yy",},},
+    {"id":{"name":"__BulbRendererDefineLight","path":"scripts/__BulbRendererDefineLight/__BulbRendererDefineLight.yy",},},
+    {"id":{"name":"__BulbRendererDefineNormal","path":"scripts/__BulbRendererDefineNormal/__BulbRendererDefineNormal.yy",},},
+    {"id":{"name":"__BulbRendererDefineOverlayUnderlay","path":"scripts/__BulbRendererDefineOverlayUnderlay/__BulbRendererDefineOverlayUnderlay.yy",},},
+    {"id":{"name":"__BulbRendererDefineVertexBuffers","path":"scripts/__BulbRendererDefineVertexBuffers/__BulbRendererDefineVertexBuffers.yy",},},
+    {"id":{"name":"__BulbSystem","path":"scripts/__BulbSystem/__BulbSystem.yy",},},
+    {"id":{"name":"__BulbTrace","path":"scripts/__BulbTrace/__BulbTrace.yy",},},
+    {"id":{"name":"BulbAmbienceSprite","path":"scripts/BulbAmbienceSprite/BulbAmbienceSprite.yy",},},
+    {"id":{"name":"BulbApplyLightingToSurface","path":"scripts/BulbApplyLightingToSurface/BulbApplyLightingToSurface.yy",},},
+    {"id":{"name":"BulbDrawLitSurface","path":"scripts/BulbDrawLitSurface/BulbDrawLitSurface.yy",},},
+    {"id":{"name":"BulbDynamicOccluder","path":"scripts/BulbDynamicOccluder/BulbDynamicOccluder.yy",},},
+    {"id":{"name":"BulbLight","path":"scripts/BulbLight/BulbLight.yy",},},
+    {"id":{"name":"BulbLightOverlay","path":"scripts/BulbLightOverlay/BulbLightOverlay.yy",},},
+    {"id":{"name":"BulbNormalMapClear","path":"scripts/BulbNormalMapClear/BulbNormalMapClear.yy",},},
+    {"id":{"name":"BulbNormalMapDrawSelf","path":"scripts/BulbNormalMapDrawSelf/BulbNormalMapDrawSelf.yy",},},
+    {"id":{"name":"BulbNormalMapDrawSprite","path":"scripts/BulbNormalMapDrawSprite/BulbNormalMapDrawSprite.yy",},},
+    {"id":{"name":"BulbNormalMapDrawSpriteExt","path":"scripts/BulbNormalMapDrawSpriteExt/BulbNormalMapDrawSpriteExt.yy",},},
+    {"id":{"name":"BulbNormalMapShaderReset","path":"scripts/BulbNormalMapShaderReset/BulbNormalMapShaderReset.yy",},},
+    {"id":{"name":"BulbNormalMapShaderSet","path":"scripts/BulbNormalMapShaderSet/BulbNormalMapShaderSet.yy",},},
+    {"id":{"name":"BulbRenderer","path":"scripts/BulbRenderer/BulbRenderer.yy",},},
+    {"id":{"name":"BulbShadowOverlay","path":"scripts/BulbShadowOverlay/BulbShadowOverlay.yy",},},
+    {"id":{"name":"BulbSpecularMapDrawSelf","path":"scripts/BulbSpecularMapDrawSelf/BulbSpecularMapDrawSelf.yy",},},
+    {"id":{"name":"BulbSpecularMapDrawSprite","path":"scripts/BulbSpecularMapDrawSprite/BulbSpecularMapDrawSprite.yy",},},
+    {"id":{"name":"BulbSpecularMapDrawSpriteExt","path":"scripts/BulbSpecularMapDrawSpriteExt/BulbSpecularMapDrawSpriteExt.yy",},},
+    {"id":{"name":"BulbSpecularMapShaderReset","path":"scripts/BulbSpecularMapShaderReset/BulbSpecularMapShaderReset.yy",},},
+    {"id":{"name":"BulbSpecularMapShaderSet","path":"scripts/BulbSpecularMapShaderSet/BulbSpecularMapShaderSet.yy",},},
+    {"id":{"name":"BulbStaticOccluder","path":"scripts/BulbStaticOccluder/BulbStaticOccluder.yy",},},
+    {"id":{"name":"BulbSunlight","path":"scripts/BulbSunlight/BulbSunlight.yy",},},
     {"id":{"name":"CrawlAI","path":"scripts/CrawlAI/CrawlAI.yy",},},
     {"id":{"name":"DummyAI","path":"scripts/DummyAI/DummyAI.yy",},},
     {"id":{"name":"EnemyStates","path":"scripts/EnemyStates/EnemyStates.yy",},},
@@ -112,8 +163,36 @@
     {"id":{"name":"PlayerStates","path":"scripts/PlayerStates/PlayerStates.yy",},},
     {"id":{"name":"Simplifier","path":"scripts/Simplifier/Simplifier.yy",},},
     {"id":{"name":"SL","path":"scripts/SL/SL.yy",},},
+    {"id":{"name":"__shdBulbHardShadows","path":"shaders/__shdBulbHardShadows/__shdBulbHardShadows.yy",},},
+    {"id":{"name":"__shdBulbHardShadowsSunlight","path":"shaders/__shdBulbHardShadowsSunlight/__shdBulbHardShadowsSunlight.yy",},},
+    {"id":{"name":"__shdBulbIntensity","path":"shaders/__shdBulbIntensity/__shdBulbIntensity.yy",},},
+    {"id":{"name":"__shdBulbKawaseDown","path":"shaders/__shdBulbKawaseDown/__shdBulbKawaseDown.yy",},},
+    {"id":{"name":"__shdBulbKawaseDownWithThreshold","path":"shaders/__shdBulbKawaseDownWithThreshold/__shdBulbKawaseDownWithThreshold.yy",},},
+    {"id":{"name":"__shdBulbKawaseUp","path":"shaders/__shdBulbKawaseUp/__shdBulbKawaseUp.yy",},},
+    {"id":{"name":"__shdBulbLightWithNormalMap","path":"shaders/__shdBulbLightWithNormalMap/__shdBulbLightWithNormalMap.yy",},},
+    {"id":{"name":"__shdBulbLightWithoutNormalMap","path":"shaders/__shdBulbLightWithoutNormalMap/__shdBulbLightWithoutNormalMap.yy",},},
+    {"id":{"name":"__shdBulbLinearToGamma","path":"shaders/__shdBulbLinearToGamma/__shdBulbLinearToGamma.yy",},},
+    {"id":{"name":"__shdBulbNormal","path":"shaders/__shdBulbNormal/__shdBulbNormal.yy",},},
+    {"id":{"name":"__shdBulbNormalUp","path":"shaders/__shdBulbNormalUp/__shdBulbNormalUp.yy",},},
+    {"id":{"name":"__shdBulbSoftShadows","path":"shaders/__shdBulbSoftShadows/__shdBulbSoftShadows.yy",},},
+    {"id":{"name":"__shdBulbSoftShadowsSunlight","path":"shaders/__shdBulbSoftShadowsSunlight/__shdBulbSoftShadowsSunlight.yy",},},
+    {"id":{"name":"__shdBulbSpecular","path":"shaders/__shdBulbSpecular/__shdBulbSpecular.yy",},},
+    {"id":{"name":"__shdBulbSpecularForce","path":"shaders/__shdBulbSpecularForce/__shdBulbSpecularForce.yy",},},
+    {"id":{"name":"__shdBulbSunlightWithNormalMap","path":"shaders/__shdBulbSunlightWithNormalMap/__shdBulbSunlightWithNormalMap.yy",},},
+    {"id":{"name":"__shdBulbSunlightWithoutNormalMap","path":"shaders/__shdBulbSunlightWithoutNormalMap/__shdBulbSunlightWithoutNormalMap.yy",},},
+    {"id":{"name":"__shdBulbThreshold","path":"shaders/__shdBulbThreshold/__shdBulbThreshold.yy",},},
+    {"id":{"name":"__shdBulbTonemapACES","path":"shaders/__shdBulbTonemapACES/__shdBulbTonemapACES.yy",},},
+    {"id":{"name":"__shdBulbTonemapBadGamma","path":"shaders/__shdBulbTonemapBadGamma/__shdBulbTonemapBadGamma.yy",},},
+    {"id":{"name":"__shdBulbTonemapClamp","path":"shaders/__shdBulbTonemapClamp/__shdBulbTonemapClamp.yy",},},
+    {"id":{"name":"__shdBulbTonemapHBD","path":"shaders/__shdBulbTonemapHBD/__shdBulbTonemapHBD.yy",},},
+    {"id":{"name":"__shdBulbTonemapNone","path":"shaders/__shdBulbTonemapNone/__shdBulbTonemapNone.yy",},},
+    {"id":{"name":"__shdBulbTonemapReinhard","path":"shaders/__shdBulbTonemapReinhard/__shdBulbTonemapReinhard.yy",},},
+    {"id":{"name":"__shdBulbTonemapReinhardExtended","path":"shaders/__shdBulbTonemapReinhardExtended/__shdBulbTonemapReinhardExtended.yy",},},
+    {"id":{"name":"__shdBulbTonemapUncharted2","path":"shaders/__shdBulbTonemapUncharted2/__shdBulbTonemapUncharted2.yy",},},
+    {"id":{"name":"__shdBulbTonemapUnreal3","path":"shaders/__shdBulbTonemapUnreal3/__shdBulbTonemapUnreal3.yy",},},
     {"id":{"name":"sh_flash","path":"shaders/sh_flash/sh_flash.yy",},},
     {"id":{"name":"sh_ink_blend","path":"shaders/sh_ink_blend/sh_ink_blend.yy",},},
+    {"id":{"name":"__sprBulbPixel","path":"sprites/__sprBulbPixel/__sprBulbPixel.yy",},},
     {"id":{"name":"sBladeHitEffect","path":"sprites/sBladeHitEffect/sBladeHitEffect.yy",},},
     {"id":{"name":"sBlockClimbable","path":"sprites/sBlockClimbable/sBlockClimbable.yy",},},
     {"id":{"name":"sBlockSlippery","path":"sprites/sBlockSlippery/sBlockSlippery.yy",},},

+ 85 - 0
scripts/BulbAmbienceSprite/BulbAmbienceSprite.gml

@@ -0,0 +1,85 @@
+/// @param renderer
+
+function BulbAmbienceSprite(_renderer) constructor
+{
+    visible = true;
+    
+    x = 0;
+    y = 0;
+    
+    sprite = undefined;
+    image  = 0;
+    xscale = 1.0;
+    yscale = 1.0;
+    angle  = 0.0;
+    blend  = c_white;
+    alpha  = 1.0;
+    
+    __oldSprite = undefined;
+    __radius    = 0;
+    __destroyed = false;
+    
+    static Destroy = function()
+    {
+        __destroyed = true;
+    }
+    
+    static __CheckSpriteDimensions = function()
+    {
+        // Redefine light sprite boundaries
+        if (sprite != __oldSprite)
+        {
+            __oldSprite = sprite;
+            
+            if ((sprite != undefined) && sprite_exists(sprite))
+            {
+                //Choose the longest axis of the sprite as the radius
+                //We apply x/y scaling in the __IsOnScreen() function
+                var _xOffset = sprite_get_xoffset(sprite);
+                var _yOffset = sprite_get_yoffset(sprite);
+                var _x = max(_xOffset, sprite_get_width( sprite) - _xOffset);
+                var _y = max(_yOffset, sprite_get_height(sprite) - _yOffset);
+                
+                __radius = sqrt(_x*_x + _y*_y);
+            }
+            else
+            {
+                __radius = 0;
+            }
+        }
+    }
+    
+    static AddToRenderer = function(_renderer)
+    {
+        if (__destroyed) return;
+        array_push(_renderer.__ambienceSpriteArray, weak_ref_create(self));
+    }
+    
+    static RemoveFromRenderer = function(_renderer)
+    {
+        var _array = _renderer.__ambienceSpriteArray;
+        var _i = array_length(_array) - 1;
+        repeat(array_length(_array))
+        {
+            var _weak = _array[_i];
+            if (weak_ref_alive(_weak))
+            {
+                if (_weak.ref == self) array_delete(_array, _i, 1);
+            }
+            else
+            {
+                array_delete(_array, _i, 1);
+            }
+            
+            --_i;
+        }
+    }
+    
+    static __IsOnScreen = function(_cameraL, _cameraT, _cameraR, _cameraB)
+    {
+        var _radius = __radius*max(xscale, yscale);
+        return (!__destroyed && visible && __BulbRectInRect(x - _radius, y - _radius, x + _radius, y + _radius, _cameraL, _cameraT, _cameraR, _cameraB));
+    }
+    
+    if (_renderer != undefined) AddToRenderer(_renderer);
+}

+ 13 - 0
scripts/BulbAmbienceSprite/BulbAmbienceSprite.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbAmbienceSprite",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbAmbienceSprite",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 11 - 0
scripts/BulbApplyLightingToSurface/BulbApplyLightingToSurface.gml

@@ -0,0 +1,11 @@
+// Feather disable all
+
+/// @param bulbRenderer
+/// @param surface
+
+function BulbApplyLightingToSurface(_renderer, _surface)
+{
+    if (_renderer == undefined) return;
+    
+    surface_copy(_surface, 0, 0, _renderer.GetOutputSurface(_surface));
+}

+ 13 - 0
scripts/BulbApplyLightingToSurface/BulbApplyLightingToSurface.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbApplyLightingToSurface",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbApplyLightingToSurface",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 58 - 0
scripts/BulbDrawLitSurface/BulbDrawLitSurface.gml

@@ -0,0 +1,58 @@
+// Feather disable all
+
+/// @param bulbRenderer
+/// @param surface
+/// @param [x]
+/// @param [y]
+/// @param [width]
+/// @param [height]
+/// @param [textureFiltering]
+/// @param [alphaBlend=false]
+
+function BulbDrawLitSurface(_renderer, _surface, _x = undefined, _y = undefined, _width = undefined, _height = undefined, _textureFiltering = undefined, _alphaBlend = false)
+{
+    if (surface_get_target() == _surface)
+    {
+        __BulbError("Cannot call BulbDrawLitSurface() when the destination surface and drawn surface are the same\nIf you are drawing the application surface, use a Post-Draw event or GUI draw event");
+    }
+    
+    if ((_x == undefined) || (_y == undefined) || (_width == undefined) || (_height == undefined))
+    {
+        var _positionArray = application_get_position();
+        _x      = _positionArray[0];
+        _y      = _positionArray[1];
+        _width  = _positionArray[2] - _x;
+        _height = _positionArray[3] - _y;
+    }
+    
+    if (_renderer == undefined)
+    {
+        if (_textureFiltering != undefined)
+        {
+            var _oldTextureFiltering = gpu_get_tex_filter();
+            gpu_set_tex_filter(_textureFiltering);
+        }
+        
+        if (_alphaBlend != undefined)
+        {
+            var _oldAlphaBlend = gpu_get_blendenable();
+            gpu_set_blendenable(_alphaBlend);
+        }
+        
+        draw_surface_stretched(_surface, _x, _y, _width, _height);
+        
+        if (_textureFiltering != undefined)
+        {
+            gpu_set_tex_filter(_oldTextureFiltering);
+        }
+        
+        if (_alphaBlend != undefined)
+        {
+            gpu_set_blendenable(_oldAlphaBlend);
+        }
+    }
+    else
+    {
+        _renderer.DrawLitSurface(_surface, _x, _y, _width, _height, _textureFiltering, _alphaBlend);
+    }
+}

+ 13 - 0
scripts/BulbDrawLitSurface/BulbDrawLitSurface.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbDrawLitSurface",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbDrawLitSurface",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 102 - 0
scripts/BulbDynamicOccluder/BulbDynamicOccluder.gml

@@ -0,0 +1,102 @@
+/// @param renderer
+
+function BulbDynamicOccluder(_renderer) constructor
+{
+    visible = true;
+    
+    x = 0;
+    y = 0;
+    
+    xscale = 1.0;
+    yscale = 1.0;
+    angle  = 0.0;
+    
+    vertexArray = [];
+    
+    __radius    = 0;
+    __destroyed = false;
+    
+    static Destroy = function()
+    {
+        __destroyed = true;
+    }
+    
+    static AddEdge = function(_x1, _y1, _x2, _y2)
+    {
+        if (__destroyed) return;
+        
+        //Choose the longest axis of the sprite as the radius
+        //We apply x/y scaling in the __IsOnScreen() function
+        __radius = max(__radius, sqrt(max(_x1*_x1 + _y1*_y1, _x2*_x2 + _y2*_y2)));
+        
+        array_push(vertexArray, _x1, _y1, _x2, _y2, _y2-_y1, _x1-_x2);
+    }
+    
+    static AddCircle = function(_radius, _x = 0, _y = 0, _edges = 24)
+    {
+        if (__destroyed) return;
+        
+        __radius = max(__radius, _radius);
+        
+        var _angle = 0;
+        var _angleStep = 360 / _edges;
+        
+        var _x2 = _x + lengthdir_x(_radius, _angle);
+        var _y2 = _y + lengthdir_y(_radius, _angle);
+        
+        repeat(_edges)
+        { 
+            _angle -= _angleStep;
+            
+            var _x1 = _x2;
+            var _y1 = _y2;
+            _x2 = _x + lengthdir_x(_radius, _angle);
+            _y2 = _y + lengthdir_y(_radius, _angle);
+            
+            array_push(vertexArray, _x1, _y1, _x2, _y2, _y2-_y1, _x1-_x2);
+        }
+    }
+    
+    static ClearEdges = function(_x1, _y1, _x2, _y2)
+    {
+        if (__destroyed) return;
+        
+        __radius = 0;
+        
+        array_resize(vertexArray, 0);
+    }
+    
+    static AddToRenderer = function(_renderer)
+    {
+        if (__destroyed) return;
+        array_push(_renderer.__dynamicOccludersArray, weak_ref_create(self));
+    }
+    
+    static RemoveFromRenderer = function(_renderer)
+    {
+        var _array = _renderer.__dynamicOccludersArray;
+        var _i = array_length(_array) - 1;
+        repeat(array_length(_array))
+        {
+            var _weak = _array[_i];
+            if (weak_ref_alive(_weak))
+            {
+                if (_weak.ref == self) array_delete(_array, _i, 1);
+            }
+            else
+            {
+                array_delete(_array, _i, 1);
+            }
+            
+            --_i;
+        }
+    }
+    
+    static __IsOnScreen = function(_cameraL, _cameraT, _cameraR, _cameraB)
+    {
+        var _radius = __radius*max(xscale, yscale);
+        return (!__destroyed && visible && __BulbRectInRect(x - _radius, y - _radius, x + _radius, y + _radius, _cameraL, _cameraT, _cameraR, _cameraB));
+    }
+    
+    if (_renderer != undefined) AddToRenderer(_renderer);
+}

+ 13 - 0
scripts/BulbDynamicOccluder/BulbDynamicOccluder.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbDynamicOccluder",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbDynamicOccluder",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 12 - 0
scripts/BulbDynamicOccluder/bulb_create_static_occluder.yy

@@ -0,0 +1,12 @@
+{
+  "isDnD": false,
+  "isCompatibility": false,
+  "parent": {
+    "name": "Bulb",
+    "path": "folders/Bulb.yy",
+  },
+  "resourceVersion": "1.0",
+  "name": "bulb_create_static_occluder",
+  "tags": [],
+  "resourceType": "GMScript",
+}

+ 96 - 0
scripts/BulbLight/BulbLight.gml

@@ -0,0 +1,96 @@
+/// @param renderer
+/// @param sprite
+/// @param image
+/// @param x
+/// @param y
+
+function BulbLight(_renderer, _sprite, _image, _x, _y) constructor
+{
+    visible = true;
+    
+    x = _x;
+    y = _y;
+    
+    normalMap  = BULB_DEFAULT_USE_NORMAL_MAP;
+    normalMapZ = BULB_DEFAULT_NORMAL_MAP_Z;
+    
+    sprite    = _sprite;
+    image     = _image;
+    xscale    = 1.0;
+    yscale    = 1.0;
+    angle     = 0.0;
+    blend     = c_white;
+    intensity = 1.0;
+    
+    penumbraSize = 0.0;
+    
+    castShadows = true;
+    
+    __oldSprite = undefined;
+    __radius    = 0;
+    __destroyed = false;
+    
+    static Destroy = function()
+    {
+        __destroyed = true;
+    }
+    
+    static AddToRenderer = function(_renderer)
+    {
+        if (__destroyed) return;
+        array_push(_renderer.__lightsArray, weak_ref_create(self));
+    }
+    
+    static RemoveFromRenderer = function(_renderer)
+    {
+        var _array = _renderer.__lightsArray;
+        var _i = array_length(_array) - 1;
+        repeat(array_length(_array))
+        {
+            var _weak = _array[_i];
+            if (weak_ref_alive(_weak))
+            {
+                if (_weak.ref == self) array_delete(_array, _i, 1);
+            }
+            else
+            {
+                array_delete(_array, _i, 1);
+            }
+            
+            --_i;
+        }
+    }
+    
+    static __CheckSpriteDimensions = function()
+    {
+        // Redefine light sprite boundaries
+        if (sprite != __oldSprite)
+        {
+            __oldSprite = sprite;
+            
+            if ((sprite != undefined) && sprite_exists(sprite))
+            {
+                //Choose the longest axis of the sprite as the radius
+                //We apply x/y scaling in the __IsOnScreen() function
+                var _xOffset = sprite_get_xoffset(sprite);
+                var _yOffset = sprite_get_yoffset(sprite);
+                var _x = max(_xOffset, sprite_get_width( sprite) - _xOffset);
+                var _y = max(_yOffset, sprite_get_height(sprite) - _yOffset);
+                
+                __radius = sqrt(_x*_x + _y*_y);
+            }
+            else
+            {
+                __radius = 0;
+            }
+        }
+    }
+    
+    static __IsOnScreen = function(_cameraL, _cameraT, _cameraR, _cameraB)
+    {
+        var _radius = __radius*max(xscale, yscale);
+        return (!__destroyed && visible && __BulbRectInRect(x - _radius, y - _radius, x + _radius, y + _radius, _cameraL, _cameraT, _cameraR, _cameraB));
+    }
+    
+    if (_renderer != undefined) AddToRenderer(_renderer);
+}

+ 13 - 0
scripts/BulbLight/BulbLight.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbLight",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbLight",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 85 - 0
scripts/BulbLightOverlay/BulbLightOverlay.gml

@@ -0,0 +1,85 @@
+/// @param renderer
+
+function BulbLightOverlay(_renderer) constructor
+{
+    visible = true;
+    
+    x = 0;
+    y = 0;
+    
+    sprite    = undefined;
+    image     = 0;
+    xscale    = 1.0;
+    yscale    = 1.0;
+    angle     = 0.0;
+    blend     = c_white;
+    intensity = 1.0;
+    
+    __oldSprite = undefined;
+    __radius    = 0;
+    __destroyed = false;
+    
+    static Destroy = function()
+    {
+        __destroyed = true;
+    }
+    
+    static __CheckSpriteDimensions = function()
+    {
+        // Redefine light sprite boundaries
+        if (sprite != __oldSprite)
+        {
+            __oldSprite = sprite;
+            
+            if ((sprite != undefined) && sprite_exists(sprite))
+            {
+                //Choose the longest axis of the sprite as the radius
+                //We apply x/y scaling in the __IsOnScreen() function
+                var _xOffset = sprite_get_xoffset(sprite);
+                var _yOffset = sprite_get_yoffset(sprite);
+                var _x = max(_xOffset, sprite_get_width( sprite) - _xOffset);
+                var _y = max(_yOffset, sprite_get_height(sprite) - _yOffset);
+                
+                __radius = sqrt(_x*_x + _y*_y);
+            }
+            else
+            {
+                __radius = 0;
+            }
+        }
+    }
+    
+    static AddToRenderer = function(_renderer)
+    {
+        if (__destroyed) return;
+        array_push(_renderer.__lightOverlayArray, weak_ref_create(self));
+    }
+    
+    static RemoveFromRenderer = function(_renderer)
+    {
+        var _array = _renderer.__lightOverlayArray;
+        var _i = array_length(_array) - 1;
+        repeat(array_length(_array))
+        {
+            var _weak = _array[_i];
+            if (weak_ref_alive(_weak))
+            {
+                if (_weak.ref == self) array_delete(_array, _i, 1);
+            }
+            else
+            {
+                array_delete(_array, _i, 1);
+            }
+            
+            --_i;
+        }
+    }
+    
+    static __IsOnScreen = function(_cameraL, _cameraT, _cameraR, _cameraB)
+    {
+        var _radius = __radius*max(xscale, yscale);
+        return (!__destroyed && visible && __BulbRectInRect(x - _radius, y - _radius, x + _radius, y + _radius, _cameraL, _cameraT, _cameraR, _cameraB));
+    }
+    
+    if (_renderer != undefined) AddToRenderer(_renderer);
+}

+ 13 - 0
scripts/BulbLightOverlay/BulbLightOverlay.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbLightOverlay",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbLightOverlay",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 86 - 0
scripts/BulbLightOverlay/BulbShadowOverlay.gml

@@ -0,0 +1,86 @@
+/// @param renderer
+
+function BulbShadowOverlay(_renderer) constructor
+{
+    visible = true;
+    
+    sprite = undefined;
+    image  = 0;
+    
+    x = 0;
+    y = 0;
+    
+    xscale = 1.0;
+    yscale = 1.0;
+    angle  = 0.0;
+    
+    alpha = 1.0;
+    
+    __oldSprite = undefined;
+    __radius    = 0;
+    __destroyed = false;
+    
+    static Destroy = function()
+    {
+        __destroyed = true;
+    }
+    
+    static __CheckSpriteDimensions = function()
+    {
+        // Redefine light sprite boundaries
+        if (sprite != __oldSprite)
+        {
+            __oldSprite = sprite;
+            
+            if ((sprite != undefined) && sprite_exists(sprite))
+            {
+                //Choose the longest axis of the sprite as the radius
+                //We apply x/y scaling in the __IsOnScreen() function
+                var _xOffset = sprite_get_xoffset(sprite);
+                var _yOffset = sprite_get_yoffset(sprite);
+                var _x = max(_xOffset, sprite_get_width( sprite) - _xOffset);
+                var _y = max(_yOffset, sprite_get_height(sprite) - _yOffset);
+                
+                __radius = sqrt(_x*_x + _y*_y);
+            }
+            else
+            {
+                __radius = 0;
+            }
+        }
+    }
+    
+    static AddToRenderer = function(_renderer)
+    {
+        if (__destroyed) return;
+        array_push(_renderer.__shadowOverlayArray, weak_ref_create(self));
+    }
+    
+    static RemoveFromRenderer = function(_renderer)
+    {
+        var _array = _renderer.__shadowOverlayArray;
+        var _i = array_length(_array) - 1;
+        repeat(array_length(_array))
+        {
+            var _weak = _array[_i];
+            if (weak_ref_alive(_weak))
+            {
+                if (_weak.ref == self) array_delete(_array, _i, 1);
+            }
+            else
+            {
+                array_delete(_array, _i, 1);
+            }
+            
+            --_i;
+        }
+    }
+    
+    static __IsOnScreen = function(_cameraL, _cameraT, _cameraR, _cameraB)
+    {
+        var _radius = __radius*max(xscale, yscale);
+        return (!__destroyed && visible && __BulbRectInRect(x - _radius, y - _radius, x + _radius, y + _radius, _cameraL, _cameraT, _cameraR, _cameraB));
+    }
+    
+    if (_renderer != undefined) AddToRenderer(_renderer);
+}

+ 26 - 0
scripts/BulbNormalMapClear/BulbNormalMapClear.gml

@@ -0,0 +1,26 @@
+// Feather disable all
+
+/// @param [baseSpecular=0]
+
+function BulbNormalMapClear(_baseSpecular = 0)
+{
+    static _u_fAlphaThreshold_Normal        = shader_get_uniform(__shdBulbNormal, "u_fAlphaThreshold");
+    static _u_fAlphaThreshold_Specular      = shader_get_uniform(__shdBulbSpecular, "u_fAlphaThreshold");
+    static _u_fAlphaThreshold_SpecularForce = shader_get_uniform(__shdBulbSpecularForce, "u_fAlphaThreshold");
+    
+    //Re-set the alpha threshold just in case
+    var _shader = shader_current();
+    
+    shader_set(__shdBulbNormal);
+    shader_set_uniform_f(_u_fAlphaThreshold_Normal, BULB_NORMAL_MAP_ALPHA_THRESHOLD);
+    
+    shader_set(__shdBulbSpecular);
+    shader_set_uniform_f(_u_fAlphaThreshold_Specular, BULB_NORMAL_MAP_ALPHA_THRESHOLD);
+    
+    shader_set(__shdBulbSpecularForce);
+    shader_set_uniform_f(_u_fAlphaThreshold_SpecularForce, BULB_NORMAL_MAP_ALPHA_THRESHOLD);
+    
+    shader_set(_shader);
+    
+    draw_clear_alpha(#0000FF, 1 - _baseSpecular);
+}

+ 13 - 0
scripts/BulbNormalMapClear/BulbNormalMapClear.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbNormalMapClear",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbNormalMapClear",
+  "parent":{
+    "name":"Normal Map",
+    "path":"folders/3rdParties/Bulb/Normal Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 8 - 0
scripts/BulbNormalMapDrawSelf/BulbNormalMapDrawSelf.gml

@@ -0,0 +1,8 @@
+// Feather disable all
+
+/// @param [spriteIndex]
+
+function BulbNormalMapDrawSelf(_spriteIndex = sprite_index)
+{
+    BulbNormalMapDrawSpriteExt(_spriteIndex, image_index, x, y, image_xscale, image_yscale, image_angle);
+}

+ 13 - 0
scripts/BulbNormalMapDrawSelf/BulbNormalMapDrawSelf.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbNormalMapDrawSelf",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbNormalMapDrawSelf",
+  "parent":{
+    "name":"Normal Map",
+    "path":"folders/3rdParties/Bulb/Normal Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 11 - 0
scripts/BulbNormalMapDrawSprite/BulbNormalMapDrawSprite.gml

@@ -0,0 +1,11 @@
+// Feather disable all
+
+/// @param sprite
+/// @param image
+/// @param x
+/// @param y
+
+function BulbNormalMapDrawSprite(_sprite, _image, _x, _y)
+{
+    draw_sprite_ext(_sprite, _image, _x, _y, 1, 1, 0, c_black, 1);
+}

+ 13 - 0
scripts/BulbNormalMapDrawSprite/BulbNormalMapDrawSprite.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbNormalMapDrawSprite",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbNormalMapDrawSprite",
+  "parent":{
+    "name":"Normal Map",
+    "path":"folders/3rdParties/Bulb/Normal Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 26 - 0
scripts/BulbNormalMapDrawSpriteExt/BulbNormalMapDrawSpriteExt.gml

@@ -0,0 +1,26 @@
+// Feather disable all
+
+/// @param sprite
+/// @param image
+/// @param x
+/// @param y
+/// @param xScale
+/// @param yScale
+/// @param angle
+
+function BulbNormalMapDrawSpriteExt(_sprite, _image, _x, _y, _xScale, _yScale, _angle)
+{
+    var _color = ((frac(_angle/360)*256*256*64) << 2)
+               | (((1000000*_xScale) >> 63) & 0x1)
+               | (((1000000*_yScale) >> 62) & 0x2);
+    
+    //The upper 22 bits in the 24-bit color are used to enough the rotation angle.
+    //
+    //We pack whether the xscale any yscale are negative into the two lowest significant bits of
+    //the red component of the color. We do this with a binary hack for extra speed.
+    //
+    //The multiplication by 1,000,000 is to ensure that scales less than 1 don't get rounded to
+    //0 when converted to an integer (which happens implicitly by the left shift operator).
+    
+    draw_sprite_ext(_sprite, _image, _x, _y, _xScale, _yScale, _angle, _color, 1);
+}

+ 13 - 0
scripts/BulbNormalMapDrawSpriteExt/BulbNormalMapDrawSpriteExt.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbNormalMapDrawSpriteExt",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbNormalMapDrawSpriteExt",
+  "parent":{
+    "name":"Normal Map",
+    "path":"folders/3rdParties/Bulb/Normal Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 7 - 0
scripts/BulbNormalMapShaderReset/BulbNormalMapShaderReset.gml

@@ -0,0 +1,7 @@
+// Feather disable all
+
+function BulbNormalMapShaderReset()
+{
+    shader_reset();
+    gpu_set_colorwriteenable(true, true, true, true);
+}

+ 13 - 0
scripts/BulbNormalMapShaderReset/BulbNormalMapShaderReset.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbNormalMapShaderReset",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbNormalMapShaderReset",
+  "parent":{
+    "name":"Normal Map",
+    "path":"folders/3rdParties/Bulb/Normal Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 9 - 0
scripts/BulbNormalMapShaderSet/BulbNormalMapShaderSet.gml

@@ -0,0 +1,9 @@
+// Feather disable all
+
+/// @param [forceFlat=false]
+
+function BulbNormalMapShaderSet(_forceFlat = false)
+{
+    shader_set(_forceFlat? __shdBulbNormalUp : __shdBulbNormal);
+    gpu_set_colorwriteenable(true, true, true, false);
+}

+ 13 - 0
scripts/BulbNormalMapShaderSet/BulbNormalMapShaderSet.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbNormalMapShaderSet",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbNormalMapShaderSet",
+  "parent":{
+    "name":"Normal Map",
+    "path":"folders/3rdParties/Bulb/Normal Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 531 - 0
scripts/BulbRenderer/BulbRenderer.gml

@@ -0,0 +1,531 @@
+/// Constructor. Creates a Bulb renderer struct that is responsible for the final renderering of
+/// lights in your game.
+/// 
+/// @param [camera]
+/// 
+/// Full list of variables:
+/// 
+/// `.ambientColor`         | `c_black`           | Baseline ambient light color
+/// `.ambientInGammaSpace`  | `false`             | Whether the above is in gamma space (`true`) or linear space {`false`)
+/// `.smooth`               | `true`              | Whether to use texture filtering (bilinear interpolation) where possible
+/// `.soft`                 | `true`              | Whether to use soft shadows
+/// `.selfLighting`         | `false`             | Whether to allow light to enter but not escape occluders. Hard shadow mode only
+/// `.exposure`             | `1.0`               | Exposure for the entire lighting render. Should usually be left at `1.0` when not in HDR mode
+/// `.ldrTonemap`           | `BULB_TONEMAP_NONE` | Tonemap to use when not in HDR mode. Should usually be left at `BULB_TONEMAP_NONE`
+/// `.hdr`                  | `false`             | Whether to use HDR rendering or not. HDR surface is 16-bit
+/// `.hdrTonemap`           | `BULB_TONEMAP_HBD`  | Tonemap to use when in HDR mode
+/// `.hdrBloomIntensity`    | `0`                 | Intensity of the bloom effect
+/// `.hdrBloomIterations`   | `3`                 | Number of Kawase blur iterations to apply to the bloom
+/// `.hdrBloomThresholdMin` | `0.6`               | Lower threshold for bloom cut-off
+/// `.hdrBloomThresholdMax` | `0.8`               | Upper threshold for bloom cut-off
+/// `.normalMap`            | Config macro        | Whether normal mapping should be used. Defaults to `BULB_DEFAULT_USE_NORMAL_MAP`
+/// 
+/// Full list of methods:
+/// 
+/// `.Free()`
+/// `.SetCamera(camera)`
+/// `.GetCamera()`
+/// `.SetSurfaceDimensions(width, height)`
+/// `.GetSurfaceDimensions()`
+/// `.Update()`
+/// `.GetOutputSurface(surface)`
+/// `.DrawLitSurface(surface, x, y, width, height, [textureFiltering], [alphaBlend])`
+/// `.GetTonemap()`
+/// `.GetLightSurface()`
+/// `.GetLightValue(x, y)`
+/// `.GetNormalMapSurface()
+/// `.DrawNormalMapDebug(x, y, width, height)`
+/// `.RefreshStaticOccluders()`
+
+function BulbRenderer(_camera) constructor
+{
+    static _system = __BulbSystem();
+    
+    static _vformat3DNormal = (function()
+    {
+        vertex_format_begin();
+        vertex_format_add_position_3d();
+        vertex_format_add_normal();
+        return vertex_format_end();
+    })();
+
+    static _vformat3DNormalTex = (function()
+    {
+        vertex_format_begin();
+        vertex_format_add_position_3d();
+        vertex_format_add_normal();
+        vertex_format_add_texcoord();
+        return vertex_format_end();
+    })();
+    
+    
+    
+    SetCamera = function(_camera)
+    {
+        if (__cameraImplicit)
+        {
+            camera_destroy(__camera);
+        }
+        
+        if (_camera != undefined)
+        {
+            __camera         = _camera;
+            __cameraImplicit = false;
+        }
+        else
+        {
+            __camera         = camera_create_view(0, 0, room_width, room_height, 0,   noone, 0, 0, 0, 0);
+            __cameraImplicit = true;
+        }
+        
+        //Set the lighting surface dimensions from the camera
+        var _projMatrix = camera_get_proj_mat(__camera);
+        var _width  = round(abs(2/_projMatrix[0]));
+        var _height = round(abs(2/_projMatrix[5]));
+        SetSurfaceDimensions(_width, _height);
+    }
+    
+    GetCamera = function()
+    {
+        return __camera;
+    }
+    
+    SetSurfaceDimensions = function(_width, _height)
+    {
+        __surfaceWidth  = _width;
+        __surfaceHeight = _height;
+        
+        GetLightSurface();
+    }
+    
+    GetSurfaceDimensions = function()
+    {
+        static _result = {};
+        
+        _result.width  = __surfaceWidth;
+        _result.height = __surfaceHeight;
+        
+        return _result;
+    }
+    
+    Update = function()
+    {
+        //Deploy PROPER MATHS in case the dev is using matrices
+        
+        var _viewMatrix = camera_get_view_mat(__camera);
+        var _projMatrix = camera_get_proj_mat(__camera);
+        
+        var _cameraCos =  _viewMatrix[ 0];
+        var _cameraSin =  _viewMatrix[ 1];
+        var _matrixX   = -_viewMatrix[12];
+        var _matrixY   = -_viewMatrix[13];
+        
+        var _cameraCX =  _matrixX*_cameraCos + _matrixY*_cameraSin;
+        var _cameraCY = -_matrixX*_cameraSin + _matrixY*_cameraCos;
+        var _cameraW  = round(abs(2/_projMatrix[0]));
+        var _cameraH  = round(abs(2/_projMatrix[5]));
+        
+        var _rotatedW = _cameraW*abs(_cameraCos) + _cameraH*abs(_cameraSin);
+        var _rotatedH = _cameraW*abs(_cameraSin) + _cameraH*abs(_cameraCos);
+        
+        var _boundaryL = _cameraCX - _rotatedW/2;
+        var _boundaryT = _cameraCY - _rotatedH/2;
+        var _boundaryR = _cameraCX + _rotatedW/2;
+        var _boundaryB = _cameraCY + _rotatedH/2;
+        
+        var _boundaryExpandedL = _boundaryL - BULB_DYNAMIC_OCCLUDER_RANGE;
+        var _boundaryExpandedT = _boundaryT - BULB_DYNAMIC_OCCLUDER_RANGE;
+        var _boundaryExpandedR = _boundaryR + BULB_DYNAMIC_OCCLUDER_RANGE;
+        var _boundaryExpandedB = _boundaryB + BULB_DYNAMIC_OCCLUDER_RANGE;
+        
+        //Force a regeneration of vertex buffers if we're swapped between hard/soft lights
+        if (soft != __oldSoft)
+        {
+            __oldSoft = soft;
+            __FreeVertexBuffers();
+        }
+        
+        //Determine whether we actually want HDR
+        var _hdr = (hdr && _system.__hdrAvailable);
+        
+        //Force regeneration/freeing of surfaces if the HDR state has changed
+        if (_hdr != __oldHDR)
+        {
+            __oldHDR = _hdr;
+            
+            if (__lightSurface != undefined)
+            {
+                surface_free(__lightSurface);
+                __lightSurface = undefined;
+            }
+            
+            if (not _hdr)
+            {
+                __FreeOutputSurface();
+            }
+        }
+        
+        //Manage bloom surfaces if the number of iterations has changed
+        if ((not _hdr) || (hdrBloomIterations != __oldHDRBloomIterations))
+        {
+            __FreeBloomSurfaces();
+        }
+        
+        //Free up memory if the normal map state has changed
+        if ((not normalMap) && __oldNormalMap)
+        {
+            __FreeNormalMapSurface();
+        }
+        
+        //Construct our wipe/static/dynamic vertex buffers
+        __UpdateVertexBuffers(_boundaryExpandedL, _boundaryExpandedT, _boundaryExpandedR, _boundaryExpandedB);
+        
+        //Create accumulating renderer surface
+        surface_set_target(GetLightSurface());
+        
+        //Set the view/projection matrices
+        camera_apply(__camera);
+        
+        //Set up some GPU state
+        var _oldNoCulling = gpu_get_cullmode();
+        var _oldTexFilter = gpu_get_tex_filter();
+        gpu_set_cullmode(cull_noculling);
+        gpu_set_tex_filter(smooth);
+        
+        //Clear the light surface with the ambient colour
+        draw_clear(__GetAmbientColor());
+        
+        //Accumulate lights and shadows onto the lighting surface
+        __AccumulateAmbienceSprite(_boundaryL, _boundaryT, _boundaryR, _boundaryB);
+        
+        if (soft)
+        {
+            __AccumulateSoftLights(_boundaryL, _boundaryT, _boundaryR, _boundaryB, _cameraCX, _cameraCY, _cameraW, _cameraH, _cameraCos, _cameraSin, selfLighting? -1 : 1);
+        }
+        else
+        {
+            __AccumulateHardLights(_boundaryL, _boundaryT, _boundaryR, _boundaryB, _cameraCX, _cameraCY, _cameraW, _cameraH, _cameraCos, _cameraSin, selfLighting? -1 : 1);
+        }
+        
+        __AccumulateShadowOverlay(_boundaryL, _boundaryT, _boundaryR, _boundaryB);
+        __AccumulateLightOverlay(_boundaryL, _boundaryT, _boundaryR, _boundaryB);
+        
+        //Restore default behaviour
+        gpu_set_colorwriteenable(true, true, true, true);
+        gpu_set_blendmode(bm_normal);
+        gpu_set_cullmode(_oldNoCulling);
+        gpu_set_tex_filter(_oldTexFilter);
+        
+        surface_reset_target();
+    }
+    
+    __GetTonemapShader = function()
+    {
+        var _hdrTonemap = GetTonemap();
+        if (_hdrTonemap == BULB_TONEMAP_NONE)
+        {
+            return __shdBulbTonemapNone;
+        }
+        else if (_hdrTonemap == BULB_TONEMAP_CLAMP)
+        {
+            return __shdBulbTonemapClamp;
+        }
+        else if (_hdrTonemap == BULB_TONEMAP_REINHARD)
+        {
+            return __shdBulbTonemapReinhard;
+        }
+        else if (_hdrTonemap == BULB_TONEMAP_REINHARD_EXTENDED)
+        {
+            return __shdBulbTonemapReinhardExtended;
+        }
+        else if (_hdrTonemap == BULB_TONEMAP_ACES)
+        {
+            return __shdBulbTonemapACES;
+        }
+        else if (_hdrTonemap == BULB_TONEMAP_UNCHARTED2)
+        {
+            return __shdBulbTonemapUncharted2;
+        }
+        else if (_hdrTonemap == BULB_TONEMAP_UNREAL3)
+        {
+            return __shdBulbTonemapUnreal3;
+        }
+        else if (_hdrTonemap == BULB_TONEMAP_HBD)
+        {
+            return __shdBulbTonemapHBD;
+        }
+        else
+        {
+            return __shdBulbTonemapBadGamma;
+        }
+    }
+    
+    GetOutputSurface = function(_surface)
+    {
+        var _surfaceWidth  = surface_get_width( _surface);
+        var _surfaceHeight = surface_get_height(_surface);
+        __GetOutputSurface(_surfaceWidth, _surfaceHeight);
+        
+        surface_set_target(__outputSurface);
+        DrawLitSurface(_surface, 0, 0, _surfaceWidth, _surfaceHeight, false, false);
+        surface_reset_target();
+        
+        return __outputSurface;
+    }
+    
+    DrawLitSurface = function(_surface, _x, _y, _width, _height, _textureFiltering = undefined, _alphaBlend = undefined)
+    {
+        if (surface_get_target() == _surface)
+        {
+            __BulbError("Cannot call .DrawLitSurface() when the destination surface and drawn surface are the same\nIf you are drawing the application surface, use a Post-Draw event or GUI draw event");
+        }
+        
+        var _oldTextureFiltering = gpu_get_tex_filter();
+        var _oldAlphaBlend       = gpu_get_blendenable();
+        
+        if (_textureFiltering != undefined) gpu_set_tex_filter(_textureFiltering);
+        if (_alphaBlend != undefined) gpu_set_blendenable(_alphaBlend);
+        
+        if ((__lightSurface != undefined) && surface_exists(__lightSurface))
+        {
+            if (hdr && _system.__hdrAvailable
+            && (hdrBloomIntensity > 0) && (hdrBloomIterations >= 1)
+            && (__lightSurface != undefined) && surface_exists(__lightSurface))
+            {
+                __KawaseBloom(__lightSurface, 1/max(0.0001, exposure));
+            }
+            
+            var _shader = __GetTonemapShader();
+            var _exposureUniform = shader_get_uniform(_shader, "u_fExposure");
+            var _sampler = shader_get_sampler_index(_shader, "u_sLightMap");
+            
+            shader_set(_shader);
+            shader_set_uniform_f(_exposureUniform, exposure);
+            texture_set_stage(_sampler, surface_get_texture(__lightSurface));
+            gpu_set_tex_filter_ext(_sampler, smooth);
+            draw_surface_stretched(_surface, _x, _y, _width, _height);
+            shader_reset();
+        }
+        else
+        {
+            draw_surface_stretched(_surface, _x, _y, _width, _height);
+        }
+        
+        gpu_set_tex_filter(_oldTextureFiltering);
+        gpu_set_blendenable(_oldAlphaBlend);
+    }
+    
+    __KawaseBloom = function(_surface, _thresholdCoeff)
+    {
+        static _u_fIntensity = shader_get_uniform(__shdBulbIntensity, "u_fIntensity");
+        static _u_vThreshold = shader_get_uniform(__shdBulbKawaseDownWithThreshold, "u_vThreshold");
+        
+        var _surfaceWidth  = surface_get_width( _surface);
+        var _surfaceHeight = surface_get_height(_surface);
+        
+        var _bloomSurfaceArray = __bloomSurfaceArray;
+        
+        //Create new bloom surfaces on demand
+        if (array_length(_bloomSurfaceArray) < hdrBloomIterations-1)
+        {
+            __FreeBloomSurfaces();
+            
+            var _bloomWidth  = _surfaceWidth;
+            var _bloomHeight = _surfaceHeight;
+            
+            //Work around compile error in LTS
+            var _surface_create = surface_create;
+            
+            var _i = 0;
+            repeat(hdrBloomIterations)
+            {
+                _bloomWidth  = _bloomWidth  div 2;
+                _bloomHeight = _bloomHeight div 2;
+                _bloomSurfaceArray[_i] = _surface_create(_bloomWidth, _bloomHeight, surface_rgba16float);
+                
+                ++_i;
+            }
+        }
+        
+        //Perform Kawase blur
+        var _oldTextureFiltering = gpu_get_tex_filter();
+        var _oldAlphaBlend       = gpu_get_blendenable();
+        
+        gpu_set_tex_filter(true);
+        gpu_set_blendenable(false);
+        
+        surface_set_target(_bloomSurfaceArray[0]);
+        shader_set(__shdBulbKawaseDownWithThreshold);
+        shader_set_uniform_f(_u_vThreshold, hdrBloomThresholdMin*_thresholdCoeff, hdrBloomThresholdMax*_thresholdCoeff);
+        shader_set_uniform_f(shader_get_uniform(__shdBulbKawaseDownWithThreshold, "u_vTexel"), texture_get_texel_width(surface_get_texture(_surface)), texture_get_texel_height(surface_get_texture(_surface)));
+        draw_surface_stretched(_surface, 0, 0, surface_get_width(_bloomSurfaceArray[0]), surface_get_height(_bloomSurfaceArray[0]));
+        shader_reset();
+        surface_reset_target();
+        
+        if (hdrBloomIterations >= 2)
+        {
+            var _i = 1;
+            repeat(hdrBloomIterations-1)
+            {
+                surface_set_target(_bloomSurfaceArray[_i]);
+                    shader_set(__shdBulbKawaseDown);
+                    shader_set_uniform_f(shader_get_uniform(__shdBulbKawaseDown, "u_vTexel"), texture_get_texel_width(surface_get_texture(_bloomSurfaceArray[_i-1])), texture_get_texel_height(surface_get_texture(_bloomSurfaceArray[_i-1])));
+                    draw_surface_stretched(_bloomSurfaceArray[_i-1], 0, 0, surface_get_width(_bloomSurfaceArray[_i]), surface_get_height(_bloomSurfaceArray[_i]));
+                surface_reset_target();
+                    
+                ++_i;
+            }
+            
+            var _i = hdrBloomIterations-1;
+            repeat(hdrBloomIterations-1)
+            {
+                surface_set_target(_bloomSurfaceArray[_i-1]);
+                    shader_set(__shdBulbKawaseUp);
+                    shader_set_uniform_f(shader_get_uniform(__shdBulbKawaseUp, "u_vTexel"), texture_get_texel_width(surface_get_texture(_bloomSurfaceArray[_i])), texture_get_texel_height(surface_get_texture(_bloomSurfaceArray[_i])));
+                    draw_surface_stretched(_bloomSurfaceArray[_i], 0, 0, surface_get_width(_bloomSurfaceArray[_i-1]), surface_get_height(_bloomSurfaceArray[_i-1]));
+                surface_reset_target();
+                
+                --_i;
+            }
+        }
+            
+        gpu_set_blendenable(true);
+        
+        surface_set_target(_surface);
+            
+            gpu_set_blendmode_ext(bm_one, bm_one);
+            shader_set(__shdBulbIntensity);
+            shader_set_uniform_f(_u_fIntensity, hdrBloomIntensity);
+            draw_surface_stretched_ext(_bloomSurfaceArray[0], 0, 0, _surfaceWidth, _surfaceHeight, c_white, 1);
+            
+            gpu_set_blendmode(bm_normal);
+            shader_reset();
+            
+        surface_reset_target();
+        
+        gpu_set_tex_filter(_oldTextureFiltering);
+        gpu_set_blendenable(_oldAlphaBlend);
+    }
+    
+    Free = function()
+    {
+        __FreeVertexBuffers();
+        __FreeLightSurface();
+        __FreeOutputSurface();
+        __FreeBloomSurfaces();
+        __FreeNormalMapSurface();
+        
+        var _nullFunc = function() {}
+        
+        //BulbRenderer()
+        SetCamera              = _nullFunc;
+        GetCamera              = _nullFunc;
+        SetSurfaceDimensions   = _nullFunc;
+        GetSurfaceDimensions   = _nullFunc;
+        Update                 = _nullFunc;
+        GetOutputSurface       = _nullFunc;
+        DrawLitSurface         = _nullFunc;
+        GetTonemap             = _nullFunc;
+        RefreshStaticOccluders = _nullFunc;
+        __KawaseBloom          = _nullFunc;
+        
+        //__BulbRendererDefineHDR()
+        __GetOutputSurface  = _nullFunc;
+        __FreeOutputSurface = _nullFunc;
+        __FreeBloomSurfaces = _nullFunc;
+        
+        //__BulbRendererDefineNormal()
+        GetNormalMapSurface    = _nullFunc;
+        DrawNormalMapDebug     = _nullFunc;
+        __FreeNormalMapSurface = _nullFunc;
+        
+        //__BulbRendererDefineOverlayUnderlay()
+        __AccumulateAmbienceSprite = _nullFunc;
+        __AccumulateShadowOverlay  = _nullFunc;
+        __AccumulateLightOverlay   = _nullFunc;
+        
+        //__BulbRendererDefineAccumulateSoft()
+        __AccumulateSoftLights = _nullFunc;
+        
+        //__BulbRendererDefineAccumulateHard()
+        __AccumulateHardLights = _nullFunc;
+        
+        //__BulbRendererDefineVertexBuffers()
+        RefreshStaticOccluders = _nullFunc;
+        __FreeVertexBuffers    = _nullFunc;
+        __UpdateVertexBuffers  = _nullFunc;
+        
+        //__BulbRendererDefineLight()
+        GetLightSurface    = _nullFunc;
+        __FreeLightSurface = _nullFunc;
+        GetLightValue      = _nullFunc;
+    }
+    
+    GetTonemap = function()
+    {
+        return (hdr && _system.__hdrAvailable)? hdrTonemap : ldrTonemap;
+    }
+    
+    __GetAmbientColor = function()
+    {
+        if (GetTonemap() == BULB_TONEMAP_BAD_GAMMA)
+        {
+            if (ambientInGammaSpace)
+            {
+                return ambientColor;
+            }
+            else
+            {
+                return make_color_rgb(255*power(color_get_red(  ambientColor)/255, 1/BULB_GAMMA),
+                                      255*power(color_get_green(ambientColor)/255, 1/BULB_GAMMA),
+                                      255*power(color_get_blue( ambientColor)/255, 1/BULB_GAMMA));
+            }
+        }
+        else
+        {
+            if (ambientInGammaSpace)
+            {
+                return make_color_rgb(255*power(color_get_red(  ambientColor)/255, BULB_GAMMA),
+                                      255*power(color_get_green(ambientColor)/255, BULB_GAMMA),
+                                      255*power(color_get_blue( ambientColor)/255, BULB_GAMMA));
+            }
+            else
+            {
+                return ambientColor;
+            }
+        }
+    }
+    
+    
+    
+    //Assign the ambient colour used for the darkest areas of the screen. This can be changed on the fly
+    ambientColor = c_black;
+    ambientInGammaSpace = false;
+    
+    //The smoothing mode controls texture filtering both when accumulating lights and when drawing the resulting surface
+    smooth = true;
+    
+    selfLighting = false;
+    
+    soft = true;
+    __oldSoft = undefined;
+    
+    exposure   = 1;
+    ldrTonemap = BULB_TONEMAP_NONE;
+    
+    __camera         = undefined;
+    __cameraImplicit = false;
+    
+    __surfaceWidth  = -1;
+    __surfaceHeight = -1;
+    
+    __BulbRendererDefineHDR();
+    __BulbRendererDefineNormal();
+    __BulbRendererDefineOverlayUnderlay();
+    __BulbRendererDefineAccumulateSoft();
+    if (_system.__hasStencil) __BulbRendererDefineAccumulateHard() else __BulbRendererDefineAccumulateHardNoStencil();
+    __BulbRendererDefineVertexBuffers();
+    __BulbRendererDefineLight();
+    
+    SetCamera(_camera);
+}

+ 13 - 0
scripts/BulbRenderer/BulbRenderer.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbRenderer",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbRenderer",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 84 - 0
scripts/BulbShadowOverlay/BulbShadowOverlay.gml

@@ -0,0 +1,84 @@
+/// @param renderer
+
+function BulbShadowOverlay(_renderer) constructor
+{
+    visible = true;
+    
+    x = 0;
+    y = 0;
+    
+    sprite = undefined;
+    image  = 0;
+    xscale = 1.0;
+    yscale = 1.0;
+    angle  = 0.0;
+    alpha  = 1.0;
+    
+    __oldSprite = undefined;
+    __radius    = 0;
+    __destroyed = false;
+    
+    static Destroy = function()
+    {
+        __destroyed = true;
+    }
+    
+    static __CheckSpriteDimensions = function()
+    {
+        // Redefine light sprite boundaries
+        if (sprite != __oldSprite)
+        {
+            __oldSprite = sprite;
+            
+            if ((sprite != undefined) && sprite_exists(sprite))
+            {
+                //Choose the longest axis of the sprite as the radius
+                //We apply x/y scaling in the __IsOnScreen() function
+                var _xOffset = sprite_get_xoffset(sprite);
+                var _yOffset = sprite_get_yoffset(sprite);
+                var _x = max(_xOffset, sprite_get_width( sprite) - _xOffset);
+                var _y = max(_yOffset, sprite_get_height(sprite) - _yOffset);
+                
+                __radius = sqrt(_x*_x + _y*_y);
+            }
+            else
+            {
+                __radius = 0;
+            }
+        }
+    }
+    
+    static AddToRenderer = function(_renderer)
+    {
+        if (__destroyed) return;
+        array_push(_renderer.__shadowOverlayArray, weak_ref_create(self));
+    }
+    
+    static RemoveFromRenderer = function(_renderer)
+    {
+        var _array = _renderer.__shadowOverlayArray;
+        var _i = array_length(_array) - 1;
+        repeat(array_length(_array))
+        {
+            var _weak = _array[_i];
+            if (weak_ref_alive(_weak))
+            {
+                if (_weak.ref == self) array_delete(_array, _i, 1);
+            }
+            else
+            {
+                array_delete(_array, _i, 1);
+            }
+            
+            --_i;
+        }
+    }
+    
+    static __IsOnScreen = function(_cameraL, _cameraT, _cameraR, _cameraB)
+    {
+        var _radius = __radius*max(xscale, yscale);
+        return (!__destroyed && visible && __BulbRectInRect(x - _radius, y - _radius, x + _radius, y + _radius, _cameraL, _cameraT, _cameraR, _cameraB));
+    }
+    
+    if (_renderer != undefined) AddToRenderer(_renderer);
+}

+ 13 - 0
scripts/BulbShadowOverlay/BulbShadowOverlay.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbShadowOverlay",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbShadowOverlay",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 9 - 0
scripts/BulbSpecularMapDrawSelf/BulbSpecularMapDrawSelf.gml

@@ -0,0 +1,9 @@
+// Feather disable all
+
+/// @param value
+/// @param [spriteIndex]
+
+function BulbSpecularMapDrawSelf(_value, _spriteIndex = sprite_index)
+{
+    BulbSpecularMapDrawSpriteExt(_spriteIndex, image_index, x, y, image_xscale, image_yscale, image_angle, _value);
+}

+ 13 - 0
scripts/BulbSpecularMapDrawSelf/BulbSpecularMapDrawSelf.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbSpecularMapDrawSelf",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbSpecularMapDrawSelf",
+  "parent":{
+    "name":"Specular Map",
+    "path":"folders/3rdParties/Bulb/Normal Map/Specular Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 12 - 0
scripts/BulbSpecularMapDrawSprite/BulbSpecularMapDrawSprite.gml

@@ -0,0 +1,12 @@
+// Feather disable all
+
+/// @param sprite
+/// @param image
+/// @param x
+/// @param y
+/// @param value
+
+function BulbSpecularMapDrawSprite(_sprite, _image, _x, _y, _value)
+{
+    draw_sprite_ext(_sprite, _image, _x, _y, 1, 1, 0, c_black, _value);
+}

+ 13 - 0
scripts/BulbSpecularMapDrawSprite/BulbSpecularMapDrawSprite.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbSpecularMapDrawSprite",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbSpecularMapDrawSprite",
+  "parent":{
+    "name":"Specular Map",
+    "path":"folders/3rdParties/Bulb/Normal Map/Specular Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 15 - 0
scripts/BulbSpecularMapDrawSpriteExt/BulbSpecularMapDrawSpriteExt.gml

@@ -0,0 +1,15 @@
+// Feather disable all
+
+/// @param sprite
+/// @param image
+/// @param x
+/// @param y
+/// @param xScale
+/// @param yScale
+/// @param angle
+/// @param value
+
+function BulbSpecularMapDrawSpriteExt(_sprite, _image, _x, _y, _xScale, _yScale, _angle, _value)
+{
+    draw_sprite_ext(_sprite, _image, _x, _y, _xScale, _yScale, _angle, c_black, _value);
+}

+ 13 - 0
scripts/BulbSpecularMapDrawSpriteExt/BulbSpecularMapDrawSpriteExt.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbSpecularMapDrawSpriteExt",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbSpecularMapDrawSpriteExt",
+  "parent":{
+    "name":"Specular Map",
+    "path":"folders/3rdParties/Bulb/Normal Map/Specular Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 8 - 0
scripts/BulbSpecularMapShaderReset/BulbSpecularMapShaderReset.gml

@@ -0,0 +1,8 @@
+// Feather disable all
+
+function BulbSpecularMapShaderReset()
+{
+    shader_reset();
+    gpu_set_colorwriteenable(true, true, true, true);
+    gpu_set_blendmode(bm_normal);
+}

+ 13 - 0
scripts/BulbSpecularMapShaderReset/BulbSpecularMapShaderReset.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbSpecularMapShaderReset",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbSpecularMapShaderReset",
+  "parent":{
+    "name":"Specular Map",
+    "path":"folders/3rdParties/Bulb/Normal Map/Specular Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 10 - 0
scripts/BulbSpecularMapShaderSet/BulbSpecularMapShaderSet.gml

@@ -0,0 +1,10 @@
+// Feather disable all
+
+/// @param [forceValue=false]
+
+function BulbSpecularMapShaderSet(_forceValue = false)
+{
+    shader_set(_forceValue? __shdBulbSpecularForce : __shdBulbSpecular);
+    gpu_set_colorwriteenable(false, false, false, true);
+    gpu_set_blendmode_ext(bm_one, bm_zero);
+}

+ 13 - 0
scripts/BulbSpecularMapShaderSet/BulbSpecularMapShaderSet.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbSpecularMapShaderSet",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbSpecularMapShaderSet",
+  "parent":{
+    "name":"Specular Map",
+    "path":"folders/3rdParties/Bulb/Normal Map/Specular Map.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 91 - 0
scripts/BulbStaticOccluder/BulbStaticOccluder.gml

@@ -0,0 +1,91 @@
+/// @param renderer
+
+function BulbStaticOccluder(_renderer) constructor
+{
+    x = 0;
+    y = 0;
+    
+    xscale = 1.0;
+    yscale = 1.0;
+    angle  = 0.0;
+    
+    vertexArray = [];
+    
+    __destroyed = false;
+    
+    static Destroy = function()
+    {
+        __destroyed = true;
+    }
+    
+    static AddEdge = function(_x1, _y1, _x2, _y2)
+    {
+        if (__destroyed) return;
+        
+        array_push(vertexArray, _x1, _y1, _x2, _y2, _y2-_y1, _x1-_x2);
+    }
+    
+    static AddCircle = function(_radius, _x = 0, _y = 0, _edges = 24)
+    {
+        if (__destroyed) return;
+        
+        var _angle = 0;
+        var _angleStep = 360 / _edges;
+        
+        var _x2 = _x + lengthdir_x(_radius, _angle);
+        var _y2 = _y + lengthdir_y(_radius, _angle);
+        
+        repeat(_edges)
+        { 
+            _angle -= _angleStep;
+            
+            var _x1 = _x2;
+            var _y1 = _y2;
+            _x2 = _x + lengthdir_x(_radius, _angle);
+            _y2 = _y + lengthdir_y(_radius, _angle);
+            
+            array_push(vertexArray, _x1, _y1, _x2, _y2, _y2-_y1, _x1-_x2);
+        }
+    }
+    
+    static ClearEdges = function(_x1, _y1, _x2, _y2)
+    {
+        if (__destroyed) return;
+        
+        array_resize(vertexArray, 0);
+    }
+    
+    static AddToRenderer = function(_renderer)
+    {
+        if (__destroyed) return;
+        
+        array_push(_renderer.__staticOccludersArray, weak_ref_create(self));
+    }
+    
+    static RemoveFromRenderer = function(_renderer)
+    {
+        var _array = _renderer.__staticOccludersArray;
+        var _i = array_length(_array) - 1;
+        repeat(array_length(_array))
+        {
+            var _weak = _array[_i];
+            if (weak_ref_alive(_weak))
+            {
+                if (_weak.ref == self) array_delete(_array, _i, 1);
+            }
+            else
+            {
+                array_delete(_array, _i, 1);
+            }
+            
+            --_i;
+        }
+    }
+    
+    static __IsOnScreen = function(_cameraL, _cameraT, _cameraR, _cameraB)
+    {
+        return (!__destroyed && visible && __BulbRectInRect(__bboxXMin, __bboxYMin, __bboxXMax, __bboxYMax, _cameraL, _cameraT, _cameraR, _cameraB));
+    }
+    
+    if (_renderer != undefined) AddToRenderer(_renderer);
+}

+ 13 - 0
scripts/BulbStaticOccluder/BulbStaticOccluder.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbStaticOccluder",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbStaticOccluder",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 53 - 0
scripts/BulbSunlight/BulbSunlight.gml

@@ -0,0 +1,53 @@
+/// @param renderer
+/// @param angle
+
+function BulbSunlight(_renderer, _angle) constructor
+{
+    visible = true;
+    
+    normalMap  = BULB_DEFAULT_USE_NORMAL_MAP;
+    normalMapZ = BULB_DEFAULT_NORMAL_MAP_Z;
+    
+    angle     = _angle;
+    blend     = c_white;
+    intensity = 1.0;
+    
+    penumbraSize = 0.0;
+    
+    __oldSprite = undefined;
+    __radius    = 0;
+    __destroyed = false;
+    
+    static Destroy = function()
+    {
+        __destroyed = true;
+    }
+    
+    static AddToRenderer = function(_renderer)
+    {
+        if (__destroyed) return;
+        array_push(_renderer.__sunlightArray, weak_ref_create(self));
+    }
+    
+    static RemoveFromRenderer = function(_renderer)
+    {
+        var _array = _renderer.__sunlightArray;
+        var _i = array_length(_array) - 1;
+        repeat(array_length(_array))
+        {
+            var _weak = _array[_i];
+            if (weak_ref_alive(_weak))
+            {
+                if (_weak.ref == self) array_delete(_array, _i, 1);
+            }
+            else
+            {
+                array_delete(_array, _i, 1);
+            }
+            
+            --_i;
+        }
+    }
+    
+    if (_renderer != undefined) AddToRenderer(_renderer);
+}

+ 13 - 0
scripts/BulbSunlight/BulbSunlight.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"BulbSunlight",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"BulbSunlight",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 49 - 0
scripts/__BulbAddOcclusionHard/__BulbAddOcclusionHard.gml

@@ -0,0 +1,49 @@
+function __BulbAddOcclusionHard(_vbuff)
+{
+    //Set up basic transforms to turn relative coordinates in arr_shadowGeometry[] into world-space coordinates
+    var _sin = dsin(angle);
+    var _cos = dcos(angle);
+    
+    var _xSin = xscale*_sin;
+    var _xCos = xscale*_cos;
+    var _ySin = yscale*_sin;
+    var _yCos = yscale*_cos;
+    
+    //Loop through every line segment, remembering that we're storing coordinate data sequentially: { Ax1, Ay1, Bx1, Bx1,   Ax2, Ay2, Bx2, Bx2, ... }
+    var _vertexArray = vertexArray;
+    var _i = 0;
+    repeat(array_length(_vertexArray) div 6)
+    {
+        //Collect first coordinate pair
+        var _oldAx = _vertexArray[_i++];
+        var _oldAy = _vertexArray[_i++];
+        var _oldBx = _vertexArray[_i++];
+        var _oldBy = _vertexArray[_i++];
+        var _oldDx = _vertexArray[_i++];
+        var _oldDy = _vertexArray[_i++];
+        
+        //...and transform
+        var _newAx = x + _oldAx*_xCos + _oldAy*_ySin;
+        var _newAy = y - _oldAx*_xSin + _oldAy*_yCos;
+        var _newBx = x + _oldBx*_xCos + _oldBy*_ySin;
+        var _newBy = y - _oldBx*_xSin + _oldBy*_yCos;
+        var _newDx =     _oldDx*_xCos + _oldDy*_ySin;
+        var _newDy =   - _oldDx*_xSin + _oldDy*_yCos;
+        
+        //Add to the vertex buffer
+        vertex_position_3d(_vbuff,   _newAx, _newAy, 0);           vertex_normal(_vbuff,   _newDx, _newDy, 0);
+        vertex_position_3d(_vbuff,   _newBx, _newBy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy, 0);
+        vertex_position_3d(_vbuff,   _newBx, _newBy, 0);           vertex_normal(_vbuff,   _newDx, _newDy, 0);
+        
+        vertex_position_3d(_vbuff,   _newAx, _newAy, 0);           vertex_normal(_vbuff,   _newDx, _newDy, 0);
+        vertex_position_3d(_vbuff,   _newAx, _newAy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy, 0);
+        vertex_position_3d(_vbuff,   _newBx, _newBy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy, 0);
+        
+        if (BULB_COMPENSATE_FOR_NEAR_OCCLUDERS)
+        {
+            vertex_position_3d(_vbuff,   _newAx, _newAy, __BULB_ZFAR);                                 vertex_normal(_vbuff,   _newDx, _newDy, 0);
+            vertex_position_3d(_vbuff,   0.5*(_newAx + _newBx), 0.5*(_newAy + _newBy), 2*__BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy, 0);
+            vertex_position_3d(_vbuff,   _newBx, _newBy, __BULB_ZFAR);                                 vertex_normal(_vbuff,   _newDx, _newDy, 0);
+        }
+    }
+}

+ 13 - 0
scripts/__BulbAddOcclusionHard/__BulbAddOcclusionHard.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbAddOcclusionHard",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbAddOcclusionHard",
+  "parent":{
+    "name":"(Pay no attention to that man behind the curtain)",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain).yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 58 - 0
scripts/__BulbAddOcclusionSoft/__BulbAddOcclusionSoft.gml

@@ -0,0 +1,58 @@
+function __BulbAddOcclusionSoft(_vbuff)
+{
+    //Set up basic transforms to turn relative coordinates in arr_shadowGeometry[] into world-space coordinates
+    var _sin = dsin(angle);
+    var _cos = dcos(angle);
+    
+    var _xSin = xscale*_sin;
+    var _xCos = xscale*_cos;
+    var _ySin = yscale*_sin;
+    var _yCos = yscale*_cos;
+    
+    //Loop through every line segment, remembering that we're storing coordinate data sequentially: { Ax1, Ay1, Bx1, Bx1,   Ax2, Ay2, Bx2, Bx2, ... }
+    var _vertexArray = vertexArray;
+    var _i = 0;
+    repeat(array_length(_vertexArray) div 6)
+    {
+        //Collect first coordinate pair
+        var _oldAx = _vertexArray[_i++];
+        var _oldAy = _vertexArray[_i++];
+        var _oldBx = _vertexArray[_i++];
+        var _oldBy = _vertexArray[_i++];
+        var _oldDx = _vertexArray[_i++];
+        var _oldDy = _vertexArray[_i++];
+        
+        //...and transform
+        var _newAx = x + _oldAx*_xCos + _oldAy*_ySin;
+        var _newAy = y - _oldAx*_xSin + _oldAy*_yCos;
+        var _newBx = x + _oldBx*_xCos + _oldBy*_ySin;
+        var _newBy = y - _oldBx*_xSin + _oldBy*_yCos;
+        var _newDx =     _oldDx*_xCos + _oldDy*_ySin;
+        var _newDy =   - _oldDx*_xSin + _oldDy*_yCos;
+        
+        ////Add to the vertex buffer
+        vertex_position_3d(_vbuff,   _newAx, _newAy, 0);           vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  1, 1);
+        vertex_position_3d(_vbuff,   _newBx, _newBy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  1, 1);
+        vertex_position_3d(_vbuff,   _newBx, _newBy, 0);           vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  1, 1);
+                                                                  
+        vertex_position_3d(_vbuff,   _newAx, _newAy, 0);           vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  1, 1);
+        vertex_position_3d(_vbuff,   _newAx, _newAy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  1, 1);
+        vertex_position_3d(_vbuff,   _newBx, _newBy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  1, 1);
+        
+        if (BULB_COMPENSATE_FOR_NEAR_OCCLUDERS)
+        {
+            vertex_position_3d(_vbuff,   _newAx, _newAy, __BULB_ZFAR);                                 vertex_normal(_vbuff,   _newDx, _newDy, 0); vertex_texcoord(_vbuff,  1, 1);
+            vertex_position_3d(_vbuff,   0.5*(_newAx + _newBx), 0.5*(_newAy + _newBy), 2*__BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy, 0); vertex_texcoord(_vbuff,  1, 1);
+            vertex_position_3d(_vbuff,   _newBx, _newBy, __BULB_ZFAR);                                 vertex_normal(_vbuff,   _newDx, _newDy, 0); vertex_texcoord(_vbuff,  1, 1);
+        }
+        
+        //Add data for the soft shadows
+        vertex_position_3d(_vbuff,   _newAx, _newAy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  1, 0);
+        vertex_position_3d(_vbuff,   _newAx, _newAy, 0);           vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  0, 1);
+        vertex_position_3d(_vbuff,   _newAx, _newAy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy,  1); vertex_texcoord(_vbuff,  0, 0);
+                                                                                                                                             
+        vertex_position_3d(_vbuff,   _newBx, _newBy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy, -1); vertex_texcoord(_vbuff,  0, 0);
+        vertex_position_3d(_vbuff,   _newBx, _newBy, 0);           vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  0, 1);
+        vertex_position_3d(_vbuff,   _newBx, _newBy, __BULB_ZFAR); vertex_normal(_vbuff,   _newDx, _newDy,  0); vertex_texcoord(_vbuff,  1, 0);
+    }
+}

+ 13 - 0
scripts/__BulbAddOcclusionSoft/__BulbAddOcclusionSoft.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbAddOcclusionSoft",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbAddOcclusionSoft",
+  "parent":{
+    "name":"(Pay no attention to that man behind the curtain)",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain).yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 27 - 0
scripts/__BulbConfig/__BulbConfig.gml

@@ -0,0 +1,27 @@
+// Distance around the edge of the camera (in pixels) to draw dynamic occluders. Increase this
+// number if you have large dynamic occluders and are experiencing pop-in.
+#macro BULB_DYNAMIC_OCCLUDER_RANGE  100
+
+// Adds an extra triangle for each occluder to compensate for situations where a light might be
+// very close to an occluder. Normally, this would cause light to bleed through the wall. Setting
+// this macro to `true` will solve near-light problems but does incur a slight performance penalty.
+#macro BULB_COMPENSATE_FOR_NEAR_OCCLUDERS  false
+
+// Whether renderers, lights, and sunlight should default to having normal map support enabled.
+// This saves a lot of time setting `.normalMap` on everything that you create. Enabling normal
+// maps has a significant performance penalty so use this carefully.
+#macro BULB_DEFAULT_USE_NORMAL_MAP  false
+
+// The alpha threshold for sprites when drawing to the normal/specular map. Anything below this
+// value will be discarded by the shader.
+#macro BULB_NORMAL_MAP_ALPHA_THRESHOLD  0.5
+
+// How intense the specular map effect should be. This generally is only noticeable when using HDR
+// lighting. The specular map is packed into the alpha channel of the normal map surface.
+#macro BULB_SPECULAR_MAP_INTENSITY  10.0
+
+// The default notional "z height" for lights and sunlight. This z value is only used when
+// calculating normal map influence on lights. A lower value brings the light closer to the plane,
+// leading to a shallower angle of attack. This leads to more intense normal maps where the edges
+// of shapes will be highlighted more strongly than the tops of shapes, especially at distance.
+#macro BULB_DEFAULT_NORMAL_MAP_Z  0.2

+ 13 - 0
scripts/__BulbConfig/__BulbConfig.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbConfig",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbConfig",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 18 - 0
scripts/__BulbConstants/__BulbConstants.gml

@@ -0,0 +1,18 @@
+// Feather disable all
+
+#macro BULB_VERSION  "22.0.7"
+#macro BULB_DATE     "2024-09-25"
+
+#macro BULB_TONEMAP_NONE               0
+#macro BULB_TONEMAP_CLAMP              1
+#macro BULB_TONEMAP_REINHARD           2
+#macro BULB_TONEMAP_REINHARD_EXTENDED  3
+#macro BULB_TONEMAP_UNCHARTED2         4
+#macro BULB_TONEMAP_HBD                5
+#macro BULB_TONEMAP_UNREAL3            6
+#macro BULB_TONEMAP_ACES               7
+#macro BULB_TONEMAP_BAD_GAMMA          8
+
+#macro BULB_GAMMA  2.2
+
+#macro BULB_HDR_AVAILABLE  (__BulbSystem().__hdrAvailable)

+ 13 - 0
scripts/__BulbConstants/__BulbConstants.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbConstants",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbConstants",
+  "parent":{
+    "name":"Bulb",
+    "path":"folders/3rdParties/Bulb.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 16 - 0
scripts/__BulbError/__BulbError.gml

@@ -0,0 +1,16 @@
+// Feather disable all
+
+function __BulbError()
+{
+    var _string = "";
+    
+    var _i = 0
+    repeat(argument_count)
+    {
+        _string += string(argument[_i]);
+        ++_i;
+    }
+    
+    show_debug_message("Bulb: " + string_replace_all(_string, "\n", "\n          "));
+    show_error("Bulb:\n" + _string + "\n ", true);
+}

+ 13 - 0
scripts/__BulbError/__BulbError.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbError",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbError",
+  "parent":{
+    "name":"(Pay no attention to that man behind the curtain)",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain).yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 6 - 0
scripts/__BulbRectInRect/__BulbRectInRect.gml

@@ -0,0 +1,6 @@
+// Feather disable all
+
+function __BulbRectInRect(_ax1, _ay1, _ax2, _ay2, _bx1, _by1, _bx2, _by2)
+{
+    return !((_bx1 > _ax2) || (_bx2 < _ax1) || (_by1 > _ay2) || (_by2 < _ay1));
+}

+ 13 - 0
scripts/__BulbRectInRect/__BulbRectInRect.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRectInRect",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRectInRect",
+  "parent":{
+    "name":"(Pay no attention to that man behind the curtain)",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain).yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 207 - 0
scripts/__BulbRendererDefineAccumulateHard/__BulbRendererDefineAccumulateHard.gml

@@ -0,0 +1,207 @@
+// Feather disable all
+
+function __BulbRendererDefineAccumulateHard()
+{
+    __AccumulateHardLights = function(_boundaryL, _boundaryT, _boundaryR, _boundaryB, _cameraCX, _cameraCY, _cameraW, _cameraH, _cameraCos, _cameraSin, _normalCoeff)
+    {
+        static _shdBulbHardShadows_u_vLight                   = shader_get_uniform(__shdBulbHardShadows,              "u_vLight"      );
+        static _shdBulbHardShadows_u_fNormalCoeff             = shader_get_uniform(__shdBulbHardShadows,              "u_fNormalCoeff");
+        static _shdBulbHardShadowsSunlight_u_vLightVector     = shader_get_uniform(__shdBulbHardShadowsSunlight,      "u_vLightVector");
+        static _shdBulbHardShadowsSunlight_u_fNormalCoeff     = shader_get_uniform(__shdBulbHardShadowsSunlight,      "u_fNormalCoeff");
+        static _shdBulbLightWithNormalMap_u_vInfo             = shader_get_uniform(__shdBulbLightWithNormalMap,       "u_vInfo"       );
+        static _shdBulbLightWithoutNormalMap_u_fIntensity     = shader_get_uniform(__shdBulbLightWithoutNormalMap,    "u_fIntensity"  );
+        static _shdBulbSunlightWithNormalMap_u_vSunInfo       = shader_get_uniform(__shdBulbSunlightWithNormalMap,    "u_vInfo"       );
+        static _sshdBulbSunlightWithoutNormalMap_u_fIntensity = shader_get_uniform(__shdBulbSunlightWithoutNormalMap, "u_fIntensity"  );
+        
+        var _oldStencilRef       = gpu_get_stencil_ref();
+        var _oldStencilFunc      = gpu_get_stencil_func();
+        var _oldStencilPass      = gpu_get_stencil_pass();
+        var _oldStencilFail      = gpu_get_stencil_fail();
+        var _oldStencilDepthFail = gpu_get_stencil_depth_fail();
+        
+        var _staticVBuffer  = __staticVBuffer;
+        var _dynamicVBuffer = __dynamicVBuffer;
+        
+        var _rendererNormalMap = normalMap;
+        if (_rendererNormalMap)
+        {
+            shader_set(__shdBulbLightWithNormalMap);
+            texture_set_stage(shader_get_sampler_index(__shdBulbLightWithNormalMap, "u_sNormalMap"), surface_get_texture(GetNormalMapSurface()));
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_vCamera"), _cameraCX, _cameraCY, _cameraW/2, _cameraH/2);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_vCameraVector"), _cameraCos, _cameraSin);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_fSpecularIntensity"), hdr? BULB_SPECULAR_MAP_INTENSITY : 0);
+            
+            shader_set(__shdBulbSunlightWithNormalMap);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbSunlightWithNormalMap, "u_fSpecularIntensity"), hdr? BULB_SPECULAR_MAP_INTENSITY : 0);
+        }
+        
+        //bm_max requires some trickery with alpha to get good-looking results
+        //Determine the blend mode and "default" shader accordingly
+        gpu_set_blendmode(bm_add);
+        
+        //Set up the coefficient to flip normals
+        //We use this to control self-renderer
+        shader_set(__shdBulbHardShadows);
+        shader_set_uniform_f(_shdBulbHardShadows_u_fNormalCoeff, _normalCoeff);
+        
+        //Also do the same for our sunlight shader, provided we have any sunlight sources
+        if (array_length(__sunlightArray) > 0)
+        {
+            shader_set(__shdBulbHardShadowsSunlight);
+            shader_set_uniform_f(_shdBulbHardShadowsSunlight_u_fNormalCoeff, _normalCoeff);
+        }
+        
+        //Set our default shader
+        shader_set(__shdBulbIntensity);
+        
+        gpu_set_colorwriteenable(true, true, true, false);
+        
+        draw_clear_stencil(0);
+        gpu_set_stencil_enable(true);
+        gpu_set_stencil_pass(stencilop_replace);
+        gpu_set_stencil_fail(stencilop_keep);
+        gpu_set_stencil_depth_fail(stencilop_keep);
+        gpu_set_stencil_func(cmpfunc_always);
+        var _stencil = 0;
+        
+        var _i = 0;
+        repeat(array_length(__lightsArray))
+        {
+            var _weak = __lightsArray[_i];
+            if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+            {
+                array_delete(__lightsArray, _i, 1);
+            }
+            else
+            {
+                with(_weak.ref)
+                {
+                    if (visible)
+                    {
+                        __CheckSpriteDimensions();
+                        
+                        //If this light is active, do some drawing
+                        if (__IsOnScreen(_boundaryL, _boundaryT, _boundaryR, _boundaryB))
+                        {
+                            if (castShadows)
+                            {
+                                ++_stencil;
+                                if (_stencil >= 256)
+                                {
+                                    draw_clear_stencil(0);
+                                    _stencil = 1;
+                                }
+                                
+                                gpu_set_stencil_ref(_stencil);
+                                
+                                //Stencil out shadow areas
+                                shader_set(__shdBulbHardShadows);
+                                shader_set_uniform_f(_shdBulbHardShadows_u_vLight, x, y);
+                                
+                                vertex_submit(_staticVBuffer,  pr_trianglelist, -1);
+                                vertex_submit(_dynamicVBuffer, pr_trianglelist, -1);
+                                
+                                if (_rendererNormalMap && normalMap)
+                                {
+                                    shader_set(__shdBulbLightWithNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithNormalMap_u_vInfo, x, y, normalMapZ, intensity);
+                                }
+                                else
+                                {
+                                    shader_set(__shdBulbLightWithoutNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithoutNormalMap_u_fIntensity, intensity);
+                                }
+                                
+                                gpu_set_stencil_func(cmpfunc_greater);
+                                draw_sprite_ext(sprite, image, x, y, xscale, yscale, angle, blend, 1);
+                                gpu_set_stencil_func(cmpfunc_always);
+                            }
+                            else
+                            {
+                                //Just draw the sprite, no fancy stuff here
+                                if (_rendererNormalMap && normalMap)
+                                {
+                                    shader_set(__shdBulbLightWithNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithNormalMap_u_vInfo, x, y, normalMapZ, intensity);
+                                }
+                                else
+                                {
+                                    shader_set(__shdBulbLightWithoutNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithoutNormalMap_u_fIntensity, intensity);
+                                }
+                                
+                                draw_sprite_ext(sprite, image, x, y, xscale, yscale, angle, blend, 1);
+                            }
+                        }
+                    }
+                }
+                
+                ++_i;
+            }
+        }
+        
+        gpu_set_stencil_enable(true);
+        
+        var _i = 0;
+        repeat(array_length(__sunlightArray))
+        {
+            var _weak = __sunlightArray[_i];
+            if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+            {
+                array_delete(__sunlightArray, _i, 1);
+            }
+            else
+            {
+                with(_weak.ref)
+                {
+                    if (visible)
+                    {
+                        ++_stencil;
+                        if (_stencil >= 256)
+                        {
+                            draw_clear_stencil(0);
+                            _stencil = 1;
+                        }
+                        
+                        gpu_set_stencil_ref(_stencil);
+                        
+                        //Stencil out shadow areas
+                        shader_set(__shdBulbHardShadowsSunlight);
+                        shader_set_uniform_f(_shdBulbHardShadowsSunlight_u_vLightVector, dcos(angle), -dsin(angle));
+                        
+                        vertex_submit(_staticVBuffer,  pr_trianglelist, -1);
+                        vertex_submit(_dynamicVBuffer, pr_trianglelist, -1);
+                        
+                        if (_rendererNormalMap && normalMap)
+                        {
+                            shader_set(__shdBulbSunlightWithNormalMap);
+                            shader_set_uniform_f(_shdBulbSunlightWithNormalMap_u_vSunInfo, dcos(angle), -dsin(angle), normalMapZ, intensity);
+                        }
+                        else
+                        {
+                            shader_set(__shdBulbLightWithoutNormalMap);
+                            shader_set_uniform_f(_shdBulbLightWithoutNormalMap_u_fIntensity, intensity);
+                        }
+                                
+                        gpu_set_stencil_func(cmpfunc_greater);
+                        draw_sprite_ext(__sprBulbPixel, 0, _boundaryL, _boundaryT, _boundaryR - _boundaryL, _boundaryB - _boundaryT, 0, blend, 1);
+                        gpu_set_stencil_func(cmpfunc_always);
+                    }
+                }
+                
+                ++_i;
+            }
+        }
+        
+        shader_reset();
+        gpu_set_colorwriteenable(true, true, true, true);
+        gpu_set_blendmode(bm_normal);
+        
+        gpu_set_stencil_enable(false);
+        gpu_set_stencil_ref(_oldStencilRef);
+        gpu_set_stencil_func(_oldStencilFunc);
+        gpu_set_stencil_pass(_oldStencilPass);
+        gpu_set_stencil_fail(_oldStencilFail);
+        gpu_set_stencil_depth_fail(_oldStencilDepthFail);
+    }
+}

+ 13 - 0
scripts/__BulbRendererDefineAccumulateHard/__BulbRendererDefineAccumulateHard.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRendererDefineAccumulateHard",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRendererDefineAccumulateHard",
+  "parent":{
+    "name":"Renderer Definitions",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 200 - 0
scripts/__BulbRendererDefineAccumulateHardNoStencil/__BulbRendererDefineAccumulateHardNoStencil.gml

@@ -0,0 +1,200 @@
+// Feather disable all
+
+function __BulbRendererDefineAccumulateHardNoStencil()
+{
+    __AccumulateHardLights = function(_boundaryL, _boundaryT, _boundaryR, _boundaryB, _cameraCX, _cameraCY, _cameraW, _cameraH, _cameraCos, _cameraSin, _normalCoeff)
+    {
+        static _shdBulbHardShadows_u_vLight                   = shader_get_uniform(__shdBulbHardShadows,              "u_vLight"      );
+        static _shdBulbHardShadows_u_fNormalCoeff             = shader_get_uniform(__shdBulbHardShadows,              "u_fNormalCoeff");
+        static _shdBulbHardShadowsSunlight_u_vLightVector     = shader_get_uniform(__shdBulbHardShadowsSunlight,      "u_vLightVector");
+        static _shdBulbHardShadowsSunlight_u_fNormalCoeff     = shader_get_uniform(__shdBulbHardShadowsSunlight,      "u_fNormalCoeff");
+        static _shdBulbLightWithNormalMap_u_vInfo             = shader_get_uniform(__shdBulbLightWithNormalMap,       "u_vInfo"       );
+        static _shdBulbLightWithoutNormalMap_u_fIntensity     = shader_get_uniform(__shdBulbLightWithoutNormalMap,    "u_fIntensity"  );
+        static _shdBulbSunlightWithNormalMap_u_vSunInfo       = shader_get_uniform(__shdBulbSunlightWithNormalMap,    "u_vInfo"       );
+        static _sshdBulbSunlightWithoutNormalMap_u_fIntensity = shader_get_uniform(__shdBulbSunlightWithoutNormalMap, "u_fIntensity"  );
+        
+        var _staticVBuffer  = __staticVBuffer;
+        var _dynamicVBuffer = __dynamicVBuffer;
+        
+        var _rendererNormalMap = normalMap;
+        if (_rendererNormalMap)
+        {
+            shader_set(__shdBulbLightWithNormalMap);
+            texture_set_stage(shader_get_sampler_index(__shdBulbLightWithNormalMap, "u_sNormalMap"), surface_get_texture(GetNormalMapSurface()));
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_vCamera"), _cameraCX, _cameraCY, _cameraW/2, _cameraH/2);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_vCameraVector"), _cameraCos, _cameraSin);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_fSpecularIntensity"), hdr? BULB_SPECULAR_MAP_INTENSITY : 0);
+            
+            shader_set(__shdBulbSunlightWithNormalMap);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbSunlightWithNormalMap, "u_fSpecularIntensity"), hdr? BULB_SPECULAR_MAP_INTENSITY : 0);
+        }
+        
+        //bm_max requires some trickery with alpha to get good-looking results
+        //Determine the blend mode and "default" shader accordingly
+        gpu_set_blendmode(bm_add);
+        
+        //Set up the coefficient to flip normals
+        //We use this to control self-renderer
+        shader_set(__shdBulbHardShadows);
+        shader_set_uniform_f(_shdBulbHardShadows_u_fNormalCoeff, _normalCoeff);
+        
+        //Also do the same for our sunlight shader, provided we have any sunlight sources
+        if (array_length(__sunlightArray) > 0)
+        {
+            shader_set(__shdBulbHardShadowsSunlight);
+            shader_set_uniform_f(_shdBulbHardShadowsSunlight_u_fNormalCoeff, _normalCoeff);
+        }
+        
+        //Set our default shader
+        shader_reset();
+        gpu_set_colorwriteenable(true, true, true, false);
+        
+        //And switch on z-testing. We'll use z-testing for stenciling
+        gpu_set_ztestenable(true);
+        gpu_set_zwriteenable(true);
+        gpu_set_zfunc(cmpfunc_lessequal);
+        
+        var _i = 0;
+        repeat(array_length(__lightsArray))
+        {
+            var _weak = __lightsArray[_i];
+            if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+            {
+                array_delete(__lightsArray, _i, 1);
+            }
+            else
+            {
+                with(_weak.ref)
+                {
+                    if (visible)
+                    {
+                        __CheckSpriteDimensions();
+                        
+                        //If this light is active, do some drawing
+                        if (__IsOnScreen(_boundaryL, _boundaryT, _boundaryR, _boundaryB))
+                        {
+                            if (castShadows)
+                            {
+                                //Turn off all RGBA writing, leaving only z-writing
+                                gpu_set_colorwriteenable(false, false, false, false);
+                                
+                                //Guarantee that we're going to write to the z-buffer with the next operations
+                                gpu_set_zfunc(cmpfunc_always);
+                                
+                                //Reset z-buffer
+                                draw_sprite_ext(sprite, image,
+                                                x, y,
+                                                xscale, yscale, angle,
+                                                c_black, 1);
+                                
+                                //Stencil out shadow areas
+                                shader_set(__shdBulbHardShadows);
+                                shader_set_uniform_f(_shdBulbHardShadows_u_vLight, x, y);
+                                vertex_submit(_staticVBuffer,  pr_trianglelist, -1);
+                                vertex_submit(_dynamicVBuffer, pr_trianglelist, -1);
+                                
+                                //Swap to drawing RGB data (no alpha to make the output surface tidier)
+                                gpu_set_colorwriteenable(true, true, true, false);
+                                gpu_set_zfunc(cmpfunc_lessequal);
+                                
+                                //Reset shader and draw the light itself, but "behind" the shadows
+                                if (_rendererNormalMap && normalMap)
+                                {
+                                    shader_set(__shdBulbLightWithNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithNormalMap_u_vInfo, x, y, normalMapZ, intensity);
+                                }
+                                else
+                                {
+                                    shader_set(__shdBulbLightWithoutNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithoutNormalMap_u_fIntensity, intensity);
+                                }
+                                
+                                draw_sprite_ext(sprite, image, x, y, xscale, yscale, angle, blend, 1);
+                            }
+                            else
+                            {
+                                //Just draw the sprite, no fancy stuff here
+                                if (_rendererNormalMap && normalMap)
+                                {
+                                    shader_set(__shdBulbLightWithNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithNormalMap_u_vInfo, x, y, normalMapZ, intensity);
+                                }
+                                else
+                                {
+                                    shader_set(__shdBulbLightWithoutNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithoutNormalMap_u_fIntensity, intensity);
+                                }
+                                
+                                draw_sprite_ext(sprite, image, x, y, xscale, yscale, angle, blend, 1);
+                            }
+                        }
+                    }
+                }
+                
+                ++_i;
+            }
+        }
+        
+        var _i = 0;
+        repeat(array_length(__sunlightArray))
+        {
+            var _weak = __sunlightArray[_i];
+            if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+            {
+                array_delete(__sunlightArray, _i, 1);
+            }
+            else
+            {
+                with(_weak.ref)
+                {
+                    if (visible)
+                    {
+                        //Turn off all RGBA writing, leaving only z-writing
+                        gpu_set_colorwriteenable(false, false, false, false);
+                        
+                        //Guarantee that we're going to write to the z-buffer with the next operations
+                        gpu_set_zfunc(cmpfunc_always);
+                        
+                        //Full surface clear of the z-buffer
+                        draw_sprite_ext(__sprBulbPixel, 0, _boundaryL, _boundaryT, _boundaryR - _boundaryL, _boundaryB - _boundaryT, 0, c_black, 0);
+                        
+                        //Stencil out shadow areas
+                        shader_set(__shdBulbHardShadowsSunlight);
+                        shader_set_uniform_f(_shdBulbHardShadowsSunlight_u_vLightVector, dcos(angle), -dsin(angle));
+                        vertex_submit(_staticVBuffer,  pr_trianglelist, -1);
+                        vertex_submit(_dynamicVBuffer, pr_trianglelist, -1);
+                        
+                        //Swap to drawing RGB data (no alpha to make the output surface tidier)
+                        gpu_set_colorwriteenable(true, true, true, false);
+                        gpu_set_zfunc(cmpfunc_lessequal);
+                        
+                        //Reset shader and draw the light itself, but "behind" the shado
+                        if (_rendererNormalMap && normalMap)
+                        {
+                            shader_set(__shdBulbSunlightWithNormalMap);
+                            shader_set_uniform_f(_shdBulbSunlightWithNormalMap_u_vSunInfo, dcos(angle), -dsin(angle), normalMapZ, intensity);
+                        }
+                        else
+                        {
+                            shader_set(__shdBulbLightWithoutNormalMap);
+                            shader_set_uniform_f(_shdBulbLightWithoutNormalMap_u_fIntensity, intensity);
+                        }
+                        
+                        
+                        draw_sprite_ext(__sprBulbPixel, 0, _boundaryL, _boundaryT, _boundaryR - _boundaryL, _boundaryB - _boundaryT, 0, blend, 1);
+                    }
+                }
+                
+                ++_i;
+            }
+        }
+        
+        shader_reset();
+        gpu_set_colorwriteenable(true, true, true, true);
+        gpu_set_blendmode(bm_normal);
+        
+        gpu_set_zfunc(cmpfunc_lessequal);
+        gpu_set_ztestenable(false);
+        gpu_set_zwriteenable(false);
+    }
+}

+ 13 - 0
scripts/__BulbRendererDefineAccumulateHardNoStencil/__BulbRendererDefineAccumulateHardNoStencil.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRendererDefineAccumulateHardNoStencil",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRendererDefineAccumulateHardNoStencil",
+  "parent":{
+    "name":"Renderer Definitions",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 175 - 0
scripts/__BulbRendererDefineAccumulateSoft/__BulbRendererDefineAccumulateSoft.gml

@@ -0,0 +1,175 @@
+// Feather disable all
+
+function __BulbRendererDefineAccumulateSoft()
+{
+    __AccumulateSoftLights = function(_boundaryL, _boundaryT, _boundaryR, _boundaryB, _cameraCX, _cameraCY, _cameraW, _cameraH, _cameraCos, _cameraSin, _normalCoeff)
+    {
+        static _shdBulbSoftShadows_u_vLight                   = shader_get_uniform(__shdBulbSoftShadows,              "u_vLight"      );
+        static _shdBulbSoftShadowsSunlight_u_vLightVector     = shader_get_uniform(__shdBulbSoftShadowsSunlight,      "u_vLightVector");
+        static _shdBulbLightWithNormalMap_u_vInfo             = shader_get_uniform(__shdBulbLightWithNormalMap,       "u_vInfo"       );
+        static _shdBulbLightWithoutNormalMap_u_fIntensity     = shader_get_uniform(__shdBulbLightWithoutNormalMap,    "u_fIntensity"  );
+        static _shdBulbSunlightWithNormalMap_u_vSunInfo       = shader_get_uniform(__shdBulbSunlightWithNormalMap,    "u_vInfo"       );
+        static _sshdBulbSunlightWithoutNormalMap_u_fIntensity = shader_get_uniform(__shdBulbSunlightWithoutNormalMap, "u_fIntensity"  );
+        
+        var _staticVBuffer  = __staticVBuffer;
+        var _dynamicVBuffer = __dynamicVBuffer;
+        
+        var _rendererNormalMap = normalMap;
+        if (_rendererNormalMap)
+        {
+            shader_set(__shdBulbLightWithNormalMap);
+            texture_set_stage(shader_get_sampler_index(__shdBulbLightWithNormalMap, "u_sNormalMap"), surface_get_texture(GetNormalMapSurface()));
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_vCamera"), _cameraCX, _cameraCY, _cameraW/2, _cameraH/2);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_vCameraVector"), _cameraCos, _cameraSin);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbLightWithNormalMap, "u_fSpecularIntensity"), hdr? BULB_SPECULAR_MAP_INTENSITY : 0);
+            
+            shader_set(__shdBulbSunlightWithNormalMap);
+            shader_set_uniform_f(shader_get_uniform(__shdBulbSunlightWithNormalMap, "u_fSpecularIntensity"), hdr? BULB_SPECULAR_MAP_INTENSITY : 0);
+        }
+        
+        var _i = 0;
+        repeat(array_length(__lightsArray))
+        {
+            var _weak = __lightsArray[_i];
+            if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+            {
+                array_delete(__lightsArray, _i, 1);
+            }
+            else
+            {
+                with(_weak.ref)
+                {
+                    if (visible)
+                    {
+                        __CheckSpriteDimensions();
+                        
+                        //If this light is active, do some drawing
+                        if (__IsOnScreen(_boundaryL, _boundaryT, _boundaryR, _boundaryB))
+                        {
+                            if (castShadows)
+                            {
+                                //Only write into the alpha channel
+                                gpu_set_colorwriteenable(false, false, false, true);
+                                
+                                //Clear the alpha channel for the light's visual area
+                                gpu_set_blendmode_ext(bm_one, bm_zero);
+                                draw_sprite_ext(sprite, image,
+                                                x, y,
+                                                xscale, yscale, angle,
+                                                c_black, 1);
+                                
+                                //Cut out shadows in the alpha channel
+                                gpu_set_blendmode(bm_subtract);
+                                
+                                shader_set(__shdBulbSoftShadows);
+                                shader_set_uniform_f(_shdBulbSoftShadows_u_vLight, x, y, penumbraSize);
+                                vertex_submit(_staticVBuffer,  pr_trianglelist, -1);
+                                vertex_submit(_dynamicVBuffer, pr_trianglelist, -1);
+                                
+                                //Set the light sprite to borrowing the alpha channel already on the surface
+                                gpu_set_colorwriteenable(true, true, true, false);
+                                gpu_set_blendmode_ext(bm_dest_alpha, bm_one);
+                                
+                                //Draw light sprite
+                                if (_rendererNormalMap && normalMap)
+                                {
+                                    shader_set(__shdBulbLightWithNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithNormalMap_u_vInfo, x, y, normalMapZ, intensity);
+                                }
+                                else
+                                {
+                                    shader_set(__shdBulbLightWithoutNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithoutNormalMap_u_fIntensity, intensity);
+                                }
+                                
+                                draw_sprite_ext(sprite, image,
+                                                x, y,
+                                                xscale, yscale, angle,
+                                                blend, 1);
+                            }
+                            else
+                            {
+                                //No shadows - draw the light sprite normally
+                                gpu_set_blendmode(bm_add);
+                                
+                                if (_rendererNormalMap && normalMap)
+                                {
+                                    shader_set(__shdBulbLightWithNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithNormalMap_u_vInfo, x, y, normalMapZ, intensity);
+                                }
+                                else
+                                {
+                                    shader_set(__shdBulbLightWithoutNormalMap);
+                                    shader_set_uniform_f(_shdBulbLightWithoutNormalMap_u_fIntensity, intensity);
+                                }
+                                
+                                draw_sprite_ext(sprite, image,
+                                                x, y,
+                                                xscale, yscale, angle,
+                                                blend, 1);
+                            }
+                        }
+                    }
+                }
+                
+                ++_i;
+            }
+        }
+        
+        var _i = 0;
+        repeat(array_length(__sunlightArray))
+        {
+            var _weak = __sunlightArray[_i];
+            if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+            {
+                array_delete(__sunlightArray, _i, 1);
+            }
+            else
+            {
+                with(_weak.ref)
+                {
+                    if (visible)
+                    {
+                        //Only write into the alpha channel
+                        gpu_set_colorwriteenable(false, false, false, true);
+                        
+                        //Clear the alpha channel for the light's visual area
+                        gpu_set_blendmode_ext(bm_one, bm_zero);
+                        draw_sprite_ext(__sprBulbPixel, 0, _boundaryL, _boundaryT, _boundaryR - _boundaryL, _boundaryB - _boundaryT, 0, c_black, 1);
+                        
+                        //Cut out shadows in the alpha channel
+                        gpu_set_blendmode(bm_subtract);
+                        
+                        shader_set(__shdBulbSoftShadowsSunlight);
+                        shader_set_uniform_f(_shdBulbSoftShadowsSunlight_u_vLightVector, dcos(angle), -dsin(angle), penumbraSize);
+                        vertex_submit(_staticVBuffer,  pr_trianglelist, -1);
+                        vertex_submit(_dynamicVBuffer, pr_trianglelist, -1);
+                        
+                        //Set the light sprite to borrowing the alpha channel already on the surface
+                        gpu_set_colorwriteenable(true, true, true, false);
+                        gpu_set_blendmode_ext(bm_dest_alpha, bm_one);
+                        
+                        //Draw light sprite
+                        if (_rendererNormalMap && normalMap)
+                        {
+                            shader_set(__shdBulbSunlightWithNormalMap);
+                            shader_set_uniform_f(_shdBulbSunlightWithNormalMap_u_vSunInfo, dcos(angle), -dsin(angle), normalMapZ, intensity);
+                        }
+                        else
+                        {
+                            shader_set(__shdBulbSunlightWithoutNormalMap);
+                            shader_set_uniform_f(_sshdBulbSunlightWithoutNormalMap_u_fIntensity, intensity);
+                        }
+                        
+                        draw_sprite_ext(__sprBulbPixel, 0, _boundaryL, _boundaryT, _boundaryR - _boundaryL, _boundaryB - _boundaryT, 0, blend, 1);
+                    }
+                }
+                
+                ++_i;
+            }
+        }
+        
+        shader_reset();
+        gpu_set_blendmode(bm_normal);
+    }
+}

+ 13 - 0
scripts/__BulbRendererDefineAccumulateSoft/__BulbRendererDefineAccumulateSoft.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRendererDefineAccumulateSoft",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRendererDefineAccumulateSoft",
+  "parent":{
+    "name":"Renderer Definitions",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 79 - 0
scripts/__BulbRendererDefineHDR/__BulbRendererDefineHDR.gml

@@ -0,0 +1,79 @@
+// Feather disable all
+
+function __BulbRendererDefineHDR()
+{
+    static _system = __BulbSystem();
+    
+    hdr         = false;
+    hdrTonemap  = BULB_TONEMAP_HBD;
+    
+    hdrBloomIntensity    = 0;
+    hdrBloomIterations   = 3;
+    hdrBloomThresholdMin = 0.4;
+    hdrBloomThresholdMax = 0.9;
+    
+    __oldHDR                = undefined;
+    __oldHDRBloomIterations = undefined;
+    
+    //Surface used for HDR composition prior to tonemapping
+    //This is a 16-bit float RGBA surface and is only created on demand
+    __outputSurface = undefined;
+    
+    __bloomSurfaceArray = [];
+    
+    
+    
+    __GetOutputSurface = function(_width, _height)
+    {
+        if ((_width <= 0) || (_height <= 0)) return undefined;
+        
+        if ((__outputSurface != undefined) && ((surface_get_width(__outputSurface) != _width) || (surface_get_height(__outputSurface) != _height)))
+        {
+            surface_free(__outputSurface);
+            __outputSurface = undefined;
+        }
+        
+        if ((__outputSurface == undefined) || !surface_exists(__outputSurface))
+        {
+            if (hdr && _system.__hdrAvailable)
+            {
+                //Work around compile error in LTS
+                var _surface_create = surface_create;
+                __outputSurface = _surface_create(_width, _height, surface_rgba16float);
+            }
+            else
+            {
+                __outputSurface = surface_create(_width, _height);
+            }
+            
+            surface_set_target(__outputSurface);
+            draw_clear(c_black);
+            surface_reset_target();
+            
+            __FreeBloomSurfaces();
+        }
+        
+        return __outputSurface;
+    }
+    
+    __FreeOutputSurface = function()
+    {
+        if ((__outputSurface != undefined) && surface_exists(__outputSurface))
+        {
+            surface_free(__outputSurface);
+            __outputSurface = undefined;
+        }
+    }
+    
+    __FreeBloomSurfaces = function()
+    {
+        var _i = 0;
+        repeat(array_length(__bloomSurfaceArray))
+        {
+            surface_free(__bloomSurfaceArray[_i]);
+            ++_i;
+        }
+        
+        array_resize(__bloomSurfaceArray, 0);
+    }
+}

+ 13 - 0
scripts/__BulbRendererDefineHDR/__BulbRendererDefineHDR.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRendererDefineHDR",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRendererDefineHDR",
+  "parent":{
+    "name":"Renderer Definitions",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 198 - 0
scripts/__BulbRendererDefineLight/__BulbRendererDefineLight.gml

@@ -0,0 +1,198 @@
+// Feather disable all
+
+function __BulbRendererDefineLight()
+{
+    //Screen-space surface for final accumulation of lights
+    //In HDR mode, this is a per-channel 64-bit RGBA surface
+    __lightSurface = undefined;
+    
+    __lightsArray   = [];
+    __sunlightArray = [];
+    
+    GetLightSurface = function()
+    {
+        if ((__surfaceWidth <= 0) || (__surfaceHeight <= 0)) return undefined;
+        
+        var _surfaceWidth  = floor(__surfaceWidth);
+        var _surfaceHeight = floor(__surfaceHeight);
+        
+        if ((__lightSurface != undefined) && ((surface_get_width(__lightSurface) != _surfaceWidth) || (surface_get_height(__lightSurface) != _surfaceHeight)))
+        {
+            surface_free(__lightSurface);
+            __lightSurface = undefined;
+        }
+        
+        if ((__lightSurface == undefined) || !surface_exists(__lightSurface))
+        {
+            //Only try to create an HDR surface if floating point surfaces are available
+            if (_system.__hdrAvailable)
+            {
+                //Work around compile error in LTS
+                var _surface_create = surface_create;
+                __lightSurface = _surface_create(_surfaceWidth, _surfaceHeight, hdr? surface_rgba16float : surface_rgba8unorm);
+            }
+            else
+            {
+                __lightSurface = surface_create(_surfaceWidth, _surfaceHeight);
+            }
+            
+            surface_set_target(__lightSurface);
+            draw_clear_alpha(c_black, 1.0);
+            surface_reset_target();
+        }
+        
+        return __lightSurface;
+    }
+    
+    __FreeLightSurface = function()
+    {
+        if ((__lightSurface != undefined) && surface_exists(__lightSurface))
+        {
+            surface_free(__lightSurface);
+            __lightSurface = undefined;
+        }
+    }
+    
+    GetLightValue = function(_worldX, _worldY)
+    {
+        var _surface = GetLightSurface();
+        
+        //Deploy PROPER MATHS in case the dev is using matrices
+        var _viewMatrix = camera_get_view_mat(__camera);
+        var _projMatrix = camera_get_proj_mat(__camera);
+        var _cameraW    = round(abs(2/_projMatrix[0]));
+        var _cameraH    = round(abs(2/_projMatrix[5]));
+        
+        var _vector = matrix_transform_vertex(matrix_multiply(_viewMatrix, _projMatrix), _worldX, _worldY, 0);
+        var _x      = _cameraW * (0.5 + 0.5*_vector[0]);
+        var _y      = _cameraH * (0.5 - 0.5*_vector[1]); //Silly GM matrix quirk
+        
+        var _result = surface_getpixel_ext(_surface, _x, _y);
+        if (not is_array(_result))
+        {
+            //Unpack 32-bit colour
+            var _resultR = ( _result        & 0xFF) / 255;
+            var _resultG = ((_result >>  8) & 0xFF) / 255;
+            var _resultB = ((_result >> 16) & 0xFF) / 255;
+            var _resultA = ((_result >> 24) & 0xFF) / 255;
+        }
+        else
+        {
+            //Unpack array of floating point values
+            var _resultR = _result[0];
+            var _resultG = _result[1];
+            var _resultB = _result[2];
+            var _resultA = _result[3];
+        }
+        
+        _resultR *= exposure;
+        _resultG *= exposure;
+        _resultB *= exposure;
+        _resultA  = 255*clamp(_resultA, 0, 1); //Clamp the alpha channel
+        
+        static _funcLuminance = function(_red, _green, _blue)
+        {
+            return 0.2126*_red + 0.7152*_green + 0.0722*_blue;
+        }
+        
+        var _tonemap = GetTonemap();
+        
+        if (_tonemap == BULB_TONEMAP_BAD_GAMMA)
+        {
+            _resultR = 255*clamp(_resultR, 0, 1);
+            _resultG = 255*clamp(_resultG, 0, 1);
+            _resultB = 255*clamp(_resultB, 0, 1);
+        }
+        else if (_tonemap == BULB_TONEMAP_HBD)
+        {
+            _resultR = max(0, _resultR - 0.004);
+            _resultG = max(0, _resultG - 0.004);
+            _resultB = max(0, _resultB - 0.004);
+            
+            _resultR = (_resultR * (6.2*_resultR + 0.5)) / (_resultR * (6.2*_resultR + 1.7) + 0.06);
+            _resultG = (_resultG * (6.2*_resultG + 0.5)) / (_resultG * (6.2*_resultG + 1.7) + 0.06);
+            _resultB = (_resultB * (6.2*_resultB + 0.5)) / (_resultB * (6.2*_resultB + 1.7) + 0.06);
+            
+            //Already includes gamma correction in calculation, no power() call needed
+            _resultR = 255*clamp(_resultR, 0, 1);
+            _resultG = 255*clamp(_resultG, 0, 1);
+            _resultB = 255*clamp(_resultB, 0, 1);
+        }
+        else if (_tonemap == BULB_TONEMAP_UNREAL3)
+        {
+            _resultR = _resultR / (_resultR + 0.155) * 1.019;
+            _resultG = _resultG / (_resultG + 0.155) * 1.019;
+            _resultB = _resultB / (_resultB + 0.155) * 1.019;
+            
+            //Already includes gamma correction in calculation, no power() call needed
+            _resultR = 255*clamp(_resultR, 0, 1);
+            _resultG = 255*clamp(_resultG, 0, 1);
+            _resultB = 255*clamp(_resultB, 0, 1);
+        }
+        else
+        {
+            if ((_tonemap == BULB_TONEMAP_NONE) || (_tonemap == BULB_TONEMAP_CLAMP))
+            {
+                //Nothing else needed
+            }
+            else if (_tonemap == BULB_TONEMAP_REINHARD)
+            {
+                var _luminance    = _funcLuminance(_resultR, _resultG, _resultB);
+                var _luminanceNew = _luminance / (1 + _luminance);
+                
+                _resultR *= _luminanceNew / _luminance;
+                _resultG *= _luminanceNew / _luminance;
+                _resultB *= _luminanceNew / _luminance;
+            }
+            else if (_tonemap == BULB_TONEMAP_REINHARD_EXTENDED)
+            {
+                var _luminance    = _funcLuminance(_resultR, _resultG, _resultB);
+                var _luminanceNew = _luminance * (1.0 + (_luminance / (4*4))) / (1 + _luminance);
+                
+                _resultR *= _luminanceNew / _luminance;
+                _resultG *= _luminanceNew / _luminance;
+                _resultB *= _luminanceNew / _luminance;
+            }
+            else if (_tonemap == BULB_TONEMAP_UNCHARTED2)
+            {
+                _resultR *= 4;
+                _resultG *= 4;
+                _resultB *= 4;
+                
+                static _uc2_A = 0.15;
+                static _uc2_B = 0.50;
+                static _uc2_C = 0.10;
+                static _uc2_D = 0.20;
+                static _uc2_E = 0.02;
+                static _uc2_F = 0.30;
+                
+                _resultR = ((_resultR*(_uc2_A*_resultR + _uc2_C*_uc2_B) + _uc2_D*_uc2_E) / (_resultR*(_uc2_A*_resultR + _uc2_B) + _uc2_D*_uc2_F)) - _uc2_E/_uc2_F;
+                _resultG = ((_resultG*(_uc2_A*_resultG + _uc2_C*_uc2_B) + _uc2_D*_uc2_E) / (_resultG*(_uc2_A*_resultG + _uc2_B) + _uc2_D*_uc2_F)) - _uc2_E/_uc2_F;
+                _resultB = ((_resultB*(_uc2_A*_resultB + _uc2_C*_uc2_B) + _uc2_D*_uc2_E) / (_resultB*(_uc2_A*_resultB + _uc2_B) + _uc2_D*_uc2_F)) - _uc2_E/_uc2_F;
+            }
+            else if (_tonemap == BULB_TONEMAP_UNREAL3)
+            {
+                _resultR = max(0, _resultR - 0.004);
+                _resultG = max(0, _resultG - 0.004);
+                _resultB = max(0, _resultB - 0.004);
+                
+                _resultR = (_resultR * (6.2*_resultR + 0.5)) / (_resultR * (6.2*_resultR + 1.7) + 0.06);
+                _resultG = (_resultG * (6.2*_resultG + 0.5)) / (_resultG * (6.2*_resultG + 1.7) + 0.06);
+                _resultB = (_resultB * (6.2*_resultB + 0.5)) / (_resultB * (6.2*_resultB + 1.7) + 0.06);
+            }
+            else if (_tonemap == BULB_TONEMAP_ACES)
+            {
+                _resultR = (_resultR*(2.51*_resultR + 0.03)) / (_resultR*(2.43*_resultR + 0.59) + 0.14);
+                _resultG = (_resultG*(2.51*_resultG + 0.03)) / (_resultG*(2.43*_resultG + 0.59) + 0.14);
+                _resultB = (_resultB*(2.51*_resultB + 0.03)) / (_resultB*(2.43*_resultB + 0.59) + 0.14);
+            }
+            
+            //Final gamma correction stage
+            _resultR = 255*clamp(power(_resultR, 1/BULB_GAMMA), 0, 1);
+            _resultG = 255*clamp(power(_resultG, 1/BULB_GAMMA), 0, 1);
+            _resultB = 255*clamp(power(_resultB, 1/BULB_GAMMA), 0, 1);
+        }
+        
+        return ((_resultA << 24) | (_resultB << 16) | (_resultG << 8) | _resultR);
+    }
+}

+ 13 - 0
scripts/__BulbRendererDefineLight/__BulbRendererDefineLight.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRendererDefineLight",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRendererDefineLight",
+  "parent":{
+    "name":"Renderer Definitions",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 52 - 0
scripts/__BulbRendererDefineNormal/__BulbRendererDefineNormal.gml

@@ -0,0 +1,52 @@
+// Feather disable all
+
+function __BulbRendererDefineNormal()
+{
+    normalMap = BULB_DEFAULT_USE_NORMAL_MAP;
+    __oldNormalMap = undefined;
+    
+    __normalSurface = undefined;
+    
+    
+    
+    GetNormalMapSurface = function()
+    {
+        if (not normalMap)
+        {
+            __BulbError("Cannot call .GetNormalMapSurface(), `normalMap` is not set to `true`");
+        }
+        
+        if ((__surfaceWidth <= 0) || (__surfaceHeight <= 0)) return undefined;
+        
+        if ((__normalSurface != undefined) && ((surface_get_width(__normalSurface) != __surfaceWidth) || (surface_get_height(__normalSurface) != __surfaceHeight)))
+        {
+            surface_free(__normalSurface);
+            __normalSurface = undefined;
+        }
+        
+        if ((__normalSurface == undefined) || !surface_exists(__normalSurface))
+        {
+            __normalSurface = surface_create(__surfaceWidth, __surfaceHeight);
+            
+            surface_set_target(__normalSurface);
+            BulbNormalMapClear();
+            surface_reset_target();
+        }
+        
+        return __normalSurface;
+    }
+    
+    DrawNormalMapDebug = function(_x, _y, _width, _height)
+    {
+        draw_surface_stretched(GetNormalMapSurface(), _x, _y, _width, _height);
+    }
+    
+    __FreeNormalMapSurface = function()
+    {
+        if ((__normalSurface != undefined) && surface_exists(__normalSurface))
+        {
+            surface_free(__normalSurface);
+            __normalSurface = undefined;
+        }
+    }
+}

+ 13 - 0
scripts/__BulbRendererDefineNormal/__BulbRendererDefineNormal.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRendererDefineNormal",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRendererDefineNormal",
+  "parent":{
+    "name":"Renderer Definitions",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 141 - 0
scripts/__BulbRendererDefineOverlayUnderlay/__BulbRendererDefineOverlayUnderlay.gml

@@ -0,0 +1,141 @@
+// Feather disable all
+
+function __BulbRendererDefineOverlayUnderlay()
+{
+    __ambienceSpriteArray = [];
+    __shadowOverlayArray  = [];
+    __lightOverlayArray   = [];
+    
+    __AccumulateAmbienceSprite = function(_boundaryL, _boundaryT, _boundaryR, _boundaryB)
+    {
+        //Now draw ambience overlay sprites, if we have any
+        var _size = array_length(__ambienceSpriteArray);
+        if (_size > 0)
+        {
+            gpu_set_colorwriteenable(true, true, true, false);
+            
+            var _i = 0;
+            repeat(_size)
+            {
+                var _weak = __ambienceSpriteArray[_i];
+                if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+                {
+                    array_delete(__ambienceSpriteArray, _i, 1);
+                }
+                else
+                {
+                    with(_weak.ref)
+                    {
+                        if (visible)
+                        {
+                            __CheckSpriteDimensions();
+                            
+                            //If this light is active, do some drawing
+                            if (__IsOnScreen(_boundaryL, _boundaryT, _boundaryR, _boundaryB))
+                            {
+                                draw_sprite_ext(sprite, image, x, y, xscale, yscale, angle, blend, alpha);
+                            }
+                        }
+                    }
+                    
+                    ++_i;
+                }
+            }
+            
+            gpu_set_colorwriteenable(true, true, true, true);
+        }
+    }
+    
+    __AccumulateShadowOverlay = function(_boundaryL, _boundaryT, _boundaryR, _boundaryB)
+    {
+        //Now draw shadow overlay sprites, if we have any
+        var _size = array_length(__shadowOverlayArray);
+        if (_size > 0)
+        {
+            //Leverage the fog system to force the colour of the sprites we draw (alpha channel passes through)
+            gpu_set_fog(true, __GetAmbientColor(), 0, 0);
+            
+            //Don't touch the alpha channel
+            gpu_set_colorwriteenable(true, true, true, false);
+            
+            var _i = 0;
+            repeat(_size)
+            {
+                var _weak = __shadowOverlayArray[_i];
+                if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+                {
+                    array_delete(__shadowOverlayArray, _i, 1);
+                }
+                else
+                {
+                    with(_weak.ref)
+                    {
+                        if (visible)
+                        {
+                            __CheckSpriteDimensions();
+                            
+                            //If this light is active, do some drawing
+                            if (__IsOnScreen(_boundaryL, _boundaryT, _boundaryR, _boundaryB))
+                            {
+                                draw_sprite_ext(sprite, image, x, y, xscale, yscale, angle, c_white, alpha);
+                            }
+                        }
+                    }
+                    
+                    ++_i;
+                }
+            }
+            
+            //Reset render state
+            gpu_set_fog(false, c_white, 0, 0);
+            gpu_set_colorwriteenable(true, true, true, true);
+        }
+    }
+    
+    __AccumulateLightOverlay = function(_boundaryL, _boundaryT, _boundaryR, _boundaryB)
+    {
+        static _u_fIntensity = shader_get_uniform(__shdBulbIntensity, "u_fIntensity");
+        
+        shader_set(__shdBulbIntensity);
+        
+        //We use the overarching blend mode for the renderer
+        gpu_set_blendmode(bm_add);
+        
+        //Don't touch the alpha channel though
+        gpu_set_colorwriteenable(true, true, true, false);
+        
+        var _i = 0;
+        repeat(array_length(__lightOverlayArray))
+        {
+            var _weak = __lightOverlayArray[_i];
+            if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+            {
+                array_delete(__lightOverlayArray, _i, 1);
+            }
+            else
+            {
+                with(_weak.ref)
+                {
+                    if (visible)
+                    {
+                        __CheckSpriteDimensions();
+                        
+                        //If this light is active, do some drawing
+                        if (__IsOnScreen(_boundaryL, _boundaryT, _boundaryR, _boundaryB))
+                        {
+                            shader_set_uniform_f(_u_fIntensity, intensity);
+                            draw_sprite_ext(sprite, image, x, y, xscale, yscale, angle, blend, 1);
+                        }
+                    }
+                }
+                
+                ++_i;
+            }
+        }
+        
+        //Reset render state
+        shader_reset();
+        gpu_set_fog(false, c_white, 0, 0);
+        gpu_set_colorwriteenable(true, true, true, true);
+    }
+}

+ 13 - 0
scripts/__BulbRendererDefineOverlayUnderlay/__BulbRendererDefineOverlayUnderlay.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRendererDefineOverlayUnderlay",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRendererDefineOverlayUnderlay",
+  "parent":{
+    "name":"Renderer Definitions",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 148 - 0
scripts/__BulbRendererDefineVertexBuffers/__BulbRendererDefineVertexBuffers.gml

@@ -0,0 +1,148 @@
+// Feather disable all
+
+function __BulbRendererDefineVertexBuffers()
+{
+    __staticVBuffer  = undefined; //Vertex buffer describing the geometry of static occluder objects
+    __dynamicVBuffer = undefined; //As above but for dynamic shadow occluders. This is updated every step
+    
+    __staticOccludersArray  = [];
+    __dynamicOccludersArray = [];
+    
+    RefreshStaticOccluders = function()
+    {
+        if (__staticVBuffer != undefined)
+        {
+            vertex_delete_buffer(__staticVBuffer);
+            __staticVBuffer = undefined;
+        }
+    }
+    
+    __FreeVertexBuffers = function()
+    {
+        if (__staticVBuffer != undefined)
+        {
+            vertex_delete_buffer(__staticVBuffer);
+            __staticVBuffer = undefined;
+        }
+        
+        if (__dynamicVBuffer != undefined)
+        {
+            vertex_delete_buffer(__dynamicVBuffer);
+            __dynamicVBuffer = undefined;
+        }
+    }
+    
+    __UpdateVertexBuffers = function(_boundaryL, _boundaryT, _boundaryR, _boundaryB)
+    {
+        //One-time construction of the static occluder geometry
+        if (__staticVBuffer == undefined)
+        {
+            //Create a new vertex buffer
+            __staticVBuffer = vertex_create_buffer();
+            var _staticVBuffer = __staticVBuffer;
+            
+            //Add static shadow caster vertices to the relevant vertex buffer
+            if (soft)
+            {
+                vertex_begin(__staticVBuffer, _vformat3DNormalTex);
+                
+                var _array = __staticOccludersArray;
+                var _i = 0;
+                repeat(array_length(_array))
+                {
+                    var _weak = _array[_i];
+                    if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+                    {
+                        array_delete(_array, _i, 1);
+                    }
+                    else
+                    {
+                        with(_weak.ref) __BulbAddOcclusionSoft(_staticVBuffer);
+                        ++_i;
+                    }
+                }
+            }
+            else
+            {
+                vertex_begin(__staticVBuffer, _vformat3DNormal);
+                
+                var _array = __staticOccludersArray;
+                var _i = 0;
+                repeat(array_length(_array))
+                {
+                    var _weak = _array[_i];
+                    if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+                    {
+                        array_delete(_array, _i, 1);
+                    }
+                    else
+                    {
+                        with(_weak.ref) __BulbAddOcclusionHard(_staticVBuffer);
+                        ++_i;
+                    }
+                }
+            }
+            
+            vertex_end(__staticVBuffer);
+            
+            //Freeze this buffer for speed boosts later on (though only if we have vertices in this buffer)
+            if (vertex_get_number(__staticVBuffer) > 0) vertex_freeze(__staticVBuffer);
+        }
+        
+        //Refresh the dynamic occluder geometry
+        if (__dynamicVBuffer == undefined) __dynamicVBuffer = vertex_create_buffer();
+        var _dynamicVBuffer = __dynamicVBuffer;
+        
+        //Add dynamic occluder vertices to the relevant vertex buffer
+        if (soft)
+        {
+            vertex_begin(_dynamicVBuffer, _vformat3DNormalTex);
+            
+            var _array = __dynamicOccludersArray;
+            var _i = 0;
+            repeat(array_length(_array))
+            {
+                var _weak = _array[_i];
+                if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+                {
+                    array_delete(_array, _i, 1);
+                }
+                else
+                {
+                    with(_weak.ref)
+                    {
+                        if (__IsOnScreen(_boundaryL, _boundaryT, _boundaryR, _boundaryB)) __BulbAddOcclusionSoft(_dynamicVBuffer);
+                    }
+                    
+                    ++_i;
+                }
+            }
+        }
+        else
+        {
+            vertex_begin(_dynamicVBuffer, _vformat3DNormal);
+            
+            var _array = __dynamicOccludersArray;
+            var _i = 0;
+            repeat(array_length(_array))
+            {
+                var _weak = _array[_i];
+                if (!weak_ref_alive(_weak) || _weak.ref.__destroyed)
+                {
+                    array_delete(_array, _i, 1);
+                }
+                else
+                {
+                    with(_weak.ref)
+                    {
+                        if (__IsOnScreen(_boundaryL, _boundaryT, _boundaryR, _boundaryB)) __BulbAddOcclusionHard(_dynamicVBuffer);
+                    }
+                    
+                    ++_i;
+                }
+            }
+        }
+        
+        vertex_end(_dynamicVBuffer);
+    }
+}

+ 13 - 0
scripts/__BulbRendererDefineVertexBuffers/__BulbRendererDefineVertexBuffers.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbRendererDefineVertexBuffers",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbRendererDefineVertexBuffers",
+  "parent":{
+    "name":"Renderer Definitions",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Renderer Definitions.yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 54 - 0
scripts/__BulbSystem/__BulbSystem.gml

@@ -0,0 +1,54 @@
+// HDR references:
+// https://www.shadertoy.com/view/WdjSW3
+// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
+// https://64.github.io/tonemapping/
+// http://slideshare.net/ozlael/hable-john-uncharted2-hdr-renderer
+// http://filmicgames.com/archives/75
+// http://filmicgames.com/archives/183
+// http://filmicgames.com/archives/190
+// http://imdoingitwrong.wordpress.com/2010/08/19/why-reinhard-desaturates-my-blacks-3/
+// http://mynameismjp.wordpress.com/2010/04/30/a-closer-look-at-tone-mapping/
+// http://renderwonk.com/publications/s2010-color-course/
+// https://mini.gmshaders.com/p/tonemaps
+// http://filmicworlds.com/blog/filmic-tonemapping-operators/
+
+#macro __BULB_ZFAR  1
+
+function __BulbSystem()
+{
+    static _system = undefined;
+    if (_system != undefined) return _system;
+    
+    __BulbTrace("Welcome to Bulb by Juju Adams! This is version " + BULB_VERSION + ", " + BULB_DATE);
+    
+    _system = {};
+    with(_system)
+    {
+        try
+        {
+            var _ = surface_rgba16float;
+            
+            __BulbTrace("HDR available");
+            __hdrAvailable = true;
+        }
+        catch(_error)
+        {
+            __BulbTrace("HDR not available");
+            __hdrAvailable = false;
+        }
+        
+        try
+        {
+            gpu_get_stencil_ref();
+            __BulbTrace("GPU stencil functions available");
+            __hasStencil = true;
+        }
+        catch(_error)
+        {
+            __BulbTrace("GPU stencil functions not available");
+            __hasStencil = false;
+        }
+    }
+    
+    return _system;
+}

+ 13 - 0
scripts/__BulbSystem/__BulbSystem.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbSystem",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbSystem",
+  "parent":{
+    "name":"(Pay no attention to that man behind the curtain)",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain).yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 15 - 0
scripts/__BulbTrace/__BulbTrace.gml

@@ -0,0 +1,15 @@
+// Feather disable all
+
+function __BulbTrace()
+{
+    var _string = "";
+    
+    var _i = 0;
+    repeat(argument_count)
+    {
+        _string += string(argument[_i]);
+        ++_i;
+    }
+    
+    show_debug_message("Bulb: " + _string);
+}

+ 13 - 0
scripts/__BulbTrace/__BulbTrace.yy

@@ -0,0 +1,13 @@
+{
+  "$GMScript":"v1",
+  "%Name":"__BulbTrace",
+  "isCompatibility":false,
+  "isDnD":false,
+  "name":"__BulbTrace",
+  "parent":{
+    "name":"(Pay no attention to that man behind the curtain)",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain).yy",
+  },
+  "resourceType":"GMScript",
+  "resourceVersion":"2.0",
+}

+ 4 - 0
shaders/__shdBulbHardShadows/__shdBulbHardShadows.fsh

@@ -0,0 +1,4 @@
+void main()
+{
+    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
+}

+ 18 - 0
shaders/__shdBulbHardShadows/__shdBulbHardShadows.vsh

@@ -0,0 +1,18 @@
+precision highp float;
+
+#define MAX_LENGTH 500.0
+
+attribute vec3 in_Position;
+attribute vec3 in_Normal;
+
+uniform vec2  u_vLight;
+uniform float u_fNormalCoeff;
+
+void main()
+{
+    vec2 delta = in_Position.xy - u_vLight;
+    float finalLength = MAX_LENGTH*step(u_fNormalCoeff*dot(delta, in_Normal.xy), 0.0);
+    
+    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION]*vec4(in_Position.xy + in_Position.z*finalLength*delta, 0.0, 1.0);
+    gl_Position.z = 0.0;
+}

+ 12 - 0
shaders/__shdBulbHardShadows/__shdBulbHardShadows.yy

@@ -0,0 +1,12 @@
+{
+  "$GMShader":"",
+  "%Name":"__shdBulbHardShadows",
+  "name":"__shdBulbHardShadows",
+  "parent":{
+    "name":"Shadow",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Shadow.yy",
+  },
+  "resourceType":"GMShader",
+  "resourceVersion":"2.0",
+  "type":1,
+}

+ 4 - 0
shaders/__shdBulbHardShadowsSunlight/__shdBulbHardShadowsSunlight.fsh

@@ -0,0 +1,4 @@
+void main()
+{
+    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
+}

+ 17 - 0
shaders/__shdBulbHardShadowsSunlight/__shdBulbHardShadowsSunlight.vsh

@@ -0,0 +1,17 @@
+precision highp float;
+
+#define MAX_LENGTH 2000.0
+
+attribute vec3 in_Position;
+attribute vec3 in_Normal;
+
+uniform vec2  u_vLightVector;
+uniform float u_fNormalCoeff;
+
+void main()
+{
+    float finalLength = MAX_LENGTH*step(u_fNormalCoeff*dot(u_vLightVector, in_Normal.xy), 0.0);
+    
+    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION]*vec4(in_Position.xy + in_Position.z*finalLength*u_vLightVector, 0.0, 1.0);
+    gl_Position.z = 0.0;
+}

+ 12 - 0
shaders/__shdBulbHardShadowsSunlight/__shdBulbHardShadowsSunlight.yy

@@ -0,0 +1,12 @@
+{
+  "$GMShader":"",
+  "%Name":"__shdBulbHardShadowsSunlight",
+  "name":"__shdBulbHardShadowsSunlight",
+  "parent":{
+    "name":"Shadow",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Shadow.yy",
+  },
+  "resourceType":"GMShader",
+  "resourceVersion":"2.0",
+  "type":1,
+}

+ 10 - 0
shaders/__shdBulbIntensity/__shdBulbIntensity.fsh

@@ -0,0 +1,10 @@
+varying vec2 v_vTexcoord;
+varying vec4 v_vColour;
+
+uniform float u_fIntensity;
+
+void main()
+{
+    gl_FragColor = v_vColour * texture2D(gm_BaseTexture, v_vTexcoord);
+    gl_FragColor.rgb *= u_fIntensity;
+}

+ 13 - 0
shaders/__shdBulbIntensity/__shdBulbIntensity.vsh

@@ -0,0 +1,13 @@
+attribute vec3 in_Position;
+attribute vec4 in_Colour;
+attribute vec2 in_TextureCoord;
+
+varying vec2 v_vTexcoord;
+varying vec4 v_vColour;
+
+void main()
+{
+    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position.xyz, 1.0);
+    v_vColour   = in_Colour;
+    v_vTexcoord = in_TextureCoord;
+}

+ 12 - 0
shaders/__shdBulbIntensity/__shdBulbIntensity.yy

@@ -0,0 +1,12 @@
+{
+  "$GMShader":"",
+  "%Name":"__shdBulbIntensity",
+  "name":"__shdBulbIntensity",
+  "parent":{
+    "name":"Shaders",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders.yy",
+  },
+  "resourceType":"GMShader",
+  "resourceVersion":"2.0",
+  "type":1,
+}

+ 12 - 0
shaders/__shdBulbKawaseDown/__shdBulbKawaseDown.fsh

@@ -0,0 +1,12 @@
+varying vec2 v_vTexcoord;
+
+uniform vec2 u_vTexel;
+
+void main()
+{
+    gl_FragColor = (4.0*texture2D(gm_BaseTexture, v_vTexcoord                                 )
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2( u_vTexel.x,         0.0))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(-u_vTexel.x,         0.0))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(        0.0,  u_vTexel.y))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(        0.0, -u_vTexel.y))) / 8.0;
+}

+ 11 - 0
shaders/__shdBulbKawaseDown/__shdBulbKawaseDown.vsh

@@ -0,0 +1,11 @@
+attribute vec3 in_Position;
+attribute vec4 in_Colour;
+attribute vec2 in_TextureCoord;
+
+varying vec2 v_vTexcoord;
+
+void main()
+{
+    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position.xyz, 1.0);
+    v_vTexcoord = in_TextureCoord;
+}

+ 12 - 0
shaders/__shdBulbKawaseDown/__shdBulbKawaseDown.yy

@@ -0,0 +1,12 @@
+{
+  "$GMShader":"",
+  "%Name":"__shdBulbKawaseDown",
+  "name":"__shdBulbKawaseDown",
+  "parent":{
+    "name":"Kawase",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Kawase.yy",
+  },
+  "resourceType":"GMShader",
+  "resourceVersion":"2.0",
+  "type":1,
+}

+ 20 - 0
shaders/__shdBulbKawaseDownWithThreshold/__shdBulbKawaseDownWithThreshold.fsh

@@ -0,0 +1,20 @@
+varying vec2 v_vTexcoord;
+
+uniform vec2 u_vThreshold;
+uniform vec2 u_vTexel;
+
+float Luminance(vec3 color)
+{
+    return dot(color, vec3(0.2126, 0.7152, 0.0722));
+}
+
+void main()
+{
+    gl_FragColor = (4.0*texture2D(gm_BaseTexture, v_vTexcoord                                 )
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2( u_vTexel.x,         0.0))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(-u_vTexel.x,         0.0))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(        0.0,  u_vTexel.y))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(        0.0, -u_vTexel.y))) / 8.0;
+    
+    gl_FragColor.rgb *= smoothstep(u_vThreshold.x, u_vThreshold.y, Luminance(gl_FragColor.rgb));
+}

+ 11 - 0
shaders/__shdBulbKawaseDownWithThreshold/__shdBulbKawaseDownWithThreshold.vsh

@@ -0,0 +1,11 @@
+attribute vec3 in_Position;
+attribute vec4 in_Colour;
+attribute vec2 in_TextureCoord;
+
+varying vec2 v_vTexcoord;
+
+void main()
+{
+    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position.xyz, 1.0);
+    v_vTexcoord = in_TextureCoord;
+}

+ 12 - 0
shaders/__shdBulbKawaseDownWithThreshold/__shdBulbKawaseDownWithThreshold.yy

@@ -0,0 +1,12 @@
+{
+  "$GMShader":"",
+  "%Name":"__shdBulbKawaseDownWithThreshold",
+  "name":"__shdBulbKawaseDownWithThreshold",
+  "parent":{
+    "name":"Kawase",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Kawase.yy",
+  },
+  "resourceType":"GMShader",
+  "resourceVersion":"2.0",
+  "type":1,
+}

+ 15 - 0
shaders/__shdBulbKawaseUp/__shdBulbKawaseUp.fsh

@@ -0,0 +1,15 @@
+varying vec2 v_vTexcoord;
+
+uniform vec2 u_vTexel;
+
+void main()
+{
+    gl_FragColor = (    texture2D(gm_BaseTexture, v_vTexcoord + vec2( 2.0*u_vTexel.x,            0.0))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(-2.0*u_vTexel.x,            0.0))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(            0.0, 2.0*u_vTexel.y))
+                 +      texture2D(gm_BaseTexture, v_vTexcoord + vec2(            0.0,-2.0*u_vTexel.y))
+                 +  2.0*texture2D(gm_BaseTexture, v_vTexcoord + vec2(     u_vTexel.x,     u_vTexel.y))
+                 +  2.0*texture2D(gm_BaseTexture, v_vTexcoord + vec2(    -u_vTexel.x,     u_vTexel.y))
+                 +  2.0*texture2D(gm_BaseTexture, v_vTexcoord + vec2(     u_vTexel.x,    -u_vTexel.y))
+                 +  2.0*texture2D(gm_BaseTexture, v_vTexcoord + vec2(    -u_vTexel.x,    -u_vTexel.y))) / 12.0;
+}

+ 11 - 0
shaders/__shdBulbKawaseUp/__shdBulbKawaseUp.vsh

@@ -0,0 +1,11 @@
+attribute vec3 in_Position;
+attribute vec4 in_Colour;
+attribute vec2 in_TextureCoord;
+
+varying vec2 v_vTexcoord;
+
+void main()
+{
+    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position.xyz, 1.0);
+    v_vTexcoord = in_TextureCoord;
+}

+ 12 - 0
shaders/__shdBulbKawaseUp/__shdBulbKawaseUp.yy

@@ -0,0 +1,12 @@
+{
+  "$GMShader":"",
+  "%Name":"__shdBulbKawaseUp",
+  "name":"__shdBulbKawaseUp",
+  "parent":{
+    "name":"Kawase",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Kawase.yy",
+  },
+  "resourceType":"GMShader",
+  "resourceVersion":"2.0",
+  "type":1,
+}

+ 29 - 0
shaders/__shdBulbLightWithNormalMap/__shdBulbLightWithNormalMap.fsh

@@ -0,0 +1,29 @@
+varying vec2 v_vTexcoord;
+varying vec4 v_vColour;
+varying vec2 v_vPosition;
+
+uniform sampler2D u_sNormalMap;
+uniform vec2      u_vCameraVector; //cos, sin
+uniform vec4      u_vInfo;
+uniform vec4      u_vCamera;
+uniform float     u_fSpecularIntensity;
+
+void main()
+{
+    #ifdef _YY_HLSL11_
+        vec2 objectPos = v_vPosition*vec2(1.0, -1.0);
+    #else
+        vec2 objectPos = v_vPosition;
+    #endif
+    
+    vec3 lightPos = vec3((u_vInfo.xy - u_vCamera.xy) / u_vCamera.zw, u_vInfo.z);
+    
+    vec4 normalSpecular = texture2D(u_sNormalMap, 0.5 + 0.5*objectPos);
+    vec3 normal = 2.0*normalSpecular.xyz - 1.0;
+    normal.xy = mat2(u_vCameraVector.x, -u_vCameraVector.y, u_vCameraVector.y, u_vCameraVector.x)*normal.xy;
+    
+    gl_FragColor = v_vColour*texture2D(gm_BaseTexture, v_vTexcoord);
+    gl_FragColor.rgb *= u_vInfo.w*((u_fSpecularIntensity*(1.0 - normalSpecular.w) + 1.0)*max(0.0, dot(normalize(lightPos - vec3(objectPos, 0.0)), normal)));
+    
+    gl_FragColor.rgb *= u_vInfo.w*(u_fSpecularIntensity*(1.0 - normalSpecular.w) + 1.0);
+}

+ 15 - 0
shaders/__shdBulbLightWithNormalMap/__shdBulbLightWithNormalMap.vsh

@@ -0,0 +1,15 @@
+attribute vec3 in_Position;
+attribute vec4 in_Colour;
+attribute vec2 in_TextureCoord;
+
+varying vec2 v_vTexcoord;
+varying vec4 v_vColour;
+varying vec2 v_vPosition;
+
+void main()
+{
+    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position.xyz, 1.0);
+    v_vColour   = in_Colour;
+    v_vTexcoord = in_TextureCoord;
+    v_vPosition = gl_Position.xy;
+}

+ 12 - 0
shaders/__shdBulbLightWithNormalMap/__shdBulbLightWithNormalMap.yy

@@ -0,0 +1,12 @@
+{
+  "$GMShader":"",
+  "%Name":"__shdBulbLightWithNormalMap",
+  "name":"__shdBulbLightWithNormalMap",
+  "parent":{
+    "name":"Light",
+    "path":"folders/3rdParties/Bulb/(System)/(Pay no attention to that man behind the curtain)/Shaders/Light.yy",
+  },
+  "resourceType":"GMShader",
+  "resourceVersion":"2.0",
+  "type":1,
+}

+ 10 - 0
shaders/__shdBulbLightWithoutNormalMap/__shdBulbLightWithoutNormalMap.fsh

@@ -0,0 +1,10 @@
+varying vec2 v_vTexcoord;
+varying vec4 v_vColour;
+
+uniform float u_fIntensity;
+
+void main()
+{
+    gl_FragColor = v_vColour * texture2D(gm_BaseTexture, v_vTexcoord);
+    gl_FragColor.rgb *= u_fIntensity;
+}

Неке датотеке нису приказане због велике количине промена