CPass.cpp 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816
  1. #include "CPass.h"
  2. #include <algorithm>
  3. #include <cctype>
  4. #include <cmath>
  5. #include <sstream>
  6. #include <type_traits>
  7. #include <utility>
  8. #include "WallpaperEngine/Desktop/UserShortcut.h"
  9. #include "WallpaperEngine/Render/Helpers/ContextAware.h"
  10. #include "WallpaperEngine/Data/Model/Effect.h"
  11. #include "WallpaperEngine/Data/Model/Material.h"
  12. #include "WallpaperEngine/Data/Model/Project.h"
  13. #include "WallpaperEngine/Data/Model/Property.h"
  14. #include "WallpaperEngine/Render/Wallpapers/CScene.h"
  15. #include "WallpaperEngine/Render/CFBO.h"
  16. #include "WallpaperEngine/Render/Objects/CImage.h"
  17. #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariable.h"
  18. #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableFloat.h"
  19. #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableInteger.h"
  20. #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableVector2.h"
  21. #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableVector3.h"
  22. #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableVector4.h"
  23. #include "WallpaperEngine/Logging/Log.h"
  24. using namespace WallpaperEngine;
  25. using namespace WallpaperEngine::Render;
  26. using namespace WallpaperEngine::Render::Objects;
  27. using namespace WallpaperEngine::Render::Shaders::Variables;
  28. using namespace WallpaperEngine::Render::Objects::Effects;
  29. extern float g_Time;
  30. extern float g_Daytime;
  31. CPass::UniformEntry::~UniformEntry () {
  32. if (!this->owned) {
  33. return;
  34. }
  35. switch (this->type) {
  36. case Double:
  37. delete static_cast<const double*> (this->value);
  38. break;
  39. case Float:
  40. delete static_cast<const float*> (this->value);
  41. break;
  42. case Integer:
  43. delete static_cast<const int*> (this->value);
  44. break;
  45. case Vector4:
  46. delete static_cast<const glm::vec4*> (this->value);
  47. break;
  48. case Vector3:
  49. delete static_cast<const glm::vec3*> (this->value);
  50. break;
  51. case Vector2:
  52. delete static_cast<const glm::vec2*> (this->value);
  53. break;
  54. case Matrix4:
  55. delete static_cast<const glm::mat4*> (this->value);
  56. break;
  57. case Matrix3:
  58. delete static_cast<const glm::mat3*> (this->value);
  59. break;
  60. }
  61. }
  62. const TextureMap DEFAULT_BINDS = {};
  63. const ImageEffectPassOverride DEFAULT_OVERRIDE = {};
  64. // objects that don't provide their own layer-to-screen mapping (text, particles) keep the old identity
  65. const glm::mat4 IDENTITY_MATRIX = glm::mat4 (1.0);
  66. const glm::mat3 IDENTITY_MATRIX3 = glm::mat3 (1.0);
  67. namespace {
  68. std::string textureSizeLabel (const std::shared_ptr<const TextureProvider>& texture) {
  69. if (texture == nullptr) {
  70. return "<null>";
  71. }
  72. return std::to_string (texture->getRealWidth ()) + "x" + std::to_string (texture->getRealHeight ());
  73. }
  74. // shader used by Wallpaper Engine's built-in "X-Ray" interactive effect (effects/xray/effect.json)
  75. const std::string XRAY_EFFECT_SHADER = "effects/xray";
  76. // The xray fragment shader gates its reveal on a projector-style sample of a small halo sprite
  77. // (g_Texture2, e.g. "particle/halo_6") taken around the pointer; g_PointerScale only controls how
  78. // far that sample zooms into the sprite, not any on-screen radius, so there's no uniform value that
  79. // makes it cover the whole screen. Instead this patches the compiled fragment source to add a
  80. // g_XrayFullReveal uniform that bypasses the halo sample entirely, forcing the hidden layer
  81. // (g_Texture1) to blend in everywhere once toggled - see patchXrayFullRevealBypass() below.
  82. const std::string XRAY_MULTIPLY_UNIFORM = "uniform float g_Multiply;";
  83. const std::string XRAY_FULL_REVEAL_UNIFORM_DECL = "uniform float g_Multiply;\nuniform float g_XrayFullReveal;";
  84. const std::string XRAY_BLEND_LINE = "blend *= (blendSample.x * blendSample.y);";
  85. const std::string XRAY_BLEND_LINE_PATCHED = "blend *= mix (blendSample.x * blendSample.y, 1.0, g_XrayFullReveal);";
  86. // Returns false (leaving fragmentSource untouched) if the anchors weren't found, e.g. because a
  87. // different spirv-cross/glslang version formats the compiled output differently - callers should
  88. // treat that as "full xray toggle becomes a no-op" rather than fail the whole shader compile.
  89. bool patchXrayFullRevealBypass (std::string& fragmentSource) {
  90. const auto blendPos = fragmentSource.find (XRAY_BLEND_LINE);
  91. const auto uniformPos = fragmentSource.find (XRAY_MULTIPLY_UNIFORM);
  92. if (blendPos == std::string::npos || uniformPos == std::string::npos) {
  93. return false;
  94. }
  95. // replace the later occurrence first so the earlier one's position stays valid
  96. fragmentSource.replace (blendPos, XRAY_BLEND_LINE.size (), XRAY_BLEND_LINE_PATCHED);
  97. fragmentSource.replace (uniformPos, XRAY_MULTIPLY_UNIFORM.size (), XRAY_FULL_REVEAL_UNIFORM_DECL);
  98. return true;
  99. }
  100. }
  101. CPass::CPass (
  102. CRenderable& renderable, std::shared_ptr<const FBOProvider> fboProvider, const MaterialPass& pass,
  103. std::optional<std::reference_wrapper<const ImageEffectPassOverride>> override,
  104. std::optional<std::reference_wrapper<const TextureMap>> binds,
  105. std::optional<std::reference_wrapper<std::string>> target
  106. ) :
  107. Helpers::ContextAware (renderable), m_renderable (renderable), m_fboProvider (std::move (fboProvider)),
  108. m_pass (pass), m_binds (binds.has_value () ? binds.value ().get () : DEFAULT_BINDS),
  109. m_override (override.has_value () ? override.value ().get () : DEFAULT_OVERRIDE), m_target (target),
  110. m_blendingmode (pass.blending), m_vao (GL_NONE) {
  111. this->m_effectTextureProjectionMatrix = &IDENTITY_MATRIX;
  112. this->m_effectTextureProjectionMatrixInverse = &IDENTITY_MATRIX;
  113. this->m_lightingModelMatrix = &IDENTITY_MATRIX;
  114. this->m_lightingNormalMatrix = &IDENTITY_MATRIX3;
  115. this->m_lightingViewProjectionMatrix = &IDENTITY_MATRIX;
  116. this->setupShaders ();
  117. glGenVertexArrays (1, &m_vao);
  118. }
  119. CPass::~CPass () {
  120. for (const auto& texture : this->m_playbackTextures) {
  121. texture->decrementUsageCount ();
  122. }
  123. for (const auto& value : this->m_uniforms | std::views::values) {
  124. delete value;
  125. }
  126. for (const auto& value : this->m_referenceUniforms | std::views::values) {
  127. delete value;
  128. }
  129. for (const auto* attrib : this->m_attribs) {
  130. delete attrib;
  131. }
  132. // text layers rebuild their passes on every glyph texture resize, so this can't be left to leak
  133. this->m_shader = nullptr;
  134. this->m_compiled = nullptr;
  135. glDeleteVertexArrays (1, &m_vao);
  136. this->m_vao = GL_NONE;
  137. if (this->releaseSharedProgram ()) {
  138. return;
  139. }
  140. if (!glIsProgram (this->m_programID)) {
  141. return; // program already invalid or deleted
  142. }
  143. GLint shaderCount = 0;
  144. glGetProgramiv (this->m_programID, GL_ATTACHED_SHADERS, &shaderCount);
  145. if (shaderCount > 0) {
  146. std::vector<GLuint> attachedShaders (shaderCount);
  147. glGetAttachedShaders (this->m_programID, shaderCount, nullptr, attachedShaders.data ());
  148. for (GLuint s : attachedShaders) {
  149. if (glIsShader (s)) {
  150. glDeleteShader (s);
  151. }
  152. }
  153. }
  154. glDeleteProgram (this->m_programID);
  155. this->m_programID = 0;
  156. }
  157. std::shared_ptr<const TextureProvider> CPass::resolveTexture (
  158. std::shared_ptr<const TextureProvider> expected, int index, std::shared_ptr<const TextureProvider> previous
  159. ) {
  160. if (expected == nullptr) {
  161. if (const auto it = this->m_fbos.find (index); it != this->m_fbos.end ()) {
  162. expected = it->second;
  163. }
  164. }
  165. const auto it = this->m_binds.find (index);
  166. if (it == this->m_binds.end ()) {
  167. return expected;
  168. }
  169. // a bind named "previous" is just another way of telling it to use whatever texture there was already
  170. if (it->second == "previous") {
  171. return this->m_previousInput ?: (previous ?: expected);
  172. }
  173. return this->resolveFBO (it->second);
  174. }
  175. void CPass::trackPlayback (const std::shared_ptr<const TextureProvider>& texture) {
  176. // the renderable already counts its own texture, and frame buffers have nothing to play back
  177. if (texture == nullptr || texture == this->m_renderable.getTexture ()
  178. || std::ranges::find (this->m_playbackTextures, texture) != this->m_playbackTextures.end ()) {
  179. return;
  180. }
  181. texture->incrementUsageCount ();
  182. this->m_playbackTextures.push_back (texture);
  183. }
  184. void CPass::updatePlaybackTextures () const {
  185. for (const auto& texture : this->m_playbackTextures) {
  186. texture->update ();
  187. }
  188. }
  189. std::optional<std::string> CPass::resolveUserTextureName (const std::string& propertyName) const {
  190. const auto& properties = this->m_renderable.getScene ().getScene ().project.properties;
  191. const auto it = properties.find (propertyName);
  192. if (it == properties.end ()) {
  193. // not actually a property reference, treat it as a literal texture name like before
  194. return propertyName;
  195. }
  196. // an assigned shortcut shows its icon, TextureCache loads it from outside the wallpaper
  197. if (it->second->is<PropertyUserShortcut> ()) {
  198. const auto shortcut = Desktop::UserShortcut::parse (it->second->getString ());
  199. const auto icon = shortcut.has_value () ? shortcut->iconPath () : std::nullopt;
  200. return icon.has_value () ? std::optional<std::string> ("$usershortcut:" + icon->string ()) : std::nullopt;
  201. }
  202. const std::string& value = it->second->getString ();
  203. if (value.empty ()) {
  204. // the "scenetexture" property exists but the user hasn't imported an image for it -
  205. // this is the normal state for most wallpapers that expose this as an optional slot
  206. return std::nullopt;
  207. }
  208. return value;
  209. }
  210. std::shared_ptr<const TextureProvider> CPass::resolveNamedTexture (const std::string& name) const {
  211. // lit materials sample every cookie spot through one alias, the scene's light cookie (sub_140190C80)
  212. if (name == "_alias_lightCookie") {
  213. return this->m_renderable.getScene ().getLightCookie ();
  214. }
  215. if (name.starts_with ("_rt_") || name.starts_with ("_alias_")) {
  216. return this->resolveFBO (name);
  217. }
  218. return this->getContext ().resolveTexture (name, this->m_renderable.getScene ().getScene ().project);
  219. }
  220. std::shared_ptr<const CFBO> CPass::resolveFBO (const std::string& name) const {
  221. std::shared_ptr<const CFBO> fbo = this->m_fboProvider->find (name);
  222. if (fbo == nullptr && name == "_rt_MipMappedFrameBuffer") {
  223. fbo = this->m_renderable.getScene ().requireMipMappedFrameBuffer ();
  224. }
  225. if (fbo == nullptr) {
  226. sLog.exception ("Tried to resolve and FBO without any luck: ", name);
  227. }
  228. return fbo;
  229. }
  230. void CPass::setupRenderFramebuffer () const {
  231. // what would go onto the scene goes into the passthrough layer drawing its children right now
  232. const auto* layerTarget = this->m_drawTo == this->m_renderable.getScene ().getFBO ()
  233. ? this->m_renderable.getScene ().getLayerTarget ()
  234. : nullptr;
  235. const auto& target = layerTarget != nullptr ? layerTarget->fbo : this->m_drawTo;
  236. glBindFramebuffer (GL_FRAMEBUFFER, target->getFramebuffer ());
  237. // Private per-object FBOs are never cleared elsewhere, so a blending pass would otherwise
  238. // accumulate stale alpha across frames. The shared scene FBO must not be cleared here though,
  239. // since it accumulates every object drawn this frame.
  240. if (this->m_drawTo != this->m_renderable.getScene ().getFBO () && !this->m_keepDestination) {
  241. GLfloat previousClearColor[4] = {};
  242. glGetFloatv (GL_COLOR_CLEAR_VALUE, previousClearColor);
  243. if (this->m_clearColor != nullptr) {
  244. glClearColor (this->m_clearColor->r, this->m_clearColor->g, this->m_clearColor->b, this->m_clearColor->a);
  245. } else {
  246. glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
  247. }
  248. glClear (GL_COLOR_BUFFER_BIT);
  249. glClearColor (previousClearColor[0], previousClearColor[1], previousClearColor[2], previousClearColor[3]);
  250. }
  251. glViewport (0, 0, target->getRealWidth (), target->getRealHeight ());
  252. // the alpha source factor must be GL_ONE, GL_SRC_ALPHA squares every blended pass's alpha and compounds through
  253. // chained effects
  254. switch (this->getBlendingMode ()) {
  255. case BlendingMode_Translucent:
  256. glEnable (GL_BLEND);
  257. glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  258. break;
  259. case BlendingMode_Additive:
  260. glEnable (GL_BLEND);
  261. glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE);
  262. break;
  263. // WE's blend state builder (sub_140099F60) turns blending off for normal on every target, the scene's
  264. // alpha is masked off anyway, so the pass's RGB replaces what's below regardless of its alpha
  265. case BlendingMode_Normal:
  266. default:
  267. glDisable (GL_BLEND);
  268. break;
  269. }
  270. // sub_140099F60 with renderer state flag 0x10: alpha op MAX and all four channels written, the color blend stays
  271. if (layerTarget != nullptr && layerTarget->alphaMax) {
  272. glBlendEquationSeparate (GL_FUNC_ADD, GL_MAX);
  273. glColorMask (true, true, true, true);
  274. }
  275. switch (this->m_pass.depthtest) {
  276. case DepthtestMode_Enabled:
  277. glEnable (GL_DEPTH_TEST);
  278. glDepthFunc (GL_LEQUAL);
  279. break;
  280. case DepthtestMode_Disabled:
  281. default:
  282. glDisable (GL_DEPTH_TEST);
  283. break;
  284. }
  285. switch (this->m_pass.cullmode) {
  286. case CullingMode_Normal:
  287. glEnable (GL_CULL_FACE);
  288. break;
  289. case CullingMode_Disable:
  290. default:
  291. glDisable (GL_CULL_FACE);
  292. break;
  293. }
  294. switch (this->m_pass.depthwrite) {
  295. case DepthwriteMode_Enabled:
  296. glDepthMask (true);
  297. break;
  298. case DepthwriteMode_Disabled:
  299. default:
  300. glDepthMask (false);
  301. break;
  302. }
  303. }
  304. void CPass::setupRenderTexture () {
  305. glUseProgram (this->m_programID);
  306. auto texture0 = this->resolveTexture0 ();
  307. const auto animation = this->resolveTextureAnimationState (texture0);
  308. this->bindTextureUnit (0, texture0, animation.currentTexture);
  309. this->bindTextureOverrides (animation.currentTexture, texture0);
  310. if (texture0 != nullptr) {
  311. this->m_texture0Resolution = *texture0->getResolution ();
  312. }
  313. // used in animations when one of the frames is vertical instead of horizontal
  314. // rotation with translation = origin and end of the image to display
  315. if (this->g_Texture0Rotation != -1) {
  316. glUniform4f (
  317. this->g_Texture0Rotation, animation.rotation.x, animation.rotation.y, animation.rotation.z,
  318. animation.rotation.w
  319. );
  320. }
  321. // this actually picks the origin point of the image from the atlast
  322. if (this->g_Texture0Translation != -1) {
  323. glUniform2f (this->g_Texture0Translation, animation.translation.x, animation.translation.y);
  324. }
  325. }
  326. std::shared_ptr<const TextureProvider> CPass::resolveTexture0 () {
  327. auto texture0 = this->resolveTexture (this->m_input, 0, this->m_input);
  328. const auto it = this->m_textures.find (0);
  329. if (it == this->m_textures.end ()) {
  330. return texture0;
  331. }
  332. auto& chain = it->second;
  333. do {
  334. texture0 = chain->texture;
  335. if (texture0 == nullptr) {
  336. if (this->m_previousInput != nullptr && this->m_previousInput->isReady ()) {
  337. return this->m_previousInput;
  338. }
  339. if (this->m_input != nullptr && this->m_input->isReady ()) {
  340. return this->m_input;
  341. }
  342. } else if (texture0->isReady ()) {
  343. return texture0;
  344. }
  345. chain = chain->next;
  346. } while (chain != nullptr);
  347. // got to the end of the chain, use previous input or current input if available
  348. if (this->m_previousInput != nullptr && this->m_previousInput->isReady ()) {
  349. return this->m_previousInput;
  350. }
  351. // last resort, doesn't matter if the input is ready or not
  352. return this->m_input;
  353. }
  354. CPass::TextureAnimationState
  355. CPass::resolveTextureAnimationState (const std::shared_ptr<const TextureProvider>& texture) const {
  356. TextureAnimationState state;
  357. if (texture == nullptr || !texture->isAnimated ()) {
  358. return state;
  359. }
  360. // scene time like every other animation, so --speed, --disable-animations and pausing apply
  361. double currentRenderTime = fmod (static_cast<double> (g_Time), this->m_renderable.getAnimationTime ());
  362. for (const auto& frameCur : texture->getFrames ()) {
  363. currentRenderTime -= frameCur->frametime;
  364. if (currentRenderTime > 0.0f) {
  365. continue;
  366. }
  367. state.currentTexture = frameCur->frameNumber;
  368. state.translation.x = frameCur->x / texture->getTextureWidth (state.currentTexture);
  369. state.translation.y = frameCur->y / texture->getTextureHeight (state.currentTexture);
  370. state.rotation.x = frameCur->width1 / static_cast<float> (texture->getTextureWidth (state.currentTexture));
  371. state.rotation.y = frameCur->width2 / static_cast<float> (texture->getTextureWidth (state.currentTexture));
  372. state.rotation.z = frameCur->height2 / static_cast<float> (texture->getTextureHeight (state.currentTexture));
  373. state.rotation.w = frameCur->height1 / static_cast<float> (texture->getTextureHeight (state.currentTexture));
  374. break;
  375. }
  376. return state;
  377. }
  378. void CPass::bindTextureUnit (int index, const std::shared_ptr<const TextureProvider>& texture, uint32_t frame) const {
  379. if (texture == nullptr) {
  380. return;
  381. }
  382. glActiveTexture (GL_TEXTURE0 + index);
  383. glBindTexture (GL_TEXTURE_2D, texture->getTextureID (frame));
  384. }
  385. void CPass::bindTextureOverrides (uint32_t currentTexture, std::shared_ptr<const TextureProvider>& texture0) const {
  386. for (auto [index, chain] : this->m_textures) {
  387. auto expectedTexture = chain->texture;
  388. do {
  389. if (expectedTexture == nullptr) {
  390. if (this->m_previousInput != nullptr && this->m_previousInput->isReady ()) {
  391. expectedTexture = this->m_previousInput;
  392. break;
  393. }
  394. if (this->m_input != nullptr && this->m_input->isReady ()) {
  395. expectedTexture = this->m_input;
  396. break;
  397. }
  398. } else if (expectedTexture->isReady ()) {
  399. break;
  400. }
  401. chain = chain->next;
  402. expectedTexture = chain == nullptr ? nullptr : chain->texture;
  403. } while (chain != nullptr);
  404. if (expectedTexture == nullptr && this->m_previousInput != nullptr && this->m_previousInput->isReady ()) {
  405. expectedTexture = this->m_previousInput;
  406. }
  407. if (expectedTexture == nullptr) {
  408. expectedTexture = this->m_input;
  409. }
  410. this->bindTextureUnit (index, expectedTexture, index == 0 ? currentTexture : 0);
  411. if (index == 0) {
  412. texture0 = expectedTexture;
  413. }
  414. }
  415. }
  416. void CPass::setupRenderReferenceUniforms () {
  417. for (const auto& value : this->m_referenceUniforms | std::views::values) {
  418. switch (value->type) {
  419. case Double:
  420. glUniform1d (value->id, *static_cast<const double*> (*value->value));
  421. break;
  422. case Float:
  423. glUniform1f (value->id, *static_cast<const float*> (*value->value));
  424. break;
  425. case Integer:
  426. glUniform1i (value->id, *static_cast<const int*> (*value->value));
  427. break;
  428. case Vector4:
  429. glUniform4fv (value->id, 1, glm::value_ptr (*static_cast<const glm::vec4*> (*value->value)));
  430. break;
  431. case Vector3:
  432. glUniform3fv (value->id, 1, glm::value_ptr (*static_cast<const glm::vec3*> (*value->value)));
  433. break;
  434. case Vector2:
  435. glUniform2fv (value->id, 1, glm::value_ptr (*static_cast<const glm::vec2*> (*value->value)));
  436. break;
  437. case Matrix4:
  438. glUniformMatrix4fv (
  439. value->id, 1, GL_FALSE, glm::value_ptr (*static_cast<const glm::mat4*> (*value->value))
  440. );
  441. break;
  442. case Matrix3:
  443. glUniformMatrix3fv (
  444. value->id, 1, GL_FALSE, glm::value_ptr (*static_cast<const glm::mat3*> (*value->value))
  445. );
  446. break;
  447. }
  448. }
  449. }
  450. void CPass::setupRenderUniforms () {
  451. for (const auto& value : this->m_uniforms | std::views::values) {
  452. switch (value->type) {
  453. case Double:
  454. glUniform1dv (value->id, value->count, static_cast<const double*> (value->value));
  455. break;
  456. case Float:
  457. glUniform1fv (value->id, value->count, static_cast<const float*> (value->value));
  458. break;
  459. case Integer:
  460. glUniform1iv (value->id, value->count, static_cast<const int*> (value->value));
  461. break;
  462. case Vector4:
  463. glUniform4fv (value->id, value->count, glm::value_ptr (*static_cast<const glm::vec4*> (value->value)));
  464. break;
  465. case Vector3:
  466. glUniform3fv (value->id, value->count, glm::value_ptr (*static_cast<const glm::vec3*> (value->value)));
  467. break;
  468. case Vector2:
  469. glUniform2fv (value->id, value->count, glm::value_ptr (*static_cast<const glm::vec2*> (value->value)));
  470. break;
  471. case Matrix4:
  472. glUniformMatrix4fv (
  473. value->id, value->count, GL_FALSE, glm::value_ptr (*static_cast<const glm::mat4*> (value->value))
  474. );
  475. break;
  476. case Matrix3:
  477. glUniformMatrix3fv (
  478. value->id, value->count, GL_FALSE, glm::value_ptr (*static_cast<const glm::mat3*> (value->value))
  479. );
  480. break;
  481. }
  482. }
  483. }
  484. void CPass::setupRenderAttributes () const {
  485. if (this->m_setupAttribsCallback) {
  486. this->m_setupAttribsCallback ();
  487. return;
  488. }
  489. for (const auto& cur : this->m_attribs) {
  490. glEnableVertexAttribArray (cur->id);
  491. glBindBuffer (GL_ARRAY_BUFFER, *cur->value);
  492. glVertexAttribPointer (cur->id, cur->elements, cur->type, GL_FALSE, 0, nullptr);
  493. #if !NDEBUG
  494. glObjectLabel (
  495. GL_BUFFER, *cur->value, -1,
  496. ("Image " + std::to_string (this->m_renderable.getId ()) + " Pass " + this->m_pass.shader + " " + cur->name)
  497. .c_str ()
  498. );
  499. #endif /* DEBUG */
  500. }
  501. }
  502. void CPass::renderGeometry () const {
  503. if (this->m_drawGeometryCallback) {
  504. this->m_drawGeometryCallback ();
  505. return;
  506. }
  507. glBindBuffer (GL_ARRAY_BUFFER, this->a_Position);
  508. glDrawArrays (GL_TRIANGLES, 0, 6);
  509. }
  510. void CPass::cleanupRenderSetup () {
  511. if (this->m_cleanupAttribsCallback) {
  512. this->m_cleanupAttribsCallback ();
  513. } else {
  514. // disable vertex attribs array
  515. for (const auto& cur : this->m_attribs) {
  516. glDisableVertexAttribArray (cur->id);
  517. }
  518. }
  519. glActiveTexture (GL_TEXTURE0);
  520. glBindTexture (GL_TEXTURE_2D, 0);
  521. for (const auto& index : this->m_textures | std::views::keys) {
  522. glActiveTexture (GL_TEXTURE0 + index);
  523. glBindTexture (GL_TEXTURE_2D, 0);
  524. }
  525. }
  526. void CPass::refreshRenderableUniforms () {
  527. const auto update = [this] (const char* name, const auto& value) {
  528. const auto it = this->m_uniforms.find (name);
  529. if (it != this->m_uniforms.end () && it->second->owned && !this->m_constantUniforms.contains (name)) {
  530. using Value = std::remove_cvref_t<decltype (value)>;
  531. *static_cast<Value*> (const_cast<void*> (it->second->value)) = value;
  532. }
  533. };
  534. update ("g_UserAlpha", this->m_renderable.getUserAlpha ());
  535. update ("g_Alpha", this->m_renderable.getAlpha ());
  536. update ("g_Color", this->m_renderable.getColor ());
  537. update ("g_Color4", this->m_neutralColor ? glm::vec4 (1.0f) : this->m_renderable.getColor4 ());
  538. }
  539. void CPass::render () {
  540. glBindVertexArray (this->m_vao);
  541. // copied when the pass was built, color and alpha scripts or animations change them every frame
  542. this->refreshRenderableUniforms ();
  543. if (this->m_pass.shader == XRAY_EFFECT_SHADER) {
  544. const bool fullReveal = this->getContext ().getApp ().getContext ().state.xray.fullReveal;
  545. this->m_xrayFullReveal = fullReveal ? 1.0f : 0.0f;
  546. }
  547. const auto& debug = this->getContext ().getApp ().getContext ().settings.render.debug;
  548. if (debug.passLog) {
  549. sLog.out (
  550. "Render pass object=", this->m_renderable.getId (), " shader=", this->m_pass.shader,
  551. " target=", this->m_target.has_value () ? this->m_target.value ().get () : std::string ("<screen/local>"),
  552. " drawTo=", this->m_drawTo ? this->m_drawTo->getName () : std::string ("<null>"),
  553. " drawSize=", textureSizeLabel (this->m_drawTo), " inputSize=", textureSizeLabel (this->m_input)
  554. );
  555. for (const auto* uniformName : { "g_TintColor", "g_CompositeColor", "g_BlendAlpha", "g_CompositeAlpha" }) {
  556. const auto uniform = this->m_uniforms.find (uniformName);
  557. if (uniform == this->m_uniforms.end ()) {
  558. continue;
  559. }
  560. switch (uniform->second->type) {
  561. case Vector3:
  562. {
  563. const auto* v = static_cast<const glm::vec3*> (uniform->second->value);
  564. sLog.out (" uniform ", uniformName, "=", v->x, " ", v->y, " ", v->z);
  565. break;
  566. }
  567. case Float:
  568. {
  569. const auto* v = static_cast<const float*> (uniform->second->value);
  570. sLog.out (" uniform ", uniformName, "=", *v);
  571. break;
  572. }
  573. default:
  574. break;
  575. }
  576. }
  577. }
  578. if (this->m_drawTo == nullptr) {
  579. sLog.error ("Skipping render pass for object ", this->m_renderable.getId (), ": no destination FBO set");
  580. return;
  581. }
  582. if (this->m_input == nullptr) {
  583. sLog.error ("Skipping render pass for object ", this->m_renderable.getId (), ": no input texture set");
  584. return;
  585. }
  586. // a passthrough layer draws its children into its buffer: model = inverse (layer world), view = identity and an
  587. // ortho of the layer's size (sub_1401ECB20), which comes down to remapping the scene's clip space
  588. const auto* layerTarget = this->m_drawTo == this->m_renderable.getScene ().getFBO () || this->m_followLayerTarget
  589. ? this->m_renderable.getScene ().getLayerTarget ()
  590. : nullptr;
  591. const glm::mat4* modelViewProjection = this->m_modelViewProjectionMatrix;
  592. const glm::mat4* modelViewProjectionInverse = this->m_modelViewProjectionMatrixInverse;
  593. const glm::mat4* viewProjection = this->m_viewProjectionMatrix;
  594. if (layerTarget != nullptr) {
  595. if (modelViewProjection != nullptr) {
  596. this->m_layerModelViewProjection = layerTarget->transform * *modelViewProjection;
  597. this->m_layerModelViewProjectionInverse = glm::inverse (this->m_layerModelViewProjection);
  598. this->m_modelViewProjectionMatrix = &this->m_layerModelViewProjection;
  599. this->m_modelViewProjectionMatrixInverse = &this->m_layerModelViewProjectionInverse;
  600. }
  601. if (viewProjection != nullptr) {
  602. this->m_layerViewProjection = layerTarget->transform * *viewProjection;
  603. this->m_viewProjectionMatrix = &this->m_layerViewProjection;
  604. }
  605. }
  606. // effectcomposebackground (refraction, ...) samples _rt_FullFrameBuffer where the layer is on screen
  607. this->m_effectModelViewProjectionMatrix = this->m_effectModelViewProjectionOverride != nullptr
  608. ? this->m_effectModelViewProjectionOverride
  609. : this->m_modelViewProjectionMatrix;
  610. this->setupRenderFramebuffer ();
  611. this->setupRenderTexture ();
  612. this->setupRenderUniforms ();
  613. this->setupRenderReferenceUniforms ();
  614. this->setupRenderAttributes ();
  615. this->renderGeometry ();
  616. this->cleanupRenderSetup ();
  617. if (layerTarget != nullptr) {
  618. this->m_modelViewProjectionMatrix = modelViewProjection;
  619. this->m_modelViewProjectionMatrixInverse = modelViewProjectionInverse;
  620. this->m_viewProjectionMatrix = viewProjection;
  621. if (layerTarget->alphaMax && this->m_drawTo == this->m_renderable.getScene ().getFBO ()) {
  622. glBlendEquation (GL_FUNC_ADD);
  623. }
  624. }
  625. }
  626. void CPass::clearDestination () const {
  627. if (this->m_drawTo == nullptr) {
  628. return;
  629. }
  630. GLfloat previousClearColor[4] = {};
  631. glGetFloatv (GL_COLOR_CLEAR_VALUE, previousClearColor);
  632. glBindFramebuffer (GL_FRAMEBUFFER, this->m_drawTo->getFramebuffer ());
  633. glViewport (0, 0, this->m_drawTo->getRealWidth (), this->m_drawTo->getRealHeight ());
  634. glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
  635. glClear (GL_COLOR_BUFFER_BIT);
  636. glClearColor (previousClearColor[0], previousClearColor[1], previousClearColor[2], previousClearColor[3]);
  637. }
  638. std::shared_ptr<const FBOProvider> CPass::getFBOProvider () const { return this->m_fboProvider; }
  639. const CRenderable& CPass::getRenderable () const { return this->m_renderable; }
  640. void CPass::setDestination (std::shared_ptr<const CFBO> drawTo) { this->m_drawTo = std::move (drawTo); }
  641. void CPass::setInput (std::shared_ptr<const TextureProvider> input) { this->m_input = std::move (input); }
  642. void CPass::setPreviousInput (std::shared_ptr<const TextureProvider> input) {
  643. this->m_previousInput = std::move (input);
  644. }
  645. void CPass::setEffectModelViewProjectionMatrix (const glm::mat4* projection) {
  646. this->m_effectModelViewProjectionOverride = projection;
  647. }
  648. void CPass::setModelViewProjectionMatrix (const glm::mat4* projection) {
  649. this->m_modelViewProjectionMatrix = projection;
  650. }
  651. void CPass::setModelViewProjectionMatrixInverse (const glm::mat4* projection) {
  652. this->m_modelViewProjectionMatrixInverse = projection;
  653. }
  654. const glm::mat4 CPass::s_identity { 1.0f };
  655. void CPass::setModelMatrix (const glm::mat4* model) { this->m_modelMatrix = model; }
  656. void CPass::setLayerModelMatrix (const glm::mat4* model) { this->m_layerModelMatrix = model; }
  657. void CPass::setFogWorld (const bool world) {
  658. this->m_fogWorld = world;
  659. const auto& fog = this->m_renderable.getScene ().getFog ();
  660. this->addUniform ("g_FogHeightParams", world ? &fog.heightParamsWorld : &fog.heightParamsLocal);
  661. if (world) {
  662. this->addUniform ("g_EyePosition", &fog.eyeWorld);
  663. }
  664. }
  665. void CPass::setViewProjectionMatrix (const glm::mat4* viewProjection) { this->m_viewProjectionMatrix = viewProjection; }
  666. void CPass::setLightingTransform (const glm::mat4* model, const glm::mat3* normal, const glm::mat4* viewProjection) {
  667. this->m_lightingModelMatrix = model;
  668. this->m_lightingNormalMatrix = normal;
  669. this->m_lightingViewProjectionMatrix = viewProjection;
  670. }
  671. void CPass::setEffectTextureProjectionMatrix (const glm::mat4* projection, const glm::mat4* inverse) {
  672. this->m_effectTextureProjectionMatrix = projection;
  673. this->m_effectTextureProjectionMatrixInverse = inverse;
  674. }
  675. void CPass::setBlendingMode (BlendingMode blendingmode) { this->m_blendingmode = blendingmode; }
  676. BlendingMode CPass::getBlendingMode () const { return this->m_blendingmode; }
  677. void CPass::setTexCoord (GLuint texcoord) { this->a_TexCoord = texcoord; }
  678. void CPass::setClearColor (const glm::vec4* color) { this->m_clearColor = color; }
  679. void CPass::setKeepDestination (bool keep) { this->m_keepDestination = keep; }
  680. void CPass::setFollowLayerTarget (bool follow) { this->m_followLayerTarget = follow; }
  681. void CPass::setNeutralColor (bool neutral) { this->m_neutralColor = neutral; }
  682. void CPass::setPosition (GLuint position) { this->a_Position = position; }
  683. const MaterialPass& CPass::getPass () const { return this->m_pass; }
  684. std::optional<std::reference_wrapper<std::string>> CPass::getTarget () const { return this->m_target; }
  685. Render::Shaders::Shader* CPass::getShader () const { return this->m_shader; }
  686. GLuint CPass::getProgramID () const { return this->m_programID; }
  687. void CPass::setGeometryCallback (
  688. GeometryCallback setupAttribs, GeometryCallback drawGeometry, GeometryCallback cleanupAttribs
  689. ) {
  690. this->m_setupAttribsCallback = std::move (setupAttribs);
  691. this->m_drawGeometryCallback = std::move (drawGeometry);
  692. this->m_cleanupAttribsCallback = std::move (cleanupAttribs);
  693. }
  694. GLuint CPass::compileShader (const char* shader, GLuint type) {
  695. const GLuint shaderID = glCreateShader (type);
  696. // Mesa mis-reads a vec3 uniform followed by a float used as vec4(vec3, float), use the g_Color4 the engine already
  697. // exposes
  698. std::string patched;
  699. if (type == GL_FRAGMENT_SHADER) {
  700. patched = shader;
  701. const std::string declarations = "uniform vec3 g_Color;\nuniform float g_Alpha;";
  702. const std::string construction = "vec4(g_Color, g_Alpha)";
  703. const auto declarationsAt = patched.find (declarations);
  704. const auto constructionAt = patched.find (construction);
  705. if (declarationsAt != std::string::npos && constructionAt != std::string::npos) {
  706. patched.replace (constructionAt, construction.size (), "g_Color4");
  707. patched.replace (declarationsAt, declarations.size (), "uniform vec4 g_Color4;");
  708. // only safe when that was the sole use of both
  709. // the generated source keeps the original as an "#if 0" block at the end, that doesn't count
  710. const size_t codeEnd = std::min (patched.size (), patched.find ("#if 0"));
  711. const auto stillUsed = [&patched, codeEnd] (const std::string& name) {
  712. size_t pos = 0;
  713. while ((pos = patched.find (name, pos)) != std::string::npos && pos < codeEnd) {
  714. const bool wordStart = pos == 0
  715. || !(std::isalnum (static_cast<unsigned char> (patched[pos - 1])) || patched[pos - 1] == '_');
  716. const size_t end = pos + name.size ();
  717. const bool wordEnd = end >= patched.size ()
  718. || !(std::isalnum (static_cast<unsigned char> (patched[end])) || patched[end] == '_');
  719. if (wordStart && wordEnd) {
  720. return true;
  721. }
  722. pos = end;
  723. }
  724. return false;
  725. };
  726. if (stillUsed ("g_Color") || stillUsed ("g_Alpha")) {
  727. patched = shader;
  728. }
  729. }
  730. shader = patched.c_str ();
  731. }
  732. glShaderSource (shaderID, 1, &shader, nullptr);
  733. glCompileShader (shaderID);
  734. GLint result = GL_FALSE;
  735. int infoLogLength = 0;
  736. glGetShaderiv (shaderID, GL_COMPILE_STATUS, &result);
  737. glGetShaderiv (shaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
  738. if (infoLogLength > 0) {
  739. const auto logBuffer = new char[infoLogLength + 1];
  740. memset (logBuffer, 0, infoLogLength + 1);
  741. glGetShaderInfoLog (shaderID, infoLogLength, nullptr, logBuffer);
  742. std::stringstream buffer;
  743. buffer << logBuffer << std::endl << "Compiled source code:" << std::endl << shader;
  744. delete[] logBuffer;
  745. if (result == GL_FALSE) {
  746. sLog.exception (buffer.str ());
  747. } else {
  748. sLog.error (buffer.str ());
  749. }
  750. }
  751. return shaderID;
  752. }
  753. void CPass::setupShaders () {
  754. const auto texture0 = this->m_renderable.getTexture ();
  755. this->m_combos.insert (this->m_pass.combos.begin (), this->m_pass.combos.end ());
  756. const auto comboEnabled = [this] (const std::string& name) {
  757. const auto override = this->m_override.combos.find (name);
  758. if (override != this->m_override.combos.end ()) {
  759. return override->second != 0;
  760. }
  761. const auto combo = this->m_combos.find (name);
  762. return combo != this->m_combos.end () && combo->second != 0;
  763. };
  764. if (comboEnabled ("LIGHTING") || comboEnabled ("REFLECTION")) {
  765. this->m_combos.insert_or_assign ("PRELIGHTING", 1);
  766. }
  767. // HDR scene rendering defines HDR for every pass (sub_1401A5C40)
  768. if (this->m_renderable.getScene ().isHDR ()) {
  769. this->m_combos.insert_or_assign ("HDR", 1);
  770. }
  771. // particle shaders read TEX0FORMAT without a formatcombo sampler, the other slots are handled below
  772. if (texture0 != nullptr) {
  773. if (texture0->getFormat () == TextureFormat_RG88) {
  774. this->m_combos.insert_or_assign ("TEX0FORMAT", 8);
  775. } else if (texture0->getFormat () == TextureFormat_R8) {
  776. this->m_combos.insert_or_assign ("TEX0FORMAT", 9);
  777. }
  778. }
  779. // TODO: review the shader textures here; ones passed to the shader shouldn't be in this list
  780. // (used later to build the textures)
  781. // use the combos copied from the pass so it includes the texture format
  782. const std::string& shaderName
  783. = this->m_override.shaderOverride.has_value () ? this->m_override.shaderOverride.value () : this->m_pass.shader;
  784. TextureMap passTextures = this->m_pass.textures;
  785. for (const auto& [index, propertyName] : this->m_pass.usertextures) {
  786. // leave the default texture (if any) in place when the user hasn't provided an override,
  787. // same rule applied when the actual texture chain gets built in setupTextureUniforms()
  788. if (const auto resolved = this->resolveUserTextureName (propertyName); resolved.has_value ()) {
  789. passTextures.insert_or_assign (index, *resolved);
  790. }
  791. }
  792. // same for the object's own user textures, otherwise the combo that enables their slot stays off
  793. TextureMap overrideTextures = this->m_override.textures;
  794. for (const auto& [index, propertyName] : this->m_override.usertextures) {
  795. if (const auto resolved = this->resolveUserTextureName (propertyName); resolved.has_value ()) {
  796. overrideTextures.insert_or_assign (index, *resolved);
  797. }
  798. }
  799. // every pass of an orthographic scene gets SCENE_ORTHO (renderer flag 0x400, sub_1401A5C40)
  800. if (this->m_renderable.getScene ().getCamera ().isOrthogonal ()) {
  801. this->m_combos.insert_or_assign ("SCENE_ORTHO", 1);
  802. }
  803. this->m_compiled = this->compileShaderSources (shaderName, passTextures, overrideTextures);
  804. this->m_shader = this->m_compiled->shader.get ();
  805. // samplers marked "formatcombo" get TEX<slot>FORMAT set to their texture's format, like wallpaper64.exe
  806. // (sub_14015EC30). Which slots those are is only known once the shader is parsed, so rebuild it when one changed
  807. if (this->applyFormatCombos (passTextures, overrideTextures)) {
  808. this->m_compiled = this->compileShaderSources (shaderName, passTextures, overrideTextures);
  809. this->m_shader = this->m_compiled->shader.get ();
  810. }
  811. // fog turns into FOG_DIST/FOG_HEIGHT for every pass whose FOG combo (the shader default included) is on
  812. // (sub_1401A5C40); that default is only known once the shader is parsed
  813. const auto& scene = this->m_renderable.getScene ();
  814. const auto comboValue = [this] (const std::string& name) {
  815. for (const ComboMap* combos :
  816. std::initializer_list<const ComboMap*> { &this->m_override.combos, &this->m_combos }) {
  817. if (const auto it = combos->find (name); it != combos->end ()) {
  818. return it->second;
  819. }
  820. }
  821. for (const auto* unit : { &this->m_shader->getFragment (), &this->m_shader->getVertex () }) {
  822. if (const auto it = unit->getDiscoveredCombos ().find (name); it != unit->getDiscoveredCombos ().end ()) {
  823. return it->second;
  824. }
  825. }
  826. return 0;
  827. };
  828. // same for LIGHTING: the light counts of the scene's lightconfig, which the LightingV1 module is generated from.
  829. // Without shadow mapping the shadow counts stay 0, like WE with its shadow setting off
  830. if (comboValue ("LIGHTING") != 0) {
  831. const auto& lighting = scene.getLightingV1 ();
  832. this->m_combos.insert_or_assign ("LIGHTS_POINT", lighting.points);
  833. this->m_combos.insert_or_assign ("LIGHTS_SPOT", lighting.spots);
  834. this->m_combos.insert_or_assign ("LIGHTS_TUBE", lighting.tubes);
  835. this->m_combos.insert_or_assign ("LIGHTS_DIRECTIONAL", lighting.directionals);
  836. this->m_combos.insert_or_assign ("LIGHTS_SPOT_SHADOW_COOKIE", 0);
  837. this->m_combos.insert_or_assign ("LIGHTS_SPOT_SHADOW", 0);
  838. this->m_combos.insert_or_assign ("LIGHTS_SPOT_COOKIE", lighting.spotCookies);
  839. this->m_combos.insert_or_assign ("LIGHTS_DIRECTIONAL_SHADOW", 0);
  840. this->m_combos.insert_or_assign ("LIGHTS_POINT_SHADOW", 0);
  841. if (lighting.spotCookies != 0) {
  842. this->m_combos.insert_or_assign ("LIGHTS_COOKIE", 1);
  843. }
  844. this->m_compiled = this->compileShaderSources (shaderName, passTextures, overrideTextures);
  845. this->m_shader = this->m_compiled->shader.get ();
  846. }
  847. if (scene.hasDistanceFog () || scene.hasHeightFog ()) {
  848. if (comboValue ("FOG") != 0) {
  849. if (scene.hasDistanceFog ()) {
  850. this->m_combos.insert_or_assign ("FOG_DIST", 1);
  851. }
  852. if (scene.hasHeightFog ()) {
  853. this->m_combos.insert_or_assign ("FOG_HEIGHT", 1);
  854. }
  855. this->m_compiled = this->compileShaderSources (shaderName, passTextures, overrideTextures);
  856. this->m_shader = this->m_compiled->shader.get ();
  857. }
  858. }
  859. std::string vertex = this->m_compiled->vertex;
  860. std::string fragment = this->m_compiled->fragment;
  861. if (shaderName == XRAY_EFFECT_SHADER) {
  862. this->m_xrayFullRevealPatched = patchXrayFullRevealBypass (fragment);
  863. if (!this->m_xrayFullRevealPatched) {
  864. sLog.error (
  865. "Full xray toggle unavailable: couldn't find the expected reveal blend line in the "
  866. "compiled effects/xray shader (spirv-cross output format may have changed)"
  867. );
  868. }
  869. }
  870. // passes with the same sources share one program (every particle system instance of a child would link its own
  871. // otherwise, hundreds in the first seconds of a rain wallpaper). Uniforms are uploaded on every draw, the sampler
  872. // units below are the same for all of them
  873. this->m_programKey = vertex + '\0' + fragment;
  874. if (const auto cached = sharedPrograms ().find (this->m_programKey); cached != sharedPrograms ().end ()) {
  875. this->m_programID = cached->second.program;
  876. cached->second.users++;
  877. } else {
  878. this->m_programID = this->linkProgram (vertex, fragment, shaderName);
  879. sharedPrograms ().emplace (this->m_programKey, SharedProgram { this->m_programID, 1 });
  880. }
  881. // bind each g_TextureN sampler to unit N explicitly, the translated GLSL collapses every layout(binding) to 0
  882. {
  883. glUseProgram (this->m_programID);
  884. for (int index = 0; index <= 9; index++) {
  885. const std::string name = "g_Texture" + std::to_string (index);
  886. const GLint loc = glGetUniformLocation (this->m_programID, name.c_str ());
  887. if (loc != -1) {
  888. glUniform1i (loc, index);
  889. }
  890. }
  891. }
  892. // first setup the default values, these will be overwritten by future values
  893. this->setupShaderVariables ();
  894. this->setupUniforms ();
  895. this->setupAttributes ();
  896. this->g_Texture0Rotation = glGetUniformLocation (this->m_programID, "g_Texture0Rotation");
  897. this->g_Texture0Translation = glGetUniformLocation (this->m_programID, "g_Texture0Translation");
  898. }
  899. std::unordered_map<std::string, std::weak_ptr<CPass::CompiledShader>>& CPass::sharedShaders () {
  900. static std::unordered_map<std::string, std::weak_ptr<CompiledShader>> shaders;
  901. return shaders;
  902. }
  903. std::shared_ptr<CPass::CompiledShader> CPass::compileShaderSources (
  904. const std::string& shaderName, const TextureMap& passTextures, const TextureMap& overrideTextures
  905. ) {
  906. // parsing and translating a shader takes a few ms, and every instance of a child particle system builds the same
  907. // one. Override constants can change the parsed defaults, those passes keep a shader of their own
  908. const bool shareable = this->m_override.constants.empty ();
  909. std::string key;
  910. if (shareable) {
  911. std::ostringstream stream;
  912. stream << shaderName << '\0' << &this->m_renderable.getAssetLocator ();
  913. const ComboMap* comboMaps[] = { &this->m_combos, &this->m_override.combos };
  914. for (const ComboMap* combos : comboMaps) {
  915. stream << '\1';
  916. for (const auto& [name, value] : *combos) {
  917. stream << name << '=' << value << ';';
  918. }
  919. }
  920. for (const TextureMap* textures : { &passTextures, &overrideTextures }) {
  921. stream << '\1';
  922. for (const auto& [index, name] : *textures) {
  923. stream << index << '=' << name << ';';
  924. }
  925. }
  926. key = stream.str ();
  927. if (const auto it = sharedShaders ().find (key); it != sharedShaders ().end ()) {
  928. if (auto cached = it->second.lock ()) {
  929. return cached;
  930. }
  931. }
  932. }
  933. static const ShaderConstantMap noConstants;
  934. auto compiled = std::make_shared<CompiledShader> ();
  935. compiled->combos = this->m_combos;
  936. compiled->overrideCombos = this->m_override.combos;
  937. compiled->passTextures = passTextures;
  938. compiled->overrideTextures = overrideTextures;
  939. compiled->shader = std::make_unique<Render::Shaders::Shader> (
  940. this->m_renderable.getAssetLocator (), shaderName, compiled->combos, compiled->overrideCombos,
  941. compiled->passTextures, compiled->overrideTextures, shareable ? noConstants : this->m_override.constants
  942. );
  943. auto [vertex, fragment]
  944. = Shaders::GLSLContext::get ().toGlsl (compiled->shader->vertex (), compiled->shader->fragment (), shaderName);
  945. compiled->vertex = std::move (vertex);
  946. compiled->fragment = std::move (fragment);
  947. if (shareable) {
  948. std::erase_if (sharedShaders (), [] (const auto& entry) { return entry.second.expired (); });
  949. sharedShaders ().insert_or_assign (key, compiled);
  950. }
  951. return compiled;
  952. }
  953. std::unordered_map<std::string, CPass::SharedProgram>& CPass::sharedPrograms () {
  954. static std::unordered_map<std::string, SharedProgram> programs;
  955. return programs;
  956. }
  957. bool CPass::releaseSharedProgram () {
  958. const auto it = sharedPrograms ().find (this->m_programKey);
  959. if (this->m_programKey.empty () || it == sharedPrograms ().end ()) {
  960. return false;
  961. }
  962. if (--it->second.users == 0) {
  963. glDeleteProgram (it->second.program);
  964. sharedPrograms ().erase (it);
  965. }
  966. this->m_programID = 0;
  967. return true;
  968. }
  969. GLuint CPass::linkProgram (const std::string& vertex, const std::string& fragment, const std::string& shaderName) {
  970. const GLuint vertexShaderID = compileShader (vertex.c_str (), GL_VERTEX_SHADER);
  971. const GLuint fragmentShaderID = compileShader (fragment.c_str (), GL_FRAGMENT_SHADER);
  972. const GLuint program = glCreateProgram ();
  973. glAttachShader (program, vertexShaderID);
  974. glAttachShader (program, fragmentShaderID);
  975. glLinkProgram (program);
  976. GLint result = GL_FALSE;
  977. int infoLogLength = 0;
  978. glGetProgramiv (program, GL_LINK_STATUS, &result);
  979. glGetProgramiv (program, GL_INFO_LOG_LENGTH, &infoLogLength);
  980. if (infoLogLength > 0) {
  981. const auto logBuffer = new char[infoLogLength + 1];
  982. memset (logBuffer, 0, infoLogLength + 1);
  983. glGetProgramInfoLog (program, infoLogLength, nullptr, logBuffer);
  984. const std::string message = logBuffer;
  985. delete[] logBuffer;
  986. if (result == GL_FALSE) {
  987. sLog.exception (message);
  988. } else {
  989. sLog.error (message);
  990. }
  991. }
  992. #if !NDEBUG
  993. glObjectLabel (GL_PROGRAM, program, -1, shaderName.c_str ());
  994. glObjectLabel (GL_SHADER, vertexShaderID, -1, (shaderName + ".vert").c_str ());
  995. glObjectLabel (GL_SHADER, fragmentShaderID, -1, (shaderName + ".frag").c_str ());
  996. #endif /* DEBUG */
  997. // once linked, the shaders themselves are no longer needed and can be detached/deleted
  998. glDetachShader (program, vertexShaderID);
  999. glDetachShader (program, fragmentShaderID);
  1000. glDeleteShader (vertexShaderID);
  1001. glDeleteShader (fragmentShaderID);
  1002. return program;
  1003. }
  1004. bool CPass::applyFormatCombos (const TextureMap& passTextures, const TextureMap& overrideTextures) {
  1005. const auto& fragment = this->m_shader->getFragment ();
  1006. bool changed = false;
  1007. for (const int slot : fragment.getFormatComboSlots ()) {
  1008. std::shared_ptr<const TextureProvider> texture;
  1009. if (slot == 0) {
  1010. texture = this->m_renderable.getTexture ();
  1011. } else {
  1012. std::optional<std::string> name;
  1013. for (const TextureMap* map : { &overrideTextures, &passTextures, &fragment.getTextures () }) {
  1014. if (const auto it = map->find (slot); it != map->end () && !it->second.empty ()) {
  1015. name = it->second;
  1016. break;
  1017. }
  1018. }
  1019. if (!name.has_value () || name->starts_with ("_rt_") || name->starts_with ("_alias_")) {
  1020. continue;
  1021. }
  1022. try {
  1023. texture
  1024. = this->getContext ().resolveTexture (*name, this->m_renderable.getScene ().getScene ().project);
  1025. } catch (const std::exception&) {
  1026. continue;
  1027. }
  1028. }
  1029. if (texture == nullptr || texture->getFormat () == TextureFormat_UNKNOWN) {
  1030. continue;
  1031. }
  1032. const std::string combo = "TEX" + std::to_string (slot) + "FORMAT";
  1033. const int format = static_cast<int> (texture->getFormat ());
  1034. if (const auto it = this->m_combos.find (combo); it == this->m_combos.end () || it->second != format) {
  1035. this->m_combos.insert_or_assign (combo, format);
  1036. changed = true;
  1037. }
  1038. }
  1039. return changed;
  1040. }
  1041. void CPass::setupAttributes () {
  1042. this->addAttribute ("a_TexCoord", GL_FLOAT, 2, &this->a_TexCoord);
  1043. this->addAttribute ("a_Position", GL_FLOAT, 3, &this->a_Position);
  1044. }
  1045. void CPass::setupTextureUniforms () {
  1046. // Vertex shaders don't carry texture info in practice, but check them first anyway;
  1047. // fragment textures are checked after and override/extend the chain.
  1048. for (const auto& [index, textureName] : this->m_shader->getVertex ().getTextures ()) {
  1049. try {
  1050. auto texture = this->resolveNamedTexture (textureName);
  1051. // create chain entry
  1052. this->m_textures[index] = std::make_shared<TextureChainEntry> (TextureChainEntry {
  1053. .texture = texture,
  1054. .next = nullptr,
  1055. });
  1056. } catch (std::runtime_error& ex) {
  1057. sLog.error (
  1058. "Cannot resolve texture '", textureName, "' (index=", index,
  1059. ", object id=", this->m_renderable.getId (), ") for fragment shader ", ex.what ()
  1060. );
  1061. }
  1062. }
  1063. for (const auto& [index, textureName] : this->m_shader->getFragment ().getTextures ()) {
  1064. try {
  1065. auto texture = this->resolveNamedTexture (textureName);
  1066. const auto it = this->m_textures.find (index);
  1067. const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
  1068. .texture = texture,
  1069. .next = it != this->m_textures.end () ? it->second : nullptr,
  1070. });
  1071. this->m_textures[index] = chain;
  1072. } catch (std::runtime_error& ex) {
  1073. sLog.error (
  1074. "Cannot resolve texture '", textureName, "' (index=", index,
  1075. ", object id=", this->m_renderable.getId (), ") for fragment shader ", ex.what ()
  1076. );
  1077. }
  1078. }
  1079. for (const auto& [index, textureName] : this->m_pass.textures) {
  1080. try {
  1081. auto texture = this->resolveNamedTexture (textureName);
  1082. const auto it = this->m_textures.find (index);
  1083. const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
  1084. .texture = texture,
  1085. .next = it != this->m_textures.end () ? it->second : nullptr,
  1086. });
  1087. this->m_textures[index] = chain;
  1088. if (textureName.find ("_rt_") != 0 && textureName.find ("_alias_") != 0) {
  1089. this->trackPlayback (texture);
  1090. }
  1091. } catch (std::runtime_error& ex) {
  1092. sLog.error (
  1093. "Cannot resolve texture '", textureName, "' (index=", index,
  1094. ", object id=", this->m_renderable.getId (), ") for pass ", ex.what ()
  1095. );
  1096. }
  1097. }
  1098. for (const auto& [index, propertyName] : this->m_pass.usertextures) {
  1099. const auto resolvedName = this->resolveUserTextureName (propertyName);
  1100. if (!resolvedName.has_value ()) {
  1101. // optional user-provided texture slot, nothing configured - keep whatever the
  1102. // regular "textures" entry already set for this index (if any)
  1103. continue;
  1104. }
  1105. const std::string& textureName = *resolvedName;
  1106. try {
  1107. auto texture = this->resolveNamedTexture (textureName);
  1108. const auto it = this->m_textures.find (index);
  1109. const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
  1110. .texture = texture,
  1111. .next = it != this->m_textures.end () ? it->second : nullptr,
  1112. });
  1113. this->m_textures[index] = chain;
  1114. if (textureName.find ("_rt_") != 0 && textureName.find ("_alias_") != 0) {
  1115. this->trackPlayback (texture);
  1116. }
  1117. } catch (std::runtime_error& ex) {
  1118. sLog.error (
  1119. "Cannot resolve user texture '", textureName, "' (index=", index,
  1120. ", object id=", this->m_renderable.getId (), ") for pass ", ex.what ()
  1121. );
  1122. }
  1123. }
  1124. // override any texture
  1125. for (const auto& [index, textureName] : this->m_override.textures) {
  1126. try {
  1127. auto texture = this->resolveNamedTexture (textureName);
  1128. const auto it = this->m_textures.find (index);
  1129. const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
  1130. .texture = texture,
  1131. .next = it != this->m_textures.end () ? it->second : nullptr,
  1132. });
  1133. this->m_textures[index] = chain;
  1134. if (textureName.find ("_rt_") != 0 && textureName.find ("_alias_") != 0) {
  1135. this->trackPlayback (texture);
  1136. }
  1137. } catch (std::runtime_error& ex) {
  1138. sLog.error (
  1139. "Cannot resolve texture '", textureName, "' (index=", index,
  1140. ", object id=", this->m_renderable.getId (), ") for override ", ex.what ()
  1141. );
  1142. }
  1143. }
  1144. for (const auto& [index, propertyName] : this->m_override.usertextures) {
  1145. const auto resolvedName = this->resolveUserTextureName (propertyName);
  1146. if (!resolvedName.has_value ()) {
  1147. continue;
  1148. }
  1149. const std::string& textureName = *resolvedName;
  1150. try {
  1151. auto texture = this->resolveNamedTexture (textureName);
  1152. const auto it = this->m_textures.find (index);
  1153. const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
  1154. .texture = texture,
  1155. .next = it != this->m_textures.end () ? it->second : nullptr,
  1156. });
  1157. this->m_textures[index] = chain;
  1158. if (textureName.find ("_rt_") != 0 && textureName.find ("_alias_") != 0) {
  1159. this->trackPlayback (texture);
  1160. }
  1161. } catch (std::runtime_error& ex) {
  1162. sLog.error (
  1163. "Cannot resolve user texture '", textureName, "' (index=", index,
  1164. ", object id=", this->m_renderable.getId (), ") for override ", ex.what ()
  1165. );
  1166. }
  1167. }
  1168. // binds are set last as they're the most important to be set
  1169. for (const auto& [index, bind] : this->m_binds) {
  1170. const auto texture = bind == "previous" ? nullptr : this->resolveFBO (bind);
  1171. const auto it = this->m_textures.find (index);
  1172. const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
  1173. .texture = texture,
  1174. .next = it != this->m_textures.end () ? it->second : nullptr,
  1175. });
  1176. this->m_textures[index] = chain;
  1177. }
  1178. std::shared_ptr<const TextureProvider> texture = this->resolveTexture (this->m_renderable.getTexture (), 0);
  1179. this->addUniform ("g_Texture0", 0);
  1180. this->addUniform ("g_Texture1", 1);
  1181. this->addUniform ("g_Texture2", 2);
  1182. this->addUniform ("g_Texture3", 3);
  1183. this->addUniform ("g_Texture4", 4);
  1184. this->addUniform ("g_Texture5", 5);
  1185. this->addUniform ("g_Texture6", 6);
  1186. this->addUniform ("g_Texture7", 7);
  1187. this->addUniform ("g_TextureReductionScale", 1.0f);
  1188. this->m_texture0Resolution = *texture->getResolution ();
  1189. this->addUniform ("g_Texture0Resolution", &this->m_texture0Resolution);
  1190. for (const auto& [textureIndex, expectedTexture] : this->m_textures) {
  1191. std::ostringstream namestream;
  1192. namestream << "g_Texture" << textureIndex << "Resolution";
  1193. texture = this->resolveTexture (expectedTexture->texture, textureIndex, texture);
  1194. const glm::vec4* res = texture->getResolution ();
  1195. this->addUniform (namestream.str (), res);
  1196. // the mip count of a mipmapped frame buffer, REFLECTION scales its roughness LOD by it
  1197. if (const auto fbo = std::dynamic_pointer_cast<const CFBO> (texture);
  1198. fbo != nullptr && fbo->getMipLevels () > 1) {
  1199. this->addUniform (
  1200. "g_Texture" + std::to_string (textureIndex) + "MipMapInfo", static_cast<float> (fbo->getMipLevels ())
  1201. );
  1202. }
  1203. }
  1204. this->addUniform ("g_Texture0Resolution", &this->m_texture0Resolution);
  1205. }
  1206. void CPass::setupUniforms () {
  1207. this->setupTextureUniforms ();
  1208. const auto& renderable = this->m_renderable;
  1209. const auto& scene = this->m_renderable.getScene ();
  1210. const auto& sceneData = this->m_renderable.getScene ().getScene ();
  1211. const auto& recorder = this->m_renderable.getScene ().getAudioContext ().getRecorder ();
  1212. // lighting variables
  1213. this->addUniform ("g_LightAmbientColor", sceneData.colors.ambient->value->getVec3 ());
  1214. this->addUniform ("g_LightSkylightColor", sceneData.colors.skylight->value->getVec3 ());
  1215. this->addUniform ("g_LightsPosition", UniformType::Vector3, scene.getLightsPosition (), 4);
  1216. this->addUniform ("g_LightsColorPremultiplied", UniformType::Vector4, scene.getLightsColorPremultiplied (), 3);
  1217. this->addUniform ("g_LightsColorRadius", UniformType::Vector4, scene.getLightsColorRadius (), 4);
  1218. // LightingV1, the arrays are as long as the scene's lightconfig counts
  1219. const auto& lighting = scene.getLightingV1 ();
  1220. const auto addLights = [this] (const char* name, const glm::vec4* values, const int count) {
  1221. if (count > 0) {
  1222. this->addUniform (name, UniformType::Vector4, values, count);
  1223. }
  1224. };
  1225. addLights ("g_LPoint_Color", lighting.pointColor, lighting.points);
  1226. addLights ("g_LPoint_Origin", lighting.pointOrigin, lighting.points);
  1227. addLights ("g_LSpot_Color", lighting.spotColor, lighting.spots);
  1228. addLights ("g_LSpot_Origin", lighting.spotOrigin, lighting.spots);
  1229. addLights ("g_LSpot_Direction", lighting.spotDirection, lighting.spots);
  1230. addLights ("g_LSpot_Exponent", lighting.spotExponent, lighting.spots);
  1231. addLights ("g_LTube_Color", lighting.tubeColor, lighting.tubes);
  1232. addLights ("g_LTube_OriginA", lighting.tubeOriginA, lighting.tubes);
  1233. addLights ("g_LTube_OriginB", lighting.tubeOriginB, lighting.tubes);
  1234. addLights ("g_LDirectional_Color", lighting.directionalColor, lighting.directionals);
  1235. addLights ("g_LDirectional_Direction", lighting.directionalDirection, lighting.directionals);
  1236. if (lighting.spotCookies > 0) {
  1237. this->addUniform (
  1238. "g_LFeature_ShadowProjection", UniformType::Matrix4, lighting.featureProjection, lighting.spotCookies
  1239. );
  1240. addLights ("g_LFeature_ShadowProjectionTransform", lighting.featureProjectionTransform, lighting.spotCookies);
  1241. }
  1242. this->addUniform ("g_FogDistanceColor", &scene.getFog ().distanceColor);
  1243. this->addUniform ("g_FogDistanceParams", &scene.getFog ().distanceParams);
  1244. this->addUniform ("g_FogHeightColor", &scene.getFog ().heightColor);
  1245. this->addUniform (
  1246. "g_FogHeightParams", this->m_fogWorld ? &scene.getFog ().heightParamsWorld : &scene.getFog ().heightParamsLocal
  1247. );
  1248. this->addUniform ("g_AltModelMatrix", &this->m_lightingModelMatrix);
  1249. this->addUniform ("g_AltNormalModelMatrix", &this->m_lightingNormalMatrix);
  1250. this->addUniform ("g_AltViewProjectionMatrix", &this->m_lightingViewProjectionMatrix);
  1251. this->addUniform (
  1252. "g_Screen",
  1253. glm::vec3 (
  1254. scene.getWidth (), scene.getHeight (),
  1255. static_cast<float> (scene.getWidth ()) / static_cast<float> (std::max (scene.getHeight (), 1))
  1256. )
  1257. );
  1258. // register variables like brightness and alpha with the layer's values, unless the pass sets them itself
  1259. const auto addRenderableUniform = [this] (const char* name, const auto& value) {
  1260. if (!this->m_constantUniforms.contains (name)) {
  1261. this->addUniform (name, value);
  1262. }
  1263. };
  1264. addRenderableUniform ("g_Brightness", renderable.getBrightness ());
  1265. addRenderableUniform ("g_UserAlpha", renderable.getUserAlpha ());
  1266. addRenderableUniform ("g_Alpha", renderable.getAlpha ());
  1267. addRenderableUniform ("g_Color", renderable.getColor ());
  1268. addRenderableUniform ("g_Color4", renderable.getColor4 ());
  1269. if (!this->m_uniforms.contains ("g_CompositeColor")) {
  1270. this->addUniform ("g_CompositeColor", renderable.getCompositeColor ());
  1271. }
  1272. // add some external variables
  1273. this->addUniform ("g_Time", &g_Time);
  1274. this->addUniform ("g_Daytime", &g_Daytime);
  1275. // add model-view-projection matrix
  1276. this->addUniform ("g_ModelViewProjectionMatrixInverse", &this->m_modelViewProjectionMatrixInverse);
  1277. this->addUniform ("g_ModelViewProjectionMatrix", &this->m_modelViewProjectionMatrix);
  1278. this->addUniform ("g_EffectModelViewProjectionMatrix", &this->m_effectModelViewProjectionMatrix);
  1279. this->addUniform ("g_ModelMatrix", &this->m_modelMatrix);
  1280. this->addUniform ("g_EffectModelMatrix", &this->m_modelMatrix);
  1281. this->addUniform ("g_LayerModelMatrix", &this->m_layerModelMatrix);
  1282. this->addUniform ("g_NormalModelMatrix", glm::identity<glm::mat3> ());
  1283. this->addUniform ("g_ViewProjectionMatrix", &this->m_viewProjectionMatrix);
  1284. this->addUniform ("g_PointerPosition", scene.getMousePosition ());
  1285. this->addUniform ("g_PointerPositionLast", scene.getMousePositionLast ());
  1286. this->addUniform ("g_ParallaxPosition", scene.getParallaxPosition ());
  1287. this->addUniform ("g_EffectTextureProjectionMatrix", &this->m_effectTextureProjectionMatrix);
  1288. this->addUniform ("g_EffectTextureProjectionMatrixInverse", &this->m_effectTextureProjectionMatrixInverse);
  1289. this->addUniform ("g_TexelSize", glm::vec2 (1.0 / scene.getWidth (), 1.0 / scene.getHeight ()));
  1290. this->addUniform ("g_TexelSizeHalf", glm::vec2 (0.5 / scene.getWidth (), 0.5 / scene.getHeight ()));
  1291. this->addUniform ("g_AudioSpectrum16Left", recorder.audio16, 16);
  1292. this->addUniform ("g_AudioSpectrum16Right", recorder.audio16 + 16, 16);
  1293. this->addUniform ("g_AudioSpectrum32Left", recorder.audio32, 32);
  1294. this->addUniform ("g_AudioSpectrum32Right", recorder.audio32 + 32, 32);
  1295. this->addUniform ("g_AudioSpectrum64Left", recorder.audio64, 64);
  1296. this->addUniform ("g_AudioSpectrum64Right", recorder.audio64 + 64, 64);
  1297. }
  1298. void CPass::addAttribute (const std::string& name, GLint type, GLint elements, const GLuint* value) {
  1299. const GLint id = glGetAttribLocation (this->m_programID, name.c_str ());
  1300. if (id == -1) {
  1301. return;
  1302. }
  1303. this->m_attribs.emplace_back (new AttribEntry (id, name, type, elements, value));
  1304. }
  1305. template <typename T> void CPass::addUniform (const std::string& name, UniformType type, T value) {
  1306. GLint id = glGetUniformLocation (this->m_programID, name.c_str ());
  1307. // parameter not found, can be ignored
  1308. if (id == -1) {
  1309. return;
  1310. }
  1311. // frees any previously registered value for this uniform name
  1312. const auto it = this->m_uniforms.find (name);
  1313. if (it != this->m_uniforms.end ()) {
  1314. delete it->second;
  1315. }
  1316. T* newValue = new T (value);
  1317. this->m_uniforms.insert_or_assign (name, new UniformEntry (id, name, type, newValue, 1, true));
  1318. }
  1319. template <typename T> void CPass::addUniform (const std::string& name, UniformType type, T* value, int count) {
  1320. // this version is used to reference to system variables so things like g_Time works fine
  1321. GLint id = glGetUniformLocation (this->m_programID, name.c_str ());
  1322. // parameter not found, can be ignored
  1323. if (id == -1) {
  1324. return;
  1325. }
  1326. if (const auto it = this->m_uniforms.find (name); it != this->m_uniforms.end ()) {
  1327. delete it->second;
  1328. }
  1329. this->m_uniforms.insert_or_assign (name, new UniformEntry (id, name, type, value, count));
  1330. }
  1331. template <typename T> void CPass::addUniform (const std::string& name, UniformType type, T** value) {
  1332. // this version is used to reference to system variables so things like g_Time works fine
  1333. const GLint id = glGetUniformLocation (this->m_programID, name.c_str ());
  1334. // parameter not found, can be ignored
  1335. if (id == -1) {
  1336. return;
  1337. }
  1338. if (const auto it = this->m_uniforms.find (name); it != this->m_uniforms.end ()) {
  1339. delete it->second;
  1340. }
  1341. this->m_referenceUniforms.insert_or_assign (
  1342. name, new ReferenceUniformEntry (id, name, type, reinterpret_cast<const void**> (value))
  1343. );
  1344. }
  1345. void CPass::setupShaderVariables () {
  1346. for (const auto& cur : this->m_shader->getVertex ().getParameters ()) {
  1347. if (!this->m_uniforms.contains (cur->getName ())) {
  1348. this->addUniform (cur);
  1349. }
  1350. }
  1351. for (const auto& cur : this->m_shader->getFragment ().getParameters ()) {
  1352. if (!this->m_uniforms.contains (cur->getName ())) {
  1353. this->addUniform (cur);
  1354. }
  1355. }
  1356. // apply material pass constants (e.g. constantshadervalues from the material JSON)
  1357. for (const auto& [name, value] : this->m_pass.constants) {
  1358. const auto [vertex, fragment] = this->m_shader->findParameter (name);
  1359. if (vertex == nullptr && fragment == nullptr) {
  1360. continue;
  1361. }
  1362. ShaderVariable* var = vertex == nullptr ? fragment : vertex;
  1363. this->addUniform (var, value->value.get ());
  1364. this->m_constantUniforms.insert (var->getName ());
  1365. }
  1366. // apply override constants (highest priority, overrides both defaults and pass constants)
  1367. for (const auto& [name, value] : this->m_override.constants) {
  1368. const auto [vertex, fragment] = this->m_shader->findParameter (name);
  1369. if (vertex == nullptr && fragment == nullptr) {
  1370. continue;
  1371. }
  1372. ShaderVariable* var = vertex == nullptr ? fragment : vertex;
  1373. this->addUniform (var, value->value.get ());
  1374. this->m_constantUniforms.insert (var->getName ());
  1375. }
  1376. // bind the full-reveal bypass uniform injected by patchXrayFullRevealBypass() (see setupShaders());
  1377. // a no-op if the patch didn't find its anchors, since the uniform then doesn't exist in the shader
  1378. if (this->m_pass.shader == XRAY_EFFECT_SHADER) {
  1379. this->addUniform ("g_XrayFullReveal", &this->m_xrayFullReveal);
  1380. }
  1381. }
  1382. void CPass::addUniform (ShaderVariable* value) {
  1383. // delegates to the (ShaderVariable*, DynamicValue*) overload, which handles the casting
  1384. this->addUniform (value, value);
  1385. }
  1386. void CPass::addUniform (const ShaderVariable* value, const DynamicValue* setting) {
  1387. if (value->is<ShaderVariableFloat> ()) {
  1388. this->addUniform (value->getName (), &setting->getFloat ());
  1389. } else if (value->is<ShaderVariableInteger> ()) {
  1390. this->addUniform (value->getName (), &setting->getInt ());
  1391. } else if (value->is<ShaderVariableVector2> ()) {
  1392. this->addUniform (value->getName (), &setting->getVec2 ());
  1393. } else if (value->is<ShaderVariableVector3> ()) {
  1394. this->addUniform (value->getName (), &setting->getVec3 ());
  1395. } else if (value->is<ShaderVariableVector4> ()) {
  1396. this->addUniform (value->getName (), &setting->getVec4 ());
  1397. } else {
  1398. sLog.error ("Cannot convert setting dynamic value to ", value->getName (), ". Using default value");
  1399. }
  1400. }
  1401. void CPass::addUniform (const std::string& name, int value) { this->addUniform (name, UniformType::Integer, value); }
  1402. void CPass::addUniform (const std::string& name, const int* value, int count) {
  1403. this->addUniform (name, UniformType::Integer, value, count);
  1404. }
  1405. void CPass::addUniform (const std::string& name, const int** value) {
  1406. this->addUniform (name, UniformType::Integer, value);
  1407. }
  1408. void CPass::addUniform (const std::string& name, double value) { this->addUniform (name, UniformType::Double, value); }
  1409. void CPass::addUniform (const std::string& name, const double* value, int count) {
  1410. this->addUniform (name, UniformType::Double, value, count);
  1411. }
  1412. void CPass::addUniform (const std::string& name, const double** value) {
  1413. this->addUniform (name, UniformType::Double, value);
  1414. }
  1415. void CPass::addUniform (const std::string& name, float value) { this->addUniform (name, UniformType::Float, value); }
  1416. void CPass::addUniform (const std::string& name, const float* value, int count) {
  1417. this->addUniform (name, UniformType::Float, value, count);
  1418. }
  1419. void CPass::addUniform (const std::string& name, const float** value) {
  1420. this->addUniform (name, UniformType::Float, value);
  1421. }
  1422. void CPass::addUniform (const std::string& name, glm::vec2 value) {
  1423. this->addUniform (name, UniformType::Vector2, value);
  1424. }
  1425. void CPass::addUniform (const std::string& name, const glm::vec2* value) {
  1426. this->addUniform (name, UniformType::Vector2, value, 1);
  1427. }
  1428. void CPass::addUniform (const std::string& name, const glm::vec2** value) {
  1429. this->addUniform (name, UniformType::Vector2, value, 1);
  1430. }
  1431. void CPass::addUniform (const std::string& name, glm::vec3 value) {
  1432. this->addUniform (name, UniformType::Vector3, value);
  1433. }
  1434. void CPass::addUniform (const std::string& name, const glm::vec3* value) {
  1435. this->addUniform (name, UniformType::Vector3, value, 1);
  1436. }
  1437. void CPass::addUniform (const std::string& name, const glm::vec3** value) {
  1438. this->addUniform (name, UniformType::Vector3, value);
  1439. }
  1440. void CPass::addUniform (const std::string& name, const glm::vec4 value) {
  1441. this->addUniform (name, UniformType::Vector4, value);
  1442. }
  1443. GLenum CPass::getDeclaredUniformType (const std::string& name) const {
  1444. GLint count = 0;
  1445. glGetProgramiv (this->m_programID, GL_ACTIVE_UNIFORMS, &count);
  1446. for (GLint index = 0; index < count; index++) {
  1447. char buffer[256];
  1448. GLsizei length = 0;
  1449. GLint size = 0;
  1450. GLenum type = GL_NONE;
  1451. glGetActiveUniform (this->m_programID, index, sizeof (buffer), &length, &size, &type, buffer);
  1452. if (name == buffer) {
  1453. return type;
  1454. }
  1455. }
  1456. return GL_NONE;
  1457. }
  1458. void CPass::addUniform (const std::string& name, const glm::vec4* value) {
  1459. // resolution uniforms are always kept as a vec4 (texture size + real size), but shaders often declare
  1460. // them as a vec2 and glUniform4 on that is a GL error that leaves the uniform at 0 (color_key_plus
  1461. // then divides by a zero resolution and the whole layer comes out NaN)
  1462. switch (this->getDeclaredUniformType (name)) {
  1463. case GL_FLOAT_VEC2:
  1464. this->addUniform (name, UniformType::Vector2, reinterpret_cast<const glm::vec2*> (value), 1);
  1465. return;
  1466. case GL_FLOAT_VEC3:
  1467. this->addUniform (name, UniformType::Vector3, reinterpret_cast<const glm::vec3*> (value), 1);
  1468. return;
  1469. default:
  1470. break;
  1471. }
  1472. this->addUniform (name, UniformType::Vector4, value, 1);
  1473. }
  1474. void CPass::addUniform (const std::string& name, const glm::vec4** value) {
  1475. this->addUniform (name, UniformType::Vector4, value);
  1476. }
  1477. void CPass::addUniform (const std::string& name, const glm::mat3& value) {
  1478. this->addUniform (name, UniformType::Matrix3, value);
  1479. }
  1480. void CPass::addUniform (const std::string& name, const glm::mat3* value) {
  1481. this->addUniform (name, UniformType::Matrix3, value, 1);
  1482. }
  1483. void CPass::addUniform (const std::string& name, const glm::mat3** value) {
  1484. this->addUniform (name, UniformType::Matrix3, value);
  1485. }
  1486. void CPass::addUniform (const std::string& name, const glm::mat4 value) {
  1487. this->addUniform (name, UniformType::Matrix4, value);
  1488. }
  1489. void CPass::addUniform (const std::string& name, const glm::mat4* value) {
  1490. this->addUniform (name, UniformType::Matrix4, value, 1);
  1491. }
  1492. void CPass::addUniform (const std::string& name, const glm::mat4** value) {
  1493. this->addUniform (name, UniformType::Matrix4, value);
  1494. }