| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816 |
- #include "CPass.h"
- #include <algorithm>
- #include <cctype>
- #include <cmath>
- #include <sstream>
- #include <type_traits>
- #include <utility>
- #include "WallpaperEngine/Desktop/UserShortcut.h"
- #include "WallpaperEngine/Render/Helpers/ContextAware.h"
- #include "WallpaperEngine/Data/Model/Effect.h"
- #include "WallpaperEngine/Data/Model/Material.h"
- #include "WallpaperEngine/Data/Model/Project.h"
- #include "WallpaperEngine/Data/Model/Property.h"
- #include "WallpaperEngine/Render/Wallpapers/CScene.h"
- #include "WallpaperEngine/Render/CFBO.h"
- #include "WallpaperEngine/Render/Objects/CImage.h"
- #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariable.h"
- #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableFloat.h"
- #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableInteger.h"
- #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableVector2.h"
- #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableVector3.h"
- #include "WallpaperEngine/Render/Shaders/Variables/ShaderVariableVector4.h"
- #include "WallpaperEngine/Logging/Log.h"
- using namespace WallpaperEngine;
- using namespace WallpaperEngine::Render;
- using namespace WallpaperEngine::Render::Objects;
- using namespace WallpaperEngine::Render::Shaders::Variables;
- using namespace WallpaperEngine::Render::Objects::Effects;
- extern float g_Time;
- extern float g_Daytime;
- CPass::UniformEntry::~UniformEntry () {
- if (!this->owned) {
- return;
- }
- switch (this->type) {
- case Double:
- delete static_cast<const double*> (this->value);
- break;
- case Float:
- delete static_cast<const float*> (this->value);
- break;
- case Integer:
- delete static_cast<const int*> (this->value);
- break;
- case Vector4:
- delete static_cast<const glm::vec4*> (this->value);
- break;
- case Vector3:
- delete static_cast<const glm::vec3*> (this->value);
- break;
- case Vector2:
- delete static_cast<const glm::vec2*> (this->value);
- break;
- case Matrix4:
- delete static_cast<const glm::mat4*> (this->value);
- break;
- case Matrix3:
- delete static_cast<const glm::mat3*> (this->value);
- break;
- }
- }
- const TextureMap DEFAULT_BINDS = {};
- const ImageEffectPassOverride DEFAULT_OVERRIDE = {};
- // objects that don't provide their own layer-to-screen mapping (text, particles) keep the old identity
- const glm::mat4 IDENTITY_MATRIX = glm::mat4 (1.0);
- const glm::mat3 IDENTITY_MATRIX3 = glm::mat3 (1.0);
- namespace {
- std::string textureSizeLabel (const std::shared_ptr<const TextureProvider>& texture) {
- if (texture == nullptr) {
- return "<null>";
- }
- return std::to_string (texture->getRealWidth ()) + "x" + std::to_string (texture->getRealHeight ());
- }
- // shader used by Wallpaper Engine's built-in "X-Ray" interactive effect (effects/xray/effect.json)
- const std::string XRAY_EFFECT_SHADER = "effects/xray";
- // The xray fragment shader gates its reveal on a projector-style sample of a small halo sprite
- // (g_Texture2, e.g. "particle/halo_6") taken around the pointer; g_PointerScale only controls how
- // far that sample zooms into the sprite, not any on-screen radius, so there's no uniform value that
- // makes it cover the whole screen. Instead this patches the compiled fragment source to add a
- // g_XrayFullReveal uniform that bypasses the halo sample entirely, forcing the hidden layer
- // (g_Texture1) to blend in everywhere once toggled - see patchXrayFullRevealBypass() below.
- const std::string XRAY_MULTIPLY_UNIFORM = "uniform float g_Multiply;";
- const std::string XRAY_FULL_REVEAL_UNIFORM_DECL = "uniform float g_Multiply;\nuniform float g_XrayFullReveal;";
- const std::string XRAY_BLEND_LINE = "blend *= (blendSample.x * blendSample.y);";
- const std::string XRAY_BLEND_LINE_PATCHED = "blend *= mix (blendSample.x * blendSample.y, 1.0, g_XrayFullReveal);";
- // Returns false (leaving fragmentSource untouched) if the anchors weren't found, e.g. because a
- // different spirv-cross/glslang version formats the compiled output differently - callers should
- // treat that as "full xray toggle becomes a no-op" rather than fail the whole shader compile.
- bool patchXrayFullRevealBypass (std::string& fragmentSource) {
- const auto blendPos = fragmentSource.find (XRAY_BLEND_LINE);
- const auto uniformPos = fragmentSource.find (XRAY_MULTIPLY_UNIFORM);
- if (blendPos == std::string::npos || uniformPos == std::string::npos) {
- return false;
- }
- // replace the later occurrence first so the earlier one's position stays valid
- fragmentSource.replace (blendPos, XRAY_BLEND_LINE.size (), XRAY_BLEND_LINE_PATCHED);
- fragmentSource.replace (uniformPos, XRAY_MULTIPLY_UNIFORM.size (), XRAY_FULL_REVEAL_UNIFORM_DECL);
- return true;
- }
- }
- CPass::CPass (
- CRenderable& renderable, std::shared_ptr<const FBOProvider> fboProvider, const MaterialPass& pass,
- std::optional<std::reference_wrapper<const ImageEffectPassOverride>> override,
- std::optional<std::reference_wrapper<const TextureMap>> binds,
- std::optional<std::reference_wrapper<std::string>> target
- ) :
- Helpers::ContextAware (renderable), m_renderable (renderable), m_fboProvider (std::move (fboProvider)),
- m_pass (pass), m_binds (binds.has_value () ? binds.value ().get () : DEFAULT_BINDS),
- m_override (override.has_value () ? override.value ().get () : DEFAULT_OVERRIDE), m_target (target),
- m_blendingmode (pass.blending), m_vao (GL_NONE) {
- this->m_effectTextureProjectionMatrix = &IDENTITY_MATRIX;
- this->m_effectTextureProjectionMatrixInverse = &IDENTITY_MATRIX;
- this->m_lightingModelMatrix = &IDENTITY_MATRIX;
- this->m_lightingNormalMatrix = &IDENTITY_MATRIX3;
- this->m_lightingViewProjectionMatrix = &IDENTITY_MATRIX;
- this->setupShaders ();
- glGenVertexArrays (1, &m_vao);
- }
- CPass::~CPass () {
- for (const auto& texture : this->m_playbackTextures) {
- texture->decrementUsageCount ();
- }
- for (const auto& value : this->m_uniforms | std::views::values) {
- delete value;
- }
- for (const auto& value : this->m_referenceUniforms | std::views::values) {
- delete value;
- }
- for (const auto* attrib : this->m_attribs) {
- delete attrib;
- }
- // text layers rebuild their passes on every glyph texture resize, so this can't be left to leak
- this->m_shader = nullptr;
- this->m_compiled = nullptr;
- glDeleteVertexArrays (1, &m_vao);
- this->m_vao = GL_NONE;
- if (this->releaseSharedProgram ()) {
- return;
- }
- if (!glIsProgram (this->m_programID)) {
- return; // program already invalid or deleted
- }
- GLint shaderCount = 0;
- glGetProgramiv (this->m_programID, GL_ATTACHED_SHADERS, &shaderCount);
- if (shaderCount > 0) {
- std::vector<GLuint> attachedShaders (shaderCount);
- glGetAttachedShaders (this->m_programID, shaderCount, nullptr, attachedShaders.data ());
- for (GLuint s : attachedShaders) {
- if (glIsShader (s)) {
- glDeleteShader (s);
- }
- }
- }
- glDeleteProgram (this->m_programID);
- this->m_programID = 0;
- }
- std::shared_ptr<const TextureProvider> CPass::resolveTexture (
- std::shared_ptr<const TextureProvider> expected, int index, std::shared_ptr<const TextureProvider> previous
- ) {
- if (expected == nullptr) {
- if (const auto it = this->m_fbos.find (index); it != this->m_fbos.end ()) {
- expected = it->second;
- }
- }
- const auto it = this->m_binds.find (index);
- if (it == this->m_binds.end ()) {
- return expected;
- }
- // a bind named "previous" is just another way of telling it to use whatever texture there was already
- if (it->second == "previous") {
- return this->m_previousInput ?: (previous ?: expected);
- }
- return this->resolveFBO (it->second);
- }
- void CPass::trackPlayback (const std::shared_ptr<const TextureProvider>& texture) {
- // the renderable already counts its own texture, and frame buffers have nothing to play back
- if (texture == nullptr || texture == this->m_renderable.getTexture ()
- || std::ranges::find (this->m_playbackTextures, texture) != this->m_playbackTextures.end ()) {
- return;
- }
- texture->incrementUsageCount ();
- this->m_playbackTextures.push_back (texture);
- }
- void CPass::updatePlaybackTextures () const {
- for (const auto& texture : this->m_playbackTextures) {
- texture->update ();
- }
- }
- std::optional<std::string> CPass::resolveUserTextureName (const std::string& propertyName) const {
- const auto& properties = this->m_renderable.getScene ().getScene ().project.properties;
- const auto it = properties.find (propertyName);
- if (it == properties.end ()) {
- // not actually a property reference, treat it as a literal texture name like before
- return propertyName;
- }
- // an assigned shortcut shows its icon, TextureCache loads it from outside the wallpaper
- if (it->second->is<PropertyUserShortcut> ()) {
- const auto shortcut = Desktop::UserShortcut::parse (it->second->getString ());
- const auto icon = shortcut.has_value () ? shortcut->iconPath () : std::nullopt;
- return icon.has_value () ? std::optional<std::string> ("$usershortcut:" + icon->string ()) : std::nullopt;
- }
- const std::string& value = it->second->getString ();
- if (value.empty ()) {
- // the "scenetexture" property exists but the user hasn't imported an image for it -
- // this is the normal state for most wallpapers that expose this as an optional slot
- return std::nullopt;
- }
- return value;
- }
- std::shared_ptr<const TextureProvider> CPass::resolveNamedTexture (const std::string& name) const {
- // lit materials sample every cookie spot through one alias, the scene's light cookie (sub_140190C80)
- if (name == "_alias_lightCookie") {
- return this->m_renderable.getScene ().getLightCookie ();
- }
- if (name.starts_with ("_rt_") || name.starts_with ("_alias_")) {
- return this->resolveFBO (name);
- }
- return this->getContext ().resolveTexture (name, this->m_renderable.getScene ().getScene ().project);
- }
- std::shared_ptr<const CFBO> CPass::resolveFBO (const std::string& name) const {
- std::shared_ptr<const CFBO> fbo = this->m_fboProvider->find (name);
- if (fbo == nullptr && name == "_rt_MipMappedFrameBuffer") {
- fbo = this->m_renderable.getScene ().requireMipMappedFrameBuffer ();
- }
- if (fbo == nullptr) {
- sLog.exception ("Tried to resolve and FBO without any luck: ", name);
- }
- return fbo;
- }
- void CPass::setupRenderFramebuffer () const {
- // what would go onto the scene goes into the passthrough layer drawing its children right now
- const auto* layerTarget = this->m_drawTo == this->m_renderable.getScene ().getFBO ()
- ? this->m_renderable.getScene ().getLayerTarget ()
- : nullptr;
- const auto& target = layerTarget != nullptr ? layerTarget->fbo : this->m_drawTo;
- glBindFramebuffer (GL_FRAMEBUFFER, target->getFramebuffer ());
- // Private per-object FBOs are never cleared elsewhere, so a blending pass would otherwise
- // accumulate stale alpha across frames. The shared scene FBO must not be cleared here though,
- // since it accumulates every object drawn this frame.
- if (this->m_drawTo != this->m_renderable.getScene ().getFBO () && !this->m_keepDestination) {
- GLfloat previousClearColor[4] = {};
- glGetFloatv (GL_COLOR_CLEAR_VALUE, previousClearColor);
- if (this->m_clearColor != nullptr) {
- glClearColor (this->m_clearColor->r, this->m_clearColor->g, this->m_clearColor->b, this->m_clearColor->a);
- } else {
- glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
- }
- glClear (GL_COLOR_BUFFER_BIT);
- glClearColor (previousClearColor[0], previousClearColor[1], previousClearColor[2], previousClearColor[3]);
- }
- glViewport (0, 0, target->getRealWidth (), target->getRealHeight ());
- // the alpha source factor must be GL_ONE, GL_SRC_ALPHA squares every blended pass's alpha and compounds through
- // chained effects
- switch (this->getBlendingMode ()) {
- case BlendingMode_Translucent:
- glEnable (GL_BLEND);
- glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- break;
- case BlendingMode_Additive:
- glEnable (GL_BLEND);
- glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE);
- break;
- // WE's blend state builder (sub_140099F60) turns blending off for normal on every target, the scene's
- // alpha is masked off anyway, so the pass's RGB replaces what's below regardless of its alpha
- case BlendingMode_Normal:
- default:
- glDisable (GL_BLEND);
- break;
- }
- // sub_140099F60 with renderer state flag 0x10: alpha op MAX and all four channels written, the color blend stays
- if (layerTarget != nullptr && layerTarget->alphaMax) {
- glBlendEquationSeparate (GL_FUNC_ADD, GL_MAX);
- glColorMask (true, true, true, true);
- }
- switch (this->m_pass.depthtest) {
- case DepthtestMode_Enabled:
- glEnable (GL_DEPTH_TEST);
- glDepthFunc (GL_LEQUAL);
- break;
- case DepthtestMode_Disabled:
- default:
- glDisable (GL_DEPTH_TEST);
- break;
- }
- switch (this->m_pass.cullmode) {
- case CullingMode_Normal:
- glEnable (GL_CULL_FACE);
- break;
- case CullingMode_Disable:
- default:
- glDisable (GL_CULL_FACE);
- break;
- }
- switch (this->m_pass.depthwrite) {
- case DepthwriteMode_Enabled:
- glDepthMask (true);
- break;
- case DepthwriteMode_Disabled:
- default:
- glDepthMask (false);
- break;
- }
- }
- void CPass::setupRenderTexture () {
- glUseProgram (this->m_programID);
- auto texture0 = this->resolveTexture0 ();
- const auto animation = this->resolveTextureAnimationState (texture0);
- this->bindTextureUnit (0, texture0, animation.currentTexture);
- this->bindTextureOverrides (animation.currentTexture, texture0);
- if (texture0 != nullptr) {
- this->m_texture0Resolution = *texture0->getResolution ();
- }
- // used in animations when one of the frames is vertical instead of horizontal
- // rotation with translation = origin and end of the image to display
- if (this->g_Texture0Rotation != -1) {
- glUniform4f (
- this->g_Texture0Rotation, animation.rotation.x, animation.rotation.y, animation.rotation.z,
- animation.rotation.w
- );
- }
- // this actually picks the origin point of the image from the atlast
- if (this->g_Texture0Translation != -1) {
- glUniform2f (this->g_Texture0Translation, animation.translation.x, animation.translation.y);
- }
- }
- std::shared_ptr<const TextureProvider> CPass::resolveTexture0 () {
- auto texture0 = this->resolveTexture (this->m_input, 0, this->m_input);
- const auto it = this->m_textures.find (0);
- if (it == this->m_textures.end ()) {
- return texture0;
- }
- auto& chain = it->second;
- do {
- texture0 = chain->texture;
- if (texture0 == nullptr) {
- if (this->m_previousInput != nullptr && this->m_previousInput->isReady ()) {
- return this->m_previousInput;
- }
- if (this->m_input != nullptr && this->m_input->isReady ()) {
- return this->m_input;
- }
- } else if (texture0->isReady ()) {
- return texture0;
- }
- chain = chain->next;
- } while (chain != nullptr);
- // got to the end of the chain, use previous input or current input if available
- if (this->m_previousInput != nullptr && this->m_previousInput->isReady ()) {
- return this->m_previousInput;
- }
- // last resort, doesn't matter if the input is ready or not
- return this->m_input;
- }
- CPass::TextureAnimationState
- CPass::resolveTextureAnimationState (const std::shared_ptr<const TextureProvider>& texture) const {
- TextureAnimationState state;
- if (texture == nullptr || !texture->isAnimated ()) {
- return state;
- }
- // scene time like every other animation, so --speed, --disable-animations and pausing apply
- double currentRenderTime = fmod (static_cast<double> (g_Time), this->m_renderable.getAnimationTime ());
- for (const auto& frameCur : texture->getFrames ()) {
- currentRenderTime -= frameCur->frametime;
- if (currentRenderTime > 0.0f) {
- continue;
- }
- state.currentTexture = frameCur->frameNumber;
- state.translation.x = frameCur->x / texture->getTextureWidth (state.currentTexture);
- state.translation.y = frameCur->y / texture->getTextureHeight (state.currentTexture);
- state.rotation.x = frameCur->width1 / static_cast<float> (texture->getTextureWidth (state.currentTexture));
- state.rotation.y = frameCur->width2 / static_cast<float> (texture->getTextureWidth (state.currentTexture));
- state.rotation.z = frameCur->height2 / static_cast<float> (texture->getTextureHeight (state.currentTexture));
- state.rotation.w = frameCur->height1 / static_cast<float> (texture->getTextureHeight (state.currentTexture));
- break;
- }
- return state;
- }
- void CPass::bindTextureUnit (int index, const std::shared_ptr<const TextureProvider>& texture, uint32_t frame) const {
- if (texture == nullptr) {
- return;
- }
- glActiveTexture (GL_TEXTURE0 + index);
- glBindTexture (GL_TEXTURE_2D, texture->getTextureID (frame));
- }
- void CPass::bindTextureOverrides (uint32_t currentTexture, std::shared_ptr<const TextureProvider>& texture0) const {
- for (auto [index, chain] : this->m_textures) {
- auto expectedTexture = chain->texture;
- do {
- if (expectedTexture == nullptr) {
- if (this->m_previousInput != nullptr && this->m_previousInput->isReady ()) {
- expectedTexture = this->m_previousInput;
- break;
- }
- if (this->m_input != nullptr && this->m_input->isReady ()) {
- expectedTexture = this->m_input;
- break;
- }
- } else if (expectedTexture->isReady ()) {
- break;
- }
- chain = chain->next;
- expectedTexture = chain == nullptr ? nullptr : chain->texture;
- } while (chain != nullptr);
- if (expectedTexture == nullptr && this->m_previousInput != nullptr && this->m_previousInput->isReady ()) {
- expectedTexture = this->m_previousInput;
- }
- if (expectedTexture == nullptr) {
- expectedTexture = this->m_input;
- }
- this->bindTextureUnit (index, expectedTexture, index == 0 ? currentTexture : 0);
- if (index == 0) {
- texture0 = expectedTexture;
- }
- }
- }
- void CPass::setupRenderReferenceUniforms () {
- for (const auto& value : this->m_referenceUniforms | std::views::values) {
- switch (value->type) {
- case Double:
- glUniform1d (value->id, *static_cast<const double*> (*value->value));
- break;
- case Float:
- glUniform1f (value->id, *static_cast<const float*> (*value->value));
- break;
- case Integer:
- glUniform1i (value->id, *static_cast<const int*> (*value->value));
- break;
- case Vector4:
- glUniform4fv (value->id, 1, glm::value_ptr (*static_cast<const glm::vec4*> (*value->value)));
- break;
- case Vector3:
- glUniform3fv (value->id, 1, glm::value_ptr (*static_cast<const glm::vec3*> (*value->value)));
- break;
- case Vector2:
- glUniform2fv (value->id, 1, glm::value_ptr (*static_cast<const glm::vec2*> (*value->value)));
- break;
- case Matrix4:
- glUniformMatrix4fv (
- value->id, 1, GL_FALSE, glm::value_ptr (*static_cast<const glm::mat4*> (*value->value))
- );
- break;
- case Matrix3:
- glUniformMatrix3fv (
- value->id, 1, GL_FALSE, glm::value_ptr (*static_cast<const glm::mat3*> (*value->value))
- );
- break;
- }
- }
- }
- void CPass::setupRenderUniforms () {
- for (const auto& value : this->m_uniforms | std::views::values) {
- switch (value->type) {
- case Double:
- glUniform1dv (value->id, value->count, static_cast<const double*> (value->value));
- break;
- case Float:
- glUniform1fv (value->id, value->count, static_cast<const float*> (value->value));
- break;
- case Integer:
- glUniform1iv (value->id, value->count, static_cast<const int*> (value->value));
- break;
- case Vector4:
- glUniform4fv (value->id, value->count, glm::value_ptr (*static_cast<const glm::vec4*> (value->value)));
- break;
- case Vector3:
- glUniform3fv (value->id, value->count, glm::value_ptr (*static_cast<const glm::vec3*> (value->value)));
- break;
- case Vector2:
- glUniform2fv (value->id, value->count, glm::value_ptr (*static_cast<const glm::vec2*> (value->value)));
- break;
- case Matrix4:
- glUniformMatrix4fv (
- value->id, value->count, GL_FALSE, glm::value_ptr (*static_cast<const glm::mat4*> (value->value))
- );
- break;
- case Matrix3:
- glUniformMatrix3fv (
- value->id, value->count, GL_FALSE, glm::value_ptr (*static_cast<const glm::mat3*> (value->value))
- );
- break;
- }
- }
- }
- void CPass::setupRenderAttributes () const {
- if (this->m_setupAttribsCallback) {
- this->m_setupAttribsCallback ();
- return;
- }
- for (const auto& cur : this->m_attribs) {
- glEnableVertexAttribArray (cur->id);
- glBindBuffer (GL_ARRAY_BUFFER, *cur->value);
- glVertexAttribPointer (cur->id, cur->elements, cur->type, GL_FALSE, 0, nullptr);
- #if !NDEBUG
- glObjectLabel (
- GL_BUFFER, *cur->value, -1,
- ("Image " + std::to_string (this->m_renderable.getId ()) + " Pass " + this->m_pass.shader + " " + cur->name)
- .c_str ()
- );
- #endif /* DEBUG */
- }
- }
- void CPass::renderGeometry () const {
- if (this->m_drawGeometryCallback) {
- this->m_drawGeometryCallback ();
- return;
- }
- glBindBuffer (GL_ARRAY_BUFFER, this->a_Position);
- glDrawArrays (GL_TRIANGLES, 0, 6);
- }
- void CPass::cleanupRenderSetup () {
- if (this->m_cleanupAttribsCallback) {
- this->m_cleanupAttribsCallback ();
- } else {
- // disable vertex attribs array
- for (const auto& cur : this->m_attribs) {
- glDisableVertexAttribArray (cur->id);
- }
- }
- glActiveTexture (GL_TEXTURE0);
- glBindTexture (GL_TEXTURE_2D, 0);
- for (const auto& index : this->m_textures | std::views::keys) {
- glActiveTexture (GL_TEXTURE0 + index);
- glBindTexture (GL_TEXTURE_2D, 0);
- }
- }
- void CPass::refreshRenderableUniforms () {
- const auto update = [this] (const char* name, const auto& value) {
- const auto it = this->m_uniforms.find (name);
- if (it != this->m_uniforms.end () && it->second->owned && !this->m_constantUniforms.contains (name)) {
- using Value = std::remove_cvref_t<decltype (value)>;
- *static_cast<Value*> (const_cast<void*> (it->second->value)) = value;
- }
- };
- update ("g_UserAlpha", this->m_renderable.getUserAlpha ());
- update ("g_Alpha", this->m_renderable.getAlpha ());
- update ("g_Color", this->m_renderable.getColor ());
- update ("g_Color4", this->m_neutralColor ? glm::vec4 (1.0f) : this->m_renderable.getColor4 ());
- }
- void CPass::render () {
- glBindVertexArray (this->m_vao);
- // copied when the pass was built, color and alpha scripts or animations change them every frame
- this->refreshRenderableUniforms ();
- if (this->m_pass.shader == XRAY_EFFECT_SHADER) {
- const bool fullReveal = this->getContext ().getApp ().getContext ().state.xray.fullReveal;
- this->m_xrayFullReveal = fullReveal ? 1.0f : 0.0f;
- }
- const auto& debug = this->getContext ().getApp ().getContext ().settings.render.debug;
- if (debug.passLog) {
- sLog.out (
- "Render pass object=", this->m_renderable.getId (), " shader=", this->m_pass.shader,
- " target=", this->m_target.has_value () ? this->m_target.value ().get () : std::string ("<screen/local>"),
- " drawTo=", this->m_drawTo ? this->m_drawTo->getName () : std::string ("<null>"),
- " drawSize=", textureSizeLabel (this->m_drawTo), " inputSize=", textureSizeLabel (this->m_input)
- );
- for (const auto* uniformName : { "g_TintColor", "g_CompositeColor", "g_BlendAlpha", "g_CompositeAlpha" }) {
- const auto uniform = this->m_uniforms.find (uniformName);
- if (uniform == this->m_uniforms.end ()) {
- continue;
- }
- switch (uniform->second->type) {
- case Vector3:
- {
- const auto* v = static_cast<const glm::vec3*> (uniform->second->value);
- sLog.out (" uniform ", uniformName, "=", v->x, " ", v->y, " ", v->z);
- break;
- }
- case Float:
- {
- const auto* v = static_cast<const float*> (uniform->second->value);
- sLog.out (" uniform ", uniformName, "=", *v);
- break;
- }
- default:
- break;
- }
- }
- }
- if (this->m_drawTo == nullptr) {
- sLog.error ("Skipping render pass for object ", this->m_renderable.getId (), ": no destination FBO set");
- return;
- }
- if (this->m_input == nullptr) {
- sLog.error ("Skipping render pass for object ", this->m_renderable.getId (), ": no input texture set");
- return;
- }
- // a passthrough layer draws its children into its buffer: model = inverse (layer world), view = identity and an
- // ortho of the layer's size (sub_1401ECB20), which comes down to remapping the scene's clip space
- const auto* layerTarget = this->m_drawTo == this->m_renderable.getScene ().getFBO () || this->m_followLayerTarget
- ? this->m_renderable.getScene ().getLayerTarget ()
- : nullptr;
- const glm::mat4* modelViewProjection = this->m_modelViewProjectionMatrix;
- const glm::mat4* modelViewProjectionInverse = this->m_modelViewProjectionMatrixInverse;
- const glm::mat4* viewProjection = this->m_viewProjectionMatrix;
- if (layerTarget != nullptr) {
- if (modelViewProjection != nullptr) {
- this->m_layerModelViewProjection = layerTarget->transform * *modelViewProjection;
- this->m_layerModelViewProjectionInverse = glm::inverse (this->m_layerModelViewProjection);
- this->m_modelViewProjectionMatrix = &this->m_layerModelViewProjection;
- this->m_modelViewProjectionMatrixInverse = &this->m_layerModelViewProjectionInverse;
- }
- if (viewProjection != nullptr) {
- this->m_layerViewProjection = layerTarget->transform * *viewProjection;
- this->m_viewProjectionMatrix = &this->m_layerViewProjection;
- }
- }
- // effectcomposebackground (refraction, ...) samples _rt_FullFrameBuffer where the layer is on screen
- this->m_effectModelViewProjectionMatrix = this->m_effectModelViewProjectionOverride != nullptr
- ? this->m_effectModelViewProjectionOverride
- : this->m_modelViewProjectionMatrix;
- this->setupRenderFramebuffer ();
- this->setupRenderTexture ();
- this->setupRenderUniforms ();
- this->setupRenderReferenceUniforms ();
- this->setupRenderAttributes ();
- this->renderGeometry ();
- this->cleanupRenderSetup ();
- if (layerTarget != nullptr) {
- this->m_modelViewProjectionMatrix = modelViewProjection;
- this->m_modelViewProjectionMatrixInverse = modelViewProjectionInverse;
- this->m_viewProjectionMatrix = viewProjection;
- if (layerTarget->alphaMax && this->m_drawTo == this->m_renderable.getScene ().getFBO ()) {
- glBlendEquation (GL_FUNC_ADD);
- }
- }
- }
- void CPass::clearDestination () const {
- if (this->m_drawTo == nullptr) {
- return;
- }
- GLfloat previousClearColor[4] = {};
- glGetFloatv (GL_COLOR_CLEAR_VALUE, previousClearColor);
- glBindFramebuffer (GL_FRAMEBUFFER, this->m_drawTo->getFramebuffer ());
- glViewport (0, 0, this->m_drawTo->getRealWidth (), this->m_drawTo->getRealHeight ());
- glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
- glClear (GL_COLOR_BUFFER_BIT);
- glClearColor (previousClearColor[0], previousClearColor[1], previousClearColor[2], previousClearColor[3]);
- }
- std::shared_ptr<const FBOProvider> CPass::getFBOProvider () const { return this->m_fboProvider; }
- const CRenderable& CPass::getRenderable () const { return this->m_renderable; }
- void CPass::setDestination (std::shared_ptr<const CFBO> drawTo) { this->m_drawTo = std::move (drawTo); }
- void CPass::setInput (std::shared_ptr<const TextureProvider> input) { this->m_input = std::move (input); }
- void CPass::setPreviousInput (std::shared_ptr<const TextureProvider> input) {
- this->m_previousInput = std::move (input);
- }
- void CPass::setEffectModelViewProjectionMatrix (const glm::mat4* projection) {
- this->m_effectModelViewProjectionOverride = projection;
- }
- void CPass::setModelViewProjectionMatrix (const glm::mat4* projection) {
- this->m_modelViewProjectionMatrix = projection;
- }
- void CPass::setModelViewProjectionMatrixInverse (const glm::mat4* projection) {
- this->m_modelViewProjectionMatrixInverse = projection;
- }
- const glm::mat4 CPass::s_identity { 1.0f };
- void CPass::setModelMatrix (const glm::mat4* model) { this->m_modelMatrix = model; }
- void CPass::setLayerModelMatrix (const glm::mat4* model) { this->m_layerModelMatrix = model; }
- void CPass::setFogWorld (const bool world) {
- this->m_fogWorld = world;
- const auto& fog = this->m_renderable.getScene ().getFog ();
- this->addUniform ("g_FogHeightParams", world ? &fog.heightParamsWorld : &fog.heightParamsLocal);
- if (world) {
- this->addUniform ("g_EyePosition", &fog.eyeWorld);
- }
- }
- void CPass::setViewProjectionMatrix (const glm::mat4* viewProjection) { this->m_viewProjectionMatrix = viewProjection; }
- void CPass::setLightingTransform (const glm::mat4* model, const glm::mat3* normal, const glm::mat4* viewProjection) {
- this->m_lightingModelMatrix = model;
- this->m_lightingNormalMatrix = normal;
- this->m_lightingViewProjectionMatrix = viewProjection;
- }
- void CPass::setEffectTextureProjectionMatrix (const glm::mat4* projection, const glm::mat4* inverse) {
- this->m_effectTextureProjectionMatrix = projection;
- this->m_effectTextureProjectionMatrixInverse = inverse;
- }
- void CPass::setBlendingMode (BlendingMode blendingmode) { this->m_blendingmode = blendingmode; }
- BlendingMode CPass::getBlendingMode () const { return this->m_blendingmode; }
- void CPass::setTexCoord (GLuint texcoord) { this->a_TexCoord = texcoord; }
- void CPass::setClearColor (const glm::vec4* color) { this->m_clearColor = color; }
- void CPass::setKeepDestination (bool keep) { this->m_keepDestination = keep; }
- void CPass::setFollowLayerTarget (bool follow) { this->m_followLayerTarget = follow; }
- void CPass::setNeutralColor (bool neutral) { this->m_neutralColor = neutral; }
- void CPass::setPosition (GLuint position) { this->a_Position = position; }
- const MaterialPass& CPass::getPass () const { return this->m_pass; }
- std::optional<std::reference_wrapper<std::string>> CPass::getTarget () const { return this->m_target; }
- Render::Shaders::Shader* CPass::getShader () const { return this->m_shader; }
- GLuint CPass::getProgramID () const { return this->m_programID; }
- void CPass::setGeometryCallback (
- GeometryCallback setupAttribs, GeometryCallback drawGeometry, GeometryCallback cleanupAttribs
- ) {
- this->m_setupAttribsCallback = std::move (setupAttribs);
- this->m_drawGeometryCallback = std::move (drawGeometry);
- this->m_cleanupAttribsCallback = std::move (cleanupAttribs);
- }
- GLuint CPass::compileShader (const char* shader, GLuint type) {
- const GLuint shaderID = glCreateShader (type);
- // Mesa mis-reads a vec3 uniform followed by a float used as vec4(vec3, float), use the g_Color4 the engine already
- // exposes
- std::string patched;
- if (type == GL_FRAGMENT_SHADER) {
- patched = shader;
- const std::string declarations = "uniform vec3 g_Color;\nuniform float g_Alpha;";
- const std::string construction = "vec4(g_Color, g_Alpha)";
- const auto declarationsAt = patched.find (declarations);
- const auto constructionAt = patched.find (construction);
- if (declarationsAt != std::string::npos && constructionAt != std::string::npos) {
- patched.replace (constructionAt, construction.size (), "g_Color4");
- patched.replace (declarationsAt, declarations.size (), "uniform vec4 g_Color4;");
- // only safe when that was the sole use of both
- // the generated source keeps the original as an "#if 0" block at the end, that doesn't count
- const size_t codeEnd = std::min (patched.size (), patched.find ("#if 0"));
- const auto stillUsed = [&patched, codeEnd] (const std::string& name) {
- size_t pos = 0;
- while ((pos = patched.find (name, pos)) != std::string::npos && pos < codeEnd) {
- const bool wordStart = pos == 0
- || !(std::isalnum (static_cast<unsigned char> (patched[pos - 1])) || patched[pos - 1] == '_');
- const size_t end = pos + name.size ();
- const bool wordEnd = end >= patched.size ()
- || !(std::isalnum (static_cast<unsigned char> (patched[end])) || patched[end] == '_');
- if (wordStart && wordEnd) {
- return true;
- }
- pos = end;
- }
- return false;
- };
- if (stillUsed ("g_Color") || stillUsed ("g_Alpha")) {
- patched = shader;
- }
- }
- shader = patched.c_str ();
- }
- glShaderSource (shaderID, 1, &shader, nullptr);
- glCompileShader (shaderID);
- GLint result = GL_FALSE;
- int infoLogLength = 0;
- glGetShaderiv (shaderID, GL_COMPILE_STATUS, &result);
- glGetShaderiv (shaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
- if (infoLogLength > 0) {
- const auto logBuffer = new char[infoLogLength + 1];
- memset (logBuffer, 0, infoLogLength + 1);
- glGetShaderInfoLog (shaderID, infoLogLength, nullptr, logBuffer);
- std::stringstream buffer;
- buffer << logBuffer << std::endl << "Compiled source code:" << std::endl << shader;
- delete[] logBuffer;
- if (result == GL_FALSE) {
- sLog.exception (buffer.str ());
- } else {
- sLog.error (buffer.str ());
- }
- }
- return shaderID;
- }
- void CPass::setupShaders () {
- const auto texture0 = this->m_renderable.getTexture ();
- this->m_combos.insert (this->m_pass.combos.begin (), this->m_pass.combos.end ());
- const auto comboEnabled = [this] (const std::string& name) {
- const auto override = this->m_override.combos.find (name);
- if (override != this->m_override.combos.end ()) {
- return override->second != 0;
- }
- const auto combo = this->m_combos.find (name);
- return combo != this->m_combos.end () && combo->second != 0;
- };
- if (comboEnabled ("LIGHTING") || comboEnabled ("REFLECTION")) {
- this->m_combos.insert_or_assign ("PRELIGHTING", 1);
- }
- // HDR scene rendering defines HDR for every pass (sub_1401A5C40)
- if (this->m_renderable.getScene ().isHDR ()) {
- this->m_combos.insert_or_assign ("HDR", 1);
- }
- // particle shaders read TEX0FORMAT without a formatcombo sampler, the other slots are handled below
- if (texture0 != nullptr) {
- if (texture0->getFormat () == TextureFormat_RG88) {
- this->m_combos.insert_or_assign ("TEX0FORMAT", 8);
- } else if (texture0->getFormat () == TextureFormat_R8) {
- this->m_combos.insert_or_assign ("TEX0FORMAT", 9);
- }
- }
- // TODO: review the shader textures here; ones passed to the shader shouldn't be in this list
- // (used later to build the textures)
- // use the combos copied from the pass so it includes the texture format
- const std::string& shaderName
- = this->m_override.shaderOverride.has_value () ? this->m_override.shaderOverride.value () : this->m_pass.shader;
- TextureMap passTextures = this->m_pass.textures;
- for (const auto& [index, propertyName] : this->m_pass.usertextures) {
- // leave the default texture (if any) in place when the user hasn't provided an override,
- // same rule applied when the actual texture chain gets built in setupTextureUniforms()
- if (const auto resolved = this->resolveUserTextureName (propertyName); resolved.has_value ()) {
- passTextures.insert_or_assign (index, *resolved);
- }
- }
- // same for the object's own user textures, otherwise the combo that enables their slot stays off
- TextureMap overrideTextures = this->m_override.textures;
- for (const auto& [index, propertyName] : this->m_override.usertextures) {
- if (const auto resolved = this->resolveUserTextureName (propertyName); resolved.has_value ()) {
- overrideTextures.insert_or_assign (index, *resolved);
- }
- }
- // every pass of an orthographic scene gets SCENE_ORTHO (renderer flag 0x400, sub_1401A5C40)
- if (this->m_renderable.getScene ().getCamera ().isOrthogonal ()) {
- this->m_combos.insert_or_assign ("SCENE_ORTHO", 1);
- }
- this->m_compiled = this->compileShaderSources (shaderName, passTextures, overrideTextures);
- this->m_shader = this->m_compiled->shader.get ();
- // samplers marked "formatcombo" get TEX<slot>FORMAT set to their texture's format, like wallpaper64.exe
- // (sub_14015EC30). Which slots those are is only known once the shader is parsed, so rebuild it when one changed
- if (this->applyFormatCombos (passTextures, overrideTextures)) {
- this->m_compiled = this->compileShaderSources (shaderName, passTextures, overrideTextures);
- this->m_shader = this->m_compiled->shader.get ();
- }
- // fog turns into FOG_DIST/FOG_HEIGHT for every pass whose FOG combo (the shader default included) is on
- // (sub_1401A5C40); that default is only known once the shader is parsed
- const auto& scene = this->m_renderable.getScene ();
- const auto comboValue = [this] (const std::string& name) {
- for (const ComboMap* combos :
- std::initializer_list<const ComboMap*> { &this->m_override.combos, &this->m_combos }) {
- if (const auto it = combos->find (name); it != combos->end ()) {
- return it->second;
- }
- }
- for (const auto* unit : { &this->m_shader->getFragment (), &this->m_shader->getVertex () }) {
- if (const auto it = unit->getDiscoveredCombos ().find (name); it != unit->getDiscoveredCombos ().end ()) {
- return it->second;
- }
- }
- return 0;
- };
- // same for LIGHTING: the light counts of the scene's lightconfig, which the LightingV1 module is generated from.
- // Without shadow mapping the shadow counts stay 0, like WE with its shadow setting off
- if (comboValue ("LIGHTING") != 0) {
- const auto& lighting = scene.getLightingV1 ();
- this->m_combos.insert_or_assign ("LIGHTS_POINT", lighting.points);
- this->m_combos.insert_or_assign ("LIGHTS_SPOT", lighting.spots);
- this->m_combos.insert_or_assign ("LIGHTS_TUBE", lighting.tubes);
- this->m_combos.insert_or_assign ("LIGHTS_DIRECTIONAL", lighting.directionals);
- this->m_combos.insert_or_assign ("LIGHTS_SPOT_SHADOW_COOKIE", 0);
- this->m_combos.insert_or_assign ("LIGHTS_SPOT_SHADOW", 0);
- this->m_combos.insert_or_assign ("LIGHTS_SPOT_COOKIE", lighting.spotCookies);
- this->m_combos.insert_or_assign ("LIGHTS_DIRECTIONAL_SHADOW", 0);
- this->m_combos.insert_or_assign ("LIGHTS_POINT_SHADOW", 0);
- if (lighting.spotCookies != 0) {
- this->m_combos.insert_or_assign ("LIGHTS_COOKIE", 1);
- }
- this->m_compiled = this->compileShaderSources (shaderName, passTextures, overrideTextures);
- this->m_shader = this->m_compiled->shader.get ();
- }
- if (scene.hasDistanceFog () || scene.hasHeightFog ()) {
- if (comboValue ("FOG") != 0) {
- if (scene.hasDistanceFog ()) {
- this->m_combos.insert_or_assign ("FOG_DIST", 1);
- }
- if (scene.hasHeightFog ()) {
- this->m_combos.insert_or_assign ("FOG_HEIGHT", 1);
- }
- this->m_compiled = this->compileShaderSources (shaderName, passTextures, overrideTextures);
- this->m_shader = this->m_compiled->shader.get ();
- }
- }
- std::string vertex = this->m_compiled->vertex;
- std::string fragment = this->m_compiled->fragment;
- if (shaderName == XRAY_EFFECT_SHADER) {
- this->m_xrayFullRevealPatched = patchXrayFullRevealBypass (fragment);
- if (!this->m_xrayFullRevealPatched) {
- sLog.error (
- "Full xray toggle unavailable: couldn't find the expected reveal blend line in the "
- "compiled effects/xray shader (spirv-cross output format may have changed)"
- );
- }
- }
- // passes with the same sources share one program (every particle system instance of a child would link its own
- // otherwise, hundreds in the first seconds of a rain wallpaper). Uniforms are uploaded on every draw, the sampler
- // units below are the same for all of them
- this->m_programKey = vertex + '\0' + fragment;
- if (const auto cached = sharedPrograms ().find (this->m_programKey); cached != sharedPrograms ().end ()) {
- this->m_programID = cached->second.program;
- cached->second.users++;
- } else {
- this->m_programID = this->linkProgram (vertex, fragment, shaderName);
- sharedPrograms ().emplace (this->m_programKey, SharedProgram { this->m_programID, 1 });
- }
- // bind each g_TextureN sampler to unit N explicitly, the translated GLSL collapses every layout(binding) to 0
- {
- glUseProgram (this->m_programID);
- for (int index = 0; index <= 9; index++) {
- const std::string name = "g_Texture" + std::to_string (index);
- const GLint loc = glGetUniformLocation (this->m_programID, name.c_str ());
- if (loc != -1) {
- glUniform1i (loc, index);
- }
- }
- }
- // first setup the default values, these will be overwritten by future values
- this->setupShaderVariables ();
- this->setupUniforms ();
- this->setupAttributes ();
- this->g_Texture0Rotation = glGetUniformLocation (this->m_programID, "g_Texture0Rotation");
- this->g_Texture0Translation = glGetUniformLocation (this->m_programID, "g_Texture0Translation");
- }
- std::unordered_map<std::string, std::weak_ptr<CPass::CompiledShader>>& CPass::sharedShaders () {
- static std::unordered_map<std::string, std::weak_ptr<CompiledShader>> shaders;
- return shaders;
- }
- std::shared_ptr<CPass::CompiledShader> CPass::compileShaderSources (
- const std::string& shaderName, const TextureMap& passTextures, const TextureMap& overrideTextures
- ) {
- // parsing and translating a shader takes a few ms, and every instance of a child particle system builds the same
- // one. Override constants can change the parsed defaults, those passes keep a shader of their own
- const bool shareable = this->m_override.constants.empty ();
- std::string key;
- if (shareable) {
- std::ostringstream stream;
- stream << shaderName << '\0' << &this->m_renderable.getAssetLocator ();
- const ComboMap* comboMaps[] = { &this->m_combos, &this->m_override.combos };
- for (const ComboMap* combos : comboMaps) {
- stream << '\1';
- for (const auto& [name, value] : *combos) {
- stream << name << '=' << value << ';';
- }
- }
- for (const TextureMap* textures : { &passTextures, &overrideTextures }) {
- stream << '\1';
- for (const auto& [index, name] : *textures) {
- stream << index << '=' << name << ';';
- }
- }
- key = stream.str ();
- if (const auto it = sharedShaders ().find (key); it != sharedShaders ().end ()) {
- if (auto cached = it->second.lock ()) {
- return cached;
- }
- }
- }
- static const ShaderConstantMap noConstants;
- auto compiled = std::make_shared<CompiledShader> ();
- compiled->combos = this->m_combos;
- compiled->overrideCombos = this->m_override.combos;
- compiled->passTextures = passTextures;
- compiled->overrideTextures = overrideTextures;
- compiled->shader = std::make_unique<Render::Shaders::Shader> (
- this->m_renderable.getAssetLocator (), shaderName, compiled->combos, compiled->overrideCombos,
- compiled->passTextures, compiled->overrideTextures, shareable ? noConstants : this->m_override.constants
- );
- auto [vertex, fragment]
- = Shaders::GLSLContext::get ().toGlsl (compiled->shader->vertex (), compiled->shader->fragment (), shaderName);
- compiled->vertex = std::move (vertex);
- compiled->fragment = std::move (fragment);
- if (shareable) {
- std::erase_if (sharedShaders (), [] (const auto& entry) { return entry.second.expired (); });
- sharedShaders ().insert_or_assign (key, compiled);
- }
- return compiled;
- }
- std::unordered_map<std::string, CPass::SharedProgram>& CPass::sharedPrograms () {
- static std::unordered_map<std::string, SharedProgram> programs;
- return programs;
- }
- bool CPass::releaseSharedProgram () {
- const auto it = sharedPrograms ().find (this->m_programKey);
- if (this->m_programKey.empty () || it == sharedPrograms ().end ()) {
- return false;
- }
- if (--it->second.users == 0) {
- glDeleteProgram (it->second.program);
- sharedPrograms ().erase (it);
- }
- this->m_programID = 0;
- return true;
- }
- GLuint CPass::linkProgram (const std::string& vertex, const std::string& fragment, const std::string& shaderName) {
- const GLuint vertexShaderID = compileShader (vertex.c_str (), GL_VERTEX_SHADER);
- const GLuint fragmentShaderID = compileShader (fragment.c_str (), GL_FRAGMENT_SHADER);
- const GLuint program = glCreateProgram ();
- glAttachShader (program, vertexShaderID);
- glAttachShader (program, fragmentShaderID);
- glLinkProgram (program);
- GLint result = GL_FALSE;
- int infoLogLength = 0;
- glGetProgramiv (program, GL_LINK_STATUS, &result);
- glGetProgramiv (program, GL_INFO_LOG_LENGTH, &infoLogLength);
- if (infoLogLength > 0) {
- const auto logBuffer = new char[infoLogLength + 1];
- memset (logBuffer, 0, infoLogLength + 1);
- glGetProgramInfoLog (program, infoLogLength, nullptr, logBuffer);
- const std::string message = logBuffer;
- delete[] logBuffer;
- if (result == GL_FALSE) {
- sLog.exception (message);
- } else {
- sLog.error (message);
- }
- }
- #if !NDEBUG
- glObjectLabel (GL_PROGRAM, program, -1, shaderName.c_str ());
- glObjectLabel (GL_SHADER, vertexShaderID, -1, (shaderName + ".vert").c_str ());
- glObjectLabel (GL_SHADER, fragmentShaderID, -1, (shaderName + ".frag").c_str ());
- #endif /* DEBUG */
- // once linked, the shaders themselves are no longer needed and can be detached/deleted
- glDetachShader (program, vertexShaderID);
- glDetachShader (program, fragmentShaderID);
- glDeleteShader (vertexShaderID);
- glDeleteShader (fragmentShaderID);
- return program;
- }
- bool CPass::applyFormatCombos (const TextureMap& passTextures, const TextureMap& overrideTextures) {
- const auto& fragment = this->m_shader->getFragment ();
- bool changed = false;
- for (const int slot : fragment.getFormatComboSlots ()) {
- std::shared_ptr<const TextureProvider> texture;
- if (slot == 0) {
- texture = this->m_renderable.getTexture ();
- } else {
- std::optional<std::string> name;
- for (const TextureMap* map : { &overrideTextures, &passTextures, &fragment.getTextures () }) {
- if (const auto it = map->find (slot); it != map->end () && !it->second.empty ()) {
- name = it->second;
- break;
- }
- }
- if (!name.has_value () || name->starts_with ("_rt_") || name->starts_with ("_alias_")) {
- continue;
- }
- try {
- texture
- = this->getContext ().resolveTexture (*name, this->m_renderable.getScene ().getScene ().project);
- } catch (const std::exception&) {
- continue;
- }
- }
- if (texture == nullptr || texture->getFormat () == TextureFormat_UNKNOWN) {
- continue;
- }
- const std::string combo = "TEX" + std::to_string (slot) + "FORMAT";
- const int format = static_cast<int> (texture->getFormat ());
- if (const auto it = this->m_combos.find (combo); it == this->m_combos.end () || it->second != format) {
- this->m_combos.insert_or_assign (combo, format);
- changed = true;
- }
- }
- return changed;
- }
- void CPass::setupAttributes () {
- this->addAttribute ("a_TexCoord", GL_FLOAT, 2, &this->a_TexCoord);
- this->addAttribute ("a_Position", GL_FLOAT, 3, &this->a_Position);
- }
- void CPass::setupTextureUniforms () {
- // Vertex shaders don't carry texture info in practice, but check them first anyway;
- // fragment textures are checked after and override/extend the chain.
- for (const auto& [index, textureName] : this->m_shader->getVertex ().getTextures ()) {
- try {
- auto texture = this->resolveNamedTexture (textureName);
- // create chain entry
- this->m_textures[index] = std::make_shared<TextureChainEntry> (TextureChainEntry {
- .texture = texture,
- .next = nullptr,
- });
- } catch (std::runtime_error& ex) {
- sLog.error (
- "Cannot resolve texture '", textureName, "' (index=", index,
- ", object id=", this->m_renderable.getId (), ") for fragment shader ", ex.what ()
- );
- }
- }
- for (const auto& [index, textureName] : this->m_shader->getFragment ().getTextures ()) {
- try {
- auto texture = this->resolveNamedTexture (textureName);
- const auto it = this->m_textures.find (index);
- const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
- .texture = texture,
- .next = it != this->m_textures.end () ? it->second : nullptr,
- });
- this->m_textures[index] = chain;
- } catch (std::runtime_error& ex) {
- sLog.error (
- "Cannot resolve texture '", textureName, "' (index=", index,
- ", object id=", this->m_renderable.getId (), ") for fragment shader ", ex.what ()
- );
- }
- }
- for (const auto& [index, textureName] : this->m_pass.textures) {
- try {
- auto texture = this->resolveNamedTexture (textureName);
- const auto it = this->m_textures.find (index);
- const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
- .texture = texture,
- .next = it != this->m_textures.end () ? it->second : nullptr,
- });
- this->m_textures[index] = chain;
- if (textureName.find ("_rt_") != 0 && textureName.find ("_alias_") != 0) {
- this->trackPlayback (texture);
- }
- } catch (std::runtime_error& ex) {
- sLog.error (
- "Cannot resolve texture '", textureName, "' (index=", index,
- ", object id=", this->m_renderable.getId (), ") for pass ", ex.what ()
- );
- }
- }
- for (const auto& [index, propertyName] : this->m_pass.usertextures) {
- const auto resolvedName = this->resolveUserTextureName (propertyName);
- if (!resolvedName.has_value ()) {
- // optional user-provided texture slot, nothing configured - keep whatever the
- // regular "textures" entry already set for this index (if any)
- continue;
- }
- const std::string& textureName = *resolvedName;
- try {
- auto texture = this->resolveNamedTexture (textureName);
- const auto it = this->m_textures.find (index);
- const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
- .texture = texture,
- .next = it != this->m_textures.end () ? it->second : nullptr,
- });
- this->m_textures[index] = chain;
- if (textureName.find ("_rt_") != 0 && textureName.find ("_alias_") != 0) {
- this->trackPlayback (texture);
- }
- } catch (std::runtime_error& ex) {
- sLog.error (
- "Cannot resolve user texture '", textureName, "' (index=", index,
- ", object id=", this->m_renderable.getId (), ") for pass ", ex.what ()
- );
- }
- }
- // override any texture
- for (const auto& [index, textureName] : this->m_override.textures) {
- try {
- auto texture = this->resolveNamedTexture (textureName);
- const auto it = this->m_textures.find (index);
- const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
- .texture = texture,
- .next = it != this->m_textures.end () ? it->second : nullptr,
- });
- this->m_textures[index] = chain;
- if (textureName.find ("_rt_") != 0 && textureName.find ("_alias_") != 0) {
- this->trackPlayback (texture);
- }
- } catch (std::runtime_error& ex) {
- sLog.error (
- "Cannot resolve texture '", textureName, "' (index=", index,
- ", object id=", this->m_renderable.getId (), ") for override ", ex.what ()
- );
- }
- }
- for (const auto& [index, propertyName] : this->m_override.usertextures) {
- const auto resolvedName = this->resolveUserTextureName (propertyName);
- if (!resolvedName.has_value ()) {
- continue;
- }
- const std::string& textureName = *resolvedName;
- try {
- auto texture = this->resolveNamedTexture (textureName);
- const auto it = this->m_textures.find (index);
- const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
- .texture = texture,
- .next = it != this->m_textures.end () ? it->second : nullptr,
- });
- this->m_textures[index] = chain;
- if (textureName.find ("_rt_") != 0 && textureName.find ("_alias_") != 0) {
- this->trackPlayback (texture);
- }
- } catch (std::runtime_error& ex) {
- sLog.error (
- "Cannot resolve user texture '", textureName, "' (index=", index,
- ", object id=", this->m_renderable.getId (), ") for override ", ex.what ()
- );
- }
- }
- // binds are set last as they're the most important to be set
- for (const auto& [index, bind] : this->m_binds) {
- const auto texture = bind == "previous" ? nullptr : this->resolveFBO (bind);
- const auto it = this->m_textures.find (index);
- const auto chain = std::make_shared<TextureChainEntry> (TextureChainEntry {
- .texture = texture,
- .next = it != this->m_textures.end () ? it->second : nullptr,
- });
- this->m_textures[index] = chain;
- }
- std::shared_ptr<const TextureProvider> texture = this->resolveTexture (this->m_renderable.getTexture (), 0);
- this->addUniform ("g_Texture0", 0);
- this->addUniform ("g_Texture1", 1);
- this->addUniform ("g_Texture2", 2);
- this->addUniform ("g_Texture3", 3);
- this->addUniform ("g_Texture4", 4);
- this->addUniform ("g_Texture5", 5);
- this->addUniform ("g_Texture6", 6);
- this->addUniform ("g_Texture7", 7);
- this->addUniform ("g_TextureReductionScale", 1.0f);
- this->m_texture0Resolution = *texture->getResolution ();
- this->addUniform ("g_Texture0Resolution", &this->m_texture0Resolution);
- for (const auto& [textureIndex, expectedTexture] : this->m_textures) {
- std::ostringstream namestream;
- namestream << "g_Texture" << textureIndex << "Resolution";
- texture = this->resolveTexture (expectedTexture->texture, textureIndex, texture);
- const glm::vec4* res = texture->getResolution ();
- this->addUniform (namestream.str (), res);
- // the mip count of a mipmapped frame buffer, REFLECTION scales its roughness LOD by it
- if (const auto fbo = std::dynamic_pointer_cast<const CFBO> (texture);
- fbo != nullptr && fbo->getMipLevels () > 1) {
- this->addUniform (
- "g_Texture" + std::to_string (textureIndex) + "MipMapInfo", static_cast<float> (fbo->getMipLevels ())
- );
- }
- }
- this->addUniform ("g_Texture0Resolution", &this->m_texture0Resolution);
- }
- void CPass::setupUniforms () {
- this->setupTextureUniforms ();
- const auto& renderable = this->m_renderable;
- const auto& scene = this->m_renderable.getScene ();
- const auto& sceneData = this->m_renderable.getScene ().getScene ();
- const auto& recorder = this->m_renderable.getScene ().getAudioContext ().getRecorder ();
- // lighting variables
- this->addUniform ("g_LightAmbientColor", sceneData.colors.ambient->value->getVec3 ());
- this->addUniform ("g_LightSkylightColor", sceneData.colors.skylight->value->getVec3 ());
- this->addUniform ("g_LightsPosition", UniformType::Vector3, scene.getLightsPosition (), 4);
- this->addUniform ("g_LightsColorPremultiplied", UniformType::Vector4, scene.getLightsColorPremultiplied (), 3);
- this->addUniform ("g_LightsColorRadius", UniformType::Vector4, scene.getLightsColorRadius (), 4);
- // LightingV1, the arrays are as long as the scene's lightconfig counts
- const auto& lighting = scene.getLightingV1 ();
- const auto addLights = [this] (const char* name, const glm::vec4* values, const int count) {
- if (count > 0) {
- this->addUniform (name, UniformType::Vector4, values, count);
- }
- };
- addLights ("g_LPoint_Color", lighting.pointColor, lighting.points);
- addLights ("g_LPoint_Origin", lighting.pointOrigin, lighting.points);
- addLights ("g_LSpot_Color", lighting.spotColor, lighting.spots);
- addLights ("g_LSpot_Origin", lighting.spotOrigin, lighting.spots);
- addLights ("g_LSpot_Direction", lighting.spotDirection, lighting.spots);
- addLights ("g_LSpot_Exponent", lighting.spotExponent, lighting.spots);
- addLights ("g_LTube_Color", lighting.tubeColor, lighting.tubes);
- addLights ("g_LTube_OriginA", lighting.tubeOriginA, lighting.tubes);
- addLights ("g_LTube_OriginB", lighting.tubeOriginB, lighting.tubes);
- addLights ("g_LDirectional_Color", lighting.directionalColor, lighting.directionals);
- addLights ("g_LDirectional_Direction", lighting.directionalDirection, lighting.directionals);
- if (lighting.spotCookies > 0) {
- this->addUniform (
- "g_LFeature_ShadowProjection", UniformType::Matrix4, lighting.featureProjection, lighting.spotCookies
- );
- addLights ("g_LFeature_ShadowProjectionTransform", lighting.featureProjectionTransform, lighting.spotCookies);
- }
- this->addUniform ("g_FogDistanceColor", &scene.getFog ().distanceColor);
- this->addUniform ("g_FogDistanceParams", &scene.getFog ().distanceParams);
- this->addUniform ("g_FogHeightColor", &scene.getFog ().heightColor);
- this->addUniform (
- "g_FogHeightParams", this->m_fogWorld ? &scene.getFog ().heightParamsWorld : &scene.getFog ().heightParamsLocal
- );
- this->addUniform ("g_AltModelMatrix", &this->m_lightingModelMatrix);
- this->addUniform ("g_AltNormalModelMatrix", &this->m_lightingNormalMatrix);
- this->addUniform ("g_AltViewProjectionMatrix", &this->m_lightingViewProjectionMatrix);
- this->addUniform (
- "g_Screen",
- glm::vec3 (
- scene.getWidth (), scene.getHeight (),
- static_cast<float> (scene.getWidth ()) / static_cast<float> (std::max (scene.getHeight (), 1))
- )
- );
- // register variables like brightness and alpha with the layer's values, unless the pass sets them itself
- const auto addRenderableUniform = [this] (const char* name, const auto& value) {
- if (!this->m_constantUniforms.contains (name)) {
- this->addUniform (name, value);
- }
- };
- addRenderableUniform ("g_Brightness", renderable.getBrightness ());
- addRenderableUniform ("g_UserAlpha", renderable.getUserAlpha ());
- addRenderableUniform ("g_Alpha", renderable.getAlpha ());
- addRenderableUniform ("g_Color", renderable.getColor ());
- addRenderableUniform ("g_Color4", renderable.getColor4 ());
- if (!this->m_uniforms.contains ("g_CompositeColor")) {
- this->addUniform ("g_CompositeColor", renderable.getCompositeColor ());
- }
- // add some external variables
- this->addUniform ("g_Time", &g_Time);
- this->addUniform ("g_Daytime", &g_Daytime);
- // add model-view-projection matrix
- this->addUniform ("g_ModelViewProjectionMatrixInverse", &this->m_modelViewProjectionMatrixInverse);
- this->addUniform ("g_ModelViewProjectionMatrix", &this->m_modelViewProjectionMatrix);
- this->addUniform ("g_EffectModelViewProjectionMatrix", &this->m_effectModelViewProjectionMatrix);
- this->addUniform ("g_ModelMatrix", &this->m_modelMatrix);
- this->addUniform ("g_EffectModelMatrix", &this->m_modelMatrix);
- this->addUniform ("g_LayerModelMatrix", &this->m_layerModelMatrix);
- this->addUniform ("g_NormalModelMatrix", glm::identity<glm::mat3> ());
- this->addUniform ("g_ViewProjectionMatrix", &this->m_viewProjectionMatrix);
- this->addUniform ("g_PointerPosition", scene.getMousePosition ());
- this->addUniform ("g_PointerPositionLast", scene.getMousePositionLast ());
- this->addUniform ("g_ParallaxPosition", scene.getParallaxPosition ());
- this->addUniform ("g_EffectTextureProjectionMatrix", &this->m_effectTextureProjectionMatrix);
- this->addUniform ("g_EffectTextureProjectionMatrixInverse", &this->m_effectTextureProjectionMatrixInverse);
- this->addUniform ("g_TexelSize", glm::vec2 (1.0 / scene.getWidth (), 1.0 / scene.getHeight ()));
- this->addUniform ("g_TexelSizeHalf", glm::vec2 (0.5 / scene.getWidth (), 0.5 / scene.getHeight ()));
- this->addUniform ("g_AudioSpectrum16Left", recorder.audio16, 16);
- this->addUniform ("g_AudioSpectrum16Right", recorder.audio16 + 16, 16);
- this->addUniform ("g_AudioSpectrum32Left", recorder.audio32, 32);
- this->addUniform ("g_AudioSpectrum32Right", recorder.audio32 + 32, 32);
- this->addUniform ("g_AudioSpectrum64Left", recorder.audio64, 64);
- this->addUniform ("g_AudioSpectrum64Right", recorder.audio64 + 64, 64);
- }
- void CPass::addAttribute (const std::string& name, GLint type, GLint elements, const GLuint* value) {
- const GLint id = glGetAttribLocation (this->m_programID, name.c_str ());
- if (id == -1) {
- return;
- }
- this->m_attribs.emplace_back (new AttribEntry (id, name, type, elements, value));
- }
- template <typename T> void CPass::addUniform (const std::string& name, UniformType type, T value) {
- GLint id = glGetUniformLocation (this->m_programID, name.c_str ());
- // parameter not found, can be ignored
- if (id == -1) {
- return;
- }
- // frees any previously registered value for this uniform name
- const auto it = this->m_uniforms.find (name);
- if (it != this->m_uniforms.end ()) {
- delete it->second;
- }
- T* newValue = new T (value);
- this->m_uniforms.insert_or_assign (name, new UniformEntry (id, name, type, newValue, 1, true));
- }
- template <typename T> void CPass::addUniform (const std::string& name, UniformType type, T* value, int count) {
- // this version is used to reference to system variables so things like g_Time works fine
- GLint id = glGetUniformLocation (this->m_programID, name.c_str ());
- // parameter not found, can be ignored
- if (id == -1) {
- return;
- }
- if (const auto it = this->m_uniforms.find (name); it != this->m_uniforms.end ()) {
- delete it->second;
- }
- this->m_uniforms.insert_or_assign (name, new UniformEntry (id, name, type, value, count));
- }
- template <typename T> void CPass::addUniform (const std::string& name, UniformType type, T** value) {
- // this version is used to reference to system variables so things like g_Time works fine
- const GLint id = glGetUniformLocation (this->m_programID, name.c_str ());
- // parameter not found, can be ignored
- if (id == -1) {
- return;
- }
- if (const auto it = this->m_uniforms.find (name); it != this->m_uniforms.end ()) {
- delete it->second;
- }
- this->m_referenceUniforms.insert_or_assign (
- name, new ReferenceUniformEntry (id, name, type, reinterpret_cast<const void**> (value))
- );
- }
- void CPass::setupShaderVariables () {
- for (const auto& cur : this->m_shader->getVertex ().getParameters ()) {
- if (!this->m_uniforms.contains (cur->getName ())) {
- this->addUniform (cur);
- }
- }
- for (const auto& cur : this->m_shader->getFragment ().getParameters ()) {
- if (!this->m_uniforms.contains (cur->getName ())) {
- this->addUniform (cur);
- }
- }
- // apply material pass constants (e.g. constantshadervalues from the material JSON)
- for (const auto& [name, value] : this->m_pass.constants) {
- const auto [vertex, fragment] = this->m_shader->findParameter (name);
- if (vertex == nullptr && fragment == nullptr) {
- continue;
- }
- ShaderVariable* var = vertex == nullptr ? fragment : vertex;
- this->addUniform (var, value->value.get ());
- this->m_constantUniforms.insert (var->getName ());
- }
- // apply override constants (highest priority, overrides both defaults and pass constants)
- for (const auto& [name, value] : this->m_override.constants) {
- const auto [vertex, fragment] = this->m_shader->findParameter (name);
- if (vertex == nullptr && fragment == nullptr) {
- continue;
- }
- ShaderVariable* var = vertex == nullptr ? fragment : vertex;
- this->addUniform (var, value->value.get ());
- this->m_constantUniforms.insert (var->getName ());
- }
- // bind the full-reveal bypass uniform injected by patchXrayFullRevealBypass() (see setupShaders());
- // a no-op if the patch didn't find its anchors, since the uniform then doesn't exist in the shader
- if (this->m_pass.shader == XRAY_EFFECT_SHADER) {
- this->addUniform ("g_XrayFullReveal", &this->m_xrayFullReveal);
- }
- }
- void CPass::addUniform (ShaderVariable* value) {
- // delegates to the (ShaderVariable*, DynamicValue*) overload, which handles the casting
- this->addUniform (value, value);
- }
- void CPass::addUniform (const ShaderVariable* value, const DynamicValue* setting) {
- if (value->is<ShaderVariableFloat> ()) {
- this->addUniform (value->getName (), &setting->getFloat ());
- } else if (value->is<ShaderVariableInteger> ()) {
- this->addUniform (value->getName (), &setting->getInt ());
- } else if (value->is<ShaderVariableVector2> ()) {
- this->addUniform (value->getName (), &setting->getVec2 ());
- } else if (value->is<ShaderVariableVector3> ()) {
- this->addUniform (value->getName (), &setting->getVec3 ());
- } else if (value->is<ShaderVariableVector4> ()) {
- this->addUniform (value->getName (), &setting->getVec4 ());
- } else {
- sLog.error ("Cannot convert setting dynamic value to ", value->getName (), ". Using default value");
- }
- }
- void CPass::addUniform (const std::string& name, int value) { this->addUniform (name, UniformType::Integer, value); }
- void CPass::addUniform (const std::string& name, const int* value, int count) {
- this->addUniform (name, UniformType::Integer, value, count);
- }
- void CPass::addUniform (const std::string& name, const int** value) {
- this->addUniform (name, UniformType::Integer, value);
- }
- void CPass::addUniform (const std::string& name, double value) { this->addUniform (name, UniformType::Double, value); }
- void CPass::addUniform (const std::string& name, const double* value, int count) {
- this->addUniform (name, UniformType::Double, value, count);
- }
- void CPass::addUniform (const std::string& name, const double** value) {
- this->addUniform (name, UniformType::Double, value);
- }
- void CPass::addUniform (const std::string& name, float value) { this->addUniform (name, UniformType::Float, value); }
- void CPass::addUniform (const std::string& name, const float* value, int count) {
- this->addUniform (name, UniformType::Float, value, count);
- }
- void CPass::addUniform (const std::string& name, const float** value) {
- this->addUniform (name, UniformType::Float, value);
- }
- void CPass::addUniform (const std::string& name, glm::vec2 value) {
- this->addUniform (name, UniformType::Vector2, value);
- }
- void CPass::addUniform (const std::string& name, const glm::vec2* value) {
- this->addUniform (name, UniformType::Vector2, value, 1);
- }
- void CPass::addUniform (const std::string& name, const glm::vec2** value) {
- this->addUniform (name, UniformType::Vector2, value, 1);
- }
- void CPass::addUniform (const std::string& name, glm::vec3 value) {
- this->addUniform (name, UniformType::Vector3, value);
- }
- void CPass::addUniform (const std::string& name, const glm::vec3* value) {
- this->addUniform (name, UniformType::Vector3, value, 1);
- }
- void CPass::addUniform (const std::string& name, const glm::vec3** value) {
- this->addUniform (name, UniformType::Vector3, value);
- }
- void CPass::addUniform (const std::string& name, const glm::vec4 value) {
- this->addUniform (name, UniformType::Vector4, value);
- }
- GLenum CPass::getDeclaredUniformType (const std::string& name) const {
- GLint count = 0;
- glGetProgramiv (this->m_programID, GL_ACTIVE_UNIFORMS, &count);
- for (GLint index = 0; index < count; index++) {
- char buffer[256];
- GLsizei length = 0;
- GLint size = 0;
- GLenum type = GL_NONE;
- glGetActiveUniform (this->m_programID, index, sizeof (buffer), &length, &size, &type, buffer);
- if (name == buffer) {
- return type;
- }
- }
- return GL_NONE;
- }
- void CPass::addUniform (const std::string& name, const glm::vec4* value) {
- // resolution uniforms are always kept as a vec4 (texture size + real size), but shaders often declare
- // them as a vec2 and glUniform4 on that is a GL error that leaves the uniform at 0 (color_key_plus
- // then divides by a zero resolution and the whole layer comes out NaN)
- switch (this->getDeclaredUniformType (name)) {
- case GL_FLOAT_VEC2:
- this->addUniform (name, UniformType::Vector2, reinterpret_cast<const glm::vec2*> (value), 1);
- return;
- case GL_FLOAT_VEC3:
- this->addUniform (name, UniformType::Vector3, reinterpret_cast<const glm::vec3*> (value), 1);
- return;
- default:
- break;
- }
- this->addUniform (name, UniformType::Vector4, value, 1);
- }
- void CPass::addUniform (const std::string& name, const glm::vec4** value) {
- this->addUniform (name, UniformType::Vector4, value);
- }
- void CPass::addUniform (const std::string& name, const glm::mat3& value) {
- this->addUniform (name, UniformType::Matrix3, value);
- }
- void CPass::addUniform (const std::string& name, const glm::mat3* value) {
- this->addUniform (name, UniformType::Matrix3, value, 1);
- }
- void CPass::addUniform (const std::string& name, const glm::mat3** value) {
- this->addUniform (name, UniformType::Matrix3, value);
- }
- void CPass::addUniform (const std::string& name, const glm::mat4 value) {
- this->addUniform (name, UniformType::Matrix4, value);
- }
- void CPass::addUniform (const std::string& name, const glm::mat4* value) {
- this->addUniform (name, UniformType::Matrix4, value, 1);
- }
- void CPass::addUniform (const std::string& name, const glm::mat4** value) {
- this->addUniform (name, UniformType::Matrix4, value);
- }
|